On May 5, 2009, at 6:53 PM, Allen Wirfs-Brock wrote:

Still does not connote "non-existent".

If all these handlers are only invoked in missing property situations then we can probably get away with an implicitly non- existent connotation.

The ones that want to be called for existent properties in order to maintain an arraylike 'length' seem to be set and delete. For delete, only for identifiers that are indexes (ToUint32(id) === id), which could be a helpful fact, or a temptation to make distinctions on identifiers that we should resist.


        delete  delete a non-existent property

Sure (hard to care about this case), but the ability to use catchalls
to emulate arrays may be an important use-case for virtualizing
systems. If delete can't be handled for existent properties then
length can't be updated.


So, maybe delete falls into a different category of handlers (perhaps along with invoke and construct on the object itself, defineProperty, etc...) that aren't about non-existent properties but are hooks on important object level meta operations (at MOPish step but it keeps the catch all story cleaner).

'delete obj.prop' or 'delete obj[id]' needs to be caught and handled by some catchall associated with obj, not the existing property value of obj.prop or obj[id]. Again the arraylike case leads the way.

Invoking an object via obj(), constructing via new obj -- these cases indeed lack a .prop or [id] to catch. They would want a lower-level MOP.

But obj.prop() is worth a catchall, this is the popular __noSuchMethod__ case, so that one does not have to create a method for each value of 'prop' and retain it in obj under that name. The call can be forwarded without any proxy or cloned method.


        getValue        called on any implicit or explicit non-existent
property
access to that is not one of the above

What is an implicit property access other than the above? I added
"has" because property lookup does not getValue, and must not, but
unless you ignore catchalls on the global object and objects named by
with statement heads, you need "has" too.

I hadn't thought through "implicit" but I was at least thinking about accesses from built-ins. I would also expect that getValue (and the others) would need to work on implicit accesses to the global object or a with object. With lookup might be trickly but I think it could be probably be make to work at least in the specification. It might be necessary to make DefaultAction contextual so that it does something different when trying to resolve a with binding

You can't getValue speculatively, the hook might have effects.

The ES4 thinking was not far off, if you squint past namespaces, from what you're proposing, provided you add 'has' or 'hasProperty' to the suite:

http://bugs.ecmascript.org/ticket/214

These catchalls (invoke as __noSuchMethod__ is absent from this ticket, but it fits the same mold as the others) run only when there's no "own" property being acted upon.

Lars's comment 5 (http://bugs.ecmascript.org/ticket/214#comment:5) is worth a read. The logic leading up to "Ergo no property managed by the catchalls is ReadOnly" is still compelling given the practical obstacles to defining ReadOnly properties on prototypes. Sure, with ES5 you can make such non-configurable (else why bother?) constants on a prototype, or make an object containing such constants be the prototype of another object, but then [[CanPut]] comes into play. Best to leave prototype-chasing to the catchall implementors, we agreed -- and so did this old ES4 ticket.

So apart from arraylikes (see below), I agree we should call catchalls only for missing "own" property accesses.


At most one of the above is called on any property reference, and
only if the property does not exist.

Besides making it impossible to virtualize arraylikes, this will make
virtual DOMs hard to implement efficiently.
Is there anything other than the delete hander that is needed to emulate arrays? What is the DOM problem?

An arraylike would want set to monitor obj[31] = "foo" where obj.length was < 32 (implying obj[31] did not exist before the assignment), and similarly delete obj[31] should retract length when obj[31] *does* exist.

And (here's a case where set would be wanted on an existing property) obj.length = 30 should retract further. But why couldn't 'length' have a custom setter, instead of trying to run a 'set' catchall on every set just to intercept 'length' updates and extend or truncate?

DOM nodelists are "live" -- they are cursors into the shared DOM tree and mutations can affect their contents. But they're also arraylike in that they have .length and can be indexed. These could be implemented (at some performance cost) by never creating "own" properties, instead always searching the DOM, caching to optimize for no mutation as needed.

So, no need for catchalls on existing direct properties of the target object, except for 'delete'. Is 'delete' the oddball catchall which should be called for existing as well as missing direct properties?


So perhaps invoke could be for non-callable (including non-existent,
i.e., imputed-undefined-value) properties.

I have some similar concerns about assignments to readonly properties and perhaps other error conditions. Maybe they are error handlers.

If only accessing a non-existent property were an error (in non-strict mode)!

E4X would want something beyond the no-direct-property condition to call the hook. The "out" for now would be the same drill as for DOM nodelists: never populate direct properties, always compute results via catchalls. I like it, it slows E4X down as a deprecation device, a warning against using it :-P. (Not really kidding...)


On a complete different plane, here is a wacky idea that integrates this work into Object.defineProperty: Object.defineProperty(obj, undefined, {invoke: function(id, args) {...}, ...});

Keeps the API surface smaller and emphasizes that this is really an way to define the handling of otherwise undefined properties.

It's worth considering, but a little obfuscated (or just obscure -- undefined as id might be taken to convert to "undefined"). I don't mind a separate defineCatchAll addition to Object, given the successful additions in ES5. What do you say?

The other benefit of defineCatchAll is its atomic (re-)configuration of catchalls. This is an implementation win where catchalls involve switching a poor-man's vptr in the instance, or some similar identity- preserving aggregation operation.

/be

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

Reply via email to