From 25ca424c2a987718e6472a67e0e6847ea13d680f Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 20 Sep 2015 19:32:03 -0400 Subject: [PATCH] mark side-effecty statements (call expressions and assignments to globals) - fixes #112 --- src/Bundle.js | 5 +++++ src/Module.js | 6 ++++++ src/Statement.js | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/Bundle.js b/src/Bundle.js index 48ca4c3..28347b1 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -67,6 +67,11 @@ export default class Bundle { entryModule.markAllStatements( true ); entryModule.markAllExports(); + // Include all side-effects + this.modules.forEach( module => { + module.markAllSideEffects(); + }); + // Sort the modules. this.orderedModules = this.sort(); diff --git a/src/Module.js b/src/Module.js index 18f9628..17810a8 100644 --- a/src/Module.js +++ b/src/Module.js @@ -437,6 +437,12 @@ export default class Module { this.markAllExports(); } + markAllSideEffects () { + this.statements.forEach( statement => { + if ( statement.hasSideEffects ) statement.mark(); + }); + } + markAllStatements ( isEntryModule ) { this.statements.forEach( statement => { if ( statement.isIncluded ) return; // TODO can this happen? probably not... diff --git a/src/Statement.js b/src/Statement.js index 91c14aa..fad15ad 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -8,6 +8,11 @@ const blockDeclarations = { 'let': true }; +const modifierNodes = { + AssignmentExpression: 'left', + UpdateExpression: 'argument' +}; + function isIife ( node, parent ) { return parent && parent.type === 'CallExpression' && node === parent.callee; } @@ -47,6 +52,7 @@ export default class Statement { this.dependantIds = []; this.namespaceReplacements = []; + this.hasSideEffects = false; this.isIncluded = false; this.isImportDeclaration = node.type === 'ImportDeclaration'; @@ -167,6 +173,19 @@ export default class Statement { if ( isFunctionDeclaration( node, parent ) ) writeDepth += 1; if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth += 1; + // If this is a top-level call expression, or an assignment to a global, + // this statement will need to be marked + if ( !readDepth ) { + if ( node.type === 'CallExpression' ) { + this.hasSideEffects = true; + } else if ( node.type in modifierNodes ) { + let subject = node[ modifierNodes[ node.type ] ]; + while ( subject.type === 'MemberExpression' ) subject = subject.object; + + if ( !this.module.locals.defines( subject.name ) ) this.hasSideEffects = true; + } + } + if ( node._scope ) scope = node._scope; this.checkForReads( scope, node, parent, !readDepth );