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
This commit is contained in:
@ -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<any>) 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<T>(type: any): InjectableDef<T>|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<T>(type: any): InjectorDef<T>|null {
|
||||
return type.hasOwnProperty(NG_INJECTOR_DEF) ? (type as any)[NG_INJECTOR_DEF] : null;
|
||||
}
|
@ -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<ValueProvider>({provide: String, useValue: GET_PROPERTY_NAME});
|
||||
getClosureSafeProperty<ValueProvider>({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<T>(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<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags
|
||||
if (_currentInjector === undefined) {
|
||||
throw new Error(`inject() must be called from an injection context`);
|
||||
} else if (_currentInjector === null) {
|
||||
const injectableDef: InjectableDef<T> = (token as any).ngInjectableDef;
|
||||
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
|
||||
if (injectableDef && injectableDef.providedIn == 'root') {
|
||||
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
||||
injectableDef.value;
|
||||
|
@ -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<any>| InjectableDefToken<any>).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<any>).ngInjectorDef as(InjectorDef<any>| 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<any>| InjectionToken<any>): Record<any> {
|
||||
const def = (token as InjectableType<any>).ngInjectableDef as InjectableDef<any>;
|
||||
if (def === undefined) {
|
||||
const injectableDef = getInjectableDef(token as InjectableType<any>);
|
||||
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<any>| InjectionToken<any>): Record<any>
|
||||
// no-args constructor.
|
||||
return makeRecord(() => new (token as Type<any>)());
|
||||
}
|
||||
return makeRecord(def.factory);
|
||||
return makeRecord(injectableDef.factory);
|
||||
}
|
||||
|
||||
function providerToRecord(provider: SingleProvider): Record<any> {
|
||||
|
Reference in New Issue
Block a user