Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions integration/_payload-limits.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
"hello_world__closure": {
"master": {
"gzip7": {
"bundle": 32793
"bundle": 33880
},
"gzip9": {
"bundle": 32758
"bundle": 33878
},
"uncompressed": {
"bundle": 100661
"bundle": 103755
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export * from './di/metadata';

export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';

export {Injector} from './di/injector';
export {Injector, InjectorDefType, InjectorDef} from './di/injector';
export {ReflectiveInjector} from './di/reflective_injector';
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
export {StaticProvider, ValueProvider, ValueSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, Provider, TypeProvider, ClassProvider, StaticClassSansProvider, ConstructorSansProvider, ClassSansProvider, ResolvedProvider} from './di/provider';
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
export {ReflectiveKey} from './di/reflective_key';
export {InjectionToken} from './di/injection_token';
193 changes: 149 additions & 44 deletions packages/core/src/di/injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,59 @@

import {Type} from '../type';
import {stringify} from '../util';
import {getClosureSafeProperty} from '../util/property';

import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
import {Inject, InjectableType, Optional, Self, SkipSelf} from './metadata';
import {ConstructorProvider, ExistingProvider, FactoryProvider, ResolvedProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
import { EMPTY_ARRAY } from '../view/util';

export const SOURCE = '__source';
const _THROW_IF_NOT_FOUND = new Object();
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;

/**
* Static information needed to instantiate an `Injector` from
* `@NgModule` decorator.
* @experimental
*/
export interface InjectorDef<T> {
/**
* Factory method which is used to instantiate the Injector definition class.
*/
factory: () => T;

/**
* A set of providers extracted from the `@NgModule` definition.
*/
providers: StaticProvider[];

/**
* A set of other `InjectorDefType`s which have been statically
* imported from `@NgModule.imports`
*/
imports: InjectorDefType<any>[];
}


/**
* A subclass of `Type` which is annotated with `@NgModule`.
*
* A `@NgModule` decorator will transform the class into `InjectorDefType`.
*
* During AoT the `@NgModule` decorator is removed and replaced with
* static fields which turn the type into `InjectorDefType`.
*
* The `InjectorDefType` contains all of the information to build the
* injector at runtime.
*
* @experimental
*/
// see: https://github.com/Microsoft/TypeScript/issues/4881
export interface InjectorDefType<T> extends Type<T> { ngInjectorDef: InjectorDef<T>; }


class _NullInjector implements Injector {
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
if (notFoundValue === _THROW_IF_NOT_FOUND) {
Expand Down Expand Up @@ -67,9 +111,13 @@ export abstract class Injector {
/**
* @deprecated from v5 use the new signature Injector.create(options)
*/
static create(providers: StaticProvider[], parent?: Injector): Injector;
static create(providers: (StaticProvider|InjectorDefType<any>)[], parent?: Injector): Injector;

static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;
static create(options: {
providers: (StaticProvider | InjectorDefType<any>)[],
parent?: Injector,
name?: string
}): Injector;

/**
* Create a new Injector which is configure using `StaticProvider`s.
Expand All @@ -79,7 +127,8 @@ export abstract class Injector {
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
*/
static create(
options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},
options: (StaticProvider|InjectorDefType<any>)[]|
{providers: (StaticProvider | InjectorDefType<any>)[], parent?: Injector, name?: string},
parent?: Injector): Injector {
if (Array.isArray(options)) {
return new StaticInjector(options, parent);
Expand All @@ -100,8 +149,8 @@ const MULTI_PROVIDER_FN = function(): any[] {
return Array.prototype.slice.call(arguments);
};
const GET_PROPERTY_NAME = {} as any;
const USE_VALUE =
getClosureSafeProperty<ValueProvider>({provide: String, useValue: GET_PROPERTY_NAME});
const USE_VALUE = getClosureSafeProperty<ValueProvider>(
{provide: String, useValue: GET_PROPERTY_NAME}, GET_PROPERTY_NAME);
const NG_TOKEN_PATH = 'ngTokenPath';
const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
const enum OptionFlags {
Expand All @@ -119,23 +168,29 @@ export class StaticInjector implements Injector {
readonly source: string|null;

private _records: Map<any, Record>;
private _moduleTypes: InjectorDefType<any>[] = [];

constructor(
providers: StaticProvider[], parent: Injector = NULL_INJECTOR, source: string|null = null) {
providers: (StaticProvider|InjectorDefType<any>)[], parent: Injector = NULL_INJECTOR,
source: string|null = null) {
this.parent = parent;
this.source = source;
const records = this._records = new Map<any, Record>();
records.set(
Injector, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
recursivelyProcessProviders(records, providers);
recursivelyProcessProviders(records, providers, this._moduleTypes);

// Injector initialization classes instantiate eagerly
this._moduleTypes.forEach((token) => this.get(token));
}

get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
get(token: any, notFoundValue?: any): any;
get(token: any, notFoundValue?: any): any {
const record = this._records.get(token);
try {
return tryResolveToken(token, record, this._records, this.parent, notFoundValue);
return tryResolveToken(this,
token, record, this._records, this.parent, notFoundValue, this._moduleTypes);
} catch (e) {
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
if (token[SOURCE]) {
Expand All @@ -155,8 +210,17 @@ export class StaticInjector implements Injector {
}
}

type SupportedProvider =
ValueProvider | ExistingProvider | StaticClassProvider | ConstructorProvider | FactoryProvider;
let _currentInjector: Injector|null = null;

export function inject(token: any, options?: any, defaultValue?: any) {
//TODO(misko): implement the lookup similar to ivy.
if (_currentInjector == null) {
throw new Error('inject() can not be invoked outside injector context');
} else {
// TODO(misko): I wonder if there is a more efficient way to do this.
return _currentInjector.get(token);
}
}

interface Record {
fn: Function;
Expand All @@ -170,9 +234,7 @@ interface DependencyRecord {
options: number;
}

type TokenPath = Array<any>;

function resolveProvider(provider: SupportedProvider): Record {
function resolveProvider(provider: ResolvedProvider): Record {
const deps = computeDeps(provider);
let fn: Function = IDENT;
let value: any = EMPTY;
Expand Down Expand Up @@ -203,23 +265,50 @@ function multiProviderMixError(token: any) {
return staticError('Cannot mix multi providers and regular providers', token);
}

function recursivelyProcessProviders(records: Map<any, Record>, provider: StaticProvider) {
if (provider) {
provider = resolveForwardRef(provider);
if (provider instanceof Array) {
function recursivelyProcessProviders(
records: Map<any, Record>, providerOrInjectorDefType: StaticProvider | InjectorDefType<any>,
moduleTypes: InjectorDefType<any>[]) {
if (providerOrInjectorDefType) {
providerOrInjectorDefType = resolveForwardRef(providerOrInjectorDefType);
if (providerOrInjectorDefType instanceof Array) {
// if we have an array recurse into the array
for (let i = 0; i < provider.length; i++) {
recursivelyProcessProviders(records, provider[i]);
for (let i = 0; i < providerOrInjectorDefType.length; i++) {
recursivelyProcessProviders(records, providerOrInjectorDefType[i], moduleTypes);
}
} else if (
typeof providerOrInjectorDefType === 'function' &&
providerOrInjectorDefType.ngInjectorDef) {
moduleTypes.push(providerOrInjectorDefType);

// For imports. Imports should be processed before providers.
if (providerOrInjectorDefType.ngInjectorDef.imports) {
providerOrInjectorDefType.ngInjectorDef.imports.forEach(
provider => { recursivelyProcessProviders(records, provider, moduleTypes); });
}

// For providers
if (providerOrInjectorDefType.ngInjectorDef.providers) {
providerOrInjectorDefType.ngInjectorDef.providers.forEach(
provider => { recursivelyProcessProviders(records, provider, moduleTypes); });
}
} else if (typeof provider === 'function') {

// For type and deps
records.set(providerOrInjectorDefType, <Record>{
token: providerOrInjectorDefType,
deps: EMPTY_ARRAY,
useNew: false,
fn: providerOrInjectorDefType.ngInjectorDef.factory,
value: EMPTY
});
} else if (typeof providerOrInjectorDefType === 'function') {
// Functions were supported in ReflectiveInjector, but are not here. For safety give useful
// error messages
throw staticError('Function/Class not supported', provider);
} else if (provider && typeof provider === 'object' && provider.provide) {
throw staticError('Function/Class not supported', providerOrInjectorDefType);
} else if (typeof providerOrInjectorDefType === 'object' && providerOrInjectorDefType.provide) {
// At this point we have what looks like a provider: {provide: ?, ....}
let token = resolveForwardRef(provider.provide);
const resolvedProvider = resolveProvider(provider);
if (provider.multi === true) {
let token = resolveForwardRef(providerOrInjectorDefType.provide);
const resolvedProvider = resolveProvider(providerOrInjectorDefType);
if (providerOrInjectorDefType.multi === true) {
// This is a multi provider.
let multiProvider: Record|undefined = records.get(token);
if (multiProvider) {
Expand All @@ -229,15 +318,15 @@ function recursivelyProcessProviders(records: Map<any, Record>, provider: Static
} else {
// Create a placeholder factory which will look up the constituents of the multi provider.
records.set(token, multiProvider = <Record>{
token: provider.provide,
token: providerOrInjectorDefType.provide,
deps: [],
useNew: false,
fn: MULTI_PROVIDER_FN,
value: EMPTY
});
}
// Treat the provider as the token.
token = provider;
token = providerOrInjectorDefType;
multiProvider.deps.push({token, options: OptionFlags.Default});
}
const record = records.get(token);
Expand All @@ -246,16 +335,16 @@ function recursivelyProcessProviders(records: Map<any, Record>, provider: Static
}
records.set(token, resolvedProvider);
} else {
throw staticError('Unexpected provider', provider);
throw staticError('Unexpected provider', providerOrInjectorDefType);
}
}
}

function tryResolveToken(
function tryResolveToken(self: Injector,
token: any, record: Record | undefined, records: Map<any, Record>, parent: Injector,
notFoundValue: any): any {
notFoundValue: any, moduleTypes: InjectorDefType<any>[]): any {
try {
return resolveToken(token, record, records, parent, notFoundValue);
return resolveToken(self, token, record, records, parent, notFoundValue, moduleTypes);
} catch (e) {
// ensure that 'e' is of type Error.
if (!(e instanceof Error)) {
Expand All @@ -271,9 +360,9 @@ function tryResolveToken(
}
}

function resolveToken(
function resolveToken(self: Injector,
token: any, record: Record | undefined, records: Map<any, Record>, parent: Injector,
notFoundValue: any): any {
notFoundValue: any, moduleTypes: InjectorDefType<any>[]): any {
let value;
if (record) {
// If we don't have a record, this implies that we don't own the provider hence don't know how
Expand All @@ -296,6 +385,7 @@ function resolveToken(
const childRecord =
options & OptionFlags.CheckSelf ? records.get(depRecord.token) : undefined;
deps.push(tryResolveToken(
self,
// Current Token to resolve
depRecord.token,
// A record which describes how to resolve the token.
Expand All @@ -306,13 +396,36 @@ function resolveToken(
// If we don't know how to resolve dependency and we should not check parent for it,
// than pass in Null injector.
!childRecord && !(options & OptionFlags.CheckParent) ? NULL_INJECTOR : parent,
options & OptionFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND));
options & OptionFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND,
// A list of all modules
moduleTypes));
}
}
record.value = value = useNew ? new (fn as any)(...deps) : fn.apply(obj, deps);
}
} else {
const ngInjectableDef = (token as InjectableType<any>).ngInjectableDef;
if (ngInjectableDef) {
const factory = ngInjectableDef.factory;
const moduleType = ngInjectableDef.scope;
// TODO(misko): moduleType may bee null, needs special handling.
for (let i = 0; i < moduleTypes.length; ++i) {
if (moduleTypes[i] === moduleType) {
try {
_currentInjector = self;
return factory();
} finally {
_currentInjector = null;
}
}
}
}

value = parent.get(token, notFoundValue);
if (value !== notFoundValue) {
records.set(
token, <Record>{token: token, fn: IDENT, deps: EMPTY, value: value, useNew: false});
}
}
return value;
}
Expand Down Expand Up @@ -378,11 +491,3 @@ 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');
}
Loading