-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
Reflect.metadata polyfill is incorrect #22
Comments
Please submit a PR for your fix. Thanks! |
OK. Given how TS copies the static members, a proper fix is harder without |
Yes, we can just update our docs and inform TypeScript users that they should load the Reflect polyfill. |
OK I will. Looking at the Core-js I think for those the only proper fix would be for TS to change its codegen. <_< |
You might open an issue with TS and explain the consequence of their generation strategy. |
Make sure that __metadata__ is the target own property and not something inherited from its prototype (important for class inheritance).
I submitted a PR. I just realized that an easy work-around for TS users is to define the utility function I am going to open a bug at TS anyway, it would be best to fix it for everyone "by default". |
Aurelia has it's own polyfills for metadata related features but
Reflect.defineMetadata
is buggy and creates problems.The issue is that it uses
ClassConstructor.__metadata__
as a backing storage without checking if it is its own field. This in turn means that a base class and all its sub-classes will share the same metadata store (at least with both TypeScript and Babel).Here's what happen in details:
When the code above is transpiled to ES5, both TS and Babel will create a function
B
and because of the attribute it will end up adding metadata toB
throughReflect.defineMetadata
.With you implementation, this will get polyfilled as an object "dictionary" stored in
B.__metadata__
. So far so good.Then when
C
extendsB
the static members ofB
are copied toC
. TS and Babel differ here. TS simply copies every own key ofB
toC
. Including__metadata__
. Babel sets the prototype ofC
toB
. Both have almost the same effect: readingC.__metadata__
will returnB
metadata storage.And now those classes share the same metadata storage. For me the bug was exposed by this scenario: we have a base class for some view models. Aurelia caches view strategy into metadata. So depending on the order we browse our views, we ended up with a ViewModel incorrectly using another viewmodel's view. This is very bad!
I fixed the issues (temporarily) by providing my own polyfill of
Reflect.defineMetadata
. Specifically I changed this line:https://github.com/aurelia/metadata/blob/master/src/metadata.js#L39
to:
The important difference here is that I only read the
__metadata__
if it is the class' own property, not something coming from the prototype. Note that I believe this fixes the issue when using Babel only. Because Babel uses prototypes but TS simply copies the properties over, which means the fix is not going to work with TS.For reference, rbuckton has implemented his polyfill using a
WeakMap
, which guarantees a distinct storage for each class, see:https://github.com/rbuckton/ReflectDecorators/blob/master/Reflect.js#L23
The text was updated successfully, but these errors were encountered: