Skip to content

Commit 44e6f85

Browse files
committed
JS: Add closure support and IIFEs and ObjectPatterns
1 parent 2b1da6e commit 44e6f85

File tree

2 files changed

+96
-3
lines changed

2 files changed

+96
-3
lines changed

javascript/ql/lib/semmle/javascript/internal/NameResolution.qll

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,61 @@ module NameResolution {
137137
node1 = type.getExpressionName() and
138138
node2 = type
139139
)
140+
or
141+
exists(ClosureModuleDeclaration decl, ClosureImport imprt |
142+
decl.getClosureNamespace() = imprt.getClosureNamespace() and
143+
node1 = decl.getContainer() and
144+
node2 = imprt
145+
)
146+
or
147+
exists(Closure::ClosureModule mod |
148+
node1 = mod.getScope().getVariable("exports") and
149+
node2 = mod
150+
)
151+
or
152+
exists(ImmediatelyInvokedFunctionExpr fun, int i |
153+
node1 = fun.getArgument(i) and
154+
node2 = fun.getParameter(i)
155+
)
156+
}
157+
158+
class ClosureImport extends CallExpr {
159+
ClosureImport() { this.getCallee().(PropAccess).getQualifiedName() = "goog.require" }
160+
161+
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
162+
}
163+
164+
class ClosureModuleDeclaration extends CallExpr {
165+
private string kind;
166+
167+
ClosureModuleDeclaration() {
168+
this.getCallee().(PropAccess).getQualifiedName() = kind and
169+
kind = ["goog.module", "goog.declareModuleId"]
170+
}
171+
172+
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
173+
174+
string getModuleKind() { result = kind }
175+
}
176+
177+
class ClosureExport extends AssignExpr {
178+
private PropAccess lhs;
179+
180+
ClosureExport() {
181+
exists(ClosureModuleDeclaration decl, Module mod |
182+
decl.getModuleKind() = "goog.module" and
183+
decl.getContainer() = mod and
184+
this.getTopLevel() = mod and
185+
this.getLhs() = lhs and
186+
lhs.getBase() = mod.getScope().getVariable("exports").getAnAccess()
187+
)
188+
}
189+
190+
string getName() { result = lhs.getPropertyName() }
191+
192+
Module getModule() { result = this.getTopLevel() }
193+
194+
Expr getValue() { result = this.getRhs() }
140195
}
141196

142197
/**
@@ -167,6 +222,11 @@ module NameResolution {
167222
node2 = access
168223
)
169224
or
225+
exists(ObjectPattern pattern |
226+
node1 = pattern and
227+
node2 = pattern.getPropertyPatternByName(name).getValuePattern()
228+
)
229+
or
170230
exists(ImportSpecifier spec |
171231
node1 = spec.getImportDeclaration().getImportedPath() and
172232
name = spec.getImportedName() and
@@ -259,6 +319,12 @@ module NameResolution {
259319
mod = enum and
260320
result = enum.getMemberByName(name).getIdentifier()
261321
)
322+
or
323+
exists(ClosureExport exprt |
324+
mod = exprt.getModule() and
325+
name = exprt.getName() and
326+
result = exprt.getValue()
327+
)
262328
}
263329

264330
/** Steps that only apply for this configuration. */
@@ -347,6 +413,16 @@ module NameResolution {
347413
)
348414
}
349415

416+
private predicate needsQualifiedName(Node node) {
417+
node = any(JSDocLocalTypeAccess t).getALexicalName().(Variable)
418+
or
419+
exists(Node prev | needsQualifiedName(prev) |
420+
ValueFlow::step(node, prev)
421+
or
422+
readStep(node, _, prev)
423+
)
424+
}
425+
350426
/**
351427
* Holds if `node` is a reference to the given module, or a qualified name rooted in that module.
352428
*
@@ -374,6 +450,23 @@ module NameResolution {
374450
access.getName() = qualifiedName
375451
)
376452
or
453+
mod = "global" and
454+
exists(JSDocLocalTypeAccess access |
455+
node = access and
456+
not exists(access.getALexicalName()) and
457+
access.getName() = qualifiedName
458+
)
459+
or
460+
mod = "global" and
461+
exists(GlobalVarAccess access |
462+
node = access and
463+
needsQualifiedName(access) and // restrict number of qualified names we generate
464+
access.getName() = qualifiedName
465+
)
466+
or
467+
mod = "global" and
468+
qualifiedName = node.(ClosureImport).getClosureNamespace()
469+
or
377470
// Additionally track through bulk re-exports (`export * from 'mod`).
378471
// These are normally handled by 'exportAs' which supports various shadowing rules,
379472
// but has no effect when the ultimate re-exported module is not resolved to a Module.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
| bar.js:5:14:5:14 | x | x |
1+
| bar.js:5:14:5:14 | x | ns.very.long.namespace |
22
| bar.js:5:14:5:18 | x.Foo | ns.very.long.namespace.Foo |
3-
| bar.js:12:14:12:17 | iife | iife |
3+
| bar.js:12:14:12:17 | iife | IIFE |
44
| bar.js:12:14:12:21 | iife.Foo | IIFE.Foo |
55
| closure.js:8:12:8:15 | goog | goog |
66
| closure.js:8:12:8:19 | goog.net | goog.net |
77
| closure.js:8:12:8:28 | goog.net.SomeType | goog.net.SomeType |
8-
| closure.js:9:12:9:14 | net | net |
8+
| closure.js:9:12:9:14 | net | goog.net |
99
| closure.js:9:12:9:23 | net.SomeType | goog.net.SomeType |
1010
| closure.js:10:12:10:19 | SomeType | goog.net.SomeType |

0 commit comments

Comments
 (0)