Skip to content

Commit 3ed9575

Browse files
committed
TS: add support for bigints
1 parent 1c6deb6 commit 3ed9575

20 files changed

+119
-6
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ export class TypeTable {
505505
if (flags === ts.TypeFlags.Never) {
506506
return "never";
507507
}
508+
if (flags === ts.TypeFlags.BigInt) {
509+
return "bigint";
510+
}
508511
if (flags & ts.TypeFlags.Null) {
509512
return "null";
510513
}
@@ -536,6 +539,11 @@ export class TypeTable {
536539
if (flags & ts.TypeFlags.StringLiteral) {
537540
return "strlit;" + (type as ts.LiteralType).value;
538541
}
542+
if (flags & ts.TypeFlags.BigIntLiteral) {
543+
let literalType = type as ts.LiteralType;
544+
let value = literalType.value as ts.PseudoBigInt;
545+
return "bigintlit;" + (value.negative ? "-" : "") + value.base10Value;
546+
}
539547
if (flags & ts.TypeFlags.Union) {
540548
let unionType = type as ts.UnionType;
541549
if (unionType.types.length === 0) {

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.semmle.js.extractor;
22

3+
import com.semmle.jcorn.TokenType;
34
import com.semmle.js.ast.DefaultVisitor;
45
import com.semmle.js.ast.INode;
56
import com.semmle.js.ast.Identifier;
@@ -64,6 +65,7 @@ public class TypeExprKinds {
6465
private static final int importVarTypeAccess = 32;
6566
private static final int optionalTypeExpr = 33;
6667
private static final int restTypeExpr = 34;
68+
private static final int bigintLiteralTypeExpr = 35;
6769

6870
public static int getTypeExprKind(final INode type, final IdContext idcontext) {
6971
Integer kind = type.accept(new DefaultVisitor<Void, Integer>() {
@@ -159,6 +161,7 @@ public Integer visit(InterfaceTypeExpr nd, Void c) {
159161

160162
@Override
161163
public Integer visit(Literal nd, Void c) {
164+
TokenType type = nd.getTokenType();
162165
if (nd.getValue() == null) {
163166
// We represent the null type as a keyword type in QL, but in the extractor AST
164167
// it is a Literal because the TypeScript AST does not distinguish those.
@@ -167,12 +170,14 @@ public Integer visit(Literal nd, Void c) {
167170
// - TypeScript documentation does not treat the null type as a literal type.
168171
// - There is an "undefined" type, but there is no "undefined" literal.
169172
return keywordTypeExpr;
170-
} else if (nd.getValue() instanceof String) {
173+
} else if (type == TokenType.string) {
171174
return stringLiteralTypeExpr;
172-
} else if (nd.getValue() instanceof Number) {
175+
} else if (type == TokenType.num) {
173176
return numberLiteralTypeExpr;
174-
} else if (nd.getValue() instanceof Boolean) {
177+
} else if (type == TokenType._true || type == TokenType._false) {
175178
return booeleanLiteralTypeExpr;
179+
} else if (type == TokenType.bigint) {
180+
return bigintLiteralTypeExpr;
176181
} else {
177182
throw new CatastrophicError("Unsupported literal type expression kind: " + nd.getValue().getClass());
178183
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ private Node convertNodeUntyped(JsonObject node, String defaultKind) throws Pars
369369
return convertAsExpression(node, loc);
370370
case "AwaitExpression":
371371
return convertAwaitExpression(node, loc);
372+
case "BigIntKeyword":
373+
return convertKeywordTypeExpr(node, loc, "bigint");
374+
case "BigIntLiteral":
375+
return convertBigIntLiteral(node, loc);
372376
case "BinaryExpression":
373377
return convertBinaryExpression(node, loc);
374378
case "Block":
@@ -822,6 +826,12 @@ private Node convertAwaitExpression(JsonObject node, SourceLocation loc) throws
822826
return new AwaitExpression(loc, convertChild(node, "expression"));
823827
}
824828

829+
private Node convertBigIntLiteral(JsonObject node, SourceLocation loc) throws ParseError {
830+
String text = node.get("text").getAsString();
831+
String value = text.substring(0, text.length() - 1); // Remove the 'n' suffix.
832+
return new Literal(loc, TokenType.bigint, value);
833+
}
834+
825835
private Node convertBinaryExpression(JsonObject node, SourceLocation loc) throws ParseError {
826836
Expression left = convertChild(node, "left");
827837
Expression right = convertChild(node, "right");

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class TypeExtractor {
3131
private static final int thisKind = 20;
3232
private static final int numberLiteralTypeKind = 21;
3333
private static final int stringLiteralTypeKind = 22;
34+
private static final int bigintLiteralTypeKind = 25;
3435

3536
static {
3637
tagToKind.put("any", 0);
@@ -57,6 +58,8 @@ public class TypeExtractor {
5758
tagToKind.put("numlit", numberLiteralTypeKind);
5859
tagToKind.put("strlit", stringLiteralTypeKind);
5960
tagToKind.put("unknown", 23);
61+
tagToKind.put("bigint", 24);
62+
tagToKind.put("bigintlit", bigintLiteralTypeKind);
6063
}
6164

6265
private static final Map<String, Integer> symbolKind = new LinkedHashMap<String, Integer>();
@@ -126,6 +129,7 @@ private void extractType(int id) {
126129

127130
case numberLiteralTypeKind:
128131
case stringLiteralTypeKind:
132+
case bigintLiteralTypeKind:
129133
firstChild = parts.length; // No children.
130134
// The string value may contain `;` so don't use the split().
131135
String value = contents.substring(parts[0].length() + 1);

javascript/ql/src/semmle/javascript/TypeScript.qll

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,9 @@ class TypeExpr extends ExprOrType, @typeexpr {
582582
/** Holds if this is the `unknown` type. */
583583
predicate isUnknownKeyword() { none() }
584584

585+
/** Holds if this is the `bigint` type. */
586+
predicate isBigInt() { none() }
587+
585588
/** Gets this type expression, with any surrounding parentheses removed. */
586589
override TypeExpr stripParens() { result = this }
587590

@@ -633,6 +636,8 @@ private class KeywordTypeExpr extends @keywordtypeexpr, TypeExpr {
633636
override predicate isObjectKeyword() { getName() = "object" }
634637

635638
override predicate isUnknownKeyword() { getName() = "unknown" }
639+
640+
override predicate isBigInt() { getName() = "bigint" }
636641
}
637642

638643
/**
@@ -798,6 +803,18 @@ class BooleanLiteralTypeExpr extends @booleanliteraltypeexpr, LiteralTypeExpr {
798803
predicate isFalse() { getValue() = "false" }
799804
}
800805

806+
/** A bigint literal used as a TypeScript type annotation. */
807+
class BigIntLiteralTypeExpr extends @bigintliteraltypeexpr, LiteralTypeExpr {
808+
/** Gets the integer value of the bigint literal, if it can be represented as a QL integer. */
809+
int getIntValue() { result = getValue().toInt() }
810+
811+
/**
812+
* Gets the floating point value of this literal if it can be represented
813+
* as a QL floating point value.
814+
*/
815+
float getFloatValue() { result = getValue().toFloat() }
816+
}
817+
801818
/**
802819
* An array type, such as `number[]`, or in general `T[]` where `T` is a type.
803820
*
@@ -1950,6 +1967,11 @@ class StringType extends Type, @stringtype { }
19501967
*/
19511968
class NumberType extends Type, @numbertype { }
19521969

1970+
/**
1971+
* The predefined `bigint` type.
1972+
*/
1973+
class BigIntType extends Type, @biginttype { }
1974+
19531975
/**
19541976
* A boolean, number, or string literal type.
19551977
*/
@@ -1998,6 +2020,23 @@ class StringLiteralType extends LiteralType, @stringliteraltype {
19982020
override string getStringValue() { type_literal_value(this, result) }
19992021
}
20002022

2023+
/**
2024+
* A bigint literal as a static type.
2025+
*/
2026+
class BigIntLiteralType extends LiteralType {
2027+
override string getStringValue() { type_literal_value(this, result) }
2028+
2029+
/**
2030+
* Gets the value of the literal as an integer.
2031+
*/
2032+
int getIntValue() { result = getStringValue().toInt() }
2033+
2034+
/**
2035+
* Gets the value of the literal as a floating-point value.
2036+
*/
2037+
float getFloatValue() { result = getStringValue().toFloat() }
2038+
}
2039+
20012040
/**
20022041
* The `boolean` type, internally represented as the union type `true | false`.
20032042
*/

javascript/ql/src/semmlecode.javascript.dbscheme

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -560,12 +560,13 @@ case @typeexpr.kind of
560560
| 32 = @importvartypeaccess
561561
| 33 = @optionaltypeexpr
562562
| 34 = @resttypeexpr
563+
| 35 = @bigintliteraltypeexpr
563564
;
564565

565566
@typeref = @typeaccess | @typedecl;
566567
@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess;
567568
@typeexpr_parent = @expr | @stmt | @property | @typeexpr;
568-
@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr;
569+
@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr | @bigintliteraltypeexpr;
569570
@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess;
570571
@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess;
571572
@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess;
@@ -612,6 +613,8 @@ case @type.kind of
612613
| 21 = @numberliteraltype
613614
| 22 = @stringliteraltype
614615
| 23 = @unknowntype
616+
| 24 = @biginttype
617+
| 25 = @bigintliteraltype
615618
;
616619

617620
@booleanliteraltype = @truetype | @falsetype;
@@ -678,8 +681,8 @@ type_property(
678681
varchar(900) name: string ref,
679682
int propertyType: @type ref);
680683

681-
@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype;
682-
@type_with_literal_value = @stringliteraltype | @numberliteraltype;
684+
@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype;
685+
@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype;
683686
type_literal_value(
684687
unique int typ: @type_with_literal_value ref,
685688
varchar(900) value: string ref);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| tst.ts:1:25:1:28 | 100n | 100.0 |
2+
| tst.ts:2:25:2:56 | 1000000 ... 000000n | 1.0E30 |
3+
| tst.ts:3:25:3:56 | 1000000 ... 000000n | 1.0E30 |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from BigIntLiteral literal
4+
select literal, literal.getFloatValue()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| tst.ts:1:25:1:28 | 100n | 100 |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from BigIntLiteral literal
4+
select literal, literal.getIntValue()

0 commit comments

Comments
 (0)