From cce03c210034e58406c35baae135c1d11d1e76e4 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Sun, 27 Sep 2020 15:05:15 -0700 Subject: [PATCH] [REFACTOR] Split managers into multiple maps This refactors managers to split different types of managers into different maps per-type. This allows more than one type of manager to be associated with a given definition. For instance, this is a useful capability for helper and modifier managers to have, since helpers and modifiers have a lot in common and may end up being able to share implementations. This also upstreams the deduplication logic from Glimmer.js, which was more stringent than in Ember. In Ember, we currently create one instance of a given manager _per-component-definition_, whereas in Glimmer.js, we create one _per-factory/owner combination_. Given that managers are generally encouraged to be mostly-stateless in general, this change shouldn't be a problem, and should be slightly more performant in apps with many component definitions. --- packages/@ember/-internals/glimmer/index.ts | 8 +- .../lib/component-managers/internal.ts | 9 +- .../glimmer/lib/components/input.ts | 11 +- .../@ember/-internals/glimmer/lib/resolver.ts | 28 ++-- .../lib/utils/custom-component-manager.ts | 40 ----- .../lib/utils/custom-modifier-manager.ts | 15 -- .../-internals/glimmer/lib/utils/managers.ts | 140 ++++++++++++++++-- 7 files changed, 152 insertions(+), 99 deletions(-) delete mode 100644 packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts delete mode 100644 packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts diff --git a/packages/@ember/-internals/glimmer/index.ts b/packages/@ember/-internals/glimmer/index.ts index beb9b2f0b23..0a65246c8a2 100644 --- a/packages/@ember/-internals/glimmer/index.ts +++ b/packages/@ember/-internals/glimmer/index.ts @@ -404,8 +404,12 @@ export { INVOKE } from './lib/helpers/action'; export { default as OutletView } from './lib/views/outlet'; export { OutletState } from './lib/utils/outlet'; export { capabilities } from './lib/component-managers/custom'; -export { setComponentManager, getComponentManager } from './lib/utils/custom-component-manager'; -export { setModifierManager, getModifierManager } from './lib/utils/custom-modifier-manager'; +export { + setComponentManager, + getComponentManager, + setModifierManager, + getModifierManager, +} from './lib/utils/managers'; export { capabilities as modifierCapabilities } from './lib/modifiers/custom'; export { isSerializationFirstNode } from './lib/utils/serialization-first-node-helpers'; export { setComponentTemplate, getComponentTemplate } from './lib/utils/component-template'; diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/internal.ts b/packages/@ember/-internals/glimmer/lib/component-managers/internal.ts index 42a0509a325..67b4dd15812 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/internal.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/internal.ts @@ -12,7 +12,7 @@ import { } from '@glimmer/interfaces'; import { createConstRef, isConstRef, Reference, valueForRef } from '@glimmer/reference'; import { registerDestructor } from '@glimmer/runtime'; -import { unwrapTemplate } from '@glimmer/util'; +import { _WeakSet, unwrapTemplate } from '@glimmer/util'; import InternalComponent from '../components/internal'; import { EmberVMEnvironment } from '../environment'; import RuntimeResolver from '../resolver'; @@ -57,6 +57,12 @@ export class InternalComponentDefinition } } +const INTERNAL_MANAGERS = new _WeakSet(); + +export function isInternalManager(manager: object): manager is InternalManager { + return INTERNAL_MANAGERS.has(manager); +} + export default class InternalManager extends AbstractComponentManager implements WithStaticLayout { @@ -66,6 +72,7 @@ export default class InternalManager constructor(private owner: Owner, private name: string) { super(); + INTERNAL_MANAGERS.add(this); } getCapabilities(): ComponentCapabilities { diff --git a/packages/@ember/-internals/glimmer/lib/components/input.ts b/packages/@ember/-internals/glimmer/lib/components/input.ts index fe66a32679c..bb0a64628a7 100644 --- a/packages/@ember/-internals/glimmer/lib/components/input.ts +++ b/packages/@ember/-internals/glimmer/lib/components/input.ts @@ -2,7 +2,7 @@ @module @ember/component */ import InternalManager from '../component-managers/internal'; -import { setManager } from '../utils/managers'; +import { setComponentManager } from '../utils/managers'; import InternalComponent from './internal'; /** @@ -117,13 +117,6 @@ export default class Input extends InternalComponent { } } -setManager( - { - factory: InternalManager.for('input'), - internal: true, - type: 'component', - }, - Input -); +setComponentManager(InternalManager.for('input'), Input); Input.toString = () => '@ember/component/input'; diff --git a/packages/@ember/-internals/glimmer/lib/resolver.ts b/packages/@ember/-internals/glimmer/lib/resolver.ts index a2f34872567..7ae05061e22 100644 --- a/packages/@ember/-internals/glimmer/lib/resolver.ts +++ b/packages/@ember/-internals/glimmer/lib/resolver.ts @@ -19,10 +19,8 @@ import { import { PartialDefinitionImpl } from '@glimmer/opcode-compiler'; import { getDynamicVar, ModifierDefinition, registerDestructor } from '@glimmer/runtime'; import { CurlyComponentDefinition } from './component-managers/curly'; -import { CustomManagerDefinition, ManagerDelegate } from './component-managers/custom'; -import InternalComponentManager, { - InternalComponentDefinition, -} from './component-managers/internal'; +import { CustomManagerDefinition } from './component-managers/custom'; +import { InternalComponentDefinition, isInternalManager } from './component-managers/internal'; import { TemplateOnlyComponentDefinition } from './component-managers/template-only'; import InternalComponent from './components/internal'; import { isClassHelper, isHelperFactory } from './helper'; @@ -44,14 +42,13 @@ import { default as queryParams } from './helpers/query-param'; import { default as readonly } from './helpers/readonly'; import { default as unbound } from './helpers/unbound'; import ActionModifierManager from './modifiers/action'; -import { CustomModifierDefinition, ModifierManagerDelegate } from './modifiers/custom'; +import { CustomModifierDefinition } from './modifiers/custom'; import OnModifierManager from './modifiers/on'; import { mountHelper } from './syntax/mount'; import { outletHelper } from './syntax/outlet'; import { Factory as TemplateFactory, OwnedTemplate } from './template'; import { getComponentTemplate } from './utils/component-template'; -import { getModifierManager } from './utils/custom-modifier-manager'; -import { getManager } from './utils/managers'; +import { getComponentManager, getModifierManager } from './utils/managers'; import { createHelperRef } from './utils/references'; function instrumentationPayload(name: string) { @@ -404,10 +401,9 @@ export default class RuntimeResolverImpl implements RuntimeResolver(`modifier:${name}`); if (modifier !== undefined) { - let managerFactory = getModifierManager>(modifier.class); - let manager = managerFactory!(owner); + let manager = getModifierManager(owner, modifier.class!); - return new CustomModifierDefinition(name, modifier, manager, this.isInteractive); + return new CustomModifierDefinition(name, modifier, manager!, this.isInteractive); } } @@ -465,16 +461,14 @@ export default class RuntimeResolverImpl implements RuntimeResolver, + manager, layout !== undefined ? layout : owner.lookup(P`template:components/-default`)!(owner) diff --git a/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts b/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts deleted file mode 100644 index e4ab147e8cc..00000000000 --- a/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Owner } from '@ember/-internals/owner'; -import { deprecate } from '@ember/debug'; -import { COMPONENT_MANAGER_STRING_LOOKUP } from '@ember/deprecated-features'; -import { ManagerDelegate } from '../component-managers/custom'; -import { getManager, ManagerFactory, setManager } from './managers'; - -export function setComponentManager( - stringOrFunction: string | ManagerFactory>, - obj: any -) { - let factory: ManagerFactory; - if (COMPONENT_MANAGER_STRING_LOOKUP && typeof stringOrFunction === 'string') { - deprecate( - 'Passing the name of the component manager to "setupComponentManager" is deprecated. Please pass a function that produces an instance of the manager.', - false, - { - id: 'deprecate-string-based-component-manager', - until: '4.0.0', - url: 'https://emberjs.com/deprecations/v3.x/#toc_component-manager-string-lookup', - } - ); - factory = function(owner: Owner) { - return owner.lookup(`component-manager:${stringOrFunction}`); - }; - } else { - factory = stringOrFunction as ManagerFactory; - } - - return setManager({ factory, internal: false, type: 'component' }, obj); -} - -export function getComponentManager(obj: any): undefined | ManagerFactory> { - let wrapper = getManager>(obj); - - if (wrapper && !wrapper.internal && wrapper.type === 'component') { - return wrapper.factory; - } else { - return undefined; - } -} diff --git a/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts b/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts deleted file mode 100644 index 8a31a370720..00000000000 --- a/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getManager, ManagerFactory, setManager } from './managers'; - -export function setModifierManager(factory: ManagerFactory, obj: any) { - return setManager({ factory, internal: false, type: 'modifier' }, obj); -} - -export function getModifierManager(obj: any): undefined | ManagerFactory { - let wrapper = getManager(obj); - - if (wrapper && !wrapper.internal && wrapper.type === 'modifier') { - return wrapper.factory; - } else { - return undefined; - } -} diff --git a/packages/@ember/-internals/glimmer/lib/utils/managers.ts b/packages/@ember/-internals/glimmer/lib/utils/managers.ts index 9abdd6d78f2..b89ea86ae07 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/managers.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/managers.ts @@ -1,34 +1,144 @@ import { Owner } from '@ember/-internals/owner'; -import { Option } from '@glimmer/interfaces'; +import { deprecate } from '@ember/debug'; +import { COMPONENT_MANAGER_STRING_LOOKUP } from '@ember/deprecated-features'; +import { ManagerDelegate as ComponentManagerDelegate } from '../component-managers/custom'; +import InternalComponentManager from '../component-managers/internal'; +import { ModifierManagerDelegate } from '../modifiers/custom'; -const MANAGERS: WeakMap> = new WeakMap(); +type ManagerDelegate = + | ComponentManagerDelegate + | InternalComponentManager + | ModifierManagerDelegate; -const getPrototypeOf = Object.getPrototypeOf; +const COMPONENT_MANAGERS = new WeakMap< + object, + ManagerFactory | InternalComponentManager> +>(); -export type ManagerFactory = (owner: Owner) => ManagerDelegate; +const MODIFIER_MANAGERS = new WeakMap>>(); -export interface ManagerWrapper { - factory: ManagerFactory; - internal: boolean; - type: 'component' | 'modifier'; -} +const MANAGER_INSTANCES: WeakMap> = new WeakMap(); + +export type ManagerFactory = (owner: Owner) => D; + +/////////// -export function setManager(wrapper: ManagerWrapper, obj: object) { - MANAGERS.set(obj, wrapper); +const getPrototypeOf = Object.getPrototypeOf; + +function setManager( + map: WeakMap, + factory: ManagerFactory, + obj: Def +): Def { + map.set(obj, factory); return obj; } -export function getManager(obj: object): Option> { +function getManager( + map: WeakMap>, + obj: object +): ManagerFactory | undefined { let pointer = obj; while (pointer !== undefined && pointer !== null) { - let manager = MANAGERS.get(pointer); + const manager = map.get(pointer); if (manager !== undefined) { - return manager as ManagerWrapper; + return manager; } pointer = getPrototypeOf(pointer); } - return null; + return undefined; +} + +function getManagerInstanceForOwner( + owner: Owner, + factory: ManagerFactory +): D { + let managers = MANAGER_INSTANCES.get(owner); + + if (managers === undefined) { + managers = new WeakMap(); + MANAGER_INSTANCES.set(owner, managers); + } + + let instance = managers.get(factory); + + if (instance === undefined) { + instance = factory(owner); + managers.set(factory, instance!); + } + + // We know for sure that it's the correct type at this point, but TS can't know + return instance as D; +} + +/////////// + +export function setModifierManager( + factory: ManagerFactory>, + definition: object +) { + return setManager(MODIFIER_MANAGERS, factory, definition); +} + +export function getModifierManager( + owner: Owner, + definition: object +): ModifierManagerDelegate | undefined { + const factory = getManager(MODIFIER_MANAGERS, definition); + + if (factory !== undefined) { + return getManagerInstanceForOwner(owner, factory); + } + + return undefined; +} + +export function setComponentManager( + stringOrFunction: + | string + | ManagerFactory | InternalComponentManager>, + obj: object +) { + let factory: ManagerFactory | InternalComponentManager>; + if (COMPONENT_MANAGER_STRING_LOOKUP && typeof stringOrFunction === 'string') { + deprecate( + 'Passing the name of the component manager to "setupComponentManager" is deprecated. Please pass a function that produces an instance of the manager.', + false, + { + id: 'deprecate-string-based-component-manager', + until: '4.0.0', + url: 'https://emberjs.com/deprecations/v3.x/#toc_component-manager-string-lookup', + } + ); + factory = function(owner: Owner) { + return owner.lookup | InternalComponentManager>( + `component-manager:${stringOrFunction}` + )!; + }; + } else { + factory = stringOrFunction as ManagerFactory< + ComponentManagerDelegate | InternalComponentManager + >; + } + + return setManager(COMPONENT_MANAGERS, factory, obj); +} + +export function getComponentManager( + owner: Owner, + definition: object +): ComponentManagerDelegate | InternalComponentManager | undefined { + const factory = getManager | InternalComponentManager>( + COMPONENT_MANAGERS, + definition + ); + + if (factory !== undefined) { + return getManagerInstanceForOwner(owner, factory); + } + + return undefined; }