Skip to content

Commit 13c6f55

Browse files
authored
Merge pull request #1085 from asger-semmle/extract-symbol
Approved by xiemaisi
2 parents 47e6210 + f2ec35c commit 13c6f55

File tree

11 files changed

+107
-12
lines changed

11 files changed

+107
-12
lines changed

javascript/extractor/lib/typescript/src/type_table.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ export class TypeTable {
251251
* A symbol string is a `;`-separated string consisting of:
252252
* - a tag string, `root`, `member`, or `other`,
253253
* - an empty string or a `file:pos` string to distinguish this from symbols with other lexical roots,
254-
* - the unqualified name of the symbol,
255-
* - for non-root symbols, the ID of the parent symbol.
254+
* - the ID of the parent symbol, or an empty string if this is a root symbol,
255+
* - the unqualified name of the symbol.
256256
*
257257
* Symbol strings serve the same dual purpose as type strings (see `typeIds`).
258258
*/
@@ -667,11 +667,11 @@ export class TypeTable {
667667
private getSymbolString(symbol: AugmentedSymbol): string {
668668
let parent = symbol.parent;
669669
if (parent == null || parent.escapedName === ts.InternalSymbolName.Global) {
670-
return "root;" + this.getSymbolDeclarationString(symbol) + ";" + symbol.name;
670+
return "root;" + this.getSymbolDeclarationString(symbol) + ";;" + symbol.name;
671671
} else if (parent.exports != null && parent.exports.get(symbol.escapedName) === symbol) {
672-
return "member;;" + symbol.name + ";" + this.getSymbolId(parent);
672+
return "member;;" + this.getSymbolId(parent) + ";" + symbol.name;
673673
} else {
674-
return "other;" + this.getSymbolDeclarationString(symbol) + ";" + symbol.name + ";" + this.getSymbolId(parent);
674+
return "other;" + this.getSymbolDeclarationString(symbol) + ";" + this.getSymbolId(parent) + ";" + symbol.name;
675675
}
676676
}
677677

javascript/extractor/src/com/semmle/ts/extractor/TypeExtractor.java

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import com.google.gson.JsonObject;
55
import com.semmle.util.trap.TrapWriter;
66
import com.semmle.util.trap.TrapWriter.Label;
7+
8+
import java.util.ArrayList;
79
import java.util.LinkedHashMap;
10+
import java.util.List;
811
import java.util.Map;
912

1013
/**
@@ -97,7 +100,7 @@ public void extract() {
97100
private void extractType(int id) {
98101
Label lbl = trapWriter.globalID("type;" + id);
99102
String contents = table.getTypeString(id);
100-
String[] parts = contents.split(";");
103+
String[] parts = split(contents);
101104
int kind = tagToKind.get(parts[0]);
102105
trapWriter.addTuple("types", lbl, kind, table.getTypeToStringValue(id));
103106
int firstChild = 1;
@@ -160,14 +163,15 @@ private void extractPropertyLookups(JsonObject lookups) {
160163
}
161164

162165
private void extractSymbol(int index) {
163-
// Format is: kind;decl;name[;parent]
164-
String[] parts = table.getSymbolString(index).split(";");
166+
// Format is: kind;decl;parent;name
167+
String[] parts = split(table.getSymbolString(index), 4);
165168
int kind = symbolKind.get(parts[0]);
166-
String name = parts[2];
169+
String name = parts[3];
167170
Label label = trapWriter.globalID("symbol;" + index);
168171
trapWriter.addTuple("symbols", label, kind, name);
169-
if (parts.length == 4) {
170-
Label parentLabel = trapWriter.globalID("symbol;" + parts[3]);
172+
String parentStr = parts[2];
173+
if (parentStr.length() > 0) {
174+
Label parentLabel = trapWriter.globalID("symbol;" + parentStr);
171175
trapWriter.addTuple("symbol_parent", label, parentLabel);
172176
}
173177
}
@@ -185,7 +189,7 @@ private void extractSymbolNameMapping(String relationName, JsonObject mappings)
185189
private void extractSignature(int index) {
186190
// Format is:
187191
// kind;numTypeParams;requiredParams;returnType(;paramName;paramType)*
188-
String[] parts = table.getSignatureString(index).split(";");
192+
String[] parts = split(table.getSignatureString(index));
189193
Label label = trapWriter.globalID("signature;" + index);
190194
int kind = Integer.parseInt(parts[0]);
191195
int numberOfTypeParameters = Integer.parseInt(parts[1]);
@@ -269,4 +273,35 @@ private void extractSelfTypes(JsonObject table) {
269273
trapWriter.globalID("type;" + typeId));
270274
}
271275
}
276+
277+
/** Like {@link #split(String)} without a limit. */
278+
private static String[] split(String input) {
279+
return split(input, -1);
280+
}
281+
282+
/**
283+
* Splits the input around the semicolon (<code>;</code>) character, preserving all empty
284+
* substrings.
285+
*
286+
* <p>At most <code>limit</code> substrings will be extracted. If the limit is reached, the last
287+
* substring will extend to the end of the string, possibly itself containing semicolons.
288+
*
289+
* <p>Note that the {@link String#split(String)} method does not preserve empty substrings at the
290+
* end of the string in case the string ends with a semicolon.
291+
*/
292+
private static String[] split(String input, int limit) {
293+
List<String> result = new ArrayList<String>();
294+
int lastPos = 0;
295+
for (int i = 0; i < input.length(); ++i) {
296+
if (input.charAt(i) == ';') {
297+
result.add(input.substring(lastPos, i));
298+
lastPos = i + 1;
299+
if (result.size() == limit - 1) break;
300+
}
301+
}
302+
result.add(input.substring(lastPos));
303+
return result.toArray(EMPTY_STRING_ARRAY);
304+
}
305+
306+
private static final String[] EMPTY_STRING_ARRAY = new String[0];
272307
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
| in unknown scope |
2+
| Array in global scope |
3+
| Intl in global scope |
4+
| Intl.CollatorOptions in global scope |
5+
| Intl.NumberFormatOptions in global scope |
6+
| MK in unknown scope |
7+
| Mapped in test.ts |
8+
| RegExp in global scope |
9+
| RegExpMatchArray in global scope |
10+
| fn in test.ts |
11+
| test.ts |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from CanonicalName name
4+
select name
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type Mapped<MK extends string = ''> = {
2+
    [mk in MK]: string
3+
};
4+
5+
export function fn(ev: Mapped) {
6+
    const props: Mapped = {
7+
        ...ev
8+
    };
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["."]
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| Bar.Foo in global scope | Bar in global scope |
2+
| Intl.CollatorOptions in global scope | Intl in global scope |
3+
| Intl.NumberFormatOptions in global scope | Intl in global scope |
4+
| fn in test.ts | test.ts |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from CanonicalName typename
4+
select typename, typename.getParent()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type Mapped<MK extends string = ';'> = {
2+
    [mk in MK]: string
3+
};
4+
5+
export function fn(ev: Mapped) {
6+
    const props: Mapped = {
7+
        ...ev
8+
    };
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["."]
3+
}

0 commit comments

Comments
 (0)