From 4a75826683432ebc3894265a85e15b21f4e609fb Mon Sep 17 00:00:00 2001 From: Miguel Jimenez Esun Date: Mon, 21 May 2018 07:52:20 -0700 Subject: [PATCH] Make constant folding more powerful Reviewed By: davidaurelio Differential Revision: D8038008 fbshipit-source-id: 06c81ccf4e816ceefeb6673f247fd0bac41429a6 --- packages/metro/package.json | 1 + .../__tests__/constant-folding-plugin-test.js | 37 +++++++++ .../worker/constant-folding-plugin.js | 82 ++++++++++--------- .../__snapshots__/basic_bundle-test.js.snap | 32 +------- yarn.lock | 4 + 5 files changed, 88 insertions(+), 68 deletions(-) diff --git a/packages/metro/package.json b/packages/metro/package.json index 5eb937c1..34f16900 100644 --- a/packages/metro/package.json +++ b/packages/metro/package.json @@ -20,6 +20,7 @@ "@babel/plugin-proposal-class-properties": "7.0.0-beta.40", "@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.40", "@babel/plugin-syntax-dynamic-import": "7.0.0-beta.40", + "@babel/plugin-syntax-nullish-coalescing-operator": "7.0.0-beta.40", "@babel/plugin-transform-arrow-functions": "7.0.0-beta.40", "@babel/plugin-transform-async-to-generator": "7.0.0-beta.40", "@babel/plugin-transform-block-scoping": "7.0.0-beta.40", diff --git a/packages/metro/src/JSTransformer/worker/__tests__/constant-folding-plugin-test.js b/packages/metro/src/JSTransformer/worker/__tests__/constant-folding-plugin-test.js index 41ae0205..2a54f7d2 100644 --- a/packages/metro/src/JSTransformer/worker/__tests__/constant-folding-plugin-test.js +++ b/packages/metro/src/JSTransformer/worker/__tests__/constant-folding-plugin-test.js @@ -38,6 +38,7 @@ function parse(code: string): TransformResult { code: false, babelrc: false, compact: true, + plugins: [require('@babel/plugin-syntax-nullish-coalescing-operator')], sourceMaps: true, }); } @@ -107,6 +108,20 @@ describe('constant expressions', () => { ); }); + it('folds null coalescing operator', () => { + const code = ` + var a = undefined ?? u(); + var b = null ?? v(); + var c = false ?? w(); + var d = 0 ?? x(); + var e = NaN ?? x(); + var f = "truthy" ?? z(); + `; + expect(fold('arbitrary.js', code)).toEqual( + 'var a=u();var b=v();var c=false;var d=0;var e=NaN;var f="truthy";', + ); + }); + it('can remode an if statement with a falsy constant test', () => { const code = ` if ('production' === 'development' || false) { @@ -149,4 +164,26 @@ describe('constant expressions', () => { `; expect(fold('arbitrary.js', code)).toEqual("{{require('c');}}"); }); + + it('folds if expressions with variables', () => { + const code = ` + var x = 3; + + if (x - 3) { + require('a'); + } + `; + + expect(fold('arbitrary.js', code)).toEqual('var x=3;'); + }); + + it('folds logical expressions with variables', () => { + const code = ` + var x = 3; + var y = (x - 3) || 4; + var z = (y - 4) && 4; + `; + + expect(fold('arbitrary.js', code)).toEqual('var x=3;var y=4;var z=0;'); + }); }); diff --git a/packages/metro/src/JSTransformer/worker/constant-folding-plugin.js b/packages/metro/src/JSTransformer/worker/constant-folding-plugin.js index 02cf719a..5b711d9c 100644 --- a/packages/metro/src/JSTransformer/worker/constant-folding-plugin.js +++ b/packages/metro/src/JSTransformer/worker/constant-folding-plugin.js @@ -18,54 +18,60 @@ function constantFoldingPlugin(context: {types: BabelTypes}) { const Conditional = { exit(path: Object) { const node = path.node; - const test = node.test; - if (t.isLiteral(test)) { - if (test.value || node.alternate) { - path.replaceWith(test.value ? node.consequent : node.alternate); - } else if (!test.value) { + const result = path.get('test').evaluate(); + + if (result.confident) { + if (result.value || node.alternate) { + path.replaceWith(result.value ? node.consequent : node.alternate); + } else if (!result.value) { path.remove(); } } }, }; + const Expression = { + exit(path: Object) { + const result = path.evaluate(); + + if (result.confident) { + path.replaceWith(t.valueToNode(result.value)); + } + }, + }; + + const LogicalExpression = { + exit(path: Object) { + const node = path.node; + const result = path.get('left').evaluate(); + + if (result.confident) { + const value = result.value; + + switch (node.operator) { + case '||': + path.replaceWith(value ? node.left : node.right); + break; + + case '&&': + path.replaceWith(value ? node.right : node.left); + break; + + case '??': + path.replaceWith(value == null ? node.right : node.left); + break; + } + } + }, + }; + return { visitor: { - BinaryExpression: { - exit(path: Object) { - const node = path.node; - if (t.isLiteral(node.left) && t.isLiteral(node.right)) { - const result = path.evaluate(); - if (result.confident) { - path.replaceWith(t.valueToNode(result.value)); - } - } - }, - }, + BinaryExpression: Expression, ConditionalExpression: Conditional, IfStatement: Conditional, - LogicalExpression: { - exit(path: Object) { - const node = path.node; - const left = node.left; - if (t.isLiteral(left)) { - const value = t.isNullLiteral(left) ? null : left.value; - if (node.operator === '||') { - path.replaceWith(value ? left : node.right); - } else { - path.replaceWith(value ? node.right : left); - } - } - }, - }, - UnaryExpression: { - exit(path: Object) { - const node = path.node; - if (node.operator === '!' && t.isLiteral(node.argument)) { - path.replaceWith(t.valueToNode(!node.argument.value)); - } - }, - }, + LogicalExpression, + UnaryExpression: Expression, }, }; } diff --git a/packages/metro/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap b/packages/metro/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap index 42d844c6..a2deed92 100644 --- a/packages/metro/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap +++ b/packages/metro/src/integration_tests/__tests__/__snapshots__/basic_bundle-test.js.snap @@ -22,16 +22,6 @@ exports[`basic_bundle bundles package with polyfills 1`] = ` hasError: false, isInitialized: false }; - - if (PRINT_REQUIRE_PATHS) { - var _path = arguments[4]; - - if (_path) { - modules[moduleId].path = _path; - } else { - throw new Error(\\"path not set on module with PRINT_REQUIRE_PATHS true. Make sure PASS_MODULE_PATHS_TO_DEFINE is true and restart Metro or rebuild bundle\\"); - } - } } function metroRequire(moduleId) { @@ -61,7 +51,7 @@ exports[`basic_bundle bundles package with polyfills 1`] = ` } var ID_MASK_SHIFT = 16; - var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; + var LOCAL_ID_MASK = 65535; function unpackModuleId(moduleId) { var segmentId = moduleId >>> ID_MASK_SHIFT; @@ -107,10 +97,6 @@ exports[`basic_bundle bundles package with polyfills 1`] = ` dependencyMap = _module.dependencyMap; try { - if (PRINT_REQUIRE_PATHS) { - console.log(\\"require file path \\" + (module.path || 'unknown')); - } - var _moduleObject = { exports: exports }; @@ -225,16 +211,6 @@ exports[`basic_bundle bundles package without polyfills 1`] = ` hasError: false, isInitialized: false }; - - if (PRINT_REQUIRE_PATHS) { - var _path = arguments[4]; - - if (_path) { - modules[moduleId].path = _path; - } else { - throw new Error(\\"path not set on module with PRINT_REQUIRE_PATHS true. Make sure PASS_MODULE_PATHS_TO_DEFINE is true and restart Metro or rebuild bundle\\"); - } - } } function metroRequire(moduleId) { @@ -264,7 +240,7 @@ exports[`basic_bundle bundles package without polyfills 1`] = ` } var ID_MASK_SHIFT = 16; - var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT; + var LOCAL_ID_MASK = 65535; function unpackModuleId(moduleId) { var segmentId = moduleId >>> ID_MASK_SHIFT; @@ -310,10 +286,6 @@ exports[`basic_bundle bundles package without polyfills 1`] = ` dependencyMap = _module.dependencyMap; try { - if (PRINT_REQUIRE_PATHS) { - console.log(\\"require file path \\" + (module.path || 'unknown')); - } - var _moduleObject = { exports: exports }; diff --git a/yarn.lock b/yarn.lock index 970e37f7..70659bfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -250,6 +250,10 @@ version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0-beta.40.tgz#db44d52ff06f784be22f2659e694cc2cf97f99f9" +"@babel/plugin-syntax-nullish-coalescing-operator@7.0.0-beta.40": + version "7.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.0.0-beta.40.tgz#1bd13137a2053b1bab0bb4e914e141fd67a10d7e" + "@babel/plugin-syntax-object-rest-spread@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0-beta.40.tgz#d5e04536062e4df685c203ae48bb19bfe2cf235c"