JS: Parse mustache-style tags as expressions

This commit is contained in:
Asger Feldthaus
2021-07-17 14:00:55 +02:00
parent d1c31db06f
commit a7cdf532fa
15 changed files with 867 additions and 4 deletions

View File

@@ -39,7 +39,7 @@ public class Options {
Position endLoc);
}
private boolean allowHashBang, allowReturnOutsideFunction, allowImportExportEverywhere;
private boolean allowHashBang, allowReturnOutsideFunction, allowImportExportEverywhere, allowGeneratedCodeExprs;
private boolean preserveParens, mozExtensions, jscript, esnext, v8Extensions, e4x;
private int ecmaVersion;
private AllowReserved allowReserved;
@@ -58,6 +58,7 @@ public class Options {
this.allowReserved = AllowReserved.YES;
this.allowReturnOutsideFunction = false;
this.allowImportExportEverywhere = false;
this.allowGeneratedCodeExprs = true;
this.allowHashBang = false;
this.onToken = null;
this.onComment = null;
@@ -75,6 +76,7 @@ public class Options {
this.allowHashBang = that.allowHashBang;
this.allowReturnOutsideFunction = that.allowReturnOutsideFunction;
this.allowImportExportEverywhere = that.allowImportExportEverywhere;
this.allowGeneratedCodeExprs = that.allowGeneratedCodeExprs;
this.preserveParens = that.preserveParens;
this.mozExtensions = that.mozExtensions;
this.jscript = that.jscript;
@@ -104,6 +106,10 @@ public class Options {
return allowImportExportEverywhere;
}
public boolean allowGeneratedCodeExprs() {
return allowGeneratedCodeExprs;
}
public boolean preserveParens() {
return preserveParens;
}

View File

@@ -52,6 +52,7 @@ import com.semmle.js.ast.ForOfStatement;
import com.semmle.js.ast.ForStatement;
import com.semmle.js.ast.FunctionDeclaration;
import com.semmle.js.ast.FunctionExpression;
import com.semmle.js.ast.GeneratedCodeExpr;
import com.semmle.js.ast.IFunction;
import com.semmle.js.ast.INode;
import com.semmle.js.ast.IPattern;
@@ -537,7 +538,7 @@ public class Parser {
}
return this.finishOp(TokenType.questionquestion, 2);
}
}
return this.finishOp(TokenType.question, 1);
}
@@ -1929,10 +1930,16 @@ public class Parser {
// Parse an object literal or binding pattern.
protected Expression parseObj(boolean isPattern, DestructuringErrors refDestructuringErrors) {
Position startLoc = this.startLoc;
if (!isPattern && options.allowGeneratedCodeExprs() && charAt(pos) == '{') {
// Parse mustache-style placeholder expression: {{ ... }} or {{{ ... }}}
return charAt(pos + 1) == '{'
? parseGeneratedCodeExpr(startLoc, "{{{", "}}}")
: parseGeneratedCodeExpr(startLoc, "{{", "}}");
}
boolean first = true;
Map<String, PropInfo> propHash = new LinkedHashMap<>();
List<Property> properties = new ArrayList<Property>();
this.next();
this.next(); // skip '{'
while (!this.eat(TokenType.braceR)) {
if (!first) {
this.expect(TokenType.comma);
@@ -1949,6 +1956,42 @@ public class Parser {
return this.finishNode(node);
}
/** Emit a token ranging from the current position until <code>endOfToken</code>. */
private Token generateTokenEndingAt(int endOfToken, TokenType tokenType) {
this.lastTokEnd = this.end;
this.lastTokStart = this.start;
this.lastTokEndLoc = this.endLoc;
this.lastTokStartLoc = this.startLoc;
this.start = this.pos;
this.startLoc = this.curPosition();
this.pos = endOfToken;
return finishToken(tokenType);
}
/** Parse a generated expression. The current token refers to the opening delimiter. */
protected Expression parseGeneratedCodeExpr(Position startLoc, String openingDelimiter, String closingDelimiter) {
// Emit a token for what's left of the opening delimiter, if there are any remaining characters
int startOfBody = startLoc.getOffset() + openingDelimiter.length();
if (this.pos != startOfBody) {
this.generateTokenEndingAt(startOfBody, TokenType.generatedCodeDelimiter);
}
// Emit a token for the generated code body
int endOfBody = this.input.indexOf(closingDelimiter, startOfBody);
if (endOfBody == -1) {
this.unexpected(startLoc);
}
Token bodyToken = this.generateTokenEndingAt(endOfBody, TokenType.generatedCodeExpr);
// Emit a token for the closing delimiter
this.generateTokenEndingAt(endOfBody + closingDelimiter.length(), TokenType.generatedCodeDelimiter);
this.next(); // produce lookahead token
return finishNode(new GeneratedCodeExpr(new SourceLocation(startLoc), openingDelimiter, closingDelimiter,
bodyToken.getValue()));
}
protected Property parseProperty(
boolean isPattern,
DestructuringErrors refDestructuringErrors,

View File

@@ -89,6 +89,8 @@ public class TokenType {
arrow = new TokenType(new Properties("=>").beforeExpr()),
template = new TokenType(new Properties("template")),
invalidTemplate = new TokenType(new Properties("invalidTemplate")),
generatedCodeExpr = new TokenType(new Properties("generatedCodeExpr")),
generatedCodeDelimiter = new TokenType(new Properties("generatedCodeDelimiter")),
ellipsis = new TokenType(new Properties("...").beforeExpr()),
backQuote =
new TokenType(new Properties("`").startsExpr()) {

View File

@@ -777,4 +777,9 @@ public class DefaultVisitor<C, R> implements Visitor<C, R> {
public R visit(XMLDotDotExpression nd, C c) {
return visit((Expression) nd, c);
}
@Override
public R visit(GeneratedCodeExpr nd, C c) {
return visit((Expression) nd, c);
}
}

View File

@@ -0,0 +1,45 @@
package com.semmle.js.ast;
/**
* A placeholder for generated code, speculatively parsed as a primary expression.
*
* <p>For example, in this snippet,
*
* <pre>
* let data = {{user_data}};
* </pre>
*
* the expression <code>{{user_data}}</code> is assumed to be filled in by a templating engine so
* that it can be parsed as an expression, and a <code>GeneratedCodeExpr</code> is thus created to
* represent it.
*/
public class GeneratedCodeExpr extends Expression {
private String openingDelimiter;
private String closingDelimiter;
private String body;
public GeneratedCodeExpr(
SourceLocation loc, String openingDelimiter, String closingDelimiter, String body) {
super("GeneratedCodeExpr", loc);
this.openingDelimiter = openingDelimiter;
this.closingDelimiter = closingDelimiter;
this.body = body;
}
public String getOpeningDelimiter() {
return openingDelimiter;
}
public String getClosingDelimiter() {
return closingDelimiter;
}
public String getBody() {
return body;
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);
}
}

View File

@@ -894,4 +894,9 @@ public class NodeCopier implements Visitor<Void, INode> {
public INode visit(XMLDotDotExpression nd, Void c) {
return new XMLDotDotExpression(visit(nd.getLoc()), copy(nd.getLeft()), copy(nd.getRight()));
}
@Override
public INode visit(GeneratedCodeExpr nd, Void c) {
return new GeneratedCodeExpr(visit(nd.getLoc()), nd.getOpeningDelimiter(), nd.getClosingDelimiter(), nd.getBody());
}
}

View File

@@ -313,4 +313,6 @@ public interface Visitor<C, R> {
public R visit(XMLQualifiedIdentifier nd, C c);
public R visit(XMLDotDotExpression nd, C c);
public R visit(GeneratedCodeExpr generatedCodeExpr, C c);
}

View File

@@ -45,6 +45,7 @@ import com.semmle.js.ast.ForOfStatement;
import com.semmle.js.ast.ForStatement;
import com.semmle.js.ast.FunctionDeclaration;
import com.semmle.js.ast.FunctionExpression;
import com.semmle.js.ast.GeneratedCodeExpr;
import com.semmle.js.ast.IFunction;
import com.semmle.js.ast.INode;
import com.semmle.js.ast.IPattern;
@@ -1162,7 +1163,7 @@ public class ASTExtractor {
if (!nd.isComputed() && "template".equals(tryGetIdentifierName(nd.getKey()))) {
extractStringValueAsHtml(nd.getValue(), valueLabel);
}
return propkey;
}
@@ -2220,6 +2221,13 @@ public class ASTExtractor {
visit(nd.getIdentifier(), key, 0, IdContext.LABEL);
return key;
}
@Override
public Label visit(GeneratedCodeExpr nd, Context c) {
Label key = super.visit(nd, c);
trapwriter.addTuple("generated_code_expr_info", key, nd.getOpeningDelimiter(), nd.getClosingDelimiter(), nd.getBody());
return key;
}
}
public List<ParseError> extract(

View File

@@ -149,6 +149,7 @@ public class ExprKinds {
exprKinds.put("ExternalModuleReference", 98);
exprKinds.put("NonNullAssertion", 105);
exprKinds.put("AngularPipeRef", 119);
exprKinds.put("GeneratedCodeExpr", 120);
}
private static final Map<IdContext, Integer> idKinds =

View File

@@ -0,0 +1,9 @@
let data1 = {{ user_data1 }};
let data2 = {{{ user_data2 }}};
if ({{something}}) {}
foo({{bar}}, {{baz}});
{{not_generated_code}} // parse as block
{{ if (not_generated_code) { } }} // parse as block
if (1 == 2) {{not_generated_code}} // parse as block
let string = "{{ not_generated_code }}"; // parse as string literal

View File

@@ -0,0 +1,3 @@
{
"experimental": true
}

View File

@@ -0,0 +1,686 @@
#10000=@"/tst.js;sourcefile"
files(#10000,"/tst.js","tst","js",0)
#10001=@"/;folder"
folders(#10001,"/","")
containerparent(#10001,#10000)
#10002=@"loc,{#10000},0,0,0,0"
locations_default(#10002,#10000,0,0,0,0)
hasLocation(#10000,#10002)
#20000=@"global_scope"
scopes(#20000,0)
#20001=@"script;{#10000},1,1"
#20002=*
comments(#20002,0,#20001," parse as block","// parse as block")
#20003=@"loc,{#10000},6,24,6,40"
locations_default(#20003,#10000,6,24,6,40)
hasLocation(#20002,#20003)
#20004=*
comments(#20004,0,#20001," parse as block","// parse as block")
#20005=@"loc,{#10000},7,35,7,51"
locations_default(#20005,#10000,7,35,7,51)
hasLocation(#20004,#20005)
#20006=*
comments(#20006,0,#20001," parse as block","// parse as block")
#20007=@"loc,{#10000},8,36,8,52"
locations_default(#20007,#10000,8,36,8,52)
hasLocation(#20006,#20007)
#20008=*
comments(#20008,0,#20001," parse as string literal","// pars ... literal")
#20009=@"loc,{#10000},9,42,9,67"
locations_default(#20009,#10000,9,42,9,67)
hasLocation(#20008,#20009)
#20010=*
lines(#20010,#20001,"let data1 = {{ user_data1 }};","
")
#20011=@"loc,{#10000},1,1,1,29"
locations_default(#20011,#10000,1,1,1,29)
hasLocation(#20010,#20011)
#20012=*
lines(#20012,#20001,"let data2 = {{{ user_data2 }}};","
")
#20013=@"loc,{#10000},2,1,2,31"
locations_default(#20013,#10000,2,1,2,31)
hasLocation(#20012,#20013)
#20014=*
lines(#20014,#20001,"if ({{something}}) {}","
")
#20015=@"loc,{#10000},3,1,3,21"
locations_default(#20015,#10000,3,1,3,21)
hasLocation(#20014,#20015)
#20016=*
lines(#20016,#20001,"foo({{bar}}, {{baz}});","
")
#20017=@"loc,{#10000},4,1,4,22"
locations_default(#20017,#10000,4,1,4,22)
hasLocation(#20016,#20017)
#20018=*
lines(#20018,#20001,"","
")
#20019=@"loc,{#10000},5,1,5,0"
locations_default(#20019,#10000,5,1,5,0)
hasLocation(#20018,#20019)
#20020=*
lines(#20020,#20001,"{{not_generated_code}} // parse as block","
")
#20021=@"loc,{#10000},6,1,6,40"
locations_default(#20021,#10000,6,1,6,40)
hasLocation(#20020,#20021)
#20022=*
lines(#20022,#20001,"{{ if (not_generated_code) { } }} // parse as block","
")
#20023=@"loc,{#10000},7,1,7,51"
locations_default(#20023,#10000,7,1,7,51)
hasLocation(#20022,#20023)
#20024=*
lines(#20024,#20001,"if (1 == 2) {{not_generated_code}} // parse as block","
")
#20025=@"loc,{#10000},8,1,8,52"
locations_default(#20025,#10000,8,1,8,52)
hasLocation(#20024,#20025)
#20026=*
lines(#20026,#20001,"let string = ""{{ not_generated_code }}""; // parse as string literal","
")
#20027=@"loc,{#10000},9,1,9,67"
locations_default(#20027,#10000,9,1,9,67)
hasLocation(#20026,#20027)
numlines(#20001,9,8,4)
#20028=*
tokeninfo(#20028,7,#20001,0,"let")
#20029=@"loc,{#10000},1,1,1,3"
locations_default(#20029,#10000,1,1,1,3)
hasLocation(#20028,#20029)
#20030=*
tokeninfo(#20030,6,#20001,1,"data1")
#20031=@"loc,{#10000},1,5,1,9"
locations_default(#20031,#10000,1,5,1,9)
hasLocation(#20030,#20031)
#20032=*
tokeninfo(#20032,8,#20001,2,"=")
#20033=@"loc,{#10000},1,11,1,11"
locations_default(#20033,#10000,1,11,1,11)
hasLocation(#20032,#20033)
#20034=*
tokeninfo(#20034,8,#20001,3,"}}")
#20035=@"loc,{#10000},1,27,1,28"
locations_default(#20035,#10000,1,27,1,28)
hasLocation(#20034,#20035)
#20036=*
tokeninfo(#20036,8,#20001,4,";")
#20037=@"loc,{#10000},1,29,1,29"
locations_default(#20037,#10000,1,29,1,29)
hasLocation(#20036,#20037)
#20038=*
tokeninfo(#20038,7,#20001,5,"let")
#20039=@"loc,{#10000},2,1,2,3"
locations_default(#20039,#10000,2,1,2,3)
hasLocation(#20038,#20039)
#20040=*
tokeninfo(#20040,6,#20001,6,"data2")
#20041=@"loc,{#10000},2,5,2,9"
locations_default(#20041,#10000,2,5,2,9)
hasLocation(#20040,#20041)
#20042=*
tokeninfo(#20042,8,#20001,7,"=")
#20043=@"loc,{#10000},2,11,2,11"
locations_default(#20043,#10000,2,11,2,11)
hasLocation(#20042,#20043)
#20044=*
tokeninfo(#20044,8,#20001,8,"}}}")
#20045=@"loc,{#10000},2,28,2,30"
locations_default(#20045,#10000,2,28,2,30)
hasLocation(#20044,#20045)
#20046=*
tokeninfo(#20046,8,#20001,9,";")
#20047=@"loc,{#10000},2,31,2,31"
locations_default(#20047,#10000,2,31,2,31)
hasLocation(#20046,#20047)
#20048=*
tokeninfo(#20048,7,#20001,10,"if")
#20049=@"loc,{#10000},3,1,3,2"
locations_default(#20049,#10000,3,1,3,2)
hasLocation(#20048,#20049)
#20050=*
tokeninfo(#20050,8,#20001,11,"(")
#20051=@"loc,{#10000},3,4,3,4"
locations_default(#20051,#10000,3,4,3,4)
hasLocation(#20050,#20051)
#20052=*
tokeninfo(#20052,8,#20001,12,"}}")
#20053=@"loc,{#10000},3,16,3,17"
locations_default(#20053,#10000,3,16,3,17)
hasLocation(#20052,#20053)
#20054=*
tokeninfo(#20054,8,#20001,13,")")
#20055=@"loc,{#10000},3,18,3,18"
locations_default(#20055,#10000,3,18,3,18)
hasLocation(#20054,#20055)
#20056=*
tokeninfo(#20056,8,#20001,14,"{")
#20057=@"loc,{#10000},3,20,3,20"
locations_default(#20057,#10000,3,20,3,20)
hasLocation(#20056,#20057)
#20058=*
tokeninfo(#20058,8,#20001,15,"}")
#20059=@"loc,{#10000},3,21,3,21"
locations_default(#20059,#10000,3,21,3,21)
hasLocation(#20058,#20059)
#20060=*
tokeninfo(#20060,6,#20001,16,"foo")
#20061=@"loc,{#10000},4,1,4,3"
locations_default(#20061,#10000,4,1,4,3)
hasLocation(#20060,#20061)
#20062=*
tokeninfo(#20062,8,#20001,17,"(")
#20063=@"loc,{#10000},4,4,4,4"
locations_default(#20063,#10000,4,4,4,4)
hasLocation(#20062,#20063)
#20064=*
tokeninfo(#20064,8,#20001,18,"}}")
#20065=@"loc,{#10000},4,10,4,11"
locations_default(#20065,#10000,4,10,4,11)
hasLocation(#20064,#20065)
#20066=*
tokeninfo(#20066,8,#20001,19,",")
#20067=@"loc,{#10000},4,12,4,12"
locations_default(#20067,#10000,4,12,4,12)
hasLocation(#20066,#20067)
#20068=*
tokeninfo(#20068,8,#20001,20,"}}")
#20069=@"loc,{#10000},4,19,4,20"
locations_default(#20069,#10000,4,19,4,20)
hasLocation(#20068,#20069)
#20070=*
tokeninfo(#20070,8,#20001,21,")")
#20071=@"loc,{#10000},4,21,4,21"
locations_default(#20071,#10000,4,21,4,21)
hasLocation(#20070,#20071)
#20072=*
tokeninfo(#20072,8,#20001,22,";")
#20073=@"loc,{#10000},4,22,4,22"
locations_default(#20073,#10000,4,22,4,22)
hasLocation(#20072,#20073)
#20074=*
tokeninfo(#20074,8,#20001,23,"{")
#20075=@"loc,{#10000},6,1,6,1"
locations_default(#20075,#10000,6,1,6,1)
hasLocation(#20074,#20075)
#20076=*
tokeninfo(#20076,8,#20001,24,"{")
#20077=@"loc,{#10000},6,2,6,2"
locations_default(#20077,#10000,6,2,6,2)
hasLocation(#20076,#20077)
#20078=*
tokeninfo(#20078,6,#20001,25,"not_generated_code")
#20079=@"loc,{#10000},6,3,6,20"
locations_default(#20079,#10000,6,3,6,20)
hasLocation(#20078,#20079)
#20080=*
tokeninfo(#20080,8,#20001,26,"}")
#20081=@"loc,{#10000},6,21,6,21"
locations_default(#20081,#10000,6,21,6,21)
hasLocation(#20080,#20081)
#20082=*
tokeninfo(#20082,8,#20001,27,"}")
#20083=@"loc,{#10000},6,22,6,22"
locations_default(#20083,#10000,6,22,6,22)
hasLocation(#20082,#20083)
#20084=*
tokeninfo(#20084,8,#20001,28,"{")
#20085=@"loc,{#10000},7,1,7,1"
locations_default(#20085,#10000,7,1,7,1)
hasLocation(#20084,#20085)
next_token(#20002,#20084)
#20086=*
tokeninfo(#20086,8,#20001,29,"{")
#20087=@"loc,{#10000},7,2,7,2"
locations_default(#20087,#10000,7,2,7,2)
hasLocation(#20086,#20087)
#20088=*
tokeninfo(#20088,7,#20001,30,"if")
#20089=@"loc,{#10000},7,4,7,5"
locations_default(#20089,#10000,7,4,7,5)
hasLocation(#20088,#20089)
#20090=*
tokeninfo(#20090,8,#20001,31,"(")
#20091=@"loc,{#10000},7,7,7,7"
locations_default(#20091,#10000,7,7,7,7)
hasLocation(#20090,#20091)
#20092=*
tokeninfo(#20092,6,#20001,32,"not_generated_code")
#20093=@"loc,{#10000},7,8,7,25"
locations_default(#20093,#10000,7,8,7,25)
hasLocation(#20092,#20093)
#20094=*
tokeninfo(#20094,8,#20001,33,")")
#20095=@"loc,{#10000},7,26,7,26"
locations_default(#20095,#10000,7,26,7,26)
hasLocation(#20094,#20095)
#20096=*
tokeninfo(#20096,8,#20001,34,"{")
#20097=@"loc,{#10000},7,28,7,28"
locations_default(#20097,#10000,7,28,7,28)
hasLocation(#20096,#20097)
#20098=*
tokeninfo(#20098,8,#20001,35,"}")
#20099=@"loc,{#10000},7,30,7,30"
locations_default(#20099,#10000,7,30,7,30)
hasLocation(#20098,#20099)
#20100=*
tokeninfo(#20100,8,#20001,36,"}")
#20101=@"loc,{#10000},7,32,7,32"
locations_default(#20101,#10000,7,32,7,32)
hasLocation(#20100,#20101)
#20102=*
tokeninfo(#20102,8,#20001,37,"}")
#20103=@"loc,{#10000},7,33,7,33"
locations_default(#20103,#10000,7,33,7,33)
hasLocation(#20102,#20103)
#20104=*
tokeninfo(#20104,7,#20001,38,"if")
#20105=@"loc,{#10000},8,1,8,2"
locations_default(#20105,#10000,8,1,8,2)
hasLocation(#20104,#20105)
next_token(#20004,#20104)
#20106=*
tokeninfo(#20106,8,#20001,39,"(")
#20107=@"loc,{#10000},8,4,8,4"
locations_default(#20107,#10000,8,4,8,4)
hasLocation(#20106,#20107)
#20108=*
tokeninfo(#20108,3,#20001,40,"1")
#20109=@"loc,{#10000},8,5,8,5"
locations_default(#20109,#10000,8,5,8,5)
hasLocation(#20108,#20109)
#20110=*
tokeninfo(#20110,8,#20001,41,"==")
#20111=@"loc,{#10000},8,7,8,8"
locations_default(#20111,#10000,8,7,8,8)
hasLocation(#20110,#20111)
#20112=*
tokeninfo(#20112,3,#20001,42,"2")
#20113=@"loc,{#10000},8,10,8,10"
locations_default(#20113,#10000,8,10,8,10)
hasLocation(#20112,#20113)
#20114=*
tokeninfo(#20114,8,#20001,43,")")
#20115=@"loc,{#10000},8,11,8,11"
locations_default(#20115,#10000,8,11,8,11)
hasLocation(#20114,#20115)
#20116=*
tokeninfo(#20116,8,#20001,44,"{")
#20117=@"loc,{#10000},8,13,8,13"
locations_default(#20117,#10000,8,13,8,13)
hasLocation(#20116,#20117)
#20118=*
tokeninfo(#20118,8,#20001,45,"{")
#20119=@"loc,{#10000},8,14,8,14"
locations_default(#20119,#10000,8,14,8,14)
hasLocation(#20118,#20119)
#20120=*
tokeninfo(#20120,6,#20001,46,"not_generated_code")
#20121=@"loc,{#10000},8,15,8,32"
locations_default(#20121,#10000,8,15,8,32)
hasLocation(#20120,#20121)
#20122=*
tokeninfo(#20122,8,#20001,47,"}")
#20123=@"loc,{#10000},8,33,8,33"
locations_default(#20123,#10000,8,33,8,33)
hasLocation(#20122,#20123)
#20124=*
tokeninfo(#20124,8,#20001,48,"}")
#20125=@"loc,{#10000},8,34,8,34"
locations_default(#20125,#10000,8,34,8,34)
hasLocation(#20124,#20125)
#20126=*
tokeninfo(#20126,7,#20001,49,"let")
#20127=@"loc,{#10000},9,1,9,3"
locations_default(#20127,#10000,9,1,9,3)
hasLocation(#20126,#20127)
next_token(#20006,#20126)
#20128=*
tokeninfo(#20128,6,#20001,50,"string")
#20129=@"loc,{#10000},9,5,9,10"
locations_default(#20129,#10000,9,5,9,10)
hasLocation(#20128,#20129)
#20130=*
tokeninfo(#20130,8,#20001,51,"=")
#20131=@"loc,{#10000},9,12,9,12"
locations_default(#20131,#10000,9,12,9,12)
hasLocation(#20130,#20131)
#20132=*
tokeninfo(#20132,4,#20001,52,"""{{ not_generated_code }}""")
#20133=@"loc,{#10000},9,14,9,39"
locations_default(#20133,#10000,9,14,9,39)
hasLocation(#20132,#20133)
#20134=*
tokeninfo(#20134,8,#20001,53,";")
#20135=@"loc,{#10000},9,40,9,40"
locations_default(#20135,#10000,9,40,9,40)
hasLocation(#20134,#20135)
#20136=*
tokeninfo(#20136,0,#20001,54,"")
#20137=@"loc,{#10000},10,1,10,0"
locations_default(#20137,#10000,10,1,10,0)
hasLocation(#20136,#20137)
next_token(#20008,#20136)
toplevels(#20001,0)
#20138=@"loc,{#10000},1,1,10,0"
locations_default(#20138,#10000,1,1,10,0)
hasLocation(#20001,#20138)
#20139=@"var;{data1};{#20000}"
variables(#20139,"data1",#20000)
#20140=@"var;{data2};{#20000}"
variables(#20140,"data2",#20000)
#20141=@"var;{string};{#20000}"
variables(#20141,"string",#20000)
#20142=*
stmts(#20142,23,#20001,0,"let dat ... ta1 }};")
hasLocation(#20142,#20011)
stmt_containers(#20142,#20001)
#20143=*
exprs(#20143,64,#20142,0,"data1 = ... ata1 }}")
#20144=@"loc,{#10000},1,5,1,28"
locations_default(#20144,#10000,1,5,1,28)
hasLocation(#20143,#20144)
enclosing_stmt(#20143,#20142)
expr_containers(#20143,#20001)
#20145=*
exprs(#20145,78,#20143,0,"data1")
hasLocation(#20145,#20031)
enclosing_stmt(#20145,#20142)
expr_containers(#20145,#20001)
literals("data1","data1",#20145)
decl(#20145,#20139)
#20146=*
exprs(#20146,120,#20143,1,"{{ user_data1 }}")
#20147=@"loc,{#10000},1,13,1,28"
locations_default(#20147,#10000,1,13,1,28)
hasLocation(#20146,#20147)
enclosing_stmt(#20146,#20142)
expr_containers(#20146,#20001)
generated_code_expr_info(#20146,"{{","}}"," user_data1 ")
#20148=*
stmts(#20148,23,#20001,1,"let dat ... a2 }}};")
hasLocation(#20148,#20013)
stmt_containers(#20148,#20001)
#20149=*
exprs(#20149,64,#20148,0,"data2 = ... ta2 }}}")
#20150=@"loc,{#10000},2,5,2,30"
locations_default(#20150,#10000,2,5,2,30)
hasLocation(#20149,#20150)
enclosing_stmt(#20149,#20148)
expr_containers(#20149,#20001)
#20151=*
exprs(#20151,78,#20149,0,"data2")
hasLocation(#20151,#20041)
enclosing_stmt(#20151,#20148)
expr_containers(#20151,#20001)
literals("data2","data2",#20151)
decl(#20151,#20140)
#20152=*
exprs(#20152,120,#20149,1,"{{{ user_data2 }}}")
#20153=@"loc,{#10000},2,13,2,30"
locations_default(#20153,#10000,2,13,2,30)
hasLocation(#20152,#20153)
enclosing_stmt(#20152,#20148)
expr_containers(#20152,#20001)
generated_code_expr_info(#20152,"{{{","}}}"," user_data2 ")
#20154=*
stmts(#20154,3,#20001,2,"if ({{s ... g}}) {}")
hasLocation(#20154,#20015)
stmt_containers(#20154,#20001)
#20155=*
exprs(#20155,120,#20154,0,"{{something}}")
#20156=@"loc,{#10000},3,5,3,17"
locations_default(#20156,#10000,3,5,3,17)
hasLocation(#20155,#20156)
enclosing_stmt(#20155,#20154)
expr_containers(#20155,#20001)
generated_code_expr_info(#20155,"{{","}}","something")
#20157=*
stmts(#20157,1,#20154,1,"{}")
#20158=@"loc,{#10000},3,20,3,21"
locations_default(#20158,#10000,3,20,3,21)
hasLocation(#20157,#20158)
stmt_containers(#20157,#20001)
#20159=*
stmts(#20159,2,#20001,3,"foo({{b ... baz}});")
hasLocation(#20159,#20017)
stmt_containers(#20159,#20001)
#20160=*
exprs(#20160,13,#20159,0,"foo({{b ... {baz}})")
#20161=@"loc,{#10000},4,1,4,21"
locations_default(#20161,#10000,4,1,4,21)
hasLocation(#20160,#20161)
enclosing_stmt(#20160,#20159)
expr_containers(#20160,#20001)
#20162=*
exprs(#20162,79,#20160,-1,"foo")
hasLocation(#20162,#20061)
enclosing_stmt(#20162,#20159)
expr_containers(#20162,#20001)
literals("foo","foo",#20162)
#20163=@"var;{foo};{#20000}"
variables(#20163,"foo",#20000)
bind(#20162,#20163)
#20164=*
exprs(#20164,120,#20160,0,"{{bar}}")
#20165=@"loc,{#10000},4,5,4,11"
locations_default(#20165,#10000,4,5,4,11)
hasLocation(#20164,#20165)
enclosing_stmt(#20164,#20159)
expr_containers(#20164,#20001)
generated_code_expr_info(#20164,"{{","}}","bar")
#20166=*
exprs(#20166,120,#20160,1,"{{baz}}")
#20167=@"loc,{#10000},4,14,4,20"
locations_default(#20167,#10000,4,14,4,20)
hasLocation(#20166,#20167)
enclosing_stmt(#20166,#20159)
expr_containers(#20166,#20001)
generated_code_expr_info(#20166,"{{","}}","baz")
#20168=*
stmts(#20168,1,#20001,4,"{{not_g ... _code}}")
#20169=@"loc,{#10000},6,1,6,22"
locations_default(#20169,#10000,6,1,6,22)
hasLocation(#20168,#20169)
stmt_containers(#20168,#20001)
#20170=*
stmts(#20170,1,#20168,0,"{not_generated_code}")
#20171=@"loc,{#10000},6,2,6,21"
locations_default(#20171,#10000,6,2,6,21)
hasLocation(#20170,#20171)
stmt_containers(#20170,#20001)
#20172=*
stmts(#20172,2,#20170,0,"not_generated_code")
hasLocation(#20172,#20079)
stmt_containers(#20172,#20001)
#20173=*
exprs(#20173,79,#20172,0,"not_generated_code")
hasLocation(#20173,#20079)
enclosing_stmt(#20173,#20172)
expr_containers(#20173,#20001)
literals("not_generated_code","not_generated_code",#20173)
#20174=@"var;{not_generated_code};{#20000}"
variables(#20174,"not_generated_code",#20000)
bind(#20173,#20174)
#20175=*
stmts(#20175,1,#20001,5,"{{ if ( ... { } }}")
#20176=@"loc,{#10000},7,1,7,33"
locations_default(#20176,#10000,7,1,7,33)
hasLocation(#20175,#20176)
stmt_containers(#20175,#20001)
#20177=*
stmts(#20177,1,#20175,0,"{ if (n ... ) { } }")
#20178=@"loc,{#10000},7,2,7,32"
locations_default(#20178,#10000,7,2,7,32)
hasLocation(#20177,#20178)
stmt_containers(#20177,#20001)
#20179=*
stmts(#20179,3,#20177,0,"if (not ... de) { }")
#20180=@"loc,{#10000},7,4,7,30"
locations_default(#20180,#10000,7,4,7,30)
hasLocation(#20179,#20180)
stmt_containers(#20179,#20001)
#20181=*
exprs(#20181,79,#20179,0,"not_generated_code")
hasLocation(#20181,#20093)
enclosing_stmt(#20181,#20179)
expr_containers(#20181,#20001)
literals("not_generated_code","not_generated_code",#20181)
bind(#20181,#20174)
#20182=*
stmts(#20182,1,#20179,1,"{ }")
#20183=@"loc,{#10000},7,28,7,30"
locations_default(#20183,#10000,7,28,7,30)
hasLocation(#20182,#20183)
stmt_containers(#20182,#20001)
#20184=*
stmts(#20184,3,#20001,6,"if (1 = ... _code}}")
#20185=@"loc,{#10000},8,1,8,34"
locations_default(#20185,#10000,8,1,8,34)
hasLocation(#20184,#20185)
stmt_containers(#20184,#20001)
#20186=*
exprs(#20186,23,#20184,0,"1 == 2")
#20187=@"loc,{#10000},8,5,8,10"
locations_default(#20187,#10000,8,5,8,10)
hasLocation(#20186,#20187)
enclosing_stmt(#20186,#20184)
expr_containers(#20186,#20001)
#20188=*
exprs(#20188,3,#20186,0,"1")
hasLocation(#20188,#20109)
enclosing_stmt(#20188,#20184)
expr_containers(#20188,#20001)
literals("1","1",#20188)
#20189=*
exprs(#20189,3,#20186,1,"2")
hasLocation(#20189,#20113)
enclosing_stmt(#20189,#20184)
expr_containers(#20189,#20001)
literals("2","2",#20189)
#20190=*
stmts(#20190,1,#20184,1,"{{not_g ... _code}}")
#20191=@"loc,{#10000},8,13,8,34"
locations_default(#20191,#10000,8,13,8,34)
hasLocation(#20190,#20191)
stmt_containers(#20190,#20001)
#20192=*
stmts(#20192,1,#20190,0,"{not_generated_code}")
#20193=@"loc,{#10000},8,14,8,33"
locations_default(#20193,#10000,8,14,8,33)
hasLocation(#20192,#20193)
stmt_containers(#20192,#20001)
#20194=*
stmts(#20194,2,#20192,0,"not_generated_code")
hasLocation(#20194,#20121)
stmt_containers(#20194,#20001)
#20195=*
exprs(#20195,79,#20194,0,"not_generated_code")
hasLocation(#20195,#20121)
enclosing_stmt(#20195,#20194)
expr_containers(#20195,#20001)
literals("not_generated_code","not_generated_code",#20195)
bind(#20195,#20174)
#20196=*
stmts(#20196,23,#20001,7,"let str ... de }}"";")
#20197=@"loc,{#10000},9,1,9,40"
locations_default(#20197,#10000,9,1,9,40)
hasLocation(#20196,#20197)
stmt_containers(#20196,#20001)
#20198=*
exprs(#20198,64,#20196,0,"string ... ode }}""")
#20199=@"loc,{#10000},9,5,9,39"
locations_default(#20199,#10000,9,5,9,39)
hasLocation(#20198,#20199)
enclosing_stmt(#20198,#20196)
expr_containers(#20198,#20001)
#20200=*
exprs(#20200,78,#20198,0,"string")
hasLocation(#20200,#20129)
enclosing_stmt(#20200,#20196)
expr_containers(#20200,#20001)
literals("string","string",#20200)
decl(#20200,#20141)
#20201=*
exprs(#20201,4,#20198,1,"""{{ not ... ode }}""")
hasLocation(#20201,#20133)
enclosing_stmt(#20201,#20196)
expr_containers(#20201,#20001)
literals("{{ not_generated_code }}","""{{ not_generated_code }}""",#20201)
#20202=*
entry_cfg_node(#20202,#20001)
#20203=@"loc,{#10000},1,1,1,0"
locations_default(#20203,#10000,1,1,1,0)
hasLocation(#20202,#20203)
#20204=*
exit_cfg_node(#20204,#20001)
hasLocation(#20204,#20137)
successor(#20196,#20200)
successor(#20201,#20198)
successor(#20200,#20201)
successor(#20198,#20204)
successor(#20184,#20188)
successor(#20189,#20186)
successor(#20188,#20189)
#20205=*
guard_node(#20205,1,#20186)
hasLocation(#20205,#20187)
successor(#20205,#20190)
#20206=*
guard_node(#20206,0,#20186)
hasLocation(#20206,#20187)
successor(#20206,#20196)
successor(#20186,#20205)
successor(#20186,#20206)
successor(#20190,#20192)
successor(#20192,#20194)
successor(#20194,#20195)
successor(#20195,#20196)
successor(#20175,#20177)
successor(#20177,#20179)
successor(#20179,#20181)
#20207=*
guard_node(#20207,1,#20181)
hasLocation(#20207,#20093)
successor(#20207,#20182)
#20208=*
guard_node(#20208,0,#20181)
hasLocation(#20208,#20093)
successor(#20208,#20184)
successor(#20181,#20207)
successor(#20181,#20208)
successor(#20182,#20184)
successor(#20168,#20170)
successor(#20170,#20172)
successor(#20172,#20173)
successor(#20173,#20175)
successor(#20159,#20162)
successor(#20166,#20160)
successor(#20164,#20166)
successor(#20162,#20164)
successor(#20160,#20168)
successor(#20154,#20155)
#20209=*
guard_node(#20209,1,#20155)
hasLocation(#20209,#20156)
successor(#20209,#20157)
#20210=*
guard_node(#20210,0,#20155)
hasLocation(#20210,#20156)
successor(#20210,#20159)
successor(#20155,#20209)
successor(#20155,#20210)
successor(#20157,#20159)
successor(#20148,#20151)
successor(#20152,#20149)
successor(#20151,#20152)
successor(#20149,#20154)
successor(#20142,#20145)
successor(#20146,#20143)
successor(#20145,#20146)
successor(#20143,#20148)
successor(#20202,#20142)
numlines(#10000,9,8,4)
filetype(#10000,"javascript")