Skip to content

Commit 4b4daa6

Browse files
committed
JS: handle accessors separately
1 parent e18b635 commit 4b4daa6

File tree

8 files changed

+123
-21
lines changed

8 files changed

+123
-21
lines changed

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,44 @@ DataFlow::SourceNode moduleMember(string path, string m) {
466466
)
467467
}
468468

469+
/**
470+
* The string `method`, `getter`, or `setter`, representing the kind of a function member
471+
* in a class.
472+
*
473+
* Can can be used with `ClassNode.getInstanceMember` to obtain members of a specific kind.
474+
*/
475+
class MemberKind extends string {
476+
MemberKind() { this = "method" or this = "getter" or this = "setter" }
477+
}
478+
479+
module MemberKind {
480+
/** The kind of a method, such as `m() {}` */
481+
MemberKind method() { result = "method" }
482+
483+
/** The kind of a getter accessor, such as `get f() {}`. */
484+
MemberKind getter() { result = "getter" }
485+
486+
/** The kind of a setter accessor, such as `set f() {}`. */
487+
MemberKind setter() { result = "setter" }
488+
489+
/** The `getter` and `setter` kinds. */
490+
MemberKind accessor() { result = getter() or result = setter() }
491+
492+
/**
493+
* Gets the member kind of a given syntactic member declaration in a class.
494+
*/
495+
MemberKind of(MemberDeclaration decl) {
496+
decl instanceof GetterMethodDeclaration and result = getter()
497+
or
498+
decl instanceof SetterMethodDeclaration and result = setter()
499+
or
500+
decl instanceof MethodDeclaration and
501+
not decl instanceof AccessorMethodDeclaration and
502+
not decl instanceof ConstructorDeclaration and
503+
result = method()
504+
}
505+
}
506+
469507
/**
470508
* A data flow node corresponding to a class definition or a function definition
471509
* acting as a class.
@@ -516,7 +554,7 @@ class ClassNode extends DataFlow::SourceNode {
516554
*
517555
* Does not include methods from superclasses.
518556
*/
519-
FunctionNode getInstanceMethod(string name) { result = impl.getInstanceMethod(name) }
557+
FunctionNode getInstanceMethod(string name) { result = impl.getInstanceMember(name, MemberKind::method()) }
520558

521559
/**
522560
* Gets an instance method declared in this class.
@@ -525,7 +563,28 @@ class ClassNode extends DataFlow::SourceNode {
525563
*
526564
* Does not include methods from superclasses.
527565
*/
528-
FunctionNode getAnInstanceMethod() { result = impl.getAnInstanceMethod() }
566+
FunctionNode getAnInstanceMethod() { result = impl.getAnInstanceMember(MemberKind::method()) }
567+
568+
/**
569+
* Gets the instance method, getter, or setter with the given name and kind.
570+
*
571+
* Does not include members from superclasses.
572+
*/
573+
FunctionNode getInstanceMember(string name, MemberKind kind) { result = impl.getInstanceMember(name, kind) }
574+
575+
/**
576+
* Gets an instance method, getter, or setter with the given kind.
577+
*
578+
* Does not include members from superclasses.
579+
*/
580+
FunctionNode getAnInstanceMember(MemberKind kind) { result = impl.getAnInstanceMember(kind) }
581+
582+
/**
583+
* Gets an instance method, getter, or setter declared in this class.
584+
*
585+
* Does not include members from superclasses.
586+
*/
587+
FunctionNode getAnInstanceMember() { result = impl.getAnInstanceMember(_) }
529588

530589
/**
531590
* Gets the static method declared in this class with the given name.
@@ -576,16 +635,14 @@ module ClassNode {
576635
abstract FunctionNode getConstructor();
577636

578637
/**
579-
* Gets an instance method with the given name, if any.
638+
* Gets the instance member with the given name and kind.
580639
*/
581-
abstract FunctionNode getInstanceMethod(string name);
640+
abstract FunctionNode getInstanceMember(string name, MemberKind kind);
582641

583642
/**
584-
* Gets an instance method of this class.
585-
*
586-
* The constructor is not considered an instance method.
643+
* Gets an instance member with the given kind.
587644
*/
588-
abstract FunctionNode getAnInstanceMethod();
645+
abstract FunctionNode getAnInstanceMember(MemberKind kind);
589646

