/
FunctionNode.ts
109 lines (101 loc) · 3.33 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
109
import { BLANK } from '../../../utils/blank';
import { type CallOptions } from '../../CallOptions';
import { type HasEffectsContext, type InclusionContext } from '../../ExecutionContext';
import { EVENT_CALLED, type NodeEvent } from '../../NodeEvents';
import FunctionScope from '../../scopes/FunctionScope';
import { type ObjectPath, PathTracker } from '../../utils/PathTracker';
import BlockStatement from '../BlockStatement';
import { type IdentifierWithVariable } from '../Identifier';
import { type ExpressionEntity, InclusionOptions, 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);
}
deoptimizeThisOnEventAtPath(
event: NodeEvent,
path: ObjectPath,
thisParameter: ExpressionEntity,
recursionTracker: PathTracker
): void {
super.deoptimizeThisOnEventAtPath(event, path, thisParameter, recursionTracker);
if (event === EVENT_CALLED && path.length === 0) {
this.scope.thisVariable.addEntityToBeDeoptimized(thisParameter);
}
}
hasEffects(): boolean {
if (!this.deoptimized) this.applyDeoptimizations();
return !!this.id?.hasEffects();
}
hasEffectsWhenCalledAtPath(
path: ObjectPath,
callOptions: CallOptions,
context: HasEffectsContext
): boolean {
if (super.hasEffectsWhenCalledAtPath(path, callOptions, context)) return true;
const thisInit = context.replacedVariableInits.get(this.scope.thisVariable);
context.replacedVariableInits.set(
this.scope.thisVariable,
callOptions.withNew
? new ObjectEntity(Object.create(null), OBJECT_PROTOTYPE)
: UNKNOWN_EXPRESSION
);
const { brokenFlow, ignore } = context;
context.ignore = {
breaks: false,
continues: false,
labels: new Set(),
returnYield: true
};
if (this.body.hasEffects(context)) return true;
context.brokenFlow = brokenFlow;
if (thisInit) {
context.replacedVariableInits.set(this.scope.thisVariable, thisInit);
} else {
context.replacedVariableInits.delete(this.scope.thisVariable);
}
context.ignore = ignore;
return false;
}
include(
context: InclusionContext,
includeChildrenRecursively: IncludeChildren,
{ includeWithoutParameterDefaults }: InclusionOptions = BLANK
): void {
this.id?.include();
super.include(context, includeChildrenRecursively, {
includeWithoutParameterDefaults:
includeWithoutParameterDefaults && !this.scope.argumentsVariable.included
});
}
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
));
}
}