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:
Miško Hevery
2018-08-29 16:34:44 -07:00
committed by Igor Minar
parent a9099e8f70
commit d5bd86ae5d
29 changed files with 245 additions and 103 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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> {