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

Decorators 2 Transform [WIP] #6107

Closed
wants to merge 23 commits into
base: master
from

Conversation

@peey
Contributor

peey commented Aug 15, 2017

Q A
Fixed Issues Progress on babel/proposals#11
Patch: Bug Fix? No
Major: Breaking Change? No
Minor: New Feature? Yes
Tests Added/Pass? Yes
Spec Compliancy? Yes
License MIT
Doc PR No
Any Dependency Changes? Yes, on decorators2 syntax plugin

Note: I'll use the terms evaluation to mean evaluation of @dec(args) and decoration to mean actual execution of the decorators

Also, note that the decorator proposal introduces the concept of an elementDescriptor, which is distinct from the propertyDescriptor we all know about.

To understand the code, you need to know that an elementDescriptor is an object with the shape:

{
  kind: "property", // I don't know what other values kind can take
  key: <the name of the method>,
  isStatic: true|false,
  descriptor: <the property descriptor of the method>
}

I've implemented the methods decorate, decorateElement, decorateClass, makeElementDescriptor, and mergeDuplicateElements in helpers.js. Out of these, decorateElement and decorateClass correspond to the spec operations DecorateElement, and DecorateClass respectively. mergeDuplicateElements corrseponds to the operation used in step 6 of both DecorateClass and DecorateElement.

The method decorate runs the whole thing. It takes care of the evaluation order (see below) and corrsponds to Runtime Semantics: ClassDefinitionEvaluation step 21 onwards.

The changes in babel-core are from #6058

