Skip to content
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

Add Class and Class Element Decorators and accessor Keyword #2417

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

pzuraq
Copy link

@pzuraq pzuraq commented May 26, 2021

This PR includes the spec changes for the current iteration of the decorators proposal. This proposal is still in progress, currently in stage 2, and the PR is mainly being opened for reviewing and iteration purposes.

@ljharb ljharb added needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 normative change Affects behavior required to correctly evaluate some ECMAScript source text pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4. labels May 27, 2021
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@jmdyck
Copy link
Collaborator

jmdyck commented May 28, 2021

Rather than make a ton of "suggested changes" here, I've made a PR against the pzuraq:decorators branch.

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@jmdyck
Copy link
Collaborator

jmdyck commented May 30, 2021

Since Decorators are only used with Class definitions, would it make sense to put the Decorators clause within the Class Definitions clause?

@ljharb
Copy link
Member

ljharb commented May 30, 2021

They’re only used with classes for now- there’s lots of interest in follow up proposals to add them in other places.

@jmdyck
Copy link
Collaborator

jmdyck commented May 30, 2021

(I did look for follow-up proposals, but only found a couple at stage 0.)

Copy link
Collaborator

@jmdyck jmdyck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the following SDOs need to be modified to handle the new syntax for ClassDeclaration, ClassExpression, and/or ClassElement:

  • BoundNames
  • ClassElementKind
  • ComputedPropertyContains
  • Early Errors
  • IsConstantDeclaration
  • IsFunctionDefinition
  • IsStatic
  • PrivateBoundIdentifiers
  • PropName

