Skip to content

Commit b4f67f2

Browse files
committed
JS: Extract types and signatures for functions
1 parent 999d10e commit b4f67f2

File tree

11 files changed

+172
-25
lines changed

11 files changed

+172
-25
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface AugmentedNode extends ts.Node {
2222
$symbol?: number;
2323
$resolvedSignature?: number;
2424
$overloadIndex?: number;
25+
$declaredSignature?: number;
2526
}
2627

2728
export type AugmentedPos = number;
@@ -263,6 +264,17 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
263264
namePart.$symbol = typeTable.getSymbolId(symbol);
264265
}
265266
}
267+
if (ts.isFunctionLike(node)) {
268+
let signature = typeChecker.getSignatureFromDeclaration(node);
269+
if (signature != null) {
270+
let kind = ts.isConstructSignatureDeclaration(node) || ts.isConstructorDeclaration(node)
271+
? ts.SignatureKind.Construct : ts.SignatureKind.Call;
272+
let id = typeTable.getSignatureId(kind, signature);
273+
if (id != null) {
274+
(node as AugmentedNode).$declaredSignature = id;
275+
}
276+
}
277+
}
266278
}
267279
}
268280
}
@@ -302,18 +314,24 @@ function isTypedNode(node: ts.Node): boolean {
302314
case ts.SyntaxKind.BinaryExpression:
303315
case ts.SyntaxKind.CallExpression:
304316
case ts.SyntaxKind.ClassExpression:
317+
case ts.SyntaxKind.ClassDeclaration:
305318
case ts.SyntaxKind.CommaListExpression:
306319
case ts.SyntaxKind.ConditionalExpression:
320+
case ts.SyntaxKind.Constructor:
307321
case ts.SyntaxKind.DeleteExpression:
308322
case ts.SyntaxKind.ElementAccessExpression:
309323
case ts.SyntaxKind.ExpressionStatement:
310324
case ts.SyntaxKind.ExpressionWithTypeArguments:
311325
case ts.SyntaxKind.FalseKeyword:
312326
case ts.SyntaxKind.FunctionDeclaration:
313327
case ts.SyntaxKind.FunctionExpression:
328+
case ts.SyntaxKind.GetAccessor:
314329
case ts.SyntaxKind.Identifier:
330+
case ts.SyntaxKind.IndexSignature:
315331
case ts.SyntaxKind.JsxExpression:
316332
case ts.SyntaxKind.LiteralType:
333+
case ts.SyntaxKind.MethodDeclaration:
334+
case ts.SyntaxKind.MethodSignature:
317335
case ts.SyntaxKind.NewExpression:
318336
case ts.SyntaxKind.NonNullExpression:
319337
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
@@ -327,6 +345,7 @@ function isTypedNode(node: ts.Node): boolean {
327345
case ts.SyntaxKind.PrefixUnaryExpression:
328346
case ts.SyntaxKind.PropertyAccessExpression:
329347
case ts.SyntaxKind.RegularExpressionLiteral:
348+
case ts.SyntaxKind.SetAccessor:
330349
case ts.SyntaxKind.StringLiteral:
331350
case ts.SyntaxKind.TaggedTemplateExpression:
332351
case ts.SyntaxKind.TemplateExpression:

javascript/extractor/src/com/semmle/js/ast/AFunctionExpression.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
public abstract class AFunctionExpression extends Expression implements IFunction {
1313
private final AFunction<? extends Node> fn;
1414
private int symbol = -1;
15+
private int declaredSignature = -1;
1516

1617
public AFunctionExpression(
1718
String type,
@@ -144,4 +145,14 @@ public int getSymbol() {
144145
public void setSymbol(int symbol) {
145146
this.symbol = symbol;
146147
}
148+
149+
@Override
150+
public int getDeclaredSignatureId() {
151+
return declaredSignature;
152+
}
153+
154+
@Override
155+
public void setDeclaredSignatureId(int id) {
156+
declaredSignature = id;
157+
}
147158
}

javascript/extractor/src/com/semmle/js/ast/FunctionDeclaration.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class FunctionDeclaration extends Statement implements IFunction {
1919
private final AFunction<? extends Node> fn;
2020
private final boolean hasDeclareKeyword;
2121
private int symbol = -1;
22+
private int staticType = -1;
23+
private int declaredSignature = -1;
2224

2325
public FunctionDeclaration(
2426
SourceLocation loc,
@@ -185,4 +187,24 @@ public int getSymbol() {
185187
public void setSymbol(int symbol) {
186188
this.symbol = symbol;
187189
}
190+
191+
@Override
192+
public int getStaticTypeId() {
193+
return staticType;
194+
}
195+
196+
@Override
197+
public void setStaticTypeId(int id) {
198+
staticType = id;
199+
}
200+
201+
@Override
202+
public int getDeclaredSignatureId() {
203+
return declaredSignature;
204+
}
205+
206+
@Override
207+
public void setDeclaredSignatureId(int id) {
208+
declaredSignature = id;
209+
}
188210
}

javascript/extractor/src/com/semmle/js/ast/IFunction.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import com.semmle.ts.ast.DecoratorList;
44
import com.semmle.ts.ast.INodeWithSymbol;
55
import com.semmle.ts.ast.ITypeExpression;
6+
import com.semmle.ts.ast.ITypedAstNode;
67
import com.semmle.ts.ast.TypeParameter;
78
import java.util.List;
89

910
/** A function declaration or expression. */
10-
public interface IFunction extends IStatementContainer, INodeWithSymbol {
11+
public interface IFunction extends IStatementContainer, INodeWithSymbol, ITypedAstNode {
1112
/** The function name; may be null for function expressions. */
1213
public Identifier getId();
1314

@@ -63,4 +64,15 @@ public interface IFunction extends IStatementContainer, INodeWithSymbol {
6364
public List<DecoratorList> getParameterDecorators();
6465

6566
public boolean hasDeclareKeyword();
67+
68+
/**
69+
* Gets the type signature of this function as determined by the TypeScript compiler, or -1 if no
70+
* call signature was extracted.
71+
*
72+
* <p>The ID refers to a signature in a table that is extracted on a per-project basis, and the
73+
* meaning of this type ID is not available at the AST level.
74+
*/
75+
public int getDeclaredSignatureId();
76+
77+
public void setDeclaredSignatureId(int id);
6678
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,7 @@ public Label visit(FunctionDeclaration nd, Context c) {
762762
trapwriter.addTuple("hasDeclareKeyword", key);
763763
}
764764
extractFunction(nd, key);
765+
emitStaticType(nd, key);
765766
return key;
766767
}
767768

@@ -833,7 +834,13 @@ private void extractFunction(IFunction nd, Label key) {
833834
extractParameterDefaultsAndTypes(nd, key, i);
834835

835836
extractFunctionAttributes(nd, key);
837+
838+
// Extract associated symbol and signature
836839
emitNodeSymbol(nd, key);
840+
if (nd.getDeclaredSignatureId() != -1) {
841+
Label signatureKey = trapwriter.globalID("signature;" + nd.getDeclaredSignatureId());
842+
trapwriter.addTuple("declared_function_signature", key, signatureKey);
843+
}
837844

838845
boolean oldIsStrict = isStrict;
839846
isStrict = bodyIsStrict;

javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.semmle.js.ast.ForStatement;
4343
import com.semmle.js.ast.FunctionDeclaration;
4444
import com.semmle.js.ast.FunctionExpression;
45+
import com.semmle.js.ast.IFunction;
4546
import com.semmle.js.ast.INode;
4647
import com.semmle.js.ast.IPattern;
4748
import com.semmle.js.ast.Identifier;
@@ -670,6 +671,13 @@ private void attachResolvedSignature(InvokeExpression node, JsonObject json) {
670671
attachSymbolInformation(node, json);
671672
}
672673

674+
/** Attached the declared call signature to a function. */
675+
private void attachDeclaredSignature(IFunction node, JsonObject json) {
676+
if (json.has("$declaredSignature")) {
677+
node.setDeclaredSignatureId(json.get("$declaredSignature").getAsInt());
678+
}
679+
}
680+
673681
/**
674682
* Convert the given array of TypeScript AST nodes into a list of JavaScript AST nodes, skipping
675683
* any {@code null} elements.
@@ -786,15 +794,18 @@ private Node convertArrayType(JsonObject node, SourceLocation loc) throws ParseE
786794
}
787795

788796
private Node convertArrowFunction(JsonObject node, SourceLocation loc) throws ParseError {
789-
return new ArrowFunctionExpression(
790-
loc,
791-
convertParameters(node),
792-
convertChild(node, "body"),
793-
false,
794-
hasModifier(node, "AsyncKeyword"),
795-
convertChildrenNotNull(node, "typeParameters"),
796-
convertParameterTypes(node),
797-
convertChildAsType(node, "type"));
797+
ArrowFunctionExpression function =
798+
new ArrowFunctionExpression(
799+
loc,
800+
convertParameters(node),
801+
convertChild(node, "body"),
802+
false,
803+
hasModifier(node, "AsyncKeyword"),
804+
convertChildrenNotNull(node, "typeParameters"),
805+
convertParameterTypes(node),
806+
convertChildAsType(node, "type"));
807+
attachDeclaredSignature(function, node);
808+
return function;
798809
}
799810

800811
private Node convertAwaitExpression(JsonObject node, SourceLocation loc) throws ParseError {
@@ -1044,6 +1055,8 @@ private Node convertConstructor(JsonObject node, SourceLocation loc) throws Pars
10441055
null,
10451056
null);
10461057
attachSymbolInformation(value, node);
1058+
attachStaticType(value, node);
1059+
attachDeclaredSignature(value, node);
10471060
List<FieldDefinition> parameterFields = convertParameterFields(node);
10481061
return new MethodDefinition(loc, flags, methodKind, key, value, parameterFields);
10491062
}
@@ -1234,6 +1247,8 @@ private Node convertFunctionDeclaration(JsonObject node, SourceLocation loc) thr
12341247
returnType,
12351248
thisParam);
12361249
attachSymbolInformation(function, node);
1250+
attachStaticType(function, node);
1251+
attachDeclaredSignature(function, node);
12371252
return fixExports(loc, function);
12381253
}
12391254

@@ -1247,18 +1262,22 @@ private Node convertFunctionExpression(JsonObject node, SourceLocation loc) thro
12471262
List<DecoratorList> paramDecorators = convertParameterDecorators(node);
12481263
ITypeExpression returnType = convertChildAsType(node, "type");
12491264
ITypeExpression thisParam = convertThisParameterType(node);
1250-
return new FunctionExpression(
1251-
loc,
1252-
fnId,
1253-
params,
1254-
fnbody,
1255-
generator,
1256-
async,
1257-
convertChildrenNotNull(node, "typeParameters"),
1258-
paramTypes,
1259-
paramDecorators,
1260-
returnType,
1261-
thisParam);
1265+
FunctionExpression function =
1266+
new FunctionExpression(
1267+
loc,
1268+
fnId,
1269+
params,
1270+
fnbody,
1271+
generator,
1272+
async,
1273+
convertChildrenNotNull(node, "typeParameters"),
1274+
paramTypes,
1275+
paramDecorators,
1276+
returnType,
1277+
thisParam);
1278+
attachStaticType(function, node);
1279+
attachDeclaredSignature(function, node);
1280+
return function;
12621281
}
12631282

12641283
private Node convertFunctionType(JsonObject node, SourceLocation loc) throws ParseError {
@@ -1591,7 +1610,7 @@ private FunctionExpression convertImplicitFunction(JsonObject node, SourceLocati
15911610
List<DecoratorList> paramDecorators = convertParameterDecorators(node);
15921611
List<TypeParameter> typeParameters = convertChildrenNotNull(node, "typeParameters");
15931612
ITypeExpression thisType = convertThisParameterType(node);
1594-
FunctionExpression method =
1613+
FunctionExpression function =
15951614
new FunctionExpression(
15961615
loc,
15971616
null,
@@ -1604,8 +1623,10 @@ private FunctionExpression convertImplicitFunction(JsonObject node, SourceLocati
16041623
paramDecorators,
16051624
returnType,
16061625
thisType);
1607-
attachSymbolInformation(method, node);
1608-
return method;
1626+
attachSymbolInformation(function, node);
1627+
attachStaticType(function, node);
1628+
attachDeclaredSignature(function, node);
1629+
return function;
16091630
}
16101631

16111632
private Node convertNamespaceDeclaration(JsonObject node, SourceLocation loc) throws ParseError {

javascript/ql/src/semmle/javascript/Functions.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,13 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
416416
* This predicate is only populated for files extracted with full TypeScript extraction.
417417
*/
418418
CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
419+
420+
/**
421+
* Gets the call signature of this function, as determined by the TypeScript compiler, if any.
422+
*/
423+
CallSignatureType getCallSignature() {
424+
declared_function_signature(this, result)
425+
}
419426
}
420427

421428
/**

javascript/ql/src/semmlecode.javascript.dbscheme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,11 @@ ast_node_type(
641641
unique int node: @typed_ast_node ref,
642642
int typ: @type ref);
643643

644+
declared_function_signature(
645+
unique int node: @function ref,
646+
int sig: @signature_type ref
647+
);
648+
644649
invoke_expr_signature(
645650
unique int node: @invokeexpr ref,
646651
int sig: @signature_type ref

javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.expected

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@ test_ExprSignature
66
| tst.ts:12:8:12:8 | x | number |
77
| tst.ts:16:8:16:8 | x | number |
88
| tst.ts:17:8:17:8 | x | any |
9+
| tst.ts:21:3:21:28 | method( ... string; | (x: number): string |
910
| tst.ts:21:10:21:10 | x | number |
11+
| tst.ts:23:3:23:38 | overloa ... number; | (x: any): any |
12+
| tst.ts:23:3:23:38 | overloa ... number; | (x: number): number |
13+
| tst.ts:23:3:23:38 | overloa ... number; | (x: string): string |
1014
| tst.ts:23:20:23:20 | x | number |
15+
| tst.ts:24:3:24:38 | overloa ... string; | (x: any): any |
16+
| tst.ts:24:3:24:38 | overloa ... string; | (x: number): number |
17+
| tst.ts:24:3:24:38 | overloa ... string; | (x: string): string |
1118
| tst.ts:24:20:24:20 | x | string |
19+
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: any): any |
20+
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: number): number |
21+
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: string): string |
1222
| tst.ts:25:20:25:20 | x | any |
1323
| tst.ts:28:5:28:5 | m | Method |
1424
| tst.ts:29:1:29:1 | m | Method |
@@ -22,14 +32,21 @@ test_ExprSignature
2232
| tst.ts:30:1:30:25 | m.overl ... ("foo") | string |
2333
| tst.ts:30:20:30:24 | "foo" | "foo" |
2434
| tst.ts:33:3:33:10 | callback | (x: number): string |
35+
| tst.ts:33:13:33:33 | (x: num ... string | (x: number): string |
2536
| tst.ts:33:14:33:14 | x | number |
37+
| tst.ts:37:3:37:18 | method(x: T): T; | (x: T): T |
2638
| tst.ts:37:10:37:10 | x | T |
2739
| tst.ts:40:10:40:12 | foo | (g: Generic<string>): string |
2840
| tst.ts:40:14:40:14 | g | Generic<string> |
2941
| tst.ts:41:10:41:10 | g | Generic<string> |
3042
| tst.ts:41:10:41:17 | g.method | (x: string): string |
3143
| tst.ts:41:10:41:24 | g.method("foo") | string |
3244
| tst.ts:41:19:41:23 | "foo" | "foo" |
45+
| tst.ts:44:15:44:15 | C | C |
46+
| tst.ts:45:3:45:25 | constru ... tring); | any |
47+
| tst.ts:45:15:45:15 | x | string |
48+
| tst.ts:46:3:46:25 | constru ... umber); | any |
49+
| tst.ts:46:15:46:15 | x | number |
3350
test_TypeReferenceSig
3451
| Callable | function | 0 | (x: number): string |
3552
| Newable | constructor | 0 | new (x: number): any |
@@ -38,3 +55,20 @@ test_TypeReferenceSig
3855
| OverloadedCallable | function | 2 | (x: any): any |
3956
| OverloadedNewable | constructor | 0 | new (x: number): OverloadedNewable |
4057
| OverloadedNewable | constructor | 1 | new (x: any): any |
58+
test_FunctionCallSig
59+
| tst.ts:2:3:2:22 | (x: number): string; | (x: number): string |
60+
| tst.ts:6:3:6:22 | (x: number): number; | (x: number): number |
61+
| tst.ts:7:3:7:22 | (x: string): string; | (x: string): string |
62+
| tst.ts:8:3:8:16 | (x: any): any; | (x: any): any |
63+
| tst.ts:12:3:12:23 | new (x: ... ): any; | new (x: number): any |
64+
| tst.ts:16:3:16:37 | new (x: ... ewable; | new (x: number): OverloadedNewable |
65+
| tst.ts:17:3:17:20 | new (x: any): any; | new (x: any): any |
66+
| tst.ts:21:3:21:28 | method( ... string; | (x: number): string |
67+
| tst.ts:23:3:23:38 | overloa ... number; | (x: number): number |
68+
| tst.ts:24:3:24:38 | overloa ... string; | (x: string): string |
69+
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: any): any |
70+
| tst.ts:33:13:33:33 | (x: num ... string | (x: number): string |
71+
| tst.ts:37:3:37:18 | method(x: T): T; | (x: T): T |
72+
| tst.ts:40:1:42:1 | functio ... oo");\\n} | (g: Generic<string>): string |
73+
| tst.ts:45:3:45:25 | constru ... tring); | new (x: string): C |
74+
| tst.ts:46:3:46:25 | constru ... umber); | new (x: number): C |

javascript/ql/test/library-tests/TypeScript/CallSignatureTypes/test.ql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ query predicate test_ExprSignature(Expr expr, string type) {
1616
query predicate test_TypeReferenceSig(TypeReference type, SignatureKind kind, int n, CallSignatureType sig) {
1717
sig = type.getSignature(kind, n)
1818
}
19+
20+
query predicate test_FunctionCallSig(Function f, CallSignatureType sig) {
21+
sig = f.getCallSignature()
22+
}

0 commit comments

Comments
 (0)