new [[HasProperty]] invariant for non-extensible objects
Maybe I didn't understand correctly, but I think this invariant is not enforced.
var obj = Object.preventExtensions({foo: "bar"});
// Object is non-extensible
Object.isExtensible(obj); // false
// foo is an observable own property
!!Object.getOwnPropertyDescriptor(obj, "foo"); // true
delete obj.foo;
// [[HasProperty]] returns false
"foo" in obj; // false
On Sun, Mar 5, 2017 at 2:52 PM, Oriol _ <oriol-bugzilla at hotmail.com> wrote:
delete obj.foo;
When you delete the property of an ordinary object, it stops being 'an
observable own property using [[GetOwnProperty]]'. So,
Object.getOwnPropertyDescriptor(obj, "foo")
returns undefined afterwards.
Yes, but the point is that if the property is removed, then [[HasProperty]] will return false, and thus the invariant can't be enforced, because an exotic object might behave like if the property was deleted under the hood.
Since you say the invariant is enforced for exotic proxy objects, you may prefer a counterexample for these:
var target = Object.preventExtensions({foo: "bar"});
var p = new Proxy(target, {
has: (target, prop) => !Reflect.deleteProperty(target, prop)
});
// Object is non-extensible
Object.isExtensible(p); // false
// foo is an observable own property
!!Object.getOwnPropertyDescriptor(p, "foo"); // true
// [[HasProperty]] returns false
"foo" in p; // false
By the time [[HasProperty]] (and "foo" in p
) finishes, the property
doesn't exist anymore, therefore this invariant is not enforced. The
invariant is: if the property exists and the object is non-extensible,
[[HasProperty]] must return true. So, there's no guarantee it returns true
if you've deleted the property.
If it's not obvious how the state of the target can be checked in order to
enforce the invariant, it can be checked as it is checked for all the other
invariants, namely in the internal method (in our case the [[HasProperty]]
internal method) of the proxy, after the hook (in our case the has
hook)
was called.
To be more explicit, the invariants allow this surprising behavior:
Object.preventExtensions(nonStandardExoticObject);
Object.isExtensible(nonStandardExoticObject); // false
'foo' in nonStandardExoticObject; // false
Object.getOwnPropertyDescriptor(nonStandardExoticObject, 'foo'); // {value:
2, enumerable: true, configurable: true, writable: true}
Le 6 mars 2017 à 11:10, Raul-Sebastian Mihăilă <raul.mihaila at gmail.com> a écrit :
To be more explicit, the invariants allow this surprising behavior:
Object.preventExtensions(nonStandardExoticObject); Object.isExtensible(nonStandardExoticObject); // false 'foo' in nonStandardExoticObject; // false Object.getOwnPropertyDescriptor(nonStandardExoticObject, 'foo'); // {value: 2, enumerable: true, configurable: true, writable: true}
That particular case should be covered by the following items in section Invariants of the Essential Internal Methods
Definitions:
A non-existent property is a property that does not exist as an own property on a non-extensible target.
And:
\[\[GetOwnProperty]] (P)
(last item): If the target is non-extensible and P is non-existent, then all future calls to [[GetOwnProperty]] (P) on the target must describe P as non-existent (i.e. [[GetOwnProperty]] (P) must return undefined).
However, it is true that the definition of “non-existent” property is at best vague, at worst incomplete. That is, instead of “does not exist as an own property”, it should say that one of the three following things happens, all of them are intuitively interpreted as implying that there does not exist an own property named P:
- [[GetOwnProperty]](P) returns undefined, or
- [[HasProperty]](P) returns false, or
- [[OwnPropertyKeys]]() returns a List that doesn't include P.
That said, there are other (often subtle) issues in that section. Few months ago, I have attempted a more rigorous reformulation; the motivation at that time was to check whether the integrity checks for Proxies were complete:
claudepache/es-invariants/blob/master/invariants.md
In that document the notion of “non-existent property” (implied: on a non-extensible object) is translated into the lock: @exists(P): false.
However, I don’t consider it as a pressing issue, because the spirit of the section Invariants of the Essential Internal Methods seems correct, even if the letter is provably incomplete or vague.
—Claude
Wouldn't it make sense to have the following [[HasProperty]] invariant for all the objects?
If the object is non-extensible and P is an observable own property using [[GetOwnProperty]], [[HasProperty]] must return true.
The same rule applies for [[OwnPropertyKeys]], where the resulted List must contain the own properties that are observable using [[GetOwnProperty]] if the object is non-extensible.
This rule is already applied for Proxy objects. Without this rule it's possible for non-standard non-extensible exotic objects to return false while returning a property descriptor from [[GetOwnProperty]] with the same argument.