Please do the following substitutions in my message:

* "created-but-initialised" → "created-but-not-initialised"
* "an ordinary object" → "an object (i.e. a value of type Object) that is not a 
Non-Constructed Object" (2x)

—Claude

Le 27 juin 2014 à 15:56, Claude Pache <[email protected]> a écrit :

> In the currently specced design of classes, the fact that the creation step 
> and the initialisation step of built-in object may be separated by arbitrary 
> user code is thought to be problematic.
> 
> Jason proposed a @@new behaviour in replacement of @@create that would avoid 
> the issue [1]. Here is a counter-proposal (or an improved proposal, ad 
> libidum), which is not tightly coupled to @@new. In fact, it is designed to 
> (well, TBH, it happens to) just work in absence of @@create or @@new hook, 
> although it is possible to introduce them.
> 
> The basic idea is the following: the creation process (the @@create step) is 
> deferred as late as possible.
> It will appear that, for built-in base classes like Array, creation occurs 
> just before initialisation, making the created-but-initialised state of such 
> objects unobservable, at least in absence of user-overridable @@create hook.
> 
> Non-Constructed Objects
> -----------------------
> 
> Non-Constructed Objects are introduced in order to describe the state of 
> not-yet-defined this-bindings.
> 
> Non-Constructed Objects is probably not be the greatest approach to spec the 
> thing, especially when considering the awful Step 3 of 
> InitializeThisBindings() algorithm below;
> it is just a convenient hack that allows me to expose the idea with minimal 
> change from the current specification draft.
> 
> A Non-Constructed Object is a special placeholder exotic object with the 
> following internal slots:
> 
> * [[Constructor]], which holds a reference to the constructor;
> * [[NonConstructed]], set to `true`.
> 
> Non-Constructed Objects may only appear as value of this-bindings inside 
> functions
> (or, using the spec language, as value of the `thisValue` component of a 
> function environment record).
> Any attempt to get an explicit reference to such an object in user code  will 
> throw an error.
> The only way to pass (implicitely) a reference to a Non-Constructed Object 
> between different function environment records,
> is through calls to `super`.
> 
> InitializeThisBindings(nonconstructedObj, obj) abstract operation
> ----------------------------------------------------------------------
> 
> This operation performs the actual initialisation of the this-bindings that 
> were previously deferred:
> 
>   1. Assert `nonconstructedThisObj` is a Non-Constructed Object.
>   2. Assert `obj` is an ordinary object.
>   3. Replace all references to `nonconstructedThisObj` with references to 
> `obj`.
>     (In particular, this step will effectively initialise the this-binding of 
> every function environment record that used to reference `nonconstructedObj`.)
> 
> Additional runtime semantics of the `this` keyword
> --------------------------------------------------
> 
> Any attempt to get an explicit reference to the `thisValue` of a function 
> environment record while it holds a Non-Constructed Object, shall throw an 
> error.
> 
> 
> new C(...args)
> ----------------
> 
> When a constructor `C` is called with arguments `args`, the following steps 
> are taken.
> In particular, the actual initialisation of the this-value is deferred.
> 
> 1. Let `thisValue` be a new Non-Constructed Object, with its internal slot 
> [[Constructor]] set to `C`.
> 2. Let `R` be the result of `C`.[[Call]](`thisValue`, `args`).
> 3. NOTE. The operation InitializeThisBinding() may have been called during 
> the previous step, meaning that `thisValue` may now be a regular object.
> 4. ReturnIfAbrupt(`R`).
> 5. If Type(`R`) is Object, return `R`.
> 6. If `thisValue` is a Non-Constructed Object, throw an error.
> 7. Return `thisValue`.
> 
> No more [[Construct]]
> ----------------------
> 
> [[Construct]] internal method is gone. Actually it is conflated with the 
> [[Call]] internal method, modified as below.
> The key fact is that [[Call]] is able to see if it should have a 
> [[Construct]]-like behaviour,
> by examining whether its `thisArgument` is a Non-Constructed Object.
> 
> 
> F.[[Call]] (thisArgument, argumentsList) for user-defined functions
> -----------------------------------------------------------------------------------
> 
> User-defined functions has the currently specced [[Call]] behaviour [Section 
> 9.2.2],
> with the following additional step inserted somewhere near the beginning of 
> the algorithm, e.g., after step 1:
> 
> 1bis. If the `thisArgument` is a Non-Constructed Object,
>   a. If `F`’s [[NeedsSuper]] internal slot is set to false (IOW, if `F`’s 
> code doesn’t contain `super`),
>       i. Let `proto` be the result of 
> GetPrototypeFromConstructor(`thisArgument`.[[Constructor]], 
> "%ObjectPrototype%").
>       ii. ReturnIfAbrupt(`proto`).
>       iii. Let `obj` be ObjectCreate(`proto`).
>       iv. Perform InitializeThisBindings(`thisArgument`, `obj`).
>       v. Assert: Now, `thisArgument` is an ordinary object.
>   b. Else, `thisArgument` is left untouched. // it is meant to be handled at 
> the occasion of the enclosed `super` call.
> 
> 
> F.[[Call]] (thisArgument, argumentsList) for bound functions
> -------------------------------------------------------------
> 
> In the algorithm sepcced in [Section 9.4.1.1], step 2 is replaced with:
> 
> 2. If the `thisArgument` is a Non-Constructed Object, let `boundThis` be 
> `thisArgument`. // this is the current [[Construct]] behaviour
> 2bis. Else, let `boundThis` be the value of `F`’s [[BoundThis]] internal 
> slot.  // this is the current [[Call]] behaviour
> 
> 
> F.[[Call]] (thisArgument, argumentsList) for the built-in Object constructor
> -----------------------------------------------------------------------------
> 
> There is no change: `Object(...)` acts as a factory rather than as a 
> constructor, as currently specified, and the `thisArgument` is ignored.
> In particular trying to subclass `Object` will lead to unexpected results. 
> Note however that `new Object` does still work.
> 
> 
> F.[[Call]] (thisArgument, argumentsList) for the built-in Array contsructor
> ---------------------------------------------------------------------------
> 
> The [[ArrayInitializationState]] internal slot is gone, and Step 4 of the 
> algorithms in [Sections 22.1.1.*] is replaced with (where `O` is the 
> this-value):
> 
> 4. If `O` is a Non-Constructed Object,
>   a. Let `proto` be the result of 
> GetPrototypeFromConstructor(`O`.[[Constructor]], "%ArrayPrototype%").
>   b. ReturnIfAbrupt(`proto`)
>   a. Let `array` be ArrayCreate(<<length>>, `proto`).
>   b. Perform InitializeThisBindings(`thisArgument`, `array`)
> 5. Else, etc.
> 
> The [[Call]] behaviour of other built-in constructors is left as an exercise 
> to the reader.
> 
> Comments
> --------
> 
> There is a nice side-effect of the proposal: The new internal check intended 
> to discriminate between call-as-function and call-as-constructor is easier 
> and more robust. In particular,
> 
> * hacks such as [[ArrayInitializationState]] are no longer needed;
> * bound functions are truly subclassable (see [2]).
> 
> However, it remains very hard for user-defined functions to distinguish 
> correctly between constructor/initialisation-calls and method/function-calls, 
> or to write code that works well in both cases. (At least the situation is 
> not worse than in ES5-.)
> 
> Optional: the @@create hook
> ----------------------------
> 
> A @@create hook can easily be placed as follows:
> In each [[Call]] internal methods defined above, a call to 
> (`thisArgument`.[[Constructor]]).@@create
> could replace the steps spanning from GetPrototypeFromConstructor(...) 
> inclusive to InitializeThisBindings(...) exclusive.
> 
> Whether such a hook is compatible with, e.g., DOM constructors, is left to 
> the appreciation of the competent people. At worst, a built-in constructor 
> could cheat by defining its own [[Call]] internal method that would refuse to 
> run the @@create hook.
> 
> Optional: the @@new hook
> -------------------------
> 
> Alternatively, the following hook may be installed:
> At the beginning of each call to F.[[Call]], the following steps are taken:
> 
> 1. if `thisArgument` is a Non-Constructed Object,
>   a. If `F` has an *own* property named @@new,
>       i. Let `R` be the result of 
> `F`[@@new].[[Call]](`thisArgument`.[[Constructor]], `argumentsList`).
>       ii. ReturnIfAbrupt(`R`).
>       iii. If Type(`R`) is Object,
>           α. InitializeThisBindings(`thisArgument`, `R`).
>       iv. Return `R`.
>   b. etc.
> 2. etc.
> 
> Note that we don’t look for inherited @@new property, in order to preserve 
> the initialise-at-latest-time behaviour.
> 
> —Claude
> 
> [Section 9.2.2]: 
> http://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
> [Section 9.4.1.1]: 
> http://people.mozilla.org/~jorendorff/es6-draft.html#sec-call
> [Sections 22.1.1.*]: 
> http://people.mozilla.org/~jorendorff/es6-draft.html#sec-array-constructor
> [1]: http://esdiscuss.org/topic/new
> [2]: 
> http://esdiscuss.org/topic/issue-when-subclassing-a-bound-function-used-as-constructor

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to