refactor(core): separate reflective injector from Injector interface

BREAKING CHANGE:
- Injector was renamed into `ReflectiveInjector`,
  as `Injector` is only an abstract class with one method on it
- `Injector.getOptional()` was changed into `Injector.get(token, notFoundValue)`
  to make implementing injectors simpler
- `ViewContainerRef.createComponent` now takes an `Injector`
  instead of `ResolvedProviders`. If a reflective injector
  should be used, create one before calling this method.
  (e.g. via `ReflectiveInjector.resolveAndCreate(…)`.
This commit is contained in:
Tobias Bosch
2016-04-14 12:35:24 -07:00
parent efbd446d18
commit 0a7d10ba55
46 changed files with 1790 additions and 1719 deletions
@@ -11,7 +11,7 @@ import {
} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {BaseException} from 'angular2/src/facade/exceptions';
import {NoAnnotationError} from 'angular2/src/core/di/exceptions';
import {NoAnnotationError} from 'angular2/src/core/di/reflective_exceptions';
import * as cpl from './compile_metadata';
import * as md from 'angular2/src/core/metadata/directives';
import * as dimd from 'angular2/src/core/metadata/di';
@@ -27,7 +27,11 @@ import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_di
import {MODULE_SUFFIX} from './util';
import {assertArrayOfStrings} from './assertions';
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
import {Provider, constructDependencies, Dependency} from 'angular2/src/core/di/provider';
import {Provider} from 'angular2/src/core/di/provider';
import {
constructDependencies,
ReflectiveDependency
} from 'angular2/src/core/di/reflective_provider';
import {
OptionalMetadata,
SelfMetadata,
@@ -185,7 +189,7 @@ export class RuntimeMetadataResolver {
getDependenciesMetadata(typeOrFunc: Type | Function,
dependencies: any[]): cpl.CompileDiDependencyMetadata[] {
var deps: Dependency[];
var deps: ReflectiveDependency[];
try {
deps = constructDependencies(typeOrFunc, dependencies);
} catch (e) {
@@ -31,8 +31,11 @@ export function getPropertyInView(property: o.Expression, viewPath: CompileView[
export function injectFromViewParentInjector(token: CompileTokenMetadata,
optional: boolean): o.Expression {
var method = optional ? 'getOptional' : 'get';
return o.THIS_EXPR.prop('parentInjector').callMethod(method, [createDiTokenExpression(token)]);
var args = [createDiTokenExpression(token)];
if (optional) {
args.push(o.NULL_EXPR);
}
return o.THIS_EXPR.prop('parentInjector').callMethod('get', args);
}
export function getViewFactoryName(component: CompileDirectiveMetadata,
+10 -9
View File
@@ -7,7 +7,7 @@ import {
print,
IS_DART
} from 'angular2/src/facade/lang';
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
import {provide, Provider, Injector, ReflectiveInjector, OpaqueToken} from 'angular2/src/core/di';
import {
APP_COMPONENT_REF_PROMISE,
APP_COMPONENT,
@@ -48,7 +48,7 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
() => { appRef._unloadComponent(ref); })
.then((componentRef) => {
ref = componentRef;
var testability = injector.getOptional(Testability);
var testability = injector.get(Testability, null);
if (isPresent(testability)) {
injector.get(TestabilityRegistry)
.registerApplication(componentRef.location.nativeElement, testability);
@@ -115,7 +115,7 @@ export function disposePlatform(): void {
function _createPlatform(providers?: Array<Type | Provider | any[]>): PlatformRef {
_platformProviders = providers;
let injector = Injector.resolveAndCreate(providers);
let injector = ReflectiveInjector.resolveAndCreate(providers);
_platform = new PlatformRef_(injector, () => {
_platform = null;
_platformProviders = null;
@@ -125,7 +125,7 @@ function _createPlatform(providers?: Array<Type | Provider | any[]>): PlatformRe
}
function _runPlatformInitializers(injector: Injector): void {
let inits: Function[] = <Function[]>injector.getOptional(PLATFORM_INITIALIZER);
let inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
if (isPresent(inits)) inits.forEach(init => init());
}
@@ -201,11 +201,11 @@ export class PlatformRef_ extends PlatformRef {
/** @internal */
_disposeListeners: Function[] = [];
constructor(private _injector: Injector, private _dispose: () => void) { super(); }
constructor(private _injector: ReflectiveInjector, private _dispose: () => void) { super(); }
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
get injector(): Injector { return this._injector; }
get injector(): ReflectiveInjector { return this._injector; }
application(providers: Array<Type | Provider | any[]>): ApplicationRef {
var app = this._initApp(createNgZone(), providers);
@@ -239,7 +239,7 @@ export class PlatformRef_ extends PlatformRef {
private _initApp(zone: NgZone,
providers: Array<Type | Provider | any[]>): Promise<ApplicationRef>|
ApplicationRef {
var injector: Injector;
var injector: ReflectiveInjector;
var app: ApplicationRef;
zone.run(() => {
providers = ListWrapper.concat(providers, [
@@ -283,7 +283,7 @@ export class PlatformRef_ extends PlatformRef {
}
function _runAppInitializers(injector: Injector): Promise<any> {
let inits: Function[] = injector.getOptional(APP_INITIALIZER);
let inits: Function[] = injector.get(APP_INITIALIZER, null);
let promises: Promise<any>[] = [];
if (isPresent(inits)) {
inits.forEach(init => {
@@ -390,7 +390,8 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
private _enforceNoNewChanges: boolean = false;
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
constructor(private _platform: PlatformRef_, private _zone: NgZone,
private _injector: ReflectiveInjector) {
super();
if (isPresent(this._zone)) {
ObservableWrapper.subscribe(this._zone.onMicrotaskEmpty,
+10 -6
View File
@@ -20,19 +20,23 @@ export * from './di/decorators';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
export {Injector} from './di/injector';
export {ReflectiveInjector} from './di/reflective_injector';
export {
Binding,
ProviderBuilder,
ResolvedBinding,
ResolvedFactory,
Dependency,
bind,
Provider,
ResolvedProvider,
provide
} from './di/provider';
export {Key} from './di/key';
export {
ResolvedReflectiveBinding,
ResolvedReflectiveFactory,
ReflectiveDependency,
ResolvedReflectiveProvider
} from './di/reflective_provider';
export {ReflectiveKey} from './di/reflective_key';
export {
NoProviderError,
AbstractProviderError,
@@ -41,5 +45,5 @@ export {
InvalidProviderError,
NoAnnotationError,
OutOfBoundsError
} from './di/exceptions';
} from './di/reflective_exceptions';
export {OpaqueToken} from './di/opaque_token';
+12 -906
View File
@@ -1,442 +1,23 @@
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {
ResolvedProvider,
Provider,
Dependency,
ProviderBuilder,
ResolvedFactory,
provide,
resolveProviders
} from './provider';
import {
AbstractProviderError,
NoProviderError,
CyclicDependencyError,
InstantiationError,
InvalidProviderError,
OutOfBoundsError
} from './exceptions';
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
import {Key} from './key';
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
var __unused: Type; // avoid unused import when Type union types are erased
// Threshold for the dynamic version
const _MAX_CONSTRUCTION_COUNTER = 10;
export const UNDEFINED: Object = CONST_EXPR(new Object());
export interface ProtoInjectorStrategy {
getProviderAtIndex(index: number): ResolvedProvider;
createInjectorStrategy(inj: Injector_): InjectorStrategy;
}
export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
provider0: ResolvedProvider = null;
provider1: ResolvedProvider = null;
provider2: ResolvedProvider = null;
provider3: ResolvedProvider = null;
provider4: ResolvedProvider = null;
provider5: ResolvedProvider = null;
provider6: ResolvedProvider = null;
provider7: ResolvedProvider = null;
provider8: ResolvedProvider = null;
provider9: ResolvedProvider = null;
keyId0: number = null;
keyId1: number = null;
keyId2: number = null;
keyId3: number = null;
keyId4: number = null;
keyId5: number = null;
keyId6: number = null;
keyId7: number = null;
keyId8: number = null;
keyId9: number = null;
constructor(protoEI: ProtoInjector, providers: ResolvedProvider[]) {
var length = providers.length;
if (length > 0) {
this.provider0 = providers[0];
this.keyId0 = providers[0].key.id;
}
if (length > 1) {
this.provider1 = providers[1];
this.keyId1 = providers[1].key.id;
}
if (length > 2) {
this.provider2 = providers[2];
this.keyId2 = providers[2].key.id;
}
if (length > 3) {
this.provider3 = providers[3];
this.keyId3 = providers[3].key.id;
}
if (length > 4) {
this.provider4 = providers[4];
this.keyId4 = providers[4].key.id;
}
if (length > 5) {
this.provider5 = providers[5];
this.keyId5 = providers[5].key.id;
}
if (length > 6) {
this.provider6 = providers[6];
this.keyId6 = providers[6].key.id;
}
if (length > 7) {
this.provider7 = providers[7];
this.keyId7 = providers[7].key.id;
}
if (length > 8) {
this.provider8 = providers[8];
this.keyId8 = providers[8].key.id;
}
if (length > 9) {
this.provider9 = providers[9];
this.keyId9 = providers[9].key.id;
}
}
getProviderAtIndex(index: number): ResolvedProvider {
if (index == 0) return this.provider0;
if (index == 1) return this.provider1;
if (index == 2) return this.provider2;
if (index == 3) return this.provider3;
if (index == 4) return this.provider4;
if (index == 5) return this.provider5;
if (index == 6) return this.provider6;
if (index == 7) return this.provider7;
if (index == 8) return this.provider8;
if (index == 9) return this.provider9;
throw new OutOfBoundsError(index);
}
createInjectorStrategy(injector: Injector_): InjectorStrategy {
return new InjectorInlineStrategy(injector, this);
}
}
export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
keyIds: number[];
constructor(protoInj: ProtoInjector, public providers: ResolvedProvider[]) {
var len = providers.length;
this.keyIds = ListWrapper.createFixedSize(len);
for (var i = 0; i < len; i++) {
this.keyIds[i] = providers[i].key.id;
}
}
getProviderAtIndex(index: number): ResolvedProvider {
if (index < 0 || index >= this.providers.length) {
throw new OutOfBoundsError(index);
}
return this.providers[index];
}
createInjectorStrategy(ei: Injector_): InjectorStrategy {
return new InjectorDynamicStrategy(this, ei);
}
}
export class ProtoInjector {
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
return new ProtoInjector(providers);
}
/** @internal */
_strategy: ProtoInjectorStrategy;
numberOfProviders: number;
constructor(providers: ResolvedProvider[]) {
this.numberOfProviders = providers.length;
this._strategy = providers.length > _MAX_CONSTRUCTION_COUNTER ?
new ProtoInjectorDynamicStrategy(this, providers) :
new ProtoInjectorInlineStrategy(this, providers);
}
getProviderAtIndex(index: number): ResolvedProvider {
return this._strategy.getProviderAtIndex(index);
}
}
export interface InjectorStrategy {
getObjByKeyId(keyId: number): any;
getObjAtIndex(index: number): any;
getMaxNumberOfObjects(): number;
resetConstructionCounter(): void;
instantiateProvider(provider: ResolvedProvider): any;
}
export class InjectorInlineStrategy implements InjectorStrategy {
obj0: any = UNDEFINED;
obj1: any = UNDEFINED;
obj2: any = UNDEFINED;
obj3: any = UNDEFINED;
obj4: any = UNDEFINED;
obj5: any = UNDEFINED;
obj6: any = UNDEFINED;
obj7: any = UNDEFINED;
obj8: any = UNDEFINED;
obj9: any = UNDEFINED;
constructor(public injector: Injector_, public protoStrategy: ProtoInjectorInlineStrategy) {}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedProvider): any { return this.injector._new(provider); }
getObjByKeyId(keyId: number): any {
var p = this.protoStrategy;
var inj = this.injector;
if (p.keyId0 === keyId) {
if (this.obj0 === UNDEFINED) {
this.obj0 = inj._new(p.provider0);
}
return this.obj0;
}
if (p.keyId1 === keyId) {
if (this.obj1 === UNDEFINED) {
this.obj1 = inj._new(p.provider1);
}
return this.obj1;
}
if (p.keyId2 === keyId) {
if (this.obj2 === UNDEFINED) {
this.obj2 = inj._new(p.provider2);
}
return this.obj2;
}
if (p.keyId3 === keyId) {
if (this.obj3 === UNDEFINED) {
this.obj3 = inj._new(p.provider3);
}
return this.obj3;
}
if (p.keyId4 === keyId) {
if (this.obj4 === UNDEFINED) {
this.obj4 = inj._new(p.provider4);
}
return this.obj4;
}
if (p.keyId5 === keyId) {
if (this.obj5 === UNDEFINED) {
this.obj5 = inj._new(p.provider5);
}
return this.obj5;
}
if (p.keyId6 === keyId) {
if (this.obj6 === UNDEFINED) {
this.obj6 = inj._new(p.provider6);
}
return this.obj6;
}
if (p.keyId7 === keyId) {
if (this.obj7 === UNDEFINED) {
this.obj7 = inj._new(p.provider7);
}
return this.obj7;
}
if (p.keyId8 === keyId) {
if (this.obj8 === UNDEFINED) {
this.obj8 = inj._new(p.provider8);
}
return this.obj8;
}
if (p.keyId9 === keyId) {
if (this.obj9 === UNDEFINED) {
this.obj9 = inj._new(p.provider9);
}
return this.obj9;
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index == 0) return this.obj0;
if (index == 1) return this.obj1;
if (index == 2) return this.obj2;
if (index == 3) return this.obj3;
if (index == 4) return this.obj4;
if (index == 5) return this.obj5;
if (index == 6) return this.obj6;
if (index == 7) return this.obj7;
if (index == 8) return this.obj8;
if (index == 9) return this.obj9;
throw new OutOfBoundsError(index);
}
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
}
export class InjectorDynamicStrategy implements InjectorStrategy {
objs: any[];
constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector_) {
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
ListWrapper.fill(this.objs, UNDEFINED);
}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedProvider): any { return this.injector._new(provider); }
getObjByKeyId(keyId: number): any {
var p = this.protoStrategy;
for (var i = 0; i < p.keyIds.length; i++) {
if (p.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
this.objs[i] = this.injector._new(p.providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index < 0 || index >= this.objs.length) {
throw new OutOfBoundsError(index);
}
return this.objs[index];
}
getMaxNumberOfObjects(): number { return this.objs.length; }
}
/**
* Used to provide dependencies that cannot be easily expressed as providers.
*/
export interface DependencyProvider {
getDependency(injector: Injector, provider: ResolvedProvider, dependency: Dependency): any;
}
const _THROW_IF_NOT_FOUND = CONST_EXPR(new Object());
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
export abstract class Injector {
/**
* Turns an array of provider definitions into an array of resolved providers.
*
* A resolution is a process of flattening multiple nested arrays and converting individual
* providers into an array of {@link ResolvedProvider}s.
*
* ### Example ([live demo](http://plnkr.co/edit/AiXTHi?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var providers = Injector.resolve([Car, [[Engine]]]);
*
* expect(providers.length).toEqual(2);
*
* expect(providers[0] instanceof ResolvedProvider).toBe(true);
* expect(providers[0].key.displayName).toBe("Car");
* expect(providers[0].dependencies.length).toEqual(1);
* expect(providers[0].factory).toBeDefined();
*
* expect(providers[1].key.displayName).toBe("Engine");
* });
* ```
*
* See {@link Injector#fromResolvedProviders} for more info.
*/
static resolve(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
return resolveProviders(providers);
}
/**
* Resolves an array of providers and creates an injector from those providers.
*
* The passed-in providers can be an array of `Type`, {@link Provider},
* or a recursive array of more providers.
*
* ### Example ([live demo](http://plnkr.co/edit/ePOccA?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Car, Engine]);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*
* This function is slower than the corresponding `fromResolvedProviders`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
*/
static resolveAndCreate(providers: Array<Type | Provider | any[]>): Injector {
var resolvedProviders = Injector.resolve(providers);
return Injector.fromResolvedProviders(resolvedProviders);
}
/**
* Creates an injector from previously resolved providers.
*
* This API is the recommended way to construct injectors in performance-sensitive parts.
*
* ### Example ([live demo](http://plnkr.co/edit/KrSMci?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var providers = Injector.resolve([Car, Engine]);
* var injector = Injector.fromResolvedProviders(providers);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*/
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
return new Injector_(ProtoInjector.fromResolvedProviders(providers));
}
/**
* @deprecated
*/
static fromResolvedBindings(providers: ResolvedProvider[]): Injector {
return Injector.fromResolvedProviders(providers);
}
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
/**
* Retrieves an instance from the injector based on the provided token.
* Throws {@link NoProviderError} if not found.
* If not found:
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
* Injector.THROW_IF_NOT_FOUND is given
* - Returns the `notFoundValue` otherwise
*
* ### Example ([live demo](http://plnkr.co/edit/HeXSHg?p=preview))
*
* ```typescript
* var injector = Injector.resolveAndCreate([
* var injector = ReflectiveInjector.resolveAndCreate([
* provide("validToken", {useValue: "Value"})
* ]);
* expect(injector.get("validToken")).toEqual("Value");
@@ -446,484 +27,9 @@ export abstract class Injector {
* `Injector` returns itself when given `Injector` as a token.
*
* ```typescript
* var injector = Injector.resolveAndCreate([]);
* var injector = ReflectiveInjector.resolveAndCreate([]);
* expect(injector.get(Injector)).toBe(injector);
* ```
*/
get(token: any): any { return unimplemented(); }
/**
* Retrieves an instance from the injector based on the provided token.
* Returns null if not found.
*
* ### Example ([live demo](http://plnkr.co/edit/tpEbEy?p=preview))
*
* ```typescript
* var injector = Injector.resolveAndCreate([
* provide("validToken", {useValue: "Value"})
* ]);
* expect(injector.getOptional("validToken")).toEqual("Value");
* expect(injector.getOptional("invalidToken")).toBe(null);
* ```
*
* `Injector` returns itself when given `Injector` as a token.
*
* ```typescript
* var injector = Injector.resolveAndCreate([]);
* expect(injector.getOptional(Injector)).toBe(injector);
* ```
*/
getOptional(token: any): any { return unimplemented(); }
/**
* Parent of this injector.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* ### Example ([live demo](http://plnkr.co/edit/eosMGo?p=preview))
*
* ```typescript
* var parent = Injector.resolveAndCreate([]);
* var child = parent.resolveAndCreateChild([]);
* expect(child.parent).toBe(parent);
* ```
*/
get parent(): Injector { return unimplemented(); }
/**
* @internal
*/
debugContext(): any { return null; }
/**
* Resolves an array of providers and creates a child injector from those providers.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* The passed-in providers can be an array of `Type`, {@link Provider},
* or a recursive array of more providers.
*
* ### Example ([live demo](http://plnkr.co/edit/opB3T4?p=preview))
*
* ```typescript
* class ParentProvider {}
* class ChildProvider {}
*
* var parent = Injector.resolveAndCreate([ParentProvider]);
* var child = parent.resolveAndCreateChild([ChildProvider]);
*
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
* ```
*
* This function is slower than the corresponding `createChildFromResolved`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
*/
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): Injector {
return unimplemented();
}
/**
* Creates a child injector from previously resolved providers.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* This API is the recommended way to construct injectors in performance-sensitive parts.
*
* ### Example ([live demo](http://plnkr.co/edit/VhyfjN?p=preview))
*
* ```typescript
* class ParentProvider {}
* class ChildProvider {}
*
* var parentProviders = Injector.resolve([ParentProvider]);
* var childProviders = Injector.resolve([ChildProvider]);
*
* var parent = Injector.fromResolvedProviders(parentProviders);
* var child = parent.createChildFromResolved(childProviders);
*
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
* ```
*/
createChildFromResolved(providers: ResolvedProvider[]): Injector { return unimplemented(); }
/**
* Resolves a provider and instantiates an object in the context of the injector.
*
* The created object does not get cached by the injector.
*
* ### Example ([live demo](http://plnkr.co/edit/yvVXoB?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Engine]);
*
* var car = injector.resolveAndInstantiate(Car);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
* ```
*/
resolveAndInstantiate(provider: Type | Provider): any { return unimplemented(); }
/**
* Instantiates an object using a resolved provider in the context of the injector.
*
* The created object does not get cached by the injector.
*
* ### Example ([live demo](http://plnkr.co/edit/ptCImQ?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Engine]);
* var carProvider = Injector.resolve([Car])[0];
* var car = injector.instantiateResolved(carProvider);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
* ```
*/
instantiateResolved(provider: ResolvedProvider): any { return unimplemented(); }
}
/**
* A dependency injection container used for instantiating objects and resolving dependencies.
*
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
* constructor dependencies.
*
* In typical use, application code asks for the dependencies in the constructor and they are
* resolved by the `Injector`.
*
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
*
* The following example creates an `Injector` configured to create `Engine` and `Car`.
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Car, Engine]);
* var car = injector.get(Car);
* expect(car instanceof Car).toBe(true);
* expect(car.engine instanceof Engine).toBe(true);
* ```
*
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
* resolve all of the object's dependencies automatically.
*/
export class Injector_ implements Injector {
/** @internal */
_strategy: InjectorStrategy;
/** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
/** @internal */
public _parent: Injector;
/**
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
private _debugContext: Function = null) {
this._proto = _proto;
this._parent = _parent;
this._strategy = _proto._strategy.createInjectorStrategy(this);
}
/**
* @internal
*/
debugContext(): any { return this._debugContext(); }
get(token: any): any { return this._getByKey(Key.get(token), null, null, false); }
getOptional(token: any): any { return this._getByKey(Key.get(token), null, null, true); }
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
get parent(): Injector { return this._parent; }
/**
* @internal
* Internal. Do not use.
* We return `any` not to export the InjectorStrategy type.
*/
get internalStrategy(): any { return this._strategy; }
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): Injector {
var resolvedProviders = Injector.resolve(providers);
return this.createChildFromResolved(resolvedProviders);
}
createChildFromResolved(providers: ResolvedProvider[]): Injector {
var proto = new ProtoInjector(providers);
var inj = new Injector_(proto);
inj._parent = this;
return inj;
}
resolveAndInstantiate(provider: Type | Provider): any {
return this.instantiateResolved(Injector.resolve([provider])[0]);
}
instantiateResolved(provider: ResolvedProvider): any {
return this._instantiateProvider(provider);
}
/** @internal */
_new(provider: ResolvedProvider): any {
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
}
private _instantiateProvider(provider: ResolvedProvider): any {
if (provider.multiProvider) {
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
}
return res;
} else {
return this._instantiate(provider, provider.resolvedFactories[0]);
}
}
private _instantiate(provider: ResolvedProvider, resolvedFactory: ResolvedFactory): any {
var factory = resolvedFactory.factory;
var deps = resolvedFactory.dependencies;
var length = deps.length;
var d0: any;
var d1: any;
var d2: any;
var d3: any;
var d4: any;
var d5: any;
var d6: any;
var d7: any;
var d8: any;
var d9: any;
var d10: any;
var d11: any;
var d12: any;
var d13: any;
var d14: any;
var d15: any;
var d16: any;
var d17: any;
var d18: any;
var d19: any;
try {
d0 = length > 0 ? this._getByDependency(provider, deps[0]) : null;
d1 = length > 1 ? this._getByDependency(provider, deps[1]) : null;
d2 = length > 2 ? this._getByDependency(provider, deps[2]) : null;
d3 = length > 3 ? this._getByDependency(provider, deps[3]) : null;
d4 = length > 4 ? this._getByDependency(provider, deps[4]) : null;
d5 = length > 5 ? this._getByDependency(provider, deps[5]) : null;
d6 = length > 6 ? this._getByDependency(provider, deps[6]) : null;
d7 = length > 7 ? this._getByDependency(provider, deps[7]) : null;
d8 = length > 8 ? this._getByDependency(provider, deps[8]) : null;
d9 = length > 9 ? this._getByDependency(provider, deps[9]) : null;
d10 = length > 10 ? this._getByDependency(provider, deps[10]) : null;
d11 = length > 11 ? this._getByDependency(provider, deps[11]) : null;
d12 = length > 12 ? this._getByDependency(provider, deps[12]) : null;
d13 = length > 13 ? this._getByDependency(provider, deps[13]) : null;
d14 = length > 14 ? this._getByDependency(provider, deps[14]) : null;
d15 = length > 15 ? this._getByDependency(provider, deps[15]) : null;
d16 = length > 16 ? this._getByDependency(provider, deps[16]) : null;
d17 = length > 17 ? this._getByDependency(provider, deps[17]) : null;
d18 = length > 18 ? this._getByDependency(provider, deps[18]) : null;
d19 = length > 19 ? this._getByDependency(provider, deps[19]) : null;
} catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
e.addKey(this, provider.key);
}
throw e;
}
var obj;
try {
switch (length) {
case 0:
obj = factory();
break;
case 1:
obj = factory(d0);
break;
case 2:
obj = factory(d0, d1);
break;
case 3:
obj = factory(d0, d1, d2);
break;
case 4:
obj = factory(d0, d1, d2, d3);
break;
case 5:
obj = factory(d0, d1, d2, d3, d4);
break;
case 6:
obj = factory(d0, d1, d2, d3, d4, d5);
break;
case 7:
obj = factory(d0, d1, d2, d3, d4, d5, d6);
break;
case 8:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
break;
case 9:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
break;
case 10:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
break;
case 11:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
break;
case 12:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
break;
case 13:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
break;
case 14:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
break;
case 15:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
break;
case 16:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
break;
case 17:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
break;
case 18:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17);
break;
case 19:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17, d18);
break;
case 20:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17, d18, d19);
break;
default:
throw new BaseException(
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
}
} catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key);
}
return obj;
}
private _getByDependency(provider: ResolvedProvider, dep: Dependency): any {
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
dep.optional);
}
private _getByKey(key: Key, lowerBoundVisibility: Object, upperBoundVisibility: Object,
optional: boolean): any {
if (key === INJECTOR_KEY) {
return this;
}
if (upperBoundVisibility instanceof SelfMetadata) {
return this._getByKeySelf(key, optional);
} else {
return this._getByKeyDefault(key, optional, lowerBoundVisibility);
}
}
/** @internal */
_throwOrNull(key: Key, optional: boolean): any {
if (optional) {
return null;
} else {
throw new NoProviderError(this, key);
}
}
/** @internal */
_getByKeySelf(key: Key, optional: boolean): any {
var obj = this._strategy.getObjByKeyId(key.id);
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
}
/** @internal */
_getByKeyDefault(key: Key, optional: boolean, lowerBoundVisibility: Object): any {
var inj: Injector;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
inj = this._parent;
} else {
inj = this;
}
while (inj instanceof Injector_) {
var inj_ = <Injector_>inj;
var obj = inj_._strategy.getObjByKeyId(key.id);
if (obj !== UNDEFINED) return obj;
inj = inj_._parent;
}
if (inj !== null) {
if (optional) {
return inj.getOptional(key.token);
} else {
return inj.get(key.token);
}
} else {
return this._throwOrNull(key, optional);
}
}
get displayName(): string {
return `Injector(providers: [${_mapProviders(this, (b: ResolvedProvider) => ` "${b.key.displayName}" `).join(", ")}])`;
}
toString(): string { return this.displayName; }
}
var INJECTOR_KEY = Key.get(Injector);
function _mapProviders(injector: Injector_, fn: Function): any[] {
var res = [];
for (var i = 0; i < injector._proto.numberOfProviders; ++i) {
res.push(fn(injector._proto.getProviderAtIndex(i)));
}
return res;
get(token: any, notFoundValue?: any): any { return unimplemented(); }
}
+23 -299
View File
@@ -1,47 +1,13 @@
import {
normalizeBool,
Type,
isBlank,
isPresent,
CONST,
CONST_EXPR,
stringify,
isArray,
isType,
isBlank,
isFunction,
normalizeBool
stringify
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {Key} from './key';
import {
InjectMetadata,
InjectableMetadata,
OptionalMetadata,
SelfMetadata,
HostMetadata,
SkipSelfMetadata,
DependencyMetadata
} from './metadata';
import {
NoAnnotationError,
MixingMultiProvidersWithRegularProvidersError,
InvalidProviderError
} from './exceptions';
import {resolveForwardRef} from './forward_ref';
/**
* `Dependency` is used by the framework to extend DI.
* This is internal to Angular and should not be used directly.
*/
export class Dependency {
constructor(public key: Key, public optional: boolean, public lowerBoundVisibility: any,
public upperBoundVisibility: any, public properties: any[]) {}
static fromKey(key: Key): Dependency { return new Dependency(key, false, null, null, []); }
}
const _EMPTY_LIST = CONST_EXPR([]);
import {BaseException} from 'angular2/src/facade/exceptions';
/**
* Describes how the {@link Injector} should instantiate a given token.
@@ -283,69 +249,6 @@ export class Binding extends Provider {
get toValue() { return this.useValue; }
}
/**
* An internal resolved representation of a {@link Provider} used by the {@link Injector}.
*
* It is usually created automatically by `Injector.resolveAndCreate`.
*
* It can be created manually, as follows:
*
* ### Example ([live demo](http://plnkr.co/edit/RfEnhh8kUEI0G3qsnIeT?p%3Dpreview&p=preview))
*
* ```typescript
* var resolvedProviders = Injector.resolve([new Provider('message', {useValue: 'Hello'})]);
* var injector = Injector.fromResolvedProviders(resolvedProviders);
*
* expect(injector.get('message')).toEqual('Hello');
* ```
*/
export interface ResolvedProvider {
/**
* A key, usually a `Type`.
*/
key: Key;
/**
* Factory function which can return an instance of an object represented by a key.
*/
resolvedFactories: ResolvedFactory[];
/**
* Indicates if the provider is a multi-provider or a regular provider.
*/
multiProvider: boolean;
}
/**
* See {@link ResolvedProvider} instead.
*
* @deprecated
*/
export interface ResolvedBinding extends ResolvedProvider {}
export class ResolvedProvider_ implements ResolvedBinding {
constructor(public key: Key, public resolvedFactories: ResolvedFactory[],
public multiProvider: boolean) {}
get resolvedFactory(): ResolvedFactory { return this.resolvedFactories[0]; }
}
/**
* An internal resolved representation of a factory function created by resolving {@link Provider}.
*/
export class ResolvedFactory {
constructor(
/**
* Factory function which can return an instance of an object represented by a key.
*/
public factory: Function,
/**
* Arguments (dependencies) to the `factory` function.
*/
public dependencies: Dependency[]) {}
}
/**
* Creates a {@link Provider}.
*
@@ -362,31 +265,6 @@ export function bind(token): ProviderBuilder {
return new ProviderBuilder(token);
}
/**
* Creates a {@link Provider}.
*
* See {@link Provider} for more details.
*
* <!-- TODO: improve the docs -->
*/
export function provide(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
useClass?: Type,
useValue?: any,
useExisting?: any,
useFactory?: Function,
deps?: Object[],
multi?: boolean
}): Provider {
return new Provider(token, {
useClass: useClass,
useValue: useValue,
useExisting: useExisting,
useFactory: useFactory,
deps: deps,
multi: multi
});
}
/**
* Helper class for the {@link bind} function.
*/
@@ -509,180 +387,26 @@ export class ProviderBuilder {
}
/**
* Resolve a single provider.
*/
export function resolveFactory(provider: Provider): ResolvedFactory {
var factoryFn: Function;
var resolvedDeps;
if (isPresent(provider.useClass)) {
var useClass = resolveForwardRef(provider.useClass);
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else if (isPresent(provider.useExisting)) {
factoryFn = (aliasInstance) => aliasInstance;
resolvedDeps = [Dependency.fromKey(Key.get(provider.useExisting))];
} else if (isPresent(provider.useFactory)) {
factoryFn = provider.useFactory;
resolvedDeps = constructDependencies(provider.useFactory, provider.dependencies);
} else {
factoryFn = () => provider.useValue;
resolvedDeps = _EMPTY_LIST;
}
return new ResolvedFactory(factoryFn, resolvedDeps);
}
/**
* Converts the {@link Provider} into {@link ResolvedProvider}.
* Creates a {@link Provider}.
*
* {@link Injector} internally only uses {@link ResolvedProvider}, {@link Provider} contains
* convenience provider syntax.
* See {@link Provider} for more details.
*
* <!-- TODO: improve the docs -->
*/
export function resolveProvider(provider: Provider): ResolvedProvider {
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], provider.multi);
}
/**
* Resolve a list of Providers.
*/
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
var normalized = _normalizeProviders(providers, []);
var resolved = normalized.map(resolveProvider);
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>()));
}
/**
* Merges a list of ResolvedProviders into a list where
* each key is contained exactly once and multi providers
* have been merged.
*/
export function mergeResolvedProviders(
providers: ResolvedProvider[],
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
for (var i = 0; i < providers.length; i++) {
var provider = providers[i];
var existing = normalizedProvidersMap.get(provider.key.id);
if (isPresent(existing)) {
if (provider.multiProvider !== existing.multiProvider) {
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
}
if (provider.multiProvider) {
for (var j = 0; j < provider.resolvedFactories.length; j++) {
existing.resolvedFactories.push(provider.resolvedFactories[j]);
}
} else {
normalizedProvidersMap.set(provider.key.id, provider);
}
} else {
var resolvedProvider;
if (provider.multiProvider) {
resolvedProvider = new ResolvedProvider_(
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
} else {
resolvedProvider = provider;
}
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
}
}
return normalizedProvidersMap;
}
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
res: Provider[]): Provider[] {
providers.forEach(b => {
if (b instanceof Type) {
res.push(provide(b, {useClass: b}));
} else if (b instanceof Provider) {
res.push(b);
} else if (b instanceof Array) {
_normalizeProviders(b, res);
} else if (b instanceof ProviderBuilder) {
throw new InvalidProviderError(b.token);
} else {
throw new InvalidProviderError(b);
}
export function provide(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
useClass?: Type,
useValue?: any,
useExisting?: any,
useFactory?: Function,
deps?: Object[],
multi?: boolean
}): Provider {
return new Provider(token, {
useClass: useClass,
useValue: useValue,
useExisting: useExisting,
useFactory: useFactory,
deps: deps,
multi: multi
});
return res;
}
export function constructDependencies(typeOrFunc: any, dependencies: any[]): Dependency[] {
if (isBlank(dependencies)) {
return _dependenciesFor(typeOrFunc);
} else {
var params: any[][] = dependencies.map(t => [t]);
return dependencies.map(t => _extractToken(typeOrFunc, t, params));
}
}
function _dependenciesFor(typeOrFunc: any): Dependency[] {
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (params.some(isBlank)) {
throw new NoAnnotationError(typeOrFunc, params);
}
return params.map((p: any[]) => _extractToken(typeOrFunc, p, params));
}
function _extractToken(typeOrFunc, metadata /*any[] | any*/, params: any[][]): Dependency {
var depProps = [];
var token = null;
var optional = false;
if (!isArray(metadata)) {
if (metadata instanceof InjectMetadata) {
return _createDependency(metadata.token, optional, null, null, depProps);
} else {
return _createDependency(metadata, optional, null, null, depProps);
}
}
var lowerBoundVisibility = null;
var upperBoundVisibility = null;
for (var i = 0; i < metadata.length; ++i) {
var paramMetadata = metadata[i];
if (paramMetadata instanceof Type) {
token = paramMetadata;
} else if (paramMetadata instanceof InjectMetadata) {
token = paramMetadata.token;
} else if (paramMetadata instanceof OptionalMetadata) {
optional = true;
} else if (paramMetadata instanceof SelfMetadata) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof HostMetadata) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof SkipSelfMetadata) {
lowerBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof DependencyMetadata) {
if (isPresent(paramMetadata.token)) {
token = paramMetadata.token;
}
depProps.push(paramMetadata);
}
}
token = resolveForwardRef(token);
if (isPresent(token)) {
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
} else {
throw new NoAnnotationError(typeOrFunc, params);
}
}
function _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility,
depProps): Dependency {
return new Dependency(Key.get(token), optional, lowerBoundVisibility, upperBoundVisibility,
depProps);
}
@@ -1,8 +1,8 @@
import {ListWrapper} from 'angular2/src/facade/collection';
import {stringify, isBlank} from 'angular2/src/facade/lang';
import {BaseException, WrappedException, unimplemented} from 'angular2/src/facade/exceptions';
import {Key} from './key';
import {Injector} from './injector';
import {ReflectiveKey} from './reflective_key';
import {ReflectiveInjector} from './reflective_injector';
function findFirstClosedCycle(keys: any[]): any[] {
var res = [];
@@ -36,15 +36,16 @@ export class AbstractProviderError extends BaseException {
message: string;
/** @internal */
keys: Key[];
keys: ReflectiveKey[];
/** @internal */
injectors: Injector[];
injectors: ReflectiveInjector[];
/** @internal */
constructResolvingMessage: Function;
constructor(injector: Injector, key: Key, constructResolvingMessage: Function) {
constructor(injector: ReflectiveInjector, key: ReflectiveKey,
constructResolvingMessage: Function) {
super("DI Exception");
this.keys = [key];
this.injectors = [injector];
@@ -52,7 +53,7 @@ export class AbstractProviderError extends BaseException {
this.message = this.constructResolvingMessage(this.keys);
}
addKey(injector: Injector, key: Key): void {
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
this.message = this.constructResolvingMessage(this.keys);
@@ -76,7 +77,7 @@ export class AbstractProviderError extends BaseException {
* ```
*/
export class NoProviderError extends AbstractProviderError {
constructor(injector: Injector, key: Key) {
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
super(injector, key, function(keys: any[]) {
var first = stringify(ListWrapper.first(keys).token);
return `No provider for ${first}!${constructResolvingPath(keys)}`;
@@ -101,7 +102,7 @@ export class NoProviderError extends AbstractProviderError {
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
*/
export class CyclicDependencyError extends AbstractProviderError {
constructor(injector: Injector, key: Key) {
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
super(injector, key, function(keys: any[]) {
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
});
@@ -136,18 +137,18 @@ export class CyclicDependencyError extends AbstractProviderError {
*/
export class InstantiationError extends WrappedException {
/** @internal */
keys: Key[];
keys: ReflectiveKey[];
/** @internal */
injectors: Injector[];
injectors: ReflectiveInjector[];
constructor(injector: Injector, originalException, originalStack, key: Key) {
constructor(injector: ReflectiveInjector, originalException, originalStack, key: ReflectiveKey) {
super("DI Exception", originalException, originalStack, null);
this.keys = [key];
this.injectors = [injector];
}
addKey(injector: Injector, key: Key): void {
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
this.injectors.push(injector);
this.keys.push(key);
}
@@ -157,7 +158,7 @@ export class InstantiationError extends WrappedException {
return `Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
}
get causeKey(): Key { return this.keys[0]; }
get causeKey(): ReflectiveKey { return this.keys[0]; }
get context() { return this.injectors[this.injectors.length - 1].debugContext(); }
}
@@ -0,0 +1,887 @@
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {Provider, ProviderBuilder, provide} from './provider';
import {
ResolvedReflectiveProvider,
ReflectiveDependency,
ResolvedReflectiveFactory,
resolveReflectiveProviders
} from './reflective_provider';
import {
AbstractProviderError,
NoProviderError,
CyclicDependencyError,
InstantiationError,
InvalidProviderError,
OutOfBoundsError
} from './reflective_exceptions';
import {Type, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
import {ReflectiveKey} from './reflective_key';
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
import {Injector, THROW_IF_NOT_FOUND} from './injector';
var __unused: Type; // avoid unused import when Type union types are erased
// Threshold for the dynamic version
const _MAX_CONSTRUCTION_COUNTER = 10;
const UNDEFINED = CONST_EXPR(new Object());
export interface ReflectiveProtoInjectorStrategy {
getProviderAtIndex(index: number): ResolvedReflectiveProvider;
createInjectorStrategy(inj: ReflectiveInjector_): ReflectiveInjectorStrategy;
}
export class ReflectiveProtoInjectorInlineStrategy implements ReflectiveProtoInjectorStrategy {
provider0: ResolvedReflectiveProvider = null;
provider1: ResolvedReflectiveProvider = null;
provider2: ResolvedReflectiveProvider = null;
provider3: ResolvedReflectiveProvider = null;
provider4: ResolvedReflectiveProvider = null;
provider5: ResolvedReflectiveProvider = null;
provider6: ResolvedReflectiveProvider = null;
provider7: ResolvedReflectiveProvider = null;
provider8: ResolvedReflectiveProvider = null;
provider9: ResolvedReflectiveProvider = null;
keyId0: number = null;
keyId1: number = null;
keyId2: number = null;
keyId3: number = null;
keyId4: number = null;
keyId5: number = null;
keyId6: number = null;
keyId7: number = null;
keyId8: number = null;
keyId9: number = null;
constructor(protoEI: ReflectiveProtoInjector, providers: ResolvedReflectiveProvider[]) {
var length = providers.length;
if (length > 0) {
this.provider0 = providers[0];
this.keyId0 = providers[0].key.id;
}
if (length > 1) {
this.provider1 = providers[1];
this.keyId1 = providers[1].key.id;
}
if (length > 2) {
this.provider2 = providers[2];
this.keyId2 = providers[2].key.id;
}
if (length > 3) {
this.provider3 = providers[3];
this.keyId3 = providers[3].key.id;
}
if (length > 4) {
this.provider4 = providers[4];
this.keyId4 = providers[4].key.id;
}
if (length > 5) {
this.provider5 = providers[5];
this.keyId5 = providers[5].key.id;
}
if (length > 6) {
this.provider6 = providers[6];
this.keyId6 = providers[6].key.id;
}
if (length > 7) {
this.provider7 = providers[7];
this.keyId7 = providers[7].key.id;
}
if (length > 8) {
this.provider8 = providers[8];
this.keyId8 = providers[8].key.id;
}
if (length > 9) {
this.provider9 = providers[9];
this.keyId9 = providers[9].key.id;
}
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index == 0) return this.provider0;
if (index == 1) return this.provider1;
if (index == 2) return this.provider2;
if (index == 3) return this.provider3;
if (index == 4) return this.provider4;
if (index == 5) return this.provider5;
if (index == 6) return this.provider6;
if (index == 7) return this.provider7;
if (index == 8) return this.provider8;
if (index == 9) return this.provider9;
throw new OutOfBoundsError(index);
}
createInjectorStrategy(injector: ReflectiveInjector_): ReflectiveInjectorStrategy {
return new ReflectiveInjectorInlineStrategy(injector, this);
}
}
export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoInjectorStrategy {
keyIds: number[];
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
var len = providers.length;
this.keyIds = ListWrapper.createFixedSize(len);
for (var i = 0; i < len; i++) {
this.keyIds[i] = providers[i].key.id;
}
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this.providers.length) {
throw new OutOfBoundsError(index);
}
return this.providers[index];
}
createInjectorStrategy(ei: ReflectiveInjector_): ReflectiveInjectorStrategy {
return new ReflectiveInjectorDynamicStrategy(this, ei);
}
}
export class ReflectiveProtoInjector {
static fromResolvedProviders(providers: ResolvedReflectiveProvider[]): ReflectiveProtoInjector {
return new ReflectiveProtoInjector(providers);
}
/** @internal */
_strategy: ReflectiveProtoInjectorStrategy;
numberOfProviders: number;
constructor(providers: ResolvedReflectiveProvider[]) {
this.numberOfProviders = providers.length;
this._strategy = providers.length > _MAX_CONSTRUCTION_COUNTER ?
new ReflectiveProtoInjectorDynamicStrategy(this, providers) :
new ReflectiveProtoInjectorInlineStrategy(this, providers);
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
return this._strategy.getProviderAtIndex(index);
}
}
export interface ReflectiveInjectorStrategy {
getObjByKeyId(keyId: number): any;
getObjAtIndex(index: number): any;
getMaxNumberOfObjects(): number;
resetConstructionCounter(): void;
instantiateProvider(provider: ResolvedReflectiveProvider): any;
}
export class ReflectiveInjectorInlineStrategy implements ReflectiveInjectorStrategy {
obj0: any = UNDEFINED;
obj1: any = UNDEFINED;
obj2: any = UNDEFINED;
obj3: any = UNDEFINED;
obj4: any = UNDEFINED;
obj5: any = UNDEFINED;
obj6: any = UNDEFINED;
obj7: any = UNDEFINED;
obj8: any = UNDEFINED;
obj9: any = UNDEFINED;
constructor(public injector: ReflectiveInjector_,
public protoStrategy: ReflectiveProtoInjectorInlineStrategy) {}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedReflectiveProvider): any {
return this.injector._new(provider);
}
getObjByKeyId(keyId: number): any {
var p = this.protoStrategy;
var inj = this.injector;
if (p.keyId0 === keyId) {
if (this.obj0 === UNDEFINED) {
this.obj0 = inj._new(p.provider0);
}
return this.obj0;
}
if (p.keyId1 === keyId) {
if (this.obj1 === UNDEFINED) {
this.obj1 = inj._new(p.provider1);
}
return this.obj1;
}
if (p.keyId2 === keyId) {
if (this.obj2 === UNDEFINED) {
this.obj2 = inj._new(p.provider2);
}
return this.obj2;
}
if (p.keyId3 === keyId) {
if (this.obj3 === UNDEFINED) {
this.obj3 = inj._new(p.provider3);
}
return this.obj3;
}
if (p.keyId4 === keyId) {
if (this.obj4 === UNDEFINED) {
this.obj4 = inj._new(p.provider4);
}
return this.obj4;
}
if (p.keyId5 === keyId) {
if (this.obj5 === UNDEFINED) {
this.obj5 = inj._new(p.provider5);
}
return this.obj5;
}
if (p.keyId6 === keyId) {
if (this.obj6 === UNDEFINED) {
this.obj6 = inj._new(p.provider6);
}
return this.obj6;
}
if (p.keyId7 === keyId) {
if (this.obj7 === UNDEFINED) {
this.obj7 = inj._new(p.provider7);
}
return this.obj7;
}
if (p.keyId8 === keyId) {
if (this.obj8 === UNDEFINED) {
this.obj8 = inj._new(p.provider8);
}
return this.obj8;
}
if (p.keyId9 === keyId) {
if (this.obj9 === UNDEFINED) {
this.obj9 = inj._new(p.provider9);
}
return this.obj9;
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index == 0) return this.obj0;
if (index == 1) return this.obj1;
if (index == 2) return this.obj2;
if (index == 3) return this.obj3;
if (index == 4) return this.obj4;
if (index == 5) return this.obj5;
if (index == 6) return this.obj6;
if (index == 7) return this.obj7;
if (index == 8) return this.obj8;
if (index == 9) return this.obj9;
throw new OutOfBoundsError(index);
}
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
}
export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStrategy {
objs: any[];
constructor(public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
public injector: ReflectiveInjector_) {
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
ListWrapper.fill(this.objs, UNDEFINED);
}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedReflectiveProvider): any {
return this.injector._new(provider);
}
getObjByKeyId(keyId: number): any {
var p = this.protoStrategy;
for (var i = 0; i < p.keyIds.length; i++) {
if (p.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
this.objs[i] = this.injector._new(p.providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index < 0 || index >= this.objs.length) {
throw new OutOfBoundsError(index);
}
return this.objs[index];
}
getMaxNumberOfObjects(): number { return this.objs.length; }
}
/**
* A ReflectiveDependency injection container used for instantiating objects and resolving
* dependencies.
*
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
* constructor dependencies.
*
* In typical use, application code asks for the dependencies in the constructor and they are
* resolved by the `Injector`.
*
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
*
* The following example creates an `Injector` configured to create `Engine` and `Car`.
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
* var car = injector.get(Car);
* expect(car instanceof Car).toBe(true);
* expect(car.engine instanceof Engine).toBe(true);
* ```
*
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
* resolve all of the object's dependencies automatically.
*/
export abstract class ReflectiveInjector implements Injector {
/**
* Turns an array of provider definitions into an array of resolved providers.
*
* A resolution is a process of flattening multiple nested arrays and converting individual
* providers into an array of {@link ResolvedReflectiveProvider}s.
*
* ### Example ([live demo](http://plnkr.co/edit/AiXTHi?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
*
* expect(providers.length).toEqual(2);
*
* expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
* expect(providers[0].key.displayName).toBe("Car");
* expect(providers[0].dependencies.length).toEqual(1);
* expect(providers[0].factory).toBeDefined();
*
* expect(providers[1].key.displayName).toBe("Engine");
* });
* ```
*
* See {@link ReflectiveInjector#fromResolvedProviders} for more info.
*/
static resolve(providers: Array<Type | Provider | any[]>): ResolvedReflectiveProvider[] {
return resolveReflectiveProviders(providers);
}
/**
* Resolves an array of providers and creates an injector from those providers.
*
* The passed-in providers can be an array of `Type`, {@link Provider},
* or a recursive array of more providers.
*
* ### Example ([live demo](http://plnkr.co/edit/ePOccA?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*
* This function is slower than the corresponding `fromResolvedProviders`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
*/
static resolveAndCreate(providers: Array<Type | Provider | any[]>,
parent: Injector = null): ReflectiveInjector {
var ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
}
/**
* Creates an injector from previously resolved providers.
*
* This API is the recommended way to construct injectors in performance-sensitive parts.
*
* ### Example ([live demo](http://plnkr.co/edit/KrSMci?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var providers = ReflectiveInjector.resolve([Car, Engine]);
* var injector = ReflectiveInjector.fromResolvedProviders(providers);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*/
static fromResolvedProviders(providers: ResolvedReflectiveProvider[],
parent: Injector = null): ReflectiveInjector {
return new ReflectiveInjector_(ReflectiveProtoInjector.fromResolvedProviders(providers),
parent);
}
/**
* @deprecated
*/
static fromResolvedBindings(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
return ReflectiveInjector.fromResolvedProviders(providers);
}
/**
* Parent of this injector.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* ### Example ([live demo](http://plnkr.co/edit/eosMGo?p=preview))
*
* ```typescript
* var parent = ReflectiveInjector.resolveAndCreate([]);
* var child = parent.resolveAndCreateChild([]);
* expect(child.parent).toBe(parent);
* ```
*/
get parent(): Injector { return unimplemented(); }
/**
* @internal
*/
debugContext(): any { return null; }
/**
* Resolves an array of providers and creates a child injector from those providers.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* The passed-in providers can be an array of `Type`, {@link Provider},
* or a recursive array of more providers.
*
* ### Example ([live demo](http://plnkr.co/edit/opB3T4?p=preview))
*
* ```typescript
* class ParentProvider {}
* class ChildProvider {}
*
* var parent = ReflectiveInjector.resolveAndCreate([ParentProvider]);
* var child = parent.resolveAndCreateChild([ChildProvider]);
*
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
* ```
*
* This function is slower than the corresponding `createChildFromResolved`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
*/
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): ReflectiveInjector {
return unimplemented();
}
/**
* Creates a child injector from previously resolved providers.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* This API is the recommended way to construct injectors in performance-sensitive parts.
*
* ### Example ([live demo](http://plnkr.co/edit/VhyfjN?p=preview))
*
* ```typescript
* class ParentProvider {}
* class ChildProvider {}
*
* var parentProviders = ReflectiveInjector.resolve([ParentProvider]);
* var childProviders = ReflectiveInjector.resolve([ChildProvider]);
*
* var parent = ReflectiveInjector.fromResolvedProviders(parentProviders);
* var child = parent.createChildFromResolved(childProviders);
*
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
* ```
*/
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
return unimplemented();
}
/**
* Resolves a provider and instantiates an object in the context of the injector.
*
* The created object does not get cached by the injector.
*
* ### Example ([live demo](http://plnkr.co/edit/yvVXoB?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
*
* var car = injector.resolveAndInstantiate(Car);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
* ```
*/
resolveAndInstantiate(provider: Type | Provider): any { return unimplemented(); }
/**
* Instantiates an object using a resolved provider in the context of the injector.
*
* The created object does not get cached by the injector.
*
* ### Example ([live demo](http://plnkr.co/edit/ptCImQ?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
* var carProvider = ReflectiveInjector.resolve([Car])[0];
* var car = injector.instantiateResolved(carProvider);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
* ```
*/
instantiateResolved(provider: ResolvedReflectiveProvider): any { return unimplemented(); }
abstract get(token: any, notFoundValue?: any): any;
}
export class ReflectiveInjector_ implements ReflectiveInjector {
private _strategy: ReflectiveInjectorStrategy;
/** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
/** @internal */
public _parent: Injector;
/**
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
private _debugContext: Function = null) {
this._proto = _proto;
this._parent = _parent;
this._strategy = _proto._strategy.createInjectorStrategy(this);
}
/**
* @internal
*/
debugContext(): any { return this._debugContext(); }
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
return this._getByKey(ReflectiveKey.get(token), null, null, notFoundValue);
}
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
get parent(): Injector { return this._parent; }
/**
* @internal
* Internal. Do not use.
* We return `any` not to export the InjectorStrategy type.
*/
get internalStrategy(): any { return this._strategy; }
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): ReflectiveInjector {
var ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return this.createChildFromResolved(ResolvedReflectiveProviders);
}
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
var proto = new ReflectiveProtoInjector(providers);
var inj = new ReflectiveInjector_(proto);
inj._parent = this;
return inj;
}
resolveAndInstantiate(provider: Type | Provider): any {
return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
}
instantiateResolved(provider: ResolvedReflectiveProvider): any {
return this._instantiateProvider(provider);
}
/** @internal */
_new(provider: ResolvedReflectiveProvider): any {
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
}
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
if (provider.multiProvider) {
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
}
return res;
} else {
return this._instantiate(provider, provider.resolvedFactories[0]);
}
}
private _instantiate(provider: ResolvedReflectiveProvider,
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
var factory = ResolvedReflectiveFactory.factory;
var deps = ResolvedReflectiveFactory.dependencies;
var length = deps.length;
var d0: any;
var d1: any;
var d2: any;
var d3: any;
var d4: any;
var d5: any;
var d6: any;
var d7: any;
var d8: any;
var d9: any;
var d10: any;
var d11: any;
var d12: any;
var d13: any;
var d14: any;
var d15: any;
var d16: any;
var d17: any;
var d18: any;
var d19: any;
try {
d0 = length > 0 ? this._getByReflectiveDependency(provider, deps[0]) : null;
d1 = length > 1 ? this._getByReflectiveDependency(provider, deps[1]) : null;
d2 = length > 2 ? this._getByReflectiveDependency(provider, deps[2]) : null;
d3 = length > 3 ? this._getByReflectiveDependency(provider, deps[3]) : null;
d4 = length > 4 ? this._getByReflectiveDependency(provider, deps[4]) : null;
d5 = length > 5 ? this._getByReflectiveDependency(provider, deps[5]) : null;
d6 = length > 6 ? this._getByReflectiveDependency(provider, deps[6]) : null;
d7 = length > 7 ? this._getByReflectiveDependency(provider, deps[7]) : null;
d8 = length > 8 ? this._getByReflectiveDependency(provider, deps[8]) : null;
d9 = length > 9 ? this._getByReflectiveDependency(provider, deps[9]) : null;
d10 = length > 10 ? this._getByReflectiveDependency(provider, deps[10]) : null;
d11 = length > 11 ? this._getByReflectiveDependency(provider, deps[11]) : null;
d12 = length > 12 ? this._getByReflectiveDependency(provider, deps[12]) : null;
d13 = length > 13 ? this._getByReflectiveDependency(provider, deps[13]) : null;
d14 = length > 14 ? this._getByReflectiveDependency(provider, deps[14]) : null;
d15 = length > 15 ? this._getByReflectiveDependency(provider, deps[15]) : null;
d16 = length > 16 ? this._getByReflectiveDependency(provider, deps[16]) : null;
d17 = length > 17 ? this._getByReflectiveDependency(provider, deps[17]) : null;
d18 = length > 18 ? this._getByReflectiveDependency(provider, deps[18]) : null;
d19 = length > 19 ? this._getByReflectiveDependency(provider, deps[19]) : null;
} catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
e.addKey(this, provider.key);
}
throw e;
}
var obj;
try {
switch (length) {
case 0:
obj = factory();
break;
case 1:
obj = factory(d0);
break;
case 2:
obj = factory(d0, d1);
break;
case 3:
obj = factory(d0, d1, d2);
break;
case 4:
obj = factory(d0, d1, d2, d3);
break;
case 5:
obj = factory(d0, d1, d2, d3, d4);
break;
case 6:
obj = factory(d0, d1, d2, d3, d4, d5);
break;
case 7:
obj = factory(d0, d1, d2, d3, d4, d5, d6);
break;
case 8:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
break;
case 9:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
break;
case 10:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
break;
case 11:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
break;
case 12:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
break;
case 13:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
break;
case 14:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
break;
case 15:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
break;
case 16:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
break;
case 17:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
break;
case 18:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17);
break;
case 19:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17, d18);
break;
case 20:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17, d18, d19);
break;
default:
throw new BaseException(
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
}
} catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key);
}
return obj;
}
private _getByReflectiveDependency(provider: ResolvedReflectiveProvider,
dep: ReflectiveDependency): any {
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
dep.optional ? null : THROW_IF_NOT_FOUND);
}
private _getByKey(key: ReflectiveKey, lowerBoundVisibility: Object, upperBoundVisibility: Object,
notFoundValue: any): any {
if (key === INJECTOR_KEY) {
return this;
}
if (upperBoundVisibility instanceof SelfMetadata) {
return this._getByKeySelf(key, notFoundValue);
} else {
return this._getByKeyDefault(key, notFoundValue, lowerBoundVisibility);
}
}
/** @internal */
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
if (notFoundValue !== THROW_IF_NOT_FOUND) {
return notFoundValue;
} else {
throw new NoProviderError(this, key);
}
}
/** @internal */
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
var obj = this._strategy.getObjByKeyId(key.id);
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
}
/** @internal */
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, lowerBoundVisibility: Object): any {
var inj: Injector;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
inj = this._parent;
} else {
inj = this;
}
while (inj instanceof ReflectiveInjector_) {
var inj_ = <ReflectiveInjector_>inj;
var obj = inj_._strategy.getObjByKeyId(key.id);
if (obj !== UNDEFINED) return obj;
inj = inj_._parent;
}
if (inj !== null) {
return inj.get(key.token, notFoundValue);
} else {
return this._throwOrNull(key, notFoundValue);
}
}
get displayName(): string {
return `ReflectiveInjector(providers: [${_mapProviders(this, (b: ResolvedReflectiveProvider) => ` "${b.key.displayName}" `).join(", ")}])`;
}
toString(): string { return this.displayName; }
}
var INJECTOR_KEY = ReflectiveKey.get(Injector);
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
var res = [];
for (var i = 0; i < injector._proto.numberOfProviders; ++i) {
res.push(fn(injector._proto.getProviderAtIndex(i)));
}
return res;
}
@@ -3,19 +3,21 @@ import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {resolveForwardRef} from './forward_ref';
/**
* A unique object used for retrieving items from the {@link Injector}.
* A unique object used for retrieving items from the {@link ReflectiveInjector}.
*
* Keys have:
* - a system-wide unique `id`.
* - a `token`.
*
* `Key` is used internally by {@link Injector} because its system-wide unique `id` allows the
* `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
* the
* injector to store created objects in a more efficient way.
*
* `Key` should not be created directly. {@link Injector} creates keys automatically when resolving
* `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
* resolving
* providers.
*/
export class Key {
export class ReflectiveKey {
/**
* Private
*/
@@ -33,7 +35,9 @@ export class Key {
/**
* Retrieves a `Key` for a token.
*/
static get(token: Object): Key { return _globalKeyRegistry.get(resolveForwardRef(token)); }
static get(token: Object): ReflectiveKey {
return _globalKeyRegistry.get(resolveForwardRef(token));
}
/**
* @returns the number of keys registered in the system.
@@ -45,16 +49,16 @@ export class Key {
* @internal
*/
export class KeyRegistry {
private _allKeys = new Map<Object, Key>();
private _allKeys = new Map<Object, ReflectiveKey>();
get(token: Object): Key {
if (token instanceof Key) return token;
get(token: Object): ReflectiveKey {
if (token instanceof ReflectiveKey) return token;
if (this._allKeys.has(token)) {
return this._allKeys.get(token);
}
var newKey = new Key(token, Key.numberOfKeys);
var newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
this._allKeys.set(token, newKey);
return newKey;
}
@@ -0,0 +1,292 @@
import {
Type,
isBlank,
isPresent,
CONST,
CONST_EXPR,
isArray,
isType
} from 'angular2/src/facade/lang';
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {ReflectiveKey} from './reflective_key';
import {
InjectMetadata,
InjectableMetadata,
OptionalMetadata,
SelfMetadata,
HostMetadata,
SkipSelfMetadata,
DependencyMetadata
} from './metadata';
import {
NoAnnotationError,
MixingMultiProvidersWithRegularProvidersError,
InvalidProviderError
} from './reflective_exceptions';
import {resolveForwardRef} from './forward_ref';
import {Provider, ProviderBuilder, provide} from './provider';
/**
* `Dependency` is used by the framework to extend DI.
* This is internal to Angular and should not be used directly.
*/
export class ReflectiveDependency {
constructor(public key: ReflectiveKey, public optional: boolean, public lowerBoundVisibility: any,
public upperBoundVisibility: any, public properties: any[]) {}
static fromKey(key: ReflectiveKey): ReflectiveDependency {
return new ReflectiveDependency(key, false, null, null, []);
}
}
const _EMPTY_LIST = CONST_EXPR([]);
/**
* An internal resolved representation of a {@link Provider} used by the {@link Injector}.
*
* It is usually created automatically by `Injector.resolveAndCreate`.
*
* It can be created manually, as follows:
*
* ### Example ([live demo](http://plnkr.co/edit/RfEnhh8kUEI0G3qsnIeT?p%3Dpreview&p=preview))
*
* ```typescript
* var resolvedProviders = Injector.resolve([new Provider('message', {useValue: 'Hello'})]);
* var injector = Injector.fromResolvedProviders(resolvedProviders);
*
* expect(injector.get('message')).toEqual('Hello');
* ```
*/
export interface ResolvedReflectiveProvider {
/**
* A key, usually a `Type`.
*/
key: ReflectiveKey;
/**
* Factory function which can return an instance of an object represented by a key.
*/
resolvedFactories: ResolvedReflectiveFactory[];
/**
* Indicates if the provider is a multi-provider or a regular provider.
*/
multiProvider: boolean;
}
/**
* See {@link ResolvedProvider} instead.
*
* @deprecated
*/
export interface ResolvedReflectiveBinding extends ResolvedReflectiveProvider {}
export class ResolvedReflectiveProvider_ implements ResolvedReflectiveBinding {
constructor(public key: ReflectiveKey, public resolvedFactories: ResolvedReflectiveFactory[],
public multiProvider: boolean) {}
get resolvedFactory(): ResolvedReflectiveFactory { return this.resolvedFactories[0]; }
}
/**
* An internal resolved representation of a factory function created by resolving {@link Provider}.
*/
export class ResolvedReflectiveFactory {
constructor(
/**
* Factory function which can return an instance of an object represented by a key.
*/
public factory: Function,
/**
* Arguments (dependencies) to the `factory` function.
*/
public dependencies: ReflectiveDependency[]) {}
}
/**
* Resolve a single provider.
*/
export function resolveReflectiveFactory(provider: Provider): ResolvedReflectiveFactory {
var factoryFn: Function;
var resolvedDeps;
if (isPresent(provider.useClass)) {
var useClass = resolveForwardRef(provider.useClass);
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else if (isPresent(provider.useExisting)) {
factoryFn = (aliasInstance) => aliasInstance;
resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
} else if (isPresent(provider.useFactory)) {
factoryFn = provider.useFactory;
resolvedDeps = constructDependencies(provider.useFactory, provider.dependencies);
} else {
factoryFn = () => provider.useValue;
resolvedDeps = _EMPTY_LIST;
}
return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
}
/**
* Converts the {@link Provider} into {@link ResolvedProvider}.
*
* {@link Injector} internally only uses {@link ResolvedProvider}, {@link Provider} contains
* convenience provider syntax.
*/
export function resolveReflectiveProvider(provider: Provider): ResolvedReflectiveProvider {
return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.token),
[resolveReflectiveFactory(provider)], provider.multi);
}
/**
* Resolve a list of Providers.
*/
export function resolveReflectiveProviders(
providers: Array<Type | Provider | any[]>): ResolvedReflectiveProvider[] {
var normalized = _normalizeProviders(providers, []);
var resolved = normalized.map(resolveReflectiveProvider);
return MapWrapper.values(
mergeResolvedReflectiveProviders(resolved, new Map<number, ResolvedReflectiveProvider>()));
}
/**
* Merges a list of ResolvedProviders into a list where
* each key is contained exactly once and multi providers
* have been merged.
*/
export function mergeResolvedReflectiveProviders(
providers: ResolvedReflectiveProvider[],
normalizedProvidersMap: Map<number, ResolvedReflectiveProvider>):
Map<number, ResolvedReflectiveProvider> {
for (var i = 0; i < providers.length; i++) {
var provider = providers[i];
var existing = normalizedProvidersMap.get(provider.key.id);
if (isPresent(existing)) {
if (provider.multiProvider !== existing.multiProvider) {
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
}
if (provider.multiProvider) {
for (var j = 0; j < provider.resolvedFactories.length; j++) {
existing.resolvedFactories.push(provider.resolvedFactories[j]);
}
} else {
normalizedProvidersMap.set(provider.key.id, provider);
}
} else {
var resolvedProvider;
if (provider.multiProvider) {
resolvedProvider = new ResolvedReflectiveProvider_(
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
} else {
resolvedProvider = provider;
}
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
}
}
return normalizedProvidersMap;
}
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
res: Provider[]): Provider[] {
providers.forEach(b => {
if (b instanceof Type) {
res.push(provide(b, {useClass: b}));
} else if (b instanceof Provider) {
res.push(b);
} else if (b instanceof Array) {
_normalizeProviders(b, res);
} else if (b instanceof ProviderBuilder) {
throw new InvalidProviderError(b.token);
} else {
throw new InvalidProviderError(b);
}
});
return res;
}
export function constructDependencies(typeOrFunc: any,
dependencies: any[]): ReflectiveDependency[] {
if (isBlank(dependencies)) {
return _dependenciesFor(typeOrFunc);
} else {
var params: any[][] = dependencies.map(t => [t]);
return dependencies.map(t => _extractToken(typeOrFunc, t, params));
}
}
function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (params.some(isBlank)) {
throw new NoAnnotationError(typeOrFunc, params);
}
return params.map((p: any[]) => _extractToken(typeOrFunc, p, params));
}
function _extractToken(typeOrFunc, metadata /*any[] | any*/,
params: any[][]): ReflectiveDependency {
var depProps = [];
var token = null;
var optional = false;
if (!isArray(metadata)) {
if (metadata instanceof InjectMetadata) {
return _createDependency(metadata.token, optional, null, null, depProps);
} else {
return _createDependency(metadata, optional, null, null, depProps);
}
}
var lowerBoundVisibility = null;
var upperBoundVisibility = null;
for (var i = 0; i < metadata.length; ++i) {
var paramMetadata = metadata[i];
if (paramMetadata instanceof Type) {
token = paramMetadata;
} else if (paramMetadata instanceof InjectMetadata) {
token = paramMetadata.token;
} else if (paramMetadata instanceof OptionalMetadata) {
optional = true;
} else if (paramMetadata instanceof SelfMetadata) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof HostMetadata) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof SkipSelfMetadata) {
lowerBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof DependencyMetadata) {
if (isPresent(paramMetadata.token)) {
token = paramMetadata.token;
}
depProps.push(paramMetadata);
}
}
token = resolveForwardRef(token);
if (isPresent(token)) {
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
} else {
throw new NoAnnotationError(typeOrFunc, params);
}
}
function _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility,
depProps): ReflectiveDependency {
return new ReflectiveDependency(ReflectiveKey.get(token), optional, lowerBoundVisibility,
upperBoundVisibility, depProps);
}
@@ -1,4 +1,9 @@
import {Key, Injector, ResolvedProvider, Provider, provide, Injectable} from 'angular2/src/core/di';
import {
Injector,
ResolvedReflectiveProvider,
Injectable,
ReflectiveInjector
} from 'angular2/src/core/di';
import {ComponentResolver} from './component_resolver';
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
import {ComponentRef} from './component_factory';
@@ -60,6 +65,7 @@ export abstract class DynamicComponentLoader {
abstract loadAsRoot(type: Type, overrideSelectorOrNode: string | any, injector: Injector,
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef>;
/**
* Creates an instance of a Component and attaches it to the View Container found at the
* `location` specified as {@link ViewContainerRef}.
@@ -101,7 +107,7 @@ export abstract class DynamicComponentLoader {
* ```
*/
abstract loadNextToLocation(type: Type, location: ViewContainerRef,
providers?: ResolvedProvider[],
providers?: ResolvedReflectiveProvider[],
projectableNodes?: any[][]): Promise<ComponentRef>;
}
@@ -122,10 +128,15 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
});
}
loadNextToLocation(type: Type, location: ViewContainerRef, providers: ResolvedProvider[] = null,
loadNextToLocation(type: Type, location: ViewContainerRef,
providers: ResolvedReflectiveProvider[] = null,
projectableNodes: any[][] = null): Promise<ComponentRef> {
return this._compiler.resolveComponent(type).then(componentFactory => {
return location.createComponent(componentFactory, location.length, providers,
var contextInjector = location.parentInjector;
var childInjector = isPresent(providers) && providers.length > 0 ?
ReflectiveInjector.fromResolvedProviders(providers, contextInjector) :
contextInjector;
return location.createComponent(componentFactory, location.length, childInjector,
projectableNodes);
});
}
@@ -1,22 +1,19 @@
import {isBlank, stringify} from 'angular2/src/facade/lang';
import {Injector, UNDEFINED} from 'angular2/src/core/di/injector';
import {isBlank, stringify, CONST_EXPR} from 'angular2/src/facade/lang';
import {Injector, THROW_IF_NOT_FOUND} from 'angular2/src/core/di/injector';
import {AppView} from './view';
const _UNDEFINED = CONST_EXPR(new Object());
export class ElementInjector extends Injector {
constructor(private _view: AppView<any>, private _nodeIndex: number) { super(); }
get(token: any): any {
var result = this._view.injectorGet(token, this._nodeIndex, UNDEFINED);
if (result === UNDEFINED) {
result = this._view.parentInjector.get(token);
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
var result = _UNDEFINED;
if (result === _UNDEFINED) {
result = this._view.injectorGet(token, this._nodeIndex, _UNDEFINED);
}
return result;
}
getOptional(token: any): any {
var result = this._view.injectorGet(token, this._nodeIndex, UNDEFINED);
if (result === UNDEFINED) {
result = this._view.parentInjector.getOptional(token);
if (result === _UNDEFINED) {
result = this._view.parentInjector.get(token, notFoundValue);
}
return result;
}
@@ -1,7 +1,8 @@
import {ListWrapper} from 'angular2/src/facade/collection';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {Injector, Injector_, ProtoInjector} from 'angular2/src/core/di/injector';
import {ResolvedProvider} from 'angular2/src/core/di/provider';
import {Injector} from 'angular2/src/core/di/injector';
import {ReflectiveInjector} from 'angular2/src/core/di/reflective_injector';
import {ResolvedReflectiveProvider} from 'angular2/src/core/di/reflective_provider';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
@@ -36,6 +37,10 @@ export abstract class ViewContainerRef {
*/
get element(): ElementRef { return <ElementRef>unimplemented(); }
get injector(): Injector { return <Injector>unimplemented(); }
get parentInjector(): Injector { return <Injector>unimplemented(); }
/**
* Destroys all Views in this container.
*/
@@ -65,18 +70,17 @@ export abstract class ViewContainerRef {
* Instantiates a single {@link Component} and inserts its Host View into this container at the
* specified `index`.
*
* The component is instantiated using its {@link ProtoViewRef `protoView`} which can be
* obtained via {@link Compiler#compileInHost}.
* The component is instantiated using its {@link ComponentFactory} which can be
* obtained via {@link ComponentResolver#resolveComponent}.
*
* If `index` is not specified, the new View will be inserted as the last View in the container.
*
* You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector}
* that will be created for the Host View.
* You can optionally specify the {@link Injector}
* that will be used for the component.
*
* Returns the {@link ComponentRef} of the Host View created for the newly instantiated Component.
*/
abstract createComponent(componentFactory: ComponentFactory, index?: number,
dynamicallyCreatedProviders?: ResolvedProvider[],
abstract createComponent(componentFactory: ComponentFactory, index?: number, injector?: Injector,
projectableNodes?: any[][]): ComponentRef;
/**
@@ -120,6 +124,10 @@ export class ViewContainerRef_ implements ViewContainerRef {
get element(): ElementRef { return this._element.elementRef; }
get injector(): Injector { return this._element.injector; }
get parentInjector(): Injector { return this._element.parentInjector; }
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
createEmbeddedView(templateRef: TemplateRef, index: number = -1): EmbeddedViewRef {
@@ -132,18 +140,11 @@ export class ViewContainerRef_ implements ViewContainerRef {
_createComponentInContainerScope: WtfScopeFn =
wtfCreateScope('ViewContainerRef#createComponent()');
createComponent(componentFactory: ComponentFactory, index: number = -1,
dynamicallyCreatedProviders: ResolvedProvider[] = null,
createComponent(componentFactory: ComponentFactory, index: number = -1, injector: Injector = null,
projectableNodes: any[][] = null): ComponentRef {
var s = this._createComponentInContainerScope();
var contextInjector = this._element.parentInjector;
var childInjector =
isPresent(dynamicallyCreatedProviders) && dynamicallyCreatedProviders.length > 0 ?
new Injector_(ProtoInjector.fromResolvedProviders(dynamicallyCreatedProviders),
contextInjector) :
contextInjector;
var componentRef = componentFactory.create(childInjector, projectableNodes);
var contextInjector = isPresent(injector) ? injector : this._element.parentInjector;
var componentRef = componentFactory.create(contextInjector, projectableNodes);
this.insert(componentRef.hostView, index);
return wtfLeave(s, componentRef);
}
@@ -8,9 +8,8 @@ import {
DynamicComponentLoader,
ComponentRef,
ViewContainerRef,
Injector,
provide,
Dependency,
ReflectiveInjector,
OnDestroy,
Output
} from 'angular2/core';
@@ -60,7 +59,7 @@ export class RouterOutlet implements OnDestroy {
var componentType = nextInstruction.componentType;
var childRouter = this._parentRouter.childRouter(componentType);
var providers = Injector.resolve([
var providers = ReflectiveInjector.resolve([
provide(RouteData, {useValue: nextInstruction.routeData}),
provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}),
provide(routerMod.Router, {useValue: childRouter})
@@ -1,4 +1,4 @@
import {Injector, Provider, PLATFORM_INITIALIZER} from 'angular2/core';
import {ReflectiveInjector, Provider, PLATFORM_INITIALIZER} from 'angular2/core';
import {BaseException, ExceptionHandler} from 'angular2/src/facade/exceptions';
import {ListWrapper} from 'angular2/src/facade/collection';
import {FunctionWrapper, isPresent, Type} from 'angular2/src/facade/lang';
@@ -6,7 +6,7 @@ import {FunctionWrapper, isPresent, Type} from 'angular2/src/facade/lang';
export class TestInjector {
private _instantiated: boolean = false;
private _injector: Injector = null;
private _injector: ReflectiveInjector = null;
private _providers: Array<Type | Provider | any[]> = [];
@@ -28,7 +28,7 @@ export class TestInjector {
}
createInjector() {
var rootInjector = Injector.resolveAndCreate(this.platformProviders);
var rootInjector = ReflectiveInjector.resolveAndCreate(this.platformProviders);
this._injector = rootInjector.resolveAndCreateChild(
ListWrapper.concat(this.applicationProviders, this._providers));
this._instantiated = true;
@@ -76,7 +76,7 @@ export function setBaseTestProviders(platformProviders: Array<Type | Provider |
testInjector.platformProviders = platformProviders;
testInjector.applicationProviders = applicationProviders;
var injector = testInjector.createInjector();
let inits: Function[] = injector.getOptional(PLATFORM_INITIALIZER);
let inits: Function[] = injector.get(PLATFORM_INITIALIZER, null);
if (isPresent(inits)) {
inits.forEach(init => init());
}
@@ -201,7 +201,7 @@ export class FunctionWithParamTokens {
/**
* Returns the value of the executed function.
*/
execute(injector: Injector): any {
execute(injector: ReflectiveInjector): any {
var params = this._tokens.map(t => injector.get(t));
return FunctionWrapper.apply(this._fn, params);
}
@@ -5,7 +5,8 @@ import {
OnChanges,
ComponentFactory,
ComponentRef,
SimpleChange
SimpleChange,
ReflectiveInjector
} from 'angular2/core';
import {NG1_SCOPE} from './constants';
import {ComponentInfo} from './metadata';
@@ -36,8 +37,8 @@ export class DowngradeNg2ComponentAdapter {
}
bootstrapNg2() {
var childInjector = this.parentInjector.resolveAndCreateChild(
[provide(NG1_SCOPE, {useValue: this.componentScope})]);
var childInjector = ReflectiveInjector.resolveAndCreate(
[provide(NG1_SCOPE, {useValue: this.componentScope})], this.parentInjector);
this.contentInsertionPoint = document.createComment('ng1 insertion point');
this.componentRef =