/
FunctionNode.ts
108 lines (100 loc) · 3.48 KB
/
FunctionNode.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { type HasEffectsContext, type InclusionContext } from '../../ExecutionContext';
import type { NodeInteraction, NodeInteractionWithThisArgument } from '../../NodeInteractions';
import { INTERACTION_CALLED } from '../../NodeInteractions';
import FunctionScope from '../../scopes/FunctionScope';
import type { ObjectPath, PathTracker } from '../../utils/PathTracker';
import type BlockStatement from '../BlockStatement';
import Identifier, { type IdentifierWithVariable } from '../Identifier';
import { UNKNOWN_EXPRESSION } from './Expression';
import FunctionBase from './FunctionBase';
import { type IncludeChildren } from './Node';
import { ObjectEntity } from './ObjectEntity';
import { OBJECT_PROTOTYPE } from './ObjectPrototype';
import type { PatternNode } from './Pattern';
export default class FunctionNode extends FunctionBase {
declare async: boolean;
declare body: BlockStatement;
declare id: IdentifierWithVariable | null;
declare params: readonly PatternNode[];
declare preventChildBlockScope: true;
declare scope: FunctionScope;
protected objectEntity: ObjectEntity | null = null;
createScope(parentScope: FunctionScope): void {
this.scope = new FunctionScope(parentScope, this.context);
}
deoptimizeThisOnInteractionAtPath(
interaction: NodeInteractionWithThisArgument,
path: ObjectPath,
recursionTracker: PathTracker
): void {
super.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
if (interaction.type === INTERACTION_CALLED && path.length === 0) {
this.scope.thisVariable.addEntityToBeDeoptimized(interaction.thisArg);
}
}
hasEffects(context: HasEffectsContext): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
return !!this.id?.hasEffects(context);
}
hasEffectsOnInteractionAtPath(
path: ObjectPath,
interaction: NodeInteraction,
context: HasEffectsContext
): boolean {
if (super.hasEffectsOnInteractionAtPath(path, interaction, context)) return true;
if (interaction.type === INTERACTION_CALLED) {
const thisInit = context.replacedVariableInits.get(this.scope.thisVariable);
context.replacedVariableInits.set(
this.scope.thisVariable,
interaction.withNew
? new ObjectEntity(Object.create(null), OBJECT_PROTOTYPE)
: UNKNOWN_EXPRESSION
);
const { brokenFlow, ignore, replacedVariableInits } = context;
context.ignore = {
breaks: false,
continues: false,
labels: new Set(),
returnYield: true
};
if (this.body.hasEffects(context)) return true;
context.brokenFlow = brokenFlow;
if (thisInit) {
replacedVariableInits.set(this.scope.thisVariable, thisInit);
} else {
replacedVariableInits.delete(this.scope.thisVariable);
}
context.ignore = ignore;
}
return false;
}
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren): void {
super.include(context, includeChildrenRecursively);
this.id?.include();
const hasArguments = this.scope.argumentsVariable.included;
for (const parameter of this.params) {
if (!(parameter instanceof Identifier) || hasArguments) {
parameter.include(context, includeChildrenRecursively);
}
}
}
initialise(): void {
super.initialise();
this.id?.declare('function', this);
}
protected getObjectEntity(): ObjectEntity {
if (this.objectEntity !== null) {
return this.objectEntity;
}
return (this.objectEntity = new ObjectEntity(
[
{
key: 'prototype',
kind: 'init',
property: new ObjectEntity([], OBJECT_PROTOTYPE)
}
],
OBJECT_PROTOTYPE
));
}
}