Open evanw opened 6 months ago
This is intentional: https://github.com/tc39/proposal-decorators/issues/329
~Babel implements this for normal fields, but not for the accessor internal private field: this is a problem, because it makes it impossible to use an accessor with decorators that return a subclass.~ EDIT: It looks like Babel implements this as defined in the proposal: https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.6&spec=false&loose=false&code_lz=AICg1gBAvAfBDGAbAhgZ1RApgDwC6YDsATDSAbwF8BKAKCTQwEEIyaIJVdlcBLeCZPHiZ0AewBOEbNAgAmANw0KNGkA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=&prettier=false&targets=Node-0.10&version=7.24.5&externalPlugins=%40babel%2Fplugin-proposal-decorators%407.24.1&assumptions=%7B%7D
Also I wonder, can static private methods be called in any way when there is a class decorator that returns a subclass?
This is intentional, class decorators should have access to the class when it is as complete as possible. The only reason that class decorators do not run after fields/static blocks is because you can end up in a split world, e.g.
class C {
static f = new C();
}
const c = new C();
If decorators ran after fields are assigned, then c
and C.f
would not be the same value if C was replaced by a class decorator. This would be very confusing, more so than not having the completely finalized class.
Class extra initializers (addInitializer
) plug that timing gap, they enable decorators to run some code after all static class fields have been assigned.
It looks like Babel implements this as defined in the proposal
It seems like perhaps Babel's implementation of MakeAutoAccessorGetter
and MakeAutoAccessorSetter
are incorrectly bound to the original class object instead of this
. Compare this auto-accessor to this manual accessor. The tests are the same in both and I'd expect both to behave the same, but the first one fails and the second one passes. My reading of MakeAutoAccessorGetter
is that it's supposed to use this
, which can then change at run-time:
- Let getterClosure be a new Abstract Closure with no parameters that captures privateStateName and performs the following steps when called:
- Let o be the this value.
- Return ? PrivateGet(o, privateStateName).
- Let getter be CreateBuiltinFunction(getterClosure, 0, "get", « »).
- Perform SetFunctionName(getter, name, "get").
- Perform MakeMethod(getter, homeObject).
- Return getter.
One easy way to tell is that the getter (which can be accessed via Object.getOwnPropertyDescriptor
) shouldn't be able to be applied to arbitrary objects that don't have the underlying accessor storage private name.
Note: I'm using Firefox and it considers the code that Babel generates to be a syntax error (specifically reference to undeclared private field or method
). Chrome and Safari both run the code that Babel generates, so I'm guessing this is a bug in Firefox. I'll have to report that as well. But anyway don't use Firefox to test this.
Chrome and Safari both run the code that Babel generates, so I'm guessing this is a bug in Firefox. I'll have to report that as well. But anyway don't use Firefox to test this.
(off topic for this discussion, I'll hide it when you react to this comment)
Already reported, and it's already fixed in Firefox nightly I think :) When running with preset-env
Babel applies a plugin to workaround that firefox bug -- it's not enabled in that REPL link because I only added the decorators plugin.
@pzuraq About this
in auto accessor, it seems like the original intention was to not use this
? https://github.com/tc39/proposal-decorators/issues/468#issuecomment-1327632249
@nicolo-ribaudo yes that was the intention, I think I misunderstood what the _homeObject_
was doing when I closed that thread. I'll work on an update to the spec, hopefully for the next meeting in June.
I don't see anything about decorators on the June agenda. Does this need to be added to that agenda for it to be addressed? Sorry, I'm not too familiar on how TC39 meetings work.
@evanw unfortunately I didn't get around to making the updates in time. I don't work full time on TC39 stuff anymore unfortunately, haven't for the past couple of years, so I have been pushing along the spec on mostly personal time. I usually find a chunk of time every several months or so to do maintenance, address issues, etc. This last chunk was spent writing the tests for decorators in test262
, which were important for implementations to be able to get all the nuances of timing and such done correctly.
There are a few small updates like this one that need to make it into the spec, and that writing the tests has helped identify, so I'll be bundling those together for the next plenary after June. Hopefully now that we have tests, this will be the last round of updates.
I'm currently working on writing some tests for decorators in preparation for adding support for them to esbuild. My reading of the specification is that decorators on the class itself can return a new object, which then becomes the ultimate value for the class binding. That happens in
ClassDefinitionEvaluation
here:When that happens, I believe that causes methods to end up on the old object (because they are added to the old value of
F
earlier) and fields to end up on the new object (because they are added to the new value ofF
later). Am I reading the specification correctly? Specifically methods are applied here:and fields are initialized here:
Note that this means accessors (which are methods that reference fields) end up being split between the old object and the new object, with the getter/setter on the old object and the storage field on the new object.
Is this intentional? Neither TypeScript nor Babel seem to do this. I wanted to check that my interpretation is correct and that this is by design before locking in my implementation and tests like that. I don't use decorators myself so I don't have any intuition for whether this is a useful thing to do or not. I'm just trying to make an accurate implementation of this specification. It just seems kind of weird that e.g. decorating a class could seemingly break all private accessors in that class.
For example: