Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f3ea810

Browse files
author
Max Schaefer
committed
JavaScript: Add parser support for E4X.
1 parent 1ad4867 commit f3ea810

23 files changed

Lines changed: 1413 additions & 13 deletions

javascript/extractor/src/com/semmle/jcorn/CustomParser.java

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
import java.util.List;
55
import java.util.regex.Matcher;
66

7+
import com.semmle.jcorn.TokenType.Properties;
78
import com.semmle.jcorn.flow.FlowParser;
89
import com.semmle.js.ast.ArrayExpression;
910
import com.semmle.js.ast.AssignmentExpression;
1011
import com.semmle.js.ast.BlockStatement;
1112
import com.semmle.js.ast.CallExpression;
1213
import com.semmle.js.ast.CatchClause;
14+
import com.semmle.js.ast.ClassExpression;
1315
import com.semmle.js.ast.ComprehensionBlock;
1416
import com.semmle.js.ast.ComprehensionExpression;
17+
import com.semmle.js.ast.Decorator;
1518
import com.semmle.js.ast.Expression;
1619
import com.semmle.js.ast.ExpressionStatement;
1720
import com.semmle.js.ast.ForInStatement;
@@ -28,8 +31,14 @@
2831
import com.semmle.js.ast.Position;
2932
import com.semmle.js.ast.SourceLocation;
3033
import com.semmle.js.ast.Statement;
34+
import com.semmle.js.ast.Token;
3135
import com.semmle.js.ast.TryStatement;
3236
import com.semmle.js.ast.VariableDeclaration;
37+
import com.semmle.js.ast.XMLAnyName;
38+
import com.semmle.js.ast.XMLAttributeSelector;
39+
import com.semmle.js.ast.XMLDotDotExpression;
40+
import com.semmle.js.ast.XMLFilterExpression;
41+
import com.semmle.js.ast.XMLQualifiedIdentifier;
3342
import com.semmle.util.data.Pair;
3443

