Skip to content

Commit ff7bc7c

Browse files
committed
JS: Track types of classes in data flow
1 parent d3c4b5d commit ff7bc7c

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -558,15 +558,19 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
558558

559559
newtype TDataFlowType =
560560
TFunctionType(Function f) or
561+
TInstanceType(DataFlow::ClassNode cls) or
561562
TAnyType()
562563

563564
class DataFlowType extends TDataFlowType {
564565
string toDebugString() {
565-
this instanceof TFunctionType and
566566
result =
567567
"TFunctionType(" + this.asFunction().toString() + ") at line " +
568568
this.asFunction().getLocation().getStartLine()
569569
or
570+
result =
571+
"TInstanceType(" + this.asInstanceOfClass().toString() + ") at line " +
572+
this.asInstanceOfClass().getLocation().getStartLine()
573+
or
570574
this instanceof TAnyType and result = "TAnyType"
571575
}
572576

@@ -575,13 +579,25 @@ class DataFlowType extends TDataFlowType {
575579
}
576580

577581
Function asFunction() { this = TFunctionType(result) }
582+
583+
DataFlow::ClassNode asInstanceOfClass() { this = TInstanceType(result) }
584+
}
585+
586+
private predicate typeStrongerThan1(DataFlowType t1, DataFlowType t2) {
587+
// 't1' is a subclass of 't2'
588+
t1.asInstanceOfClass() = t2.asInstanceOfClass().getADirectSubClass()
578589
}
579590

580591
/**
581592
* Holds if `t1` is strictly stronger than `t2`.
582593
*/
583594
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
584-
t1 instanceof TFunctionType and t2 = TAnyType()
595+
typeStrongerThan1(t1, t2)
596+
or
597+
// Ensure all types are transitively stronger than 'any'
598+
not typeStrongerThan1(t1, _) and
599+
not t1 = TAnyType() and
600+
t2 = TAnyType()
585601
}
586602

587603
private DataFlowType getPreciseType(Node node) {
@@ -590,6 +606,9 @@ private DataFlowType getPreciseType(Node node) {
590606
result = TFunctionType(f)
591607
)
592608
or
609+
result.asInstanceOfClass() =
610+
unique(DataFlow::ClassNode cls | cls.getAnInstanceReference().getALocalUse() = node)
611+
or
593612
result = getPreciseType(node.getImmediatePredecessor())
594613
or
595614
result = getPreciseType(node.(PostUpdateNode).getPreUpdateNode())
@@ -683,18 +702,27 @@ predicate neverSkipInPathGraph(Node node) {
683702
string ppReprType(DataFlowType t) { none() }
684703

685704
pragma[inline]
686-
private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) {
705+
private predicate compatibleTypesWithAny(DataFlowType t1, DataFlowType t2) {
687706
t1 != TAnyType() and
688707
t2 = TAnyType()
689708
}
690709

710+
pragma[nomagic]
711+
private predicate compatibleTypes1(DataFlowType t1, DataFlowType t2) {
712+
t1.asInstanceOfClass().getADirectSubClass+() = t2.asInstanceOfClass()
713+
}
714+
691715
pragma[inline]
692716
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
693717
t1 = t2
694718
or
695-
compatibleTypesNonSymRefl(t1, t2)
719+
compatibleTypesWithAny(t1, t2)
720+
or
721+
compatibleTypesWithAny(t2, t1)
722+
or
723+
compatibleTypes1(t1, t2)
696724
or
697-
compatibleTypesNonSymRefl(t2, t1)
725+
compatibleTypes1(t2, t1)
698726
}
699727

700728
predicate forceHighPrecision(Content c) { none() }

javascript/ql/test/library-tests/TripleDot/subclass.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ class Subclass3 extends Base {
2929
this.baseMethod("safe");
3030
}
3131
subclassMethod(x) {
32-
sink(x); // $ SPURIOUS: hasValueFlow=sub1 hasValueFlow=sub2
32+
sink(x);
3333
}
3434
}

0 commit comments

Comments
 (0)