Skip to content

Commit 73a097d

Browse files
committed
[MERGE #5767 @wyrichte] Fixes #5637 - Execution of the get attribute on an object should be performed with the same context from which it was called from.
Merge pull request #5767 from wyrichte:build/wyrichte/super_this_1
2 parents e2a9c77 + 45a8bfc commit 73a097d

File tree

3 files changed

+183
-2
lines changed

3 files changed

+183
-2
lines changed

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7781,16 +7781,20 @@ void EmitCallTarget(
77817781
funcInfo->ReleaseLoc(pnodeBinTarget->AsParseNodeSuperReference()->pnodeThis);
77827782
funcInfo->ReleaseLoc(pnodeBinTarget->pnode1);
77837783

7784-
// Function calls on the 'super' object should maintain current 'this' pointer
7784+
// Function calls on the 'super' object should maintain current 'this' pointer.
77857785
*thisLocation = pnodeBinTarget->AsParseNodeSuperReference()->pnodeThis->location;
77867786
*releaseThisLocation = false;
7787+
7788+
uint cacheId = funcInfo->FindOrAddInlineCacheId(protoLocation, propertyId, false, false);
7789+
byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::LdSuperFld,
7790+
pnodeTarget->location, protoLocation, *thisLocation, cacheId, false);
77877791
}
77887792
else
77897793
{
77907794
*thisLocation = pnodeBinTarget->pnode1->location;
7795+
EmitMethodFld(pnodeBinTarget, protoLocation, propertyId, byteCodeGenerator, funcInfo);
77917796
}
77927797

