tc39/proposal-decorators

private access into a decorator pararameter

pabloalmunia opened this issue · 9 comments

First a simple check, this code work perfectly_

class Test {
  #priv = 1;

  checkPriv() {
    const that = this;
    return function() {
      return that.#priv
    }
  }
}

const test = new Test();
console.log(test.checkPriv()() === 1);

The returned function of checkPriv() can access to the private member because is written into the class body.

As a result, Is this working too?

function guard(checker) {
  return function (fn, context) {
    return function (...args) {
      let result = checker.call(this, this);
      if (result) {
        return fn.apply(this, arguments);
      }
    }
  }
}


class C {
  #open = false;

  @guard (that => that.#open)
  run() {
    console.log('running...');
  }
}

const c = new C();
c.run();

The access to that.#pp is written into the class and I not sure the correct behaviour.

I would expect that to work since its within the class body, but would that also mean C (the class) also be accessible there? Would that allow the class itself to be passed in to decorators?

 @guard (C)

Absolutely that (referring to the OP) would work. Anything you pass in would be accessible there.

would that also mean C (the class) also be accessible there? Would that allow the class itself to be passed in to decorators?

I think that the class is not really defined when the member decorator is called and, as result, the @deco(klass) is not possible.

I think that the class is not really defined when the member decorator is called and, as result, the @deco(klass) is not possible.

@pabloalmunia are you tracking this for the playground? Looks like you can pass the class into decorators currently (0.5.0).

@senocular The transpilation process has some limitations that the real compiler does not have. One of them is not being able to include code within the class construction process. To overcome this limitation, the transpiler executes the member decorators when the class is already built.

C.prototype.m = decorator(C.prototype.m, {
  kind: "method",
  name: "m",
  isStatic: false,
  isPrivate: false,
  ...__PrepareMetadata(C.prototype, "public", "m")
}) ?? C.prototype.m;

Still, the specification is unambiguous on this aspect, and the transpiler limitation should not be taken as the norm.

A possible solution for the transpiler would be to rename the class with a temp name until the whole process of member decorating has been completed and rename it back to the original name before applying the class and static decorators. What do you think of this possible strategy? Is it appropriate and necessary to maintain the highest possible fidelity to the standard?

@pabloalmunia I think you'd probably have to rename. While you can evaluate decorators prior to the class being built, for example calling the guard(C) part of @guard(C) (thereby ensuring C is not accessible in that case), I imagine the class would have to exist for the execution of the decorators themselves. This is important for maintaining things like the home object for methods of that class since that would get lost if you attempted to create the method individually as a function first without having it created as part of the class.

I was going to create an issue for clarifying execution order of decorators but it looks like you already did (#425) :)

Well @senocular, as you recomended we have formed the class with a temporal name and rename this when the class is defined after the all member decorators has been executed and before the class decorators and static decorators are executed.

Here is an example: https://link.javascriptdecorators.org/h2B6EF If you remove the comments from @decorator(C) and run the code, an error is thrown: "Cannot access C before initialization".

Given that the evaluation of the decorators comes before everything else, I'd expect each instance of @catchClass(C) to throw. This is something related to what I recently posted in 425

Yes, the behaviour is completelly different if the DecoratorExpression is evaluated before start the process or interspersed with the decorators call.