Skip to content

Commit d4da7b3

Browse files
brad4dcopybara-github
authored andcommitted
InlineAndCollapseProperties fix for new TS 5.2 emit shape
TS 5.2. emits code like this: ``` // Alias variable created without being initialized. var alias; var C = class { // references to alias in here, because TS doesn't like using // the class name. }; alias = C; C.staticProperty = someValue; ``` This code shape confused `InlineAndCollapseProperties` causing it both to back off from some inlining (bigger output code) and fail to back off similarly for collapsing (broken output code). PiperOrigin-RevId: 564552797
1 parent 35345da commit d4da7b3

File tree

4 files changed

+63
-31
lines changed

4 files changed

+63
-31
lines changed

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

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ private boolean declarationHasFollowingObjectSpreadSibling(Node declaration) {
410410
private class BuildGlobalNamespace extends NodeTraversal.AbstractPreOrderCallback {
411411
private @Nullable Node curModuleRoot = null;
412412
private @Nullable ModuleMetadata curMetadata = null;
413+
413414
/** Collect the references in pre-order. */
414415
@Override
415416
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
@@ -1216,9 +1217,13 @@ final class Name implements StaticSlot {
12161217

12171218
// The children of this name. Must be null if there are no children.
12181219
@Nullable List<Name> props;
1219-
/** The first global assignment to a name. */
1220+
1221+
/** The declaration of a name. */
12201222
private @Nullable Ref declaration;
12211223

1224+
/** The first global assignment to a name. */
1225+
private @Nullable Ref initialization;
1226+
12221227
/**
12231228
* Keep track of which Nodes are Refs for this Name.
12241229
*
@@ -1311,6 +1316,10 @@ boolean usesHasOwnProperty() {
13111316
return declaration;
13121317
}
13131318

1319+
public @Nullable Ref getInitialization() {
1320+
return initialization;
1321+
}
1322+
13141323
boolean isFunction() {
13151324
return getBooleanProperty(NameProp.FUNCTION);
13161325
}
@@ -1455,6 +1464,12 @@ private void updateStateForAddedRef(Ref ref) {
14551464
if (declaration == null) {
14561465
declaration = ref;
14571466
}
1467+
if (initialization == null && !ref.isUninitializedDeclaration()) {
1468+
// Record the reference where the first value is actually assigned.
1469+
// Do not include the automatically-assigned `undefined` value case.
1470+
// e.g. `var name;`
1471+
initialization = ref;
1472+
}
14581473
if (firstDeclarationJSDocInfo == null) {
14591474
// JSDocInfo from the first SET_FROM_GLOBAL will be assumed to be canonical
14601475
// Note that this will not change if the first declaration is later removed
@@ -1725,7 +1740,8 @@ boolean isCollapsingExplicitlyDenied() {
17251740
*/
17261741
Inlinability calculateInlinability() {
17271742
// Only simple aliases with direct usage are inlineable.
1728-
if (inExterns() || globalSets != 1 || localSets != 0) {
1743+
// Exactly 2 global sets could be OK, if the first is the declaration with no initializer.
1744+
if (inExterns() || !hasOneRealGlobalSet() || localSets != 0) {
17291745
return Inlinability.DO_NOT_INLINE;
17301746
}
17311747

@@ -1761,6 +1777,10 @@ Inlinability calculateInlinability() {
17611777
return collapsibility;
17621778
}
17631779

1780+
private boolean hasOneRealGlobalSet() {
1781+
return globalSets == 1 || (globalSets == 2 && declaration.isUninitializedDeclaration());
1782+
}
1783+
17641784
boolean canCollapse() {
17651785
return canCollapseOrInline().canCollapse();
17661786
}
@@ -1914,13 +1934,13 @@ private void logDecision(Inlinability inlinability, String reason) {
19141934

19151935
private Inlinability getUnsafeInlinabilityBasedOnDeclaredType() {
19161936
if (this.getBooleanProperty(NameProp.CONSTRUCTOR_TYPE)) {
1917-
return Inlinability.INLINE_BUT_KEEP_DECLARATION_CLASS;
1937+
return Inlinability.INLINE_BUT_KEEP_DECLARATION_CLASS;
19181938
} else if (this.getBooleanProperty(NameProp.INTERFACE_TYPE)) {
1919-
return Inlinability.INLINE_BUT_KEEP_DECLARATION_INTERFACE;
1939+
return Inlinability.INLINE_BUT_KEEP_DECLARATION_INTERFACE;
19201940
} else if (this.getBooleanProperty(NameProp.ENUM_TYPE)) {
1921-
return Inlinability.INLINE_BUT_KEEP_DECLARATION_ENUM;
1941+
return Inlinability.INLINE_BUT_KEEP_DECLARATION_ENUM;
19221942
} else if (this.getBooleanProperty(NameProp.NOT_A_TYPE)) {
1923-
return Inlinability.DO_NOT_INLINE;
1943+
return Inlinability.DO_NOT_INLINE;
19241944
}
19251945
throw new IllegalStateException(
19261946
SimpleFormat.format("name missing declaredType value: %s", this));
@@ -2133,11 +2153,11 @@ private boolean valueImplicitlySupportsAliasing() {
21332153
return true;
21342154
}
21352155
if (getBooleanProperty(NameProp.FUNCTION)) {
2136-
// We want ES5 ctors/interfaces to behave consistently with ES6 because:
2137-
// - transpilation should not change behaviour
2138-
// - updating code shouldn't be hindered by behaviour changes
2139-
@Nullable JSDocInfo jsdoc = getJSDocInfo();
2140-
return jsdoc != null && jsdoc.isConstructorOrInterface();
2156+
// We want ES5 ctors/interfaces to behave consistently with ES6 because:
2157+
// - transpilation should not change behaviour
2158+
// - updating code shouldn't be hindered by behaviour changes
2159+
@Nullable JSDocInfo jsdoc = getJSDocInfo();
2160+
return jsdoc != null && jsdoc.isConstructorOrInterface();
21412161
}
21422162
return false;
21432163
}
@@ -2472,6 +2492,11 @@ boolean isSetFromGlobal() {
24722492
return this.type == Type.SET_FROM_GLOBAL || this.type == Type.GET_AND_SET_FROM_GLOBAL;
24732493
}
24742494

2495+
/** True for `var name;` and similar cases. */
2496+
boolean isUninitializedDeclaration() {
2497+
return node.isName() && NodeUtil.isNameDeclaration(node.getParent()) && !node.hasChildren();
2498+
}
2499+
24752500
@Override
24762501
public String toString() {
24772502
return MoreObjects.toStringHelper(this)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,8 @@ private void inlineGlobalAliasIfPossible(Name name, Ref alias, GlobalNamespace n
835835

836836
if (aliasInlinability.shouldRemoveDeclaration()) {
837837
// Rewrite the initialization of the alias.
838-
Ref aliasDeclaration = aliasingName.getDeclaration();
839-
if (aliasDeclaration.isTwin()) {
838+
Ref aliasInitialization = aliasingName.getInitialization();
839+
if (aliasInitialization.isTwin()) {
840840
// This is in a nested assign.
841841
// Replace
842842
// a.b = aliasing.name = aliased.name
@@ -846,7 +846,7 @@ private void inlineGlobalAliasIfPossible(Name name, Ref alias, GlobalNamespace n
846846
Node aliasGrandparent = aliasParent.getParent();
847847
aliasParent.replaceWith(alias.getNode().detach());
848848
// Remove the ref to 'aliasing.name' entirely
849-
aliasingName.removeRef(aliasDeclaration);
849+
aliasingName.removeRef(aliasInitialization);
850850
// Force GlobalNamespace to revisit the new reference to 'aliased.name' and update its
851851
// internal state.
852852
newNodes.add(new AstChange(alias.scope, alias.getNode()));

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ public void testTs52OutputChange() {
8383
" method2() {",
8484
" return alias.staticPropOnAlias;",
8585
" }",
86-
"}",
86+
"};",
8787
"alias = module$exports$C;",
8888
"(() => {",
89-
" alias.staticPropOnAlias = 1",
89+
" alias.staticPropOnAlias = 1;",
9090
"})();",
9191
"module$exports$C.staticPropOnC = 2;",
9292
"")),
@@ -96,19 +96,18 @@ public void testTs52OutputChange() {
9696
"var alias;",
9797
"var module$exports$C = class {",
9898
" method1() {",
99-
" return alias.staticPropOnC;",
99+
" return module$exports$C$staticPropOnC;",
100100
" }",
101101
" method2() {",
102-
" return alias.staticPropOnAlias;",
102+
" return module$exports$C$staticPropOnAlias;",
103103
" }",
104-
"}",
105-
"alias = module$exports$C;",
104+
"};",
105+
"var module$exports$C$staticPropOnAlias;",
106+
"alias = null;",
106107
"(() => {",
107-
" alias.staticPropOnAlias = 1",
108+
" module$exports$C$staticPropOnAlias = 1;",
108109
"})();",
109-
// TODO : b/299055739 - bad collapse
110110
"var module$exports$C$staticPropOnC = 2;",
111-
"",
112111
"")));
113112
}
114113

test/com/google/javascript/jscomp/integration/ES2021IntegrationTest.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,12 @@ public void logicalAssignmentPropertyReferenceNotTranspiledOutput1() {
133133
options,
134134
lines(
135135
"const foo = {}", //
136-
"foo.x &&= 'something';"),
137-
lines("let a; (a = {}).a && (a.a = 'something')"));
136+
"foo.x &&= 'something';",
137+
"alert(foo.x);"),
138+
lines(
139+
"var a;", //
140+
"a &&= 'something';",
141+
"alert(a);"));
138142
}
139143

140144
@Test
@@ -281,10 +285,12 @@ public void logicalAsssignmentsPropertyReferenceCastType_supportedOnlyWithoutTra
281285
lines(
282286
"const obj = {};", //
283287
"obj.baa = true;",
284-
"/** @type {?} */ (obj.baa) &&= 5"),
288+
"/** @type {?} */ (obj.baa) &&= 5;",
289+
"alert(obj.baa);"),
285290
lines(
286-
"const a = {a:!0}", //
287-
"a.a && (a.a = 5)"));
291+
"var a = !0;", //
292+
"a = 5;",
293+
"alert(a);"));
288294
}
289295

290296
@Test
@@ -322,10 +328,12 @@ public void logicalAsssignmentsPropertyReferenceCastType_supportedWithTranspilat
322328
lines(
323329
"const obj = {};", //
324330
"obj.baa = true;",
325-
"/** @type {?} */ (obj.baa) &&= 5"),
331+
"/** @type {?} */ (obj.baa) &&= 5",
332+
"alert(obj.baa);"),
326333
lines(
327-
"const a = {a:!0}", //
328-
"a.a && (a.a = 5)"));
334+
"var a = !0;", //
335+
"a = 5;",
336+
"alert(a);"));
329337
}
330338

331339
@Test

0 commit comments

Comments
 (0)