Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/codeql/support/reusables/versions-compilers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
Eclipse compiler for Java (ECJ) [5]_",``.java``
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9",Not applicable,``.py``
TypeScript [7]_,"2.6-4.2",Standard TypeScript compiler,"``.ts``, ``.tsx``"
TypeScript [7]_,"2.6-4.4",Standard TypeScript compiler,"``.ts``, ``.tsx``"

.. container:: footnote-group

Expand Down
2 changes: 2 additions & 0 deletions javascript/change-notes/2021-09-01-typescript-4.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lgtm,codescanning
* TypeScript 4.4 is now supported.
2 changes: 2 additions & 0 deletions javascript/change-notes/2021-09-07-static-initializer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lgtm,codescanning
* Support for the ECMAScript proposed feature "class static initialization blocks" has been added.
2 changes: 1 addition & 1 deletion javascript/extractor/lib/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper",
"private": true,
"dependencies": {
"typescript": "4.3.5"
"typescript": "4.4.2"
},
"scripts": {
"build": "tsc --project tsconfig.json",
Expand Down
8 changes: 4 additions & 4 deletions javascript/extractor/lib/typescript/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
version "12.7.11"
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446

typescript@4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
typescript@4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
5 changes: 5 additions & 0 deletions javascript/extractor/src/com/semmle/jcorn/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.SpreadElement;
import com.semmle.js.ast.Statement;
import com.semmle.js.ast.StaticInitializer;
import com.semmle.js.ast.Super;
import com.semmle.js.ast.SwitchCase;
import com.semmle.js.ast.SwitchStatement;
Expand Down Expand Up @@ -3244,6 +3245,10 @@ protected MemberDefinition<?> parseClassMember(boolean hadConstructor) {
PropertyInfo pi = new PropertyInfo(false, isGenerator, methodStartLoc);
this.parsePropertyName(pi);
boolean isStatic = isMaybeStatic && this.type != TokenType.parenL;
if (isStatic && this.type == TokenType.braceL) {
BlockStatement block = parseBlock(false);
return new StaticInitializer(block.getLoc(), block);
}
if (isStatic) {
if (isGenerator) this.unexpected();
isGenerator = this.eat(TokenType.star);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,4 +782,9 @@ public R visit(XMLDotDotExpression nd, C c) {
public R visit(GeneratedCodeExpr nd, C c) {
return visit((Expression) nd, c);
}

@Override
public R visit(StaticInitializer nd, C c) {
return visit((MemberDefinition<BlockStatement>) nd, c);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* <p>A member definition has a name and an optional initial value, whose type is given by the type
* parameter {@code V}.
*/
public abstract class MemberDefinition<V extends Expression> extends Node {
public abstract class MemberDefinition<V extends Node> extends Node {
/** A bitmask of flags defined in {@linkplain DeclarationFlags}. */
private final int flags;

Expand All @@ -21,7 +21,7 @@ public abstract class MemberDefinition<V extends Expression> extends Node {
*/
private final Expression key;

/** The initial value of the member. */
/** The initial value / initializer of the member. */
private final V value;

/** The decorators applied to this member, if any. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,11 @@ public INode visit(XMLDotDotExpression nd, Void c) {

@Override
public INode visit(GeneratedCodeExpr nd, Void c) {
return new GeneratedCodeExpr(visit(nd.getLoc()), nd.getOpeningDelimiter(), nd.getClosingDelimiter(), nd.getBody());
return new GeneratedCodeExpr(visit(nd.getLoc()), nd.getOpeningDelimiter(), nd.getClosingDelimiter(), nd.getBody());
}

@Override
public INode visit(StaticInitializer nd, Void c) {
return new StaticInitializer(visit(nd.getLoc()), copy(nd.getValue()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.semmle.js.ast;

/**
* A static initializer block in a class. E.g. ```TypeScript class Foo { static
* bar : number; static { Foo.bar = 42; } }
*/
public class StaticInitializer extends MemberDefinition<BlockStatement> {
public StaticInitializer(SourceLocation loc, BlockStatement body) {
super("StaticInitializer", loc, DeclarationFlags.static_, null, body);
}

@Override
public boolean isConcrete() {
return false;
}

@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);
}
}
2 changes: 2 additions & 0 deletions javascript/extractor/src/com/semmle/js/ast/Visitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,6 @@ public interface Visitor<C, R> {
public R visit(XMLDotDotExpression nd, C c);

public R visit(GeneratedCodeExpr generatedCodeExpr, C c);

public R visit(StaticInitializer nd, C c);
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.SpreadElement;
import com.semmle.js.ast.Statement;
import com.semmle.js.ast.StaticInitializer;
import com.semmle.js.ast.Super;
import com.semmle.js.ast.SwitchCase;
import com.semmle.js.ast.SwitchStatement;
Expand Down Expand Up @@ -1613,6 +1614,8 @@ public Label visit(MemberDefinition<?> nd, Context c) {
int kind;
if (nd instanceof MethodDefinition) {
kind = getMethodKind((MethodDefinition) nd);
} else if (nd instanceof StaticInitializer) {
kind = 10;
} else {
kind = getFieldKind((FieldDefinition) nd);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.SpreadElement;
import com.semmle.js.ast.Statement;
import com.semmle.js.ast.StaticInitializer;
import com.semmle.js.ast.Super;
import com.semmle.js.ast.SwitchCase;
import com.semmle.js.ast.SwitchStatement;
Expand Down Expand Up @@ -1163,10 +1164,11 @@ public Void visit(ClassExpression nd, SuccessorInfo i) {
private Map<Expression, AClass> constructor2Class = new LinkedHashMap<>();

private Void visit(Node nd, AClass ac, SuccessorInfo i) {
for (MemberDefinition<?> m : ac.getBody().getBody())
if (m.isConstructor() && m.isConcrete()) constructor2Class.put(m.getValue(), ac);
for (MemberDefinition<?> md : ac.getBody().getBody()) {
if (md.isConstructor() && md.isConcrete()) constructor2Class.put((Expression)md.getValue(), ac);
}
visitSequence(ac.getId(), ac.getSuperClass(), ac.getBody(), nd);
writeSuccessors(nd, visitSequence(getStaticFields(ac.getBody()), getDecoratorsOfClass(ac), i.getAllSuccessors()));
writeSuccessors(nd, visitSequence(getStaticInitializers(ac.getBody()), getDecoratorsOfClass(ac), i.getAllSuccessors()));
return null;
}

Expand Down Expand Up @@ -1618,19 +1620,24 @@ private List<MemberDefinition<?>> getMethods(ClassBody nd) {
return mds;
}

private List<MemberDefinition<?>> getStaticFields(ClassBody nd) {
List<MemberDefinition<?>> mds = new ArrayList<>();
for (MemberDefinition<?> md : nd.getBody()) {
if (md instanceof FieldDefinition && md.isStatic()) mds.add(md);
/**
* Gets the static fields, and static initializer blocks, from `nd`.
*/
private List<Node> getStaticInitializers(ClassBody nd) {
List<Node> nodes = new ArrayList<>();
for (MemberDefinition<?> node : nd.getBody()) {
if (node instanceof FieldDefinition && ((FieldDefinition)node).isStatic()) nodes.add(node);
if (node instanceof StaticInitializer) nodes.add(node.getValue());
}
return mds;
return nodes;
}

private List<FieldDefinition> getConcreteInstanceFields(ClassBody nd) {
List<FieldDefinition> fds = new ArrayList<>();
for (MemberDefinition<?> md : nd.getBody())
for (MemberDefinition<?> md : nd.getBody()) {
if (md instanceof FieldDefinition && !md.isStatic() && md.isConcrete())
fds.add((FieldDefinition) md);
}
return fds;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2021-07-28";
public static final String EXTRACTOR_VERSION = "2021-09-01";

public static final Pattern NEWLINE = Pattern.compile("\n");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.SpreadElement;
import com.semmle.js.ast.Statement;
import com.semmle.js.ast.StaticInitializer;
import com.semmle.js.ast.Super;
import com.semmle.js.ast.SwitchCase;
import com.semmle.js.ast.SwitchStatement;
Expand Down Expand Up @@ -632,6 +633,8 @@ private Node convertNodeUntyped(JsonObject node, String defaultKind) throws Pars
return convertWithStatement(node, loc);
case "YieldExpression":
return convertYieldExpression(node, loc);
case "ClassStaticBlockDeclaration":
return convertStaticInitializerBlock(node, loc);
default:
throw new ParseError(
"Unsupported TypeScript syntax " + kind, getSourceLocation(node).getStart());
Expand Down Expand Up @@ -866,6 +869,11 @@ private Node convertBinaryExpression(JsonObject node, SourceLocation loc) throws
}
}

private Node convertStaticInitializerBlock(JsonObject node, SourceLocation loc) throws ParseError {
BlockStatement body = new BlockStatement(loc, convertChildren(node.get("body").getAsJsonObject(), "statements"));
return new StaticInitializer(loc, body);
}

private Node convertBlock(JsonObject node, SourceLocation loc) throws ParseError {
return new BlockStatement(loc, convertChildren(node, "statements"));
}
Expand Down
19 changes: 19 additions & 0 deletions javascript/ql/lib/semmle/javascript/Classes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod
}

override string getAPrimaryQlClass() { result = "ClassDefinition" }

/**
* Gets a static initializer of this class, if any.
*/
BlockStmt getAStaticInitializerBlock() {
exists(StaticInitializer init | init.getDeclaringClass() = this | result = init.getBody())
}
}

/**
Expand Down Expand Up @@ -1134,6 +1141,18 @@ class ParameterField extends FieldDeclaration, @parameter_field {
override TypeAnnotation getTypeAnnotation() { result = getParameter().getTypeAnnotation() }
}

/**
* A static initializer in a class.
*/
class StaticInitializer extends MemberDefinition, @static_initializer {
/**
* Gets the body of the static initializer.
*/
BlockStmt getBody() { result.getParent() = this }

override Expr getNameExpr() { none() }
}

/**
* A call signature declared in an interface.
*
Expand Down
3 changes: 2 additions & 1 deletion javascript/ql/lib/semmlecode.javascript.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ stmt_containers (unique int stmt: @stmt ref,
jump_targets (unique int jump: @stmt ref,
int target: @stmt ref);

@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr;
@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer;
@stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration;

case @stmt.kind of
Expand Down Expand Up @@ -521,6 +521,7 @@ case @property.kind of
| 7 = @enum_member
| 8 = @proper_field
| 9 = @parameter_field
| 10 = @static_initializer
;

@property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration;
Expand Down
4 changes: 4 additions & 0 deletions javascript/ql/lib/semmlecode.javascript.dbscheme.stats
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,10 @@
<v>2693</v>
</e>
<e>
<k>@static_initializer</k>
<v>100</v>
</e>
<e>
<k>@local_type_access</k>
<v>25491</v>
</e>
Expand Down
2 changes: 2 additions & 0 deletions javascript/ql/lib/upgrade.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: add @static_initializer property kind
compatibility: backwards
32 changes: 31 additions & 1 deletion javascript/ql/test/library-tests/CFG/CFG.expected
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,37 @@
| staticFieldsTS | 6 | D | 6 | new D() |
| staticFieldsTS | 6 | instance | 6 | D |
| staticFieldsTS | 6 | new D() | 6 | static ... ew D(); |
| staticFieldsTS | 6 | static ... ew D(); | 8 | exit node of <toplevel> |
| staticFieldsTS | 6 | static ... ew D(); | 9 | export ... ;\\n }\\n} |
| staticFieldsTS | 9 | E | 9 | constructor |
| staticFieldsTS | 9 | class E ... ;\\n }\\n} | 10 | f |
| staticFieldsTS | 9 | constructor | 9 | function in constructor() {} |
| staticFieldsTS | 9 | constructor() {} | 9 | class E ... ;\\n }\\n} |
| staticFieldsTS | 9 | entry node of () {} | 9 | {} |
| staticFieldsTS | 9 | export ... ;\\n }\\n} | 9 | E |
| staticFieldsTS | 9 | function in constructor() {} | 9 | constructor() {} |
| staticFieldsTS | 9 | {} | 9 | exit node of () {} |
| staticFieldsTS | 10 | f | 10 | false |
| staticFieldsTS | 10 | false | 10 | static ... false; |
| staticFieldsTS | 10 | static ... false; | 11 | static ... ();\\n } |
| staticFieldsTS | 11 | static ... ();\\n } | 12 | E.f = new C(); |
| staticFieldsTS | 12 | C | 12 | new C() |
| staticFieldsTS | 12 | E | 12 | f |
| staticFieldsTS | 12 | E.f | 12 | C |
| staticFieldsTS | 12 | E.f = new C() | 14 | g |
| staticFieldsTS | 12 | E.f = new C(); | 12 | E |
| staticFieldsTS | 12 | f | 12 | E.f |
| staticFieldsTS | 12 | new C() | 12 | E.f = new C() |
| staticFieldsTS | 14 | 1337 | 14 | static ... = 1337; |
| staticFieldsTS | 14 | g | 14 | 1337 |
| staticFieldsTS | 14 | static ... = 1337; | 15 | static ... ();\\n } |
| staticFieldsTS | 15 | static ... ();\\n } | 16 | E.g = new D(); |
| staticFieldsTS | 16 | D | 16 | new D() |
| staticFieldsTS | 16 | E | 16 | g |
| staticFieldsTS | 16 | E.g | 16 | D |
| staticFieldsTS | 16 | E.g = new D() | 18 | exit node of <toplevel> |
| staticFieldsTS | 16 | E.g = new D(); | 16 | E |
| staticFieldsTS | 16 | g | 16 | E.g |
| staticFieldsTS | 16 | new D() | 16 | E.g = new D() |
| switch | 1 | entry node of <toplevel> | 14 | f |
| switch | 1 | switch ... 19;\\n} | 2 | x |
| switch | 2 | x | 6 | case\\n ... 19; |
Expand Down
2 changes: 2 additions & 0 deletions javascript/ql/test/library-tests/CFG/StaticInit.expected
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
| staticFields.js:2:3:2:28 | static ... ew C(); | Field initializer occurs after its class is created |
| staticFieldsTS.ts:2:3:2:31 | static ... ew C(); | Field initializer occurs after its class is created |
| staticFieldsTS.ts:6:3:6:31 | static ... ew D(); | Field initializer occurs after its class is created |
| staticFieldsTS.ts:10:3:10:32 | static ... false; | Field initializer occurs after its class is created |
| staticFieldsTS.ts:14:3:14:30 | static ... = 1337; | Field initializer occurs after its class is created |
11 changes: 11 additions & 0 deletions javascript/ql/test/library-tests/CFG/staticFieldsTS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,14 @@ class C {
export class D {
static instance: D = new D();
}

export class E {
static f: C | boolean = false;
static {
E.f = new C();
}
static g: D | number = 1337;
static {
E.g = new D();
}
}
18 changes: 18 additions & 0 deletions javascript/ql/test/library-tests/Classes/staticInitializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class MyClass {
static x = 1;
constructor() {
this.y = 2;
}
static {
MyClass.z = 3;
}
foo() {
this.t = 4;
}
static bar() {
this.u = 5;
}
static {
this.v = 6;
}
}
Loading