Skip to content

Commit fb0703a

Browse files
lauraharkercopybara-github
authored andcommitted
Merge JsAst/SourceAst classes into CompilerInput
Any usages of JsAst in the Java API can be replaced with CompilerInput PiperOrigin-RevId: 696348172
1 parent 7c9aafb commit fb0703a

File tree

8 files changed

+138
-226
lines changed

8 files changed

+138
-226
lines changed

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ public void parse() {
12431243
public Node parse(SourceFile file) {
12441244
initCompilerOptionsIfTesting();
12451245
logger.finest("Parsing: " + file.getName());
1246-
return new JsAst(file).getAstRoot(this);
1246+
return new CompilerInput(file).getAstRoot(this);
12471247
}
12481248

12491249
PassConfig getPassConfig() {
@@ -3818,19 +3818,19 @@ void initializeSyntheticCodeInput() {
38183818
checkState(
38193819
!this.inputsById.containsKey(SYNTHETIC_CODE_INPUT_ID),
38203820
"Already initialized synthetic input");
3821-
JsAst ast = new JsAst(SourceFile.fromCode(SYNTHETIC_CODE_INPUT_ID.getIdName(), ""));
3821+
CompilerInput ast =
3822+
new CompilerInput(SourceFile.fromCode(SYNTHETIC_CODE_INPUT_ID.getIdName(), ""));
38223823
if (inputsById.containsKey(ast.getInputId())) {
38233824
throw new IllegalStateException("Conflicting synthetic id name");
38243825
}
3825-
CompilerInput input = new CompilerInput(ast, false);
38263826
jsRoot.addChildToFront(checkNotNull(ast.getAstRoot(this)));
38273827

38283828
JSChunk firstChunk = Iterables.getFirst(getChunks(), null);
38293829
if (firstChunk.getName().equals(JSChunk.STRONG_CHUNK_NAME)) {
3830-
firstChunk.add(input);
3830+
firstChunk.add(ast);
38313831
}
3832-
input.setChunk(firstChunk);
3833-
putCompilerInput(input);
3832+
ast.setChunk(firstChunk);
3833+
putCompilerInput(ast);
38343834

38353835
commentsPerFile.put(SYNTHETIC_CODE_INPUT_ID.getIdName(), ImmutableList.of());
38363836
reportChangeToChangeScope(ast.getAstRoot(this));

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

+118-31
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.common.base.Preconditions.checkArgument;
2020
import static com.google.common.base.Preconditions.checkNotNull;
2121
import static com.google.common.base.Preconditions.checkState;
22+
import static com.google.javascript.jscomp.base.JSCompObjects.identical;
2223

2324
import com.google.common.base.Preconditions;
2425
import com.google.common.collect.ImmutableCollection;
@@ -30,7 +31,9 @@
3031
import com.google.javascript.jscomp.deps.ModuleLoader;
3132
import com.google.javascript.jscomp.deps.ModuleLoader.ModulePath;
3233
import com.google.javascript.jscomp.deps.SimpleDependencyInfo;
34+
import com.google.javascript.jscomp.parsing.ParserRunner;
3335
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
36+
import com.google.javascript.rhino.IR;
3437
import com.google.javascript.rhino.InputId;
3538
import com.google.javascript.rhino.JSDocInfo;
3639
import com.google.javascript.rhino.Node;
@@ -40,6 +43,7 @@
4043
import java.util.List;
4144
import java.util.Map;
4245
import java.util.TreeMap;
46+
import java.util.function.Supplier;
4347
import org.jspecify.annotations.Nullable;
4448

4549
/**
@@ -54,9 +58,10 @@ public class CompilerInput implements DependencyInfo {
5458
// Info about where the file lives.
5559
private JSChunk chunk;
5660
private final InputId id;
61+
private final SourceFile sourceFile;
5762

58-
// The AST.
59-
private final SourceAst ast;
63+
// The lazily constructed AST.
64+
private final JsAst ast = new JsAst();
6065

6166
// DependencyInfo to delegate to.
6267
private DependencyInfo dependencyInfo;
@@ -98,46 +103,36 @@ public void setTypedScope(@Nullable TypedScope typedScope) {
98103
// with the AST node. Once (when?) we enforce that extern files always contain an @externs
99104
// annotation, we can store the extern bit in the AST node, and make SourceFile immutable.
100105

101-
public CompilerInput(SourceAst ast) {
102-
this(ast, ast.getSourceFile().getName(), false);
103-
}
104-
105-
public CompilerInput(SourceAst ast, boolean isExtern) {
106-
this.ast = ast;
107-
this.id = ast.getInputId();
108-
109-
if (isExtern) {
110-
setIsExtern();
111-
}
106+
public CompilerInput(SourceFile file, InputId inputId) {
107+
this(file, inputId, false);
112108
}
113109

114110
/**
115111
* @deprecated the inputId is read from the SourceAst. Use CompilerInput(ast, isExtern)
116112
*/
117113
@Deprecated
118-
public CompilerInput(SourceAst ast, String inputId, boolean isExtern) {
119-
this(ast, new InputId(inputId), isExtern);
114+
public CompilerInput(SourceFile sourceFile, String inputId, boolean isExtern) {
115+
this(sourceFile, new InputId(inputId), isExtern);
120116
}
121117

122118
/**
123119
* @deprecated the inputId is read from the SourceAst. Use CompilerInput(ast, isExtern)
124120
*/
125121
@Deprecated
126-
public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) {
127-
this(ast, isExtern);
128-
checkArgument(
129-
inputId.equals(ast.getInputId()),
130-
"mismatched input ids\nctor param: %s\nast.getInputId(): %s",
131-
inputId,
132-
ast.getInputId());
122+
public CompilerInput(SourceFile sourceFile, InputId inputId, boolean isExtern) {
123+
this.sourceFile = sourceFile;
124+
this.id = inputId;
125+
if (isExtern) {
126+
setIsExtern();
127+
}
133128
}
134129

135130
public CompilerInput(SourceFile file) {
136131
this(file, false);
137132
}
138133

139134
public CompilerInput(SourceFile file, boolean isExtern) {
140-
this(new JsAst(file), isExtern);
135+
this(file, new InputId(file.getName()), isExtern);
141136
}
142137

143138
/** Returns a name for this input. Must be unique across all inputs. */
@@ -170,7 +165,7 @@ public void clearAst() {
170165
}
171166

172167
public SourceFile getSourceFile() {
173-
return ast.getSourceFile();
168+
return sourceFile;
174169
}
175170

176171
/** Sets an abstract compiler for doing parsing. */
@@ -341,7 +336,7 @@ private DependencyInfo generateDependencyInfo() {
341336

342337
// If the code is a JsAst, then it was originally JS code, and is compatible with the
343338
// regex-based parsing of JsFileRegexParser.
344-
if (ast instanceof JsAst && JsFileRegexParser.isSupported() && compiler.preferRegexParser()) {
339+
if (JsFileRegexParser.isSupported() && compiler.preferRegexParser()) {
345340
// Look at the source code.
346341
// Note: it's OK to use getName() instead of
347342
// getPathRelativeToClosureBase() here because we're not using
@@ -353,7 +348,7 @@ private DependencyInfo generateDependencyInfo() {
353348
.setModuleLoader(compiler.getModuleLoader())
354349
.setIncludeGoogBase(true)
355350
.parseFile(getName(), getName(), getCode());
356-
return new LazyParsedDependencyInfo(info, (JsAst) ast, compiler);
351+
return new LazyParsedDependencyInfo(info, this, compiler);
357352
} catch (IOException e) {
358353
compiler
359354
.getErrorManager()
@@ -573,22 +568,22 @@ void overrideModule(JSChunk chunk) {
573568
}
574569

575570
public boolean isExtern() {
576-
if (ast == null || ast.getSourceFile() == null) {
571+
if (sourceFile == null) {
577572
return false;
578573
}
579-
return ast.getSourceFile().isExtern();
574+
return sourceFile.isExtern();
580575
}
581576

582577
void setIsExtern() {
583578
// TODO(tjgq): Add a precondition check here. People are passing in null, but they shouldn't be.
584-
if (ast == null || ast.getSourceFile() == null) {
579+
if (sourceFile == null) {
585580
return;
586581
}
587-
ast.getSourceFile().setKind(SourceKind.EXTERN);
582+
sourceFile.setKind(SourceKind.EXTERN);
588583
}
589584

590585
public int getLineOffset(int lineno) {
591-
return ast.getSourceFile().getLineOffset(lineno);
586+
return sourceFile.getLineOffset(lineno);
592587
}
593588

594589
@Override
@@ -636,4 +631,96 @@ public enum ModuleType {
636631
JSON,
637632
IMPORTED_SCRIPT
638633
}
634+
635+
public FeatureSet getFeatures(AbstractCompiler compiler) {
636+
var unused = this.ast.getAstRoot(compiler); // parse if required
637+
return this.ast.features;
638+
}
639+
640+
/**
641+
* Wraps the Rhino AST for this CompilerInput.
642+
*
643+
* <p>Use this wrapper class instead of directly storing a Node because the AST is lazily
644+
* constructed. CompilerInputs are created before the parsing step, and in some cases the AST for
645+
* a given CompilerInput may never be parsed if dependency pruning is enabled.
646+
*
647+
* <p>Additionally if using precompiled libraries, this class handles deserializing the AST from
648+
* the TypedAST format, instead of parsing it from the source.
649+
*/
650+
private final class JsAst {
651+
private @Nullable Node root;
652+
private FeatureSet features;
653+
654+
public Node getAstRoot(AbstractCompiler compiler) {
655+
if (this.isParsed()) {
656+
return this.root;
657+
}
658+
659+
Supplier<Node> astRootSource = compiler.getTypedAstDeserializer(sourceFile);
660+
if (astRootSource != null) {
661+
this.root = astRootSource.get();
662+
this.features = (FeatureSet) this.root.getProp(Node.FEATURE_SET);
663+
} else {
664+
this.parse(compiler);
665+
}
666+
checkState(identical(this.root.getStaticSourceFile(), sourceFile));
667+
this.root.setInputId(id);
668+
// Clear the cached source after parsing. It will be re-read for snippet generation if
669+
// needed.
670+
sourceFile.clearCachedSource();
671+
return this.root;
672+
}
673+
674+
public void clearAst() {
675+
root = null;
676+
// While we're at it, clear out any saved text in the source file on
677+
// the assumption that if we're dumping the parse tree, then we probably
678+
// assume regenerating everything else is a smart idea also.
679+
sourceFile.clearCachedSource();
680+
}
681+
682+
private boolean isParsed() {
683+
return root != null;
684+
}
685+
686+
private void parse(AbstractCompiler compiler) {
687+
try {
688+
ParserRunner.ParseResult result =
689+
ParserRunner.parse(
690+
sourceFile,
691+
sourceFile.getCode(),
692+
compiler.getParserConfig(
693+
sourceFile.isExtern()
694+
? AbstractCompiler.ConfigContext.EXTERNS
695+
: AbstractCompiler.ConfigContext.DEFAULT),
696+
compiler.getDefaultErrorReporter());
697+
root = result.ast;
698+
features = result.features;
699+
700+
if (compiler.getOptions().preservesDetailedSourceInfo()) {
701+
compiler.addComments(sourceFile.getName(), result.comments);
702+
}
703+
if (result.sourceMapURL != null && compiler.getOptions().resolveSourceMapAnnotations) {
704+
boolean parseInline = compiler.getOptions().parseInlineSourceMaps;
705+
SourceFile sourceMapSourceFile =
706+
SourceMapResolver.extractSourceMap(sourceFile, result.sourceMapURL, parseInline);
707+
if (sourceMapSourceFile != null) {
708+
compiler.addInputSourceMap(
709+
sourceFile.getName(), new SourceMapInput(sourceMapSourceFile));
710+
}
711+
}
712+
} catch (IOException e) {
713+
compiler.report(
714+
JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName(), e.getMessage()));
715+
}
716+
717+
if (root == null) {
718+
root = IR.script();
719+
}
720+
721+
// Set the source name so that the compiler passes can track
722+
// the source file and module.
723+
root.setStaticSourceFile(sourceFile);
724+
}
725+
}
639726
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,8 @@ static class BannedCodePattern extends AbstractRule {
14121412

14131413
ImmutableList.Builder<TemplateAstMatcher> builder = ImmutableList.builder();
14141414
for (String value : requirement.getValueList()) {
1415-
Node parseRoot = new JsAst(SourceFile.fromCode("<template>", value)).getAstRoot(compiler);
1415+
Node parseRoot =
1416+
new CompilerInput(SourceFile.fromCode("<template>", value)).getAstRoot(compiler);
14161417
if (!parseRoot.hasOneChild() || !parseRoot.getFirstChild().isFunction()) {
14171418
throw new InvalidRequirementSpec("invalid conformance template: " + value);
14181419
}

0 commit comments

Comments
 (0)