Skip to content

Commit 6a93a61

Browse files
committed
Fix generated set trap for proxy cases
Fixes jsdom/jsdom#3565.
1 parent 6a20d3c commit 6a93a61

File tree

3 files changed

+172
-564
lines changed

3 files changed

+172
-564
lines changed

lib/constructs/interface.js

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ class Interface {
582582
generateExport() {
583583
this.str += `
584584
exports.is = value => {
585-
return utils.isObject(value) && utils.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
585+
return utils.isObject(value) && Object.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
586586
};
587587
exports.isImpl = value => {
588588
return utils.isObject(value) && value instanceof Impl.implementation;
@@ -666,7 +666,7 @@ class Interface {
666666
conditions.push(supportsPropertyName(O, P));
667667
}
668668
if (overrideBuiltins) {
669-
conditions.push(`!utils.hasOwn(${O}, ${P})`);
669+
conditions.push(`!Object.hasOwn(${O}, ${P})`);
670670
} else {
671671
// TODO: create a named properties object.
672672
conditions.push(`!(${P} in ${O})`);
@@ -987,33 +987,7 @@ class Interface {
987987
if (ownDesc === undefined) {
988988
ownDesc = Reflect.getOwnPropertyDescriptor(target, P);
989989
}
990-
if (ownDesc === undefined) {
991-
const parent = Reflect.getPrototypeOf(target);
992-
if (parent !== null) {
993-
return Reflect.set(parent, P, V, receiver);
994-
}
995-
ownDesc = { writable: true, enumerable: true, configurable: true, value: undefined };
996-
}
997-
if (!ownDesc.writable) {
998-
return false;
999-
}
1000-
if (!utils.isObject(receiver)) {
1001-
return false;
1002-
}
1003-
const existingDesc = Reflect.getOwnPropertyDescriptor(receiver, P);
1004-
let valueDesc;
1005-
if (existingDesc !== undefined) {
1006-
if (existingDesc.get || existingDesc.set) {
1007-
return false;
1008-
}
1009-
if (!existingDesc.writable) {
1010-
return false;
1011-
}
1012-
valueDesc = { value: V };
1013-
} else {
1014-
valueDesc = { writable: true, enumerable: true, configurable: true, value: V };
1015-
}
1016-
return Reflect.defineProperty(receiver, P, valueDesc);
990+
return utils.ordinarySetWithOwnDescriptor(target, P, V, receiver, ownDesc);
1017991
}
1018992
`;
1019993

@@ -1065,7 +1039,7 @@ class Interface {
10651039
}
10661040
if (!overrideBuiltins) {
10671041
needFallback = true;
1068-
this.str += "if (!utils.hasOwn(target, P)) {";
1042+
this.str += "if (!Object.hasOwn(target, P)) {";
10691043
}
10701044

10711045
if (!hasNamedSetter) {

lib/output/utils.js

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ function isObject(value) {
55
return (typeof value === "object" && value !== null) || typeof value === "function";
66
}
77

8-
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
8+
const call = Function.call.bind(Function.call);
99

1010
// Like `Object.assign`, but using `[[GetOwnProperty]]` and `[[DefineOwnProperty]]`
1111
// instead of `[[Get]]` and `[[Set]]` and only allowing objects
@@ -34,7 +34,7 @@ const ctorRegistrySymbol = Symbol.for("[webidl2js] constructor registry");
3434
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
3535

3636
function initCtorRegistry(globalObject) {
37-
if (hasOwn(globalObject, ctorRegistrySymbol)) {
37+
if (Object.hasOwn(globalObject, ctorRegistrySymbol)) {
3838
return globalObject[ctorRegistrySymbol];
3939
}
4040

@@ -137,6 +137,56 @@ function iteratorResult([key, value], kind) {
137137
return { value: result, done: false };
138138
}
139139

140+
function ordinarySetWithOwnDescriptor(target, property, value, receiver, ownDesc) {
141+
if (ownDesc === undefined) {
142+
const parent = Reflect.getPrototypeOf(target);
143+
if (parent !== null) {
144+
return Reflect.set(parent, property, value, receiver);
145+
}
146+
ownDesc = { writable: true, enumerable: true, configurable: true, value: undefined };
147+
}
148+
if (isDataDescriptor(ownDesc)) {
149+
if (!ownDesc.writable) {
150+
return false;
151+
}
152+
if (!isObject(receiver)) {
153+
return false;
154+
}
155+
const existingDesc = Reflect.getOwnPropertyDescriptor(receiver, property);
156+
if (existingDesc !== undefined) {
157+
if (isAccessorDescriptor(existingDesc)) {
158+
return false;
159+
}
160+
if (existingDesc.writable === false) {
161+
return false;
162+
}
163+
const valueDesc = { value };
164+
return Reflect.defineOwnProperty(receiver, property, valueDesc);
165+
}
166+
167+
return Reflect.defineOwnProperty(
168+
receiver,
169+
property,
170+
{ value, writable: true, enumerable: true, configurable: true }
171+
);
172+
}
173+
174+
const setter = ownDesc.set;
175+
if (setter === undefined) {
176+
return false;
177+
}
178+
call(setter, receiver, value);
179+
return true;
180+
}
181+
182+
function isDataDescriptor(desc) {
183+
return Object.hasOwn(desc, "value") || Object.hasOwn(desc, "writable");
184+
}
185+
186+
function isAccessorDescriptor(desc) {
187+
return Object.hasOwn(desc, "get") || Object.hasOwn(desc, "set");
188+
}
189+
140190
const supportsPropertyIndex = Symbol("supports property index");
141191
const supportedPropertyIndices = Symbol("supported property indices");
142192
const supportsPropertyName = Symbol("supports property name");
@@ -156,7 +206,6 @@ const asyncIteratorEOI = Symbol("async iterator end of iteration");
156206

157207
module.exports = exports = {
158208
isObject,
159-
hasOwn,
160209
define,
161210
newObjectInRealm,
162211
wrapperSymbol,
@@ -186,5 +235,6 @@ module.exports = exports = {
186235
asyncIteratorReturn,
187236
asyncIteratorInit,
188237
asyncIteratorEOI,
189-
iteratorResult
238+
iteratorResult,
239+
ordinarySetWithOwnDescriptor
190240
};

0 commit comments

Comments
 (0)