extract regexp literals from string concatenations

This commit is contained in:
Erik Krogh Kristensen
2021-09-24 18:41:03 +02:00
parent 9478faf040
commit 12305aae42
16 changed files with 3460 additions and 149 deletions

View File

@@ -3,6 +3,8 @@ package com.semmle.js.extractor;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.Stack;
@@ -164,6 +166,9 @@ import com.semmle.util.locations.SourceMap;
import com.semmle.util.trap.TrapWriter;
import com.semmle.util.trap.TrapWriter.Label;
import com.semmle.util.files.FileLineOffsetCache;
/** Extractor for AST-based information; invoked by the {@link JSExtractor}. */
public class ASTExtractor {
private final TrapWriter trapwriter;
@@ -567,12 +572,17 @@ public class ASTExtractor {
String valueString = nd.getStringValue();
trapwriter.addTuple("literals", valueString, source, key);
Position start = nd.getLoc().getStart();
com.semmle.util.locations.Position startPos = new com.semmle.util.locations.Position(start.getLine(), start.getColumn(), start.getOffset());
if (nd.isRegExp()) {
OffsetTranslation offsets = new OffsetTranslation();
offsets.set(0, 1); // skip the initial '/'
regexpExtractor.extract(source.substring(1, source.lastIndexOf('/')), offsets, nd, false);
SourceMap sourceMap = SourceMap.legacyWithStartPos(SourceMap.fromString(nd.getRaw()).offsetBy(0, offsets), startPos);
regexpExtractor.extract(source.substring(1, source.lastIndexOf('/')), sourceMap, nd, false);
} else if (nd.isStringLiteral() && !c.isInsideType() && nd.getRaw().length() < 1000) {
regexpExtractor.extract(valueString, makeStringLiteralOffsets(nd.getRaw()), nd, true);
SourceMap sourceMap = SourceMap.legacyWithStartPos(SourceMap.fromString(nd.getRaw()).offsetBy(0, makeStringLiteralOffsets(nd.getRaw())), startPos);
regexpExtractor.extract(valueString, sourceMap, nd, true);
// Scan the string for template tags, if we're in a context where such tags are relevant.
if (scopeManager.isInTemplateFile()) {
@@ -593,6 +603,48 @@ public class ASTExtractor {
return '0' <= ch && ch <= '7';
}
private String getStringConcatResult(Expression exp) {
if (exp instanceof BinaryExpression) {
BinaryExpression be = (BinaryExpression) exp;
if (be.getOperator().equals("+")) {
String left = getStringConcatResult(be.getLeft());
String right = getStringConcatResult(be.getRight());
if (left != null && right != null) {
return left + right;
}
}
} else if (exp instanceof Literal) {
Literal lit = (Literal) exp;
if (!lit.isStringLiteral()) {
return null;
}
return lit.getStringValue();
}
return null;
}
private OffsetTranslation computeStringConcatOffset(Expression exp) {
if (exp instanceof Literal && ((Literal)exp).isStringLiteral()) {
String raw = ((Literal) exp).getRaw();
return makeStringLiteralOffsets(raw);
}
if (exp instanceof BinaryExpression) {
BinaryExpression be = (BinaryExpression) exp;
OffsetTranslation left = computeStringConcatOffset(be.getLeft());
OffsetTranslation right = computeStringConcatOffset(be.getRight());
if (left == null || right == null) {
return null;
}
int delta = be.getRight().getLoc().getStart().getOffset() - be.getLeft().getLoc().getStart().getOffset();
int offset = getStringConcatResult(be.getLeft()).length();
return left.append(right, offset, delta);
}
return null;
}
/**
* Builds a translation from offsets in a string value back to its original raw literal text
* (including quotes).
@@ -786,11 +838,31 @@ public class ASTExtractor {
return key;
}
// set to determine which BinaryExpression has been extracted as regexp
private Set<Expression> extractedAsRegexp = new HashSet<>();
@Override
public Label visit(BinaryExpression nd, Context c) {
Label key = super.visit(nd, c);
extractedAsRegexp.add(nd.getLeft());
extractedAsRegexp.add(nd.getRight());
visit(nd.getLeft(), key, 0);
visit(nd.getRight(), key, 1);
if (extractedAsRegexp.contains(nd)) {
return key;
}
String rawString = getStringConcatResult(nd);
if (rawString == null) {
return key;
}
if (rawString.length() > 1000 && !rawString.trim().isEmpty()) {
return key;
}
OffsetTranslation offsets = computeStringConcatOffset(nd);
Position start = nd.getLoc().getStart();
com.semmle.util.locations.Position startPos = new com.semmle.util.locations.Position(start.getLine(), start.getColumn(), start.getOffset());
SourceMap sourceMap = SourceMap.legacyWithStartPos(SourceMap.fromString(nd.getLoc().getSource()).offsetBy(0, offsets), startPos);
regexpExtractor.extract(rawString, sourceMap, nd, true);
return key;
}

View File

@@ -43,7 +43,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2021-10-25";
public static final String EXTRACTOR_VERSION = "2021-10-28";
public static final Pattern NEWLINE = Pattern.compile("\n");

View File

@@ -43,7 +43,7 @@ import com.semmle.js.ast.regexp.ZeroWidthPositiveLookahead;
import com.semmle.js.ast.regexp.ZeroWidthPositiveLookbehind;
import com.semmle.js.parser.RegExpParser;
import com.semmle.js.parser.RegExpParser.Result;
import com.semmle.util.locations.OffsetTranslation;
import com.semmle.util.locations.SourceMap;
import com.semmle.util.trap.TrapWriter;
import com.semmle.util.trap.TrapWriter.Label;
@@ -52,8 +52,7 @@ public class RegExpExtractor {
private final TrapWriter trapwriter;
private final LocationManager locationManager;
private final RegExpParser parser = new RegExpParser();
private Position literalStart;
private OffsetTranslation offsets;
private SourceMap sourceMap;
public RegExpExtractor(TrapWriter trapwriter, LocationManager locationManager) {
this.trapwriter = trapwriter;
@@ -122,17 +121,14 @@ public class RegExpExtractor {
}
public void emitLocation(SourceElement term, Label lbl) {
int col = literalStart.getColumn();
int sl, sc, el, ec;
sl = el = literalStart.getLine();
sc = col + offsets.get(term.getLoc().getStart().getColumn());
ec = col + offsets.get(term.getLoc().getEnd().getColumn());
sc += 1; // convert to 1-based
ec += 1; // convert to 1-based
ec -= 1; // convert to inclusive
int sl = sourceMap.getStart(term.getLoc().getStart().getColumn()).getLine();
int sc = sourceMap.getStart(term.getLoc().getStart().getColumn()).getColumn() + 1; // convert to 1-based
int el = sourceMap.getEnd(term.getLoc().getEnd().getColumn()).getLine();
int ec = sourceMap.getEnd(term.getLoc().getEnd().getColumn()).getColumn() - 1; // convert to inclusive
locationManager.emitSnippetLocation(lbl, sl, sc, el, ec);
}
private class V implements Visitor {
private Label parent;
private int idx;
@@ -348,16 +344,13 @@ public class RegExpExtractor {
}
}
public void extract(
String src, OffsetTranslation offsets, Node parent, boolean isSpeculativeParsing) {
public void extract(String src, SourceMap sourceMap, Node parent, boolean isSpeculativeParsing) {
Result res = parser.parse(src);
if (isSpeculativeParsing && res.getErrors().size() > 0) {
return;
}
this.literalStart = parent.getLoc().getStart();
this.offsets = offsets;
this.sourceMap = sourceMap;
RegExpTerm ast = res.getAST();
new V().visit(ast, trapwriter.localID(parent), 0);

View File

@@ -589,170 +589,176 @@ locations_default(#20204,#10000,3,26,3,26)
hasLocation(#20203,#20204)
regexp_const_value(#20203,"b")
#20205=*
exprs(#20205,79,#20182,-2,"j")
hasLocation(#20205,#20075)
enclosing_stmt(#20205,#20181)
expr_containers(#20205,#20001)
literals("j","j",#20205)
#20206=@"var;{j};{#20000}"
variables(#20206,"j",#20000)
bind(#20205,#20206)
regexpterm(#20205,14,#20197,0,"ab")
#20206=@"loc,{#10000},3,22,3,26"
locations_default(#20206,#10000,3,22,3,26)
hasLocation(#20205,#20206)
regexp_const_value(#20205,"ab")
#20207=*
exprs(#20207,89,#20182,-3,"<k.l><M/></k.l>")
#20208=@"loc,{#10000},3,33,3,47"
locations_default(#20208,#10000,3,33,3,47)
hasLocation(#20207,#20208)
exprs(#20207,79,#20182,-2,"j")
hasLocation(#20207,#20075)
enclosing_stmt(#20207,#20181)
expr_containers(#20207,#20001)
literals("j","j",#20207)
#20208=@"var;{j};{#20000}"
variables(#20208,"j",#20000)
bind(#20207,#20208)
#20209=*
exprs(#20209,14,#20207,-1,"k.l")
#20210=@"loc,{#10000},3,34,3,36"
locations_default(#20210,#10000,3,34,3,36)
exprs(#20209,89,#20182,-3,"<k.l><M/></k.l>")
#20210=@"loc,{#10000},3,33,3,47"
locations_default(#20210,#10000,3,33,3,47)
hasLocation(#20209,#20210)
enclosing_stmt(#20209,#20181)
expr_containers(#20209,#20001)
#20211=*
exprs(#20211,79,#20209,0,"k")
hasLocation(#20211,#20081)
exprs(#20211,14,#20209,-1,"k.l")
#20212=@"loc,{#10000},3,34,3,36"
locations_default(#20212,#10000,3,34,3,36)
hasLocation(#20211,#20212)
enclosing_stmt(#20211,#20181)
expr_containers(#20211,#20001)
literals("k","k",#20211)
#20212=@"var;{k};{#20000}"
variables(#20212,"k",#20000)
bind(#20211,#20212)
#20213=*
exprs(#20213,0,#20209,1,"l")
hasLocation(#20213,#20085)
exprs(#20213,79,#20211,0,"k")
hasLocation(#20213,#20081)
enclosing_stmt(#20213,#20181)
expr_containers(#20213,#20001)
literals("l","l",#20213)
#20214=*
exprs(#20214,89,#20207,-2,"<M/>")
#20215=@"loc,{#10000},3,38,3,41"
locations_default(#20215,#10000,3,38,3,41)
hasLocation(#20214,#20215)
enclosing_stmt(#20214,#20181)
expr_containers(#20214,#20001)
literals("k","k",#20213)
#20214=@"var;{k};{#20000}"
variables(#20214,"k",#20000)
bind(#20213,#20214)
#20215=*
exprs(#20215,0,#20211,1,"l")
hasLocation(#20215,#20085)
enclosing_stmt(#20215,#20181)
expr_containers(#20215,#20001)
literals("l","l",#20215)
#20216=*
exprs(#20216,79,#20214,-1,"M")
hasLocation(#20216,#20091)
exprs(#20216,89,#20209,-2,"<M/>")
#20217=@"loc,{#10000},3,38,3,41"
locations_default(#20217,#10000,3,38,3,41)
hasLocation(#20216,#20217)
enclosing_stmt(#20216,#20181)
expr_containers(#20216,#20001)
literals("M","M",#20216)
#20217=@"var;{M};{#20000}"
variables(#20217,"M",#20000)
bind(#20216,#20217)
#20218=*
stmts(#20218,2,#20001,3,"<n {...props}/>;")
hasLocation(#20218,#20009)
stmt_containers(#20218,#20001)
#20219=*
exprs(#20219,89,#20218,0,"<n {...props}/>")
#20220=@"loc,{#10000},4,1,4,15"
locations_default(#20220,#10000,4,1,4,15)
hasLocation(#20219,#20220)
enclosing_stmt(#20219,#20218)
expr_containers(#20219,#20001)
exprs(#20218,79,#20216,-1,"M")
hasLocation(#20218,#20091)
enclosing_stmt(#20218,#20181)
expr_containers(#20218,#20001)
literals("M","M",#20218)
#20219=@"var;{M};{#20000}"
variables(#20219,"M",#20000)
bind(#20218,#20219)
#20220=*
stmts(#20220,2,#20001,3,"<n {...props}/>;")
hasLocation(#20220,#20009)
stmt_containers(#20220,#20001)
#20221=*
exprs(#20221,0,#20219,-1,"n")
hasLocation(#20221,#20121)
enclosing_stmt(#20221,#20218)
exprs(#20221,89,#20220,0,"<n {...props}/>")
#20222=@"loc,{#10000},4,1,4,15"
locations_default(#20222,#10000,4,1,4,15)
hasLocation(#20221,#20222)
enclosing_stmt(#20221,#20220)
expr_containers(#20221,#20001)
literals("n","n",#20221)
#20222=*
properties(#20222,#20219,0,3,"{...props}")
#20223=@"loc,{#10000},4,4,4,13"
locations_default(#20223,#10000,4,4,4,13)
hasLocation(#20222,#20223)
#20223=*
exprs(#20223,0,#20221,-1,"n")
hasLocation(#20223,#20121)
enclosing_stmt(#20223,#20220)
expr_containers(#20223,#20001)
literals("n","n",#20223)
#20224=*
exprs(#20224,66,#20222,1,"...props")
hasLocation(#20224,#20223)
enclosing_stmt(#20224,#20218)
expr_containers(#20224,#20001)
#20225=*
exprs(#20225,79,#20224,0,"props")
hasLocation(#20225,#20127)
enclosing_stmt(#20225,#20218)
expr_containers(#20225,#20001)
literals("props","props",#20225)
#20226=@"var;{props};{#20000}"
variables(#20226,"props",#20000)
bind(#20225,#20226)
properties(#20224,#20221,0,3,"{...props}")
#20225=@"loc,{#10000},4,4,4,13"
locations_default(#20225,#10000,4,4,4,13)
hasLocation(#20224,#20225)
#20226=*
exprs(#20226,66,#20224,1,"...props")
hasLocation(#20226,#20225)
enclosing_stmt(#20226,#20220)
expr_containers(#20226,#20001)
#20227=*
stmts(#20227,2,#20001,4,"<><a/><b/></>")
hasLocation(#20227,#20011)
stmt_containers(#20227,#20001)
#20228=*
exprs(#20228,89,#20227,0,"<><a/><b/></>")
hasLocation(#20228,#20011)
enclosing_stmt(#20228,#20227)
expr_containers(#20228,#20001)
exprs(#20227,79,#20226,0,"props")
hasLocation(#20227,#20127)
enclosing_stmt(#20227,#20220)
expr_containers(#20227,#20001)
literals("props","props",#20227)
#20228=@"var;{props};{#20000}"
variables(#20228,"props",#20000)
bind(#20227,#20228)
#20229=*
exprs(#20229,89,#20228,-2,"<a/>")
#20230=@"loc,{#10000},5,3,5,6"
locations_default(#20230,#10000,5,3,5,6)
hasLocation(#20229,#20230)
enclosing_stmt(#20229,#20227)
expr_containers(#20229,#20001)
stmts(#20229,2,#20001,4,"<><a/><b/></>")
hasLocation(#20229,#20011)
stmt_containers(#20229,#20001)
#20230=*
exprs(#20230,89,#20229,0,"<><a/><b/></>")
hasLocation(#20230,#20011)
enclosing_stmt(#20230,#20229)
expr_containers(#20230,#20001)
#20231=*
exprs(#20231,0,#20229,-1,"a")
hasLocation(#20231,#20143)
enclosing_stmt(#20231,#20227)
exprs(#20231,89,#20230,-2,"<a/>")
#20232=@"loc,{#10000},5,3,5,6"
locations_default(#20232,#10000,5,3,5,6)
hasLocation(#20231,#20232)
enclosing_stmt(#20231,#20229)
expr_containers(#20231,#20001)
literals("a","a",#20231)
#20232=*
exprs(#20232,89,#20228,-3,"<b/>")
#20233=@"loc,{#10000},5,7,5,10"
locations_default(#20233,#10000,5,7,5,10)
hasLocation(#20232,#20233)
enclosing_stmt(#20232,#20227)
expr_containers(#20232,#20001)
#20233=*
exprs(#20233,0,#20231,-1,"a")
hasLocation(#20233,#20143)
enclosing_stmt(#20233,#20229)
expr_containers(#20233,#20001)
literals("a","a",#20233)
#20234=*
exprs(#20234,0,#20232,-1,"b")
hasLocation(#20234,#20151)
enclosing_stmt(#20234,#20227)
exprs(#20234,89,#20230,-3,"<b/>")
#20235=@"loc,{#10000},5,7,5,10"
locations_default(#20235,#10000,5,7,5,10)
hasLocation(#20234,#20235)
enclosing_stmt(#20234,#20229)
expr_containers(#20234,#20001)
literals("b","b",#20234)
#20235=*
entry_cfg_node(#20235,#20001)
#20236=@"loc,{#10000},1,1,1,0"
locations_default(#20236,#10000,1,1,1,0)
hasLocation(#20235,#20236)
#20236=*
exprs(#20236,0,#20234,-1,"b")
hasLocation(#20236,#20151)
enclosing_stmt(#20236,#20229)
expr_containers(#20236,#20001)
literals("b","b",#20236)
#20237=*
exit_cfg_node(#20237,#20001)
hasLocation(#20237,#20163)
successor(#20227,#20231)
successor(#20234,#20232)
successor(#20232,#20228)
successor(#20231,#20229)
successor(#20229,#20234)
successor(#20228,#20237)
successor(#20218,#20221)
successor(#20225,#20224)
successor(#20224,#20222)
successor(#20222,#20219)
successor(#20221,#20225)
successor(#20219,#20227)
entry_cfg_node(#20237,#20001)
#20238=@"loc,{#10000},1,1,1,0"
locations_default(#20238,#10000,1,1,1,0)
hasLocation(#20237,#20238)
#20239=*
exit_cfg_node(#20239,#20001)
hasLocation(#20239,#20163)
successor(#20229,#20233)
successor(#20236,#20234)
successor(#20234,#20230)
successor(#20233,#20231)
successor(#20231,#20236)
successor(#20230,#20239)
successor(#20220,#20223)
successor(#20227,#20226)
successor(#20226,#20224)
successor(#20224,#20221)
successor(#20223,#20227)
successor(#20221,#20229)
successor(#20181,#20184)
successor(#20216,#20214)
successor(#20214,#20207)
successor(#20213,#20209)
successor(#20211,#20213)
successor(#20209,#20216)
successor(#20207,#20182)
successor(#20205,#20211)
successor(#20218,#20216)
successor(#20216,#20209)
successor(#20215,#20211)
successor(#20213,#20215)
successor(#20211,#20218)
successor(#20209,#20182)
successor(#20207,#20213)
successor(#20202,#20197)
successor(#20199,#20202)
successor(#20197,#20194)
successor(#20196,#20199)
successor(#20194,#20205)
successor(#20194,#20207)
successor(#20191,#20185)
successor(#20190,#20187)
successor(#20189,#20190)
successor(#20187,#20191)
successor(#20185,#20196)
successor(#20184,#20189)
successor(#20182,#20218)
successor(#20182,#20220)
successor(#20169,#20174)
successor(#20180,#20178)
successor(#20179,#20180)
@@ -765,6 +771,6 @@ successor(#20170,#20181)
successor(#20165,#20168)
successor(#20168,#20166)
successor(#20166,#20169)
successor(#20235,#20165)
successor(#20237,#20165)
numlines(#10000,5,5,0)
filetype(#10000,"javascript")

View File

@@ -0,0 +1,14 @@
var reg = new RegExp("foo" + "bar");
var reg2 = new RegExp("foo"
+ "bar");
var reg3 = new RegExp(
"foo" + "bar");
var reg4 = new RegExp(
"foo" +
"bar" +
"baz" +
"qux"
);

View File

@@ -0,0 +1,727 @@
#10000=@"/multipart.js;sourcefile"
files(#10000,"/multipart.js")
#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=*
lines(#20002,#20001,"var reg = new RegExp(""foo"" + ""bar"");","
")
#20003=@"loc,{#10000},1,1,1,36"
locations_default(#20003,#10000,1,1,1,36)
hasLocation(#20002,#20003)
#20004=*
lines(#20004,#20001,"","
")
#20005=@"loc,{#10000},2,1,2,0"
locations_default(#20005,#10000,2,1,2,0)
hasLocation(#20004,#20005)
#20006=*
lines(#20006,#20001,"var reg2 = new RegExp(""foo""","
")
#20007=@"loc,{#10000},3,1,3,27"
locations_default(#20007,#10000,3,1,3,27)
hasLocation(#20006,#20007)
#20008=*
lines(#20008,#20001," + ""bar"");","
")
#20009=@"loc,{#10000},4,1,4,13"
locations_default(#20009,#10000,4,1,4,13)
hasLocation(#20008,#20009)
indentation(#10000,4," ",4)
#20010=*
lines(#20010,#20001,"","
")
#20011=@"loc,{#10000},5,1,5,0"
locations_default(#20011,#10000,5,1,5,0)
hasLocation(#20010,#20011)
#20012=*
lines(#20012,#20001,"var reg3 = new RegExp(","
")
#20013=@"loc,{#10000},6,1,6,22"
locations_default(#20013,#10000,6,1,6,22)
hasLocation(#20012,#20013)
#20014=*
lines(#20014,#20001," ""foo"" + ""bar"");","
")
#20015=@"loc,{#10000},7,1,7,19"
locations_default(#20015,#10000,7,1,7,19)
hasLocation(#20014,#20015)
indentation(#10000,7," ",4)
#20016=*
lines(#20016,#20001,"","
")
#20017=@"loc,{#10000},8,1,8,0"
locations_default(#20017,#10000,8,1,8,0)
hasLocation(#20016,#20017)
#20018=*
lines(#20018,#20001,"var reg4 = new RegExp(","
")
#20019=@"loc,{#10000},9,1,9,22"
locations_default(#20019,#10000,9,1,9,22)
hasLocation(#20018,#20019)
#20020=*
lines(#20020,#20001," ""foo"" + ","
")
#20021=@"loc,{#10000},10,1,10,12"
locations_default(#20021,#10000,10,1,10,12)
hasLocation(#20020,#20021)
indentation(#10000,10," ",4)
#20022=*
lines(#20022,#20001," ""bar"" + ","
")
#20023=@"loc,{#10000},11,1,11,12"
locations_default(#20023,#10000,11,1,11,12)
hasLocation(#20022,#20023)
indentation(#10000,11," ",4)
#20024=*
lines(#20024,#20001," ""baz"" + ","
")
#20025=@"loc,{#10000},12,1,12,12"
locations_default(#20025,#10000,12,1,12,12)
hasLocation(#20024,#20025)
indentation(#10000,12," ",4)
#20026=*
lines(#20026,#20001," ""qux""","
")
#20027=@"loc,{#10000},13,1,13,9"
locations_default(#20027,#10000,13,1,13,9)
hasLocation(#20026,#20027)
indentation(#10000,13," ",4)
#20028=*
lines(#20028,#20001,");","")
#20029=@"loc,{#10000},14,1,14,2"
locations_default(#20029,#10000,14,1,14,2)
hasLocation(#20028,#20029)
numlines(#20001,14,11,0)
#20030=*
tokeninfo(#20030,7,#20001,0,"var")
#20031=@"loc,{#10000},1,1,1,3"
locations_default(#20031,#10000,1,1,1,3)
hasLocation(#20030,#20031)
#20032=*
tokeninfo(#20032,6,#20001,1,"reg")
#20033=@"loc,{#10000},1,5,1,7"
locations_default(#20033,#10000,1,5,1,7)
hasLocation(#20032,#20033)
#20034=*
tokeninfo(#20034,8,#20001,2,"=")
#20035=@"loc,{#10000},1,9,1,9"
locations_default(#20035,#10000,1,9,1,9)
hasLocation(#20034,#20035)
#20036=*
tokeninfo(#20036,7,#20001,3,"new")
#20037=@"loc,{#10000},1,11,1,13"
locations_default(#20037,#10000,1,11,1,13)
hasLocation(#20036,#20037)
#20038=*
tokeninfo(#20038,6,#20001,4,"RegExp")
#20039=@"loc,{#10000},1,15,1,20"
locations_default(#20039,#10000,1,15,1,20)
hasLocation(#20038,#20039)
#20040=*
tokeninfo(#20040,8,#20001,5,"(")
#20041=@"loc,{#10000},1,21,1,21"
locations_default(#20041,#10000,1,21,1,21)
hasLocation(#20040,#20041)
#20042=*
tokeninfo(#20042,4,#20001,6,"""foo""")
#20043=@"loc,{#10000},1,22,1,26"
locations_default(#20043,#10000,1,22,1,26)
hasLocation(#20042,#20043)
#20044=*
tokeninfo(#20044,8,#20001,7,"+")
#20045=@"loc,{#10000},1,28,1,28"
locations_default(#20045,#10000,1,28,1,28)
hasLocation(#20044,#20045)
#20046=*
tokeninfo(#20046,4,#20001,8,"""bar""")
#20047=@"loc,{#10000},1,30,1,34"
locations_default(#20047,#10000,1,30,1,34)
hasLocation(#20046,#20047)
#20048=*
tokeninfo(#20048,8,#20001,9,")")
#20049=@"loc,{#10000},1,35,1,35"
locations_default(#20049,#10000,1,35,1,35)
hasLocation(#20048,#20049)
#20050=*
tokeninfo(#20050,8,#20001,10,";")
#20051=@"loc,{#10000},1,36,1,36"
locations_default(#20051,#10000,1,36,1,36)
hasLocation(#20050,#20051)
#20052=*
tokeninfo(#20052,7,#20001,11,"var")
#20053=@"loc,{#10000},3,1,3,3"
locations_default(#20053,#10000,3,1,3,3)
hasLocation(#20052,#20053)
#20054=*
tokeninfo(#20054,6,#20001,12,"reg2")
#20055=@"loc,{#10000},3,5,3,8"
locations_default(#20055,#10000,3,5,3,8)
hasLocation(#20054,#20055)
#20056=*
tokeninfo(#20056,8,#20001,13,"=")
#20057=@"loc,{#10000},3,10,3,10"
locations_default(#20057,#10000,3,10,3,10)
hasLocation(#20056,#20057)
#20058=*
tokeninfo(#20058,7,#20001,14,"new")
#20059=@"loc,{#10000},3,12,3,14"
locations_default(#20059,#10000,3,12,3,14)
hasLocation(#20058,#20059)
#20060=*
tokeninfo(#20060,6,#20001,15,"RegExp")
#20061=@"loc,{#10000},3,16,3,21"
locations_default(#20061,#10000,3,16,3,21)
hasLocation(#20060,#20061)
#20062=*
tokeninfo(#20062,8,#20001,16,"(")
#20063=@"loc,{#10000},3,22,3,22"
locations_default(#20063,#10000,3,22,3,22)
hasLocation(#20062,#20063)
#20064=*
tokeninfo(#20064,4,#20001,17,"""foo""")
#20065=@"loc,{#10000},3,23,3,27"
locations_default(#20065,#10000,3,23,3,27)
hasLocation(#20064,#20065)
#20066=*
tokeninfo(#20066,8,#20001,18,"+")
#20067=@"loc,{#10000},4,5,4,5"
locations_default(#20067,#10000,4,5,4,5)
hasLocation(#20066,#20067)
#20068=*
tokeninfo(#20068,4,#20001,19,"""bar""")
#20069=@"loc,{#10000},4,7,4,11"
locations_default(#20069,#10000,4,7,4,11)
hasLocation(#20068,#20069)
#20070=*
tokeninfo(#20070,8,#20001,20,")")
#20071=@"loc,{#10000},4,12,4,12"
locations_default(#20071,#10000,4,12,4,12)
hasLocation(#20070,#20071)
#20072=*
tokeninfo(#20072,8,#20001,21,";")
#20073=@"loc,{#10000},4,13,4,13"
locations_default(#20073,#10000,4,13,4,13)
hasLocation(#20072,#20073)
#20074=*
tokeninfo(#20074,7,#20001,22,"var")
#20075=@"loc,{#10000},6,1,6,3"
locations_default(#20075,#10000,6,1,6,3)
hasLocation(#20074,#20075)
#20076=*
tokeninfo(#20076,6,#20001,23,"reg3")
#20077=@"loc,{#10000},6,5,6,8"
locations_default(#20077,#10000,6,5,6,8)
hasLocation(#20076,#20077)
#20078=*
tokeninfo(#20078,8,#20001,24,"=")
#20079=@"loc,{#10000},6,10,6,10"
locations_default(#20079,#10000,6,10,6,10)
hasLocation(#20078,#20079)
#20080=*
tokeninfo(#20080,7,#20001,25,"new")
#20081=@"loc,{#10000},6,12,6,14"
locations_default(#20081,#10000,6,12,6,14)
hasLocation(#20080,#20081)
#20082=*
tokeninfo(#20082,6,#20001,26,"RegExp")
#20083=@"loc,{#10000},6,16,6,21"
locations_default(#20083,#10000,6,16,6,21)
hasLocation(#20082,#20083)
#20084=*
tokeninfo(#20084,8,#20001,27,"(")
#20085=@"loc,{#10000},6,22,6,22"
locations_default(#20085,#10000,6,22,6,22)
hasLocation(#20084,#20085)
#20086=*
tokeninfo(#20086,4,#20001,28,"""foo""")
#20087=@"loc,{#10000},7,5,7,9"
locations_default(#20087,#10000,7,5,7,9)
hasLocation(#20086,#20087)
#20088=*
tokeninfo(#20088,8,#20001,29,"+")
#20089=@"loc,{#10000},7,11,7,11"
locations_default(#20089,#10000,7,11,7,11)
hasLocation(#20088,#20089)
#20090=*
tokeninfo(#20090,4,#20001,30,"""bar""")
#20091=@"loc,{#10000},7,13,7,17"
locations_default(#20091,#10000,7,13,7,17)
hasLocation(#20090,#20091)
#20092=*
tokeninfo(#20092,8,#20001,31,")")
#20093=@"loc,{#10000},7,18,7,18"
locations_default(#20093,#10000,7,18,7,18)
hasLocation(#20092,#20093)
#20094=*
tokeninfo(#20094,8,#20001,32,";")
#20095=@"loc,{#10000},7,19,7,19"
locations_default(#20095,#10000,7,19,7,19)
hasLocation(#20094,#20095)
#20096=*
tokeninfo(#20096,7,#20001,33,"var")
#20097=@"loc,{#10000},9,1,9,3"
locations_default(#20097,#10000,9,1,9,3)
hasLocation(#20096,#20097)
#20098=*
tokeninfo(#20098,6,#20001,34,"reg4")
#20099=@"loc,{#10000},9,5,9,8"
locations_default(#20099,#10000,9,5,9,8)
hasLocation(#20098,#20099)
#20100=*
tokeninfo(#20100,8,#20001,35,"=")
#20101=@"loc,{#10000},9,10,9,10"
locations_default(#20101,#10000,9,10,9,10)
hasLocation(#20100,#20101)
#20102=*
tokeninfo(#20102,7,#20001,36,"new")
#20103=@"loc,{#10000},9,12,9,14"
locations_default(#20103,#10000,9,12,9,14)
hasLocation(#20102,#20103)
#20104=*
tokeninfo(#20104,6,#20001,37,"RegExp")
#20105=@"loc,{#10000},9,16,9,21"
locations_default(#20105,#10000,9,16,9,21)
hasLocation(#20104,#20105)
#20106=*
tokeninfo(#20106,8,#20001,38,"(")
#20107=@"loc,{#10000},9,22,9,22"
locations_default(#20107,#10000,9,22,9,22)
hasLocation(#20106,#20107)
#20108=*
tokeninfo(#20108,4,#20001,39,"""foo""")
#20109=@"loc,{#10000},10,5,10,9"
locations_default(#20109,#10000,10,5,10,9)
hasLocation(#20108,#20109)
#20110=*
tokeninfo(#20110,8,#20001,40,"+")
#20111=@"loc,{#10000},10,11,10,11"
locations_default(#20111,#10000,10,11,10,11)
hasLocation(#20110,#20111)
#20112=*
tokeninfo(#20112,4,#20001,41,"""bar""")
#20113=@"loc,{#10000},11,5,11,9"
locations_default(#20113,#10000,11,5,11,9)
hasLocation(#20112,#20113)
#20114=*
tokeninfo(#20114,8,#20001,42,"+")
#20115=@"loc,{#10000},11,11,11,11"
locations_default(#20115,#10000,11,11,11,11)
hasLocation(#20114,#20115)
#20116=*
tokeninfo(#20116,4,#20001,43,"""baz""")
#20117=@"loc,{#10000},12,5,12,9"
locations_default(#20117,#10000,12,5,12,9)
hasLocation(#20116,#20117)
#20118=*
tokeninfo(#20118,8,#20001,44,"+")
#20119=@"loc,{#10000},12,11,12,11"
locations_default(#20119,#10000,12,11,12,11)
hasLocation(#20118,#20119)
#20120=*
tokeninfo(#20120,4,#20001,45,"""qux""")
#20121=@"loc,{#10000},13,5,13,9"
locations_default(#20121,#10000,13,5,13,9)
hasLocation(#20120,#20121)
#20122=*
tokeninfo(#20122,8,#20001,46,")")
#20123=@"loc,{#10000},14,1,14,1"
locations_default(#20123,#10000,14,1,14,1)
hasLocation(#20122,#20123)
#20124=*
tokeninfo(#20124,8,#20001,47,";")
#20125=@"loc,{#10000},14,2,14,2"
locations_default(#20125,#10000,14,2,14,2)
hasLocation(#20124,#20125)
#20126=*
tokeninfo(#20126,0,#20001,48,"")
#20127=@"loc,{#10000},14,3,14,2"
locations_default(#20127,#10000,14,3,14,2)
hasLocation(#20126,#20127)
toplevels(#20001,0)
#20128=@"loc,{#10000},1,1,14,2"
locations_default(#20128,#10000,1,1,14,2)
hasLocation(#20001,#20128)
#20129=@"var;{reg};{#20000}"
variables(#20129,"reg",#20000)
#20130=@"var;{reg2};{#20000}"
variables(#20130,"reg2",#20000)
#20131=@"var;{reg3};{#20000}"
variables(#20131,"reg3",#20000)
#20132=@"var;{reg4};{#20000}"
variables(#20132,"reg4",#20000)
#20133=*
stmts(#20133,18,#20001,0,"var reg ... ""bar"");")
hasLocation(#20133,#20003)
stmt_containers(#20133,#20001)
#20134=*
exprs(#20134,64,#20133,0,"reg = n ... ""bar"")")
#20135=@"loc,{#10000},1,5,1,35"
locations_default(#20135,#10000,1,5,1,35)
hasLocation(#20134,#20135)
enclosing_stmt(#20134,#20133)
expr_containers(#20134,#20001)
#20136=*
exprs(#20136,78,#20134,0,"reg")
hasLocation(#20136,#20033)
enclosing_stmt(#20136,#20133)
expr_containers(#20136,#20001)
literals("reg","reg",#20136)
decl(#20136,#20129)
#20137=*
exprs(#20137,12,#20134,1,"new Reg ... ""bar"")")
#20138=@"loc,{#10000},1,11,1,35"
locations_default(#20138,#10000,1,11,1,35)
hasLocation(#20137,#20138)
enclosing_stmt(#20137,#20133)
expr_containers(#20137,#20001)
#20139=*
exprs(#20139,79,#20137,-1,"RegExp")
hasLocation(#20139,#20039)
enclosing_stmt(#20139,#20133)
expr_containers(#20139,#20001)
literals("RegExp","RegExp",#20139)
#20140=@"var;{RegExp};{#20000}"
variables(#20140,"RegExp",#20000)
bind(#20139,#20140)
#20141=*
exprs(#20141,34,#20137,0,"""foo"" + ""bar""")
#20142=@"loc,{#10000},1,22,1,34"
locations_default(#20142,#10000,1,22,1,34)
hasLocation(#20141,#20142)
enclosing_stmt(#20141,#20133)
expr_containers(#20141,#20001)
#20143=*
exprs(#20143,4,#20141,0,"""foo""")
hasLocation(#20143,#20043)
enclosing_stmt(#20143,#20133)
expr_containers(#20143,#20001)
literals("foo","""foo""",#20143)
#20144=*
regexpterm(#20144,14,#20143,0,"foo")
#20145=@"loc,{#10000},1,23,1,25"
locations_default(#20145,#10000,1,23,1,25)
hasLocation(#20144,#20145)
regexp_const_value(#20144,"foo")
#20146=*
exprs(#20146,4,#20141,1,"""bar""")
hasLocation(#20146,#20047)
enclosing_stmt(#20146,#20133)
expr_containers(#20146,#20001)
literals("bar","""bar""",#20146)
#20147=*
regexpterm(#20147,14,#20146,0,"bar")
#20148=@"loc,{#10000},1,31,1,33"
locations_default(#20148,#10000,1,31,1,33)
hasLocation(#20147,#20148)
regexp_const_value(#20147,"bar")
#20149=*
regexpterm(#20149,14,#20141,0,"foobar")
#20150=@"loc,{#10000},1,23,1,33"
locations_default(#20150,#10000,1,23,1,33)
hasLocation(#20149,#20150)
regexp_const_value(#20149,"foobar")
#20151=*
stmts(#20151,18,#20001,1,"var reg ... ""bar"");")
#20152=@"loc,{#10000},3,1,4,13"
locations_default(#20152,#10000,3,1,4,13)
hasLocation(#20151,#20152)
stmt_containers(#20151,#20001)
#20153=*
exprs(#20153,64,#20151,0,"reg2 = ... ""bar"")")
#20154=@"loc,{#10000},3,5,4,12"
locations_default(#20154,#10000,3,5,4,12)
hasLocation(#20153,#20154)
enclosing_stmt(#20153,#20151)
expr_containers(#20153,#20001)
#20155=*
exprs(#20155,78,#20153,0,"reg2")
hasLocation(#20155,#20055)
enclosing_stmt(#20155,#20151)
expr_containers(#20155,#20001)
literals("reg2","reg2",#20155)
decl(#20155,#20130)
#20156=*
exprs(#20156,12,#20153,1,"new Reg ... ""bar"")")
#20157=@"loc,{#10000},3,12,4,12"
locations_default(#20157,#10000,3,12,4,12)
hasLocation(#20156,#20157)
enclosing_stmt(#20156,#20151)
expr_containers(#20156,#20001)
#20158=*
exprs(#20158,79,#20156,-1,"RegExp")
hasLocation(#20158,#20061)
enclosing_stmt(#20158,#20151)
expr_containers(#20158,#20001)
literals("RegExp","RegExp",#20158)
bind(#20158,#20140)
#20159=*
exprs(#20159,34,#20156,0,"""foo""\n + ""bar""")
#20160=@"loc,{#10000},3,23,4,11"
locations_default(#20160,#10000,3,23,4,11)
hasLocation(#20159,#20160)
enclosing_stmt(#20159,#20151)
expr_containers(#20159,#20001)
#20161=*
exprs(#20161,4,#20159,0,"""foo""")
hasLocation(#20161,#20065)
enclosing_stmt(#20161,#20151)
expr_containers(#20161,#20001)
literals("foo","""foo""",#20161)
#20162=*
regexpterm(#20162,14,#20161,0,"foo")
#20163=@"loc,{#10000},3,24,3,26"
locations_default(#20163,#10000,3,24,3,26)
hasLocation(#20162,#20163)
regexp_const_value(#20162,"foo")
#20164=*
exprs(#20164,4,#20159,1,"""bar""")
hasLocation(#20164,#20069)
enclosing_stmt(#20164,#20151)
expr_containers(#20164,#20001)
literals("bar","""bar""",#20164)
#20165=*
regexpterm(#20165,14,#20164,0,"bar")
#20166=@"loc,{#10000},4,8,4,10"
locations_default(#20166,#10000,4,8,4,10)
hasLocation(#20165,#20166)
regexp_const_value(#20165,"bar")
#20167=*
regexpterm(#20167,14,#20159,0,"foobar")
#20168=@"loc,{#10000},3,24,4,11"
locations_default(#20168,#10000,3,24,4,11)
hasLocation(#20167,#20168)
regexp_const_value(#20167,"foobar")
#20169=*
stmts(#20169,18,#20001,2,"var reg ... ""bar"");")
#20170=@"loc,{#10000},6,1,7,19"
locations_default(#20170,#10000,6,1,7,19)
hasLocation(#20169,#20170)
stmt_containers(#20169,#20001)
#20171=*
exprs(#20171,64,#20169,0,"reg3 = ... ""bar"")")
#20172=@"loc,{#10000},6,5,7,18"
locations_default(#20172,#10000,6,5,7,18)
hasLocation(#20171,#20172)
enclosing_stmt(#20171,#20169)
expr_containers(#20171,#20001)
#20173=*
exprs(#20173,78,#20171,0,"reg3")
hasLocation(#20173,#20077)
enclosing_stmt(#20173,#20169)
expr_containers(#20173,#20001)
literals("reg3","reg3",#20173)
decl(#20173,#20131)
#20174=*
exprs(#20174,12,#20171,1,"new Reg ... ""bar"")")
#20175=@"loc,{#10000},6,12,7,18"
locations_default(#20175,#10000,6,12,7,18)
hasLocation(#20174,#20175)
enclosing_stmt(#20174,#20169)
expr_containers(#20174,#20001)
#20176=*
exprs(#20176,79,#20174,-1,"RegExp")
hasLocation(#20176,#20083)
enclosing_stmt(#20176,#20169)
expr_containers(#20176,#20001)
literals("RegExp","RegExp",#20176)
bind(#20176,#20140)
#20177=*
exprs(#20177,34,#20174,0,"""foo"" + ""bar""")
#20178=@"loc,{#10000},7,5,7,17"
locations_default(#20178,#10000,7,5,7,17)
hasLocation(#20177,#20178)
enclosing_stmt(#20177,#20169)
expr_containers(#20177,#20001)
#20179=*
exprs(#20179,4,#20177,0,"""foo""")
hasLocation(#20179,#20087)
enclosing_stmt(#20179,#20169)
expr_containers(#20179,#20001)
literals("foo","""foo""",#20179)
#20180=*
regexpterm(#20180,14,#20179,0,"foo")
#20181=@"loc,{#10000},7,6,7,8"
locations_default(#20181,#10000,7,6,7,8)
hasLocation(#20180,#20181)
regexp_const_value(#20180,"foo")
#20182=*
exprs(#20182,4,#20177,1,"""bar""")
hasLocation(#20182,#20091)
enclosing_stmt(#20182,#20169)
expr_containers(#20182,#20001)
literals("bar","""bar""",#20182)
#20183=*
regexpterm(#20183,14,#20182,0,"bar")
#20184=@"loc,{#10000},7,14,7,16"
locations_default(#20184,#10000,7,14,7,16)
hasLocation(#20183,#20184)
regexp_const_value(#20183,"bar")
#20185=*
regexpterm(#20185,14,#20177,0,"foobar")
#20186=@"loc,{#10000},7,6,7,16"
locations_default(#20186,#10000,7,6,7,16)
hasLocation(#20185,#20186)
regexp_const_value(#20185,"foobar")
#20187=*
stmts(#20187,18,#20001,3,"var reg ... qux""\n);")
#20188=@"loc,{#10000},9,1,14,2"
locations_default(#20188,#10000,9,1,14,2)
hasLocation(#20187,#20188)
stmt_containers(#20187,#20001)
#20189=*
exprs(#20189,64,#20187,0,"reg4 = ... ""qux""\n)")
#20190=@"loc,{#10000},9,5,14,1"
locations_default(#20190,#10000,9,5,14,1)
hasLocation(#20189,#20190)
enclosing_stmt(#20189,#20187)
expr_containers(#20189,#20001)
#20191=*
exprs(#20191,78,#20189,0,"reg4")
hasLocation(#20191,#20099)
enclosing_stmt(#20191,#20187)
expr_containers(#20191,#20001)
literals("reg4","reg4",#20191)
decl(#20191,#20132)
#20192=*
exprs(#20192,12,#20189,1,"new Reg ... ""qux""\n)")
#20193=@"loc,{#10000},9,12,14,1"
locations_default(#20193,#10000,9,12,14,1)
hasLocation(#20192,#20193)
enclosing_stmt(#20192,#20187)
expr_containers(#20192,#20001)
#20194=*
exprs(#20194,79,#20192,-1,"RegExp")
hasLocation(#20194,#20105)
enclosing_stmt(#20194,#20187)
expr_containers(#20194,#20001)
literals("RegExp","RegExp",#20194)
bind(#20194,#20140)
#20195=*
exprs(#20195,34,#20192,0,"""foo"" + ... ""qux""")
#20196=@"loc,{#10000},10,5,13,9"
locations_default(#20196,#10000,10,5,13,9)
hasLocation(#20195,#20196)
enclosing_stmt(#20195,#20187)
expr_containers(#20195,#20001)
#20197=*
exprs(#20197,34,#20195,0,"""foo"" + ... ""baz""")
#20198=@"loc,{#10000},10,5,12,9"
locations_default(#20198,#10000,10,5,12,9)
hasLocation(#20197,#20198)
enclosing_stmt(#20197,#20187)
expr_containers(#20197,#20001)
#20199=*
exprs(#20199,34,#20197,0,"""foo"" + \n ""bar""")
#20200=@"loc,{#10000},10,5,11,9"
locations_default(#20200,#10000,10,5,11,9)
hasLocation(#20199,#20200)
enclosing_stmt(#20199,#20187)
expr_containers(#20199,#20001)
#20201=*
exprs(#20201,4,#20199,0,"""foo""")
hasLocation(#20201,#20109)
enclosing_stmt(#20201,#20187)
expr_containers(#20201,#20001)
literals("foo","""foo""",#20201)
#20202=*
regexpterm(#20202,14,#20201,0,"foo")
#20203=@"loc,{#10000},10,6,10,8"
locations_default(#20203,#10000,10,6,10,8)
hasLocation(#20202,#20203)
regexp_const_value(#20202,"foo")
#20204=*
exprs(#20204,4,#20199,1,"""bar""")
hasLocation(#20204,#20113)
enclosing_stmt(#20204,#20187)
expr_containers(#20204,#20001)
literals("bar","""bar""",#20204)
#20205=*
regexpterm(#20205,14,#20204,0,"bar")
#20206=@"loc,{#10000},11,6,11,8"
locations_default(#20206,#10000,11,6,11,8)
hasLocation(#20205,#20206)
regexp_const_value(#20205,"bar")
#20207=*
exprs(#20207,4,#20197,1,"""baz""")
hasLocation(#20207,#20117)
enclosing_stmt(#20207,#20187)
expr_containers(#20207,#20001)
literals("baz","""baz""",#20207)
#20208=*
regexpterm(#20208,14,#20207,0,"baz")
#20209=@"loc,{#10000},12,6,12,8"
locations_default(#20209,#10000,12,6,12,8)
hasLocation(#20208,#20209)
regexp_const_value(#20208,"baz")
#20210=*
exprs(#20210,4,#20195,1,"""qux""")
hasLocation(#20210,#20121)
enclosing_stmt(#20210,#20187)
expr_containers(#20210,#20001)
literals("qux","""qux""",#20210)
#20211=*
regexpterm(#20211,14,#20210,0,"qux")
#20212=@"loc,{#10000},13,6,13,8"
locations_default(#20212,#10000,13,6,13,8)
hasLocation(#20211,#20212)
regexp_const_value(#20211,"qux")
#20213=*
regexpterm(#20213,14,#20195,0,"foobarbazqux")
#20214=@"loc,{#10000},10,6,13,9"
locations_default(#20214,#10000,10,6,13,9)
hasLocation(#20213,#20214)
regexp_const_value(#20213,"foobarbazqux")
#20215=*
entry_cfg_node(#20215,#20001)
#20216=@"loc,{#10000},1,1,1,0"
locations_default(#20216,#10000,1,1,1,0)
hasLocation(#20215,#20216)
#20217=*
exit_cfg_node(#20217,#20001)
hasLocation(#20217,#20127)
successor(#20187,#20191)
successor(#20210,#20195)
successor(#20207,#20197)
successor(#20204,#20199)
successor(#20201,#20204)
successor(#20199,#20207)
successor(#20197,#20210)
successor(#20195,#20192)
successor(#20194,#20201)
successor(#20192,#20189)
successor(#20191,#20194)
successor(#20189,#20217)
successor(#20169,#20173)
successor(#20182,#20177)
successor(#20179,#20182)
successor(#20177,#20174)
successor(#20176,#20179)
successor(#20174,#20171)
successor(#20173,#20176)
successor(#20171,#20187)
successor(#20151,#20155)
successor(#20164,#20159)
successor(#20161,#20164)
successor(#20159,#20156)
successor(#20158,#20161)
successor(#20156,#20153)
successor(#20155,#20158)
successor(#20153,#20169)
successor(#20133,#20136)
successor(#20146,#20141)
successor(#20143,#20146)
successor(#20141,#20137)
successor(#20139,#20143)
successor(#20137,#20134)
successor(#20136,#20139)
successor(#20134,#20151)
successor(#20215,#20133)
numlines(#10000,14,11,0)
filetype(#10000,"javascript")

View File

@@ -1559,6 +1559,14 @@ class URShiftExpr extends @urshift_expr, BinaryExpr {
*/
class AddExpr extends @add_expr, BinaryExpr {
override string getOperator() { result = "+" }
/**
* Gets the value of this string concatenation parsed as a regular expression, if possible.
*
* All string literals have an associated regular expression tree, provided they can
* be parsed without syntax errors.
*/
RegExpTerm asRegExp() { this = result.getParent() }
}
/**

View File

@@ -155,7 +155,7 @@ class RegExpTerm extends Locatable, @regexpterm {
exists(RegExpParent parent | parent = getRootTerm().getParent() |
parent instanceof RegExpLiteral
or
parent.(StringLiteral).flow() instanceof RegExpPatternSource
parent.(Expr).flow() instanceof RegExpPatternSource
)
}
@@ -1104,6 +1104,30 @@ private class StringRegExpPatternSource extends RegExpPatternSource {
override RegExpTerm getRegExpTerm() { result = asExpr().(StringLiteral).asRegExp() }
}
/**
* A node whose string value may flow to a position where it is interpreted
* as a part of a regular expression.
*/
private class StringConcatRegExpPatternSource extends RegExpPatternSource {
DataFlow::Node parse;
StringConcatRegExpPatternSource() { this = regExpSource(parse) }
override DataFlow::Node getAParse() { result = parse }
override DataFlow::SourceNode getARegExpObject() {
exists(DataFlow::InvokeNode constructor |
constructor = DataFlow::globalVarRef("RegExp").getAnInvocation() and
parse = constructor.getArgument(0) and
result = constructor
)
}
override string getPattern() { result = getStringValue() }
override RegExpTerm getRegExpTerm() { result = asExpr().(AddExpr).asRegExp() }
}
module RegExp {
/** Gets the string `"?"` used to represent a regular expression whose flags are unknown. */
string unknownFlag() { result = "?" }

View File

@@ -855,7 +855,7 @@ regexpterm (unique int id: @regexpterm,
int idx: int ref,
varchar(900) tostring: string ref);
@regexpparent = @regexpterm | @regexp_literal | @string_literal;
@regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr;
case @regexpterm.kind of
0 = @regexp_alt

View File

@@ -512,3 +512,8 @@
| tst.js:384:15:384:26 | ([AB]\|[ab])* | Strings with many repetitions of 'A' can start matching anywhere after the start of the preceeding ([AB]\|[ab])*C |
| tst.js:385:14:385:25 | ([DE]\|[de])* | Strings with many repetitions of 'd' can start matching anywhere after the start of the preceeding ([DE]\|[de])*F |
| tst.js:388:14:388:20 | (a\|aa)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a\|aa)*$ |
| tst.js:391:6:394:6 | (a\|aa)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a\|aa)*b$ |
| tst.js:398:7:399:5 | (c\|cc)* | Strings with many repetitions of 'c' can start matching anywhere after the start of the preceeding ((c\|cc)*\|(d\|dd)*\|(e\|ee)*)f$ |
| tst.js:399:7:400:5 | (d\|dd)* | Strings with many repetitions of 'd' can start matching anywhere after the start of the preceeding ((c\|cc)*\|(d\|dd)*\|(e\|ee)*)f$ |
| tst.js:400:7:401:2 | (e\|ee)* | Strings with many repetitions of 'e' can start matching anywhere after the start of the preceeding ((c\|cc)*\|(d\|dd)*\|(e\|ee)*)f$ |
| tst.js:404:6:405:8 | (g\|gg)* | Strings with many repetitions of 'g' can start matching anywhere after the start of the preceeding (g\|gg)*h$ |

View File

@@ -183,3 +183,8 @@
| tst.js:385:14:385:25 | ([DE]\|[de])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'd'. |
| tst.js:387:27:387:33 | (a\|aa)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. |
| tst.js:388:14:388:20 | (a\|aa)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. |
| tst.js:391:6:394:6 | (a\|aa)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. |
| tst.js:398:7:399:5 | (c\|cc)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'cc'. |
| tst.js:399:7:400:5 | (d\|dd)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'dd'. |
| tst.js:400:7:401:2 | (e\|ee)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'ee'. |
| tst.js:404:6:405:8 | (g\|gg)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'gg'. |

View File

@@ -385,4 +385,21 @@ var good47 = /([AB]|[ab])*C/;
var bad92 = /([DE]|[de])*F/i;
var bad93 = /(?<=^v?|\sv?)(a|aa)*$/;
var bad94 = /(a|aa)*$/;
var bad94 = /(a|aa)*$/;
var bad95 = new RegExp(
"(a" +
"|" +
"aa)*" +
"b$"
);
var bad96 = new RegExp("(" +
"(c|cc)*|" +
"(d|dd)*|" +
"(e|ee)*" +
")f$");
var bad97 = new RegExp(
"(g|gg" +
")*h$");

View File

@@ -15,11 +15,15 @@
| tst-IncompleteHostnameRegExp.js:38:3:38:43 | ^(http\|https):\\/\\/www.example.com\\/p\\/f\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:38:2:38:44 | /^(http ... p\\/f\\// | here |
| tst-IncompleteHostnameRegExp.js:39:5:39:30 | http:\\/\\/sub.example.com\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:39:2:39:33 | /^(http ... om\\/)/g | here |
| tst-IncompleteHostnameRegExp.js:40:3:40:29 | ^https?:\\/\\/api.example.com | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:40:2:40:30 | /^https ... le.com/ | here |
| tst-IncompleteHostnameRegExp.js:41:42:41:48 | ^https?://.+\\.example\\.com/ | This regular expression has an unrestricted wildcard '.+' which may cause 'example\\.com/' to be matched anywhere in the URL, outside the hostname. | tst-IncompleteHostnameRegExp.js:41:13:41:71 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:41:42:41:70 | ^https?://.+\\.example\\.com/ | This string, which is used as a regular expression $@, has an unrestricted wildcard '.+' which may cause 'example\\.com/' to be matched anywhere in the URL, outside the hostname. | tst-IncompleteHostnameRegExp.js:41:13:41:71 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:43:3:43:32 | ^https:\\/\\/[a-z]*.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:43:2:43:33 | /^https ... e.com$/ | here |
| tst-IncompleteHostnameRegExp.js:44:32:44:45 | .+.example.net | This regular expression has an unescaped '.' before 'example.net', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:44:9:44:101 | '^proto ... ernal)' | here |
| tst-IncompleteHostnameRegExp.js:44:47:44:62 | .+.example-a.com | This regular expression has an unescaped '.' before 'example-a.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:44:9:44:101 | '^proto ... ernal)' | here |
| tst-IncompleteHostnameRegExp.js:44:64:44:79 | .+.example-b.com | This regular expression has an unescaped '.' before 'example-b.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:44:9:44:101 | '^proto ... ernal)' | here |
| tst-IncompleteHostnameRegExp.js:48:42:48:47 | ^https?://.+.example\\.com/ | This regular expression has an unescaped '.' before 'example\\.com/', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:48:13:48:69 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:48:42:48:47 | ^https?://.+.example\\.com/ | This regular expression has an unrestricted wildcard '.+' which may cause 'example\\.com/' to be matched anywhere in the URL, outside the hostname. | tst-IncompleteHostnameRegExp.js:48:13:48:69 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:48:42:48:68 | ^https?://.+.example\\.com/ | This string, which is used as a regular expression $@, has an unescaped '.' before 'example\\.com/', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:48:13:48:69 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:48:42:48:68 | ^https?://.+.example\\.com/ | This string, which is used as a regular expression $@, has an unrestricted wildcard '.+' which may cause 'example\\.com/' to be matched anywhere in the URL, outside the hostname. | tst-IncompleteHostnameRegExp.js:48:13:48:69 | '^http: ... \\.com/' | here |
| tst-IncompleteHostnameRegExp.js:53:14:53:35 | test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:53:13:53:36 | 'test.' ... e.com$' | here |
| tst-IncompleteHostnameRegExp.js:59:5:59:20 | foo.example\\.com | This regular expression has an unescaped '.' before 'example\\.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:59:2:59:32 | /^(foo. ... ever)$/ | here |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: add string concatenations as regexp parents
compatibility: backwards