Skip to content

Commit f032d8c

Browse files
lauraharkercopybara-github
authored andcommitted
Fix deserialization to put closureUnaware features on the main AST
This makes it consistent with the parser/IRFactory & lets us fix a TODO & re-enable ast validation in TranspileAndOptimizeClosureUnawareTest. In the long run, maybe we should instead track closureUnaware features separately. But at the moment, the added complexity doesn't seem worth it. PiperOrigin-RevId: 890576326
1 parent 619234a commit f032d8c

3 files changed

Lines changed: 45 additions & 15 deletions

File tree

src/com/google/javascript/jscomp/serialization/ScriptNodeDeserializer.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,11 @@ private ScriptNodeDeserializer owner() {
100100
* Visits a single AST node and returns the corresponding Node.
101101
*
102102
* @param astNode The AST node to visit.
103-
* @param contextStack The context stack to use for the node. If feature collection should not
104-
* occur, this should be null.
103+
* @param contextStack The context stack to use for the node.
105104
* @param sourceFileTemplate A template node to use for the node.
106105
*/
107106
private Node visit(
108-
AstNode astNode,
109-
@Nullable List<FeatureContext> contextStack,
110-
@Nullable Node sourceFileTemplate) {
107+
AstNode astNode, List<FeatureContext> contextStack, @Nullable Node sourceFileTemplate) {
111108
if (sourceFileTemplate == null || astNode.getSourceFile() != 0) {
112109
// 0 == 'not set'
113110
sourceFileTemplate =
@@ -141,12 +138,13 @@ private Node visit(
141138
AstNode serializedShadowChild = astNode.getChild(0);
142139
Node closureUnawareSubtreeTemplateNode = sourceFileTemplate.cloneTree();
143140
closureUnawareSubtreeTemplateNode.setIsInClosureUnawareSubtree(true);
144-
// Unlike normal deserialization, we want to avoid recording features for code within the
145-
// shadow because we don't run transpilation passes over it and later stages of the compiler
146-
// attempt to validate that transpilation has successfully run over the entire AST and no
147-
// features remain that won't work in the given language output level.
141+
// Note: we intentionally pass `contextStack` to visit() in order to capture any
142+
// language features present in the shadow AST, like const syntax, into the main AST
143+
// featureset. We are doing this to align ScriptNodeDeserializer with the existing behavior
144+
// in IRFactory. (In the future we could consider instead giving each shadow AST script
145+
// its own separate featureset, but that would be a larger change.)
148146
Node shadowedCode =
149-
this.visit(serializedShadowChild, null, closureUnawareSubtreeTemplateNode);
147+
this.visit(serializedShadowChild, contextStack, closureUnawareSubtreeTemplateNode);
150148
this.owner().setOriginalNameIfPresent(serializedShadowChild, shadowedCode);
151149
// The shadowed code is only the "source" parts of the shadow structure, and does not
152150
// include the synthetic code that is needed for the compiler to consider it a valid
@@ -169,9 +167,7 @@ private Node visit(
169167
this.owner().setOriginalNameIfPresent(child, deserializedChild);
170168
}
171169

172-
if (contextStack != null) {
173-
contextStack.removeLast();
174-
}
170+
contextStack.removeLast();
175171

176172
return n;
177173
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ public void setup() {
5454
this.outputCount = 0;
5555
this.extraOptions = options -> {};
5656
this.mode = NestedCompilerRunner.Mode.TRANSPILE_AND_OPTIMIZE;
57-
// TODO: b/494231728 - re-enable this.
58-
disableAstValidation();
5957
}
6058

6159
@Override

test/com/google/javascript/jscomp/serialization/SerializeAndDeserializeAstTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.google.javascript.jscomp.colors.ColorRegistry;
3737
import com.google.javascript.jscomp.colors.StandardColors;
3838
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
39+
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
3940
import com.google.javascript.jscomp.serialization.TypedAstDeserializer.DeserializedAst;
4041
import com.google.javascript.jscomp.testing.CodeSubTree;
4142
import com.google.javascript.jscomp.testing.TestExternsBuilder;
@@ -851,6 +852,41 @@ public void visit(NodeTraversal t, Node n, Node parent) {
851852
NodeTraversal.traverse(result.compiler, shadowedContent, cb);
852853
}
853854

855+
@Test
856+
public void shadowAsts_featuresSetOnMainAstScript() throws IOException {
857+
Result result =
858+
runWithoutExpected(
859+
externs(new TestExternsBuilder().addClosureExterns().build()),
860+
srcs(
861+
"""
862+
/** @closureUnaware @fileoverview */
863+
goog.module('test');
864+
865+
/** @closureUnaware */
866+
(function() {
867+
const x = 5;
868+
})();
869+
"""));
870+
871+
Node shadowHost =
872+
CodeSubTree.findFirstNode(result.sourceRoot, (n) -> n.getClosureUnawareShadow() != null);
873+
Node shadowScript = shadowHost.getClosureUnawareShadow().getOnlyChild();
874+
Node mainScript = result.sourceRoot.getOnlyChild();
875+
checkState(mainScript.isScript(), mainScript);
876+
checkState(shadowScript.isScript(), shadowScript);
877+
878+
// The main AST script contains the "const" feature; the shadow AST featureset is null.
879+
// NOTE: this behavior was selected for consistency with the parser/IRFactory and to address
880+
// AstValidator breakages. However, we could consider instead setting the featureset on
881+
// the shadow script directly, and omitting CONST_DECLARATIONS in the main script featureset.
882+
// At present, the main advantage of doing so seems to be a potential performance gain because
883+
// transpilation passes on the main AST can skip any features only in the shadow AST, but we
884+
// don't expect that performance gain to be significant.
885+
assertThat(mainScript.getProp(Node.FEATURE_SET))
886+
.isEqualTo(FeatureSet.BARE_MINIMUM.with(Feature.CONST_DECLARATIONS));
887+
assertThat(shadowScript.getProp(Node.FEATURE_SET)).isNull();
888+
}
889+
854890
@Test
855891
public void includesRuntimeLibraryPaths() throws IOException {
856892
enableGatherExternProperties();

0 commit comments

Comments
 (0)