-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
evaluate-arrow-function-expression.ts
122 lines (99 loc) · 4.57 KB
/
evaluate-arrow-function-expression.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
117
118
119
120
121
122
import type {EvaluatorOptions} from "./evaluator-options.js";
import type {LexicalEnvironment} from "../lexical-environment/lexical-environment.js";
import {getFromLexicalEnvironment, pathInLexicalEnvironmentEquals, setInLexicalEnvironment} from "../lexical-environment/lexical-environment.js";
import {cloneLexicalEnvironment} from "../lexical-environment/clone-lexical-environment.js";
import type {Literal} from "../literal/literal.js";
import {evaluateParameterDeclarations} from "./evaluate-parameter-declarations.js";
import {RETURN_SYMBOL} from "../util/return/return-symbol.js";
import {hasModifier} from "../util/modifier/has-modifier.js";
import type {TS} from "../../type/ts.js";
/**
* Evaluates, or attempts to evaluate, an ArrowFunction
*/
export function evaluateArrowFunctionExpression(options: EvaluatorOptions<TS.ArrowFunction>): Literal {
const {node, environment, evaluate, stack, typescript, getCurrentError} = options;
const arrowFunctionExpression = hasModifier(node, typescript.SyntaxKind.AsyncKeyword)
? async (...args: Literal[]) => {
// Prepare a lexical environment for the function context
const localLexicalEnvironment: LexicalEnvironment = cloneLexicalEnvironment(environment, node);
const nextOptions = {...options, environment: localLexicalEnvironment};
// Define a new binding for a return symbol within the environment
setInLexicalEnvironment({...nextOptions, path: RETURN_SYMBOL, value: false, newBinding: true});
// Define a new binding for the arguments given to the function
// eslint-disable-next-line prefer-rest-params
setInLexicalEnvironment({...nextOptions, path: "arguments", value: arguments, newBinding: true});
// Evaluate the parameters based on the given arguments
evaluateParameterDeclarations(
{
...options,
node: node.parameters,
environment: localLexicalEnvironment
},
args
);
if (getCurrentError() != null) {
return;
}
// If the body is a block, evaluate it as a statement
if (typescript.isBlock(node.body)) {
evaluate.statement(node.body, nextOptions);
if (getCurrentError() != null) {
return;
}
// If a 'return' has occurred within the block, pop the Stack and return that value
if (pathInLexicalEnvironmentEquals(node, localLexicalEnvironment, true, RETURN_SYMBOL)) {
return stack.pop();
}
// Otherwise, return 'undefined'. Nothing is returned from the function
else return undefined;
}
// Otherwise, the body is itself an expression
else {
return evaluate.expression(node.body, nextOptions);
}
}
: (...args: Literal[]) => {
// Prepare a lexical environment for the function context
const localLexicalEnvironment: LexicalEnvironment = cloneLexicalEnvironment(environment, node);
const nextOptions = {...options, environment: localLexicalEnvironment};
// Define a new binding for a return symbol within the environment
setInLexicalEnvironment({...nextOptions, path: RETURN_SYMBOL, value: false, newBinding: true});
// Define a new binding for the arguments given to the function
// eslint-disable-next-line prefer-rest-params
setInLexicalEnvironment({...nextOptions, path: "arguments", value: arguments, newBinding: true});
// Evaluate the parameters based on the given arguments
evaluateParameterDeclarations(
{
...options,
node: node.parameters,
environment: localLexicalEnvironment
},
args
);
if (getCurrentError() != null) {
return;
}
// If the body is a block, evaluate it as a statement
if (typescript.isBlock(node.body)) {
evaluate.statement(node.body, nextOptions);
if (getCurrentError() != null) {
return;
}
// If a 'return' has occurred within the block, pop the Stack and return that value
if (pathInLexicalEnvironmentEquals(node, localLexicalEnvironment, true, RETURN_SYMBOL)) {
return stack.pop();
}
// Otherwise, return 'undefined'. Nothing is returned from the function
else return undefined;
}
// Otherwise, the body is itself an expression
else {
return evaluate.expression(node.body, nextOptions);
}
};
arrowFunctionExpression.toString = () => `[Function: anonymous]`;
// Make sure to use the Function that is contained within the Realm. Otherwise, 'instanceof' checks may fail
// since this particular function comes from the executing context.
Object.setPrototypeOf(arrowFunctionExpression, getFromLexicalEnvironment(node, environment, "Function")!.literal as CallableFunction);
return arrowFunctionExpression;
}