-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normative: Add initializer callback for side effects #165
Changes from 1 commit
9f6d669
5bba880
14b8919
9c3c148
77543ec
77e1922
9acf8f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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> | ||||||
|
@@ -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> | ||||||
|
@@ -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> | ||||||
|
||||||
|
||||||
|
@@ -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" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
key: String, Symbol or Private Name, | ||||||
placement: "static", "prototype", or "own" | ||||||
descriptor: PropertyDescriptor, | ||||||
|
@@ -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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be made more readable? (I don't know if it valid from a spec-grammar point of view)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, done |
||||||
1. Return the Record { [[Elements]]: _elements_, [[Finishers]]: _finishers_ }. | ||||||
</emu-alg> | ||||||
</emu-clause> | ||||||
|
@@ -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_). | ||||||
|
@@ -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. | ||||||
littledan marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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"`, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: "If kind is not" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
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> | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might consider allowing "static" as the decorator way of doing https://github.com/tc39/proposal-class-static-block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree; static should be allowed here since they also can have initializers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this as a way towards a static block. This is just a way that a decorator can add extra code to run. But decorators are already able to run code at the end of when a class is executing, in the finisher callback. We could allow this out of consistency (it would have slightly different timing from finishers), but I don't see a use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A use case is the same as for "own" - being able to invoke a setter on a superclass (in this case, on a superclass's constructor that had, say,
static set foo(v) {}
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ljharb What would be the problem with using a finisher for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly you could use a finisher for that - but then you'd be forced to use a class-level decorator to change the semantics of a specific field, so instead of:
you'd need to have:
or:
both of which are decidedly less ergonomic and elegant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why that would be the case. You can use a finisher on a static field declaration too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ljharb, wait, but you can use
finisher
with a field or a method decorator. It is not required to use class-level decorators to activate finishers.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh, that wasn't clear to me. In that case - instead of this "initializer" approach, is there a reason not to allow finishers on "own" fields, to provide an "instance finisher"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ljharb yeah, I came to the same idea in my comment below :)