diff --git a/src/ast/nodes/BlockStatement.js b/src/ast/nodes/BlockStatement.js index 1aad2de6573..c344510567c 100644 --- a/src/ast/nodes/BlockStatement.js +++ b/src/ast/nodes/BlockStatement.js @@ -1,12 +1,12 @@ import Statement from './shared/Statement.js'; import BlockScope from '../scopes/BlockScope'; -import UndefinedIdentifier from './shared/UndefinedIdentifier'; +import { UNKNOWN_ASSIGNMENT } from '../values'; export default class BlockStatement extends Statement { bindImplicitReturnExpressionToScope () { const lastStatement = this.body[ this.body.length - 1 ]; if ( !lastStatement || lastStatement.type !== 'ReturnStatement' ) { - this.scope.addReturnExpression( new UndefinedIdentifier() ); + this.scope.addReturnExpression( UNKNOWN_ASSIGNMENT ); } } diff --git a/src/ast/nodes/UnaryExpression.js b/src/ast/nodes/UnaryExpression.js index 4e50e2b875b..0b79f177472 100644 --- a/src/ast/nodes/UnaryExpression.js +++ b/src/ast/nodes/UnaryExpression.js @@ -1,6 +1,5 @@ import Node from '../Node.js'; -import { UNKNOWN_VALUE } from '../values'; -import UndefinedIdentifier from './shared/UndefinedIdentifier'; +import { UNKNOWN_ASSIGNMENT, UNKNOWN_VALUE } from '../values'; import ExecutionPathOptions from '../ExecutionPathOptions'; const operators = { @@ -16,7 +15,7 @@ const operators = { export default class UnaryExpression extends Node { bindNode () { if ( this.operator === 'delete' ) { - this.argument.bindAssignmentAtPath( [], new UndefinedIdentifier(), ExecutionPathOptions.create() ); + this.argument.bindAssignmentAtPath( [], UNKNOWN_ASSIGNMENT, ExecutionPathOptions.create() ); } } diff --git a/src/ast/nodes/shared/FunctionNode.js b/src/ast/nodes/shared/FunctionNode.js index b3116c07d8a..3a7d1a16470 100644 --- a/src/ast/nodes/shared/FunctionNode.js +++ b/src/ast/nodes/shared/FunctionNode.js @@ -51,6 +51,11 @@ export default class FunctionNode extends Node { || this.body.hasEffects( innerOptions ); } + includeInBundle () { + this.scope.variables.arguments.includeVariable(); + return super.includeInBundle(); + } + initialiseNode () { this.prototypeObject = new VirtualObjectExpression(); } diff --git a/src/ast/nodes/shared/UndefinedIdentifier.js b/src/ast/nodes/shared/UndefinedIdentifier.js deleted file mode 100644 index 46a561a09f6..00000000000 --- a/src/ast/nodes/shared/UndefinedIdentifier.js +++ /dev/null @@ -1,19 +0,0 @@ -import Node from '../../Node'; - -export default class UndefinedIdentifier extends Node { - hasEffects () { - return false; - } - - hasEffectsWhenAccessedAtPath ( path ) { - return path.length > 0; - } - - hasEffectsWhenAssignedAtPath ( path ) { - return path.length > 0; - } - - toString () { - return 'undefined'; - } -} diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js index ebdc5c58cfd..6d9c472e997 100644 --- a/src/ast/scopes/ModuleScope.js +++ b/src/ast/scopes/ModuleScope.js @@ -2,7 +2,7 @@ import { forOwn } from '../../utils/object.js'; import relativeId from '../../utils/relativeId.js'; import Scope from './Scope.js'; import LocalVariable from '../variables/LocalVariable'; -import UndefinedIdentifier from '../nodes/shared/UndefinedIdentifier'; +import { UNKNOWN_ASSIGNMENT } from '../values'; export default class ModuleScope extends Scope { constructor ( module ) { @@ -12,7 +12,7 @@ export default class ModuleScope extends Scope { } ); this.module = module; - this.variables.this = new LocalVariable( 'this', null, new UndefinedIdentifier() ); + this.variables.this = new LocalVariable( 'this', null, UNKNOWN_ASSIGNMENT ); } deshadow ( names ) { diff --git a/src/ast/scopes/Scope.js b/src/ast/scopes/Scope.js index 28e9a409fcf..cc8548e6b67 100644 --- a/src/ast/scopes/Scope.js +++ b/src/ast/scopes/Scope.js @@ -1,7 +1,7 @@ import { blank, keys } from '../../utils/object.js'; import LocalVariable from '../variables/LocalVariable'; import ExportDefaultVariable from '../variables/ExportDefaultVariable'; -import UndefinedIdentifier from '../nodes/shared/UndefinedIdentifier'; +import { UNKNOWN_ASSIGNMENT } from '../values'; export default class Scope { constructor ( options = {} ) { @@ -28,7 +28,7 @@ export default class Scope { variable.addDeclaration( identifier ); options.init && variable.bindAssignmentAtPath( [], options.init ); } else { - this.variables[ name ] = new LocalVariable( identifier.name, identifier, options.init || new UndefinedIdentifier() ); + this.variables[ name ] = new LocalVariable( identifier.name, identifier, options.init || UNKNOWN_ASSIGNMENT ); } return this.variables[ name ]; } diff --git a/src/ast/values.js b/src/ast/values.js index 04ce1d31a7d..8d97af1d64f 100644 --- a/src/ast/values.js +++ b/src/ast/values.js @@ -4,8 +4,9 @@ export const UNKNOWN_ASSIGNMENT = { type: 'UNKNOWN', bindAssignmentAtPath: () => {}, bindCallAtPath: () => {}, + forEachReturnExpressionWhenCalledAtPath: () => {}, hasEffectsWhenAccessedAtPath: path => path.length > 0, - hasEffectsWhenAssignedAtPath: () => true, + hasEffectsWhenAssignedAtPath: path => path.length > 0, hasEffectsWhenCalledAtPath: () => true, someReturnExpressionWhenCalledAtPath: () => true, toString: () => '[[UNKNOWN]]' diff --git a/src/ast/variables/ArgumentsVariable.js b/src/ast/variables/ArgumentsVariable.js index 4199623a79d..0dd1e0c7b68 100644 --- a/src/ast/variables/ArgumentsVariable.js +++ b/src/ast/variables/ArgumentsVariable.js @@ -26,11 +26,10 @@ export default class ArgumentsVariable extends LocalVariable { } hasEffectsWhenAssignedAtPath ( path, options ) { - if ( path.length === 0 ) { - return true; - } - return getParameterVariable( path, options ) - .hasEffectsWhenAssignedAtPath( path.slice( 1 ), options ); + return path.length === 0 + || this.included + || getParameterVariable( path, options ) + .hasEffectsWhenAssignedAtPath( path.slice( 1 ), options ); } hasEffectsWhenCalledAtPath ( path, callOptions, options ) { diff --git a/src/ast/variables/ReplaceableInitStructuredAssignmentTracker.js b/src/ast/variables/ReplaceableInitStructuredAssignmentTracker.js index 96d28f0189e..83ed45bb8ba 100644 --- a/src/ast/variables/ReplaceableInitStructuredAssignmentTracker.js +++ b/src/ast/variables/ReplaceableInitStructuredAssignmentTracker.js @@ -1,5 +1,6 @@ import StructuredAssignmentTracker from './StructuredAssignmentTracker'; import LocalVariable from './LocalVariable'; +import ExecutionPathOptions from '../ExecutionPathOptions'; export default class ReplaceableInitStructuredAssignmentTracker extends StructuredAssignmentTracker { constructor () { @@ -9,13 +10,13 @@ export default class ReplaceableInitStructuredAssignmentTracker extends Structur addAtPath ( path, assignment ) { if ( path.length > 0 ) { - this._init.bindAssignmentAtPath( path, assignment ); + this._init.bindAssignmentAtPath( path, assignment, ExecutionPathOptions.create() ); } super.addAtPath( path, assignment ); } addInit ( assignment ) { - this._init.bindAssignmentAtPath( [], assignment ); + this._init.bindAssignmentAtPath( [], assignment, ExecutionPathOptions.create() ); } forEachAtPath ( path, callback ) { diff --git a/src/ast/variables/StructuredAssignmentTracker.js b/src/ast/variables/StructuredAssignmentTracker.js index d1b2500460f..d591dc9865e 100644 --- a/src/ast/variables/StructuredAssignmentTracker.js +++ b/src/ast/variables/StructuredAssignmentTracker.js @@ -69,18 +69,6 @@ export default class StructuredAssignmentTracker { } } - hasEqualAtPath ( path, equalAssignment ) { - if ( path.length === 0 ) { - return Array.from( this._assignments.get( SET_KEY ) ).find( assignment => equalAssignment.equals( assignment ) ); - } else { - const [ nextPath, ...remainingPath ] = path; - if ( !this._assignments.has( nextPath ) ) { - return false; - } - return this._assignments.get( nextPath ).hasAtPath( remainingPath, equalAssignment ); - } - } - someAtPath ( path, predicateFunction ) { const [ nextPath, ...remainingPath ] = path; return Array.from( this._assignments.get( SET_KEY ) ).some( assignment => predicateFunction( path, assignment ) )