Skip to content

Commit

Permalink
Normative: Add kind: "initializer" for side effects
Browse files Browse the repository at this point in the history
Many decorators will want to perform a side effect when instantiating
a class, for example:
- To select [[Set]] rather than [[DefineOwnProperty]] semantics for
  defining a field
- To store the field in an entirely different place, neither an ordinary
  property or private field (e.g., MobX stores some fields in a Map)
- To register the instance under construction in some way (e.g., from
  a class decorator). "Instance finishers" have been proposed for this
  purpose, but it's not clear when to run such a finisher. For many use
  cases (e.g., interacting with the DOM), an instance "starter" is just
  as good, due to run-to-completion semantics.

This patch permits decorators to create elements of the form
  { kind: "initializer", placement: "own", initializer: fn }
to be used purely for their side effect, with evaluation semantics
and ordering identical to fields, and interspersed with them in
evaluation order.
  • Loading branch information
littledan committed Nov 2, 2018
1 parent 5a7d9c8 commit 9f6d669
Showing 1 changed file with 57 additions and 12 deletions.
69 changes: 57 additions & 12 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ <h1>The ElementDescriptor Specification Type</h1>
<tr> <th>Field Name</th> <th>Value</th> </tr>
</thead>
<tbody>
<tr> <td>[[Kind]]</td> <td>One of `"method"` or `"field"`</td> </tr>
<tr> <td>[[Kind]]</td> <td>One of `"method"`, `"field"` or `"initializer"`</td> </tr>
<tr> <td>[[Key]]</td> <td>A Property Key or %PrivateName% object</td> </tr>
<tr> <td>[[Descriptor]]</td> <td>A Property Descriptor</td> </tr>
<tr> <td>[[Placement]]</td> <td>One of `"static"`, `"prototype"`, or `"own"`</td> </tr>
Expand Down Expand Up @@ -188,6 +188,16 @@ <h1>The ElementDescriptor Specification Type</h1>
<li>_element_.[[Descriptor]].[[Configurable]] is *false*.</li>
</ul>
</li>
<li>
If _element_.[[Kind]] is `"initializer", then
<ul>
<li>_element_.[[Key]] absent.</li>
<li>_element_.[[Descriptor]] is absent.</li>
<li>_element_.[[Placement]] is `"own"`.</li>
<li>_element_.[[Initializer]] is present.</li>
<li>_element_.[[Decorators]] is absent.</li>
</ul>
</li>
</ul>
</p>
</emu-clause>
Expand Down Expand Up @@ -408,6 +418,27 @@ <h1>CoalesceClassElements ( _elements_ )</h1>
<emu-note>In the case of public class elements, coalescing corresponds in semantics to ValidateAndApplyPropertyDescriptor. Note that this algorithm only coalesces method and accessor declarations, and it leaves field declarations as is.</emu-note>
</emu-clause>

<emu-clause id="initialize-public-instance-elements" aoid="InitializeInstanceFields">
<h1>InitializeInstanceElements ( _O_, _constructor_ )</h1>

<emu-alg>
1. Assert: Type ( _O_ ) is Object.
1. Assert: Assert _constructor_ is an ECMAScript function object.
1. Let _elements_ be the value of _F_'s [[Elements]] internal slot.
1. For each item _element_ in order from _elements_,
1. If _element_.[[Placement]] is `"own"` and _element_.[[Kind]] is `"method"`,
1. Perform ? DefineClassElement(_O_, _element_).
1. For each item _element_ in order from _elements_,
1. If _element_.[[Placement]] is `"own"` and _element_.[[Kind]] is `"field"`,
1. Assert: _element_.[[Descriptor]] does not have a [[Value]], [[Get]] or [[Set]] slot.
1. Perform ? DefineClassElement(_O_, _element_).
1. <ins>If _element_.[[Kind]] is `"initializer"`,</ins>
1. <ins>Assert: _element_.[[Placement]] is `"own"`.</ins>
1. <ins>Perform ? Call(_element_.[[Initializer]], _O_).</ins>
1. Return.
</emu-alg>
</emu-clause>

</emu-clause>


Expand Down Expand Up @@ -563,7 +594,7 @@ <h1>Element Descriptors</h1>
<p>An <dfn>element descriptor</dfn> describes an element of a class or object literal and has the following shape:</p>
<pre><code class=typescript>
interface ElementDescriptor {
kind: "method" or "field"
kind: "method", "initializer" or "field"
key: String, Symbol or Private Name,
placement: "static", "prototype", or "own"
descriptor: PropertyDescriptor,
Expand Down Expand Up @@ -689,7 +720,7 @@ <h1>DecorateConstructor ( _elements_, _decorators_ )</h1>
1. Append _elementsAndFinisher_.[[Finisher]] to _finishers_.
1. If _elementsAndFinisher_.[[Elements]] is not *undefined*,
1. Set _elements_ to the concatenation of _elementsAndFinisher_.[[Elements]] and _privateElements_.
1. If there are two class elements _a_ and _b_ in _elements_ such that _a_.[[Key]] is _b_.[[Key]] and _a_.[[Placement]] is _b_.[[Placement]], throw a *TypeError* exception.
1. If there are two class elements _a_ and _b_ in _elements_ such that _a_.[[Kind]] is not `"initializer"` and _b_.[[Kind]] is not `"initializer"`, _a_.[[Key]] is _b_.[[Key]], and _a_.[[Placement]] is _b_.[[Placement]], throw a *TypeError* exception.
1. Return the Record { [[Elements]]: _elements_, [[Finishers]]: _finishers_ }.
</emu-alg>
</emu-clause>
Expand Down Expand Up @@ -744,12 +775,14 @@ <h1>FromElementDescriptor ( _element_ )</h1>
1. Let _desc_ be PropertyDescriptor{ [[Value]]: `"Descriptor"`, [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.
1. Perform ! DefinePropertyOrThrow(_obj_, @@toStringTag, _desc_).
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"kind"`, _element_.[[Kind]]).
1. Let _key_ be _element_.[[Key]].
1. If _key_ is a Private Name, set _key_ to ? PrivateNameObject(_key_).
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"key"`, _key_).
1. If _element_.[[Kind]] is `"method"` or `"field"`,
1. Let _key_ be _element_.[[Key]].
1. If _key_ is a Private Name, set _key_ to ? PrivateNameObject(_key_).
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"key"`, _key_).
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"placement"`, _element_.[[Placement]]).
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"descriptor"`, ! FromPropertyDescriptor(_element_.[[Descriptor]])).
1. If _element_.[[Kind]] is `"field"`,
1. If _element_.[[Kind]] is `"method"` or `"field"`,
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"descriptor"`, ! FromPropertyDescriptor(_element_.[[Descriptor]])).
1. If _element_.[[Kind]] is `"field"` or `"initializer"`,
1. Let _initializer_ be _element_.[[Initializer]].
1. If _initializer_ is ~empty~, set _initializer_ to *undefined*.
1. Perform ! CreateDataPropertyOrThrow(_obj_, `"initializer"`, _initializer_).
Expand Down Expand Up @@ -784,23 +817,35 @@ <h1>ToElementDescriptor ( _elementObject_ )</h1>
1. If _kind_ is not one of `"method"` or `"field"`, throw a *TypeError* exception.
1. Let _key_ be ? Get(_elementObject_, `"key"`).
1. If _key_ has a [[PrivateName]] internal slot, set _key_ to _key_.[[PrivateName]].
1. If _kind_ is `"initializer"`,
1. If _key_ is not *undefined*, throw a *TypeError* exception.
1. Otherwise, set _key_ to ? ToPropertyKey(_key_).
1. Let _placement_ be ? ToString(? Get(_elementObject_, `"placement"`)).
1. If _placement_ is not one of `"static"`, `"prototype"`, or `"own"`, throw a *TypeError* exception.
1. Let _descriptor_ be ? ToPropertyDescriptor(? Get(_elementObject_, `"descriptor"`)).
1. If _kind_ is `"initializer"`,
1. If _placement_ is not `"own"`, throw a *TypeError* exception.
1. Let _descriptor_ be ? Get(_elementObject_, `"descriptor"`)
1. If _kind_ is `"initializer"`,
1. If _descriptor_ is not *undefined*, throw a *TypeError* exception.
1. Otherwise,
1. Set _descriptor_ to ? ToPropertyDescriptor(_descriptor_).
1. Let _initializer_ be ? Get(_elementObject_, `"initializer"`).
1. Let _elements_ be ? Get(_elementObject_, `"elements"`).
1. If _elements_ is not *undefined*, throw a *TypeError* exception.
1. If _kind_ not `"field"`,
1. If _kind_ not `"field"` or `"initializer"`,
1. If _initializer_ is not *undefined*, throw a *TypeError* exception.
1. If _key_ is a Private Name,
1. If _descriptor_.[[Enumerable]] is *true*, throw a *TypeError* exception.
1. If _descriptor_.[[Configurable]] is *true*, throw a *TypeError* exception.
1. If _placement_ is `"prototype"`, throw a *TypeError* exception.
1. If _kind_ is `"field"`,
1. If _descriptor_ has a [[Get]], [[Set]] or [[Value]] internal slot, throw a *TypeError* exception.
1. Let _element_ be the ElementDescriptor { [[Kind]]: _kind_, [[Key]]: _key_, [[Placement]]: _placement_, [[Descriptor]]: _descriptor_ }.
1. If _kind_ is `"field"`, set _element_.[[Initializer]] to _initializer_.
1. Let _element_ be the ElementDescriptor { [[Kind]]: _kind_, [[Placement]]: _placement_ }.
1. If _kind_ is `"method"` or `"field",
1. Set _element_.[[Key]] to _key_.
1. Set _element_.[[Descriptor]] to _descriptor_.
1. If _kind_ is `"field"` or `"initializer"`,
1. Set _element_.[[Initializer]] to _initializer_.
1. Return _element_.
</emu-alg>
</emu-clause>
Expand Down

0 comments on commit 9f6d669

Please sign in to comment.