ember-cli-deploy-original icon indicating copy to clipboard operation
ember-cli-deploy-original copied to clipboard

Add circle ci build badge

Open achambers opened this issue 11 years ago • 0 comments

achambers avatar Nov 25 '14 17:11 achambers

Similarly, class references within the class scope should be separate from class references in the declared scope (this not specific to statics or initializers).

class C {
  static getter () { return C }
}

const { getter } = C
C = null
console.assert(getter() !== null)

Actual:
Assertion error

Expected:
(no error)

senocular avatar Aug 13 '21 16:08 senocular

This a collateral error with the fix about the access to the class from members decorator. It is very complicate covert both situación, we need descompuse the class into the Experimental Transpiler and it is a very hard process. We need time for research what is the solution/cost balance.

pabloalmunia avatar Aug 13 '21 17:08 pabloalmunia

Typescript does it using an alias for all internal references to the class. And by moving the definition of static initializers outside of the class body, they can access that alias from the declaration scope. They do something like:

let C = C_alias = class C {
    // static field removed from class body

    // other class members would also refer to the alias
    getter () {
        return C_alias; // was: `return C;`
    }
};
C.fieldC = C_alias; // uses alias, not original class name
C = decorate(...) // if decorated, happens after
// ...

senocular avatar Aug 13 '21 18:08 senocular

Well, we have been working on a different approach to provide a solution to class access from decorators before the class definition (member decorators) and after their definition (class and static decorators).

In the process of code analyzing for transpilation, we can identify whether the call to the decorator includes any reference to the class, either as a parameter or as part of the body of a inline function or as part of an expression. In these cases the transpilation process inserts an exception in the transpiled code.

Now, this code launch an error:

function getClass (fn) {
  console.log(fn().name)
  return function(value, context) {}
}

class A {
  @getClass (() => A)
  a() {}
}

but this code work perfectly:

function getClass (fn) {
  console.log(fn().name)
  return function(value, context) {}
}

class A {
  @getClass (() => A)
  static a() {}
}

Please, check our solution and give us your point of view.

pabloalmunia avatar Aug 24 '21 16:08 pabloalmunia

The exception should only occur on access of that identifier prior to its initialization. Referring to it alone is not a problem. It's only when you try to evaluate the identifier that, if done before the binding is made, an error is thrown.

So for the first example, the error should occur inside the decorator (wrapper) function:

function getClass (fn) {
  console.log(fn().name) // ERROR: An error thrown here (evaluating A in () => A)
  return function(value, context) {}
}

class A {
  @getClass (() => A)
  a() {}
}

However, this should not throw

function getClass (fn) {
  // A never accessed, no error
  return function(value, context) {}
}

class A {
  @getClass (() => A)
  a() {}
}

Similarly, for statics, while this should be fine (second example)

function getClass (fn) {
  console.log(fn().name) // A is initialized by this point, no error
  return function(value, context) {}
}

class A {
  @getClass (() => A)
  static a() {}
}

This should throw

function getClass (fn) {
  console.log(fn.name)
  return function(value, context) {}
}

class A {
  @getClass (A) // ERROR: an error thrown here (evaluating A)
  static a() {}
}

This is because decorators are evaluated before they're called. Evaluation of member decorators (both static and instance) happen before the binding of the class to the class name identifier. For instance decorators (non-static), they're also called before that binding, but static decorators are not, only called after the class name binding is made. This is an ordering I was outlining in: https://github.com/tc39/proposal-decorators/issues/425#issuecomment-896963752

Another consideration is class expressions. These do not seem to work at all in the transpiler.

function noop () {}

customElements.define(
  'my-element',
  class MyElement extends HTMLElement {
    @noop
    static a() {}
  }
)

Results in:

"Uncaught SyntaxError: Invalid or unexpected token"

In this case specifically the class name is not part of the outer scope so that local identifier is needed to refer to the class directly.

senocular avatar Aug 24 '21 18:08 senocular

The exception should only occur on access of that identifier prior to its initialization. Referring to it alone is not a problem. It's only when you try to evaluate the identifier that, if done before the binding is made, an error is thrown.

Yes, is a mistake in the new implementation.

This is because decorators are evaluated before they're called. Evaluation of member decorators (both static and instance) happen before the binding of the class to the class name identifier. For instance decorators (non-static), they're also called before that binding, but static decorators are not, only called after the class name binding is made. This is an ordering I was outlining in: tc39/proposal-decorators#425 (comment)

I'm not sure about the order for static decorators. In my humble opinion the static decorator is called after the class build. I will try to clarify this point with @pzuraq.

pabloalmunia avatar Aug 25 '21 09:08 pabloalmunia

I'm not sure about the order for static decorators. In my humble opinion the static decorator is called after the class build. I will try to clarify this point with @pzuraq.

Right, called after the class is defined, but not evaluated. All member decorators, instance and static, are evaluated at the same time before the class is accessible. Instance decorators are also called before the class is accessible, but static are called after. From the readme:

  1. Decorator expressions (the thing after the @) are evaluated interspersed with computed property names.
  2. Decorators are called (as functions) during class definition, after the methods have been evaluated but before the constructor and prototype have been put together.

Statics need access to the class for use cases like

class A {
  static instance = new A()
}

senocular avatar Aug 25 '21 12:08 senocular