From 94c385292942428a14a023f7588ab04bfecc3b34 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 10 Jul 2015 17:02:31 -0400 Subject: [PATCH] ignore IIFEs for the purposes of determining strong/weak dependencies --- src/Statement.js | 48 ++++++++++++++----- test/function/iife-strong-dependencies/A.js | 15 ++++++ test/function/iife-strong-dependencies/B.js | 11 +++++ test/function/iife-strong-dependencies/C.js | 11 +++++ test/function/iife-strong-dependencies/D.js | 15 ++++++ .../iife-strong-dependencies/_config.js | 15 ++++++ .../function/iife-strong-dependencies/main.js | 14 ++++++ 7 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 test/function/iife-strong-dependencies/A.js create mode 100644 test/function/iife-strong-dependencies/B.js create mode 100644 test/function/iife-strong-dependencies/C.js create mode 100644 test/function/iife-strong-dependencies/D.js create mode 100644 test/function/iife-strong-dependencies/_config.js create mode 100644 test/function/iife-strong-dependencies/main.js diff --git a/src/Statement.js b/src/Statement.js index 9a76712..3cfc4a9 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -4,6 +4,10 @@ import getLocation from './utils/getLocation'; import walk from './ast/walk'; import Scope from './ast/Scope'; +function isIife ( node, parent ) { + return parent && parent.type === 'CallExpression' && node === parent.callee; +} + export default class Statement { constructor ( node, magicString, module, index ) { this.node = node; @@ -38,7 +42,7 @@ export default class Statement { let scope = this.scope; walk( this.node, { - enter ( node ) { + enter ( node, parent ) { let newScope; magicString.addSourcemapLocation( node.start ); @@ -66,10 +70,12 @@ export default class Statement { break; case 'BlockStatement': - newScope = new Scope({ - parent: scope, - block: true - }); + if ( !/Function/.test( parent.type ) ) { + newScope = new Scope({ + parent: scope, + block: true + }); + } break; @@ -105,16 +111,35 @@ export default class Statement { } }); + // This allows us to track whether we're looking at code that will + // be executed immediately (either outside a function, or immediately + // inside an IIFE), for the purposes of determining whether dependencies + // are strong or weak. It's not bulletproof, since it wouldn't catch... + // + // var calledImmediately = function () { + // doSomethingWith( strongDependency ); + // } + // calledImmediately(); + // + // ...but it's better than nothing + let depth = 0; + if ( !this.isImportDeclaration ) { walk( this.node, { enter: ( node, parent ) => { - if ( node._scope ) scope = node._scope; + if ( node._scope ) { + if ( !scope.isBlockScope && !isIife( node, parent ) ) depth += 1; + scope = node._scope; + } - this.checkForReads( scope, node, parent ); + this.checkForReads( scope, node, parent, !depth ); this.checkForWrites( scope, node ); }, - leave: ( node ) => { - if ( node._scope ) scope = scope.parent; + leave: ( node, parent ) => { + if ( node._scope ) { + if ( !scope.isBlockScope && !isIife( node, parent ) ) depth -= 1; + scope = scope.parent; + } } }); } @@ -124,7 +149,7 @@ export default class Statement { }); } - checkForReads ( scope, node, parent ) { + checkForReads ( scope, node, parent, strong ) { if ( node.type === 'Identifier' ) { // disregard the `bar` in `foo.bar` - these appear as Identifier nodes if ( parent.type === 'MemberExpression' && node !== parent.object ) { @@ -143,8 +168,7 @@ export default class Statement { if ( ( !definingScope || definingScope.depth === 0 ) && !this.defines[ node.name ] ) { this.dependsOn[ node.name ] = true; - - if ( !scope.parent ) this.stronglyDependsOn[ node.name ] = true; + if ( strong ) this.stronglyDependsOn[ node.name ] = true; } } } diff --git a/test/function/iife-strong-dependencies/A.js b/test/function/iife-strong-dependencies/A.js new file mode 100644 index 0000000..2c51ca5 --- /dev/null +++ b/test/function/iife-strong-dependencies/A.js @@ -0,0 +1,15 @@ +import { B } from './B'; + +export var A = function () { + this.isA = true; +}; + +A.prototype = { + b: function () { + var Constructor = B; + + return function () { + return new Constructor(); + }; + }() +}; diff --git a/test/function/iife-strong-dependencies/B.js b/test/function/iife-strong-dependencies/B.js new file mode 100644 index 0000000..f06d45d --- /dev/null +++ b/test/function/iife-strong-dependencies/B.js @@ -0,0 +1,11 @@ +import { A } from './A'; + +export var B = function () { + this.isB = true; +}; + +B.prototype = { + a: function () { + return new A(); + } +}; diff --git a/test/function/iife-strong-dependencies/C.js b/test/function/iife-strong-dependencies/C.js new file mode 100644 index 0000000..9da8331 --- /dev/null +++ b/test/function/iife-strong-dependencies/C.js @@ -0,0 +1,11 @@ +import { D } from './D'; + +export var C = function () { + this.isC = true; +}; + +C.prototype = { + d: function () { + return new D(); + } +}; diff --git a/test/function/iife-strong-dependencies/D.js b/test/function/iife-strong-dependencies/D.js new file mode 100644 index 0000000..ad3afa0 --- /dev/null +++ b/test/function/iife-strong-dependencies/D.js @@ -0,0 +1,15 @@ +import { C } from './C'; + +export var D = function () { + this.isD = true; +}; + +D.prototype = { + c: function () { + var Constructor = C; + + return function () { + return new Constructor(); + }; + }() +}; diff --git a/test/function/iife-strong-dependencies/_config.js b/test/function/iife-strong-dependencies/_config.js new file mode 100644 index 0000000..10cdaca --- /dev/null +++ b/test/function/iife-strong-dependencies/_config.js @@ -0,0 +1,15 @@ +var assert = require( 'assert' ); + +module.exports = { + description: 'does not treat references inside IIFEs as weak dependencies', // edge case encountered in THREE.js codebase + exports: function ( exports ) { + assert.ok( exports.a1.isA ); + assert.ok( exports.b1.isB ); + assert.ok( exports.c1.isC ); + assert.ok( exports.d1.isD ); + assert.ok( exports.a2.isA ); + assert.ok( exports.b2.isB ); + assert.ok( exports.c2.isC ); + assert.ok( exports.d2.isD ); + } +}; diff --git a/test/function/iife-strong-dependencies/main.js b/test/function/iife-strong-dependencies/main.js new file mode 100644 index 0000000..1eaa49d --- /dev/null +++ b/test/function/iife-strong-dependencies/main.js @@ -0,0 +1,14 @@ +import { A } from './A'; +import { B } from './B'; +import { C } from './C'; +import { D } from './D'; + +export var a1 = new A(); +export var b1 = new B(); +export var c1 = new C(); +export var d1 = new D(); + +export var a2 = b1.a(); +export var b2 = a1.b(); +export var c2 = d1.c(); +export var d2 = c1.d();