/
ArrowFunctionExpression.ts
116 lines (102 loc) · 3.52 KB
/
ArrowFunctionExpression.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
110
111
112
113
114
115
116
import { CallOptions } from '../CallOptions';
import { BROKEN_FLOW_NONE, HasEffectsContext, InclusionContext } from '../ExecutionContext';
import ReturnValueScope from '../scopes/ReturnValueScope';
import Scope from '../scopes/Scope';
import { ObjectPath, UnknownKey, UNKNOWN_PATH } from '../utils/PathTracker';
import BlockStatement from './BlockStatement';
import Identifier from './Identifier';
import * as NodeType from './NodeType';
import RestElement from './RestElement';
import { UNKNOWN_EXPRESSION } from './shared/Expression';
import { ExpressionNode, GenericEsTreeNode, IncludeChildren, NodeBase } from './shared/Node';
import { PatternNode } from './shared/Pattern';
import SpreadElement from './SpreadElement';
export default class ArrowFunctionExpression extends NodeBase {
body!: BlockStatement | ExpressionNode;
params!: PatternNode[];
preventChildBlockScope!: true;
scope!: ReturnValueScope;
type!: NodeType.tArrowFunctionExpression;
createScope(parentScope: Scope) {
this.scope = new ReturnValueScope(parentScope, this.context);
}
deoptimizePath(path: ObjectPath) {
// A reassignment of UNKNOWN_PATH is considered equivalent to having lost track
// which means the return expression needs to be reassigned
if (path.length === 1 && path[0] === UnknownKey) {
this.scope.getReturnExpression().deoptimizePath(UNKNOWN_PATH);
}
}
// Arrow functions do not mutate their context
deoptimizeThisOnEventAtPath() {}
getReturnExpressionWhenCalledAtPath(path: ObjectPath) {
return path.length === 0 ? this.scope.getReturnExpression() : UNKNOWN_EXPRESSION;
}
hasEffects() {
return false;
}
hasEffectsWhenAccessedAtPath(path: ObjectPath) {
return path.length > 1;
}
hasEffectsWhenAssignedAtPath(path: ObjectPath) {
return path.length > 1;
}
hasEffectsWhenCalledAtPath(
path: ObjectPath,
_callOptions: CallOptions,
context: HasEffectsContext
): boolean {
if (path.length > 0) return true;
for (const param of this.params) {
if (param.hasEffects(context)) return true;
}
const { ignore, brokenFlow } = context;
context.ignore = {
breaks: false,
continues: false,
labels: new Set(),
returnAwaitYield: true
};
if (this.body.hasEffects(context)) return true;
context.ignore = ignore;
context.brokenFlow = brokenFlow;
return false;
}
include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) {
this.included = true;
for (const param of this.params) {
if (!(param instanceof Identifier)) {
param.include(context, includeChildrenRecursively);
}
}
const { brokenFlow } = context;
context.brokenFlow = BROKEN_FLOW_NONE;
this.body.include(context, includeChildrenRecursively);
context.brokenFlow = brokenFlow;
}
includeCallArguments(context: InclusionContext, args: (ExpressionNode | SpreadElement)[]): void {
this.scope.includeCallArguments(context, args);
}
initialise() {
this.scope.addParameterVariables(
this.params.map(param => param.declare('parameter', UNKNOWN_EXPRESSION)),
this.params[this.params.length - 1] instanceof RestElement
);
if (this.body instanceof BlockStatement) {
this.body.addImplicitReturnExpressionToScope();
} else {
this.scope.addReturnExpression(this.body);
}
}
parseNode(esTreeNode: GenericEsTreeNode) {
if (esTreeNode.body.type === NodeType.BlockStatement) {
this.body = new this.context.nodeConstructors.BlockStatement(
esTreeNode.body,
this,
this.scope.hoistedBodyVarScope
);
}
super.parseNode(esTreeNode);
}
}
ArrowFunctionExpression.prototype.preventChildBlockScope = true;