@@ -37,39 +37,162 @@ class GoogFunctionCallStmt extends ExprStmt {
3737 Expr getAnArgument ( ) { result = getArgument ( _) }
3838}
3939
40+ private abstract class GoogNamespaceRef extends ExprOrStmt {
41+ abstract string getNamespaceId ( ) ;
42+ }
43+
4044/**
4145 * A call to `goog.provide`.
4246 */
43- class GoogProvide extends GoogFunctionCallStmt {
47+ class GoogProvide extends GoogFunctionCallStmt , GoogNamespaceRef {
4448 GoogProvide ( ) { getFunctionName ( ) = "provide" }
4549
4650 /** Gets the identifier of the namespace created by this call. */
47- string getNamespaceId ( ) { result = getArgument ( 0 ) .( ConstantString ) .getStringValue ( ) }
51+ override string getNamespaceId ( ) { result = getArgument ( 0 ) .( ConstantString ) .getStringValue ( ) }
4852}
4953
5054/**
5155 * A call to `goog.require`.
5256 */
53- class GoogRequire extends GoogFunctionCallStmt {
57+ class GoogRequire extends GoogFunctionCall , GoogNamespaceRef {
5458 GoogRequire ( ) { getFunctionName ( ) = "require" }
5559
5660 /** Gets the identifier of the namespace imported by this call. */
57- string getNamespaceId ( ) { result = getArgument ( 0 ) .( ConstantString ) .getStringValue ( ) }
61+ override string getNamespaceId ( ) { result = getArgument ( 0 ) .( ConstantString ) .getStringValue ( ) }
62+ }
63+
64+ private class GoogRequireImport extends GoogRequire , Import {
65+ /** Gets the module in which this import appears. */
66+ override Module getEnclosingModule ( ) { result = getTopLevel ( ) }
67+
68+ /** Gets the (unresolved) path that this import refers to. */
69+ override PathExpr getImportedPath ( ) {
70+ result = getArgument ( 0 )
71+ }
5872}
5973
6074/**
61- * A Closure module, that is, a toplevel that contains a call to `goog.provide` or
62- * `goog.require`.
75+ * A call to `goog.module` or `goog.declareModuleId`.
6376 */
64- class ClosureModule extends TopLevel {
77+ class GoogModuleDeclaration extends GoogFunctionCallStmt , GoogNamespaceRef {
78+ GoogModuleDeclaration ( ) {
79+ getFunctionName ( ) = "module" or
80+ getFunctionName ( ) = "declareModuleId"
81+ }
82+
83+ /** Gets the identifier of the namespace imported by this call. */
84+ override string getNamespaceId ( ) { result = getArgument ( 0 ) .( ConstantString ) .getStringValue ( ) }
85+ }
86+
87+ /**
88+ * A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`.
89+ */
90+ class ClosureModule extends Module {
6591 ClosureModule ( ) {
92+ getAChildStmt ( ) instanceof GoogModuleDeclaration
93+ }
94+
95+ /**
96+ * Gets the call to `goog.module()` or `goog.declareModuleId` in this module.
97+ */
98+ GoogModuleDeclaration getModuleDeclaration ( ) {
99+ result = getAChildStmt ( )
100+ }
101+
102+ /**
103+ * Gets the namespace of this module.
104+ */
105+ string getNamespaceId ( ) { result = getModuleDeclaration ( ) .getNamespaceId ( ) }
106+
107+ override Module getAnImportedModule ( ) {
108+ exists ( GoogRequireImport imprt |
109+ imprt .getEnclosingModule ( ) = this and
110+ result .( ClosureModule ) .getNamespaceId ( ) = imprt .getNamespaceId ( )
111+ )
112+ }
113+
114+ /**
115+ * Gets the top-level `exports` variable in this module, if this module is defined by
116+ * a `good.module` call.
117+ *
118+ * This variable denotes the object exported from this module.
119+ *
120+ * Has no result for ES6 modules using `goog.declareModuleId`.
121+ */
122+ Variable getExportsVariable ( ) {
123+ getModuleDeclaration ( ) .getFunctionName ( ) = "module" and
124+ result .getScope ( ) = this .getScope ( ) and
125+ result .getName ( ) = "exports"
126+ }
127+
128+ override predicate exports ( string name , ASTNode export ) {
129+ // exports.foo = bar
130+ export .( AssignExpr ) .getLhs ( ) .( PropAccess ) .accesses ( getExportsVariable ( ) .getAnAccess ( ) , name )
131+ or
132+ // exports = { foo: bar }
133+ exists ( VarDef def |
134+ def .getTarget ( ) = getExportsVariable ( ) .getAReference ( ) and
135+ def .getSource ( ) .( ObjectExpr ) .getPropertyByName ( name ) = export
136+ )
137+ }
138+ }
139+
140+ /**
141+ * A global Closure script, that is, a toplevel that is executed in the global scope and
142+ * contains a toplevel call to `goog.provide` or `goog.require`.
143+ */
144+ class ClosureScript extends TopLevel {
145+ ClosureScript ( ) {
146+ not this instanceof ClosureModule and
66147 getAChildStmt ( ) instanceof GoogProvide or
67- getAChildStmt ( ) instanceof GoogRequire
148+ getAChildStmt ( ) . ( ExprStmt ) . getExpr ( ) instanceof GoogRequire
68149 }
69150
70151 /** Gets the identifier of a namespace required by this module. */
71- string getARequiredNamespace ( ) { result = getAChildStmt ( ) .( GoogRequire ) .getNamespaceId ( ) }
152+ string getARequiredNamespace ( ) { result = getAChildStmt ( ) .( ExprStmt ) . getExpr ( ) . ( GoogRequire ) .getNamespaceId ( ) }
72153
73154 /** Gets the identifer of a namespace provided by this module. */
74155 string getAProvidedNamespace ( ) { result = getAChildStmt ( ) .( GoogProvide ) .getNamespaceId ( ) }
75156}
157+
158+ /**
159+ * Holds if `name` is a closure namespace, including proper namespace prefixes.
160+ */
161+ pragma [ noinline]
162+ predicate isClosureLibraryNamespacePath ( string name ) {
163+ exists ( string namespace | namespace = any ( GoogNamespaceRef provide ) .getNamespaceId ( ) |
164+ name = namespace .substring ( 0 , namespace .indexOf ( "." ) )
165+ or
166+ name = namespace
167+ )
168+ }
169+
170+ /**
171+ * Gets the closure namespace path addressed by the given dataflow node, if any.
172+ */
173+ string getClosureLibraryAccessPath ( DataFlow:: SourceNode node ) {
174+ isClosureLibraryNamespacePath ( result ) and
175+ node = DataFlow:: globalVarRef ( result )
176+ or
177+ isClosureLibraryNamespacePath ( result ) and
178+ exists ( DataFlow:: PropRead read | node = read |
179+ result = getClosureLibraryAccessPath ( read .getBase ( ) .getALocalSource ( ) ) + "." + read .getPropertyName ( )
180+ )
181+ or
182+ // Associate an access path with the immediate RHS of a store on a closure namespace.
183+ // This is to support patterns like:
184+ // foo.bar = { baz() {} }
185+ exists ( DataFlow:: PropWrite write |
186+ node = write .getRhs ( ) and
187+ result = getWrittenClosureLibraryAccessPath ( write )
188+ )
189+ or
190+ result = node .asExpr ( ) .( GoogRequire ) .getNamespaceId ( )
191+ }
192+
193+ /**
194+ * Gets the closure namespace path written to by the given property write, if any.
195+ */
196+ string getWrittenClosureLibraryAccessPath ( DataFlow:: PropWrite node ) {
197+ result = getClosureLibraryAccessPath ( node .getBase ( ) .getALocalSource ( ) ) + "." + node .getPropertyName ( )
198+ }
0 commit comments