From cb345fb2d62fe95e189e698b2c70b0caf93719b3 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Fri, 28 Apr 2023 22:03:58 +0200 Subject: [PATCH] Ensure arguments are deoptimized when arguments variable is used --- src/ast/nodes/shared/FunctionBase.ts | 5 +++++ src/ast/nodes/shared/FunctionNode.ts | 5 +++++ src/ast/variables/ArgumentsVariable.ts | 22 +++++++++++++++++++ .../deoptimize-via-arguments/_config.js | 3 +++ .../samples/deoptimize-via-arguments/main.js | 17 ++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 test/function/samples/deoptimize-via-arguments/_config.js create mode 100644 test/function/samples/deoptimize-via-arguments/main.js diff --git a/src/ast/nodes/shared/FunctionBase.ts b/src/ast/nodes/shared/FunctionBase.ts index f1298c0a2f6..c1fa969f3b2 100644 --- a/src/ast/nodes/shared/FunctionBase.ts +++ b/src/ast/nodes/shared/FunctionBase.ts @@ -54,8 +54,11 @@ export default abstract class FunctionBase extends NodeBase { argument.deoptimizePath(UNKNOWN_PATH); } else if (parameter instanceof Identifier) { parameters[position][0].addEntityToBeDeoptimized(argument); + this.addArgumentToBeDeoptimized(argument); } else if (parameter) { argument.deoptimizePath(UNKNOWN_PATH); + } else { + this.addArgumentToBeDeoptimized(argument); } } } else { @@ -181,6 +184,8 @@ export default abstract class FunctionBase extends NodeBase { super.parseNode(esTreeNode); } + protected addArgumentToBeDeoptimized(_argument: ExpressionEntity) {} + protected applyDeoptimizations() {} protected abstract getObjectEntity(): ObjectEntity; diff --git a/src/ast/nodes/shared/FunctionNode.ts b/src/ast/nodes/shared/FunctionNode.ts index 809b0a64ccb..3385ef2b37b 100644 --- a/src/ast/nodes/shared/FunctionNode.ts +++ b/src/ast/nodes/shared/FunctionNode.ts @@ -5,6 +5,7 @@ import FunctionScope from '../../scopes/FunctionScope'; import type { ObjectPath, PathTracker } from '../../utils/PathTracker'; import type BlockStatement from '../BlockStatement'; import Identifier, { type IdentifierWithVariable } from '../Identifier'; +import type { ExpressionEntity } from './Expression'; import { UNKNOWN_EXPRESSION } from './Expression'; import FunctionBase from './FunctionBase'; import { type IncludeChildren } from './Node'; @@ -95,6 +96,10 @@ export default class FunctionNode extends FunctionBase { this.id?.declare('function', this); } + protected addArgumentToBeDeoptimized(argument: ExpressionEntity) { + this.scope.argumentsVariable.addArgumentToBeDeoptimized(argument); + } + protected getObjectEntity(): ObjectEntity { if (this.objectEntity !== null) { return this.objectEntity; diff --git a/src/ast/variables/ArgumentsVariable.ts b/src/ast/variables/ArgumentsVariable.ts index eb26d5da75f..1b13d6c9c08 100644 --- a/src/ast/variables/ArgumentsVariable.ts +++ b/src/ast/variables/ArgumentsVariable.ts @@ -1,16 +1,38 @@ import type { AstContext } from '../../Module'; import type { NodeInteraction } from '../NodeInteractions'; import { INTERACTION_ACCESSED } from '../NodeInteractions'; +import type { ExpressionEntity } from '../nodes/shared/Expression'; import { UNKNOWN_EXPRESSION } from '../nodes/shared/Expression'; import type { ObjectPath } from '../utils/PathTracker'; +import { UNKNOWN_PATH } from '../utils/PathTracker'; import LocalVariable from './LocalVariable'; export default class ArgumentsVariable extends LocalVariable { + private deoptimizedArguments: ExpressionEntity[] = []; + constructor(context: AstContext) { super('arguments', null, UNKNOWN_EXPRESSION, context); } + addArgumentToBeDeoptimized(argument: ExpressionEntity): void { + if (this.included) { + argument.deoptimizePath(UNKNOWN_PATH); + } else { + this.deoptimizedArguments.push(argument); + } + } + hasEffectsOnInteractionAtPath(path: ObjectPath, { type }: NodeInteraction): boolean { return type !== INTERACTION_ACCESSED || path.length > 1; } + + include() { + if (!this.included) { + super.include(); + for (const argument of this.deoptimizedArguments) { + argument.deoptimizePath(UNKNOWN_PATH); + } + this.deoptimizedArguments.length = 0; + } + } } diff --git a/test/function/samples/deoptimize-via-arguments/_config.js b/test/function/samples/deoptimize-via-arguments/_config.js new file mode 100644 index 00000000000..c71d20260b1 --- /dev/null +++ b/test/function/samples/deoptimize-via-arguments/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'deoptimize call parameters when arguments variable is used' +}; diff --git a/test/function/samples/deoptimize-via-arguments/main.js b/test/function/samples/deoptimize-via-arguments/main.js new file mode 100644 index 00000000000..1b9072bb086 --- /dev/null +++ b/test/function/samples/deoptimize-via-arguments/main.js @@ -0,0 +1,17 @@ +function mutate(a) { + arguments[0].x = true; + arguments[1].x = true; +} + +var obj1 = { + x: false +}; + +var obj2 = { + x: false +}; + +mutate(obj1, obj2); + +assert.ok(obj1.x ? true : false); +assert.ok(obj2.x ? true : false);