blob: b0d97049c6db06c8d3904285caa5b30ec500c77e [file] [log] [blame]
"use strict";
function assert(x) {
if (!x)
throw new Error("Bad assert!");
}
function shouldThrow(func, expectedError) {
var errorThrown = false;
try {
func();
} catch (error) {
errorThrown = true;
if (String(error) !== expectedError)
throw new Error(`Bad error: ${error}`);
}
if (!errorThrown)
throw new Error("Didn't throw!");
}
const customTestGetterSetter = $vm.createCustomTestGetterSetter();
Object.setPrototypeOf(customTestGetterSetter, {
set customValue(_v) { throw new Error("Should be unreachable!"); },
set customValueGlobalObject(_v) { throw new Error("Should be unreachable!"); },
set customAccessor(_v) { throw new Error("Should be unreachable!"); },
set customAccessorGlobalObject(_v) { throw new Error("Should be unreachable!"); },
});
const primitives = [true, 1, "", Symbol(), 1n];
for (let primitive of primitives) {
let prototype = Object.getPrototypeOf(primitive);
Object.setPrototypeOf(prototype, customTestGetterSetter);
}
function getObjects() {
return [
...primitives.map(Object),
Object.create(customTestGetterSetter),
Object.create(Object.create(customTestGetterSetter)),
Object.create(new Proxy(customTestGetterSetter, {})),
Object.create($vm.createProxy(customTestGetterSetter)),
];
}
function getBases() {
return [
...primitives,
...getObjects(),
];
}
// CustomAccessor: exception check
(() => {
for (let base of getBases()) {
for (let i = 0; i < 100; ++i) {
let value = {};
base.customAccessor = value;
assert(value.hasOwnProperty("result"));
}
assert(Reflect.set(Object(base), "customAccessor", {}));
}
})();
// CustomValue: exception check
(() => {
for (let base of getBases()) {
for (let i = 0; i < 100; ++i) {
let value = {};
base.customValue = value;
assert(value.hasOwnProperty("result"));
}
assert(Reflect.set(Object(base), "customValue", {}));
}
})();
// CustomAccessor: setter returns |false|
(() => {
for (let base of getBases()) {
for (let i = 0; i < 100; ++i)
base.customAccessor = 1;
// This is intentional:
assert(!base.hasOwnProperty("customAccessor"));
assert(Reflect.set(Object(base), "customAccessor", 1));
}
})();
// CustomValue: setter returns |false|
// FIXME: Once legacy RegExp features are implemented, there would be no use case for calling CustomValue setter if receiver is altered.
(() => {
for (let base of getBases()) {
for (let i = 0; i < 100; ++i)
base.customValue = 1;
assert(!base.hasOwnProperty("customValue"));
assert(Reflect.set(Object(base), "customValue", 1));
}
})();
// CustomAccessor: no setter
(() => {
for (let base of getBases()) {
for (let i = 0; i < 100; ++i)
shouldThrow(() => { base.customAccessorReadOnly = {}; }, "TypeError: Attempted to assign to readonly property.");
assert(!Reflect.set(Object(base), "customAccessorReadOnly", {}));
}
})();
// CustomValue: no setter
(() => {
for (let primitive of primitives) {
for (let i = 0; i < 100; ++i)
shouldThrow(() => { primitive.customValueNoSetter = {}; }, "TypeError: Attempted to assign to readonly property.");
}
for (let object of getObjects()) {
for (let i = 0; i < 100; ++i)
object.customValueNoSetter = {};
assert(object.hasOwnProperty("customValueNoSetter"));
assert(Reflect.set(object, "customValueNoSetter", {}));
}
for (let object of getObjects()) {
Object.preventExtensions(object);
for (let i = 0; i < 100; ++i) {
shouldThrow(() => { object.customValueNoSetter = {}; }, "TypeError: Attempting to define property on object that is not extensible.");
assert(!Reflect.set(object, "customValueNoSetter", {}));
}
}
})();