3544
/**
@@ -155,6 +164,20 @@ protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) {
155164
List<Expression> args = this.parseExprList(TokenType.parenR, false, false, null);
156165
CallExpression node = new CallExpression(new SourceLocation(startLoc), name, new ArrayList<>(), args, false, false);
157166
return this.finishNode(node);
167+
} else if (options.e4x() && this.type == at) {
168+
// this could be either a decorator or an attribute selector; we first
169+
// try parsing it as a decorator, and then convert it to an attribute selector
170+
// if the next token turns out not to be `class`
171+
List<Decorator> decorators = parseDecorators();
172+
Expression attr = null;
173+
if (decorators.size() > 1 ||
174+
this.type == TokenType._class ||
175+
((attr = decoratorToAttributeSelector(decorators.get(0))) == null)) {
176+
ClassExpression ce = (ClassExpression) this.parseClass(startLoc, false);
177+
ce.addDecorators(decorators);
178+
return ce;
179+
}
180+
return attr;
158181
} else {
159182
return super.parseExprAtom(refDestructuringErrors);
160183
}
@@ -320,4 +343,170 @@ protected Expression parseNew() {
320343
}
321344
return res;
322345
}
346+
347+
/*
348+
* E4X
349+
*
350+
* PrimaryExpression :
351+
* PropertyIdentifier
352+
* XMLInitialiser
353+
* XMLListInitialiser
354+
*
355+
* PropertyIdentifier :
356+
* AttributeIdentifier
357+
* QualifiedIdentifier
358+
* WildcardIdent
359+
*
360+
* AttributeIdentifier :
361+
* @ PropertySelector
362+
* @ QualifiedIdentifier
363+
* @ [ Expression ]
364+
*
365+
* PropertySelector :
366+
* Identifier
367+
* WildcardIdentifier
368+
*
369+
* QualifiedIdentifier :
370+
* PropertySelector :: PropertySelector
371+
* PropertySelector :: [ Expression ]
372+
*
373+
* WildcardIdentifier :
374+
* *
375+
*
376+
* MemberExpression :
377+
* MemberExpression . PropertyIdentifier
378+
* MemberExpression .. Identifier
379+
* MemberExpression .. PropertyIdentifier
380+
* MemberExpression . ( Expression )
381+
*
382+
* DefaultXMLNamespaceStatement :
383+
* default xml namespace = Expression
384+
*/
385+
386+
protected TokenType doubleDot = new TokenType(new Properties(":").beforeExpr());
387+
388+
@Override
389+
protected Token getTokenFromCode(int code) {
390+
if (options.e4x() && code == '.' && charAt(this.pos+1) == '.' && charAt(this.pos+2) != '.') {
391+
this.pos += 2;
392+
return this.finishToken(doubleDot);
393+
}
394+
return super.getTokenFromCode(code);
395+
}
396+
397+
// add parsing of E4X property, attribute and descendant accesses, as well as filter expressions
398+
@Override
399+
protected Pair<Expression, Boolean> parseSubscript(Expression base, Position startLoc, boolean noCalls) {
400+
if (options.e4x() && this.eat(TokenType.dot)) {
401+
SourceLocation start = new SourceLocation(startLoc);
402+
if (this.eat(TokenType.parenL)) {
403+
Expression filter = parseExpression(false, null);
404+
this.expect(TokenType.parenR);
405+
return Pair.make(this.finishNode(new XMLFilterExpression(start, base, filter)), true);
406+
}
407+
408+
Expression property = this.parsePropertyIdentifierOrIdentifier();
409+
MemberExpression node = new MemberExpression(start, base, property, false, false, isOnOptionalChain(false, base));
410+
return Pair.make(this.finishNode(node), true);
411+
} else if (this.eat(doubleDot)) {
412+
SourceLocation start = new SourceLocation(startLoc);
413+
Expression property = this.parsePropertyIdentifierOrIdentifier();
414+
return Pair.make(this.finishNode(new XMLDotDotExpression(start, base, property)), true);
415+
}
416+
return super.parseSubscript(base, startLoc, noCalls);
417+
}
418+
419+
/**
420+
* Parse a an attribute identifier, a wildcard identifier, a qualified identifier,
421+
* or a plain identifier.
422+
*/
423+
protected Expression parsePropertyIdentifierOrIdentifier() {
424+
Position start = this.startLoc;
425+
if (this.eat(at)) {
426+
// attribute identifier
427+
return parseAttributeIdentifier(new SourceLocation(start));
428+
} else {
429+
return parsePossiblyQualifiedIdentifier();
430+
}
431+
}
432+
433+
/**
434+
* Parse a wildcard identifier, a qualified identifier, or a plain identifier.
435+
*/
436+
protected Expression parsePossiblyQualifiedIdentifier() {
437+
SourceLocation start = new SourceLocation(startLoc);
438+
Expression res = parsePropertySelector(start);
439+
440+
if (!this.eat(doubleColon))
441+
return res;
442+
443+
if (this.eat(TokenType.bracketL)) {
444+
Expression e = parseExpression(false, null);
445+
this.expect(TokenType.bracketR);
446+
return this.finishNode(new XMLQualifiedIdentifier(start, res, e, true));
447+
} else {
448+
Expression e = parsePropertySelector(new SourceLocation(startLoc));
449+
return this.finishNode(new XMLQualifiedIdentifier(start, res, e, false));
450+
}
451+
}
452+
453+
/**
454+
* Parse a property selector, that is, either a wildcard identifier or a plain identifier.
455+
*/
456+
protected Expression parsePropertySelector(SourceLocation start) {
457+
Expression res;
458+
if (this.eat(TokenType.star)) {
459+
// wildcard identifier
460+
res = this.finishNode(new XMLAnyName(start));
461+
} else {
462+
res = this.parseIdent(true);
463+
}
464+
return res;
465+
}
466+
467+
/**
468+
* Parse an attribute identifier, either computed ({@code [ Expr ]}) or a possibly
469+
* qualified identifier.
470+
*/
471+
protected Expression parseAttributeIdentifier(SourceLocation start) {
472+
if (this.eat(TokenType.bracketL)) {
473+
Expression idx = parseExpression(false, null);
474+
this.expect(TokenType.bracketR);
475+
return this.finishNode(new XMLAttributeSelector(start, idx, true));
476+
} else {
477+
return this.finishNode(new XMLAttributeSelector(start, parsePossiblyQualifiedIdentifier(), false));
478+
}
479+
}
480+
481+
@Override
482+
protected Expression parseDecoratorBody() {
483+
SourceLocation start = new SourceLocation(startLoc);
484+
if (options.e4x() && this.eat(TokenType.bracketL)) {
485+
// this must be an attribute selector, so only allow a single expression
486+
// followed by a right bracket, which will later be converted by
487+
// `decoratorToAttributeSelector` below
488+
List<Expression> elements = new ArrayList<>();
489+
elements.add(parseExpression(false, null));
490+
this.expect(TokenType.bracketR);
491+
return this.finishNode(new ArrayExpression(start, elements));
492+
}
493+
494+
return super.parseDecoratorBody();
495+
}
496+
497+
/**
498+
* Convert a decorator that resulted from mis-parsing an attribute selector into
499+
* an attribute selector.
500+
*/
501+
protected XMLAttributeSelector decoratorToAttributeSelector(Decorator d) {
502+
Expression e = d.getExpression();
503+
if (e instanceof ArrayExpression) {
504+
ArrayExpression ae = (ArrayExpression) e;
505+
if (ae.getElements().size() == 1)
506+
return new XMLAttributeSelector(d.getLoc(), ae.getElements().get(0), true);
507+
} else if (e instanceof Identifier) {
508+
return new XMLAttributeSelector(d.getLoc(), e, false);
509+
}
510+
return null;
511+
}
323512
}

