Merge pull request #323 from esben-semmle/js/always-return-type-inference

JS: additional return type inference
This commit is contained in:
Max Schaefer
2018-11-07 08:25:28 +00:00
committed by GitHub
33 changed files with 442 additions and 24 deletions

View File

@@ -756,7 +756,7 @@ module DataFlow {
/**
* A data flow node representing an explicit (that is, non-reflective) function call.
*/
private class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
override CallExpr astNode;
}

View File

@@ -91,17 +91,19 @@ class AnalyzedNode extends DataFlow::Node {
/** Gets a type inferred for this node. */
pragma[nomagic] InferredType getAType() {
result = getALocalValue().getType()
result = getAValue().getType()
}
/** Gets a primitive type to which the value of this node can be coerced. */
/**
* Gets a primitive type to which the value of this node can be coerced.
*/
PrimitiveType getAPrimitiveType() {
result = getALocalValue().toPrimitive().getType()
result = getAValue().toPrimitive().getType()
}
/** Gets a Boolean value that this node evaluates to. */
boolean getABooleanValue() {
result = getALocalValue().getBooleanValue()
result = getAValue().getBooleanValue()
}
/** Gets the unique Boolean value that this node evaluates to, if any. */
@@ -166,7 +168,7 @@ class AnalyzedNode extends DataFlow::Node {
/** Holds if the flow analysis can infer at least one abstract value for this node. */
predicate hasFlow() {
exists(getALocalValue())
exists(getAValue())
}
}

View File

@@ -242,18 +242,25 @@ private class AnalyzedBinaryExpr extends DataFlow::AnalyzedValueNode {
}
}
/**
* Gets a primitive type to which the local value of `e` can be coerced.
*/
private PrimitiveType getALocalPrimitiveType(Expr e) {
result = e.analyze().getALocalValue().toPrimitive().getType()
}
/**
* Holds if `e` may hold a string value.
*/
private predicate maybeString(Expr e) {
e.analyze().getAPrimitiveType() = TTString()
getALocalPrimitiveType(e) = TTString()
}
/**
* Holds if `e` may hold a non-string value.
*/
private predicate maybeNonString(Expr e) {
e.analyze().getAPrimitiveType() != TTString()
getALocalPrimitiveType(e) != TTString()
}
/**

View File

@@ -14,8 +14,7 @@ import semmle.javascript.dataflow.Configuration
*/
predicate shouldTrackProperties(AbstractValue obj) {
obj instanceof AbstractExportsObject or
obj instanceof AbstractModuleObject or
obj instanceof AbstractObjectLiteral
obj instanceof AbstractModuleObject
}
/**

View File

@@ -143,6 +143,23 @@ abstract class CallWithAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
}
}
/**
* A call with inter-procedural type inference for the return value.
*
* Unlike `CallWithAnalyzedReturnFlow`, this only contributes to `getAValue()`, not `getALocalValue()`.
*/
abstract class CallWithNonLocalAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
/**
* Gets a called function.
*/
abstract AnalyzedFunction getACallee();
override AbstractValue getAValue() {
result = getACallee().getAReturnValue()
}
}
/**
* Flow analysis for the return value of IIFEs.
*/
@@ -210,4 +227,30 @@ private class LocalFunctionCallWithAnalyzedReturnFlow extends CallWithAnalyzedRe
result = f.analyze()
}
}
}
pragma[noinline]
private predicate hasExplicitDefiniteCallee(DataFlow::Impl::ExplicitCallNode call, DataFlow::AnalyzedNode callee) {
callee = call.getCalleeNode() and
not callee.getALocalValue().isIndefinite(_)
}
/**
* Enables inter-procedural type inference for the return value of a call to a type-inferred callee.
*/
private class TypeInferredCalleeWithAnalyzedReturnFlow extends CallWithNonLocalAnalyzedReturnFlow {
DataFlow::FunctionNode fun;
TypeInferredCalleeWithAnalyzedReturnFlow() {
exists (DataFlow::AnalyzedNode calleeNode |
hasExplicitDefiniteCallee(this, calleeNode) and
calleeNode.getALocalValue().(AbstractFunction).getFunction().flow() = fun
)
}
override AnalyzedFunction getACallee() {
result = fun
}
}