JS: Store in the Java AST

This commit is contained in:
Asger F
2023-03-02 13:27:29 +01:00
parent c715de2a10
commit 8d9060f1f9
9 changed files with 63 additions and 34 deletions

View File

@@ -314,8 +314,8 @@ public class ESNextParser extends JSXParser {
this.parseExportSpecifiersMaybe(specifiers, exports);
}
Literal source = (Literal) this.parseExportFrom(specifiers, null, true);
Expression assertion = this.parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST
return this.finishNode(new ExportNamedDeclaration(exportStart, null, specifiers, source));
Expression assertion = this.parseImportOrExportAssertionAndSemicolon();
return this.finishNode(new ExportNamedDeclaration(exportStart, null, specifiers, source, assertion));
}
return super.parseExportRest(exportStart, exports);
@@ -331,8 +331,8 @@ public class ESNextParser extends JSXParser {
List<ExportSpecifier> specifiers = CollectionUtil.makeList(nsSpec);
this.parseExportSpecifiersMaybe(specifiers, exports);
Literal source = (Literal) this.parseExportFrom(specifiers, null, true);
Expression assertion = this.parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST
return this.finishNode(new ExportNamedDeclaration(exportStart, null, specifiers, source));
Expression assertion = this.parseImportOrExportAssertionAndSemicolon();
return this.finishNode(new ExportNamedDeclaration(exportStart, null, specifiers, source, assertion));
}
return super.parseExportAll(exportStart, starLoc, exports);
@@ -437,12 +437,12 @@ public class ESNextParser extends JSXParser {
*/
private DynamicImport parseDynamicImport(Position startLoc) {
Expression source = parseMaybeAssign(false, null, null);
Expression assertion = null;
Expression attributes = null;
if (this.eat(TokenType.comma)) {
assertion = this.parseMaybeAssign(false, null, null); // TODO: store in AST
attributes = this.parseMaybeAssign(false, null, null);
}
this.expect(TokenType.parenR);
DynamicImport di = this.finishNode(new DynamicImport(new SourceLocation(startLoc), source));
DynamicImport di = this.finishNode(new DynamicImport(new SourceLocation(startLoc), source, attributes));
return di;
}

View File

@@ -3429,10 +3429,10 @@ public class Parser {
declaration = null;
specifiers = this.parseExportSpecifiers(exports);
source = parseExportFrom(specifiers, source, false);
assertion = parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST
assertion = parseImportOrExportAssertionAndSemicolon();
}
return this.finishNode(
new ExportNamedDeclaration(loc, declaration, specifiers, (Literal) source));
new ExportNamedDeclaration(loc, declaration, specifiers, (Literal) source, assertion));
}
/** Parses the 'from' clause of an export, not including the assertion or semicolon. */
@@ -3460,8 +3460,8 @@ public class Parser {
protected ExportDeclaration parseExportAll(
SourceLocation loc, Position starLoc, Set<String> exports) {
Expression source = parseExportFrom(null, null, true);
Expression assertion = parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST
return this.finishNode(new ExportAllDeclaration(loc, (Literal) source));
Expression assertion = parseImportOrExportAssertionAndSemicolon();
return this.finishNode(new ExportAllDeclaration(loc, (Literal) source, assertion));
}
private void checkExport(Set<String> exports, String name, Position pos) {
@@ -3549,9 +3549,9 @@ public class Parser {
if (this.type != TokenType.string) this.unexpected();
source = (Literal) this.parseExprAtom(null);
}
Expression assertion = this.parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST
Expression assertion = this.parseImportOrExportAssertionAndSemicolon();
if (specifiers == null) return null;
return this.finishNode(new ImportDeclaration(loc, specifiers, source));
return this.finishNode(new ImportDeclaration(loc, specifiers, source, assertion));
}
// Parses a comma-separated list of module imports.

View File

@@ -943,12 +943,12 @@ public class FlowParser extends ESNextParser {
// `export type { foo, bar };`
List<ExportSpecifier> specifiers = this.parseExportSpecifiers(exports);
this.parseExportFrom(specifiers, null, false);
this.parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST?
this.parseImportOrExportAssertionAndSemicolon();
return null;
} else if (this.eat(TokenType.star)) {
if (this.eatContextual("as")) this.parseIdent(true);
this.parseExportFrom(null, null, true);
this.parseImportOrExportAssertionAndSemicolon(); // TODO: store in AST?
this.parseImportOrExportAssertionAndSemicolon();
return null;
} else {
// `export type Foo = Bar;`

View File

@@ -2,16 +2,23 @@ package com.semmle.js.ast;
public class DynamicImport extends Expression {
private final Expression source;
private final Expression attributes;
public DynamicImport(SourceLocation loc, Expression source) {
public DynamicImport(SourceLocation loc, Expression source, Expression attributes) {
super("DynamicImport", loc);
this.source = source;
this.attributes = attributes;
}
public Expression getSource() {
return source;
}
/** Returns the second "argument" provided to the import, such as <code>{ assert: { type: "json" }}</code>. */
public Expression getAttributes() {
return attributes;
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);

View File

@@ -9,16 +9,22 @@ package com.semmle.js.ast;
*/
public class ExportAllDeclaration extends ExportDeclaration {
private final Literal source;
private final Expression assertion;
public ExportAllDeclaration(SourceLocation loc, Literal source) {
public ExportAllDeclaration(SourceLocation loc, Literal source, Expression assertion) {
super("ExportAllDeclaration", loc);
this.source = source;
this.assertion = assertion;
}
public Literal getSource() {
return source;
}
public Expression getAssertion() {
return assertion;
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);

View File

@@ -15,20 +15,22 @@ public class ExportNamedDeclaration extends ExportDeclaration {
private final Statement declaration;
private final List<ExportSpecifier> specifiers;
private final Literal source;
private final Expression assertion;
private final boolean hasTypeKeyword;
public ExportNamedDeclaration(
SourceLocation loc, Statement declaration, List<ExportSpecifier> specifiers, Literal source) {
this(loc, declaration, specifiers, source, false);
SourceLocation loc, Statement declaration, List<ExportSpecifier> specifiers, Literal source, Expression assertion) {
this(loc, declaration, specifiers, source, assertion, false);
}
public ExportNamedDeclaration(
SourceLocation loc, Statement declaration, List<ExportSpecifier> specifiers, Literal source,
boolean hasTypeKeyword) {
Expression assertion, boolean hasTypeKeyword) {
super("ExportNamedDeclaration", loc);
this.declaration = declaration;
this.specifiers = specifiers;
this.source = source;
this.assertion = assertion;
this.hasTypeKeyword = hasTypeKeyword;
}
@@ -57,6 +59,11 @@ public class ExportNamedDeclaration extends ExportDeclaration {
return v.visit(this, c);
}
/** Returns the expression after the <code>assert</code> keyword, if any, such as <code>{ type: "json" }</code>. */
public Expression getAssertion() {
return assertion;
}
/** Returns true if this is an <code>export type</code> declaration. */
public boolean hasTypeKeyword() {
return hasTypeKeyword;

View File

@@ -23,18 +23,21 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
/** The module from which declarations are imported. */
private final Literal source;
private final Expression assertion;
private int symbol = -1;
private boolean hasTypeKeyword;
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source) {
this(loc, specifiers, source, false);
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source, Expression assertion) {
this(loc, specifiers, source, assertion, false);
}
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source, boolean hasTypeKeyword) {
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source, Expression assertion, boolean hasTypeKeyword) {
super("ImportDeclaration", loc);
this.specifiers = specifiers;
this.source = source;
this.assertion = assertion;
this.hasTypeKeyword = hasTypeKeyword;
}
@@ -46,6 +49,11 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
return specifiers;
}
/** Returns the expression after the <code>assert</code> keyword, if any, such as <code>{ type: "json" }</code>. */
public Expression getAssertion() {
return assertion;
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);

View File

@@ -523,7 +523,7 @@ public class NodeCopier implements Visitor<Void, INode> {
@Override
public ExportAllDeclaration visit(ExportAllDeclaration nd, Void c) {
return new ExportAllDeclaration(visit(nd.getLoc()), copy(nd.getSource()));
return new ExportAllDeclaration(visit(nd.getLoc()), copy(nd.getSource()), copy(nd.getAssertion()));
}
@Override
@@ -537,7 +537,8 @@ public class NodeCopier implements Visitor<Void, INode> {
visit(nd.getLoc()),
copy(nd.getDeclaration()),
copy(nd.getSpecifiers()),
copy(nd.getSource()));
copy(nd.getSource()),
copy(nd.getAssertion()));
}
@Override
@@ -558,7 +559,7 @@ public class NodeCopier implements Visitor<Void, INode> {
@Override
public ImportDeclaration visit(ImportDeclaration nd, Void c) {
return new ImportDeclaration(
visit(nd.getLoc()), copy(nd.getSpecifiers()), copy(nd.getSource()));
visit(nd.getLoc()), copy(nd.getSpecifiers()), copy(nd.getSource()), copy(nd.getAssertion()), nd.hasTypeKeyword());
}
@Override
@@ -678,7 +679,7 @@ public class NodeCopier implements Visitor<Void, INode> {
@Override
public INode visit(DynamicImport nd, Void c) {
return new DynamicImport(visit(nd.getLoc()), copy(nd.getSource()));
return new DynamicImport(visit(nd.getLoc()), copy(nd.getSource()), copy(nd.getAttributes()));
}
@Override

View File

@@ -342,7 +342,7 @@ public class TypeScriptASTConverter {
return convertArrowFunction(node, loc);
case "AsExpression":
return convertTypeAssertionExpression(node, loc);
case "SatisfiesExpression":
case "SatisfiesExpression":
return convertSatisfiesExpression(node, loc);
case "AwaitExpression":
return convertAwaitExpression(node, loc);
@@ -888,7 +888,7 @@ public class TypeScriptASTConverter {
private Node convertCallExpression(JsonObject node, SourceLocation loc) throws ParseError {
List<Expression> arguments = convertChildren(node, "arguments");
if (arguments.size() == 1 && hasKind(node.get("expression"), "ImportKeyword")) {
return new DynamicImport(loc, arguments.get(0));
return new DynamicImport(loc, arguments.get(0), null); // TODO: preserve import attributes
}
Expression callee = convertChild(node, "expression");
List<ITypeExpression> typeArguments = convertChildrenAsTypes(node, "typeArguments");
@@ -1199,9 +1199,9 @@ public class TypeScriptASTConverter {
hasKind(node.get("exportClause"), "NamespaceExport")
? Collections.singletonList(convertChild(node, "exportClause"))
: convertChildren(node.get("exportClause").getAsJsonObject(), "elements");
return new ExportNamedDeclaration(loc, null, specifiers, source, hasTypeKeyword);
return new ExportNamedDeclaration(loc, null, specifiers, source, null, hasTypeKeyword); // TODO: preserve import assertions
} else {
return new ExportAllDeclaration(loc, source);
return new ExportAllDeclaration(loc, source, null); // TODO: preserve import assertions
}
}
@@ -1400,7 +1400,7 @@ public class TypeScriptASTConverter {
}
hasTypeKeyword = importClause.get("isTypeOnly").getAsBoolean();
}
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src, hasTypeKeyword);
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src, null, hasTypeKeyword); // TODO: preserve import assertions
attachSymbolInformation(importDecl, node);
return importDecl;
}
@@ -1746,7 +1746,7 @@ public class TypeScriptASTConverter {
if (hasFlag(node, "NestedNamespace")) {
// In a nested namespace declaration `namespace A.B`, the nested namespace `B`
// is implicitly exported.
return new ExportNamedDeclaration(loc, decl, new ArrayList<>(), null);
return new ExportNamedDeclaration(loc, decl, new ArrayList<>(), null, null); // TODO: preserve import assertion
} else {
return fixExports(loc, decl);
}
@@ -2455,7 +2455,7 @@ public class TypeScriptASTConverter {
advance(loc, skipped);
// capture group 1 is `default`, if present
if (m.group(1) == null)
return new ExportNamedDeclaration(outerLoc, (Statement) decl, new ArrayList<>(), null);
return new ExportNamedDeclaration(outerLoc, (Statement) decl, new ArrayList<>(), null, null); // TODO: preserve import assertions
return new ExportDefaultDeclaration(outerLoc, decl);
}
return decl;