javascript/extractor/src/com/semmle/jcorn/Options.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface OnCommentCallback {
3232
}
3333

3434
private boolean allowHashBang, allowReturnOutsideFunction, allowImportExportEverywhere;
35-
private boolean preserveParens, mozExtensions, jscript, esnext, v8Extensions;
35+
private boolean preserveParens, mozExtensions, jscript, esnext, v8Extensions, e4x;
3636
private int ecmaVersion;
3737
private AllowReserved allowReserved;
3838
private String sourceType;
@@ -59,6 +59,7 @@ public Options() {
5959
this.jscript = false;
6060
this.esnext = false;
6161
this.v8Extensions = false;
62+
this.e4x = false;
6263
this.onRecoverableError = null;
6364
}
6465

@@ -71,6 +72,7 @@ public Options(Options that) {
7172
this.jscript = that.jscript;
7273
this.esnext = that.esnext;
7374
this.v8Extensions = that.v8Extensions;
75+
this.e4x = that.e4x;
7476
this.ecmaVersion = that.ecmaVersion;
7577
this.allowReserved = that.allowReserved;
7678
this.sourceType = that.sourceType;
@@ -114,6 +116,10 @@ public boolean v8Extensions() {
114116
return v8Extensions;
115117
}
116118

119+
public boolean e4x() {
120+
return e4x;
121+
}
122+
117123
public Identifiers.Dialect getDialect() {
118124
switch (ecmaVersion) {
119125
case 3:
@@ -183,6 +189,10 @@ public void v8Extensions(boolean v8Extensions) {
183189
this.v8Extensions = v8Extensions;
184190
}
185191

192+
public void e4x(boolean e4x) {
193+
this.e4x = e4x;
194+
}
195+
186196
public Options preserveParens(boolean preserveParens) {
187197
this.preserveParens = preserveParens;
188198
return this;

javascript/extractor/src/com/semmle/jcorn/Parser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1478,7 +1478,7 @@ protected Expression parseSubscripts(Expression base, int startPos, Position sta
14781478
}
14791479
}
14801480

1481-
private boolean isOnOptionalChain(boolean optional, Expression base) {
1481+
protected boolean isOnOptionalChain(boolean optional, Expression base) {
14821482
return optional || base instanceof Chainable && ((Chainable)base).isOnOptionalChain();
14831483
}
14841484

javascript/extractor/src/com/semmle/js/ast/DefaultVisitor.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,4 +734,29 @@ public R visit(OptionalTypeExpr nd, C c) {
734734
public R visit(RestTypeExpr nd, C c) {
735735
return visit((TypeExpression) nd, c);
736736
}
737+
738+
@Override
739+
public R visit(XMLAnyName nd, C c) {
740+
return visit((Expression) nd, c);
741+
}
742+
743+
@Override
744+
public R visit(XMLAttributeSelector nd, C c) {
745+
return visit((Expression) nd, c);
746+
}
747+
748+
@Override
749+
public R visit(XMLFilterExpression nd, C c) {
750+
return visit((Expression) nd, c);
751+
}
752+
753+
@Override
754+
public R visit(XMLQualifiedIdentifier nd, C c) {
755+
return visit((Expression) nd, c);
756+
}
757+
758+
@Override
759+
public R visit(XMLDotDotExpression nd, C c) {
760+
return visit((Expression) nd, c);
761+
}
737762
}

javascript/extractor/src/com/semmle/js/ast/MemberDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public abstract class MemberDefinition<V extends Expression> extends Node {
1818
/**
1919
* The name of the member.
2020
*
21-
* If {@link #isComputed} is false, this must be an {@link Identifier}, otherwise
21+
* If {@link #isComputed()} is false, this must be an {@link Identifier}, otherwise
2222
* it can be an arbitrary expression.
2323
*/
2424
private final Expression key;

javascript/extractor/src/com/semmle/js/ast/NodeCopier.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,4 +704,29 @@ public INode visit(OptionalTypeExpr nd, Void c) {
704704
public INode visit(RestTypeExpr nd, Void c) {
705705
return new RestTypeExpr(visit(nd.getLoc()), copy(nd.getArrayType()));
706706
}
707+
708+
@Override
709+
public INode visit(XMLAnyName nd, Void c) {
710+
return new XMLAnyName(visit(nd.getLoc()));
711+
}
712+
713+
@Override
714+
public INode visit(XMLAttributeSelector nd, Void c) {
715+
return new XMLAttributeSelector(visit(nd.getLoc()), copy(nd.getAttribute()), nd.isComputed());
716+
}
717+
718+
@Override
719+
public INode visit(XMLFilterExpression nd, Void c) {
720+
return new XMLFilterExpression(visit(nd.getLoc()), copy(nd.getLeft()), copy(nd.getRight()));
721+
}
722+
723+
@Override
724+
public INode visit(XMLQualifiedIdentifier nd, Void c) {
725+
return new XMLQualifiedIdentifier(visit(nd.getLoc()), copy(nd.getLeft()), copy(nd.getRight()), nd.isComputed());
726+
}
727+
728+
@Override
729+
public INode visit(XMLDotDotExpression nd, Void c) {
730+
return new XMLDotDotExpression(visit(nd.getLoc()), copy(nd.getLeft()), copy(nd.getRight()));
731+
}
707732
}

javascript/extractor/src/com/semmle/js/ast/Visitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,9 @@ public interface Visitor<C, R> {
174174
public R visit(ImportTypeExpr nd, C c);
175175
public R visit(OptionalTypeExpr nd, C c);
176176
public R visit(RestTypeExpr nd, C c);
177+
public R visit(XMLAnyName nd, C c);
178+
public R visit(XMLAttributeSelector nd, C c);
179+
public R visit(XMLFilterExpression nd, C c);
180+
public R visit(XMLQualifiedIdentifier nd, C c);
181+
public R visit(XMLDotDotExpression nd, C c);
177182
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.semmle.js.ast;
2+
3+
public class XMLAnyName extends Expression {
4+
public XMLAnyName(SourceLocation loc) {
5+
super("XMLAnyName", loc);
6+
}
7+
8+
@Override
9+
public <C, R> R accept(Visitor<C, R> v, C c) {
10+
return v.visit(this, c);
11+
}
12+
13+
}

0 commit comments

Comments
 (0)