diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java index a2f006e32395..b4739b061fb2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java @@ -819,6 +819,7 @@ public Label visit(Program nd, Context c) { // add all declared global (or module-scoped) names, both non-lexical and lexical scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, false, DeclKind.none)); scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, true, DeclKind.none)); + scopeManager.addVariables("this"); visitAll(nd.getBody(), toplevelLabel); @@ -1070,6 +1071,9 @@ private void extractFunction(IFunction nd, Label key) { scopeManager.enterScope((Node) nd); scopeManager.addNames(locals); + if (!(nd instanceof ArrowFunctionExpression)) { + scopeManager.addVariables("this"); + } // The name of a function expression binds to its own scope. if (nd.getId() != null && nd instanceof AFunctionExpression) { @@ -1541,8 +1545,9 @@ private Label visit(AClass ac, Label key, Node scopeNode, boolean isClassExpress if (!isClassExpression) { visit(ac.getId(), key, 0, IdContext.VAR_AND_TYPE_DECL); } + scopeManager.enterScope(scopeNode); + scopeManager.addVariables("this"); // 'this' in static field initialiers refers to the class if (ac.hasId() || ac.hasTypeParameters()) { - scopeManager.enterScope(scopeNode); if (isClassExpression && ac.hasId()) { scopeManager.addVariables(ac.getId().getName()); scopeManager.addTypeName(ac.getId().getName()); @@ -1565,9 +1570,7 @@ private Label visit(AClass ac, Label key, Node scopeNode, boolean isClassExpress addDefaultConstructor(ac); } visit(ac.getBody(), key, 2); - if (ac.hasId() || ac.hasTypeParameters()) { - scopeManager.leaveScope(); - } + scopeManager.leaveScope(); emitNodeSymbol(ac, key); return key; } @@ -1893,6 +1896,15 @@ public Label visit(JSXThisExpr nd, Context c) { return visit((ThisExpression) nd, c); } + @Override + public Label visit(ThisExpression nd, Context c) { + Label key = super.visit(nd, c); + if (c.idcontext == IdContext.VAR_BIND || c.idcontext == IdContext.VAR_IN_TYPE_BIND) { + addVariableBinding("bind", key, "this"); + } + return key; + } + @Override public Label visit(JSXMemberExpression nd, Context c) { Label key = super.visit(nd, c); diff --git a/javascript/ql/lib/semmlecode.javascript.dbscheme b/javascript/ql/lib/semmlecode.javascript.dbscheme index 578367e82a25..26a123164be8 100644 --- a/javascript/ql/lib/semmlecode.javascript.dbscheme +++ b/javascript/ql/lib/semmlecode.javascript.dbscheme @@ -453,7 +453,7 @@ is_arguments_object (int id: @variable ref); @lexical_name = @variable | @local_type_name | @local_namespace_name; -@bind_id = @varaccess | @local_var_type_access; +@bind_id = @varaccess | @local_var_type_access | @this_expr; bind (unique int id: @bind_id ref, int decl: @variable ref); diff --git a/javascript/ql/test/library-tests/variables/tests.expected b/javascript/ql/test/library-tests/variables/tests.expected index 0e3d568b570c..537281e159db 100644 --- a/javascript/ql/test/library-tests/variables/tests.expected +++ b/javascript/ql/test/library-tests/variables/tests.expected @@ -54,23 +54,30 @@ getAnAssignedExpr | y | let.js:19:12:19:13 | 19 | getDeclaringContainer | arrayPatternDefault.js:1:2:1:1 | arguments | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} | +| arrayPatternDefault.js:1:2:1:1 | this | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} | | arrayPatternDefault.js:1:11:1:11 | o | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} | | arrayPatternDefault.js:2:8:2:8 | x | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} | | assignments.js:3:1:3:0 | arguments | assignments.js:3:1:6:1 | functio ... = 56;\\n} | +| assignments.js:3:1:3:0 | this | assignments.js:3:1:6:1 | functio ... = 56;\\n} | | assignments.js:4:6:4:6 | g | assignments.js:3:1:6:1 | functio ... = 56;\\n} | | assignments.js:4:10:4:9 | arguments | assignments.js:4:10:4:24 | function h() {} | +| assignments.js:4:10:4:9 | this | assignments.js:4:10:4:24 | function h() {} | | assignments.js:4:19:4:19 | h | assignments.js:4:10:4:24 | function h() {} | +| defaultargs.js:1:2:1:1 | this | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} | | defaultargs.js:2:7:2:7 | x | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} | | defaultargs.js:2:10:2:18 | arguments | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} | | defaultargs.js:3:3:3:2 | arguments | defaultargs.js:3:3:3:25 | functio ... = x) {} | +| defaultargs.js:3:3:3:2 | this | defaultargs.js:3:3:3:25 | functio ... = x) {} | | defaultargs.js:3:12:3:12 | f | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} | | defaultargs.js:3:14:3:14 | x | defaultargs.js:3:3:3:25 | functio ... = x) {} | | defaultargs.js:3:17:3:17 | y | defaultargs.js:3:3:3:25 | functio ... = x) {} | | defaultargs.js:4:3:4:2 | arguments | defaultargs.js:4:3:4:51 | functio ... [0]) {} | +| defaultargs.js:4:3:4:2 | this | defaultargs.js:4:3:4:51 | functio ... [0]) {} | | defaultargs.js:4:12:4:12 | g | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} | | defaultargs.js:4:14:4:14 | x | defaultargs.js:4:3:4:51 | functio ... [0]) {} | | defaultargs.js:4:32:4:32 | y | defaultargs.js:4:3:4:51 | functio ... [0]) {} | | for.js:1:2:1:1 | arguments | for.js:1:2:5:1 | functio ... x;\\n} | +| for.js:1:2:1:1 | this | for.js:1:2:5:1 | functio ... x;\\n} | | for.js:1:11:1:11 | o | for.js:1:2:5:1 | functio ... x;\\n} | | for.js:2:7:2:7 | x | for.js:1:2:5:1 | functio ... x;\\n} | | legacyletstmt.js:3:6:3:6 | x | legacyletstmt.js:1:1:8:0 | | @@ -82,20 +89,26 @@ getDeclaringContainer | let.js:6:17:6:17 | x | let.js:1:1:22:0 | | | let.js:9:18:9:18 | x | let.js:1:1:22:0 | | | let.js:14:1:14:0 | arguments | let.js:14:1:21:1 | functio ... }\\n} | +| let.js:14:1:14:0 | this | let.js:14:1:21:1 | functio ... }\\n} | | let.js:14:14:14:14 | x | let.js:14:1:21:1 | functio ... }\\n} | | let.js:17:11:17:11 | y | let.js:14:1:21:1 | functio ... }\\n} | | typeoftype.ts:1:1:1:0 | arguments | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} | +| typeoftype.ts:1:1:1:0 | this | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} | | typeoftype.ts:2:7:2:7 | x | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} | | typeoftype.ts:3:3:3:2 | arguments | typeoftype.ts:3:3:5:3 | functio ... e x\\n } | +| typeoftype.ts:3:3:3:2 | this | typeoftype.ts:3:3:5:3 | functio ... e x\\n } | | typeoftype.ts:3:12:3:12 | g | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} | | typeoftype.ts:4:9:4:9 | y | typeoftype.ts:3:3:5:3 | functio ... e x\\n } | | variables.js:8:1:8:0 | arguments | variables.js:8:1:12:1 | functio ... ar x;\\n} | +| variables.js:8:1:8:0 | this | variables.js:8:1:12:1 | functio ... ar x;\\n} | | variables.js:9:6:9:6 | x | variables.js:8:1:12:1 | functio ... ar x;\\n} | | variables.js:13:1:13:0 | arguments | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | +| variables.js:13:1:13:0 | this | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | | variables.js:13:12:13:12 | y | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | | variables.js:13:15:13:15 | z | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | | variables.js:15:6:15:6 | x | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | | variables.js:16:2:16:1 | arguments | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} | +| variables.js:16:2:16:1 | this | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} | | variables.js:16:11:16:11 | h | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} | | variables.js:16:13:16:13 | z | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} | | variables.js:18:11:18:11 | y | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |