Making "super" work outside a literal?

Sean Eagan seaneagan1 at
Thu Jun 23 14:00:25 PDT 2011

Ok, sorry it took me so long, but I agree that the general idea of
Object.defineMethod is the best solution.  I think it can be improved
upon though, by adding the following:

Ability to use any property descriptor attributes when defining methods.
Ability to assign accessor getters and setters as well which use the
assigned to object as |super|.
Avoid having to add a "defineMethod" proxy trap.

Here are the two potential solutions I have in mind for this:

* Have Object.defineProperty create new functions with updated
[[Super]] when the "value", "get", or "set" attributes are functions
that use super.

* Add an Object.defineSuperProperty which is the same as the existing
Object.defineProperty except that it creates new functions with
updated [[Super]] when the "value", "get", or "set" attributes are
functions that use super.  It could potentially throw if no super
functions are passed.  This one may still require a
"defineSuperProperty" proxy trap, not sure.

Also, I wonder if there is a need for a Function.prototype.isSuper
similar to Function.prototype.isGenerator?

On Thu, Jun 23, 2011 at 10:51 AM, Allen Wirfs-Brock
<allen at> wrote:
> On Jun 23, 2011, at 3:14 PM, Sean Eagan wrote:
>> On Thu, Jun 23, 2011 at 4:56 AM, Allen Wirfs-Brock
>> <allen at> wrote:
>>> If you don't have actual implementation experience with dynamic languages I
>>> think you guys will just have to take Brendan's and my word for this.
>> I now understand and fully agree with your performance arguments
>> against dynamic super.  However, I still believe that the dynamic
>> super semantics are largely superior to the static super semantics.
>> Fortunately, I think the dynamic super semantics can be simulated via
>> static super, thus yielding the best of both approaches!
>> Here's what I propose:
>> Terminology:
>> super function - a function that uses |super|
>> super delegate function - A function which delegates to a super
>> function stored in its [[SuperFunction]] internal property, passing a
>> |super| binding stored in its [[Super]] internal property.
> so every super function still requires an implicit super parameter
>> Semantics:
>> When a super function is assigned as a method or accessor getter or
>> setter either via an object literal, Object.defineProperty or direct
>> assignment,
> this would add overhead to every [[Put]].  a test to see if the value is a super function.  The way to avoid the test would be defer the test until the value of the property was retrieved  (first time only perhaps. This is essentially the same as  the dynamic super implementation strategy of conditionally only passing the current object to a super function.  So, you have traded overhead on ever call for over head on every [[Put]].  [[Put]]'s probaby occur less often than [[Call]]'s but may still be too much.
>> a super delegate function is created and assigned instead
>> whose [[SuperFunction]] internal property is the super function being
>> assigned, and whose [[Super]] internal property is the [[Prototype]]
>> internal property of the object being assigned to.
>> When a super delegate function is subsequently accessed from an
>> object, e.g. o.methodThatUsesSuper or
>> Object.getOwnPropertyDescriptor(o,
>> "accessorWithGetterOrSetterThatUsesSuper"), this resolves to the
>> [[TargetFunction]] internal property of the super delegate function.
> I believe you are saying that upon property revival, a super delegate function reference is replaced with the value of its [[SuperFunction]].  This means there is an additional check (is the property value a super delegate function) on every [[Get]].
>> When a super function is called directly, e.g. superFunction() or
>>, |super| is resolved as the [[Prototype]]
>> internal property of the |this| of the call.
> How does it know it is being called directly?
> This seems to be the primary thing this approach adds to simple static super.  Why is this really important?  Is there a common usage case that requires this.  I don't believe it has been addressed by other OO languages.
>> When a super delegate function is called, e.g.
>> o.methodThatUsesSuper(), o.accessorWithGetterThatUsesSuper, or
>> o.accessorWithSetterThatUsesSuper = "foo", |super| is bound to the
>> super delegate function's [[Super]] internal property.
> in other words, the super delegate function calls the super function passing the value of its [[Super]] as the implicit super parameter
>> The equivalence class of a super function in  == and === consists of
>> the super function and all of its corresponding super delegate
>> functions, to illustrate:
>> let f = function(){return super.x},
>>    o1 = {method: f},
>>    o2 = Object.create(null, {method: {get: f}});
>> assert(f === o1.method); // true
>> assert(o1.method === Object.getOwnPropertyDescriptor(o2, "method").get); // true
>> Thus, the static super implementation is completely abstracted from
>> the user, and they benefit from the full dynamic super semantics!
> At the expense of additional runtime overhead in common runtime semantic primitives.   Why is this equivalence important?  Why isn't it reasonable that two function objects derived from the same declaration but with different super bindings will compare as not ===.  This doesn't seem any more unusual then the fact that a single function declaration with no free references still creates different function objects each time it is evaluated.
>> I'm sure there are still details to work out, but it seems quite
>> promising to me.
>> Cheers,
>> Sean Eagan

Sean Eagan

More information about the es-discuss mailing list