Skip to content

Commit c0d8383

Browse files
Closure Teamcopybara-github
authored andcommitted
Declare all the computed side effected field keys as separate statements added before the class declaration.
All the static and non-static computed field keys are now in the order of declaration. PiperOrigin-RevId: 566481271
1 parent b90d75a commit c0d8383

File tree

2 files changed

+48
-97
lines changed

2 files changed

+48
-97
lines changed

src/com/google/javascript/jscomp/RewriteClassMembers.java

Lines changed: 40 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
7777
case COMPUTED_FIELD_DEF:
7878
checkState(!classStack.isEmpty());
7979
if (NodeUtil.canBeSideEffected(n.getFirstChild())) {
80-
classStack.peek().computedFieldsWithSideEffectsToMove.push(n);
80+
classStack.peek().computedPropMembers.add(n);
8181
}
8282
classStack.peek().enterField(n);
8383
break;
@@ -99,10 +99,8 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
9999
break;
100100
case COMPUTED_PROP:
101101
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);
106104
}
107105
break;
108106
default:
@@ -141,11 +139,12 @@ public void visit(NodeTraversal t, Node n, Node parent) {
141139

142140
/** Transpile the actual class members themselves */
143141
private void visitClass(NodeTraversal t, Node classNode) {
144-
ClassRecord currClassRecord = classStack.pop();
142+
ClassRecord currClassRecord = classStack.remove();
145143
checkState(currClassRecord.classNode == classNode, "unexpected node: %s", classNode);
146144
if (currClassRecord.cannotConvert) {
147145
return;
148146
}
147+
rewriteSideEffectedComputedProp(t, currClassRecord);
149148
rewriteInstanceMembers(t, currClassRecord);
150149
rewriteStaticMembers(t, currClassRecord);
151150
}
@@ -176,68 +175,6 @@ private void visitSuper(NodeTraversal t, Node n) {
176175
}
177176
}
178177

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-
241178
/**
242179
* Extracts the expression in the LHS of a computed field to not disturb possible side effects and
243180
* 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(
262199
*/
263200
private void extractExpressionFromCompField(
264201
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);
271203

