Skip to content
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

[BUGFIX release] Simplify factory instantiation #19088

Merged
merged 4 commits into from
Aug 10, 2020

Conversation

pzuraq
Copy link
Contributor

@pzuraq pzuraq commented Aug 8, 2020

Simplifies factory instantiation and object creation in a number of ways:

  1. Removes the side-channeling done to provide the Owner and Factory during initialization. These are instead provided using native Symbols on the props that are provided during create.
  2. Converts to using native symbols wherever possible (and in browsers that support them), so these properties are non-enumerable in general.
  3. Removes the notion of FrameworkClasses that receive the Owner as a constructor param. Any CoreObject registered on the container will now receive the owner as the first parameter.

See the individual commits for more detailed explanations of the changes.

Benchmark results:

artifact-64.pdf

Chris Garrett added 3 commits August 7, 2020 13:00
Removes `_initFactory` as a method and instead sets the owner factory
directly in an INIT_FACTORY_MAP. If the factory being initialized
extends from CoreObject, it will pull the factory off immediately and
use it.

Simplifies passing the owner to CoreObject as well. The owner is now
always passed as an injection, then plucked off and passed as the first
argument to the constructor. It is not gotten through side-channeling
the factory at all.
FrameworkClass limited the owner injection to just a few specific
classes, but the brand check required to figure out what those classes
were was costly. This removes it and updates to passing the owner to any
object that extends from CoreObject.
Converts to native symbol in as many places as we can, and uses the
benefits of native symbols non-enumerability in a few places, such as
assigning properties OWNER to the instance of CoreObject/EmberObject.

A few symbols still need to be enumerable, either because they are
passed into mixins (which do not support non-enumerable symbols) or
because we use enumerability in other ways. We should look into removing
these in the future.
@rwjblue
Copy link
Member

rwjblue commented Aug 8, 2020

$ tsc --noEmit
##[error]packages/@ember/-internals/container/lib/container.ts(270,9): error TS6133: 'proxy' is declared but its value is never read.

@pzuraq pzuraq force-pushed the refactor/simplify-factory-instantiation branch from 1ad23fe to 60453d0 Compare August 9, 2020 15:43
Copy link
Member

@rwjblue rwjblue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes generally look good to me, only a few remaining concerns from my perspective:

  • What enumerable properties remain on an object if it operates with the right protocol? Specifically, does Object.keys(EmberObject.create()) return anything, is it platform dependent? I think we need to bring back tests for this, it has been important in the past. IMHO, if we end up increasing the number of own properties this probably can't land in release (e.g. it would at least need to flow through the beta cycle).
  • Should we consider adding a version of setOwner that does not set LEGACY_OWNER (either another export, or a third argument)? I'd much prefer to never export OWNER and force all users (internal and external) through the same protocol...
  • I haven't looked at what is up with the tests.

@pzuraq pzuraq force-pushed the refactor/simplify-factory-instantiation branch from 60453d0 to 29a6513 Compare August 9, 2020 16:23
@pzuraq
Copy link
Contributor Author

pzuraq commented Aug 9, 2020

What enumerable properties remain on an object if it operates with the right protocol? Specifically, does Object.keys(EmberObject.create()) return anything, is it platform dependent? I think we need to bring back tests for this, it has been important in the past. IMHO, if we end up increasing the number of own properties this probably can't land in release (e.g. it would at least need to flow through the beta cycle).

Added a test for this. No enumerable properties are added, on any platform, for EmberObject specifically.

Should we consider adding a version of setOwner that does not set LEGACY_OWNER (either another export, or a third argument)? I'd much prefer to never export OWNER and force all users (internal and external) through the same protocol...

We also need to export the symbols for defining the getter/setter in IE11, which is how IE11 makes these properties non-enumerable on CoreObject. Definitely agree though, we should force everything through the protocol once we drop support for older platforms.

I haven't looked at what is up with the tests.

Fixed them! IE11 test was failing because we defined the non-enumerable getter/setter on EmberObject, not CoreObject, and we always set those properties on CoreObject now (so we don't have to do any checks to see if they're undefined). Moved the getter/setter to CoreObject instead, it makes sense to have them there.

@@ -470,8 +468,12 @@ function buildInjections(
typeInjections: Injection[],
injections: Injection[]
): BuildInjectionsResult {
let injectionsHash = {};

injectionsHash[OWNER as any] = container.owner!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need both OWNER and LEGACY_OWNER? Specifically, if you were using a non-Ember.CoreObject based class with a static create...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if we add LEGACY_OWNER here, it will result in an enumerable LEGACY_OWNER property on Ember.CoreObject unless we delete it or purposefully ignore it, which seems bad.

Given that Symbol works with Object.assign, I think it should be fine in most real use cases to just do this. The only one we've seen that relied on enumerability used setOwner, not the container API.

If we do think we need it though, I think we have to accept that CoreObject will need to do a bit more work to ignore it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, I do think we should do this. An idea that @pzuraq came up with in chat was to make this use setOwner (and therefore set LEGACY_OWNER) but also add a set [LEGACY_OWNER]() {} on CoreObject to "black hole" the property (and avoid enumerability).

Comment on lines +149 to +155

let type = typeof method;
if (type === 'string' || type === 'symbol') {
method = target[method as string] as Function;
}

method.apply(target, params);
(method as Function).apply(target, params);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we resolve the perf concern here? Should we do this (which is what I think I had suggested/mentioned), or go back to:

if (typeof method === 'string' || typeof method === 'symbol') {
  method = target[method] as Function;
}

@pzuraq pzuraq force-pushed the refactor/simplify-factory-instantiation branch from 29a6513 to 035f61b Compare August 10, 2020 17:32
@pzuraq pzuraq force-pushed the refactor/simplify-factory-instantiation branch from 035f61b to 56495f7 Compare August 10, 2020 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants