Added intersection support

This commit is contained in:
Napalys
2025-03-02 19:24:32 +01:00
parent fa5093f6ad
commit 381b5ebe8a
5 changed files with 250 additions and 226 deletions

View File

@@ -0,0 +1,22 @@
package com.semmle.js.ast.regexp;
import com.semmle.js.ast.SourceLocation;
import java.util.List;
public class CharacterClassIntersection extends RegExpTerm {
private final List<RegExpTerm> intersections;
public CharacterClassIntersection(SourceLocation loc, List<RegExpTerm> intersections) {
super(loc, "CharacterClassIntersection");
this.intersections = intersections;
}
@Override
public void accept(Visitor v) {
v.visit(this);
}
public List<RegExpTerm> getIntersections() {
return intersections;
}
}

View File

@@ -63,4 +63,6 @@ public interface Visitor {
public void visit(UnicodePropertyEscape nd);
public void visit(CharacterClassQuotedString nd);
public void visit(CharacterClassIntersection nd);
}

View File

@@ -23,6 +23,7 @@ import com.semmle.js.ast.regexp.Error;
import com.semmle.js.ast.regexp.Group;
import com.semmle.js.ast.regexp.HexEscapeSequence;
import com.semmle.js.ast.regexp.IdentityEscape;
import com.semmle.js.ast.regexp.CharacterClassIntersection;
import com.semmle.js.ast.regexp.Literal;
import com.semmle.js.ast.regexp.NamedBackReference;
import com.semmle.js.ast.regexp.NonWordBoundary;
@@ -94,6 +95,7 @@ public class RegExpExtractor {
termkinds.put("ZeroWidthNegativeLookbehind", 26);
termkinds.put("UnicodePropertyEscape", 27);
termkinds.put("CharacterClassQuotedString", 28);
termkinds.put("CharacterClassIntersection", 29);
}
private static final String[] errmsgs =
@@ -352,6 +354,14 @@ public class RegExpExtractor {
Label lbl = extractTerm(nd, parent, idx);
visit(nd.getTerm(), lbl, 0);
}
@Override
public void visit(CharacterClassIntersection nd) {
Label lbl = extractTerm(nd, parent, idx);
int i = 0;
for (RegExpTerm element : nd.getIntersections())
visit(element, lbl, i++);
}
}
public void extract(String src, SourceMap sourceMap, Node parent, boolean isSpeculativeParsing, String flags) {

View File

@@ -19,6 +19,7 @@ import com.semmle.js.ast.regexp.Error;
import com.semmle.js.ast.regexp.Group;
import com.semmle.js.ast.regexp.HexEscapeSequence;
import com.semmle.js.ast.regexp.IdentityEscape;
import com.semmle.js.ast.regexp.CharacterClassIntersection;
import com.semmle.js.ast.regexp.NamedBackReference;
import com.semmle.js.ast.regexp.NonWordBoundary;
import com.semmle.js.ast.regexp.OctalEscape;
@@ -37,6 +38,7 @@ import com.semmle.js.ast.regexp.ZeroWidthPositiveLookahead;
import com.semmle.js.ast.regexp.ZeroWidthPositiveLookbehind;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** A parser for ECMAScript 2018 regular expressions. */
@@ -561,10 +563,16 @@ public class RegExpParser {
return this.finishTerm(new CharacterClass(loc, elements, inverted));
}
private enum CharacterClassType {
STANDARD,
INTERSECTION,
}
// ECMA 2024 `v` flag allows nested character classes.
private RegExpTerm parseNestedCharacterClass() {
SourceLocation loc = new SourceLocation(pos());
List<RegExpTerm> elements = new ArrayList<>();
CharacterClassType classType = CharacterClassType.STANDARD;
this.match("[");
boolean inverted = this.match("^");
@@ -576,11 +584,23 @@ public class RegExpParser {
if (lookahead("[")) {
elements.add(parseNestedCharacterClass());
}
else if (lookahead("&&")) {
this.match("&&");
classType = CharacterClassType.INTERSECTION;
}
else {
elements.add(this.parseCharacterClassElement());
}
}
return this.finishTerm(new CharacterClass(loc, elements, inverted));
// Create appropriate RegExpTerm based on the detected class type
switch (classType) {
case INTERSECTION:
return this.finishTerm(new CharacterClass(loc, Collections.singletonList(new CharacterClassIntersection(loc, elements)), inverted));
case STANDARD:
default:
return this.finishTerm(new CharacterClass(loc, elements, inverted));
}
}
private static final List<String> escapeClasses = Arrays.asList("d", "D", "s", "S", "w", "W");

View File

@@ -164,264 +164,234 @@ regexpterm(#20052,23,#20051,0,"[[abc]&&[bcd]]")
locations_default(#20053,#10000,1,2,1,15)
hasLocation(#20052,#20053)
#20054=*
regexpterm(#20054,23,#20052,0,"[abc]")
#20055=@"loc,{#10000},1,3,1,7"
locations_default(#20055,#10000,1,3,1,7)
hasLocation(#20054,#20055)
#20056=*
regexpterm(#20056,14,#20054,0,"a")
#20057=@"loc,{#10000},1,4,1,4"
locations_default(#20057,#10000,1,4,1,4)
hasLocation(#20056,#20057)
regexp_const_value(#20056,"a")
#20058=*
regexpterm(#20058,14,#20054,1,"b")
#20059=@"loc,{#10000},1,5,1,5"
locations_default(#20059,#10000,1,5,1,5)
hasLocation(#20058,#20059)
regexp_const_value(#20058,"b")
#20060=*
regexpterm(#20060,14,#20054,2,"c")
#20061=@"loc,{#10000},1,6,1,6"
locations_default(#20061,#10000,1,6,1,6)
hasLocation(#20060,#20061)
regexp_const_value(#20060,"c")
#20062=*
regexpterm(#20062,14,#20052,1,"&")
#20063=@"loc,{#10000},1,8,1,8"
locations_default(#20063,#10000,1,8,1,8)
hasLocation(#20062,#20063)
regexp_const_value(#20062,"&")
#20064=*
regexpterm(#20064,14,#20052,2,"&")
#20065=@"loc,{#10000},1,9,1,9"
locations_default(#20065,#10000,1,9,1,9)
hasLocation(#20064,#20065)
regexp_const_value(#20064,"&")
#20066=*
regexpterm(#20066,23,#20052,3,"[bcd]")
#20067=@"loc,{#10000},1,10,1,14"
locations_default(#20067,#10000,1,10,1,14)
hasLocation(#20066,#20067)
#20068=*
regexpterm(#20068,14,#20066,0,"b")
#20069=@"loc,{#10000},1,11,1,11"
locations_default(#20069,#10000,1,11,1,11)
hasLocation(#20068,#20069)
regexp_const_value(#20068,"b")
#20070=*
regexpterm(#20070,14,#20066,1,"c")
#20071=@"loc,{#10000},1,12,1,12"
locations_default(#20071,#10000,1,12,1,12)
hasLocation(#20070,#20071)
regexp_const_value(#20070,"c")
#20072=*
regexpterm(#20072,14,#20066,2,"d")
#20073=@"loc,{#10000},1,13,1,13"
locations_default(#20073,#10000,1,13,1,13)
hasLocation(#20072,#20073)
regexp_const_value(#20072,"d")
regexpterm(#20054,29,#20052,0,"[[abc]&&[bcd]]")
hasLocation(#20054,#20053)
#20055=*
regexpterm(#20055,23,#20054,0,"[abc]")
#20056=@"loc,{#10000},1,3,1,7"
locations_default(#20056,#10000,1,3,1,7)
hasLocation(#20055,#20056)
#20057=*
regexpterm(#20057,14,#20055,0,"a")
#20058=@"loc,{#10000},1,4,1,4"
locations_default(#20058,#10000,1,4,1,4)
hasLocation(#20057,#20058)
regexp_const_value(#20057,"a")
#20059=*
regexpterm(#20059,14,#20055,1,"b")
#20060=@"loc,{#10000},1,5,1,5"
locations_default(#20060,#10000,1,5,1,5)
hasLocation(#20059,#20060)
regexp_const_value(#20059,"b")
#20061=*
regexpterm(#20061,14,#20055,2,"c")
#20062=@"loc,{#10000},1,6,1,6"
locations_default(#20062,#10000,1,6,1,6)
hasLocation(#20061,#20062)
regexp_const_value(#20061,"c")
#20063=*
regexpterm(#20063,23,#20054,1,"[bcd]")
#20064=@"loc,{#10000},1,10,1,14"
locations_default(#20064,#10000,1,10,1,14)
hasLocation(#20063,#20064)
#20065=*
regexpterm(#20065,14,#20063,0,"b")
#20066=@"loc,{#10000},1,11,1,11"
locations_default(#20066,#10000,1,11,1,11)
hasLocation(#20065,#20066)
regexp_const_value(#20065,"b")
#20067=*
regexpterm(#20067,14,#20063,1,"c")
#20068=@"loc,{#10000},1,12,1,12"
locations_default(#20068,#10000,1,12,1,12)
hasLocation(#20067,#20068)
regexp_const_value(#20067,"c")
#20069=*
regexpterm(#20069,14,#20063,2,"d")
#20070=@"loc,{#10000},1,13,1,13"
locations_default(#20070,#10000,1,13,1,13)
hasLocation(#20069,#20070)
regexp_const_value(#20069,"d")
#20071=*
stmts(#20071,2,#20001,1,"/abc&&bcd/v;")
#20072=@"loc,{#10000},2,1,2,12"
locations_default(#20072,#10000,2,1,2,12)
hasLocation(#20071,#20072)
stmt_containers(#20071,#20001)
#20073=*
exprs(#20073,5,#20071,0,"/abc&&bcd/v")
hasLocation(#20073,#20035)
enclosing_stmt(#20073,#20071)
expr_containers(#20073,#20001)
literals("/abc&&bcd/v","/abc&&bcd/v",#20073)
#20074=*
stmts(#20074,2,#20001,1,"/abc&&bcd/v;")
#20075=@"loc,{#10000},2,1,2,12"
locations_default(#20075,#10000,2,1,2,12)
regexpterm(#20074,14,#20073,0,"abc&&bcd")
#20075=@"loc,{#10000},2,2,2,9"
locations_default(#20075,#10000,2,2,2,9)
hasLocation(#20074,#20075)
stmt_containers(#20074,#20001)
regexp_const_value(#20074,"abc&&bcd")
#20076=*
exprs(#20076,5,#20074,0,"/abc&&bcd/v")
hasLocation(#20076,#20035)
enclosing_stmt(#20076,#20074)
expr_containers(#20076,#20001)
literals("/abc&&bcd/v","/abc&&bcd/v",#20076)
#20077=*
regexpterm(#20077,14,#20076,0,"abc&&bcd")
#20078=@"loc,{#10000},2,2,2,9"
locations_default(#20078,#10000,2,2,2,9)
hasLocation(#20077,#20078)
regexp_const_value(#20077,"abc&&bcd")
stmts(#20076,2,#20001,2,"/[abc]&&[bcd]/v;")
#20077=@"loc,{#10000},3,1,3,16"
locations_default(#20077,#10000,3,1,3,16)
hasLocation(#20076,#20077)
stmt_containers(#20076,#20001)
#20078=*
exprs(#20078,5,#20076,0,"/[abc]&&[bcd]/v")
hasLocation(#20078,#20039)
enclosing_stmt(#20078,#20076)
expr_containers(#20078,#20001)
literals("/[abc]&&[bcd]/v","/[abc]&&[bcd]/v",#20078)
#20079=*
stmts(#20079,2,#20001,2,"/[abc]&&[bcd]/v;")
#20080=@"loc,{#10000},3,1,3,16"
locations_default(#20080,#10000,3,1,3,16)
regexpterm(#20079,1,#20078,0,"[abc]&&[bcd]")
#20080=@"loc,{#10000},3,2,3,13"
locations_default(#20080,#10000,3,2,3,13)
hasLocation(#20079,#20080)
stmt_containers(#20079,#20001)
#20081=*
exprs(#20081,5,#20079,0,"/[abc]&&[bcd]/v")
hasLocation(#20081,#20039)
enclosing_stmt(#20081,#20079)
expr_containers(#20081,#20001)
literals("/[abc]&&[bcd]/v","/[abc]&&[bcd]/v",#20081)
#20082=*
regexpterm(#20082,1,#20081,0,"[abc]&&[bcd]")
#20083=@"loc,{#10000},3,2,3,13"
locations_default(#20083,#10000,3,2,3,13)
hasLocation(#20082,#20083)
#20084=*
regexpterm(#20084,23,#20082,0,"[abc]")
#20085=@"loc,{#10000},3,2,3,6"
locations_default(#20085,#10000,3,2,3,6)
hasLocation(#20084,#20085)
#20086=*
regexpterm(#20086,14,#20084,0,"a")
#20087=@"loc,{#10000},3,3,3,3"
locations_default(#20087,#10000,3,3,3,3)
hasLocation(#20086,#20087)
regexp_const_value(#20086,"a")
#20088=*
regexpterm(#20088,14,#20084,1,"b")
#20089=@"loc,{#10000},3,4,3,4"
locations_default(#20089,#10000,3,4,3,4)
hasLocation(#20088,#20089)
regexp_const_value(#20088,"b")
#20090=*
regexpterm(#20090,14,#20084,2,"c")
#20091=@"loc,{#10000},3,5,3,5"
locations_default(#20091,#10000,3,5,3,5)
hasLocation(#20090,#20091)
regexp_const_value(#20090,"c")
#20092=*
regexpterm(#20092,14,#20082,1,"&&")
#20093=@"loc,{#10000},3,7,3,8"
locations_default(#20093,#10000,3,7,3,8)
hasLocation(#20092,#20093)
regexp_const_value(#20092,"&&")
#20094=*
regexpterm(#20094,23,#20082,2,"[bcd]")
#20095=@"loc,{#10000},3,9,3,13"
locations_default(#20095,#10000,3,9,3,13)
hasLocation(#20094,#20095)
#20096=*
regexpterm(#20096,14,#20094,0,"b")
#20097=@"loc,{#10000},3,10,3,10"
locations_default(#20097,#10000,3,10,3,10)
hasLocation(#20096,#20097)
regexp_const_value(#20096,"b")
#20098=*
regexpterm(#20098,14,#20094,1,"c")
#20099=@"loc,{#10000},3,11,3,11"
locations_default(#20099,#10000,3,11,3,11)
hasLocation(#20098,#20099)
regexp_const_value(#20098,"c")
#20100=*
regexpterm(#20100,14,#20094,2,"d")
#20101=@"loc,{#10000},3,12,3,12"
locations_default(#20101,#10000,3,12,3,12)
hasLocation(#20100,#20101)
regexp_const_value(#20100,"d")
regexpterm(#20081,23,#20079,0,"[abc]")
#20082=@"loc,{#10000},3,2,3,6"
locations_default(#20082,#10000,3,2,3,6)
hasLocation(#20081,#20082)
#20083=*
regexpterm(#20083,14,#20081,0,"a")
#20084=@"loc,{#10000},3,3,3,3"
locations_default(#20084,#10000,3,3,3,3)
hasLocation(#20083,#20084)
regexp_const_value(#20083,"a")
#20085=*
regexpterm(#20085,14,#20081,1,"b")
#20086=@"loc,{#10000},3,4,3,4"
locations_default(#20086,#10000,3,4,3,4)
hasLocation(#20085,#20086)
regexp_const_value(#20085,"b")
#20087=*
regexpterm(#20087,14,#20081,2,"c")
#20088=@"loc,{#10000},3,5,3,5"
locations_default(#20088,#10000,3,5,3,5)
hasLocation(#20087,#20088)
regexp_const_value(#20087,"c")
#20089=*
regexpterm(#20089,14,#20079,1,"&&")
#20090=@"loc,{#10000},3,7,3,8"
locations_default(#20090,#10000,3,7,3,8)
hasLocation(#20089,#20090)
regexp_const_value(#20089,"&&")
#20091=*
regexpterm(#20091,23,#20079,2,"[bcd]")
#20092=@"loc,{#10000},3,9,3,13"
locations_default(#20092,#10000,3,9,3,13)
hasLocation(#20091,#20092)
#20093=*
regexpterm(#20093,14,#20091,0,"b")
#20094=@"loc,{#10000},3,10,3,10"
locations_default(#20094,#10000,3,10,3,10)
hasLocation(#20093,#20094)
regexp_const_value(#20093,"b")
#20095=*
regexpterm(#20095,14,#20091,1,"c")
#20096=@"loc,{#10000},3,11,3,11"
locations_default(#20096,#10000,3,11,3,11)
hasLocation(#20095,#20096)
regexp_const_value(#20095,"c")
#20097=*
regexpterm(#20097,14,#20091,2,"d")
#20098=@"loc,{#10000},3,12,3,12"
locations_default(#20098,#10000,3,12,3,12)
hasLocation(#20097,#20098)
regexp_const_value(#20097,"d")
#20099=*
stmts(#20099,2,#20001,3,"/[[abc] ... [c]]/v;")
#20100=@"loc,{#10000},7,1,7,23"
locations_default(#20100,#10000,7,1,7,23)
hasLocation(#20099,#20100)
stmt_containers(#20099,#20001)
#20101=*
exprs(#20101,5,#20099,0,"/[[abc] ... &[c]]/v")
hasLocation(#20101,#20043)
enclosing_stmt(#20101,#20099)
expr_containers(#20101,#20001)
literals("/[[abc]&&[bcd]&&[c]]/v","/[[abc]&&[bcd]&&[c]]/v",#20101)
#20102=*
stmts(#20102,2,#20001,3,"/[[abc] ... [c]]/v;")
#20103=@"loc,{#10000},7,1,7,23"
locations_default(#20103,#10000,7,1,7,23)
regexpterm(#20102,23,#20101,0,"[[abc]&&[bcd]&&[c]]")
#20103=@"loc,{#10000},7,2,7,20"
locations_default(#20103,#10000,7,2,7,20)
hasLocation(#20102,#20103)
stmt_containers(#20102,#20001)
#20104=*
exprs(#20104,5,#20102,0,"/[[abc] ... &[c]]/v")
hasLocation(#20104,#20043)
enclosing_stmt(#20104,#20102)
expr_containers(#20104,#20001)
literals("/[[abc]&&[bcd]&&[c]]/v","/[[abc]&&[bcd]&&[c]]/v",#20104)
regexpterm(#20104,29,#20102,0,"[[abc]&&[bcd]&&[c]]")
hasLocation(#20104,#20103)
#20105=*
regexpterm(#20105,23,#20104,0,"[[abc]&&[bcd]&&[c]]")
#20106=@"loc,{#10000},7,2,7,20"
locations_default(#20106,#10000,7,2,7,20)
regexpterm(#20105,23,#20104,0,"[abc]")
#20106=@"loc,{#10000},7,3,7,7"
locations_default(#20106,#10000,7,3,7,7)
hasLocation(#20105,#20106)
#20107=*
regexpterm(#20107,23,#20105,0,"[abc]")
#20108=@"loc,{#10000},7,3,7,7"
locations_default(#20108,#10000,7,3,7,7)
regexpterm(#20107,14,#20105,0,"a")
#20108=@"loc,{#10000},7,4,7,4"
locations_default(#20108,#10000,7,4,7,4)
hasLocation(#20107,#20108)
regexp_const_value(#20107,"a")
#20109=*
regexpterm(#20109,14,#20107,0,"a")
#20110=@"loc,{#10000},7,4,7,4"
locations_default(#20110,#10000,7,4,7,4)
regexpterm(#20109,14,#20105,1,"b")
#20110=@"loc,{#10000},7,5,7,5"
locations_default(#20110,#10000,7,5,7,5)
hasLocation(#20109,#20110)
regexp_const_value(#20109,"a")
regexp_const_value(#20109,"b")
#20111=*
regexpterm(#20111,14,#20107,1,"b")
#20112=@"loc,{#10000},7,5,7,5"
locations_default(#20112,#10000,7,5,7,5)
regexpterm(#20111,14,#20105,2,"c")
#20112=@"loc,{#10000},7,6,7,6"
locations_default(#20112,#10000,7,6,7,6)
hasLocation(#20111,#20112)
regexp_const_value(#20111,"b")
regexp_const_value(#20111,"c")
#20113=*
regexpterm(#20113,14,#20107,2,"c")
#20114=@"loc,{#10000},7,6,7,6"
locations_default(#20114,#10000,7,6,7,6)
regexpterm(#20113,23,#20104,1,"[bcd]")
#20114=@"loc,{#10000},7,10,7,14"
locations_default(#20114,#10000,7,10,7,14)
hasLocation(#20113,#20114)
regexp_const_value(#20113,"c")
#20115=*
regexpterm(#20115,14,#20105,1,"&")
#20116=@"loc,{#10000},7,8,7,8"
locations_default(#20116,#10000,7,8,7,8)
regexpterm(#20115,14,#20113,0,"b")
#20116=@"loc,{#10000},7,11,7,11"
locations_default(#20116,#10000,7,11,7,11)
hasLocation(#20115,#20116)
regexp_const_value(#20115,"&")
regexp_const_value(#20115,"b")
#20117=*
regexpterm(#20117,14,#20105,2,"&")
#20118=@"loc,{#10000},7,9,7,9"
locations_default(#20118,#10000,7,9,7,9)
regexpterm(#20117,14,#20113,1,"c")
#20118=@"loc,{#10000},7,12,7,12"
locations_default(#20118,#10000,7,12,7,12)
hasLocation(#20117,#20118)
regexp_const_value(#20117,"&")
regexp_const_value(#20117,"c")
#20119=*
regexpterm(#20119,23,#20105,3,"[bcd]")
#20120=@"loc,{#10000},7,10,7,14"
locations_default(#20120,#10000,7,10,7,14)
regexpterm(#20119,14,#20113,2,"d")
#20120=@"loc,{#10000},7,13,7,13"
locations_default(#20120,#10000,7,13,7,13)
hasLocation(#20119,#20120)
regexp_const_value(#20119,"d")
#20121=*
regexpterm(#20121,14,#20119,0,"b")
#20122=@"loc,{#10000},7,11,7,11"
locations_default(#20122,#10000,7,11,7,11)
regexpterm(#20121,23,#20104,2,"[c]")
#20122=@"loc,{#10000},7,17,7,19"
locations_default(#20122,#10000,7,17,7,19)
hasLocation(#20121,#20122)
regexp_const_value(#20121,"b")
#20123=*
regexpterm(#20123,14,#20119,1,"c")
#20124=@"loc,{#10000},7,12,7,12"
locations_default(#20124,#10000,7,12,7,12)
regexpterm(#20123,14,#20121,0,"c")
#20124=@"loc,{#10000},7,18,7,18"
locations_default(#20124,#10000,7,18,7,18)
hasLocation(#20123,#20124)
regexp_const_value(#20123,"c")
#20125=*
regexpterm(#20125,14,#20119,2,"d")
#20126=@"loc,{#10000},7,13,7,13"
locations_default(#20126,#10000,7,13,7,13)
entry_cfg_node(#20125,#20001)
#20126=@"loc,{#10000},1,1,1,0"
locations_default(#20126,#10000,1,1,1,0)
hasLocation(#20125,#20126)
regexp_const_value(#20125,"d")
#20127=*
regexpterm(#20127,14,#20105,4,"&")
#20128=@"loc,{#10000},7,15,7,15"
locations_default(#20128,#10000,7,15,7,15)
hasLocation(#20127,#20128)
regexp_const_value(#20127,"&")
#20129=*
regexpterm(#20129,14,#20105,5,"&")
#20130=@"loc,{#10000},7,16,7,16"
locations_default(#20130,#10000,7,16,7,16)
hasLocation(#20129,#20130)
regexp_const_value(#20129,"&")
#20131=*
regexpterm(#20131,23,#20105,6,"[c]")
#20132=@"loc,{#10000},7,17,7,19"
locations_default(#20132,#10000,7,17,7,19)
hasLocation(#20131,#20132)
#20133=*
regexpterm(#20133,14,#20131,0,"c")
#20134=@"loc,{#10000},7,18,7,18"
locations_default(#20134,#10000,7,18,7,18)
hasLocation(#20133,#20134)
regexp_const_value(#20133,"c")
#20135=*
entry_cfg_node(#20135,#20001)
#20136=@"loc,{#10000},1,1,1,0"
locations_default(#20136,#10000,1,1,1,0)
hasLocation(#20135,#20136)
#20137=*
exit_cfg_node(#20137,#20001)
hasLocation(#20137,#20047)
successor(#20102,#20104)
successor(#20104,#20137)
successor(#20079,#20081)
successor(#20081,#20102)
successor(#20074,#20076)
successor(#20076,#20079)
exit_cfg_node(#20127,#20001)
hasLocation(#20127,#20047)
successor(#20099,#20101)
successor(#20101,#20127)
successor(#20076,#20078)
successor(#20078,#20099)
successor(#20071,#20073)
successor(#20073,#20076)
successor(#20049,#20051)
successor(#20051,#20074)
successor(#20135,#20049)
successor(#20051,#20071)
successor(#20125,#20049)
numlines(#10000,7,4,7)
filetype(#10000,"javascript")