-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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.
- Loading branch information
Chris Garrett
committed
Sep 27, 2020
1 parent
74a99a2
commit cce03c2
Showing
7 changed files
with
152 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 0 additions & 40 deletions
40
packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts
This file was deleted.
Oops, something went wrong.
15 changes: 0 additions & 15 deletions
15
packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts
This file was deleted.
Oops, something went wrong.
140 changes: 125 additions & 15 deletions
140
packages/@ember/-internals/glimmer/lib/utils/managers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<object, ManagerWrapper<unknown>> = new WeakMap(); | ||
type ManagerDelegate = | ||
| ComponentManagerDelegate<unknown> | ||
| InternalComponentManager | ||
| ModifierManagerDelegate<unknown>; | ||
|
||
const getPrototypeOf = Object.getPrototypeOf; | ||
const COMPONENT_MANAGERS = new WeakMap< | ||
object, | ||
ManagerFactory<ComponentManagerDelegate<unknown> | InternalComponentManager> | ||
>(); | ||
|
||
export type ManagerFactory<ManagerDelegate> = (owner: Owner) => ManagerDelegate; | ||
const MODIFIER_MANAGERS = new WeakMap<object, ManagerFactory<ModifierManagerDelegate<unknown>>>(); | ||
|
||
export interface ManagerWrapper<ManagerDelegate> { | ||
factory: ManagerFactory<ManagerDelegate>; | ||
internal: boolean; | ||
type: 'component' | 'modifier'; | ||
} | ||
const MANAGER_INSTANCES: WeakMap<Owner, WeakMap<ManagerFactory, unknown>> = new WeakMap(); | ||
|
||
export type ManagerFactory<D extends ManagerDelegate = ManagerDelegate> = (owner: Owner) => D; | ||
|
||
/////////// | ||
|
||
export function setManager<ManagerDelegate>(wrapper: ManagerWrapper<ManagerDelegate>, obj: object) { | ||
MANAGERS.set(obj, wrapper); | ||
const getPrototypeOf = Object.getPrototypeOf; | ||
|
||
function setManager<Def extends object>( | ||
map: WeakMap<object, ManagerFactory>, | ||
factory: ManagerFactory, | ||
obj: Def | ||
): Def { | ||
map.set(obj, factory); | ||
return obj; | ||
} | ||
|
||
export function getManager<ManagerDelegate>(obj: object): Option<ManagerWrapper<ManagerDelegate>> { | ||
function getManager<D extends ManagerDelegate>( | ||
map: WeakMap<object, ManagerFactory<D>>, | ||
obj: object | ||
): ManagerFactory<D> | 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<ManagerDelegate>; | ||
return manager; | ||
} | ||
|
||
pointer = getPrototypeOf(pointer); | ||
} | ||
|
||
return null; | ||
return undefined; | ||
} | ||
|
||
function getManagerInstanceForOwner<D extends ManagerDelegate>( | ||
owner: Owner, | ||
factory: ManagerFactory<D> | ||
): 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<ModifierManagerDelegate<unknown>>, | ||
definition: object | ||
) { | ||
return setManager(MODIFIER_MANAGERS, factory, definition); | ||
} | ||
|
||
export function getModifierManager( | ||
owner: Owner, | ||
definition: object | ||
): ModifierManagerDelegate<unknown> | undefined { | ||
const factory = getManager(MODIFIER_MANAGERS, definition); | ||
|
||
if (factory !== undefined) { | ||
return getManagerInstanceForOwner(owner, factory); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
export function setComponentManager( | ||
stringOrFunction: | ||
| string | ||
| ManagerFactory<ComponentManagerDelegate<unknown> | InternalComponentManager>, | ||
obj: object | ||
) { | ||
let factory: ManagerFactory<ComponentManagerDelegate<unknown> | 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<ComponentManagerDelegate<unknown> | InternalComponentManager>( | ||
`component-manager:${stringOrFunction}` | ||
)!; | ||
}; | ||
} else { | ||
factory = stringOrFunction as ManagerFactory< | ||
ComponentManagerDelegate<unknown> | InternalComponentManager | ||
>; | ||
} | ||
|
||
return setManager(COMPONENT_MANAGERS, factory, obj); | ||
} | ||
|
||
export function getComponentManager( | ||
owner: Owner, | ||
definition: object | ||
): ComponentManagerDelegate<unknown> | InternalComponentManager | undefined { | ||
const factory = getManager<ComponentManagerDelegate<unknown> | InternalComponentManager>( | ||
COMPONENT_MANAGERS, | ||
definition | ||
); | ||
|
||
if (factory !== undefined) { | ||
return getManagerInstanceForOwner(owner, factory); | ||
} | ||
|
||
return undefined; | ||
} |