diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java index f0e3c0d004f..7e344613b2e 100644 --- a/javascript/extractor/src/com/semmle/jcorn/Parser.java +++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java @@ -5,6 +5,7 @@ import static com.semmle.jcorn.Whitespace.lineBreak; import com.semmle.jcorn.Identifiers.Dialect; import com.semmle.jcorn.Options.AllowReserved; +import com.semmle.jcorn.TokenType.Properties; import com.semmle.js.ast.ArrayExpression; import com.semmle.js.ast.ArrayPattern; import com.semmle.js.ast.ArrowFunctionExpression; @@ -44,6 +45,7 @@ import com.semmle.js.ast.INode; import com.semmle.js.ast.IPattern; import com.semmle.js.ast.Identifier; import com.semmle.js.ast.IfStatement; +import com.semmle.js.ast.FieldDefinition; import com.semmle.js.ast.ImportDeclaration; import com.semmle.js.ast.ImportDefaultSpecifier; import com.semmle.js.ast.ImportNamespaceSpecifier; @@ -124,6 +126,7 @@ public class Parser { private boolean inModule; protected boolean inFunction; protected boolean inGenerator; + protected boolean inClass; protected boolean inAsync; protected boolean inTemplateElement; protected int pos; @@ -240,8 +243,8 @@ public class Parser { // Used to signify the start of a potential arrow function this.potentialArrowAt = -1; - // Flags to track whether we are in a function, a generator, an async function. - this.inFunction = this.inGenerator = this.inAsync = false; + // Flags to track whether we are in a function, a generator, an async function, a class. + this.inFunction = this.inGenerator = this.inAsync = this.inClass = false; // Positions to delayed-check that yield/await does not exist in default parameters. this.yieldPos = this.awaitPos = 0; // Labels in scope. @@ -651,6 +654,9 @@ public class Parser { case 58: ++this.pos; return this.finishToken(TokenType.colon); + case 35: + ++this.pos; + return this.finishToken(TokenType.pound); case 63: return this.readToken_question(); @@ -2191,6 +2197,7 @@ public class Parser { // identifiers. protected Identifier parseIdent(boolean liberal) { Position startLoc = this.startLoc; + boolean isPrivateField = liberal && this.eat(TokenType.pound); if (liberal && this.options.allowReserved() == AllowReserved.NEVER) liberal = false; String name = null; if (this.type == TokenType.name) { @@ -2199,9 +2206,9 @@ public class Parser { && (this.options.ecmaVersion() >= 6 || inputSubstring(this.start, this.end).indexOf("\\") == -1)) this.raiseRecoverable(this.start, "The keyword '" + this.value + "' is reserved"); - if (this.inGenerator && this.value.equals("yield")) + if (!isPrivateField && this.inGenerator && this.value.equals("yield")) this.raiseRecoverable(this.start, "Can not use 'yield' as identifier inside a generator"); - if (this.inAsync && this.value.equals("await")) + if (!isPrivateField && this.inAsync && this.value.equals("await")) this.raiseRecoverable( this.start, "Can not use 'await' as identifier inside an async function"); name = String.valueOf(this.value); @@ -2213,6 +2220,12 @@ public class Parser { this.unexpected(); } this.next(); + if (isPrivateField) { + if (!this.inClass) { + this.raiseRecoverable(this.start, "Cannot use private fields outside a class"); + } + name = "#" + name; + } Identifier node = new Identifier(new SourceLocation(startLoc), name); return this.finishNode(node); } @@ -3127,6 +3140,8 @@ public class Parser { // Parse a class declaration or literal (depending on the // `isStatement` parameter). protected Node parseClass(Position startLoc, boolean isStatement) { + boolean oldInClass = this.inClass; + this.inClass = true; SourceLocation loc = new SourceLocation(startLoc); this.next(); Identifier id = this.parseClassId(isStatement); @@ -3145,6 +3160,8 @@ public class Parser { Node node; if (isStatement) node = new ClassDeclaration(loc, id, superClass, classBody); else node = new ClassExpression(loc, id, superClass, classBody); + + this.inClass = oldInClass; return this.finishNode(node); } diff --git a/javascript/extractor/src/com/semmle/jcorn/TokenType.java b/javascript/extractor/src/com/semmle/jcorn/TokenType.java index 25d6ee9cb53..e3c005c1b6a 100644 --- a/javascript/extractor/src/com/semmle/jcorn/TokenType.java +++ b/javascript/extractor/src/com/semmle/jcorn/TokenType.java @@ -85,6 +85,7 @@ public class TokenType { dot = new TokenType(new Properties(".")), questiondot = new TokenType(new Properties("?.")), question = new TokenType(new Properties("?").beforeExpr()), + pound = new TokenType(kw("#")), arrow = new TokenType(new Properties("=>").beforeExpr()), template = new TokenType(new Properties("template")), invalidTemplate = new TokenType(new Properties("invalidTemplate")), diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index b8589c21393..aba2e3b5bb9 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -535,6 +535,10 @@ module DataFlow { */ pragma[noinline] predicate accesses(Node base, string p) { getBase() = base and getPropertyName() = p } + + predicate isPrivateField() { + getPropertyName().charAt(0) = "#" and getPropertyNameExpr() instanceof Label + } } /** diff --git a/javascript/ql/test/library-tests/Classes/ClassFlow.qll b/javascript/ql/test/library-tests/Classes/ClassFlow.qll new file mode 100644 index 00000000000..6a6d44b667f --- /dev/null +++ b/javascript/ql/test/library-tests/Classes/ClassFlow.qll @@ -0,0 +1,17 @@ +import javascript + +class Configuration extends DataFlow::Configuration { + Configuration() { this = "ClassDataFlowTestingConfig" } + + override predicate isSource(DataFlow::Node source) { + source.getEnclosingExpr().(StringLiteral).getValue().toLowerCase() = "source" + } + + override predicate isSink(DataFlow::Node sink) { + any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() = sink + } +} + +query predicate dataflow(DataFlow::Node pred, DataFlow::Node succ) { + any(Configuration c).hasFlow(pred, succ) +} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Classes/PrivateField.qll b/javascript/ql/test/library-tests/Classes/PrivateField.qll new file mode 100644 index 00000000000..a5b4a5ef04a --- /dev/null +++ b/javascript/ql/test/library-tests/Classes/PrivateField.qll @@ -0,0 +1,9 @@ +import javascript + +query string getAccessModifier(DataFlow::PropRef ref, Expr prop) { + prop = ref.getPropertyNameExpr() and + if ref.isPrivateField() then + result = "Private" + else + result = "Public" +} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Classes/dataflow.js b/javascript/ql/test/library-tests/Classes/dataflow.js new file mode 100644 index 00000000000..bd4454f6b8e --- /dev/null +++ b/javascript/ql/test/library-tests/Classes/dataflow.js @@ -0,0 +1,17 @@ +(function () { + var source = "source"; + + class Foo { + #priv = source; + getPriv() { + return this.#priv; + } + + getFalsePrivate() { + return this["#priv"]; // not the same field as above. But we currently don't distinguish private and "public" fields. + } + } + sink(new Foo().getPriv()); // NOT OK. + + sink(new Foo().getFalsePrivate()); // OK [FP: Is FP because we do nothing special about dataflow for private fields.] +})(); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Classes/privateFields.js b/javascript/ql/test/library-tests/Classes/privateFields.js new file mode 100644 index 00000000000..169482e5826 --- /dev/null +++ b/javascript/ql/test/library-tests/Classes/privateFields.js @@ -0,0 +1,22 @@ +class Foo { + #privDecl = 3; + #if = "if"; // "keywords" are ok. + reads() { + var foo = this.#privUse; + var bar = this["#publicComputed"] + var baz = this.#if; + } + + equals(o) { + return this.#privDecl === o.#privDecl; + } + + writes() { + this.#privDecl = 4; + this["#public"] = 5; + } + + #privSecond; // is a PropNode, not a PropRef. Doesn't matter. + + ["#publicField"] = 6; +} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Classes/tests.expected b/javascript/ql/test/library-tests/Classes/tests.expected index 3673235a6c2..664a82e4422 100644 --- a/javascript/ql/test/library-tests/Classes/tests.expected +++ b/javascript/ql/test/library-tests/Classes/tests.expected @@ -1,5 +1,9 @@ test_FieldInits +| dataflow.js:5:3:5:17 | #priv = source; | dataflow.js:5:11:5:16 | source | | fields.js:3:3:3:8 | y = 42 | fields.js:3:7:3:8 | 42 | +| privateFields.js:2:2:2:15 | #privDecl = 3; | privateFields.js:2:14:2:14 | 3 | +| privateFields.js:3:2:3:12 | #if = "if"; | privateFields.js:3:8:3:11 | "if" | +| privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | privateFields.js:21:21:21:21 | 6 | test_ComputedMethods | tst.js:3:3:3:56 | ["const ... r. */ } | | tst.js:13:3:13:10 | [m]() {} | @@ -15,9 +19,11 @@ test_ClassNodeStaticMethod | points.js:20:1:33:1 | class C ... ;\\n }\\n} | className | points.js:30:19:32:3 | () {\\n ... t";\\n } | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | constructor | staticConstructor.js:2:21:2:59 | () { re ... tor"; } | test_ClassDefinitions +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | | fields.js:1:1:4:1 | class C ... = 42\\n} | | points.js:1:1:18:1 | class P ... ;\\n }\\n} | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | | tst.js:1:9:4:1 | class { ... */ }\\n} | | tst.js:6:1:8:1 | class B ... t); }\\n} | @@ -25,17 +31,27 @@ test_ClassDefinitions test_AccessorMethods | points.js:7:3:9:3 | get dis ... y);\\n } | test_Fields +| dataflow.js:5:3:5:17 | #priv = source; | dataflow.js:5:3:5:7 | #priv | | fields.js:2:3:2:4 | x; | fields.js:2:3:2:3 | x | | fields.js:3:3:3:8 | y = 42 | fields.js:3:3:3:3 | y | +| privateFields.js:2:2:2:15 | #privDecl = 3; | privateFields.js:2:2:2:10 | #privDecl | +| privateFields.js:3:2:3:12 | #if = "if"; | privateFields.js:3:2:3:4 | #if | +| privateFields.js:19:2:19:13 | #privSecond; | privateFields.js:19:2:19:12 | #privSecond | +| privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | privateFields.js:21:3:21:16 | "#publicField" | test_ClassDefinition_getName +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | Foo | | fields.js:1:1:4:1 | class C ... = 42\\n} | C | | points.js:1:1:18:1 | class P ... ;\\n }\\n} | Point | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | ColouredPoint | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | Foo | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | MyClass | | tst.js:1:9:4:1 | class { ... */ }\\n} | A | | tst.js:6:1:8:1 | class B ... t); }\\n} | B | | tst.js:11:1:14:1 | class C ... () {}\\n} | C | test_MethodDefinitions +| dataflow.js:4:12:4:11 | constructor() {} | dataflow.js:4:12:4:11 | constructor | dataflow.js:4:12:4:11 | () {} | dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | +| dataflow.js:6:3:8:3 | getPriv ... iv;\\n\\t\\t} | dataflow.js:6:3:6:9 | getPriv | dataflow.js:6:10:8:3 | () {\\n\\t\\t ... iv;\\n\\t\\t} | dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | +| dataflow.js:10:3:12:3 | getFals ... . \\n\\t\\t} | dataflow.js:10:3:10:17 | getFalsePrivate | dataflow.js:10:18:12:3 | () {\\n\\t\\t ... . \\n\\t\\t} | dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | | fields.js:1:9:1:8 | constructor() {} | fields.js:1:9:1:8 | constructor | fields.js:1:9:1:8 | () {} | fields.js:1:1:4:1 | class C ... = 42\\n} | | points.js:2:3:5:3 | constru ... y;\\n } | points.js:2:3:2:13 | constructor | points.js:2:14:5:3 | (x, y) ... y;\\n } | points.js:1:1:18:1 | class P ... ;\\n }\\n} | | points.js:7:3:9:3 | get dis ... y);\\n } | points.js:7:7:7:10 | dist | points.js:7:11:9:3 | () {\\n ... y);\\n } | points.js:1:1:18:1 | class P ... ;\\n }\\n} | @@ -44,6 +60,10 @@ test_MethodDefinitions | points.js:21:3:24:3 | constru ... c;\\n } | points.js:21:3:21:13 | constructor | points.js:21:14:24:3 | (x, y, ... c;\\n } | points.js:20:1:33:1 | class C ... ;\\n }\\n} | | points.js:26:3:28:3 | toStrin ... ur;\\n } | points.js:26:3:26:10 | toString | points.js:26:11:28:3 | () {\\n ... ur;\\n } | points.js:20:1:33:1 | class C ... ;\\n }\\n} | | points.js:30:3:32:3 | static ... t";\\n } | points.js:30:10:30:18 | className | points.js:30:19:32:3 | () {\\n ... t";\\n } | points.js:20:1:33:1 | class C ... ;\\n }\\n} | +| privateFields.js:1:11:1:10 | constructor() {} | privateFields.js:1:11:1:10 | constructor | privateFields.js:1:11:1:10 | () {} | privateFields.js:1:1:22:1 | class F ... = 6;\\n} | +| privateFields.js:4:2:8:2 | reads() ... #if;\\n\\t} | privateFields.js:4:2:4:6 | reads | privateFields.js:4:7:8:2 | () {\\n\\t\\t ... #if;\\n\\t} | privateFields.js:1:1:22:1 | class F ... = 6;\\n} | +| privateFields.js:10:2:12:2 | equals( ... ecl;\\n\\t} | privateFields.js:10:2:10:7 | equals | privateFields.js:10:8:12:2 | (o) {\\n\\t ... ecl;\\n\\t} | privateFields.js:1:1:22:1 | class F ... = 6;\\n} | +| privateFields.js:14:2:17:2 | writes( ... = 5;\\n\\t} | privateFields.js:14:2:14:7 | writes | privateFields.js:14:8:17:2 | () {\\n\\t\\t ... = 5;\\n\\t} | privateFields.js:1:1:22:1 | class F ... = 6;\\n} | | 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} | | 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} | | 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} | @@ -53,6 +73,10 @@ test_MethodDefinitions | tst.js:12:3:12:8 | m() {} | tst.js:12:3:12:3 | m | tst.js:12:4:12:8 | () {} | tst.js:11:1:14:1 | class C ... () {}\\n} | | tst.js:13:3:13:10 | [m]() {} | tst.js:13:4:13:4 | m | tst.js:13:6:13:10 | () {} | tst.js:11:1:14:1 | class C ... () {}\\n} | test_getAMember +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | dataflow.js:4:12:4:11 | constructor() {} | +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | dataflow.js:5:3:5:17 | #priv = source; | +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | dataflow.js:6:3:8:3 | getPriv ... iv;\\n\\t\\t} | +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | dataflow.js:10:3:12:3 | getFals ... . \\n\\t\\t} | | fields.js:1:1:4:1 | class C ... = 42\\n} | fields.js:1:9:1:8 | constructor() {} | | fields.js:1:1:4:1 | class C ... = 42\\n} | fields.js:2:3:2:4 | x; | | fields.js:1:1:4:1 | class C ... = 42\\n} | fields.js:3:3:3:8 | y = 42 | @@ -63,6 +87,14 @@ test_getAMember | points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:21:3:24:3 | constru ... c;\\n } | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:26:3:28:3 | toStrin ... ur;\\n } | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:30:3:32:3 | static ... t";\\n } | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:1:11:1:10 | constructor() {} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:2:2:2:15 | #privDecl = 3; | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:3:2:3:12 | #if = "if"; | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:4:2:8:2 | reads() ... #if;\\n\\t} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:10:2:12:2 | equals( ... ecl;\\n\\t} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:14:2:17:2 | writes( ... = 5;\\n\\t} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:19:2:19:13 | #privSecond; | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:1:15:1:14 | constructor() {} | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:2:3:2:59 | static ... tor"; } | | tst.js:1:9:4:1 | class { ... */ }\\n} | tst.js:2:3:2:50 | "constr ... r. */ } | @@ -72,6 +104,9 @@ test_getAMember | tst.js:11:1:14:1 | class C ... () {}\\n} | tst.js:12:3:12:8 | m() {} | | tst.js:11:1:14:1 | class C ... () {}\\n} | tst.js:13:3:13:10 | [m]() {} | test_MethodNames +| dataflow.js:4:12:4:11 | constructor() {} | constructor | +| dataflow.js:6:3:8:3 | getPriv ... iv;\\n\\t\\t} | getPriv | +| dataflow.js:10:3:12:3 | getFals ... . \\n\\t\\t} | getFalsePrivate | | fields.js:1:9:1:8 | constructor() {} | constructor | | points.js:2:3:5:3 | constru ... y;\\n } | constructor | | points.js:7:3:9:3 | get dis ... y);\\n } | dist | @@ -80,6 +115,10 @@ test_MethodNames | points.js:21:3:24:3 | constru ... c;\\n } | constructor | | points.js:26:3:28:3 | toStrin ... ur;\\n } | toString | | points.js:30:3:32:3 | static ... t";\\n } | className | +| privateFields.js:1:11:1:10 | constructor() {} | constructor | +| privateFields.js:4:2:8:2 | reads() ... #if;\\n\\t} | reads | +| privateFields.js:10:2:12:2 | equals( ... ecl;\\n\\t} | equals | +| privateFields.js:14:2:17:2 | writes( ... = 5;\\n\\t} | writes | | staticConstructor.js:1:15:1:14 | constructor() {} | constructor | | staticConstructor.js:2:3:2:59 | static ... tor"; } | constructor | | tst.js:2:3:2:50 | "constr ... r. */ } | constructor | @@ -94,27 +133,96 @@ test_SuperExpr | points.js:27:12:27:16 | super | | tst.js:7:19:7:23 | super | test_SyntheticConstructors +| dataflow.js:4:12:4:11 | constructor() {} | | fields.js:1:9:1:8 | constructor() {} | +| privateFields.js:1:11:1:10 | constructor() {} | | staticConstructor.js:1:15:1:14 | constructor() {} | | tst.js:11:9:11:8 | constructor() {} | test_ConstructorDefinitions +| dataflow.js:4:12:4:11 | constructor() {} | | fields.js:1:9:1:8 | constructor() {} | | points.js:2:3:5:3 | constru ... y;\\n } | | points.js:21:3:24:3 | constru ... c;\\n } | +| privateFields.js:1:11:1:10 | constructor() {} | | staticConstructor.js:1:15:1:14 | constructor() {} | | tst.js:2:3:2:50 | "constr ... r. */ } | | tst.js:7:3:7:38 | constru ... get); } | | tst.js:11:9:11:8 | constructor() {} | test_ClassNodeConstructor +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | dataflow.js:4:12:4:11 | () {} | | fields.js:1:1:4:1 | class C ... = 42\\n} | fields.js:1:9:1:8 | () {} | | points.js:1:1:18:1 | class P ... ;\\n }\\n} | points.js:2:14:5:3 | (x, y) ... y;\\n } | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | points.js:21:14:24:3 | (x, y, ... c;\\n } | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | privateFields.js:1:11:1:10 | () {} | | staticConstructor.js:1:1:3:1 | class M ... r"; }\\n} | staticConstructor.js:1:15:1:14 | () {} | | tst.js:1:9:4:1 | class { ... */ }\\n} | tst.js:2:16:2:50 | () { /* ... r. */ } | | tst.js:6:1:8:1 | class B ... t); }\\n} | tst.js:7:14:7:38 | () { su ... get); } | | tst.js:11:1:14:1 | class C ... () {}\\n} | tst.js:11:9:11:8 | () {} | test_ClassNodeInstanceMethod +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | getFalsePrivate | dataflow.js:10:18:12:3 | () {\\n\\t\\t ... . \\n\\t\\t} | +| dataflow.js:4:2:13:2 | class F ... \\n\\t\\t}\\n\\t} | getPriv | dataflow.js:6:10:8:3 | () {\\n\\t\\t ... iv;\\n\\t\\t} | | points.js:1:1:18:1 | class P ... ;\\n }\\n} | toString | points.js:11:11:13:3 | () {\\n ... )";\\n } | | points.js:20:1:33:1 | class C ... ;\\n }\\n} | toString | points.js:26:11:28:3 | () {\\n ... ur;\\n } | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | equals | privateFields.js:10:8:12:2 | (o) {\\n\\t ... ecl;\\n\\t} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | reads | privateFields.js:4:7:8:2 | () {\\n\\t\\t ... #if;\\n\\t} | +| privateFields.js:1:1:22:1 | class F ... = 6;\\n} | writes | privateFields.js:14:8:17:2 | () {\\n\\t\\t ... = 5;\\n\\t} | | tst.js:1:9:4:1 | class { ... */ }\\n} | constructor | tst.js:3:18:3:56 | () { /* ... r. */ } | | tst.js:11:1:14:1 | class C ... () {}\\n} | m | tst.js:12:4:12:8 | () {} | +getAccessModifier +| dataflow.js:4:12:4:11 | constructor() {} | dataflow.js:4:12:4:11 | constructor | Public | +| dataflow.js:5:3:5:17 | #priv = source; | dataflow.js:5:3:5:7 | #priv | Private | +| dataflow.js:6:3:8:3 | getPriv ... iv;\\n\\t\\t} | dataflow.js:6:3:6:9 | getPriv | Public | +| dataflow.js:7:11:7:20 | this.#priv | dataflow.js:7:16:7:20 | #priv | Private | +| dataflow.js:10:3:12:3 | getFals ... . \\n\\t\\t} | dataflow.js:10:3:10:17 | getFalsePrivate | Public | +| dataflow.js:11:11:11:23 | this["#priv"] | dataflow.js:11:16:11:22 | "#priv" | Public | +| dataflow.js:14:7:14:23 | new Foo().getPriv | dataflow.js:14:17:14:23 | getPriv | Public | +| dataflow.js:16:7:16:31 | new Foo ... Private | dataflow.js:16:17:16:31 | getFalsePrivate | Public | +| fields.js:1:9:1:8 | constructor() {} | fields.js:1:9:1:8 | constructor | Public | +| fields.js:3:3:3:8 | y = 42 | fields.js:3:3:3:3 | y | Public | +| points.js:2:3:5:3 | constru ... y;\\n } | points.js:2:3:2:13 | constructor | Public | +| points.js:3:5:3:10 | this.x | points.js:3:10:3:10 | x | Public | +| points.js:4:5:4:10 | this.y | points.js:4:10:4:10 | y | Public | +| points.js:7:3:9:3 | get dis ... y);\\n } | points.js:7:7:7:10 | dist | Public | +| points.js:8:12:8:20 | Math.sqrt | points.js:8:17:8:20 | sqrt | Public | +| points.js:8:22:8:27 | this.x | points.js:8:27:8:27 | x | Public | +| points.js:8:29:8:34 | this.x | points.js:8:34:8:34 | x | Public | +| points.js:8:36:8:41 | this.y | points.js:8:41:8:41 | y | Public | +| points.js:8:43:8:48 | this.y | points.js:8:48:8:48 | y | Public | +| points.js:11:3:13:3 | toStrin ... )";\\n } | points.js:11:3:11:10 | toString | Public | +| points.js:12:18:12:23 | this.x | points.js:12:23:12:23 | x | Public | +| points.js:12:34:12:39 | this.y | points.js:12:39:12:39 | y | Public | +| points.js:15:3:17:3 | static ... t";\\n } | points.js:15:10:15:18 | className | Public | +| points.js:21:3:24:3 | constru ... c;\\n } | points.js:21:3:21:13 | constructor | Public | +| points.js:23:5:23:15 | this.colour | points.js:23:10:23:15 | colour | Public | +| points.js:26:3:28:3 | toStrin ... ur;\\n } | points.js:26:3:26:10 | toString | Public | +| points.js:27:12:27:25 | super.toString | points.js:27:18:27:25 | toString | Public | +| points.js:27:40:27:50 | this.colour | points.js:27:45:27:50 | colour | Public | +| points.js:30:3:32:3 | static ... t";\\n } | points.js:30:10:30:18 | className | Public | +| privateFields.js:1:11:1:10 | constructor() {} | privateFields.js:1:11:1:10 | constructor | Public | +| privateFields.js:2:2:2:15 | #privDecl = 3; | privateFields.js:2:2:2:10 | #privDecl | Private | +| privateFields.js:3:2:3:12 | #if = "if"; | privateFields.js:3:2:3:4 | #if | Private | +| privateFields.js:4:2:8:2 | reads() ... #if;\\n\\t} | privateFields.js:4:2:4:6 | reads | Public | +| privateFields.js:5:13:5:25 | this.#privUse | privateFields.js:5:18:5:25 | #privUse | Private | +| privateFields.js:6:13:6:35 | this["# ... puted"] | privateFields.js:6:18:6:34 | "#publicComputed" | Public | +| privateFields.js:7:13:7:20 | this.#if | privateFields.js:7:18:7:20 | #if | Private | +| privateFields.js:10:2:12:2 | equals( ... ecl;\\n\\t} | privateFields.js:10:2:10:7 | equals | Public | +| privateFields.js:11:10:11:23 | this.#privDecl | privateFields.js:11:15:11:23 | #privDecl | Private | +| privateFields.js:11:29:11:39 | o.#privDecl | privateFields.js:11:31:11:39 | #privDecl | Private | +| privateFields.js:14:2:17:2 | writes( ... = 5;\\n\\t} | privateFields.js:14:2:14:7 | writes | Public | +| privateFields.js:15:3:15:16 | this.#privDecl | privateFields.js:15:8:15:16 | #privDecl | Private | +| privateFields.js:16:3:16:17 | this["#public"] | privateFields.js:16:8:16:16 | "#public" | Public | +| privateFields.js:21:2:21:22 | ["#publ ... "] = 6; | privateFields.js:21:3:21:16 | "#publicField" | Public | +| staticConstructor.js:1:15:1:14 | constructor() {} | staticConstructor.js:1:15:1:14 | constructor | Public | +| staticConstructor.js:2:3:2:59 | static ... tor"; } | staticConstructor.js:2:10:2:20 | constructor | Public | +| staticConstructor.js:4:1:4:11 | console.log | staticConstructor.js:4:9:4:11 | log | Public | +| staticConstructor.js:4:13:4:31 | MyClass.constructor | staticConstructor.js:4:21:4:31 | constructor | Public | +| tst.js:2:3:2:50 | "constr ... r. */ } | tst.js:2:3:2:15 | "constructor" | Public | +| tst.js:3:3:3:56 | ["const ... r. */ } | tst.js:3:4:3:16 | "constructor" | Public | +| tst.js:7:3:7:38 | constru ... get); } | tst.js:7:3:7:13 | constructor | Public | +| tst.js:11:9:11:8 | constructor() {} | tst.js:11:9:11:8 | constructor | Public | +| tst.js:12:3:12:8 | m() {} | tst.js:12:3:12:3 | m | Public | +| tst.js:13:3:13:10 | [m]() {} | tst.js:13:4:13:4 | m | Public | +| tst.js:17:3:17:20 | m() { return 42; } | tst.js:17:3:17:3 | m | Public | +dataflow +| dataflow.js:2:15:2:22 | "source" | dataflow.js:14:7:14:25 | new Foo().getPriv() | +| dataflow.js:2:15:2:22 | "source" | dataflow.js:16:7:16:33 | new Foo ... ivate() | diff --git a/javascript/ql/test/library-tests/Classes/tests.ql b/javascript/ql/test/library-tests/Classes/tests.ql index 3009546636c..41fb13f3af0 100644 --- a/javascript/ql/test/library-tests/Classes/tests.ql +++ b/javascript/ql/test/library-tests/Classes/tests.ql @@ -16,3 +16,5 @@ import SyntheticConstructors import ConstructorDefinitions import ClassNodeConstructor import ClassNodeInstanceMethod +import PrivateField +import ClassFlow \ No newline at end of file