Skip to content

Commit fdbf5f7

Browse files
committed
add JS support for static initializers
1 parent cc0d864 commit fdbf5f7

File tree

5 files changed

+134
-20
lines changed

5 files changed

+134
-20
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Support for the ECMAScript proposed feature "class static initialization blocks" has been added.

javascript/extractor/src/com/semmle/jcorn/Parser.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.semmle.js.ast.SourceLocation;
8484
import com.semmle.js.ast.SpreadElement;
8585
import com.semmle.js.ast.Statement;
86+
import com.semmle.js.ast.StaticInitializer;
8687
import com.semmle.js.ast.Super;
8788
import com.semmle.js.ast.SwitchCase;
8889
import com.semmle.js.ast.SwitchStatement;
@@ -3244,6 +3245,10 @@ protected MemberDefinition<?> parseClassMember(boolean hadConstructor) {
32443245
PropertyInfo pi = new PropertyInfo(false, isGenerator, methodStartLoc);
32453246
this.parsePropertyName(pi);
32463247
boolean isStatic = isMaybeStatic && this.type != TokenType.parenL;
3248+
if (isStatic && this.type == TokenType.braceL) {
3249+
BlockStatement block = parseBlock(false);
3250+
return new StaticInitializer(block.getLoc(), block);
3251+
}
32473252
if (isStatic) {
32483253
if (isGenerator) this.unexpected();
32493254
isGenerator = this.eat(TokenType.star);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class MyClass {
2+
static x = 1;
3+
constructor() {
4+
this.y = 2;
5+
}
6+
static {
7+
MyClass.z = 3;
8+
}
9+
foo() {
10+
this.t = 4;
11+
}
12+
static bar() {
13+
this.u = 5;
14+
}
15+
static {
16+
this.v = 6;
17+
}
18+
}

javascript/ql/test/library-tests/Classes/tests.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,31 @@ test_FieldInits
44
| privateFields.js:2:2:2:15 | #privDecl = 3; | privateFields.js:2:14:2:14 | 3 |
55
| privateFields.js:3:2:3:12 | #if = "if"; | privateFields.js:3:8:3:11 | "if" |
66
| privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | privateFields.js:21:21:21:21 | 6 |
7+
| staticInitializer.js:2:3:2:15 | static x = 1; | staticInitializer.js:2:14:2:14 | 1 |
78
test_ComputedMethods
89
| tst.js:3:3:3:56 | ["const ... r. */ } |
910
| tst.js:13:3:13:10 | [m]() {} |
1011
test_StaticMethods
1112
| points.js:15:3:17:3 | static ... t";\\n } |
1213
| points.js:30:3:32:3 | static ... t";\\n } |
1314
| staticConstructor.js:2:3:2:59 | static ... tor"; } |
15+
| staticInitializer.js:12:3:14:3 | static ... 5;\\n } |
1416
test_ClassDefinition_getSuperClass
1517
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:20:29:20:33 | Point |
1618
| tst.js:6:1:8:1 | class B ... t); }\\n} | tst.js:6:17:6:17 | A |
1719
test_ClassNodeStaticMethod
1820
| points.js:1:1:18:1 | class P ... ;\\n }\\n} | className | points.js:15:19:17:3 | () {\\n ... t";\\n } |
1921
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | className | points.js:30:19:32:3 | () {\\n ... t";\\n } |
2022
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | constructor | staticConstructor.js:2:21:2:59 | () { re ... tor"; } |
23+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | bar | staticInitializer.js:12:13:14:3 | () {\\n ... 5;\\n } |
2124
test_ClassDefinitions
2225
| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} |
2326
| fields.js:1:1:4:1 | class C ... = 42\\n} |
2427
| points.js:1:1:18:1 | class P ... ;\\n }\\n} |
2528
| points.js:20:1:33:1 | class C ... ;\\n }\\n} |
2629
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} |
2730
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} |
31+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} |
2832
| tst.js:1:9:4:1 | class { ... */ }\\n} |
2933
| tst.js:6:1:8:1 | class B ... t); }\\n} |
3034
| tst.js:11:1:14:1 | class C ... () {}\\n} |
@@ -38,13 +42,15 @@ test_Fields
3842
| privateFields.js:3:2:3:12 | #if = "if"; | privateFields.js:3:2:3:4 | #if |
3943
| privateFields.js:19:2:19:13 | #privSecond; | privateFields.js:19:2:19:12 | #privSecond |
4044
| privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | privateFields.js:21:3:21:16 | "#publicField" |
45+
| staticInitializer.js:2:3:2:15 | static x = 1; | staticInitializer.js:2:10:2:10 | x |
4146
test_ClassDefinition_getName
4247
| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | Foo |
4348
| fields.js:1:1:4:1 | class C ... = 42\\n} | C |
4449
| points.js:1:1:18:1 | class P ... ;\\n }\\n} | Point |
4550
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | ColouredPoint |
4651
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | Foo |
4752
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | MyClass |
53+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | MyClass |
4854
| tst.js:1:9:4:1 | class { ... */ }\\n} | A |
4955
| tst.js:6:1:8:1 | class B ... t); }\\n} | B |
5056
| tst.js:11:1:14:1 | class C ... () {}\\n} | C |
@@ -67,6 +73,9 @@ test_MethodDefinitions
6773
| privateFields.js:23:2:26:2 | calls() ... l();\\n\\t} | privateFields.js:23:2:23:6 | calls | privateFields.js:23:7:26:2 | () {\\n\\t\\t ... l();\\n\\t} | privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} |
6874
| staticConstructor.js:1:15:1:14 | constructor() {} | staticConstructor.js:1:15:1:14 | constructor | staticConstructor.js:1:15:1:14 | () {} | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} |
6975
| staticConstructor.js:2:3:2:59 | static ... tor"; } | staticConstructor.js:2:10:2:20 | constructor | staticConstructor.js:2:21:2:59 | () { re ... tor"; } | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} |
76+
| staticInitializer.js:3:3:5:3 | constru ... 2;\\n } | staticInitializer.js:3:3:3:13 | constructor | staticInitializer.js:3:14:5:3 | () {\\n ... 2;\\n } | staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} |
77+
| staticInitializer.js:9:3:11:3 | foo() { ... 4;\\n } | staticInitializer.js:9:3:9:5 | foo | staticInitializer.js:9:6:11:3 | () {\\n ... 4;\\n } | staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} |
78+
| staticInitializer.js:12:3:14:3 | static ... 5;\\n } | staticInitializer.js:12:10:12:12 | bar | staticInitializer.js:12:13:14:3 | () {\\n ... 5;\\n } | staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} |
7079
| tst.js:2:3:2:50 | "constr ... r. */ } | tst.js:2:3:2:15 | "constructor" | tst.js:2:16:2:50 | () { /* ... r. */ } | tst.js:1:9:4:1 | class { ... */ }\\n} |
7180
| tst.js:3:3:3:56 | ["const ... r. */ } | tst.js:3:4:3:16 | "constructor" | tst.js:3:18:3:56 | () { /* ... r. */ } | tst.js:1:9:4:1 | class { ... */ }\\n} |
7281
| tst.js:7:3:7:38 | constru ... get); } | tst.js:7:3:7:13 | constructor | tst.js:7:14:7:38 | () { su ... get); } | tst.js:6:1:8:1 | class B ... t); }\\n} |
@@ -99,6 +108,12 @@ test_getAMember
99108
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | privateFields.js:23:2:26:2 | calls() ... l();\\n\\t} |
100109
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:1:15:1:14 | constructor() {} |
101110
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:2:3:2:59 | static ... tor"; } |
111+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:2:3:2:15 | static x = 1; |
112+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:3:3:5:3 | constru ... 2;\\n } |
113+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:6:10:8:3 | {\\n M ... 3;\\n } |
114+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:9:3:11:3 | foo() { ... 4;\\n } |
115+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:12:3:14:3 | static ... 5;\\n } |
116+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:15:10:17:3 | {\\n t ... 6;\\n } |
102117
| tst.js:1:9:4:1 | class { ... */ }\\n} | tst.js:2:3:2:50 | "constr ... r. */ } |
103118
| tst.js:1:9:4:1 | class { ... */ }\\n} | tst.js:3:3:3:56 | ["const ... r. */ } |
104119
| tst.js:6:1:8:1 | class B ... t); }\\n} | tst.js:7:3:7:38 | constru ... get); } |
@@ -124,6 +139,9 @@ test_MethodNames
124139
| privateFields.js:23:2:26:2 | calls() ... l();\\n\\t} | calls |
125140
| staticConstructor.js:1:15:1:14 | constructor() {} | constructor |
126141
| staticConstructor.js:2:3:2:59 | static ... tor"; } | constructor |
142+
| staticInitializer.js:3:3:5:3 | constru ... 2;\\n } | constructor |
143+
| staticInitializer.js:9:3:11:3 | foo() { ... 4;\\n } | foo |
144+
| staticInitializer.js:12:3:14:3 | static ... 5;\\n } | bar |
127145
| tst.js:2:3:2:50 | "constr ... r. */ } | constructor |
128146
| tst.js:3:3:3:56 | ["const ... r. */ } | constructor |
129147
| tst.js:7:3:7:38 | constru ... get); } | constructor |
@@ -148,6 +166,7 @@ test_ConstructorDefinitions
148166
| points.js:21:3:24:3 | constru ... c;\\n } |
149167
| privateFields.js:1:11:1:10 | constructor() {} |
150168
| staticConstructor.js:1:15:1:14 | constructor() {} |
169+
| staticInitializer.js:3:3:5:3 | constru ... 2;\\n } |
151170
| tst.js:2:3:2:50 | "constr ... r. */ } |
152171
| tst.js:7:3:7:38 | constru ... get); } |
153172
| tst.js:11:9:11:8 | constructor() {} |
@@ -158,6 +177,7 @@ test_ClassNodeConstructor
158177
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:21:14:24:3 | (x, y, ... c;\\n } |
159178
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | privateFields.js:1:11:1:10 | () {} |
160179
| staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:1:15:1:14 | () {} |
180+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:3:14:5:3 | () {\\n ... 2;\\n } |
161181
| tst.js:1:9:4:1 | class { ... */ }\\n} | tst.js:2:16:2:50 | () { /* ... r. */ } |
162182
| tst.js:6:1:8:1 | class B ... t); }\\n} | tst.js:7:14:7:38 | () { su ... get); } |
163183
| tst.js:11:1:14:1 | class C ... () {}\\n} | tst.js:11:9:11:8 | () {} |
@@ -170,6 +190,7 @@ test_ClassNodeInstanceMethod
170190
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | equals | privateFields.js:10:8:12:2 | (o) {\\n\\t ... ecl;\\n\\t} |
171191
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | reads | privateFields.js:4:7:8:2 | () {\\n\\t\\t ... #if;\\n\\t} |
172192
| privateFields.js:1:1:27:1 | class F ... );\\n\\t}\\n} | writes | privateFields.js:14:8:17:2 | () {\\n\\t\\t ... = 5;\\n\\t} |
193+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | foo | staticInitializer.js:9:6:11:3 | () {\\n ... 4;\\n } |
173194
| tst.js:1:9:4:1 | class { ... */ }\\n} | constructor | tst.js:3:18:3:56 | () { /* ... r. */ } |
174195
| tst.js:11:1:14:1 | class C ... () {}\\n} | m | tst.js:12:4:12:8 | () {} |
175196
getAccessModifier
@@ -223,6 +244,15 @@ getAccessModifier
223244
| staticConstructor.js:2:3:2:59 | static ... tor"; } | staticConstructor.js:2:10:2:20 | constructor | Public |
224245
| staticConstructor.js:4:1:4:11 | console.log | staticConstructor.js:4:9:4:11 | log | Public |
225246
| staticConstructor.js:4:13:4:31 | MyClass.constructor | staticConstructor.js:4:21:4:31 | constructor | Public |
247+
| staticInitializer.js:2:3:2:15 | static x = 1; | staticInitializer.js:2:10:2:10 | x | Public |
248+
| staticInitializer.js:3:3:5:3 | constru ... 2;\\n } | staticInitializer.js:3:3:3:13 | constructor | Public |
249+
| staticInitializer.js:4:5:4:10 | this.y | staticInitializer.js:4:10:4:10 | y | Public |
250+
| staticInitializer.js:7:5:7:13 | MyClass.z | staticInitializer.js:7:13:7:13 | z | Public |
251+
| staticInitializer.js:9:3:11:3 | foo() { ... 4;\\n } | staticInitializer.js:9:3:9:5 | foo | Public |
252+
| staticInitializer.js:10:5:10:10 | this.t | staticInitializer.js:10:10:10:10 | t | Public |
253+
| staticInitializer.js:12:3:14:3 | static ... 5;\\n } | staticInitializer.js:12:10:12:12 | bar | Public |
254+
| staticInitializer.js:13:5:13:10 | this.u | staticInitializer.js:13:10:13:10 | u | Public |
255+
| staticInitializer.js:16:5:16:10 | this.v | staticInitializer.js:16:10:16:10 | v | Public |
226256
| tst.js:2:3:2:50 | "constr ... r. */ } | tst.js:2:3:2:15 | "constructor" | Public |
227257
| tst.js:3:3:3:56 | ["const ... r. */ } | tst.js:3:4:3:16 | "constructor" | Public |
228258
| tst.js:7:3:7:38 | constru ... get); } | tst.js:7:3:7:13 | constructor | Public |
@@ -233,3 +263,6 @@ getAccessModifier
233263
dataflow
234264
| dataflow.js:2:15:2:22 | "source" | dataflow.js:14:7:14:25 | new Foo().getPriv() |
235265
| dataflow.js:2:15:2:22 | "source" | dataflow.js:16:7:16:33 | new Foo ... ivate() |
266+
staticInitializer
267+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:6:10:8:3 | {\\n M ... 3;\\n } |
268+
| staticInitializer.js:1:1:18:1 | class M ... ;\\n }\\n} | staticInitializer.js:15:10:17:3 | {\\n t ... 6;\\n } |
Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,76 @@
1-
import FieldInits
2-
import ComputedMethods
3-
import StaticMethods
4-
import ClassDefinition_getSuperClass
5-
import ClassNodeStaticMethod
6-
import ClassDefinitions
7-
import AccessorMethods
8-
import Fields
9-
import ClassDefinition_getName
10-
import MethodDefinitions
11-
import getAMember
12-
import MethodNames
13-
import NewTargetExpr
14-
import SuperExpr
15-
import SyntheticConstructors
16-
import ConstructorDefinitions
17-
import ClassNodeConstructor
18-
import ClassNodeInstanceMethod
19-
import PrivateField
20-
import ClassFlow
1+
import javascript
2+
3+
query predicate test_FieldInits(FieldDefinition field, Expr res) { res = field.getInit() }
4+
5+
query predicate test_ComputedMethods(MethodDefinition md) { md.isComputed() }
6+
7+
query predicate test_StaticMethods(MethodDefinition md) { md.isStatic() }
8+
9+
query predicate test_ClassDefinition_getSuperClass(ClassDefinition cd, Expr res) {
10+
res = cd.getSuperClass()
11+
}
12+
13+
query predicate test_ClassNodeStaticMethod(
14+
DataFlow::ClassNode class_, string name, DataFlow::FunctionNode res
15+
) {
16+
res = class_.getStaticMethod(name)
17+
}
18+
19+
query predicate test_ClassDefinitions(ClassDefinition cd) { any() }
20+
21+
query predicate test_AccessorMethods(AccessorMethodDefinition amd) { any() }
22+
23+
query predicate test_Fields(FieldDefinition field, Expr res) { res = field.getNameExpr() }
24+
25+
query predicate test_ClassDefinition_getName(ClassDefinition cd, string res) { res = cd.getName() }
26+
27+
query predicate test_MethodDefinitions(
28+
MethodDefinition md, Expr res0, FunctionExpr res1, ClassDefinition res2
29+
) {
30+
res0 = md.getNameExpr() and res1 = md.getBody() and res2 = md.getDeclaringClass()
31+
}
32+
33+
query predicate test_getAMember(ClassDefinition c, MemberDeclaration res) { res = c.getAMember() }
34+
35+
query predicate test_MethodNames(MethodDefinition md, string res) { res = md.getName() }
36+
37+
query predicate test_NewTargetExpr(NewTargetExpr e) { any() }
38+
39+
query predicate test_SuperExpr(SuperExpr s) { any() }
40+
41+
query predicate test_SyntheticConstructors(ConstructorDefinition cd) { cd.isSynthetic() }
42+
43+
query predicate test_ConstructorDefinitions(ConstructorDefinition cd) { any() }
44+
45+
query predicate test_ClassNodeConstructor(DataFlow::ClassNode class_, DataFlow::FunctionNode res) {
46+
res = class_.getConstructor()
47+
}
48+
49+
query predicate test_ClassNodeInstanceMethod(
50+
DataFlow::ClassNode class_, string name, DataFlow::FunctionNode res
51+
) {
52+
res = class_.getInstanceMethod(name)
53+
}
54+
55+
query string getAccessModifier(DataFlow::PropRef ref, Expr prop) {
56+
prop = ref.getPropertyNameExpr() and
57+
if ref.isPrivateField() then result = "Private" else result = "Public"
58+
}
59+
60+
class Configuration extends DataFlow::Configuration {
61+
Configuration() { this = "ClassDataFlowTestingConfig" }
62+
63+
override predicate isSource(DataFlow::Node source) {
64+
source.getEnclosingExpr().(StringLiteral).getValue().toLowerCase() = "source"
65+
}
66+
67+
override predicate isSink(DataFlow::Node sink) {
68+
any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() = sink
69+
}
70+
}
71+
72+
query predicate dataflow(DataFlow::Node pred, DataFlow::Node succ) {
73+
any(Configuration c).hasFlow(pred, succ)
74+
}
75+
76+
query BlockStmt staticInitializer(ClassDefinition cd) { result = cd.getAStaticInitializerBlock() }

0 commit comments

Comments
 (0)