7793-
EmitMethodFld(pnodeBinTarget, protoLocation, propertyId, byteCodeGenerator, funcInfo);
77947798
break;
77957799
}
77967800

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
var tests = [
9+
{
10+
name: "The execution of the get or set attribute on an object should be \
11+
performed with the same context from which they were called from.",
12+
body: function () {
13+
var a = {};
14+
var f = 'f';
15+
class C {
16+
// get and set should execute in the context of their caller, in
17+
// this example that would be "a"
18+
set f(x) { assert.isTrue(a === this); }
19+
get f() {
20+
assert.isTrue(a === this);
21+
22+
// Necessary as get must be called as a function and should
23+
// not throw an error
24+
return function () { }
25+
}
26+
foo() { assert.isTrue(a === this) }
27+
}
28+
class D extends C {
29+
// The current context is "a" and not D since apply() and call()
30+
// overrided D.
31+
g() { super.f(); }
32+
h() { super.f = 5; }
33+
i() { super.f; }
34+
j() { super['f'] }
35+
k() { super['f']() }
36+
l() { super['f'] = 5 }
37+
m() { super[f] }
38+
n() { super[f]() }
39+
o() { super[f] = 5 }
40+
p() { super.foo() }
41+
}
42+
new D().g.apply(a);
43+
new D().g.call(a);
44+
new D().h.apply(a);
45+
new D().h.call(a);
46+
new D().i.apply(a);
47+
new D().i.call(a);
48+
new D().j.apply(a);
49+
new D().j.call(a);
50+
new D().k.apply(a);
51+
new D().k.call(a);
52+
new D().l.apply(a);
53+
new D().l.call(a);
54+
55+
// These tests are not run as they are not currently fixed. Tests using a property
56+
// accessor with bracket notation (ex: super[prop]) will result in a LdElemI_A
57+
// opcode which cannot take another property to be used as the context during the
58+
// execution of the property access - this limitation may require the addition of
59+
// another opcode (possibly adding the opcode LdSuperElemI_A).
60+
61+
// new D().m.apply(a);
62+
// new D().m.call(a);
63+
// new D().n.apply(a);
64+
// new D().n.call(a);
65+
// new D().o.apply(a);
66+
// new D().o.call(a);
67+
68+
new D().p.apply(a);
69+
new D().p.call(a);
70+
}
71+
},
72+
{
73+
name: "The execution of the get or set attribute on an object should be \
74+
performed with the same context from which they were called from. This test uses \
75+
lambda functions as an alternative way of obtaining the class' context.",
76+
body: function () {
77+
var a = {};
78+
class C {
79+
set f(x) { assert.isTrue(a === (() => this)()); }
80+
get f() {
81+
assert.isTrue(a === (() => this)());
82+
return function () { }
83+
}
84+
foo() { assert.isTrue(a === this) }
85+
}
86+
class D extends C {
87+
g() { super.f(); }
88+
h() { super.f = 5; }
89+
i() { super.f; }
90+
j() { super['f'] }
91+
k() { super['f']() }
92+
l() { super['f'] = 5 }
93+
m() { super[f] }
94+
n() { super[f]() }
95+
o() { super[f] = 5 }
96+
p() { super.foo() }
97+
}
98+
new D().g.apply(a);
99+
new D().g.call(a);
100+
new D().h.apply(a);
101+
new D().h.call(a);
102+
new D().i.apply(a);
103+
new D().i.call(a);
104+
new D().j.apply(a);
105+
new D().j.call(a);
106+
new D().k.apply(a);
107+
new D().k.call(a);
108+
new D().l.apply(a);
109+
new D().l.call(a);
110+
// new D().m.apply(a);
111+
// new D().m.call(a);
112+
// new D().n.apply(a);
113+
// new D().n.call(a);
114+
// new D().o.apply(a);
115+
// new D().o.call(a);
116+
new D().p.apply(a);
117+
new D().p.call(a);
118+
}
119+
},
120+
{
121+
name: "The execution of the get or set attribute on an object should be \
122+
performed with the same context from which they were called from. This test uses \
123+
lambda functions as an alternative way of obtaining the class' context. This text \
124+
also uses eval statements.",
125+
body: function () {
126+
var a = {};
127+
class C {
128+
set f(x) { eval("assert.isTrue(a === (() => this)())"); }
129+
get f() {
130+
eval("assert.isTrue(a === (() => this)())");
131+
return function () { }
132+
}
133+
foo() { assert.isTrue(a === this) }
134+
}
135+
class D extends C {
136+
g() { eval("super.f();") }
137+
h() { eval("super.f = 5;") }
138+
i() { eval("super.f;") }
139+
j() { eval("super['f']") }
140+
k() { eval("super['f']()") }
141+
l() { eval("super['f'] = 5") }
142+
m() { eval("super[f]") }
143+
n() { eval("super[f]()") }
144+
o() { eval("super[f] = 5") }
145+
p() { eval("super.foo()") }
146+
}
147+
eval("new D().g.apply(a);");
148+
eval("new D().g.call(a);");
149+
eval("new D().h.apply(a);");
150+
eval("new D().h.call(a);");
151+
eval("new D().i.apply(a);");
152+
eval("new D().i.call(a);");
153+
eval("new D().j.apply(a);");
154+
eval("new D().j.call(a);");
155+
eval("new D().k.apply(a);");
156+
eval("new D().k.call(a);");
157+
eval("new D().l.apply(a);");
158+
eval("new D().l.call(a);");
159+
// eval("new D().m.apply(a);");
160+
// eval("new D().m.call(a);");
161+
// eval("new D().n.apply(a);");
162+
// eval("new D().n.call(a);");
163+
// eval("new D().o.apply(a);");
164+
// eval("new D().o.call(a);");
165+
eval("new D().p.apply(a);");
166+
eval("new D().p.call(a);");
167+
}
168+
},
169+
]
170+
171+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/Miscellaneous/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,10 @@
124124
<compile-flags>-args summary -endargs</compile-flags>
125125
</default>
126126
</test>
127+
<test>
128+
<default>
129+
<files>KeepContextInSuper.js</files>
130+
<compile-flags>-args summary -endargs</compile-flags>
131+
</default>
132+
</test>
127133
</regress-exe>

0 commit comments

Comments
 (0)