/
ParameterScope.ts
94 lines (88 loc) · 3.15 KB
/
ParameterScope.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
import { logDuplicateArgumentNameError } from '../../utils/logs';
import type { InclusionContext } from '../ExecutionContext';
import type Identifier from '../nodes/Identifier';
import SpreadElement from '../nodes/SpreadElement';
import type { ExpressionEntity } from '../nodes/shared/Expression';
import ParameterVariable from '../variables/ParameterVariable';
import CatchBodyScope from './CatchBodyScope';
import ChildScope from './ChildScope';
import FunctionBodyScope from './FunctionBodyScope';
export default class ParameterScope extends ChildScope {
readonly bodyScope: ChildScope;
parameters: readonly ParameterVariable[][] = [];
private hasRest = false;
constructor(parent: ChildScope, isCatchScope: boolean) {
super(parent, parent.context);
this.bodyScope = isCatchScope ? new CatchBodyScope(this) : new FunctionBodyScope(this);
}
/**
* Adds a parameter to this scope. Parameters must be added in the correct
* order, i.e. from left to right.
*/
addParameterDeclaration(identifier: Identifier): ParameterVariable {
const { name, start } = identifier;
const existingParameter = this.variables.get(name);
if (existingParameter) {
return this.context.error(logDuplicateArgumentNameError(name), start);
}
const variable = new ParameterVariable(name, identifier, this.context);
this.variables.set(name, variable);
// We also add it to the body scope to detect name conflicts with local
// variables. We still need the intermediate scope, though, as parameter
// defaults are NOT taken from the body scope but from the parameters or
// outside scope.
this.bodyScope.addHoistedVariable(name, variable);
return variable;
}
addParameterVariables(parameters: ParameterVariable[][], hasRest: boolean): void {
this.parameters = parameters;
for (const parameterList of parameters) {
for (const parameter of parameterList) {
parameter.alwaysRendered = true;
}
}
this.hasRest = hasRest;
}
includeCallArguments(
context: InclusionContext,
parameters: readonly (ExpressionEntity | SpreadElement)[]
): void {
let calledFromTryStatement = false;
let argumentIncluded = false;
const restParameter = this.hasRest && this.parameters[this.parameters.length - 1];
for (const checkedArgument of parameters) {
if (checkedArgument instanceof SpreadElement) {
for (const argument of parameters) {
argument.include(context, false);
}
break;
}
}
for (let index = parameters.length - 1; index >= 0; index--) {
const parameterVariables = this.parameters[index] || restParameter;
const argument = parameters[index];
if (parameterVariables) {
calledFromTryStatement = false;
if (parameterVariables.length === 0) {
// handle empty destructuring
argumentIncluded = true;
} else {
for (const variable of parameterVariables) {
if (variable.included) {
argumentIncluded = true;
}
if (variable.calledFromTryStatement) {
calledFromTryStatement = true;
}
}
}
}
if (!argumentIncluded && argument.shouldBeIncluded(context)) {
argumentIncluded = true;
}
if (argumentIncluded) {
argument.include(context, calledFromTryStatement);
}
}
}
}