Skip to content

Commit b078f9a

Browse files
committed
Proxy [[Construct]] trap confuses super call flag
When we call the proxy [[Construct]] method from a JIT helper, it passes an argument in the 0th slot. That makes `JavascriptOperators::GetAndAssertIsConstructorSuperCall` think that we have a `new.target` and are in a super call chain regardless of if we are or are not in a super call chain. This flag is then used to control constructing the 'this' binding. When we get it wrong, 'this' is bound to the proxy itself instead of a new object. Fixes: https://microsoft.visualstudio.com/OS/_workitems/edit/21193960/
1 parent 9bd8f5e commit b078f9a

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

lib/Runtime/Language/JavascriptOperators.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6323,7 +6323,8 @@ using namespace Js;
63236323
JavascriptProxy * proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
63246324
if (proxy)
63256325
{
6326-
Arguments args(CallInfo(CallFlags_New, 1), &instance);
6326+
Var dummy = nullptr;
6327+
Arguments args(CallInfo(CallFlags_New, 1), &dummy);
63276328
return requestContext->GetThreadContext()->ExecuteImplicitCall(proxy, Js::ImplicitCall_Accessor, [=]()->Js::Var
63286329
{
63296330
return proxy->ConstructorTrap(args, requestContext, 0);

test/Bugs/bug_OS21193960.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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: "OS21193960: Proxy [[Construct]] trap confuses super call flag",
11+
body: function () {
12+
function test(){
13+
let ctorCount = 0;
14+
function ctor() {
15+
if (new.target !== undefined) {
16+
ctorCount++;
17+
this.prop0 = 123;
18+
assert.isTrue(proxy === new.target, "proxy === new.target");
19+
} else {
20+
assert.fail('call ctor');
21+
}
22+
}
23+
24+
let ctor_prototype = ctor.prototype;
25+
let getCount = 0;
26+
let proxyHandler = {
27+
['get'](handler, prop, target) {
28+
getCount++;
29+
assert.isTrue(prop !== 'prop0', "prop !== 'prop0'");
30+
switch(prop) {
31+
case 'prototype':
32+
return ctor_prototype;
33+
}
34+
}
35+
};
36+
37+
var proxy = new Proxy(ctor, proxyHandler);
38+
let newProxy = new proxy;
39+
40+
assert.isTrue(proxy !== newProxy, 'proxy !== newProxy');
41+
assert.areEqual('object', typeof newProxy, '"object" === typeof newProxy');
42+
assert.areEqual(1, ctorCount, "1 === ctorCount");
43+
44+
assert.areEqual(123, newProxy.prop0, "123 === newProxy.prop0");
45+
assert.areEqual(0, getCount, "0 === getCount");
46+
};
47+
48+
test();
49+
test();
50+
}
51+
},
52+
{
53+
name: "Proxy target is a base class",
54+
body: function () {
55+
function test(){
56+
let ctorCount = 0;
57+
class ctor {
58+
constructor() {
59+
if (new.target !== undefined) {
60+
ctorCount++;
61+
this.prop0 = 123;
62+
assert.isTrue(proxy === new.target, "proxy === new.target");
63+
} else {
64+
assert.fail('call ctor');
65+
}
66+
}
67+
}
68+
69+
let ctor_prototype = ctor.prototype;
70+
let getCount = 0;
71+
let proxyHandler = {
72+
['get'](handler, prop, target) {
73+
getCount++;
74+
assert.isTrue(prop !== 'prop0', "prop !== 'prop0'");
75+
switch(prop) {
76+
case 'prototype':
77+
return ctor_prototype;
78+
}
79+
}
80+
};
81+
82+
var proxy = new Proxy(ctor, proxyHandler);
83+
let newProxy = new proxy;
84+
85+
assert.isTrue(proxy !== newProxy, 'proxy !== newProxy');
86+
assert.areEqual('object', typeof newProxy, '"object" === typeof newProxy');
87+
assert.areEqual(1, ctorCount, "1 === ctorCount");
88+
89+
assert.areEqual(123, newProxy.prop0, "123 === newProxy.prop0");
90+
assert.areEqual(1, getCount, "1 === getCount");
91+
};
92+
93+
test();
94+
test();
95+
}
96+
},
97+
{
98+
name: "Proxy target is a derived class",
99+
body: function () {
100+
function test(){
101+
let baseCount = 0;
102+
class base {
103+
constructor() {
104+
if (new.target !== undefined) {
105+
baseCount++;
106+
assert.isTrue(proxy === new.target, "proxy === new.target");
107+
} else {
108+
assert.fail('call base');
109+
}
110+
}
111+
};
112+
113+
let ctorCount = 0;
114+
class ctor extends base {
115+
constructor() {
116+
if (new.target !== undefined) {
117+
ctorCount++;
118+
super();
119+
this.prop0 = 123;
120+
assert.isTrue(proxy === new.target, "proxy === new.target");
121+
} else {
122+
assert.fail('call ctor');
123+
}
124+
}
125+
}
126+
127+
let ctor_prototype = ctor.prototype;
128+
let getCount = 0;
129+
let proxyHandler = {
130+
['get'](handler, prop, target) {
131+
getCount++;
132+
assert.isTrue(prop !== 'prop0', "prop !== 'prop0'");
133+
switch(prop) {
134+
case 'prototype':
135+
return ctor_prototype;
136+
}
137+
}
138+
};
139+
140+
var proxy = new Proxy(ctor, proxyHandler);
141+
let newProxy = new proxy;
142+
143+
assert.isTrue(proxy !== newProxy, 'proxy !== newProxy');
144+
assert.areEqual('object', typeof newProxy, '"object" === typeof newProxy');
145+
assert.areEqual(1, ctorCount, "1 === ctorCount");
146+
assert.areEqual(1, baseCount, "1 === baseCount");
147+
148+
assert.areEqual(123, newProxy.prop0, "123 === newProxy.prop0");
149+
assert.areEqual(1, getCount, "1 === getCount");
150+
};
151+
152+
test();
153+
test();
154+
}
155+
},
156+
];
157+
158+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/Bugs/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,4 +606,10 @@
606606
<compile-flags>-args summary -endargs</compile-flags>
607607
</default>
608608
</test>
609+
<test>
610+
<default>
611+
<files>bug_OS21193960.js</files>
612+
<compile-flags>-maxinterpretcount:1 -maxsimplejitruncount:2 -args summary -endargs</compile-flags>
613+
</default>
614+
</test>
609615
</regress-exe>

0 commit comments

Comments
 (0)