JS: Make Closure concepts based on AST instead

This commit is contained in:
Asger F
2025-04-11 13:00:47 +02:00
parent 50e4ac8298
commit b5a4fc0041
2 changed files with 67 additions and 40 deletions

View File

@@ -5,17 +5,49 @@
import javascript
module Closure {
/** A call to `goog.require` */
class RequireCallExpr extends CallExpr {
RequireCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.require" }
/** Gets the imported namespace name. */
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
}
/** A call to `goog.provide` */
class ProvideCallExpr extends CallExpr {
ProvideCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.provide" }
/** Gets the imported namespace name. */
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
}
/** A call to `goog.module` or `goog.declareModuleId`. */
private class ModuleDeclarationCall extends CallExpr {
private string kind;
ModuleDeclarationCall() {
this.getCallee().(PropAccess).getQualifiedName() = kind and
kind = ["goog.module", "goog.declareModuleId"]
}
/** Gets the declared namespace. */
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
/** Gets the string `goog.module` or `goog.declareModuleId` depending on which method is being called. */
string getModuleKind() { result = kind }
}
/**
* A reference to a Closure namespace.
*/
class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {
deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {
/**
* Gets the namespace being referenced.
*/
string getClosureNamespace() { result = super.getClosureNamespace() }
}
module ClosureNamespaceRef {
deprecated module ClosureNamespaceRef {
/**
* A reference to a Closure namespace.
*
@@ -32,10 +64,10 @@ module Closure {
/**
* A data flow node that returns the value of a closure namespace.
*/
class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range {
}
deprecated class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range
{ }
module ClosureNamespaceAccess {
deprecated module ClosureNamespaceAccess {
/**
* A data flow node that returns the value of a closure namespace.
*
@@ -47,7 +79,7 @@ module Closure {
/**
* A call to a method on the `goog.` namespace, as a closure reference.
*/
abstract private class DefaultNamespaceRef extends DataFlow::MethodCallNode,
abstract deprecated private class DefaultNamespaceRef extends DataFlow::MethodCallNode,
ClosureNamespaceRef::Range
{
DefaultNamespaceRef() { this = DataFlow::globalVarRef("goog").getAMethodCall() }
@@ -59,14 +91,14 @@ module Closure {
* Holds if `node` is the data flow node corresponding to the expression in
* a top-level expression statement.
*/
private predicate isTopLevelExpr(DataFlow::Node node) {
deprecated private predicate isTopLevelExpr(DataFlow::Node node) {
any(TopLevel tl).getAChildStmt().(ExprStmt).getExpr().flow() = node
}
/**
* A top-level call to `goog.provide`.
*/
private class DefaultClosureProvideCall extends DefaultNamespaceRef {
deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef {
DefaultClosureProvideCall() {
this.getMethodName() = "provide" and
isTopLevelExpr(this)
@@ -76,13 +108,14 @@ module Closure {
/**
* A top-level call to `goog.provide`.
*/
class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall
deprecated class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall
{ }
/**
* A call to `goog.require`.
*/
private class DefaultClosureRequireCall extends DefaultNamespaceRef, ClosureNamespaceAccess::Range
deprecated private class DefaultClosureRequireCall extends DefaultNamespaceRef,
ClosureNamespaceAccess::Range
{
DefaultClosureRequireCall() { this.getMethodName() = "require" }
}
@@ -90,13 +123,13 @@ module Closure {
/**
* A call to `goog.require`.
*/
class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall
deprecated class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall
{ }
/**
* A top-level call to `goog.module` or `goog.declareModuleId`.
*/
private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef {
deprecated private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef {
DefaultClosureModuleDeclaration() {
(this.getMethodName() = "module" or this.getMethodName() = "declareModuleId") and
isTopLevelExpr(this)
@@ -106,41 +139,29 @@ module Closure {
/**
* A top-level call to `goog.module` or `goog.declareModuleId`.
*/
class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration
deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration
{ }
private GlobalVariable googVariable() { variables(result, "goog", any(GlobalScope sc)) }
pragma[nomagic]
private MethodCallExpr googModuleDeclExpr() {
result.getReceiver() = googVariable().getAnAccess() and
result.getMethodName() = ["module", "declareModuleId"]
}
pragma[nomagic]
private MethodCallExpr googModuleDeclExprInContainer(StmtContainer container) {
result = googModuleDeclExpr() and
container = result.getContainer()
}
pragma[noinline]
private ClosureRequireCall getARequireInTopLevel(ClosureModule m) { result.getTopLevel() = m }
private RequireCallExpr getARequireInTopLevel(ClosureModule m) { result.getTopLevel() = m }
/**
* A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`.
*/
class ClosureModule extends Module {
ClosureModule() { exists(googModuleDeclExprInContainer(this)) }
private ModuleDeclarationCall decl;
ClosureModule() { decl.getTopLevel() = this }
/**
* Gets the call to `goog.module` or `goog.declareModuleId` in this module.
*/
ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this }
deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this }
/**
* Gets the namespace of this module.
*/
string getClosureNamespace() { result = this.getModuleDeclaration().getClosureNamespace() }
string getClosureNamespace() { result = decl.getClosureNamespace() }
override Module getAnImportedModule() {
result.(ClosureModule).getClosureNamespace() =
@@ -156,7 +177,7 @@ module Closure {
* Has no result for ES6 modules using `goog.declareModuleId`.
*/
Variable getExportsVariable() {
this.getModuleDeclaration().getMethodName() = "module" and
decl.getModuleKind() = "goog.module" and
result = this.getScope().getVariable("exports")
}
@@ -185,15 +206,15 @@ module Closure {
ClosureScript() {
not this instanceof ClosureModule and
(
any(ClosureProvideCall provide).getTopLevel() = this
any(ProvideCallExpr provide).getTopLevel() = this
or
any(ClosureRequireCall require).getTopLevel() = this
any(RequireCallExpr require).getTopLevel() = this
)
}
/** Gets the identifier of a namespace required by this module. */
string getARequiredNamespace() {
exists(ClosureRequireCall require |
exists(RequireCallExpr require |
require.getTopLevel() = this and
result = require.getClosureNamespace()
)
@@ -201,7 +222,7 @@ module Closure {
/** Gets the identifer of a namespace provided by this module. */
string getAProvidedNamespace() {
exists(ClosureProvideCall require |
exists(ProvideCallExpr require |
require.getTopLevel() = this and
result = require.getClosureNamespace()
)
@@ -213,7 +234,13 @@ module Closure {
*/
pragma[noinline]
predicate isClosureNamespace(string name) {
exists(string namespace | namespace = any(ClosureNamespaceRef ref).getClosureNamespace() |
exists(string namespace |
namespace =
[
any(RequireCallExpr ref).getClosureNamespace(),
any(ModuleDeclarationCall c).getClosureNamespace()
]
|
name = namespace.substring(0, namespace.indexOf("."))
or
name = namespace

View File

@@ -34,7 +34,7 @@ module AccessPath {
not this.accessesGlobal(_) and
not this instanceof DataFlow::PropRead and
not this instanceof PropertyProjection and
not this instanceof Closure::ClosureNamespaceAccess and
not this.asExpr() instanceof Closure::RequireCallExpr and
not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and
not FlowSteps::identityFunctionStep(_, this)
}
@@ -139,8 +139,8 @@ module AccessPath {
result = join(fromReference(prop.getBase(), root), "[number]")
)
or
exists(Closure::ClosureNamespaceAccess acc | node = acc |
result = acc.getClosureNamespace() and
exists(Closure::RequireCallExpr req | node = req.flow() |
result = req.getClosureNamespace() and
root.isGlobal()
)
or