archives

« Bugzilla Issues Index

#2489 — 6.1.7.3, 9.1.2: Interleaved Proxy handler calls can violate [[SetPrototypeOf]] invariant


6.1.7.3 Invariants of the Essential Internal Methods, [[SetPrototypeOf]] (V):

> If target is non-extensible, [[SetPrototypeOf]] must return false,
> unless V is the SameValue as the target’s observed [[GetPrototypeOf]] value.

This invariant can be violated in 9.1.2 [[SetPrototypeOf]] when interleaved Proxy handlers calls take place.


Test case:
---
let observedProto;
let obj = {};
let proxy = new Proxy({}, {
getPrototypeOf(t) {
// Interleaved Proxy handler call
print(`(2) IsExtensible(obj) = ${Reflect.isExtensible(obj)}`);
// Make object non-extensible and retrieve [[Prototype]]
Reflect.preventExtensions(obj);
observedProto = Reflect.getPrototypeOf(obj);
print(`(3) IsExtensible(obj) = ${Reflect.isExtensible(obj)}`);
return Reflect.getPrototypeOf(t);
}
});

// Change [[Prototype]] of `obj`
print(`(1) IsExtensible(obj) = ${Reflect.isExtensible(obj)}`);
let setProtoResult = Reflect.setPrototypeOf(obj, proxy);

// Inspect current [[Prototype]] of `obj`
print(`(4) IsExtensible(obj) = ${Reflect.isExtensible(obj)}`);
let currentProto = Reflect.getPrototypeOf(obj);
print(`observedProto === currentProto? ${observedProto === currentProto}`);
print(`setProtoResult = ${setProtoResult}`);
---

Output:
---
(1) IsExtensible(obj) = true
(2) IsExtensible(obj) = true
(3) IsExtensible(obj) = false
(4) IsExtensible(obj) = false
observedProto === currentProto? false
setProtoResult = true
---

Expected:
`setProtoResult` should be false to indicate that the [[SetPrototypeOf]] operation was rejected. And `observedProto === currentProto` should yield `true`.


fixed in rev23 editor's draft

changed Ordinary Object [[SetPrototypeOf]] to recheck the preconditions after the final internal call to [[GetPrototypeOf]]

The [[SetPrototypeOf]] internal method for prozy objects already worked that way.


fixed in rev23 draft