590647
/**
591648
* Gets the static method of this class with the given name.
@@ -618,20 +675,20 @@ module ClassNode {
618675

619676
override FunctionNode getConstructor() { result = astNode.getConstructor().getBody().flow() }
620677

621-
override FunctionNode getInstanceMethod(string name) {
678+
override FunctionNode getInstanceMember(string name, MemberKind kind) {
622679
exists(MethodDeclaration method |
623680
method = astNode.getMethod(name) and
624681
not method.isStatic() and
625-
not method instanceof ConstructorDeclaration and
682+
kind = MemberKind::of(method) and
626683
result = method.getBody().flow()
627684
)
628685
}
629686

630-
override FunctionNode getAnInstanceMethod() {
687+
override FunctionNode getAnInstanceMember(MemberKind kind) {
631688
exists(MethodDeclaration method |
632689
method = astNode.getAMethod() and
633690
not method.isStatic() and
634-
not method instanceof ConstructorDeclaration and
691+
kind = MemberKind::of(method) and
635692
result = method.getBody().flow()
636693
)
637694
}
@@ -671,12 +728,36 @@ module ClassNode {
671728

672729
override FunctionNode getConstructor() { result = this }
673730

674-
override FunctionNode getInstanceMethod(string name) {
731+
private PropertyAccessor getAnAccessor(MemberKind kind) {
732+
result.getObjectExpr() = getAPrototypeReference().asExpr() and
733+
(
734+
kind = MemberKind::getter() and
735+
result instanceof PropertyGetter
736+
or
737+
kind = MemberKind::setter() and
738+
result instanceof PropertySetter
739+
)
740+
}
741+
742+
override FunctionNode getInstanceMember(string name, MemberKind kind) {
743+
kind = MemberKind::method() and
675744
result = getAPrototypeReference().getAPropertySource(name)
745+
or
746+
exists (PropertyAccessor accessor |
747+
accessor = getAnAccessor(kind) and
748+
accessor.getName() = name and
749+
result = accessor.getInit().flow()
750+
)
676751
}
677752

678-
override FunctionNode getAnInstanceMethod() {
753+
override FunctionNode getAnInstanceMember(MemberKind kind) {
754+
kind = MemberKind::method() and
679755
result = getAPrototypeReference().getAPropertyWrite().getRhs().getALocalSource()
756+
or
757+
exists (PropertyAccessor accessor |
758+
accessor = getAnAccessor(kind) and
759+
result = accessor.getInit().flow()
760+
)
680761
}
681762

682763
override FunctionNode getStaticMethod(string name) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| tst.js:4:17:4:21 | () {} | A.instanceMethod | method |
2+
| tst.js:7:6:7:10 | () {} | A.bar | method |
3+
| tst.js:9:10:9:14 | () {} | A.baz | getter |
4+
| tst.js:17:19:17:31 | function() {} | B.foo | method |
5+
| tst.js:21:19:21:31 | function() {} | C.bar | method |
6+
| tst.js:25:13:25:17 | () {} | D.getter | getter |
7+
| tst.js:26:13:26:18 | (x) {} | D.setter | setter |
8+
| tst.js:27:4:27:8 | () {} | D.m | method |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from DataFlow::ClassNode cls, string name, string kind
4+
select cls.getInstanceMember(name, kind), cls.getName() + "." + name, kind
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| tst.js:4:17:4:21 | () {} | A.instanceMethod |
22
| tst.js:7:6:7:10 | () {} | A.bar |
3-
| tst.js:15:19:15:31 | function() {} | B.foo |
4-
| tst.js:19:19:19:31 | function() {} | C.bar |
3+
| tst.js:17:19:17:31 | function() {} | B.foo |
4+
| tst.js:21:19:21:31 | function() {} | C.bar |
5+
| tst.js:27:4:27:8 | () {} | D.m |
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import javascript
22

33
from DataFlow::ClassNode cls, string name
4-
select cls.getAnInstanceMethod(name), cls.getName() + "." + name
4+
select cls.getInstanceMethod(name), cls.getName() + "." + name
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
| tst.js:11:1:11:21 | class A ... ds A {} | A2 | tst.js:3:1:8:1 | class A ... () {}\\n} | A |
2-
| tst.js:13:1:13:15 | function B() {} | B | tst.js:3:1:8:1 | class A ... () {}\\n} | A |
3-
| tst.js:17:1:17:15 | function C() {} | C | tst.js:13:1:13:15 | function B() {} | B |
1+
| tst.js:13:1:13:21 | class A ... ds A {} | A2 | tst.js:3:1:10:1 | class A ... () {}\\n} | A |
2+
| tst.js:15:1:15:15 | function B() {} | B | tst.js:3:1:10:1 | class A ... () {}\\n} | A |
3+
| tst.js:19:1:19:15 | function C() {} | C | tst.js:15:1:15:15 | function B() {} | B |

javascript/ql/test/library-tests/ClassNode/tst.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ class A {
55
static staticMethod() {}
66

77
bar() {}
8+
9+
get baz() {}
810
}
911

1012

@@ -17,3 +19,10 @@ B.prototype.foo = function() {}
1719
function C() {}
1820
C.prototype = new B();
1921
C.prototype.bar = function() {}
22+
23+
function D() {}
24+
D.prototype = {
25+
get getter() {},
26+
set setter(x) {},
27+
m() {}
28+
}

javascript/ql/test/library-tests/Classes/ClassNodeInstanceMethod.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
| points.js:1:1:18:1 | class P ... ;\\n }\\n} | dist | points.js:7:11:9:3 | () {\\n ... y);\\n } |
21
| points.js:1:1:18:1 | class P ... ;\\n }\\n} | toString | points.js:11:11:13:3 | () {\\n ... )";\\n } |
32
| points.js:20:1:33:1 | class C ... ;\\n }\\n} | toString | points.js:26:11:28:3 | () {\\n ... ur;\\n } |
43
| tst.js:1:9:4:1 | class { ... */ }\\n} | constructor | tst.js:3:18:3:56 | () { /* ... r. */ } |

0 commit comments

Comments
 (0)