@@ -6,103 +6,142 @@ import javascript
66
77module Closure {
88 /**
9- * A call to a function in the `goog` namespace such as `goog.provide` or `goog.load` .
9+ * A reference to a Closure namespace.
1010 */
11- class GoogFunctionCall extends CallExpr {
12- GoogFunctionCall ( ) {
13- exists ( GlobalVariable gv | gv .getName ( ) = "goog" |
14- this .getCallee ( ) .( DotExpr ) .getBase ( ) = gv .getAnAccess ( )
15- )
16- }
11+ class ClosureNamespaceRef extends DataFlow:: Node {
12+ ClosureNamespaceRef:: Range range ;
13+
14+ ClosureNamespaceRef ( ) { this = range }
1715
18- /** Gets the name of the invoked function. */
19- string getFunctionName ( ) { result = getCallee ( ) .( DotExpr ) .getPropertyName ( ) }
16+ /**
17+ * Gets the namespace being referenced.
18+ */
19+ string getClosureNamespace ( ) { result = range .getClosureNamespace ( ) }
20+ }
21+
22+ module ClosureNamespaceRef {
23+ /**
24+ * A reference to a Closure namespace.
25+ *
26+ * Can be subclassed to classify additional nodes as namespace references.
27+ */
28+ abstract class Range extends DataFlow:: Node {
29+ /**
30+ * Gets the namespace being referenced.
31+ */
32+ abstract string getClosureNamespace ( ) ;
33+ }
2034 }
2135
2236 /**
23- * An expression statement consisting of a call to a function
24- * in the `goog` namespace.
37+ * A data flow node that returns the value of a closure namespace.
2538 */
26- class GoogFunctionCallStmt extends ExprStmt {
27- GoogFunctionCallStmt ( ) { super .getExpr ( ) instanceof GoogFunctionCall }
28-
29- override GoogFunctionCall getExpr ( ) { result = super .getExpr ( ) }
39+ class ClosureNamespaceAccess extends ClosureNamespaceRef {
40+ override ClosureNamespaceAccess:: Range range ;
41+ }
3042
31- /** Gets the name of the invoked function. */
32- string getFunctionName ( ) { result = getExpr ( ) .getFunctionName ( ) }
43+ module ClosureNamespaceAccess {
44+ /**
45+ * A data flow node that returns the value of a closure namespace.
46+ *
47+ * Can be subclassed to classify additional nodes as namespace accesses.
48+ */
49+ abstract class Range extends ClosureNamespaceRef:: Range { }
50+ }
3351
34- /** Gets the `i`th argument to the invoked function. */
35- Expr getArgument ( int i ) { result = getExpr ( ) .getArgument ( i ) }
52+ /**
53+ * A call to a method on the `goog.` namespace, as a closure reference.
54+ */
55+ abstract private class DefaultNamespaceRef extends DataFlow:: MethodCallNode ,
56+ ClosureNamespaceRef:: Range {
57+ DefaultNamespaceRef ( ) { this = DataFlow:: globalVarRef ( "goog" ) .getAMethodCall ( ) }
3658
37- /** Gets an argument to the invoked function. */
38- Expr getAnArgument ( ) { result = getArgument ( _) }
59+ override string getClosureNamespace ( ) { result = getArgument ( 0 ) .asExpr ( ) .getStringValue ( ) }
3960 }
4061
41- abstract private class GoogNamespaceRef extends ExprOrStmt {
42- abstract string getNamespaceId ( ) ;
62+ /**
63+ * Holds if `node` is the data flow node corresponding to the expression in
64+ * a top-level expression statement.
65+ */
66+ private predicate isTopLevelExpr ( DataFlow:: Node node ) {
67+ node .getTopLevel ( ) .getAChildStmt ( ) .( ExprStmt ) .getExpr ( ) .flow ( ) = node
4368 }
4469
4570 /**
46- * A call to `goog.provide`.
71+ * A top-level call to `goog.provide`.
4772 */
48- class GoogProvide extends GoogFunctionCallStmt , GoogNamespaceRef {
49- GoogProvide ( ) { getFunctionName ( ) = "provide" }
73+ private class DefaultClosureProvideCall extends DefaultNamespaceRef {
74+ DefaultClosureProvideCall ( ) {
75+ getMethodName ( ) = "provide" and
76+ isTopLevelExpr ( this )
77+ }
78+ }
5079
51- /** Gets the identifier of the namespace created by this call. */
52- override string getNamespaceId ( ) { result = getArgument ( 0 ) .getStringValue ( ) }
80+ /**
81+ * A top-level call to `goog.provide`.
82+ */
83+ class ClosureProvideCall extends ClosureNamespaceRef , DataFlow:: MethodCallNode {
84+ override DefaultClosureProvideCall range ;
5385 }
5486
5587 /**
5688 * A call to `goog.require`.
5789 */
58- class GoogRequire extends GoogFunctionCall , GoogNamespaceRef {
59- GoogRequire ( ) { getFunctionName ( ) = "require" }
60-
61- /** Gets the identifier of the namespace imported by this call. */
62- override string getNamespaceId ( ) { result = getArgument ( 0 ) .getStringValue ( ) }
90+ private class DefaultClosureRequireCall extends DefaultNamespaceRef , ClosureNamespaceAccess:: Range {
91+ DefaultClosureRequireCall ( ) { getMethodName ( ) = "require" }
6392 }
6493
65- private class GoogRequireImport extends GoogRequire , Import {
66- /** Gets the module in which this import appears. */
67- override Module getEnclosingModule ( ) { result = getTopLevel ( ) }
68-
69- /** Gets the (unresolved) path that this import refers to. */
70- override PathExpr getImportedPath ( ) { result = getArgument ( 0 ) }
94+ /**
95+ * A call to `goog.require`.
96+ */
97+ class ClosureRequireCall extends ClosureNamespaceAccess , DataFlow:: MethodCallNode {
98+ override DefaultClosureRequireCall range ;
7199 }
72100
73101 /**
74- * A call to `goog.module` or `goog.declareModuleId`.
102+ * A top-level call to `goog.module` or `goog.declareModuleId`.
75103 */
76- class GoogModuleDeclaration extends GoogFunctionCallStmt , GoogNamespaceRef {
77- GoogModuleDeclaration ( ) {
78- getFunctionName ( ) = "module" or
79- getFunctionName ( ) = "declareModuleId"
104+ private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef {
105+ DefaultClosureModuleDeclaration ( ) {
106+ ( getMethodName ( ) = "module" or getMethodName ( ) = "declareModuleId" ) and
107+ isTopLevelExpr ( this )
80108 }
109+ }
81110
82- /** Gets the identifier of the namespace imported by this call. */
83- override string getNamespaceId ( ) { result = getArgument ( 0 ) .getStringValue ( ) }
111+ /**
112+ * A top-level call to `goog.module` or `goog.declareModuleId`.
113+ */
114+ class ClosureModuleDeclaration extends ClosureNamespaceRef , DataFlow:: MethodCallNode {
115+ override DefaultClosureModuleDeclaration range ;
84116 }
85117
86118 /**
87119 * A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`.
88120 */
89121 class ClosureModule extends Module {
90- ClosureModule ( ) { getAChildStmt ( ) instanceof GoogModuleDeclaration }
122+ ClosureModule ( ) {
123+ // Use AST-based predicate to cut recursive dependencies.
124+ exists ( MethodCallExpr call |
125+ getAStmt ( ) .( ExprStmt ) .getExpr ( ) = call and
126+ call .getReceiver ( ) .( GlobalVarAccess ) .getName ( ) = "goog" and
127+ ( call .getMethodName ( ) = "module" or call .getMethodName ( ) = "declareModuleId" )
128+ )
129+ }
91130
92131 /**
93132 * Gets the call to `goog.module` or `goog.declareModuleId` in this module.
94133 */
95- GoogModuleDeclaration getModuleDeclaration ( ) { result = getAChildStmt ( ) }
134+ ClosureModuleDeclaration getModuleDeclaration ( ) { result . getTopLevel ( ) = this }
96135
97136 /**
98137 * Gets the namespace of this module.
99138 */
100- string getNamespaceId ( ) { result = getModuleDeclaration ( ) .getNamespaceId ( ) }
139+ string getClosureNamespace ( ) { result = getModuleDeclaration ( ) .getClosureNamespace ( ) }
101140
102141 override Module getAnImportedModule ( ) {
103- exists ( GoogRequireImport imprt |
104- imprt .getEnclosingModule ( ) = this and
105- result .( ClosureModule ) .getNamespaceId ( ) = imprt .getNamespaceId ( )
142+ exists ( ClosureRequireCall imprt |
143+ imprt .getTopLevel ( ) = this and
144+ result .( ClosureModule ) .getClosureNamespace ( ) = imprt .getClosureNamespace ( )
106145 )
107146 }
108147
@@ -115,7 +154,7 @@ module Closure {
115154 * Has no result for ES6 modules using `goog.declareModuleId`.
116155 */
117156 Variable getExportsVariable ( ) {
118- getModuleDeclaration ( ) .getFunctionName ( ) = "module" and
157+ getModuleDeclaration ( ) .getMethodName ( ) = "module" and
119158 result = getScope ( ) .getVariable ( "exports" )
120159 }
121160
@@ -139,42 +178,52 @@ module Closure {
139178 class ClosureScript extends TopLevel {
140179 ClosureScript ( ) {
141180 not this instanceof ClosureModule and
142- getAChildStmt ( ) instanceof GoogProvide
143- or
144- getAChildStmt ( ) .( ExprStmt ) .getExpr ( ) instanceof GoogRequire
181+ (
182+ any ( ClosureProvideCall provide ) .getTopLevel ( ) = this
183+ or
184+ any ( ClosureRequireCall require ) .getTopLevel ( ) = this
185+ )
145186 }
146187
147188 /** Gets the identifier of a namespace required by this module. */
148189 string getARequiredNamespace ( ) {
149- result = getAChildStmt ( ) .( ExprStmt ) .getExpr ( ) .( GoogRequire ) .getNamespaceId ( )
190+ exists ( ClosureRequireCall require |
191+ require .getTopLevel ( ) = this and
192+ result = require .getClosureNamespace ( )
193+ )
150194 }
151195
152196 /** Gets the identifer of a namespace provided by this module. */
153- string getAProvidedNamespace ( ) { result = getAChildStmt ( ) .( GoogProvide ) .getNamespaceId ( ) }
197+ string getAProvidedNamespace ( ) {
198+ exists ( ClosureProvideCall require |
199+ require .getTopLevel ( ) = this and
200+ result = require .getClosureNamespace ( )
201+ )
202+ }
154203 }
155204
156205 /**
157206 * Holds if `name` is a closure namespace, including proper namespace prefixes.
158207 */
159208 pragma [ noinline]
160- predicate isLibraryNamespacePath ( string name ) {
161- exists ( string namespace | namespace = any ( GoogNamespaceRef provide ) . getNamespaceId ( ) |
209+ predicate isClosureNamespace ( string name ) {
210+ exists ( string namespace | namespace = any ( ClosureNamespaceRef ref ) . getClosureNamespace ( ) |
162211 name = namespace .substring ( 0 , namespace .indexOf ( "." ) )
163212 or
164213 name = namespace
165214 )
166215 }
167216
168217 /**
169- * Gets the closure namespace path addressed by the given dataflow node, if any.
218+ * Gets the closure namespace path addressed by the given data flow node, if any.
170219 */
171- string getLibraryAccessPath ( DataFlow:: SourceNode node ) {
172- isLibraryNamespacePath ( result ) and
220+ string getClosureNamespaceFromSourceNode ( DataFlow:: SourceNode node ) {
221+ isClosureNamespace ( result ) and
173222 node = DataFlow:: globalVarRef ( result )
174223 or
175224 exists ( DataFlow:: SourceNode base , string basePath , string prop |
176- basePath = getLibraryAccessPath ( base ) and
177- isLibraryNamespacePath ( basePath ) and
225+ basePath = getClosureNamespaceFromSourceNode ( base ) and
226+ isClosureNamespace ( basePath ) and
178227 node = base .getAPropertyRead ( prop ) and
179228 result = basePath + "." + prop
180229 )
@@ -184,21 +233,24 @@ module Closure {
184233 // foo.bar = { baz() {} }
185234 exists ( DataFlow:: PropWrite write |
186235 node = write .getRhs ( ) and
187- result = getWrittenLibraryAccessPath ( write )
236+ result = getWrittenClosureNamespace ( write )
188237 )
189238 or
190- result = node .asExpr ( ) . ( GoogRequire ) . getNamespaceId ( )
239+ result = node .( ClosureNamespaceAccess ) . getClosureNamespace ( )
191240 }
192241
193242 /**
194243 * Gets the closure namespace path written to by the given property write, if any.
195244 */
196- string getWrittenLibraryAccessPath ( DataFlow:: PropWrite node ) {
197- result = getLibraryAccessPath ( node .getBase ( ) .getALocalSource ( ) ) + "." + node .getPropertyName ( )
245+ string getWrittenClosureNamespace ( DataFlow:: PropWrite node ) {
246+ result = getClosureNamespaceFromSourceNode ( node .getBase ( ) .getALocalSource ( ) ) + "." +
247+ node .getPropertyName ( )
198248 }
199249
200250 /**
201- * Gets a dataflow node that refers to the given value exported from a Closure module.
251+ * Gets a data flow node that refers to the given value exported from a Closure module.
202252 */
203- DataFlow:: SourceNode moduleImport ( string moduleName ) { getLibraryAccessPath ( result ) = moduleName }
253+ DataFlow:: SourceNode moduleImport ( string moduleName ) {
254+ getClosureNamespaceFromSourceNode ( result ) = moduleName
255+ }
204256}
0 commit comments