Not own property getters

Alex Kodat alexkodat at gmail.com
Thu Sep 7 23:25:06 UTC 2017


If I run the following:

let foo = {a: 0, b: 1, c:1};
let bar = (
  new Proxy(
    foo,
    {get:
      (target, prop) => {
        if (prop in target) return target[prop];
        if (typeof prop == "symbol") return target[prop];
        if (prop == "inspect") return;
        if (prop == "toJSON") return;
        throw Error("Bad property: " +  prop);
      }
    }
  )
);
// bar = foo;

for (let i = 0; i < 1000000; i++) {
  bar.a = bar.b;
  bar.b = bar.c;
  bar.c = bar.c + i;
}

with the bar = foo assignment above commented out (so using a proxy) and not commented out (so direct object access) and V8 (Chrome), SpiderMonkey (Firefox), and Chakra (Edge) the difference between proxy and not was about a factor of 300-500 (perhaps unsurprisingly, all the JS engines were close-ish to each other).

While perhaps this can be improved, it’s a lot to ask of an optimizer to detect a proxy and figure out what it's doing to the degree that V8 (and I suspect other browsers) detect that bar.a is always at the same offset in bar and always an integer (as long as bar's shape doesn't change) and so optimize the call to a handful of machine language instructions. Let's say some superstar gets the difference down to a factor of 20 (I suspect that's unlikely). That's still a big price to pay for detecting errors or say automatically returning 0 for an undefined property.

----
Alex Kodat

From: Michał Wadas [mailto:michalwadas at gmail.com] 
Sent: Thursday, September 7, 2017 3:55 PM
To: Alex Kodat <alexkodat at gmail.com>
Cc: es-discuss at mozilla.org
Subject: Re: Not own property getters

Why can't you use proxy for this? 

On 7 Sep 2017 10:05 pm, "Alex Kodat" <mailto:alexkodat at gmail.com> wrote:
In a previous post I had proposed an Object.guard (originally badly named as
Object.lock) to make it possible to catch references to non-existent
properties in code that uses an object.

While that proposal didn't exactly generate a lot of enthusiasm, I now
realize that there would be a better way of accomplished what I had proposed
with a more general purpose facility. Specifically, if there were an
Object.notOwnPropertyValue(<object>, <getter>) function that indicated a
getter to be invoked on  a request for a not own property of the object. If
a request for a not own property of <object> is received <getter> is called
with this set to the receiver and the only argument set to the requested
property.

My proposed Object.guard function could just be accomplished by something
like

   Object.notOwnPropertyValue(myObject, (prop) => {throw Error("Bad
property: " + prop);})

For the purposes of catching bad property references, this is better than my
previous proposal because it eliminates the need for [[Get]] (P, Receiver)
to maintain a guard barrier state as it works it way down the prototype
chain. Instead, if someone wanted to not throw for properties on the
prototype chain, the notOwnPropertyValue getter could itself check for the
property in the receiver's prototype chain:

   Object.notOwnPropertyValue(
     myObject,
     (prop) => {
       if (prop in Object.getPrototypeOf(myObject) return
Object.getPrototypeOf(myObject)[prop];
       throw Error("Bad property: " + prop);
     }
   }

Obviously this could be done more tidily and efficiently but hopefully the
point is clear.

A simple alternative use (to a bad property reference catcher) of this
capability would be something like

   let counts = {};
   Object.notOwnPropertyValue(counts, () => 0);
   someStringArray.forEach((s) => counts[s]++;);

Note that I suspect for most purposes one would set
Object.notOwnPropertyValue on a prototype so there'd be no cost to getting a
property off any prototypes until a property has not been found yet when we
get to a prototype with Object.notOwnPropertyValue set. So for my purposes,
I'd create some Guard (my own class) object that would be just above Object
in the prototype chain of most of my classes that would throw on an invalid
property reference (my choice whether or not to allow Object.prototype
properties to be accessed via property accessors on the Guard class
objects).

I believe this would be relatively simple to implement and, beyond the
definition of Object.notOwnPropertyValue, would require only a minor change
to the [[Get]] (P, Receiver) description in the spec:

1. Assert: IsPropertyKey(P) is true.
2. Let desc be O.[[GetOwnProperty]](P).
3. ReturnIfAbrupt(desc).
4. If desc is undefined, then
   Insert 1 =>. Let getter be O.[[GetNotOwnProperty]].
   Insert 2 =>. If getter is not undefined return Call(getter, Receiver, P)

   a. Let parent be O.[[GetPrototypeOf]]().
   b. ReturnIfAbrupt(parent).
   c. If parent is null, return undefined.
   d. Return parent.[[Get]](P, Receiver).
5. If IsDataDescriptor(desc) is true, return desc.[[Value]].
6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be
desc.[[Get]].
7. If getter is undefined, return undefined.
8. Return Call(getter, Receiver).

An alternative would allow argument 1 of Object.notOwnPropertyValue to be a
non-function value in which case it would simply be returned rather than
called. In the weird case where you want the result of a not own property
reference to be a function, you'd have to define a getter that returns the
function. Some sort of formal memoization support would, of course, obviate
the need for a non-function not own property value. So maybe the function
described here should be called Object.notOwnPropertyGetter.

Implementation could be accomplished by having a slot in every object for a
notOwnPropertyValue getter or by a bit indicating that there is indeed such
a getter and then a hidden Symbol property that references the getter. The
latter means that the cost of this feature is one bit per simple object and
a bit more overhead if the bit is on. Plus, presumably
Object.getOwnPropertySymbols would have to be smart enough not to display
the hidden Symbol. But, this is an implementation detail.

Finally a big hand wave for now about how one implements the ability to get
rid of a not own property getter -- there are a lot of ways this can be
accomplished and it's not worth discussing if there's a truck-sized hole in
this proposal or if someone tells me that this is the Nth time this feature
has been proposed (I did find the Firefox __noSuchMethod__ Object.prototype
method). I suspect another response will be to tell me to use a proxy but
that would be an insanely heavyweight way of accomplishing such a simple
task.

If no one blows a hole in this proposal, I guess I'll figure out how to turn
it into a formal proposal so I can be the proud owner of a stage 0 proposal.

Thanks

----
Alex Kodat


_______________________________________________
es-discuss mailing list
mailto:es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss



More information about the es-discuss mailing list