Assumptions Made: (Please feel free to correct)

  1. evaluation order is as the following: (relevant spec operation)
    a. evaluate all method decorators in order [step 21.g. of above spec link] (left to right / outer to inner)
    b. (decoration) execute all method decorators in reverse order step 24.a.(right to left / inner to outer)
    c. evaluate all class decorators in order step 29. (assuming that DecoratorListEvaluation happens just before this, since it's not explicitly mentioned)
    d. (decoration) execute all class decorators in reverse order step 29
  2. the signature of decorators is as follows:
// for member decorator @foo(a,b,c)
function foo(a, b, c) {
   return (descriptor) => {descriptor, extras, finishers};
} 

// for member decorator @foo
function foo(descriptor) {return {descriptor, extras, finishers}}

// for class decorator @foo(a,b,c)
function foo(a,b,c) {
  // elements is an array of  descriptors of all members after running the decorators
  return (ctor, parent, memberDescriptors) => {constructor, elements, finishers}
}

// for class decorator @foo
function (ctor, parent, memberDescriptors) {return {constructor, elements, finishers}}
  1. The spec uses two forms - a finisher attached to the elementDescriptor and explicitly returning finishers array. I've largely ignored the former because it doesn't seem to make any sense.
  2. The spec is clear that elementDescriptor is passed to method decorators and not propertyDescriptor, and that's how I had implemented it in the past. However, I changed it to use propertyDescriptor because using elementDescriptor would imply that the decorator can change the key of the method being decorated, and if it is static or not. I'm not sure if this behaviour is intended or unintended. I need more feedback on this from the spec designers. Changing back to elementDescriptor shouldn't be too much trouble.

TODO / WIP:

  • tests for heritage
  • configurability check and error if a decorator tries to configure a non-configurable descriptor
  • handling undecorated accessors descriptors (getters and setters)
  • more tests for transformation, optimizing for special cases.

@peey peey closed this Aug 15, 2017

@peey peey changed the title from Decorators 2 Transform [WIP to Decorators 2 Transform [WIP] Aug 15, 2017

@peey

This comment has been minimized.

Show comment
Hide comment
@peey

peey Aug 15, 2017

Contributor

I accidentally opened this PR with incomplete info. Now that I've edited it to include all details, I'm reopening

Contributor

peey commented Aug 15, 2017

I accidentally opened this PR with incomplete info. Now that I've edited it to include all details, I'm reopening

@peey peey reopened this Aug 15, 2017

@hzoo hzoo requested review from littledan and diervo Aug 15, 2017

@hzoo hzoo added the es-proposal label Aug 15, 2017

@littledan

This comment has been minimized.

Show comment
Hide comment
@littledan

littledan Aug 15, 2017

I'm proposing some changes to the decorator proposal at https://github.com/littledan/proposal-unified-class-features . You can see a summary of the changes in this presentation. The committee and decorators champions seemed to view it positively. I'd be interested to hear what you think.

I don't think this follow-on proposal should affect most of the implementation. I'll review this patch as an implementation of this decorators proposal.

littledan commented Aug 15, 2017

I'm proposing some changes to the decorator proposal at https://github.com/littledan/proposal-unified-class-features . You can see a summary of the changes in this presentation. The committee and decorators champions seemed to view it positively. I'd be interested to hear what you think.

I don't think this follow-on proposal should affect most of the implementation. I'll review this patch as an implementation of this decorators proposal.

@jkrems

This comment has been minimized.

Show comment
Hide comment
@jkrems

jkrems Aug 15, 2017

Contributor

@littledan Does removing finishers from element decorators mean they can no longer safely attach meta data? Especially if finishers can now replace the class completely? I thought this was done explicitly to allow meta data "annotation"-style decorators that are hard or impossible to implement with stage 0?

Contributor

jkrems commented Aug 15, 2017

@littledan Does removing finishers from element decorators mean they can no longer safely attach meta data? Especially if finishers can now replace the class completely? I thought this was done explicitly to allow meta data "annotation"-style decorators that are hard or impossible to implement with stage 0?

@babel-bot babel-bot referenced this pull request Aug 15, 2017

Open

Decorators (Stage 2) #11

1 of 2 tasks complete

@hzoo hzoo referenced this pull request Aug 15, 2017

Merged

Create aug-16.md #32

@littledan

This comment has been minimized.

Show comment
Hide comment
@littledan

littledan Aug 15, 2017

@jkrems You can attach metadata by putting it on the methods in an additional property, or making a WeakMap keyed off of the method. Would that work for you?

littledan commented Aug 15, 2017

@jkrems You can attach metadata by putting it on the methods in an additional property, or making a WeakMap keyed off of the method. Would that work for you?

@jkrems

This comment has been minimized.

Show comment
Hide comment
@jkrems

jkrems Aug 15, 2017

Contributor

@littledan The nice thing about finalizers was that it allowed to the 2nd thing safely - no matter what other decorators would do.

@injectDataSource
class MyResource {
  @deprecated // not 100% sure about evaluation order
  @GET('/users/{id}') // wants to attach meta data to `getUser`
  async getUser(params) { /* ... */ }
}

We're assuming here that:

  1. deprecated wraps the actual function value that GET saw in another function that logs a deprecation warning.
  2. injectDataSource could be something like a redux connect helper. The point being: It wants to wrap the constructor itself.

In the previous stage 2 iteration, @GET could use a finalizer to myWeakMap.set(finalCtor, metaData). If it tries to do the same during its own run, it would get the MyResource class before being wrapped. And the meta data would get lost. If it tries to attach the meta data to the function/property value itself, it will be swallowed by deprecated.

Contributor

jkrems commented Aug 15, 2017

@littledan The nice thing about finalizers was that it allowed to the 2nd thing safely - no matter what other decorators would do.

@injectDataSource
class MyResource {
  @deprecated // not 100% sure about evaluation order
  @GET('/users/{id}') // wants to attach meta data to `getUser`
  async getUser(params) { /* ... */ }
}

We're assuming here that:

  1. deprecated wraps the actual function value that GET saw in another function that logs a deprecation warning.
  2. injectDataSource could be something like a redux connect helper. The point being: It wants to wrap the constructor itself.

In the previous stage 2 iteration, @GET could use a finalizer to myWeakMap.set(finalCtor, metaData). If it tries to do the same during its own run, it would get the MyResource class before being wrapped. And the meta data would get lost. If it tries to attach the meta data to the function/property value itself, it will be swallowed by deprecated.

@Kovensky

I just looked at the tests for now; will look at the code later

Show outdated Hide outdated ...abel-plugin-transform-decorators-2/test/fixtures/examples/logger/exec.js
}
class Foo {
@overrider(() => 3) method() {

This comment has been minimized.

@Kovensky

Kovensky Aug 16, 2017

Member

What is this here? Is it the constructor, the outer this, or the newly created instance?

Could be worth having a test for it.

@Kovensky

Kovensky Aug 16, 2017

Member

What is this here? Is it the constructor, the outer this, or the newly created instance?

Could be worth having a test for it.

Show outdated Hide outdated ...babel-plugin-transform-decorators-2/test/fixtures/examples/spare/exec.js
undecorated() {}
}, [["undecorated"]], [["method", [methDec]]], void 0)([classDec]);

This comment has been minimized.

@Kovensky

Kovensky Aug 16, 2017

Member

(Having not looked at the decorate implementation yet) Too many arrays? Some of these look like they could be flattened one level, but I'm only guessing at the meaning of each parameter.

@Kovensky

Kovensky Aug 16, 2017

Member

(Having not looked at the decorate implementation yet) Too many arrays? Some of these look like they could be flattened one level, but I'm only guessing at the meaning of each parameter.

This comment has been minimized.

@peey

peey Aug 16, 2017

Contributor

currently I'm using const [key, isStatic] = [..] so that's why the inner items of those are arrays (in case of a non static member, I just omit the second array element). We could certainly simplify it further if needed, e.g. if it's static then it'll look like ["method", true] otherwise it'll look like just "method" and not ["method"]

@peey

peey Aug 16, 2017

Contributor

currently I'm using const [key, isStatic] = [..] so that's why the inner items of those are arrays (in case of a non static member, I just omit the second array element). We could certainly simplify it further if needed, e.g. if it's static then it'll look like ["method", true] otherwise it'll look like just "method" and not ["method"]

Show outdated Hide outdated ...es/babel-plugin-transform-decorators-2/test/fixtures/wip/wip/expected.js
@@ -21,6 +21,6 @@ function t() {
function t() {
for (var i = 0; i < arguments.length; i++) {
return arguments.length <= i ? undefined : arguments[i];

This comment has been minimized.

@hzoo

hzoo Aug 16, 2017

Member

this should be fixed

@hzoo

hzoo Aug 16, 2017

Member

this should be fixed

This comment has been minimized.

@peey

peey Aug 17, 2017

Contributor

I'm not sure if this is related to this PR. I'll investigate more

@peey

peey Aug 17, 2017

Contributor

I'm not sure if this is related to this PR. I'll investigate more

@danez danez closed this Aug 31, 2017

@danez danez reopened this Aug 31, 2017

@danez danez changed the base branch from 7.0 to master Aug 31, 2017

@hzoo hzoo referenced this pull request Sep 14, 2017

Closed

July 2017 #19

10 of 10 tasks complete
@peey

This comment has been minimized.

Show comment
Hide comment
@peey

peey Sep 16, 2017

Contributor

Apologies for responding late to the review. Uni workload has started again.

Updates: Now we're merging getters and setters & other things pointed out by littledan in their review

Todo:

  • Refactor merging so we can merge when adding extras instead of overriding
  • Look into if other props things like enumerable, configurable, writable & value (in case of data descriptor) are also meant to be merged. @littledan thoughts on this?
  • Fix the failing test for x instanceof Foo if Foo has been processed by the transform
  • Incorporate nicolo's #6254
Contributor

peey commented Sep 16, 2017

Apologies for responding late to the review. Uni workload has started again.

Updates: Now we're merging getters and setters & other things pointed out by littledan in their review

Todo:

  • Refactor merging so we can merge when adding extras instead of overriding
  • Look into if other props things like enumerable, configurable, writable & value (in case of data descriptor) are also meant to be merged. @littledan thoughts on this?
  • Fix the failing test for x instanceof Foo if Foo has been processed by the transform
  • Incorporate nicolo's #6254
Fix parentage tests. It's not an issue
I had missed extends in the test case but the asserts were expecting it.
I thought that tests were failing for more sinister reasons, but
thankfully it isn't so
@littledan

I've confirmed with @bterlson and @wycats that http://tc39.github.io/proposal-unified-class-features should be considered to subsume http://tc39.github.io/proposal-decorators for the planned spec text.

@hzoo hzoo referenced this pull request Sep 27, 2017

Merged

Create sept-26.md #36

2 of 9 tasks complete

@hzoo hzoo added the Priority: High label Nov 29, 2017

@hzoo hzoo added this to the Babel 7.next milestone Dec 27, 2017

buschtoens added a commit to buschtoens/acme-v2 that referenced this pull request Feb 28, 2018

@nicolo-ribaudo nicolo-ribaudo referenced this pull request Mar 10, 2018

Closed

Decorators #7542

1 of 2 tasks complete
@peey

This comment has been minimized.

Show comment
Hide comment
@peey

peey Mar 12, 2018

Contributor

Update: @nicolo-ribaudo asked me if they could take this up, and I told them they could.

The outstanding work on this PR would have been the todo in my previous comment and then any changes between the spec at the time this PR was made and the spec now (I'm not sure about the exact status of this, seems like http://tc39.github.io/proposal-unified-class-features that @littledan's comment specified is dead and http://tc39.github.io/proposal-decorators has been updated)

Since Babel's codebase has changed much (and the proposal may have too), I guess Nicolo's taking the route of reworking this feature and salvaging code from this PR as needed in #7542 . Kudos and good luck!

Here's what might be useful from this PR:

Contributor

peey commented Mar 12, 2018

Update: @nicolo-ribaudo asked me if they could take this up, and I told them they could.

The outstanding work on this PR would have been the todo in my previous comment and then any changes between the spec at the time this PR was made and the spec now (I'm not sure about the exact status of this, seems like http://tc39.github.io/proposal-unified-class-features that @littledan's comment specified is dead and http://tc39.github.io/proposal-decorators has been updated)

Since Babel's codebase has changed much (and the proposal may have too), I guess Nicolo's taking the route of reworking this feature and salvaging code from this PR as needed in #7542 . Kudos and good luck!

Here's what might be useful from this PR:

@nicolo-ribaudo nicolo-ribaudo referenced this pull request May 18, 2018

Merged

Add support for the new decorators proposal #7976

1 of 4 tasks complete
@nicolo-ribaudo

This comment has been minimized.

Show comment
Hide comment
@nicolo-ribaudo

nicolo-ribaudo Jun 3, 2018

Member

Closing in favor #7976. Thank you @peey for your work!

Member

nicolo-ribaudo commented Jun 3, 2018

Closing in favor #7976. Thank you @peey for your work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment