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

Skip to content

Commit 2901b5e

Browse files
committed
JS: Add OffsetTranslation table (preserving behavior)
1 parent c327ee5 commit 2901b5e

5 files changed

Lines changed: 146 additions & 4 deletions

File tree

javascript/extractor/src/com/semmle/js/extractor/LocationManager.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public void setHasLocationTable(String hasLocation) {
6565

6666
/**
6767
* Emit location information for an AST node. The node's location is translated from the parser's
68-
* 0-based column numbering scheme into our 1-based scheme and then emitted as a snippet location.
68+
* 0-based column numbering scheme with exclusive offsets into our 1-based scheme with inclusive
69+
* end-offsets and then emitted as a snippet location.
6970
*/
7071
public void emitNodeLocation(SourceElement nd, Label lbl) {
7172
int sl = nd.getLoc().getStart().getLine(),
@@ -86,7 +87,15 @@ public void emitNodeLocation(SourceElement nd, Label lbl) {
8687
emitSnippetLocation(lbl, sl, sc, el, ec);
8788
}
8889

89-
/** Emit a relative location in the current snippet. */
90+
/**
91+
* Emit a relative location in the current snippet.
92+
*
93+
* @param lbl label to associate with the location
94+
* @param sl start line (1-based)
95+
* @param sc start column (1-based, inclusive)
96+
* @param el end line (1-based)
97+
* @param ec end column (1-based, inclusive)
98+
*/
9099
public void emitSnippetLocation(Label lbl, int sl, int sc, int el, int ec) {
91100
Position start = translatePosition(new Position(sl, sc, -1));
92101
Position end = translatePosition(new Position(el, ec, -1));
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.semmle.js.extractor;
2+
3+
import com.semmle.util.data.IntList;
4+
5+
/**
6+
* A mapping of some source range into a set of intervals in an output source range.
7+
*
8+
* <p>The mapping is constructed by adding "anchors": input/output pairs that correspond to the
9+
* beginning of an interval, which is assumed to end at the next anchor.
10+
*/
11+
public class OffsetTranslation {
12+
private IntList anchors = IntList.create();
13+
private IntList deltas = IntList.create();
14+
15+
/** Returns the mapping of x. */
16+
public int get(int x) {
17+
int index = anchors.binarySearch(x);
18+
if (index < 0) {
19+
// The insertion point is -index - 1.
20+
// Get the index immediately before that.
21+
index = -index - 2;
22+
if (index < 0) {
23+
// If queried before the first anchor, use the first anchor anyway.
24+
index = 0;
25+
}
26+
}
27+
return x + deltas.get(index);
28+
}
29+
30+
/**
31+
* Maps the given input offset to the given output offset.
32+
*
33+
* <p>This is added as an anchor. Any offset is mapped based on its closest preceding anchor.
34+
*/
35+
public void set(int from, int to) {
36+
anchors.add(from);
37+
deltas.add(to - from);
38+
}
39+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.semmle.js.extractor;
2+
3+
import com.semmle.util.data.IntList;
4+
5+
/**
6+
* A mapping from integers to integers, is encoded as a sequence of consecutive intervals and their
7+
* corresponding output intervals.
8+
*/
9+
public class OffsetTranslationBuilder {
10+
private IntList anchors = IntList.create();
11+
private IntList deltas = IntList.create();
12+
13+
/** Returns the mapping of x. */
14+
public int get(int x) {
15+
int index = anchors.binarySearch(x);
16+
if (index < 0) {
17+
// The insertion point is -index - 1.
18+
// Get the index immediately before that.
19+
index = -index - 2;
20+
if (index < 0) {
21+
// If queried before the first anchor, use the first anchor anyway.
22+
index = 0;
23+
}
24+
}
25+
return x + deltas.get(index);
26+
}
27+
28+
/**
29+
* Maps the given input offset to the given output offset.
30+
*
31+
* <p>This is added as an anchor. Any offset is mapped based on its closest preceding anchor.
32+
*/
33+
public void set(int from, int to) {
34+
anchors.add(from);
35+
deltas.add(to - from);
36+
}
37+
}

javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class RegExpExtractor {
5151
private final LocationManager locationManager;
5252
private final RegExpParser parser = new RegExpParser();
5353
private Position literalStart;
54+
private OffsetTranslation offsets;
5455

5556
public RegExpExtractor(TrapWriter trapwriter, LocationManager locationManager) {
5657
this.trapwriter = trapwriter;
@@ -121,8 +122,12 @@ private Label extractTerm(RegExpTerm term, Label parent, int idx) {
121122
public void emitLocation(SourceElement term, Label lbl) {
122123
int sl, sc, el, ec;
123124
sl = el = literalStart.getLine();
124-
sc = literalStart.getColumn() + 2 + term.getLoc().getStart().getColumn();
125-
ec = literalStart.getColumn() + 1 + term.getLoc().getEnd().getColumn();
125+
// the offset table accounts for the position on the line and for skipping the initial '/'
126+
sc = offsets.get(term.getLoc().getStart().getColumn());
127+
ec = offsets.get(term.getLoc().getEnd().getColumn());
128+
sc += 1; // convert to 1-based
129+
ec += 1; // convert to 1-based
130+
ec -= 1; // convert to inclusive
126131
locationManager.emitSnippetLocation(lbl, sl, sc, el, ec);
127132
}
128133

@@ -349,6 +354,8 @@ public void extract(String src, Node parent, boolean isSpeculativeParsing) {
349354
}
350355

351356
this.literalStart = parent.getLoc().getStart();
357+
offsets = new OffsetTranslation();
358+
offsets.set(0, literalStart.getColumn() + 1); // add 1 to skip the leading '/' or quote
352359
RegExpTerm ast = res.getAST();
353360
new V().visit(ast, trapwriter.localID(parent), 0);
354361

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.semmle.js.extractor.test;
2+
3+
import com.semmle.js.extractor.OffsetTranslation;
4+
import org.junit.Assert;
5+
import org.junit.Test;
6+
7+
public class OffsetTranslationTest {
8+
@Test
9+
public void testBasic() {
10+
OffsetTranslation table = new OffsetTranslation();
11+
table.set(0, 10);
12+
table.set(100, 250);
13+
Assert.assertEquals(10, table.get(0));
14+
Assert.assertEquals(15, table.get(5));
15+
Assert.assertEquals(85, table.get(75));
16+
Assert.assertEquals(109, table.get(99));
17+
Assert.assertEquals(250, table.get(100));
18+
Assert.assertEquals(251, table.get(101));
19+
}
20+
21+
@Test
22+
public void testLookupBefore() {
23+
OffsetTranslation table = new OffsetTranslation();
24+
table.set(0, 10);
25+
table.set(100, 250);
26+
Assert.assertEquals(9, table.get(-1));
27+
}
28+
29+
@Test
30+
public void testIdentity() {
31+
OffsetTranslation table = new OffsetTranslation();
32+
table.set(0, 0);
33+
Assert.assertEquals(0, table.get(0));
34+
Assert.assertEquals(75, table.get(75));
35+
}
36+
37+
@Test
38+
public void testDuplicateAnchor() {
39+
OffsetTranslation table = new OffsetTranslation();
40+
table.set(0, 0);
41+
table.set(10, 100);
42+
table.set(10, 100);
43+
table.set(20, 150);
44+
Assert.assertEquals(1, table.get(1));
45+
Assert.assertEquals(100, table.get(10));
46+
Assert.assertEquals(101, table.get(11));
47+
Assert.assertEquals(150, table.get(20));
48+
Assert.assertEquals(151, table.get(21));
49+
}
50+
}

0 commit comments

Comments
 (0)