[Harmony proxies] Proxies, prototype chain and inheritance

David Bruant david.bruant at labri.fr
Mon Mar 21 16:08:48 PDT 2011


Hi,

Proxies can be helpful to emulate multiple inheritance
(http://journal.stuffwithstuff.com/2011/02/21/multiple-inheritance-in-javascript/).
Long story short, with the get and set traps, you can emulate this
multiple inheritance without having the required prototype chain.
However, since "Object.getPrototypeOf" and "instanceof" cannot be
trapped, these cannot be used to observe inheritance. There are
perfectly valid reasons for which they cannot be trapped, I won't
question this. My present suggestion is a mechanism to unable
inheritance observation while keeping a couple of language properties safe.

One ECMAScript invariant when dealing with objects is that the prototype
chain of an object is set once for all at object creation. Consequently,
for all function F, the ordered list of objects traversed while calling
F.[[HasInstance]] (ES5 15.3.5.3 step 4 a) will always be the same
throughout the entire object lifetime. My idea consists of being allowed
to provide this such a list at proxy creation time. With this list,
multiple inheritance can be observed if 'instanceof' calls looks at this
list. This is the idea, then comes "how to achieve it?". Everything said
from now on is just thoughts of how the idea could be achieved, neither
definitive thoughts nor exhaustive.

The idea can be achieved at different degrees of consistency with how
objects currently work. Here are the questions that I have asked myself
while thinking about the topic:
At initialization, should this list replace the second prototype
argument of Proxy.create or should it be provided as a complement just
for the purpose of instanceof?
What level of consistency with Object.getPrototypeOf?
Let's imagine for a minute that the list replaces the prototype object
as second argument:
---------------
// a, b, c are already existing object
function A(){} A.prototype = a;
function B(){} B.prototype = b;
function C(){} C.prototype = c;

var p = Proxy.create(handler, [a,b,c]);
// p instanceof A && p instanceof B && p instanceof C === true
--------------
Instead of returning "a", "Object.getPrototypeOf(p)" could return
something like: "Proxy.create(forwardingHandler(a), [b,c]);". Please
notice the recursivity. If the list is empty, Object.getPrototypeOf
returns null, obviously.
One little issue is that "Object.getPrototypeOf(p) !==
Object.getPrototypeOf(p)" since Proxy.create creates a new object (new
identity) each time, but a cache will solve the issue easily and the
same object can be returned each time. This cache trick will preserve
the invariant that an object has a unique prototype chain.
Two invariants (at least) are broken, though:
- p instanceof A iff there exists an n such that A.prototype ===
(Object.getPrototypeOf)^n(p)
-if two objects (o1, o2) inherit from the same two object (a, b) in the
prototype chain, then there is a unique partial prototype chain going
from a to b or b to a.
However, thanks to the forwarding proxy we keep a way for all "A"
instances (created through "new A()" or with my proxy mechanism) to
communicate and all still inherit (actually or virtually) from the
"same" "A.prototype" object.

The idea is a bit new, so there are certainly plenty of other questions
I haven't thought of. And once again, this was just one possible
beginning of how to achieve my idea above.

Anyway, through the "getPropertyDescriptor", "getPropertyNames", "has",
"get", "set" and "enumerate" traps (let's call them "prototype-climbing
traps" since there behavior on regular object is to climb the prototype
chain), there are already plenty of ways to break ECMAScript inheritance
invariants. My personal opinion is that "Object.getPrototypeOf" and
"instanceof" are currently overprotected with regard to the already
broken invariants. I agree that trapping them is too dangerous, because
it would provide too much freedom (and there is the security issue with
instanceof). However, I think there is room in between to provide some
power to the proxy writer while keeping things under control. I have
tried to show one such idea and one way to partially achieve this idea,
there are different achievement tracks for the same idea and certainly
other ideas.


Another way of solving the inconsistency I see in providing the 6
"prototype-climbing" traps ("getPropertyDescriptor", "getPropertyNames",
"has", "get", "set" and "enumerate") and protecting
Object.getPrototypeOf and instanceof could be to provide a softer
version of proxies where these traps wouldn't be provided.
Prototype-climbing internal methods would be delegated to the ECMAScript
engine with the default behavior they have on objects (but still calling
own traps when their algorithm requires them to). By design of this
softer version, proxy writers would be ensured that inheritance will be
done properly for them and they don't have to worry about it. They would
just have to take care of the own properties "layer". (This paragraph is
a slightly different topic. If you feel it requires its own thread, feel
free to fork)

David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110322/3f055979/attachment.html>


More information about the es-discuss mailing list