(Note that currently, 4 of the 5 RHSs of ClassElement are chain productions, and so often have implicit definitions for SDOs, but they aren't chain productions in this PR, and so will need explicit definitions.)

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@ljharb ljharb force-pushed the master branch 3 times, most recently from 3d0c24c to 7a79833 Compare June 29, 2021 02:21
spec.html Outdated Show resolved Hide resolved
@pzuraq pzuraq force-pushed the decorators branch 2 times, most recently from 4e4deae to f210f0e Compare December 26, 2021 04:44
@pzuraq
Copy link
Author

pzuraq commented Dec 26, 2021

I believe the following SDOs need to be modified to handle the new syntax for ClassDeclaration, ClassExpression, and/or ClassElement:

  • BoundNames
  • ClassElementKind
  • ComputedPropertyContains
  • Early Errors
  • IsConstantDeclaration
  • IsFunctionDefinition
  • IsStatic
  • PrivateBoundIdentifiers
  • PropName

(Note that currently, 4 of the 5 RHSs of ClassElement are chain productions, and so often have implicit definitions for SDOs, but they aren't chain productions in this PR, and so will need explicit definitions.)

@jmdyck regarding this comment, I'm not quite sure I understand what needs to be done here. Why do these productions need to be updated exactly?

@jmdyck
Copy link
Collaborator

jmdyck commented Dec 26, 2021

Consider the production ClassElement : FieldDefinition in the status quo. It's a chain production, so (e.g.) the SDO ComputedPropertyContains doesn't need to have a definition for that production; instead we just chain down to FieldDefinition, for which ComputedPropertyContains does have an explicit definition.

But this PR replaces that production with ClassElement : DecoratorList? FieldDefinition, which is not a chain production, so there needs to be an explicit definition of ComputedPropertyContains for that production.

And likewise for the other alternatives of ClassElement that now have DecoratorList?, and likewise for the other SDOs that reach ClassElement.

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
1. Let _scope_ be the LexicalEnvironment of the running execution context.
1. Let _privateScope_ be the running execution context's PrivateEnvironment.
1. Let _sourceText_ be the empty sequence of Unicode code points.
1. Let _initializer_ be ! OrdinaryFunctionCreate(%Function.prototype%, _sourceText_, _formalParameterList_, _Initializer_, ~non-lexical-this~, _scope_, _privateScope_).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an initializer x = 123 it will return a function with the proper env and private env.

function () {
    = 123
}

The result function does not have a valid Body (the Initializer is passed in).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still an issue? Not sure I understood it, I would think that this is a syntax error

spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
@pzuraq
Copy link
Author

pzuraq commented Mar 3, 2022

Still working on addressing the feedback items, but I made a number of updates so far and wanted to get those updates up so folks could start reviewing sooner rather than later. Updates since last draft:

  • Separate parsing class elements and defining them so that we can control the order of definition and decorator application. This also allows us to avoid large numbers of optional arguments for various SDOs, instead the SDOs are now responsible for returning a ClassElementDefinition record, and the invoker of the SDO is then responsible for actually decorating and defining the element.

  • Run field decorators after method decorators, per Is metadata too confusing? proposal-decorators#424 (comment)

  • Extract and centralize class element decoration into a single abstract operation, so we don't have to worry about keeping it in sync in several different places.

  • Fix completions in ClassDefinitionEvaluation (need to reset the env after each possible abrupt completion).

  • Refactor adding private methods to instance to pull directly from Class.[[Elements]], so we don't have multiple sources of truth for the elements that are defined on a class. Class.[[Elements]] is the canonical source of truth after ClassDefinitionEvaluation, and any operations afterward such as instantiation should base themselves on that.

    NOTE: This does mean that the abstract operation for instantiating private methods on the instance has to "run" and filter out the private methods from all of the other elements per-instantiation in the spec, but my assumption here was that the exact details of the spec wouldn't need to be followed and implementations could, for instance, filter out and store the private method definitions at class definition time and then copy them onto the instance all at once. If we need to make a note to that effect, we can, and if it's important for the spec to run the AO only at definition time for some reason we can do that, it's just a bit more complicated.

  • Refactor to use anonymous function syntax.

Open Todos

  • Update SDOs that were previously relying on chain productions for items that no longer have them
  • Final review pass for formatting, linting, and overall correctness

Sidenote: It would be really nice to put comments inline in the spec to clarify the thinking for a specific portion, unsure if that's a feature anyone would be interested in?

spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
@pzuraq
Copy link
Author

pzuraq commented Mar 6, 2023

PR has been rebased, I squashed the existing commits to make rebasing easier overall. The only new commit contains non-normative fixes. The remaining issues open in this thread are all normative I believe, and I will be opening up separate PRs for each of them so we can discuss at the upcoming plenary.

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@ljharb ljharb requested a review from jmdyck March 7, 2023 20:28
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@doehyunbaek
Copy link
Contributor

Hi, esmeta typecheck is failing because InitializeInstanceElements, which calls InitializePrivateMethods, expects to return throw completion, as exmplified here.

InitializePrivateMethods, on the other hand, declares it might return abrupt completion, which is not a subtype of throw completion, as seen here.

Changing the return type of InitializePrivateMethods, as this commit doehyunbaek@8a2b51d actually solves the issue.

spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
strager added a commit to quick-lint/quick-lint-js that referenced this pull request Sep 9, 2023
Class field accessors are part of the decorators proposal.
tc39/ecma262#2417
strager added a commit to quick-lint/quick-lint-js that referenced this pull request Sep 9, 2023
Class field accessors are part of the decorators proposal.
tc39/ecma262#2417
Updates

- Refactor to use ClassElementDefinition more extensively
- Separate parsing class elements and defining them so that we can
  control the order of definition and decorator application
- Extract and centralize class element decoration into a single place
- Fix completions in ClassDefinitionEvaluation (need to reset the env
  after each possible abrupt completion).
- Refactor adding private methods to instance to pull directly from
  `Class.[[Elements]]`, so we don't have multiple sources of truth for
  the elements that are defined on a class. `Class.[[Elements]]` is the
  canonical source of truth after ClassDefinitionEvaluation, and any
  operations afterward such as instantiation should base themselves on
  that.
- Refactor to use anonymous function syntax.

Non-normative fixes

Fixes based on feedback from @jmdyck

Remove dynamic [[HomeObject]] from decorator return values

Throw an error if the value passed to `addInitializer` is not callable

Set the name of the `addInitializer` function

Remove setFunctionName from decorator application

Call decorators with their natural `this` value.

For more details, see this issue: tc39/proposal-decorators#487

Reverse initializer order

Update field and accessor extra initializers to run after definition
@pzuraq
Copy link
Author

pzuraq commented Feb 2, 2024

PR has been updated and rebased, and all changes have been addressed!

spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
@pzuraq
Copy link
Author

pzuraq commented Feb 2, 2024

@jmdyck all issues resolved I believe, except the one that @ljharb disagreed on. Happy to change it either way, just want to make sure it's the right way.

@pzuraq pzuraq marked this pull request as ready for review February 2, 2024 21:41
Comment on lines +4687 to +4688
The DecoratorDefinition type is a Record used in the specification of
decorators.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The DecoratorDefinition type is a Record used in the specification of
decorators.
The DecoratorDefinition type is a Record used in the specification of decorators.
Comment on lines +4692 to +4695
are defined by
<emu-xref href="#table-decoratordefinition-fields"></emu-xref>. Such
values are referred to as
<dfn variants="DecoratorDefinition Record">DecoratorDefinition Records</dfn>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we soft-wrap pretty consistently, not hard-wrap

Comment on lines +24679 to +24691
1. If _kind_ is ~method~, then
1. Let _kindStr_ be *"method"*.
1. Else if _kind_ is ~getter~, then
1. Let _kindStr_ be *"getter"*.
1. Else if _kind_ is ~setter~, then
1. Let _kindStr_ be *"setter"*.
1. Else if _kind_ is ~accessor~, then
1. Let _kindStr_ be *"accessor"*.
1. Else if _kind_ is ~field~, then
1. Let _kindStr_ be *"field"*.
1. Else,
1. Assert: _kind_ is ~class~.
1. Let _kindStr_ be *"class"*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aside for the editors: kind of feels like we need a SpecValueToString() AO or something for cases like this. i think atomics has one too.

Comment on lines +6897 to +6904
1. Let _existing_ be *undefined*.
1. If _privateMethods_ contains a PrivateElement _e_ such that _e_.[[Key]] is _element_.[[Key]], then
1. Assert: _e_.[[Kind]] is ~accessor~.
1. Set _existing_ to _e_.
1. If _e_.[[Get]] is not *undefined*, set _getter_ to _existing_.[[Get]].
1. If _e_.[[Set]] is not *undefined*, set _setter_ to _existing_.[[Set]].
1. Let _privateElement_ be PrivateElement { [[Key]]: _element_.[[Key]], [[Kind]]: ~accessor~, [[Get]]: _getter_, [[Set]]: _setter_ }.
1. If _existing_ is not *undefined*, replace _existing_ in _privateMethods_ with _privateElement_.
Copy link

@lpardosixtosMs lpardosixtosMs May 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that steps 6 through 9 define a no-op if there is already a private accessor pair with a setter and a getter and the same key, is this correct? If so, could we reword 7.c and 7.d? It seems a little strange to use _e_ and _existing_ in the same line when _existing_ was set to _e_ in the previous step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 normative change Affects behavior required to correctly evaluate some ECMAScript source text pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4.