272204
Node compExpression = memberField.getFirstChild().detach();
273205
Node compFieldVar =
274-
astFactory.createSingleVarNameDeclaration(
275-
generateUniqueCompFieldVarName(t), compExpression);
206+
astFactory
207+
.createSingleVarNameDeclaration(generateUniqueCompFieldVarName(t), compExpression)
208+
.srcrefTreeIfMissing(record.classNode);
276209
Node compFieldName = compFieldVar.getFirstChild();
277210
memberField.addChildToFront(compFieldName.cloneNode());
278211
compFieldVar.insertBefore(record.insertionPointBeforeClass);
@@ -284,6 +217,21 @@ private String generateUniqueCompFieldVarName(NodeTraversal t) {
284217
return COMP_FIELD_VAR + compiler.getUniqueIdSupplier().getUniqueId(t.getInput());
285218
}
286219

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+
287235
/** Rewrites and moves all instance fields */
288236
private void rewriteInstanceMembers(NodeTraversal t, ClassRecord record) {
289237
Deque<Node> instanceMembers = record.instanceMembers;
@@ -297,15 +245,11 @@ private void rewriteInstanceMembers(NodeTraversal t, ClassRecord record) {
297245
Node insertionPoint = addTemporaryInsertionPoint(ctorBlock);
298246

299247
while (!instanceMembers.isEmpty()) {
300-
Node instanceMember = instanceMembers.pop();
248+
Node instanceMember = instanceMembers.remove();
301249
checkState(
302250
instanceMember.isMemberFieldDef() || instanceMember.isComputedFieldDef(), instanceMember);
303251

304252
Node thisNode = astFactory.createThisForEs6ClassMember(instanceMember);
305-
if (instanceMember.isComputedFieldDef()
306-
&& NodeUtil.canBeSideEffected(instanceMember.getFirstChild())) {
307-
extractExpressionFromCompField(t, record, instanceMember);
308-
}
309253
Node transpiledNode =
310254
instanceMember.isMemberFieldDef()
311255
? convNonCompFieldToGetProp(thisNode, instanceMember.detach())
@@ -323,9 +267,10 @@ private void rewriteInstanceMembers(NodeTraversal t, ClassRecord record) {
323267
/** Rewrites and moves all static blocks and fields */
324268
private void rewriteStaticMembers(NodeTraversal t, ClassRecord record) {
325269
Deque<Node> staticMembers = record.staticMembers;
270+
Node insertionPoint = addTemporaryInsertionPointAfterNode(record.insertionPointAfterClass);
326271

327272
while (!staticMembers.isEmpty()) {
328-
Node staticMember = staticMembers.pop();
273+
Node staticMember = staticMembers.remove();
329274
// if the name is a property access, we want the whole chain of accesses, while for other
330275
// cases we only want the name node
331276
Node nameToUse = record.createNewNameReferenceNode().srcrefTree(staticMember);
@@ -343,17 +288,15 @@ private void rewriteStaticMembers(NodeTraversal t, ClassRecord record) {
343288
transpiledNode = convNonCompFieldToGetProp(nameToUse, staticMember.detach());
344289
break;
345290
case COMPUTED_FIELD_DEF:
346-
if (NodeUtil.canBeSideEffected(staticMember.getFirstChild())) {
347-
extractExpressionFromCompField(t, record, staticMember);
348-
}
349291
transpiledNode = convCompFieldToGetElem(nameToUse, staticMember.detach());
350292
break;
351293
default:
352294
throw new IllegalStateException(String.valueOf(staticMember));
353295
}
354-
transpiledNode.insertAfter(record.insertionPointAfterClass);
296+
transpiledNode.insertBefore(insertionPoint);
355297
t.reportCodeChange();
356298
}
299+
insertionPoint.detach();
357300
}
358301

359302
/**
@@ -413,6 +356,12 @@ private Node addTemporaryInsertionPoint(Node ctorBlock) {
413356
return tempNode;
414357
}
415358

359+
private Node addTemporaryInsertionPointAfterNode(Node node) {
360+
Node tempNode = IR.empty();
361+
tempNode.insertAfter(node);
362+
return tempNode;
363+
}
364+
416365
/**
417366
* Gets the location of the statement declaring the class
418367
*
@@ -455,8 +404,8 @@ private static final class ClassRecord {
455404
final Deque<Node> instanceMembers = new ArrayDeque<>();
456405
// Static fields + static blocks
457406
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<>();
460409

461410
// Set of all the Vars defined in the constructor arguments scope and constructor body scope
462411
ImmutableSet<Var> constructorVars = ImmutableSet.of();
@@ -480,15 +429,15 @@ private static final class ClassRecord {
480429
void enterField(Node field) {
481430
checkArgument(field.isComputedFieldDef() || field.isMemberFieldDef());
482431
if (field.isStaticMember()) {
483-
staticMembers.push(field);
432+
staticMembers.add(field);
484433
} else {
485-
instanceMembers.offer(field);
434+
instanceMembers.add(field);
486435
}
487436
}
488437

489438
void recordStaticBlock(Node block) {
490439
checkArgument(NodeUtil.isClassStaticBlock(block));
491-
staticMembers.push(block);
440+
staticMembers.add(block);
492441
}
493442

494443
void recordConstructorScope(Scope s) {

test/com/google/javascript/jscomp/RewriteClassMembersTest.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -582,16 +582,18 @@ public void testSideEffectsInComputedField() {
582582
expected(
583583
lines(
584584
"function foo(num) {}",
585-
"var COMPFIELD$0;",
586-
"var COMPFIELD$1;",
587-
"var COMPFIELD$2;",
585+
"var COMPFIELD$0 = 'f' + foo(1);",
586+
"var COMPFIELD$1 = 'm' + foo(2);",
587+
"var COMPFIELD$2 = foo(3);",
588+
"var COMPFIELD$3 = foo(4);",
589+
"var COMPFIELD$4 = foo(5);",
588590
"class Baz {",
589591
" constructor() {",
590592
" this[COMPFIELD$0];",
591-
" this[COMPFIELD$1] = 2;",
593+
" this[COMPFIELD$3] = 2;",
592594
" }",
593-
" [(COMPFIELD$0 = 'f' + foo(1), 'm' + foo(2))]() {}",
594-
" get [(COMPFIELD$2 = foo(3), (COMPFIELD$1 = foo(4), foo(5)))]() {}",
595+
" [COMPFIELD$1]() {}",
596+
" get [COMPFIELD$4]() {}",
595597
"}",
596598
"Baz.x = foo(6);",
597599
"Baz[COMPFIELD$2] = foo(7);")));

0 commit comments

Comments
 (0)