From d5bd86ae5d33442504e3dde5217449645c001c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Wed, 29 Aug 2018 16:34:44 -0700 Subject: [PATCH] fix(ivy): don't accidently read the inherited definition (#25736) Create getter methods `getXXXDef` for each definition which uses `hasOwnProperty` to verify that we don't accidently read form the parent class. Fixes: #24011 Fixes: #25026 PR Close #25736 --- .../bazel/injectable_def/app/test/app_spec.ts | 17 ++++++++ packages/core/src/core_private_export.ts | 2 +- packages/core/src/di/defs.ts | 19 +++++++++ packages/core/src/di/injector.ts | 17 ++------ packages/core/src/di/r3_injector.ts | 20 +++++----- packages/core/src/ivy_switch_legacy.ts | 9 ++--- packages/core/src/metadata/directives.ts | 8 ++-- packages/core/src/render3/assert.ts | 6 ++- packages/core/src/render3/component.ts | 4 +- packages/core/src/render3/component_ref.ts | 3 +- packages/core/src/render3/definition.ts | 31 +++++++++++++-- packages/core/src/render3/di.ts | 8 ++-- .../features/inherit_definition_feature.ts | 2 + packages/core/src/render3/{jit => }/fields.ts | 17 ++++---- packages/core/src/render3/jit/directive.ts | 2 +- packages/core/src/render3/jit/injectable.ts | 7 ++-- packages/core/src/render3/jit/module.ts | 17 ++++---- packages/core/src/render3/jit/pipe.ts | 2 +- packages/core/src/render3/ng_module_ref.ts | 5 ++- packages/core/src/util/property.ts | 4 +- packages/core/src/view/ng_module.ts | 8 ++-- packages/core/src/view/services.ts | 10 +++-- .../hello_world/bundle.golden_symbols.json | 21 ++++++++++ .../hello_world_r2/bundle.golden_symbols.json | 15 +++---- .../injection/bundle.golden_symbols.json | 15 +++++-- .../bundling/todo/bundle.golden_symbols.json | 27 +++++++++++++ .../todo_r2/bundle.golden_symbols.json | 39 +++++++++++++++++-- packages/core/testing/src/r3_test_bed.ts | 7 ++-- packages/core/testing/src/test_bed.ts | 6 +-- 29 files changed, 245 insertions(+), 103 deletions(-) rename packages/core/src/render3/{jit => }/fields.ts (56%) diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts index 571252f744..d94ec80347 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts @@ -143,6 +143,23 @@ describe('ngInjectableDef Bazel Integration', () => { expect(TestBed.get(Service).value).toEqual(true); }); + it('does not override existing ngInjectableDef in case of inheritance', () => { + @Injectable({ + providedIn: 'root', + useValue: new ParentService(false), + }) + class ParentService { + constructor(public value: boolean) {} + } + + // ChildServices exteds ParentService but does not have @Injectable + class ChildService extends ParentService {} + + TestBed.configureTestingModule({}); + // We are asserting that system throws an error, rather than taking the inherited annotation. + expect(() => TestBed.get(ChildService).value).toThrowError(/ChildService/); + }); + it('NgModule injector understands requests for INJECTABLE', () => { TestBed.configureTestingModule({ providers: [{provide: 'foo', useValue: 'bar'}], diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index a9dd899762..7b0c67165e 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -13,7 +13,7 @@ export {devModeEqual as ɵdevModeEqual} from './change_detection/change_detectio export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/change_detection_util'; export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants'; export {Console as ɵConsole} from './console'; -export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef} from './di/defs'; +export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, getInjectableDef as ɵgetInjectableDef} from './di/defs'; export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector'; export {APP_ROOT as ɵAPP_ROOT} from './di/scope'; export {ivyEnabled as ɵivyEnabled} from './ivy_switch'; diff --git a/packages/core/src/di/defs.ts b/packages/core/src/di/defs.ts index 77f46d1f99..83bf53990d 100644 --- a/packages/core/src/di/defs.ts +++ b/packages/core/src/di/defs.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {NG_INJECTABLE_DEF, NG_INJECTOR_DEF} from '../render3/fields'; import {Type} from '../type'; import {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider'; @@ -160,3 +161,21 @@ export function defineInjector(options: {factory: () => any, providers?: any[], factory: options.factory, providers: options.providers || [], imports: options.imports || [], } as InjectorDef) as never; } + +/** + * Read the `ngInjectableDef` type in a way which is immune to accidentally reading inherited value. + * + * @param type type which may have `ngInjectableDef` + */ +export function getInjectableDef(type: any): InjectableDef|null { + return type.hasOwnProperty(NG_INJECTABLE_DEF) ? (type as any)[NG_INJECTABLE_DEF] : null; +} + +/** + * Read the `ngInjectorDef` type in a way which is immune to accidentally reading inherited value. + * + * @param type type which may have `ngInjectorDef` + */ +export function getInjectorDef(type: any): InjectorDef|null { + return type.hasOwnProperty(NG_INJECTOR_DEF) ? (type as any)[NG_INJECTOR_DEF] : null; +} \ No newline at end of file diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts index 0b70e82f09..d1189790fa 100644 --- a/packages/core/src/di/injector.ts +++ b/packages/core/src/di/injector.ts @@ -8,8 +8,9 @@ import {Type} from '../type'; import {stringify} from '../util'; +import {getClosureSafeProperty} from '../util/property'; -import {InjectableDef, defineInjectable} from './defs'; +import {InjectableDef, defineInjectable, getInjectableDef} from './defs'; import {resolveForwardRef} from './forward_ref'; import {InjectionToken} from './injection_token'; import {Inject, Optional, Self, SkipSelf} from './metadata'; @@ -115,9 +116,8 @@ const CIRCULAR = IDENT; const MULTI_PROVIDER_FN = function(): any[] { return Array.prototype.slice.call(arguments); }; -const GET_PROPERTY_NAME = {} as any; export const USE_VALUE = - getClosureSafeProperty({provide: String, useValue: GET_PROPERTY_NAME}); + getClosureSafeProperty({provide: String, useValue: getClosureSafeProperty}); const NG_TOKEN_PATH = 'ngTokenPath'; const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath'; const enum OptionFlags { @@ -397,15 +397,6 @@ function staticError(text: string, obj: any): Error { return new Error(formatError(text, obj)); } -function getClosureSafeProperty(objWithPropertyToExtract: T): string { - for (let key in objWithPropertyToExtract) { - if (objWithPropertyToExtract[key] === GET_PROPERTY_NAME) { - return key; - } - } - throw Error('!prop'); -} - /** * Injection flags for DI. */ @@ -462,7 +453,7 @@ export function inject(token: Type| InjectionToken, flags = InjectFlags if (_currentInjector === undefined) { throw new Error(`inject() must be called from an injection context`); } else if (_currentInjector === null) { - const injectableDef: InjectableDef = (token as any).ngInjectableDef; + const injectableDef: InjectableDef|null = getInjectableDef(token); if (injectableDef && injectableDef.providedIn == 'root') { return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() : injectableDef.value; diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 9696aa1e82..5d6395b449 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -10,9 +10,9 @@ import {OnDestroy} from '../metadata/lifecycle_hooks'; import {Type} from '../type'; import {stringify} from '../util'; -import {InjectableDef, InjectableType, InjectorDef, InjectorType, InjectorTypeWithProviders} from './defs'; +import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './defs'; import {resolveForwardRef} from './forward_ref'; -import {InjectableDefToken, InjectionToken} from './injection_token'; +import {InjectionToken} from './injection_token'; import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector'; import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider'; import {APP_ROOT} from './scope'; @@ -161,10 +161,8 @@ export class R3Injector { if (record === undefined) { // No record, but maybe the token is scoped to this injector. Look for an ngInjectableDef // with a scope matching this injector. - const def = couldBeInjectableType(token) && - (token as InjectableType| InjectableDefToken).ngInjectableDef || - undefined; - if (def !== undefined && this.injectableDefInScope(def)) { + const def = couldBeInjectableType(token) && getInjectableDef(token); + if (def && this.injectableDefInScope(def)) { // Found an ngInjectableDef and it's scoped to this injector. Pretend as if it was here // all along. record = injectableDefRecord(token); @@ -207,7 +205,7 @@ export class R3Injector { // read, so care is taken to only do the read once. // First attempt to read the ngInjectorDef. - let def = (defOrWrappedDef as InjectorType).ngInjectorDef as(InjectorDef| undefined); + let def = getInjectorDef(defOrWrappedDef); // If that's not present, then attempt to read ngModule from the InjectorDefTypeWithProviders. const ngModule = @@ -228,7 +226,7 @@ export class R3Injector { // Finally, if defOrWrappedType was an `InjectorDefTypeWithProviders`, then the actual // `InjectorDef` is on its `ngModule`. if (ngModule !== undefined) { - def = ngModule.ngInjectorDef; + def = getInjectorDef(ngModule); } // If no definition was found, it might be from exports. Remove it. @@ -331,8 +329,8 @@ export class R3Injector { } function injectableDefRecord(token: Type| InjectionToken): Record { - const def = (token as InjectableType).ngInjectableDef as InjectableDef; - if (def === undefined) { + const injectableDef = getInjectableDef(token as InjectableType); + if (injectableDef === null) { if (token instanceof InjectionToken) { throw new Error(`Token ${stringify(token)} is missing an ngInjectableDef definition.`); } @@ -340,7 +338,7 @@ function injectableDefRecord(token: Type| InjectionToken): Record // no-args constructor. return makeRecord(() => new (token as Type)()); } - return makeRecord(def.factory); + return makeRecord(injectableDef.factory); } function providerToRecord(provider: SingleProvider): Record { diff --git a/packages/core/src/ivy_switch_legacy.ts b/packages/core/src/ivy_switch_legacy.ts index 806ffb6ce3..17bdfe7f28 100644 --- a/packages/core/src/ivy_switch_legacy.ts +++ b/packages/core/src/ivy_switch_legacy.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs'; +import {InjectableType, InjectorType, defineInjectable, defineInjector, getInjectableDef} from './di/defs'; import {InjectableProvider} from './di/injectable'; import {inject, injectArgs} from './di/injector'; import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './di/provider'; @@ -59,9 +59,8 @@ function preR3NgModuleCompile(moduleType: InjectorType, metadata: NgModule) }); } -const GET_PROPERTY_NAME = {} as any; -const USE_VALUE = getClosureSafeProperty( - {provide: String, useValue: GET_PROPERTY_NAME}, GET_PROPERTY_NAME); +const USE_VALUE = + getClosureSafeProperty({provide: String, useValue: getClosureSafeProperty}); const EMPTY_ARRAY: any[] = []; function convertInjectableProviderToFactory(type: Type, provider?: InjectableProvider): () => @@ -106,7 +105,7 @@ function convertInjectableProviderToFactory(type: Type, provider?: Injectab function preR3InjectableCompile( injectableType: InjectableType, options: {providedIn?: Type| 'root' | null} & InjectableProvider): void { - if (options && options.providedIn !== undefined && injectableType.ngInjectableDef === undefined) { + if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) { injectableType.ngInjectableDef = defineInjectable({ providedIn: options.providedIn, factory: convertInjectableProviderToFactory(injectableType, options), diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index c63d1a20e4..44a6bbd0db 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -9,12 +9,15 @@ import {ChangeDetectionStrategy} from '../change_detection/constants'; import {Provider} from '../di'; import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE, R3_COMPILE_PIPE} from '../ivy_switch'; +import {NG_BASE_DEF} from '../render3/fields'; import {Type} from '../type'; import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators'; import {fillProperties} from '../util/property'; + import {ViewEncapsulation} from './view'; + /** * Type of the Directive decorator / constructor function. */ @@ -778,11 +781,6 @@ const initializeBaseDef = (target: any): void => { } }; -/** - * Used to get the minified alias of ngBaseDef - */ -const NG_BASE_DEF = Object.keys({ngBaseDef: true})[0]; - /** * Does the work of creating the `ngBaseDef` property for the @Input and @Output decorators. * @param key "inputs" or "outputs" diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index ed570acfe8..e20ec0c258 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {getComponentDef, getNgModuleDef} from './definition'; + // The functions in this file verify that the assumptions we are making // about state in an instruction are correct before implementing any logic. // They are meant only to be called in dev mode as sanity checks. @@ -62,7 +64,7 @@ export function assertComponentType( actual: any, msg: string = 'Type passed in is not ComponentType, it does not have \'ngComponentDef\' property.') { - if (!actual.ngComponentDef) { + if (!getComponentDef(actual)) { throwError(msg); } } @@ -71,7 +73,7 @@ export function assertNgModuleType( actual: any, msg: string = 'Type passed in is not NgModuleType, it does not have \'ngModuleDef\' property.') { - if (!actual.ngModuleDef) { + if (!getNgModuleDef(actual)) { throwError(msg); } } diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 94a2c0382d..b756e5aa6f 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -20,6 +20,7 @@ import {LElementNode} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {LViewData, LViewFlags, RootContext, BINDING_INDEX, INJECTOR, CONTEXT, TVIEW} from './interfaces/view'; import {stringify} from './util'; +import {getComponentDef} from './definition'; /** Options that control how the component should be bootstrapped. */ @@ -96,8 +97,7 @@ export function renderComponent( ngDevMode && assertComponentType(componentType); const rendererFactory = opts.rendererFactory || domRendererFactory3; const sanitizer = opts.sanitizer || null; - const componentDef = - (componentType as ComponentType).ngComponentDef as ComponentDefInternal; + const componentDef = getComponentDef(componentType) !; if (componentDef.type != componentType) componentDef.type = componentType; // The first index of the first selector is the tag name. diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 7fe8db11ba..8fdac685ca 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -18,6 +18,7 @@ import {Type} from '../type'; import {assertComponentType, assertDefined} from './assert'; import {LifecycleHooksFeature, createRootContext} from './component'; +import {getComponentDef} from './definition'; import {adjustBlueprintForNewNode, baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, queueHostBindingForCheck, renderEmbeddedTemplate, setHostBindings} from './instructions'; import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition'; import {LElementNode, TNode, TNodeType} from './interfaces/node'; @@ -28,7 +29,7 @@ import {RootViewRef, ViewRef} from './view_ref'; export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver { resolveComponentFactory(component: Type): viewEngine_ComponentFactory { ngDevMode && assertComponentType(component); - const componentDef = (component as ComponentType).ngComponentDef; + const componentDef = getComponentDef(component) !; return new ComponentFactory(componentDef); } } diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 2ac600cc28..2969c44b2a 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -14,6 +14,7 @@ import {NgModuleDef, NgModuleDefInternal} from '../metadata/ng_module'; import {ViewEncapsulation} from '../metadata/view'; import {Type} from '../type'; +import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields'; import {BaseDef, ComponentDefFeature, ComponentDefInternal, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDefFeature, DirectiveDefInternal, DirectiveType, DirectiveTypesOrFactory, PipeDefInternal, PipeType, PipeTypesOrFactory} from './interfaces/definition'; import {CssSelectorList, SelectorFlags} from './interfaces/projection'; @@ -328,19 +329,19 @@ export function defineComponent(componentDefinition: { export function extractDirectiveDef(type: DirectiveType& ComponentType): DirectiveDefInternal|ComponentDefInternal { - const def = type.ngComponentDef || type.ngDirectiveDef; + const def = getComponentDef(type) || getDirectiveDef(type); if (ngDevMode && !def) { throw new Error(`'${type.name}' is neither 'ComponentType' or 'DirectiveType'.`); } - return def; + return def !; } export function extractPipeDef(type: PipeType): PipeDefInternal { - const def = type.ngPipeDef; + const def = getPipeDef(type); if (ngDevMode && !def) { throw new Error(`'${type.name}' is not a 'PipeType'.`); } - return def; + return def !; } export function defineNgModule(def: {type: T} & Partial>): never { @@ -662,3 +663,25 @@ export function definePipe(pipeDef: { onDestroy: pipeDef.type.prototype.ngOnDestroy || null }) as never; } + +/** + * The following getter methods retrieve the definition form the type. Currently the retrieval + * honors inheritance, but in the future we may change the rule to require that definitions are + * explicit. This would require some sort of migration strategy. + */ + +export function getComponentDef(type: any): ComponentDefInternal|null { + return (type as any)[NG_COMPONENT_DEF] || null; +} + +export function getDirectiveDef(type: any): DirectiveDefInternal|null { + return (type as any)[NG_DIRECTIVE_DEF] || null; +} + +export function getPipeDef(type: any): PipeDefInternal|null { + return (type as any)[NG_PIPE_DEF] || null; +} + +export function getNgModuleDef(type: any): NgModuleDefInternal|null { + return (type as any)[NG_MODULE_DEF] || null; +} diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 99cfaad77b..71474845ee 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -10,6 +10,7 @@ // correctly implementing its interfaces for backwards compatibility. import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; +import {getInjectableDef, getInjectorDef} from '../di/defs'; import {InjectionToken} from '../di/injection_token'; import {InjectFlags, Injector, NullInjector, inject, setCurrentInjector} from '../di/injector'; import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; @@ -24,6 +25,7 @@ import {Type} from '../type'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; import {ComponentFactoryResolver} from './component_ref'; +import {getComponentDef, getDirectiveDef, getPipeDef} from './definition'; import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; import {VIEWS} from './interfaces/container'; import {DirectiveDefInternal, RenderFlags} from './interfaces/definition'; @@ -797,9 +799,9 @@ export function getOrCreateTemplateRef(di: LInjector): viewEngine_TemplateRef export function getFactoryOf(type: Type): ((type?: Type) => T)|null { const typeAny = type as any; - const def = typeAny.ngComponentDef || typeAny.ngDirectiveDef || typeAny.ngPipeDef || - typeAny.ngInjectableDef || typeAny.ngInjectorDef; - if (def === undefined || def.factory === undefined) { + const def = getComponentDef(typeAny) || getDirectiveDef(typeAny) || + getPipeDef(typeAny) || getInjectableDef(typeAny) || getInjectorDef(typeAny); + if (!def || def.factory === undefined) { return null; } return def.factory; diff --git a/packages/core/src/render3/features/inherit_definition_feature.ts b/packages/core/src/render3/features/inherit_definition_feature.ts index 38ad498590..878cf79559 100644 --- a/packages/core/src/render3/features/inherit_definition_feature.ts +++ b/packages/core/src/render3/features/inherit_definition_feature.ts @@ -39,11 +39,13 @@ export function InheritDefinitionFeature( while (superType) { let superDef: DirectiveDefInternal|ComponentDefInternal|undefined = undefined; if (isComponentDef(definition)) { + // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ngComponentDef || superType.ngDirectiveDef; } else { if (superType.ngComponentDef) { throw new Error('Directives cannot inherit Components'); } + // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ngDirectiveDef; } diff --git a/packages/core/src/render3/jit/fields.ts b/packages/core/src/render3/fields.ts similarity index 56% rename from packages/core/src/render3/jit/fields.ts rename to packages/core/src/render3/fields.ts index 0937656784..90c62010fc 100644 --- a/packages/core/src/render3/jit/fields.ts +++ b/packages/core/src/render3/fields.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {getClosureSafeProperty} from '../../util/property'; +import {getClosureSafeProperty} from '../util/property'; -const TARGET = {} as any; - -export const NG_COMPONENT_DEF = getClosureSafeProperty({ngComponentDef: TARGET}, TARGET); -export const NG_DIRECTIVE_DEF = getClosureSafeProperty({ngDirectiveDef: TARGET}, TARGET); -export const NG_INJECTABLE_DEF = getClosureSafeProperty({ngInjectableDef: TARGET}, TARGET); -export const NG_INJECTOR_DEF = getClosureSafeProperty({ngInjectorDef: TARGET}, TARGET); -export const NG_PIPE_DEF = getClosureSafeProperty({ngPipeDef: TARGET}, TARGET); -export const NG_MODULE_DEF = getClosureSafeProperty({ngModuleDef: TARGET}, TARGET); +export const NG_COMPONENT_DEF = getClosureSafeProperty({ngComponentDef: getClosureSafeProperty}); +export const NG_DIRECTIVE_DEF = getClosureSafeProperty({ngDirectiveDef: getClosureSafeProperty}); +export const NG_INJECTABLE_DEF = getClosureSafeProperty({ngInjectableDef: getClosureSafeProperty}); +export const NG_INJECTOR_DEF = getClosureSafeProperty({ngInjectorDef: getClosureSafeProperty}); +export const NG_PIPE_DEF = getClosureSafeProperty({ngPipeDef: getClosureSafeProperty}); +export const NG_MODULE_DEF = getClosureSafeProperty({ngModuleDef: getClosureSafeProperty}); +export const NG_BASE_DEF = getClosureSafeProperty({ngBaseDef: getClosureSafeProperty}); diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts index 13ea793982..74d482b503 100644 --- a/packages/core/src/render3/jit/directive.ts +++ b/packages/core/src/render3/jit/directive.ts @@ -13,9 +13,9 @@ import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from import {ViewEncapsulation} from '../../metadata/view'; import {Type} from '../../type'; import {stringify} from '../../util'; +import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from '../fields'; import {angularCoreEnv} from './environment'; -import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from './fields'; import {patchComponentDefWithScope, transitiveScopesFor} from './module'; import {getReflect, reflectDependencies} from './util'; diff --git a/packages/core/src/render3/jit/injectable.ts b/packages/core/src/render3/jit/injectable.ts index c2cdd0f44d..cd1ea9af77 100644 --- a/packages/core/src/render3/jit/injectable.ts +++ b/packages/core/src/render3/jit/injectable.ts @@ -12,9 +12,9 @@ import {Injectable} from '../../di/injectable'; import {ClassSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from '../../di/provider'; import {Type} from '../../type'; import {getClosureSafeProperty} from '../../util/property'; +import {NG_INJECTABLE_DEF} from '../fields'; import {angularCoreEnv} from './environment'; -import {NG_INJECTABLE_DEF} from './fields'; import {convertDependencies, reflectDependencies} from './util'; @@ -106,9 +106,8 @@ function isUseClassProvider(meta: Injectable): meta is UseClassProvider { return (meta as UseClassProvider).useClass !== undefined; } -const GET_PROPERTY_NAME = {} as any; -const USE_VALUE = getClosureSafeProperty( - {provide: String, useValue: GET_PROPERTY_NAME}, GET_PROPERTY_NAME); +const USE_VALUE = + getClosureSafeProperty({provide: String, useValue: getClosureSafeProperty}); function isUseValueProvider(meta: Injectable): meta is Injectable&ValueSansProvider { return USE_VALUE in meta; diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 7b203b5ad8..4a3e853491 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -10,10 +10,11 @@ import {Expression, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, Wrapped import {ModuleWithProviders, NgModule, NgModuleDefInternal, NgModuleTransitiveScopes} from '../../metadata/ng_module'; import {Type} from '../../type'; +import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition'; +import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_INJECTOR_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from '../fields'; import {ComponentDefInternal} from '../interfaces/definition'; import {angularCoreEnv} from './environment'; -import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_INJECTOR_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields'; import {reflectDependencies} from './util'; const EMPTY_ARRAY: Type[] = []; @@ -100,7 +101,7 @@ function setScopeOnDeclaredComponents(moduleType: Type, ngModule: NgModule) if (declaration.hasOwnProperty(NG_COMPONENT_DEF)) { // An `ngComponentDef` field exists - go ahead and patch the component directly. const component = declaration as Type& {ngComponentDef: ComponentDefInternal}; - const componentDef = component.ngComponentDef; + const componentDef = getComponentDef(component) !; patchComponentDefWithScope(componentDef, transitiveScopes); } else if ( !declaration.hasOwnProperty(NG_DIRECTIVE_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) { @@ -117,10 +118,10 @@ function setScopeOnDeclaredComponents(moduleType: Type, ngModule: NgModule) export function patchComponentDefWithScope( componentDef: ComponentDefInternal, transitiveScopes: NgModuleTransitiveScopes) { componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives) - .map(dir => dir.ngDirectiveDef || dir.ngComponentDef) + .map(dir => getDirectiveDef(dir) || getComponentDef(dir) !) .filter(def => !!def); componentDef.pipeDefs = () => - Array.from(transitiveScopes.compilation.pipes).map(pipe => pipe.ngPipeDef); + Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef(pipe) !); } /** @@ -134,7 +135,7 @@ export function transitiveScopesFor(moduleType: Type): NgModuleTransitiveS if (!isNgModule(moduleType)) { throw new Error(`${moduleType.name} does not have an ngModuleDef`); } - const def = moduleType.ngModuleDef; + const def = getNgModuleDef(moduleType) !; if (def.transitiveCompileScopes !== null) { return def.transitiveCompileScopes; @@ -154,7 +155,7 @@ export function transitiveScopesFor(moduleType: Type): NgModuleTransitiveS def.declarations.forEach(declared => { const declaredWithDefs = declared as Type& { ngPipeDef?: any; }; - if (declaredWithDefs.ngPipeDef !== undefined) { + if (getPipeDef(declaredWithDefs)) { scopes.compilation.pipes.add(declared); } else { // Either declared has an ngComponentDef or ngDirectiveDef, or it's a component which hasn't @@ -204,7 +205,7 @@ export function transitiveScopesFor(moduleType: Type): NgModuleTransitiveS scopes.compilation.pipes.add(entry); scopes.exported.pipes.add(entry); }); - } else if (exportedTyped.ngPipeDef !== undefined) { + } else if (getNgModuleDef(exportedTyped)) { scopes.exported.pipes.add(exportedTyped); } else { scopes.exported.directives.add(exportedTyped); @@ -248,5 +249,5 @@ function isModuleWithProviders(value: any): value is ModuleWithProviders { } function isNgModule(value: Type): value is Type&{ngModuleDef: NgModuleDefInternal} { - return (value as{ngModuleDef?: NgModuleDefInternal}).ngModuleDef !== undefined; + return !!getNgModuleDef(value); } diff --git a/packages/core/src/render3/jit/pipe.ts b/packages/core/src/render3/jit/pipe.ts index f5f724817d..1cbb26a684 100644 --- a/packages/core/src/render3/jit/pipe.ts +++ b/packages/core/src/render3/jit/pipe.ts @@ -10,10 +10,10 @@ import {WrappedNodeExpr, compilePipeFromMetadata, jitExpression} from '@angular/ import {Pipe} from '../../metadata/directives'; import {Type} from '../../type'; +import {NG_PIPE_DEF} from '../fields'; import {stringify} from '../util'; import {angularCoreEnv} from './environment'; -import {NG_PIPE_DEF} from './fields'; import {reflectDependencies} from './util'; export function compilePipe(type: Type, meta: Pipe): void { diff --git a/packages/core/src/render3/ng_module_ref.ts b/packages/core/src/render3/ng_module_ref.ts index 6b6aeb6198..a07b2ddd59 100644 --- a/packages/core/src/render3/ng_module_ref.ts +++ b/packages/core/src/render3/ng_module_ref.ts @@ -16,6 +16,7 @@ import {Type} from '../type'; import {stringify} from '../util'; import {assertDefined} from './assert'; import {ComponentFactoryResolver} from './component_ref'; +import {getNgModuleDef} from './definition'; export interface NgModuleType { ngModuleDef: NgModuleDefInternal; } @@ -35,12 +36,12 @@ export class NgModuleRef extends viewEngine_NgModuleRef implements Interna constructor(ngModuleType: Type, parentInjector: Injector|null) { super(); - const ngModuleDef = (ngModuleType as any as NgModuleType).ngModuleDef; + const ngModuleDef = getNgModuleDef(ngModuleType); ngDevMode && assertDefined( ngModuleDef, `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`); - this._bootstrapComponents = ngModuleDef.bootstrap; + this._bootstrapComponents = ngModuleDef !.bootstrap; const additionalProviders: StaticProvider[] = [ COMPONENT_FACTORY_RESOLVER, { provide: viewEngine_NgModuleRef, diff --git a/packages/core/src/util/property.ts b/packages/core/src/util/property.ts index da02f03f58..9baa361d94 100644 --- a/packages/core/src/util/property.ts +++ b/packages/core/src/util/property.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -export function getClosureSafeProperty(objWithPropertyToExtract: T, target: any): string { +export function getClosureSafeProperty(objWithPropertyToExtract: T): string { for (let key in objWithPropertyToExtract) { - if (objWithPropertyToExtract[key] === target) { + if (objWithPropertyToExtract[key] === getClosureSafeProperty as any) { return key; } } diff --git a/packages/core/src/view/ng_module.ts b/packages/core/src/view/ng_module.ts index bd660bf817..1ce1187665 100644 --- a/packages/core/src/view/ng_module.ts +++ b/packages/core/src/view/ng_module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {InjectableDef} from '../di/defs'; +import {InjectableDef, getInjectableDef} from '../di/defs'; import {resolveForwardRef} from '../di/forward_ref'; import {INJECTOR, InjectFlags, Injector, setCurrentInjector} from '../di/injector'; import {APP_ROOT} from '../di/scope'; @@ -97,6 +97,7 @@ export function resolveNgModuleDep( return data; } const providerDef = data._def.providersByKey[tokenKey]; + let injectableDef: InjectableDef|null; if (providerDef) { let providerInstance = data._providers[providerDef.index]; if (providerInstance === undefined) { @@ -104,9 +105,8 @@ export function resolveNgModuleDep( _createProviderInstance(data, providerDef); } return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance; - } else if (depDef.token.ngInjectableDef && targetsModule(data, depDef.token.ngInjectableDef)) { - const injectableDef = depDef.token.ngInjectableDef as InjectableDef; - const key = tokenKey; + } else if ( + (injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) { const index = data._providers.length; data._def.providersByKey[depDef.tokenKey] = { flags: NodeFlags.TypeFactoryProvider | NodeFlags.LazyProvider, diff --git a/packages/core/src/view/services.ts b/packages/core/src/view/services.ts index e25a7b29b2..c3766184e8 100644 --- a/packages/core/src/view/services.ts +++ b/packages/core/src/view/services.ts @@ -8,6 +8,7 @@ import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node'; import {Injector} from '../di'; +import {InjectableDef, getInjectableDef} from '../di/defs'; import {InjectableType} from '../di/injectable'; import {ErrorHandler} from '../error_handler'; import {isDevMode} from '../is_dev_mode'; @@ -169,8 +170,9 @@ const viewDefOverrides = new Map(); function debugOverrideProvider(override: ProviderOverride) { providerOverrides.set(override.token, override); - if (typeof override.token === 'function' && override.token.ngInjectableDef && - typeof override.token.ngInjectableDef.providedIn === 'function') { + let injectableDef: InjectableDef|null; + if (typeof override.token === 'function' && (injectableDef = getInjectableDef(override.token)) && + typeof injectableDef.providedIn === 'function') { providerOverridesWithScope.set(override.token as InjectableType, override); } } @@ -276,7 +278,7 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi }); def.modules.forEach(module => { providerOverridesWithScope.forEach((override, token) => { - if (token.ngInjectableDef.providedIn === module) { + if (getInjectableDef(token) !.providedIn === module) { hasOverrides = true; hasDeprecatedOverrides = hasDeprecatedOverrides || override.deprecatedBehavior; } @@ -304,7 +306,7 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi if (providerOverridesWithScope.size > 0) { let moduleSet = new Set(def.modules); providerOverridesWithScope.forEach((override, token) => { - if (moduleSet.has(token.ngInjectableDef.providedIn)) { + if (moduleSet.has(getInjectableDef(token) !.providedIn)) { let provider = { token: token, flags: diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index d8940de31d..13fc4f31bd 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -50,9 +50,18 @@ { "name": "NEXT" }, + { + "name": "NG_COMPONENT_DEF" + }, + { + "name": "NG_DIRECTIVE_DEF" + }, { "name": "NG_ELEMENT_ID" }, + { + "name": "NG_PIPE_DEF" + }, { "name": "NG_PROJECT_AS_ATTR_NAME" }, @@ -200,6 +209,15 @@ { "name": "getChildLNode" }, + { + "name": "getClosureSafeProperty" + }, + { + "name": "getComponentDef" + }, + { + "name": "getDirectiveDef" + }, { "name": "getLElementFromComponent" }, @@ -221,6 +239,9 @@ { "name": "getParentLNode" }, + { + "name": "getPipeDef" + }, { "name": "getPreviousOrParentNode" }, diff --git a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json index 8fa753be8a..536af7008d 100644 --- a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json @@ -710,12 +710,6 @@ { "name": "FunctionExpr" }, - { - "name": "GET_PROPERTY_NAME" - }, - { - "name": "GET_PROPERTY_NAME$2" - }, { "name": "GOOG_GET_MSG" }, @@ -1001,6 +995,9 @@ { "name": "NG_CONTENT_SELECT_ATTR" }, + { + "name": "NG_INJECTABLE_DEF" + }, { "name": "NG_NON_BINDABLE_ATTR" }, @@ -2939,9 +2936,6 @@ { "name": "getClosureSafeProperty" }, - { - "name": "getClosureSafeProperty$1" - }, { "name": "getComponentViewDefinitionFactory" }, @@ -2969,6 +2963,9 @@ { "name": "getHtmlTagDefinition" }, + { + "name": "getInjectableDef" + }, { "name": "getNgZone" }, diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index 46d7e236f9..415465fef9 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -8,9 +8,6 @@ { "name": "EMPTY_ARRAY$2" }, - { - "name": "GET_PROPERTY_NAME" - }, { "name": "INJECTOR" }, @@ -20,6 +17,12 @@ { "name": "InjectionToken" }, + { + "name": "NG_INJECTABLE_DEF" + }, + { + "name": "NG_INJECTOR_DEF" + }, { "name": "NOT_YET" }, @@ -86,6 +89,12 @@ { "name": "getClosureSafeProperty" }, + { + "name": "getInjectableDef" + }, + { + "name": "getInjectorDef" + }, { "name": "getNullInjector" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 8b1d82038d..875ce2c4b1 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -80,9 +80,21 @@ { "name": "NEXT" }, + { + "name": "NG_COMPONENT_DEF" + }, + { + "name": "NG_DIRECTIVE_DEF" + }, { "name": "NG_ELEMENT_ID" }, + { + "name": "NG_INJECTABLE_DEF" + }, + { + "name": "NG_PIPE_DEF" + }, { "name": "NG_PROJECT_AS_ATTR_NAME" }, @@ -557,18 +569,30 @@ { "name": "getClosestComponentAncestor" }, + { + "name": "getClosureSafeProperty" + }, + { + "name": "getComponentDef" + }, { "name": "getCurrentSanitizer" }, { "name": "getCurrentView" }, + { + "name": "getDirectiveDef" + }, { "name": "getInitialIndex" }, { "name": "getInitialValue" }, + { + "name": "getInjectableDef" + }, { "name": "getLElementFromComponent" }, @@ -620,6 +644,9 @@ { "name": "getParentState" }, + { + "name": "getPipeDef" + }, { "name": "getPointers" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index 4c3e67cf0b..2a3bff32ac 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -311,9 +311,6 @@ { "name": "FormatWidth" }, - { - "name": "GET_PROPERTY_NAME" - }, { "name": "GROUP_SEP" }, @@ -461,9 +458,27 @@ { "name": "NEXT" }, + { + "name": "NG_COMPONENT_DEF" + }, + { + "name": "NG_DIRECTIVE_DEF" + }, { "name": "NG_ELEMENT_ID" }, + { + "name": "NG_INJECTABLE_DEF" + }, + { + "name": "NG_INJECTOR_DEF" + }, + { + "name": "NG_MODULE_DEF" + }, + { + "name": "NG_PIPE_DEF" + }, { "name": "NG_PROJECT_AS_ATTR_NAME" }, @@ -1553,6 +1568,9 @@ { "name": "getClosureSafeProperty" }, + { + "name": "getComponentDef" + }, { "name": "getCurrencySymbol" }, @@ -1580,6 +1598,9 @@ { "name": "getDebugNode" }, + { + "name": "getDirectiveDef" + }, { "name": "getErrorLogger" }, @@ -1592,6 +1613,12 @@ { "name": "getInitialValue" }, + { + "name": "getInjectableDef" + }, + { + "name": "getInjectorDef" + }, { "name": "getLElementFromComponent" }, @@ -1655,6 +1682,9 @@ { "name": "getNamedFormat" }, + { + "name": "getNgModuleDef" + }, { "name": "getNgZone" }, @@ -1703,6 +1733,9 @@ { "name": "getParentState" }, + { + "name": "getPipeDef" + }, { "name": "getPlatform" }, diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 6958f1bd28..b1247aae6f 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, Injector, NgModule, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵNgModuleDefInternal as NgModuleDefInternal, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3DebugRendererFactory2 as Render3DebugRendererFactory2, ɵRender3NgModuleRef as NgModuleRef, ɵWRAP_RENDERER_FACTORY2 as WRAP_RENDERER_FACTORY2, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core'; +import {Component, Directive, Injector, NgModule, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵInjectableDef as InjectableDef, ɵNgModuleDefInternal as NgModuleDefInternal, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3DebugRendererFactory2 as Render3DebugRendererFactory2, ɵRender3NgModuleRef as NgModuleRef, ɵWRAP_RENDERER_FACTORY2 as WRAP_RENDERER_FACTORY2, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core'; import {ComponentFixture} from './component_fixture'; import {MetadataOverride} from './metadata_override'; @@ -314,9 +314,10 @@ export class TestBedRender3 implements Injector, TestBed { */ overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): void { + let injectableDef: InjectableDef|null; const isRoot = - (typeof token !== 'string' && token.ngInjectableDef && - token.ngInjectableDef.providedIn === 'root'); + (typeof token !== 'string' && (injectableDef = getInjectableDef(token)) && + injectableDef.providedIn === 'root'); const overrides = isRoot ? this._rootProviderOverrides : this._providerOverrides; if (provider.useFactory) { diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 71fb3605cc..c56bc25472 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ApplicationInitStatus, CompilerOptions, Component, Directive, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core'; +import {ApplicationInitStatus, CompilerOptions, Component, Directive, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵInjectableDef as InjectableDef, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵgetInjectableDef as getInjectableDef, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core'; import {AsyncTestCompleter} from './async_test_completer'; import {ComponentFixture} from './component_fixture'; @@ -549,8 +549,8 @@ export class TestBedViewEngine implements Injector, TestBed { deps?: any[], }, deprecated = false): void { - if (typeof token !== 'string' && token.ngInjectableDef && - token.ngInjectableDef.providedIn === 'root') { + let def: InjectableDef|null = null; + if (typeof token !== 'string' && (def = getInjectableDef(token)) && def.providedIn === 'root') { if (provider.useFactory) { this._rootProviderOverrides.push( {provide: token, useFactory: provider.useFactory, deps: provider.deps || []});