| "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", {})); |
| } |
| } |
| })(); |