Skip to content

Commit e4901ed

Browse files
committed
JS: Handle .extend called on any component
1 parent 2a79817 commit e4901ed

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

javascript/ql/lib/semmle/javascript/frameworks/Vue.qll

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,28 @@ module Vue {
3939
*/
4040
DataFlow::SourceNode vue() { result = vueLibrary().getAnImmediateUse() }
4141

42+
/** An API node referring to a component or `Vue`. */
43+
private API::Node component() {
44+
result = vueLibrary()
45+
or
46+
result = component().getMember("extend").getReturn()
47+
or
48+
result = vueLibrary().getMember("component").getReturn()
49+
or
50+
result = API::root().getASuccessor(any(VueFileImportEntryPoint e))
51+
}
52+
4253
/**
43-
* A call to `vue.extend`.
54+
* A call to `Vue.extend` or `extend` on a component.
4455
*/
45-
private class VueExtend extends API::CallNode {
46-
VueExtend() { this = vueLibrary().getMember("extend").getACall() }
56+
private class VueExtendCall extends API::CallNode {
57+
VueExtendCall() { this = component().getMember("extend").getACall() }
4758
}
4859

4960
private newtype TComponent =
5061
MkVueInstance(API::NewNode def) { def = vueLibrary().getAnInstantiation() } or
51-
MkExtendedVue(VueExtend extend) or
52-
MkExtendedInstance(VueExtend extend, API::NewNode sub) {
62+
MkExtendedVue(VueExtendCall extend) or
63+
MkExtendedInstance(VueExtendCall extend, API::NewNode sub) {
5364
sub = extend.getReturn().getAnInstantiation()
5465
} or
5566
MkComponentRegistration(API::CallNode def) { def = vueLibrary().getMember("component").getACall() } or
@@ -406,7 +417,7 @@ module Vue {
406417
* An extended Vue from `Vue.extend({...})`.
407418
*/
408419
class ExtendedVue extends Component, MkExtendedVue {
409-
VueExtend extend;
420+
VueExtendCall extend;
410421

411422
ExtendedVue() { this = MkExtendedVue(extend) }
412423

@@ -425,13 +436,19 @@ module Vue {
425436
override DataFlow::Node getOwnOptionsObject() { result = extend.getArgument(0) }
426437

427438
override Template::Element getTemplateElement() { none() }
439+
440+
override Component getABaseComponent() {
441+
result = Component.super.getABaseComponent()
442+
or
443+
result.getComponentRef().getMember("extend").getACall() = extend
444+
}
428445
}
429446

430447
/**
431448
* An instance of an extended Vue, for example `instance` of `var Ext = Vue.extend({...}); var instance = new Ext({...})`.
432449
*/
433450
class ExtendedInstance extends Component, MkExtendedInstance {
434-
VueExtend extend;
451+
VueExtendCall extend;
435452
API::NewNode sub;
436453

437454
ExtendedInstance() { this = MkExtendedInstance(extend, sub) }
@@ -457,6 +474,12 @@ module Vue {
457474
}
458475

459476
override Template::Element getTemplateElement() { none() }
477+
478+
override Component getABaseComponent() {
479+
result = Component.super.getABaseComponent()
480+
or
481+
result.getComponentRef().getAnInstantiation() = sub
482+
}
460483
}
461484

462485
/**

javascript/ql/test/library-tests/frameworks/Vue/tests.expected

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ component_getAPropertyValue
2929
| tst.js:85:1:87:2 | new Vue ... e; }\\n}) | created | tst.js:86:38:86:41 | true |
3030
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) | dataA | tst.js:89:22:89:23 | 42 |
3131
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | dataA | tst.js:100:18:100:19 | 42 |
32-
| tst.js:107:12:109:2 | new Vue ... 23 }\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
32+
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
3333
| tst.js:110:16:112:2 | new Vue ... base\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
34+
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
35+
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | fromSubclass2 | tst.js:115:18:115:20 | 100 |
3436
component_getOption
3537
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue | watch | compont-with-route.vue:10:12:16:5 | {\\n ... }\\n } |
3638
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | data | single-component-file-1.vue:6:11:6:45 | functio ... 42 } } |
@@ -62,9 +64,11 @@ component_getOption
6264
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) | data | tst.js:95:9:95:9 | f |
6365
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | data | tst.js:100:9:100:21 | { dataA: 42 } |
6466
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | methods | tst.js:101:12:103:3 | {\\n\\t\\t\\tm: ... ; }\\n\\t\\t} |
65-
| tst.js:107:12:109:2 | new Vue ... 23 }\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
67+
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
6668
| tst.js:110:16:112:2 | new Vue ... base\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
6769
| tst.js:110:16:112:2 | new Vue ... base\\n}) | extends | tst.js:111:11:111:14 | base |
70+
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
71+
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | data | tst.js:114:8:116:2 | {\\n\\t\\tfro ... 100\\n\\t} |
6872
component
6973
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue |
7074
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue |
@@ -87,8 +91,9 @@ component
8791
| tst.js:85:1:87:2 | new Vue ... e; }\\n}) |
8892
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) |
8993
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) |
90-
| tst.js:107:12:109:2 | new Vue ... 23 }\\n}) |
94+
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) |
9195
| tst.js:110:16:112:2 | new Vue ... base\\n}) |
96+
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) |
9297
instance_heapStep
9398
| Unit | compont-with-route.vue:31:14:31:34 | this.$r ... ery.foo | compont-with-route.vue:2:8:2:21 | v-html=dataA |
9499
| Unit | single-component-file-1.vue:6:40:6:41 | 42 | single-component-file-1.vue:2:8:2:21 | v-html=dataA |

javascript/ql/test/library-tests/frameworks/Vue/tst.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,14 @@ new Vue({
104104
});
105105
});
106106

107-
let base = new Vue({
107+
let base = Vue.extend({
108108
data: { fromBase: 123 }
109109
});
110110
let subclass = new Vue({
111111
extends: base
112112
});
113+
let subclass2 = base.extend({
114+
data: {
115+
fromSubclass2: 100
116+
}
117+
});

0 commit comments

Comments
 (0)