Suggestion: Proxy.[[GetOwnProperty]] caching non-configurable, non-writable data descriptors?

# Alex Vincent (10 months ago)

Proxies are a little slow right now, and maybe the rules of ECMAScript can safely allow for caching a non-configurable, non-writable data descriptor.

Specifically, in ValidateAndApplyPropertyDescriptor from ES8 (section 9.1.6.3, step 7a-iii), we've already shown current and desc both have the following traits:

  1. isDataDescriptor(current) and isDataDescriptor(desc) return true
  2. [[Configurable]] is false
  3. [[Writable]] is false
  4. SameValue(Desc.[[Value]], current.[[Value]]) is true
  5. The [[GetOwnProperty]] or [[DefineOwnProperty]] internal method is executing, indirectly calling ValidateAndApplyPropertyDescriptor for this case.
  6. The [[Get]] or [[Set]] internal methods is probably executing, calling [[GetOwnProperty]] or [[DefineOwnProperty]] for this case.

That is a very specific set of traits. As I understand it, it means the proxy target's property named P in Proxy.[[GetOwnProperty]] is permanently locked and can never change - and so neither can the proxy ever report a different value. Therefore, calling on the proxy handler for that property a second time is, at least in my view, either redundant or unnecessarily expensive.

I would suggest that implementers in section 9.5 (Proxy internals) optionally have a private Map where values that are non-configurable and non-writable are stored. Then [[GetOwnProperty]] could insert a couple of new steps into its algorithm. Between steps 4 and 5 of the current algorithm, I would add: "If the optional [[Map]] object exists and [[Map]].has(P) returns true, return [[Map]].get(P)." In step 17 of the current algorithm, I would add a substep: "If isDataDescriptor(resultDesc) and resultDesc.[[Writable]] is false and the optional [[Map]] object exists, call [[Map]].set(P, resultDesc). (If the [[Map]] internal slot exists but does not contain a Map, the [[Map]] may be filled with a Map at this time.)"

I would also suggest similar changes for [[DefineOwnProperty]]. (Section 9.5.6)

Alternatively, if storing a specific descriptor in the Map is unpalatable, the specification could request storing resultDesc.[[Value]] in step 17, and create a new non-configurable, non-writable descriptor before the current step 5.

Also, if a Map is too expensive, the spec could allow for an equivalent native map<string, JSValue> or whatever other appropriate data structure

fills the need.

Counter-point (1): if the intent of the proxy is to treat all property look-ups equally, then caching the non-writable, non-configurable descriptors goes against that intent - because now the trap is invoked only once for properties returning this type of descriptor. But there's a lot of steps, including invoking a custom proxy handler trap with an unknown number of steps, and a lot of complexity in that trap to ensure what it returns will pass all the assertions in the spec.

Counter-point (2): I don't know how common it is to have a property descriptor that meets all six criteria at the beginning of this post, in particular that both [[Configurable]] and [[Writable]] are false. So this does add another pointer, at least to null initially, for the [[Map]] slot. This is why I am proposing the [[Map]] slot as optional for implementers. Let the engines decide whether it's warranted or not as an optimization.

Thoughts?

Alex Vincent

Hayward, CA, USA

# Oriol _ (10 months ago)

Instead of storing the properties in a map, I think you could first call [[GetOwnProperty]] on the target, and if it's not configurable nor writable, return the same descriptor without calling the proxy trap.

However, I disagree with this kind of things. Proxy traps are not only useful because of the value they return, they can also have desirable side-effects.

# Caridy Patiño (10 months ago)

We had some preliminary discussions around proxies, and how to potentially fix some mistakes from the past, some details here: tvcutsem/es-lab#21, tvcutsem/es-lab#21

But this is very tricky, specially because Proxies were designed with the idea that the handler could also be a Proxy, that’s why we do all the gymnastics in the spec.

V8 has started some effort to improve proxies: v8project.blogspot.com/2017/10/optimizing-proxies.html, v8project.blogspot.com/2017/10/optimizing-proxies.html

My suggestion is to wait and see how far they can go, and we can attempt to change the spec if there is anything that prevent them from achieving a great deal of improvements based on the current spec.

./caridy

# Alex Vincent (10 months ago)

---------- Forwarded message ---------- From: Oriol _ <oriol-bugzilla at hotmail.com> Instead of storing the properties in a map, I think you could first call [[GetOwnProperty]] on the target, and if it's not configurable nor writable, return the same descriptor without calling the proxy trap.

I believe that's what my original suggestion was.

However, I disagree with this kind of things. Proxy traps are not only

useful because of the value they return, they can also have desirable side-effects.

--Oriol

You make an annoyingly good point here... :-) I made a similar point a few months ago to someone who wanted to create a "hybrid" object which hid the proxy as the prototype of a regular object, for speed. shudder

That said, I still believe the algorithm I pointed out goes through a lot of steps that can be unnecessary in the scenario I laid out... except that the creator of the proxy has no way of telling the implementing engine that. There's no "proxy configuration options" that I can modify to pass these optimizing hints to the engine. Maybe that's something we might need.

I know, this is quite a stretch, suggesting more API for proxies. (I still want Reflect.parse to happen, after all, and unlike kai zhu, I think operator overloading is a good thing...) I "blame" (actually, credit) Tom van Cutsem for that, as he made a suggestion about optional properties being an object in my own Membrane API recently. It was a good idea there, and for other algorithmic shortcuts in proxy traps, it might be a good idea here.

I also take Caridy's point about Google Chrome 62's optimizations that are coming. I didn't know that was actually happening until Caridy posted about it here.

Thanks for your response, though: it's nice to know someone cares about this.

Alex