diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/CharacterClassQuotedString.java b/javascript/extractor/src/com/semmle/js/ast/regexp/CharacterClassQuotedString.java new file mode 100644 index 00000000000..b92e9e67c72 --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/CharacterClassQuotedString.java @@ -0,0 +1,25 @@ +package com.semmle.js.ast.regexp; + +import com.semmle.js.ast.SourceLocation; + +/** + * A '\q{}' escape sequence in a regular expression, which is a special extension + * to standard regular expressions. + */ +public class CharacterClassQuotedString extends RegExpTerm { + private final RegExpTerm term; + + public CharacterClassQuotedString(SourceLocation loc, RegExpTerm term) { + super(loc, "CharacterClassQuotedString"); + this.term = term; + } + + public RegExpTerm getTerm() { + return term; + } + + @Override + public void accept(Visitor v) { + v.visit(this); + } +} diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java index 3671a55694b..4af27e6aa8a 100644 --- a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java @@ -61,4 +61,6 @@ public interface Visitor { public void visit(ZeroWidthNegativeLookbehind nd); public void visit(UnicodePropertyEscape nd); + + public void visit(CharacterClassQuotedString nd); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java index 5c02a4e9935..1fc69458c10 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java @@ -10,6 +10,7 @@ import com.semmle.js.ast.regexp.BackReference; import com.semmle.js.ast.regexp.Caret; import com.semmle.js.ast.regexp.CharacterClass; import com.semmle.js.ast.regexp.CharacterClassEscape; +import com.semmle.js.ast.regexp.CharacterClassQuotedString; import com.semmle.js.ast.regexp.CharacterClassRange; import com.semmle.js.ast.regexp.Constant; import com.semmle.js.ast.regexp.ControlEscape; @@ -92,6 +93,7 @@ public class RegExpExtractor { termkinds.put("ZeroWidthPositiveLookbehind", 25); termkinds.put("ZeroWidthNegativeLookbehind", 26); termkinds.put("UnicodePropertyEscape", 27); + termkinds.put("CharacterClassQuotedString", 28); } private static final String[] errmsgs = @@ -344,6 +346,12 @@ public class RegExpExtractor { visit(nd.getLeft(), lbl, 0); visit(nd.getRight(), lbl, 1); } + + @Override + public void visit(CharacterClassQuotedString nd) { + Label lbl = extractTerm(nd, parent, idx); + visit(nd.getTerm(), lbl, 0); + } } public void extract(String src, SourceMap sourceMap, Node parent, boolean isSpeculativeParsing, String flags) { diff --git a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java index bc39ff31316..478c0c25f3b 100644 --- a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java @@ -6,6 +6,7 @@ import com.semmle.js.ast.regexp.BackReference; import com.semmle.js.ast.regexp.Caret; import com.semmle.js.ast.regexp.CharacterClass; import com.semmle.js.ast.regexp.CharacterClassEscape; +import com.semmle.js.ast.regexp.CharacterClassQuotedString; import com.semmle.js.ast.regexp.CharacterClassRange; import com.semmle.js.ast.regexp.Constant; import com.semmle.js.ast.regexp.ControlEscape; @@ -283,6 +284,45 @@ public class RegExpParser { return this.finishTerm(this.parseQuantifierOpt(loc, this.parseAtom())); } + private RegExpTerm parseDisjunctionInsideQuotedString() { + SourceLocation loc = new SourceLocation(pos()); + List disjuncts = new ArrayList<>(); + disjuncts.add(this.parseAlternativeInsideQuotedString()); + while (this.match("|")) { + disjuncts.add(this.parseAlternativeInsideQuotedString()); + } + if (disjuncts.size() == 1) return disjuncts.get(0); + return this.finishTerm(new Disjunction(loc, disjuncts)); + } + + private RegExpTerm parseAlternativeInsideQuotedString() { + SourceLocation loc = new SourceLocation(pos()); + StringBuilder sb = new StringBuilder(); + boolean escaped = false; + while (true) { + // If we're at the end of the string, something went wrong. + if (this.atEOS()) { + this.error(Error.UNEXPECTED_EOS); + break; + } + // We can end parsing if we're not escaped and we see a `|` which would mean Alternation + // or `}` which would mean the end of the Quoted String. + if(!escaped && this.lookahead(null, "|", "}")){ + break; + } + char c = this.nextChar(); + // Track whether the character is an escape character. + escaped = !escaped && (c == '\\'); + sb.append(c); + } + + String literal = sb.toString(); + loc.setEnd(pos()); + loc.setSource(literal); + + return new Constant(loc, literal); + } + private RegExpTerm parseQuantifierOpt(SourceLocation loc, RegExpTerm atom) { if (this.match("*")) return this.finishTerm(new Star(loc, atom, !this.match("?"))); if (this.match("+")) return this.finishTerm(new Plus(loc, atom, !this.match("?"))); @@ -427,6 +467,12 @@ public class RegExpParser { return this.finishTerm(new NamedBackReference(loc, name, "\\k<" + name + ">")); } + if (this.match("q{")) { + RegExpTerm term = parseDisjunctionInsideQuotedString(); + this.expectRBrace(); + return this.finishTerm(new CharacterClassQuotedString(loc, term)); + } + if (this.match("p{", "P{")) { String name = this.readIdentifier(); if (this.match("=")) { diff --git a/javascript/extractor/tests/es2024/output/trap/regex_quoted_string.js.trap b/javascript/extractor/tests/es2024/output/trap/regex_quoted_string.js.trap index 8b95ac62741..822a1302bcf 100644 --- a/javascript/extractor/tests/es2024/output/trap/regex_quoted_string.js.trap +++ b/javascript/extractor/tests/es2024/output/trap/regex_quoted_string.js.trap @@ -115,317 +115,172 @@ regexpterm(#20037,23,#20036,0,"[\q{abc}]") locations_default(#20038,#10000,1,2,1,10) hasLocation(#20037,#20038) #20039=* -regexpterm(#20039,21,#20037,0,"\q") -#20040=@"loc,{#10000},1,3,1,4" -locations_default(#20040,#10000,1,3,1,4) +regexpterm(#20039,28,#20037,0,"\q{abc}") +#20040=@"loc,{#10000},1,3,1,9" +locations_default(#20040,#10000,1,3,1,9) hasLocation(#20039,#20040) -regexp_const_value(#20039,"q") #20041=* -regexpterm(#20041,14,#20037,1,"{") -#20042=@"loc,{#10000},1,5,1,5" -locations_default(#20042,#10000,1,5,1,5) +regexpterm(#20041,14,#20039,0,"abc") +#20042=@"loc,{#10000},1,6,1,8" +locations_default(#20042,#10000,1,6,1,8) hasLocation(#20041,#20042) -regexp_const_value(#20041,"{") +regexp_const_value(#20041,"abc") #20043=* -regexpterm(#20043,14,#20037,2,"a") -#20044=@"loc,{#10000},1,6,1,6" -locations_default(#20044,#10000,1,6,1,6) -hasLocation(#20043,#20044) -regexp_const_value(#20043,"a") +stmts(#20043,2,#20001,1,"/[\q{ab ... cb}]/v;") +hasLocation(#20043,#20005) +stmt_containers(#20043,#20001) +#20044=* +exprs(#20044,5,#20043,0,"/[\q{abc|cbd|dcb}]/v") +hasLocation(#20044,#20017) +enclosing_stmt(#20044,#20043) +expr_containers(#20044,#20001) +literals("/[\q{abc|cbd|dcb}]/v","/[\q{abc|cbd|dcb}]/v",#20044) #20045=* -regexpterm(#20045,14,#20037,3,"b") -#20046=@"loc,{#10000},1,7,1,7" -locations_default(#20046,#10000,1,7,1,7) +regexpterm(#20045,23,#20044,0,"[\q{abc|cbd|dcb}]") +#20046=@"loc,{#10000},2,2,2,18" +locations_default(#20046,#10000,2,2,2,18) hasLocation(#20045,#20046) -regexp_const_value(#20045,"b") #20047=* -regexpterm(#20047,14,#20037,4,"c") -#20048=@"loc,{#10000},1,8,1,8" -locations_default(#20048,#10000,1,8,1,8) +regexpterm(#20047,28,#20045,0,"\q{abc|cbd|dcb}") +#20048=@"loc,{#10000},2,3,2,17" +locations_default(#20048,#10000,2,3,2,17) hasLocation(#20047,#20048) -regexp_const_value(#20047,"c") #20049=* -regexpterm(#20049,14,#20037,5,"}") -#20050=@"loc,{#10000},1,9,1,9" -locations_default(#20050,#10000,1,9,1,9) +regexpterm(#20049,0,#20047,0,"abc|cbd|dcb") +#20050=@"loc,{#10000},2,6,2,16" +locations_default(#20050,#10000,2,6,2,16) hasLocation(#20049,#20050) -regexp_const_value(#20049,"}") #20051=* -stmts(#20051,2,#20001,1,"/[\q{ab ... cb}]/v;") -hasLocation(#20051,#20005) -stmt_containers(#20051,#20001) -#20052=* -exprs(#20052,5,#20051,0,"/[\q{abc|cbd|dcb}]/v") -hasLocation(#20052,#20017) -enclosing_stmt(#20052,#20051) -expr_containers(#20052,#20001) -literals("/[\q{abc|cbd|dcb}]/v","/[\q{abc|cbd|dcb}]/v",#20052) +regexpterm(#20051,14,#20049,0,"abc") +#20052=@"loc,{#10000},2,6,2,8" +locations_default(#20052,#10000,2,6,2,8) +hasLocation(#20051,#20052) +regexp_const_value(#20051,"abc") #20053=* -regexpterm(#20053,23,#20052,0,"[\q{abc|cbd|dcb}]") -#20054=@"loc,{#10000},2,2,2,18" -locations_default(#20054,#10000,2,2,2,18) +regexpterm(#20053,14,#20049,1,"cbd") +#20054=@"loc,{#10000},2,10,2,12" +locations_default(#20054,#10000,2,10,2,12) hasLocation(#20053,#20054) +regexp_const_value(#20053,"cbd") #20055=* -regexpterm(#20055,21,#20053,0,"\q") -#20056=@"loc,{#10000},2,3,2,4" -locations_default(#20056,#10000,2,3,2,4) +regexpterm(#20055,14,#20049,2,"dcb") +#20056=@"loc,{#10000},2,14,2,16" +locations_default(#20056,#10000,2,14,2,16) hasLocation(#20055,#20056) -regexp_const_value(#20055,"q") +regexp_const_value(#20055,"dcb") #20057=* -regexpterm(#20057,14,#20053,1,"{") -#20058=@"loc,{#10000},2,5,2,5" -locations_default(#20058,#10000,2,5,2,5) -hasLocation(#20057,#20058) -regexp_const_value(#20057,"{") +stmts(#20057,2,#20001,2,"/[\q{\}}]/v;") +hasLocation(#20057,#20007) +stmt_containers(#20057,#20001) +#20058=* +exprs(#20058,5,#20057,0,"/[\q{\}}]/v") +hasLocation(#20058,#20021) +enclosing_stmt(#20058,#20057) +expr_containers(#20058,#20001) +literals("/[\q{\}}]/v","/[\q{\}}]/v",#20058) #20059=* -regexpterm(#20059,14,#20053,2,"a") -#20060=@"loc,{#10000},2,6,2,6" -locations_default(#20060,#10000,2,6,2,6) +regexpterm(#20059,23,#20058,0,"[\q{\}}]") +#20060=@"loc,{#10000},3,2,3,9" +locations_default(#20060,#10000,3,2,3,9) hasLocation(#20059,#20060) -regexp_const_value(#20059,"a") #20061=* -regexpterm(#20061,14,#20053,3,"b") -#20062=@"loc,{#10000},2,7,2,7" -locations_default(#20062,#10000,2,7,2,7) +regexpterm(#20061,28,#20059,0,"\q{\}}") +#20062=@"loc,{#10000},3,3,3,8" +locations_default(#20062,#10000,3,3,3,8) hasLocation(#20061,#20062) -regexp_const_value(#20061,"b") #20063=* -regexpterm(#20063,14,#20053,4,"c") -#20064=@"loc,{#10000},2,8,2,8" -locations_default(#20064,#10000,2,8,2,8) +regexpterm(#20063,14,#20061,0,"\}") +#20064=@"loc,{#10000},3,6,3,7" +locations_default(#20064,#10000,3,6,3,7) hasLocation(#20063,#20064) -regexp_const_value(#20063,"c") +regexp_const_value(#20063,"\}") #20065=* -regexpterm(#20065,14,#20053,5,"|") -#20066=@"loc,{#10000},2,9,2,9" -locations_default(#20066,#10000,2,9,2,9) -hasLocation(#20065,#20066) -regexp_const_value(#20065,"|") +stmts(#20065,2,#20001,3,"/[\q{\{}]/v;") +hasLocation(#20065,#20009) +stmt_containers(#20065,#20001) +#20066=* +exprs(#20066,5,#20065,0,"/[\q{\{}]/v") +hasLocation(#20066,#20025) +enclosing_stmt(#20066,#20065) +expr_containers(#20066,#20001) +literals("/[\q{\{}]/v","/[\q{\{}]/v",#20066) #20067=* -regexpterm(#20067,14,#20053,6,"c") -#20068=@"loc,{#10000},2,10,2,10" -locations_default(#20068,#10000,2,10,2,10) +regexpterm(#20067,23,#20066,0,"[\q{\{}]") +#20068=@"loc,{#10000},4,2,4,9" +locations_default(#20068,#10000,4,2,4,9) hasLocation(#20067,#20068) -regexp_const_value(#20067,"c") #20069=* -regexpterm(#20069,14,#20053,7,"b") -#20070=@"loc,{#10000},2,11,2,11" -locations_default(#20070,#10000,2,11,2,11) +regexpterm(#20069,28,#20067,0,"\q{\{}") +#20070=@"loc,{#10000},4,3,4,8" +locations_default(#20070,#10000,4,3,4,8) hasLocation(#20069,#20070) -regexp_const_value(#20069,"b") #20071=* -regexpterm(#20071,14,#20053,8,"d") -#20072=@"loc,{#10000},2,12,2,12" -locations_default(#20072,#10000,2,12,2,12) +regexpterm(#20071,14,#20069,0,"\{") +#20072=@"loc,{#10000},4,6,4,7" +locations_default(#20072,#10000,4,6,4,7) hasLocation(#20071,#20072) -regexp_const_value(#20071,"d") +regexp_const_value(#20071,"\{") #20073=* -regexpterm(#20073,14,#20053,9,"|") -#20074=@"loc,{#10000},2,13,2,13" -locations_default(#20074,#10000,2,13,2,13) -hasLocation(#20073,#20074) -regexp_const_value(#20073,"|") +stmts(#20073,2,#20001,4,"/[\q{cc|\}a|cc}]/v;") +hasLocation(#20073,#20011) +stmt_containers(#20073,#20001) +#20074=* +exprs(#20074,5,#20073,0,"/[\q{cc|\}a|cc}]/v") +hasLocation(#20074,#20029) +enclosing_stmt(#20074,#20073) +expr_containers(#20074,#20001) +literals("/[\q{cc|\}a|cc}]/v","/[\q{cc|\}a|cc}]/v",#20074) #20075=* -regexpterm(#20075,14,#20053,10,"d") -#20076=@"loc,{#10000},2,14,2,14" -locations_default(#20076,#10000,2,14,2,14) +regexpterm(#20075,23,#20074,0,"[\q{cc|\}a|cc}]") +#20076=@"loc,{#10000},5,2,5,16" +locations_default(#20076,#10000,5,2,5,16) hasLocation(#20075,#20076) -regexp_const_value(#20075,"d") #20077=* -regexpterm(#20077,14,#20053,11,"c") -#20078=@"loc,{#10000},2,15,2,15" -locations_default(#20078,#10000,2,15,2,15) +regexpterm(#20077,28,#20075,0,"\q{cc|\}a|cc}") +#20078=@"loc,{#10000},5,3,5,15" +locations_default(#20078,#10000,5,3,5,15) hasLocation(#20077,#20078) -regexp_const_value(#20077,"c") #20079=* -regexpterm(#20079,14,#20053,12,"b") -#20080=@"loc,{#10000},2,16,2,16" -locations_default(#20080,#10000,2,16,2,16) +regexpterm(#20079,0,#20077,0,"cc|\}a|cc") +#20080=@"loc,{#10000},5,6,5,14" +locations_default(#20080,#10000,5,6,5,14) hasLocation(#20079,#20080) -regexp_const_value(#20079,"b") #20081=* -regexpterm(#20081,14,#20053,13,"}") -#20082=@"loc,{#10000},2,17,2,17" -locations_default(#20082,#10000,2,17,2,17) +regexpterm(#20081,14,#20079,0,"cc") +#20082=@"loc,{#10000},5,6,5,7" +locations_default(#20082,#10000,5,6,5,7) hasLocation(#20081,#20082) -regexp_const_value(#20081,"}") +regexp_const_value(#20081,"cc") #20083=* -stmts(#20083,2,#20001,2,"/[\q{\}}]/v;") -hasLocation(#20083,#20007) -stmt_containers(#20083,#20001) -#20084=* -exprs(#20084,5,#20083,0,"/[\q{\}}]/v") -hasLocation(#20084,#20021) -enclosing_stmt(#20084,#20083) -expr_containers(#20084,#20001) -literals("/[\q{\}}]/v","/[\q{\}}]/v",#20084) +regexpterm(#20083,14,#20079,1,"\}a") +#20084=@"loc,{#10000},5,9,5,11" +locations_default(#20084,#10000,5,9,5,11) +hasLocation(#20083,#20084) +regexp_const_value(#20083,"\}a") #20085=* -regexpterm(#20085,23,#20084,0,"[\q{\}}]") -#20086=@"loc,{#10000},3,2,3,9" -locations_default(#20086,#10000,3,2,3,9) +regexpterm(#20085,14,#20079,2,"cc") +#20086=@"loc,{#10000},5,13,5,14" +locations_default(#20086,#10000,5,13,5,14) hasLocation(#20085,#20086) +regexp_const_value(#20085,"cc") #20087=* -regexpterm(#20087,21,#20085,0,"\q") -#20088=@"loc,{#10000},3,3,3,4" -locations_default(#20088,#10000,3,3,3,4) +entry_cfg_node(#20087,#20001) +#20088=@"loc,{#10000},1,1,1,0" +locations_default(#20088,#10000,1,1,1,0) hasLocation(#20087,#20088) -regexp_const_value(#20087,"q") #20089=* -regexpterm(#20089,14,#20085,1,"{") -#20090=@"loc,{#10000},3,5,3,5" -locations_default(#20090,#10000,3,5,3,5) -hasLocation(#20089,#20090) -regexp_const_value(#20089,"{") -#20091=* -regexpterm(#20091,21,#20085,2,"\}") -#20092=@"loc,{#10000},3,6,3,7" -locations_default(#20092,#10000,3,6,3,7) -hasLocation(#20091,#20092) -regexp_const_value(#20091,"}") -#20093=* -regexpterm(#20093,14,#20085,3,"}") -#20094=@"loc,{#10000},3,8,3,8" -locations_default(#20094,#10000,3,8,3,8) -hasLocation(#20093,#20094) -regexp_const_value(#20093,"}") -#20095=* -stmts(#20095,2,#20001,3,"/[\q{\{}]/v;") -hasLocation(#20095,#20009) -stmt_containers(#20095,#20001) -#20096=* -exprs(#20096,5,#20095,0,"/[\q{\{}]/v") -hasLocation(#20096,#20025) -enclosing_stmt(#20096,#20095) -expr_containers(#20096,#20001) -literals("/[\q{\{}]/v","/[\q{\{}]/v",#20096) -#20097=* -regexpterm(#20097,23,#20096,0,"[\q{\{}]") -#20098=@"loc,{#10000},4,2,4,9" -locations_default(#20098,#10000,4,2,4,9) -hasLocation(#20097,#20098) -#20099=* -regexpterm(#20099,21,#20097,0,"\q") -#20100=@"loc,{#10000},4,3,4,4" -locations_default(#20100,#10000,4,3,4,4) -hasLocation(#20099,#20100) -regexp_const_value(#20099,"q") -#20101=* -regexpterm(#20101,14,#20097,1,"{") -#20102=@"loc,{#10000},4,5,4,5" -locations_default(#20102,#10000,4,5,4,5) -hasLocation(#20101,#20102) -regexp_const_value(#20101,"{") -#20103=* -regexpterm(#20103,21,#20097,2,"\{") -#20104=@"loc,{#10000},4,6,4,7" -locations_default(#20104,#10000,4,6,4,7) -hasLocation(#20103,#20104) -regexp_const_value(#20103,"{") -#20105=* -regexpterm(#20105,14,#20097,3,"}") -#20106=@"loc,{#10000},4,8,4,8" -locations_default(#20106,#10000,4,8,4,8) -hasLocation(#20105,#20106) -regexp_const_value(#20105,"}") -#20107=* -stmts(#20107,2,#20001,4,"/[\q{cc|\}a|cc}]/v;") -hasLocation(#20107,#20011) -stmt_containers(#20107,#20001) -#20108=* -exprs(#20108,5,#20107,0,"/[\q{cc|\}a|cc}]/v") -hasLocation(#20108,#20029) -enclosing_stmt(#20108,#20107) -expr_containers(#20108,#20001) -literals("/[\q{cc|\}a|cc}]/v","/[\q{cc|\}a|cc}]/v",#20108) -#20109=* -regexpterm(#20109,23,#20108,0,"[\q{cc|\}a|cc}]") -#20110=@"loc,{#10000},5,2,5,16" -locations_default(#20110,#10000,5,2,5,16) -hasLocation(#20109,#20110) -#20111=* -regexpterm(#20111,21,#20109,0,"\q") -#20112=@"loc,{#10000},5,3,5,4" -locations_default(#20112,#10000,5,3,5,4) -hasLocation(#20111,#20112) -regexp_const_value(#20111,"q") -#20113=* -regexpterm(#20113,14,#20109,1,"{") -#20114=@"loc,{#10000},5,5,5,5" -locations_default(#20114,#10000,5,5,5,5) -hasLocation(#20113,#20114) -regexp_const_value(#20113,"{") -#20115=* -regexpterm(#20115,14,#20109,2,"c") -#20116=@"loc,{#10000},5,6,5,6" -locations_default(#20116,#10000,5,6,5,6) -hasLocation(#20115,#20116) -regexp_const_value(#20115,"c") -#20117=* -regexpterm(#20117,14,#20109,3,"c") -#20118=@"loc,{#10000},5,7,5,7" -locations_default(#20118,#10000,5,7,5,7) -hasLocation(#20117,#20118) -regexp_const_value(#20117,"c") -#20119=* -regexpterm(#20119,14,#20109,4,"|") -#20120=@"loc,{#10000},5,8,5,8" -locations_default(#20120,#10000,5,8,5,8) -hasLocation(#20119,#20120) -regexp_const_value(#20119,"|") -#20121=* -regexpterm(#20121,21,#20109,5,"\}") -#20122=@"loc,{#10000},5,9,5,10" -locations_default(#20122,#10000,5,9,5,10) -hasLocation(#20121,#20122) -regexp_const_value(#20121,"}") -#20123=* -regexpterm(#20123,14,#20109,6,"a") -#20124=@"loc,{#10000},5,11,5,11" -locations_default(#20124,#10000,5,11,5,11) -hasLocation(#20123,#20124) -regexp_const_value(#20123,"a") -#20125=* -regexpterm(#20125,14,#20109,7,"|") -#20126=@"loc,{#10000},5,12,5,12" -locations_default(#20126,#10000,5,12,5,12) -hasLocation(#20125,#20126) -regexp_const_value(#20125,"|") -#20127=* -regexpterm(#20127,14,#20109,8,"c") -#20128=@"loc,{#10000},5,13,5,13" -locations_default(#20128,#10000,5,13,5,13) -hasLocation(#20127,#20128) -regexp_const_value(#20127,"c") -#20129=* -regexpterm(#20129,14,#20109,9,"c") -#20130=@"loc,{#10000},5,14,5,14" -locations_default(#20130,#10000,5,14,5,14) -hasLocation(#20129,#20130) -regexp_const_value(#20129,"c") -#20131=* -regexpterm(#20131,14,#20109,10,"}") -#20132=@"loc,{#10000},5,15,5,15" -locations_default(#20132,#10000,5,15,5,15) -hasLocation(#20131,#20132) -regexp_const_value(#20131,"}") -#20133=* -entry_cfg_node(#20133,#20001) -#20134=@"loc,{#10000},1,1,1,0" -locations_default(#20134,#10000,1,1,1,0) -hasLocation(#20133,#20134) -#20135=* -exit_cfg_node(#20135,#20001) -hasLocation(#20135,#20033) -successor(#20107,#20108) -successor(#20108,#20135) -successor(#20095,#20096) -successor(#20096,#20107) -successor(#20083,#20084) -successor(#20084,#20095) -successor(#20051,#20052) -successor(#20052,#20083) +exit_cfg_node(#20089,#20001) +hasLocation(#20089,#20033) +successor(#20073,#20074) +successor(#20074,#20089) +successor(#20065,#20066) +successor(#20066,#20073) +successor(#20057,#20058) +successor(#20058,#20065) +successor(#20043,#20044) +successor(#20044,#20057) successor(#20035,#20036) -successor(#20036,#20051) -successor(#20133,#20035) +successor(#20036,#20043) +successor(#20087,#20035) numlines(#10000,5,5,0) filetype(#10000,"javascript")