Skip to content

Commit

Permalink
refactor: make logic for cache and update more clear
Browse files Browse the repository at this point in the history
  • Loading branch information
liuly0322 committed Apr 25, 2024
1 parent 142e304 commit a6393a0
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 64 deletions.
52 changes: 2 additions & 50 deletions src/ast/nodes/shared/FunctionBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ import {
} from '../../NodeInteractions';
import type ReturnValueScope from '../../scopes/ReturnValueScope';
import type { ObjectPath, PathTracker } from '../../utils/PathTracker';
import {
EMPTY_PATH,
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH,
UnknownKey
} from '../../utils/PathTracker';
import { UNKNOWN_PATH, UnknownKey } from '../../utils/PathTracker';
import { UNDEFINED_EXPRESSION } from '../../values';
import type ParameterVariable from '../../variables/ParameterVariable';
import type Variable from '../../variables/Variable';
Expand Down Expand Up @@ -69,58 +64,15 @@ export default abstract class FunctionBase extends NodeBase {
this.flags = setFlag(this.flags, Flag.generator, value);
}

private parameterLiteralValues: LiteralValueOrUnknown[] = [];
private updateParameterVariableValues(_arguments: InteractionCalledArguments): void {
for (let position = 0; position < this.params.length; position++) {
const parameter = this.params[position];
// We only consider Identifier for now.
// This excludes parameters with default values and rest parameters.
if (!(parameter instanceof Identifier)) {
continue;
}
const parameterVariable = parameter.variable as ParameterVariable;

if (parameterVariable.isReassigned) {
continue;
}
const knownParameterValue = parameterVariable.getKnownValue();
const argument = _arguments[position + 1] ?? UNDEFINED_EXPRESSION;

// The first call which includes this function
if (knownParameterValue === null) {
parameterVariable.setKnownValue(argument);
this.parameterLiteralValues[position] = argument.getLiteralValueAtPath(
EMPTY_PATH,
SHARED_RECURSION_TRACKER,
parameterVariable
);
continue;
}

// the same literal or identifier, do nothing
if (
knownParameterValue === argument ||
(knownParameterValue instanceof Identifier &&
argument instanceof Identifier &&
knownParameterValue.variable === argument.variable)
) {
continue;
}

const oldValue = this.parameterLiteralValues[position];
if (typeof oldValue === 'symbol') {
parameterVariable.markReassigned();
continue;
}
// add tracking for the new argument
const newValue = argument.getLiteralValueAtPath(
EMPTY_PATH,
SHARED_RECURSION_TRACKER,
parameterVariable
);
if (newValue !== oldValue) {
parameterVariable.markReassigned();
}
parameterVariable.updateKnownValue(argument);
}
}

Expand Down
76 changes: 62 additions & 14 deletions src/ast/variables/ParameterVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { HasEffectsContext } from '../ExecutionContext';
import type { NodeInteraction } from '../NodeInteractions';
import { INTERACTION_ASSIGNED, INTERACTION_CALLED } from '../NodeInteractions';
import type ExportDefaultDeclaration from '../nodes/ExportDefaultDeclaration';
import type Identifier from '../nodes/Identifier';
import Identifier from '../nodes/Identifier';
import type { ExpressionEntity, LiteralValueOrUnknown } from '../nodes/shared/Expression';
import {
deoptimizeInteraction,
Expand All @@ -15,6 +15,7 @@ import {
} from '../nodes/shared/Expression';
import type { ObjectPath, ObjectPathKey } from '../utils/PathTracker';
import {
EMPTY_PATH,
PathTracker,
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH,
Expand Down Expand Up @@ -91,32 +92,78 @@ export default class ParameterVariable extends LocalVariable {
}

private knownValue: ExpressionEntity | null = null;
getKnownValue(): ExpressionEntity | null {
return this.knownValue;
private knownValueLiteral: LiteralValueOrUnknown = UnknownValue;
/**
* Update the known value of the parameter variable.
* Must be called for every function call, so it can track all the arguments,
* and deoptimizeCache itself to mark reassigned if the argument is changed.
* @param argument The argument of the function call
*/
updateKnownValue(argument: ExpressionEntity) {
if (this.isReassigned) {
return;
}

if (this.knownValue === null) {
this.knownValue = argument;
this.knownValueLiteral = argument.getLiteralValueAtPath(
EMPTY_PATH,
SHARED_RECURSION_TRACKER,
this
);
return;
}

// the same literal or identifier, do nothing
if (
this.knownValue === argument ||
(this.knownValue instanceof Identifier &&
argument instanceof Identifier &&
this.knownValue.variable === argument.variable)
) {
return;
}

const oldValue = this.knownValueLiteral;
if (typeof oldValue === 'symbol') {
this.markReassigned();
return;
}
// add tracking for the new argument
const newValue = argument.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this);
if (newValue !== oldValue) {
this.markReassigned();
}
}

private frozenValue: ExpressionEntity | null = null;
/**
* If we are sure about the value of this parameter, we can set it here.
* It can be a literal or the only possible value of the parameter.
* We can and can only set it once before the function body analysis.
* @param value The known value of the parameter to be set.
* This function freezes the known value of the parameter variable,
* so the optimization starts with a certain ExpressionEntity.
* The optimization can be undone by calling `markReassigned`.
* @returns the frozen value
*/
setKnownValue(value: ExpressionEntity): void {
this.knownValue = value;
getKnownValue(): ExpressionEntity {
if (this.frozenValue === null) {
this.frozenValue = this.knownValue || UNKNOWN_EXPRESSION;
}
return this.frozenValue;
}

getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
origin: DeoptimizableEntity
): LiteralValueOrUnknown {
if (this.isReassigned || !this.knownValue) {
if (this.isReassigned) {
return UnknownValue;
}
const knownValue = this.getKnownValue();
this.expressionsUseTheKnownValue.push(origin);
return recursionTracker.withTrackedEntityAtPath(
path,
this.knownValue,
() => this.knownValue!.getLiteralValueAtPath(path, recursionTracker, origin),
knownValue,
() => knownValue.getLiteralValueAtPath(path, recursionTracker, origin),
UnknownValue
);
}
Expand All @@ -126,10 +173,11 @@ export default class ParameterVariable extends LocalVariable {
interaction: NodeInteraction,
context: HasEffectsContext
): boolean {
if (this.isReassigned || !this.knownValue || interaction.type === INTERACTION_ASSIGNED) {
if (this.isReassigned || interaction.type === INTERACTION_ASSIGNED) {
return super.hasEffectsOnInteractionAtPath(path, interaction, context);
}
return this.knownValue.hasEffectsOnInteractionAtPath(path, interaction, context);
const knownValue = this.getKnownValue();
return knownValue.hasEffectsOnInteractionAtPath(path, interaction, context);
}

deoptimizeArgumentsOnInteractionAtPath(interaction: NodeInteraction, path: ObjectPath): void {
Expand Down

0 comments on commit a6393a0

Please sign in to comment.