Skip to content

Commit e675e67

Browse files
committed
SetProperty doesn't handle the case where the receiver object has a non-writable property
When checking for non-writable properties while trying to set a property, we only check the object. If the receiver has a non-writable property we should throw in strict mode. Fixes: #5948
1 parent 2f98542 commit e675e67

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

lib/Runtime/Language/JavascriptOperators.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,8 +2697,19 @@ using namespace Js;
26972697
*result = FALSE;
26982698
Var setterValueOrProxy = nullptr;
26992699
DescriptorFlags flags = None;
2700+
bool receiverNonWritable = false;
2701+
2702+
if (receiver != object && !isRoot)
2703+
{
2704+
Var receiverSetter = nullptr;
2705+
PropertyValueInfo receiverInfo;
2706+
DescriptorFlags receiverFlags = VarTo<RecyclableObject>(receiver)->GetSetter(propertyId, &receiverSetter, &receiverInfo, requestContext);
2707+
receiverNonWritable = ((receiverFlags & Data) == Data && (receiverFlags & Writable) == None);
2708+
}
2709+
27002710
if ((isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableRootProperty(object, propertyId, &setterValueOrProxy, &flags, info, requestContext)) ||
2701-
(!isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(object, propertyId, &setterValueOrProxy, &flags, info, requestContext)))
2711+
(!isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(object, propertyId, &setterValueOrProxy, &flags, info, requestContext) ||
2712+
receiverNonWritable))
27022713
{
27032714
if ((flags & Accessor) == Accessor)
27042715
{
@@ -2746,7 +2757,7 @@ using namespace Js;
27462757
}
27472758
else
27482759
{
2749-
Assert((flags & Data) == Data && (flags & Writable) == None);
2760+
Assert(((flags & Data) == Data && (flags & Writable) == None) || receiverNonWritable);
27502761
if (!allowUndecInConsoleScope)
27512762
{
27522763
if (flags & Const)

test/es6/ES6Super.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,18 @@ var tests = [
4848
"use strict";
4949
var obj0 = {
5050
m() {
51+
assert.areEqual(this, obj0);
52+
assert.areEqual(this.__proto__, base, "this.__proto__ === base");
5153
super.prop = "identifier";
52-
Object.freeze(obj0);
54+
assert.isTrue(this.hasOwnProperty('prop'), "this.hasOwnProperty('prop')");
55+
Object.freeze(this);
5356
super.prop = "super";
5457
}
5558
};
5659

5760
var obj1 = {
5861
m() {
62+
assert.areEqual(this, obj1);
5963
super['prop'] = "expression";
6064
Object.freeze(obj1);
6165
super['prop'] = "super";

test/es6/super_bugs.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,120 @@ var tests = [
7777
assert.areEqual("Base", new Derived().test());
7878
}
7979
},
80+
{
81+
name: "Class member trying to write to non-writable property of receiver object via super-dot-assignment throws",
82+
body: function ()
83+
{
84+
class Base {}
85+
class Derived extends Base {
86+
test() {
87+
assert.areEqual(this, obj, 'this === obj');
88+
89+
super.prop = 'something';
90+
}
91+
}
92+
93+
var obj = new Derived();
94+
Object.defineProperty(obj, 'prop', { writable:false, value:'nothing' });
95+
assert.throws(()=>obj.test(), TypeError, 'Class methods are strict mode code. Cannot write to non-writable properties.', 'Assignment to read-only properties is not allowed in strict mode');
96+
assert.areEqual('nothing', obj.prop);
97+
}
98+
},
99+
{
100+
name: "Class member trying to write to writable property of receiver object via super-dot-assignment is fine",
101+
body: function ()
102+
{
103+
class Base {}
104+
class Derived extends Base {
105+
test() {
106+
assert.areEqual(this, obj, 'this === obj');
107+
108+
super.prop = 'something';
109+
}
110+
}
111+
112+
var obj = new Derived();
113+
Object.defineProperty(obj, 'prop', { writable:true, value:'nothing' });
114+
obj.test();
115+
assert.isTrue(obj.hasOwnProperty('prop'));
116+
assert.areEqual('something', obj.prop);
117+
}
118+
},
119+
{
120+
name: "Function writing to non-writable property of receiver object via super-dot-assignment throws in strict mode",
121+
body: function ()
122+
{
123+
function ctor() { }
124+
ctor.prototype = {
125+
test() {
126+
'use strict';
127+
super.prop = 'something';
128+
}
129+
};
130+
131+
var obj = new ctor();
132+
Object.defineProperty(obj, 'prop', { writable:false, value:'nothing' });
133+
assert.throws(()=>obj.test(), TypeError, 'Strict mode code throws if we try to write to non-writable properties.', 'Assignment to read-only properties is not allowed in strict mode');
134+
assert.areEqual('nothing', obj.prop);
135+
}
136+
},
137+
{
138+
name: "Function writing to non-writable property of receiver object via super-dot-assignment silently fails in sloppy mode",
139+
body: function ()
140+
{
141+
function ctor() { }
142+
ctor.prototype = {
143+
test() {
144+
super.prop = 'something';
145+
}
146+
};
147+
148+
var obj = new ctor();
149+
Object.defineProperty(obj, 'prop', { writable:false, value:'nothing' });
150+
obj.test();
151+
assert.areEqual('nothing', obj.prop);
152+
}
153+
},
154+
{
155+
name: "Writing property to receiver via super-dot-assignment when receiver prototype-chain contains non-writable property doesn't throw",
156+
body: function ()
157+
{
158+
function ctor() { }
159+
ctor.prototype = {
160+
test() {
161+
'use strict';
162+
super.prop = 'something';
163+
}
164+
};
165+
166+
var obj = new ctor();
167+
Object.defineProperty(obj.__proto__, 'prop', { writable:false, value:'nothing' });
168+
obj.test();
169+
assert.isTrue(obj.hasOwnProperty('prop'));
170+
assert.areEqual('something', obj.prop);
171+
assert.areEqual('nothing', obj.__proto__.prop);
172+
}
173+
},
174+
{
175+
name: "Writing property to receiver via super-dot-assignment when receiver prototype-chain contains getter but no setter doesn't throw",
176+
body: function ()
177+
{
178+
function ctor() { }
179+
ctor.prototype = {
180+
test() {
181+
'use strict';
182+
super.prop = 'something';
183+
}
184+
};
185+
186+
var obj = new ctor();
187+
Object.defineProperty(obj.__proto__, 'prop', { get:()=>'nothing' });
188+
obj.test();
189+
assert.isTrue(obj.hasOwnProperty('prop'));
190+
assert.areEqual('something', obj.prop);
191+
assert.areEqual('nothing', obj.__proto__.prop);
192+
}
193+
},
80194
];
81195

82196
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

0 commit comments

Comments
 (0)