5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {CompilerError} from '..';
8
import {
10
- DeclarationId,
9
+ convertHoistedLValueKind,
10
+ IdentifierId,
11
InstructionKind,
12
ReactiveFunction,
13
ReactiveInstruction,
14
+ ReactiveScopeBlock,
15
ReactiveStatement,
16
} from '../HIR';
17
+import {empty, Stack} from '../Utils/Stack';
18
import {
19
ReactiveFunctionTransform,
20
Transformed,
26
* original instruction kind.
27
*/
28
export function pruneHoistedContexts(fn: ReactiveFunction): void {
27
- const hoistedIdentifiers: HoistedIdentifiers = new Map();
28
- visitReactiveFunction(fn, new Visitor(), hoistedIdentifiers);
29
+ visitReactiveFunction(fn, new Visitor(), {
30
+ activeScopes: empty(),
31
+ });
32
}
33
31
-const REWRITTEN_HOISTED_CONST: unique symbol = Symbol(
32
- 'REWRITTEN_HOISTED_CONST',
33
-);
34
-const REWRITTEN_HOISTED_LET: unique symbol = Symbol('REWRITTEN_HOISTED_LET');
34
+type VisitorState = {
35
+ activeScopes: Stack<Set<IdentifierId>>;
36
+};
37
36
-type HoistedIdentifiers = Map<
37
- DeclarationId,
38
- | InstructionKind
39
- | typeof REWRITTEN_HOISTED_CONST
40
- | typeof REWRITTEN_HOISTED_LET
41
->;
42
-
43
-class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
38
+class Visitor extends ReactiveFunctionTransform<VisitorState> {
39
+ override visitScope(scope: ReactiveScopeBlock, state: VisitorState): void {
40
+ state.activeScopes = state.activeScopes.push(
41
+ new Set(scope.scope.declarations.keys()),
42
+ );
43
+ this.traverseScope(scope, state);
44
+ state.activeScopes.pop();
45
+ }
46
override transformInstruction(
47
instruction: ReactiveInstruction,
46
- state: HoistedIdentifiers,
48
+ state: VisitorState,
49
): Transformed<ReactiveStatement> {
50
this.visitInstruction(instruction, state);
51
52
/**
53
* Remove hoisted declarations to preserve TDZ
54
*/
53
- if (
54
- instruction.value.kind === 'DeclareContext' &&
55
- instruction.value.lvalue.kind === 'HoistedConst'
56
- ) {
57
- state.set(
58
- instruction.value.lvalue.place.identifier.declarationId,
59
- InstructionKind.Const,
60
- );
61
- return {kind: 'remove'};
62
- }
63
-
64
- if (
65
- instruction.value.kind === 'DeclareContext' &&
66
- instruction.value.lvalue.kind === 'HoistedLet'
67
- ) {
68
- state.set(
69
- instruction.value.lvalue.place.identifier.declarationId,
70
- InstructionKind.Let,
55
+ if (instruction.value.kind === 'DeclareContext') {
56
+ const maybeNonHoisted = convertHoistedLValueKind(
57
+ instruction.value.lvalue.kind,
58
);
72
- return {kind: 'remove'};
59
+ if (maybeNonHoisted != null) {
60
+ return {kind: 'remove'};
61
+ }
62
}
74
-
63
if (
76
- instruction.value.kind === 'DeclareContext' &&
77
- instruction.value.lvalue.kind === 'HoistedFunction'
64
+ instruction.value.kind === 'StoreContext' &&
65
+ instruction.value.lvalue.kind !== InstructionKind.Reassign
66
) {
79
- state.set(
80
- instruction.value.lvalue.place.identifier.declarationId,
81
- InstructionKind.Function,
82
- );
83
- return {kind: 'remove'};
84
- }
85
-
86
- if (instruction.value.kind === 'StoreContext') {
87
- const kind = state.get(
88
- instruction.value.lvalue.place.identifier.declarationId,
67
+ /**
68
+ * Rewrite StoreContexts let/const/functions that will be pre-declared in
69
+ * codegen to reassignments.
70
+ */
71
+ const lvalueId = instruction.value.lvalue.place.identifier.id;
72
+ const isDeclaredByScope = state.activeScopes.find(scope =>
73
+ scope.has(lvalueId),
74
);
90
- if (kind != null) {
91
- CompilerError.invariant(kind !== REWRITTEN_HOISTED_CONST, {
92
- reason: 'Expected exactly one store to a hoisted const variable',
93
- loc: instruction.loc,
94
- });
95
- if (
96
- kind === InstructionKind.Const ||
97
- kind === InstructionKind.Function
98
- ) {
99
- state.set(
100
- instruction.value.lvalue.place.identifier.declarationId,
101
- REWRITTEN_HOISTED_CONST,
102
- );
103
- return {
104
- kind: 'replace',
105
- value: {
106
- kind: 'instruction',
107
- instruction: {
108
- ...instruction,
109
- value: {
110
- ...instruction.value,
111
- lvalue: {
112
- ...instruction.value.lvalue,
113
- kind,
114
- },
115
- type: null,
116
- kind: 'StoreLocal',
117
- },
118
- },
119
- },
120
- };
121
- } else if (kind !== REWRITTEN_HOISTED_LET) {
122
- /**
123
- * Context variables declared with let may have reassignments. Only
124
- * insert a `DeclareContext` for the first encountered `StoreContext`
125
- * instruction.
126
- */
127
- state.set(
128
- instruction.value.lvalue.place.identifier.declarationId,
129
- REWRITTEN_HOISTED_LET,
130
- );
131
- return {
132
- kind: 'replace-many',
133
- value: [
134
- {
135
- kind: 'instruction',
136
- instruction: {
137
- id: instruction.id,
138
- lvalue: null,
139
- value: {
140
- kind: 'DeclareContext',
141
- lvalue: {
142
- kind: InstructionKind.Let,
143
- place: {...instruction.value.lvalue.place},
144
- },
145
- loc: instruction.value.loc,
146
- },
147
- loc: instruction.loc,
148
- },
149
- },
150
- {kind: 'instruction', instruction},
151
- ],
152
- };
153
- }
75
+ if (isDeclaredByScope) {
76
+ instruction.value.lvalue.kind = InstructionKind.Reassign;
77
}
78
}
79