@@ -77,7 +77,7 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
77
77
case COMPUTED_FIELD_DEF :
78
78
checkState (!classStack .isEmpty ());
79
79
if (NodeUtil .canBeSideEffected (n .getFirstChild ())) {
80
- classStack .peek ().computedFieldsWithSideEffectsToMove . push (n );
80
+ classStack .peek ().computedPropMembers . add (n );
81
81
}
82
82
classStack .peek ().enterField (n );
83
83
break ;
@@ -99,10 +99,8 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
99
99
break ;
100
100
case COMPUTED_PROP :
101
101
checkState (!classStack .isEmpty ());
102
- ClassRecord record = classStack .peek ();
103
- if (!record .computedFieldsWithSideEffectsToMove .isEmpty ()) {
104
- moveComputedFieldsWithSideEffectsInsideComputedFunction (t , record , n );
105
- record .computedFieldsWithSideEffectsToMove .clear ();
102
+ if (NodeUtil .canBeSideEffected (n .getFirstChild ())) {
103
+ classStack .peek ().computedPropMembers .add (n );
106
104
}
107
105
break ;
108
106
default :
@@ -141,11 +139,12 @@ public void visit(NodeTraversal t, Node n, Node parent) {
141
139
142
140
/** Transpile the actual class members themselves */
143
141
private void visitClass (NodeTraversal t , Node classNode ) {
144
- ClassRecord currClassRecord = classStack .pop ();
142
+ ClassRecord currClassRecord = classStack .remove ();
145
143
checkState (currClassRecord .classNode == classNode , "unexpected node: %s" , classNode );
146
144
if (currClassRecord .cannotConvert ) {
147
145
return ;
148
146
}
147
+ rewriteSideEffectedComputedProp (t , currClassRecord );
149
148
rewriteInstanceMembers (t , currClassRecord );
150
149
rewriteStaticMembers (t , currClassRecord );
151
150
}
@@ -176,68 +175,6 @@ private void visitSuper(NodeTraversal t, Node n) {
176
175
}
177
176
}
178
177
179
- /**
180
- * Move computed fields with side effects into a computed function (Token is COMPUTED_PROP) to
181
- * preserve the original behavior of possible side effects such as printing something in the call
182
- * to a function in the computed field and function.
183
- *
184
- * <p>The computed fields that get moved into a computed function must be located above the
185
- * computed function and below any other computed functions in the class. Any computed field not
186
- * above a computed function gets extracted by {@link #extractExpressionFromCompField}.
187
- *
188
- * <p>E.g.
189
- *
190
- * <pre>
191
- * function bar(num) {}
192
- * class Foo {
193
- * [bar(1)] = 9;
194
- * [bar(2)]() {}
195
- * }
196
- * </pre>
197
- *
198
- * becomes
199
- *
200
- * <pre>
201
- * function bar(num) {}
202
- * var $jscomp$compfield$0;
203
- * class Foo {
204
- * [$jscomp$compfield$0] = 9;
205
- * [($jscomp$compfield$0 = bar(1), bar(2))]() {}
206
- * }
207
- * </pre>
208
- */
209
- private void moveComputedFieldsWithSideEffectsInsideComputedFunction (
210
- NodeTraversal t , ClassRecord record , Node computedFunction ) {
211
- checkArgument (computedFunction .isComputedProp (), computedFunction );
212
- Deque <Node > computedFields = record .computedFieldsWithSideEffectsToMove ;
213
- while (!computedFields .isEmpty ()) {
214
- Node computedField = computedFields .pop ();
215
- Node computedFieldVar =
216
- astFactory
217
- .createSingleVarNameDeclaration (generateUniqueCompFieldVarName (t ))
218
- .srcrefTreeIfMissing (record .classNode );
219
- // `var $jscomp$compfield$0` is inserted before the class
220
- computedFieldVar .insertBefore (record .insertionPointBeforeClass );
221
- Node newNameNode = computedFieldVar .getFirstChild ();
222
- Node fieldLhsExpression = computedField .getFirstChild ();
223
- // Computed field changes from `[fieldLhs] = rhs;` to `[$jscomp$compfield$0] = rhs;`
224
- fieldLhsExpression .replaceWith (newNameNode .cloneNode ());
225
- Node assignComputedLhsExpressionToNewVarName =
226
- astFactory
227
- .createAssign (newNameNode .cloneNode (), fieldLhsExpression )
228
- .srcrefTreeIfMissing (computedField );
229
- Node existingComputedFunctionExpression = computedFunction .getFirstChild ();
230
- Node newComma =
231
- astFactory
232
- .createComma (
233
- assignComputedLhsExpressionToNewVarName ,
234
- existingComputedFunctionExpression .detach ())
235
- .srcrefTreeIfMissing (computedFunction );
236
- // `[funcExpr]() {}` becomes `[($jscomp$compfield$0 = fieldLhs, funcExpr)]() {}`
237
- computedFunction .addChildToFront (newComma );
238
- }
239
- }
240
-
241
178
/**
242
179
* Extracts the expression in the LHS of a computed field to not disturb possible side effects and
243
180
* allow for this and super to be used in the LHS of a computed field in certain cases. Does not
@@ -262,17 +199,13 @@ private void moveComputedFieldsWithSideEffectsInsideComputedFunction(
262
199
*/
263
200
private void extractExpressionFromCompField (
264
201
NodeTraversal t , ClassRecord record , Node memberField ) {
265
- checkArgument (memberField .isComputedFieldDef (), memberField );
266
- // Computed field was already moved into a computed function and doesn't need to be extracted
267
- if (memberField .getFirstChild ().isName ()
268
- && memberField .getFirstChild ().getString ().startsWith (COMP_FIELD_VAR )) {
269
- return ;
270
- }
202
+ checkArgument (memberField .isComputedFieldDef () || memberField .isComputedProp (), memberField );
271
203
272
204
Node compExpression = memberField .getFirstChild ().detach ();
273
205
Node compFieldVar =
274
- astFactory .createSingleVarNameDeclaration (
275
- generateUniqueCompFieldVarName (t ), compExpression );
206
+ astFactory
207
+ .createSingleVarNameDeclaration (generateUniqueCompFieldVarName (t ), compExpression )
208
+ .srcrefTreeIfMissing (record .classNode );
276
209
Node compFieldName = compFieldVar .getFirstChild ();
277
210
memberField .addChildToFront (compFieldName .cloneNode ());
278
211
compFieldVar .insertBefore (record .insertionPointBeforeClass );
@@ -284,6 +217,21 @@ private String generateUniqueCompFieldVarName(NodeTraversal t) {
284
217
return COMP_FIELD_VAR + compiler .getUniqueIdSupplier ().getUniqueId (t .getInput ());
285
218
}
286
219
220
+ /** Rewrites and moves all side effected computed field keys to the top */
221
+ private void rewriteSideEffectedComputedProp (NodeTraversal t , ClassRecord record ) {
222
+ Deque <Node > computedPropMembers = record .computedPropMembers ;
223
+
224
+ if (computedPropMembers .isEmpty ()) {
225
+ return ;
226
+ }
227
+
228
+ while (!computedPropMembers .isEmpty ()) {
229
+ Node computedPropMember = computedPropMembers .remove ();
230
+ extractExpressionFromCompField (t , record , computedPropMember );
231
+ }
232
+ t .reportCodeChange ();
233
+ }
234
+
287
235
/** Rewrites and moves all instance fields */
288
236
private void rewriteInstanceMembers (NodeTraversal t , ClassRecord record ) {
289
237
Deque <Node > instanceMembers = record .instanceMembers ;
@@ -297,15 +245,11 @@ private void rewriteInstanceMembers(NodeTraversal t, ClassRecord record) {
297
245
Node insertionPoint = addTemporaryInsertionPoint (ctorBlock );
298
246
299
247
while (!instanceMembers .isEmpty ()) {
300
- Node instanceMember = instanceMembers .pop ();
248
+ Node instanceMember = instanceMembers .remove ();
301
249
checkState (
302
250
instanceMember .isMemberFieldDef () || instanceMember .isComputedFieldDef (), instanceMember );
303
251
304
252
Node thisNode = astFactory .createThisForEs6ClassMember (instanceMember );
305
- if (instanceMember .isComputedFieldDef ()
306
- && NodeUtil .canBeSideEffected (instanceMember .getFirstChild ())) {
307
- extractExpressionFromCompField (t , record , instanceMember );
308
- }
309
253
Node transpiledNode =
310
254
instanceMember .isMemberFieldDef ()
311
255
? convNonCompFieldToGetProp (thisNode , instanceMember .detach ())
@@ -323,9 +267,10 @@ private void rewriteInstanceMembers(NodeTraversal t, ClassRecord record) {
323
267
/** Rewrites and moves all static blocks and fields */
324
268
private void rewriteStaticMembers (NodeTraversal t , ClassRecord record ) {
325
269
Deque <Node > staticMembers = record .staticMembers ;
270
+ Node insertionPoint = addTemporaryInsertionPointAfterNode (record .insertionPointAfterClass );
326
271
327
272
while (!staticMembers .isEmpty ()) {
328
- Node staticMember = staticMembers .pop ();
273
+ Node staticMember = staticMembers .remove ();
329
274
// if the name is a property access, we want the whole chain of accesses, while for other
330
275
// cases we only want the name node
331
276
Node nameToUse = record .createNewNameReferenceNode ().srcrefTree (staticMember );
@@ -343,17 +288,15 @@ private void rewriteStaticMembers(NodeTraversal t, ClassRecord record) {
343
288
transpiledNode = convNonCompFieldToGetProp (nameToUse , staticMember .detach ());
344
289
break ;
345
290
case COMPUTED_FIELD_DEF :
346
- if (NodeUtil .canBeSideEffected (staticMember .getFirstChild ())) {
347
- extractExpressionFromCompField (t , record , staticMember );
348
- }
349
291
transpiledNode = convCompFieldToGetElem (nameToUse , staticMember .detach ());
350
292
break ;
351
293
default :
352
294
throw new IllegalStateException (String .valueOf (staticMember ));
353
295
}
354
- transpiledNode .insertAfter ( record . insertionPointAfterClass );
296
+ transpiledNode .insertBefore ( insertionPoint );
355
297
t .reportCodeChange ();
356
298
}
299
+ insertionPoint .detach ();
357
300
}
358
301
359
302
/**
@@ -413,6 +356,12 @@ private Node addTemporaryInsertionPoint(Node ctorBlock) {
413
356
return tempNode ;
414
357
}
415
358
359
+ private Node addTemporaryInsertionPointAfterNode (Node node ) {
360
+ Node tempNode = IR .empty ();
361
+ tempNode .insertAfter (node );
362
+ return tempNode ;
363
+ }
364
+
416
365
/**
417
366
* Gets the location of the statement declaring the class
418
367
*
@@ -455,8 +404,8 @@ private static final class ClassRecord {
455
404
final Deque <Node > instanceMembers = new ArrayDeque <>();
456
405
// Static fields + static blocks
457
406
final Deque <Node > staticMembers = new ArrayDeque <>();
458
- // Computed fields with side effects after the most recent computed member function
459
- final Deque <Node > computedFieldsWithSideEffectsToMove = new ArrayDeque <>();
407
+ // computed props with side effects
408
+ final Deque <Node > computedPropMembers = new ArrayDeque <>();
460
409
461
410
// Set of all the Vars defined in the constructor arguments scope and constructor body scope
462
411
ImmutableSet <Var > constructorVars = ImmutableSet .of ();
@@ -480,15 +429,15 @@ private static final class ClassRecord {
480
429
void enterField (Node field ) {
481
430
checkArgument (field .isComputedFieldDef () || field .isMemberFieldDef ());
482
431
if (field .isStaticMember ()) {
483
- staticMembers .push (field );
432
+ staticMembers .add (field );
484
433
} else {
485
- instanceMembers .offer (field );
434
+ instanceMembers .add (field );
486
435
}
487
436
}
488
437
489
438
void recordStaticBlock (Node block ) {
490
439
checkArgument (NodeUtil .isClassStaticBlock (block ));
491
- staticMembers .push (block );
440
+ staticMembers .add (block );
492
441
}
493
442
494
443
void recordConstructorScope (Scope s ) {
0 commit comments