refactor(core): simplify ReflectiveInjector by removing code for Dart implementation (#14126)

ReflectiveInjector previously used two strategies for resolving dependencies. These
were to support the Dart implementation, but are no longer needed. A result of this
PR is there is no longer a 20 dependency limit and the generated code is smaller.

PR Close #14126
This commit is contained in:
Jason Aden 2017-01-25 17:21:54 -08:00 committed by Miško Hevery
parent da41a954b5
commit c37af2af5a
3 changed files with 490 additions and 932 deletions

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '../type';
import {Injector, THROW_IF_NOT_FOUND} from './injector';
import {Self, SkipSelf} from './metadata';
import {Provider} from './provider';
@ -16,308 +14,8 @@ import {ReflectiveKey} from './reflective_key';
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
// Threshold for the dynamic version
const _MAX_CONSTRUCTION_COUNTER = 10;
const UNDEFINED = 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[]) {
const 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[]) {
const len = providers.length;
this.keyIds = new Array(len);
for (let 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 {
const p = this.protoStrategy;
const 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 = new Array(protoStrategy.providers.length).fill(UNDEFINED);
}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedReflectiveProvider): any {
return this.injector._new(provider);
}
getObjByKeyId(keyId: number): any {
const p = this.protoStrategy;
for (let 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.
@ -447,8 +145,7 @@ export abstract class ReflectiveInjector implements Injector {
*/
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
ReflectiveInjector {
return new ReflectiveInjector_(
ReflectiveProtoInjector.fromResolvedProviders(providers), parent);
return new ReflectiveInjector_(providers, parent);
}
@ -580,45 +277,46 @@ export abstract class ReflectiveInjector implements Injector {
}
export class ReflectiveInjector_ implements ReflectiveInjector {
private _strategy: ReflectiveInjectorStrategy;
/** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
public _providers: ResolvedReflectiveProvider[];
/** @internal */
public _parent: Injector;
keyIds: number[];
objs: any[];
/**
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null) {
this._proto = _proto;
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) {
this._providers = _providers;
this._parent = _parent;
this._strategy = _proto._strategy.createInjectorStrategy(this);
const len = _providers.length;
this.keyIds = new Array(len);
this.objs = new Array(len);
for (let i = 0; i < len; i++) {
this.keyIds[i] = _providers[i].key.id;
this.objs[i] = UNDEFINED;
}
}
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
return this._getByKey(ReflectiveKey.get(token), null, null, notFoundValue);
return this._getByKey(ReflectiveKey.get(token), 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: Provider[]): ReflectiveInjector {
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return this.createChildFromResolved(ResolvedReflectiveProviders);
}
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
const proto = new ReflectiveProtoInjector(providers);
const inj = new ReflectiveInjector_(proto);
const inj = new ReflectiveInjector_(providers);
inj._parent = this;
return inj;
}
@ -631,14 +329,23 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
return this._instantiateProvider(provider);
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this._providers.length) {
throw new OutOfBoundsError(index);
}
return this._providers[index];
}
/** @internal */
_new(provider: ResolvedReflectiveProvider): any {
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
}
private _getMaxNumberOfObjects(): number { return this.objs.length; }
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
if (provider.multiProvider) {
const res = new Array(provider.resolvedFactories.length);
@ -655,50 +362,11 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
provider: ResolvedReflectiveProvider,
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
const factory = ResolvedReflectiveFactory.factory;
const deps = ResolvedReflectiveFactory.dependencies;
const length = deps.length;
let d0: any;
let d1: any;
let d2: any;
let d3: any;
let d4: any;
let d5: any;
let d6: any;
let d7: any;
let d8: any;
let d9: any;
let d10: any;
let d11: any;
let d12: any;
let d13: any;
let d14: any;
let d15: any;
let d16: any;
let d17: any;
let d18: any;
let d19: any;
let deps: 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;
deps =
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
} catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
e.addKey(this, provider.key);
@ -708,106 +376,45 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
let obj: any;
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 Error(
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
}
obj = factory(...deps);
} 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 _getByReflectiveDependency(dep: ReflectiveDependency): any {
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
}
private _getByKey(
key: ReflectiveKey, lowerBoundVisibility: Object, upperBoundVisibility: Object,
notFoundValue: any): any {
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any {
if (key === INJECTOR_KEY) {
return this;
}
if (upperBoundVisibility instanceof Self) {
if (visibility instanceof Self) {
return this._getByKeySelf(key, notFoundValue);
} else {
return this._getByKeyDefault(key, notFoundValue, lowerBoundVisibility);
return this._getByKeyDefault(key, notFoundValue, visibility);
}
}
private _getObjByKeyId(keyId: number): any {
for (let i = 0; i < this.keyIds.length; i++) {
if (this.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
this.objs[i] = this._new(this._providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
/** @internal */
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
if (notFoundValue !== THROW_IF_NOT_FOUND) {
@ -819,15 +426,15 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
/** @internal */
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
const obj = this._strategy.getObjByKeyId(key.id);
const obj = this._getObjByKeyId(key.id);
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
}
/** @internal */
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, lowerBoundVisibility: Object): any {
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any {
let inj: Injector;
if (lowerBoundVisibility instanceof SkipSelf) {
if (visibility instanceof SkipSelf) {
inj = this._parent;
} else {
inj = this;
@ -835,7 +442,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
while (inj instanceof ReflectiveInjector_) {
const inj_ = <ReflectiveInjector_>inj;
const obj = inj_._strategy.getObjByKeyId(key.id);
const obj = inj_._getObjByKeyId(key.id);
if (obj !== UNDEFINED) return obj;
inj = inj_._parent;
}
@ -859,9 +466,9 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
const INJECTOR_KEY = ReflectiveKey.get(Injector);
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
const res: any[] = new Array(injector._proto.numberOfProviders);
for (let i = 0; i < injector._proto.numberOfProviders; ++i) {
res[i] = fn(injector._proto.getProviderAtIndex(i));
const res: any[] = new Array(injector._providers.length);
for (let i = 0; i < injector._providers.length; ++i) {
res[i] = fn(injector.getProviderAtIndex(i));
}
return res;
}

View File

@ -25,11 +25,10 @@ interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider,
*/
export class ReflectiveDependency {
constructor(
public key: ReflectiveKey, public optional: boolean, public lowerBoundVisibility: any,
public upperBoundVisibility: any, public properties: any[]) {}
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {}
static fromKey(key: ReflectiveKey): ReflectiveDependency {
return new ReflectiveDependency(key, false, null, null, []);
return new ReflectiveDependency(key, false, null);
}
}
@ -219,20 +218,18 @@ function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
function _extractToken(
typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency {
const depProps: any[] = [];
let token: any = null;
let optional = false;
if (!Array.isArray(metadata)) {
if (metadata instanceof Inject) {
return _createDependency(metadata.token, optional, null, null, depProps);
return _createDependency(metadata.token, optional, null);
} else {
return _createDependency(metadata, optional, null, null, depProps);
return _createDependency(metadata, optional, null);
}
}
let lowerBoundVisibility: any = null;
let upperBoundVisibility: any = null;
let visibility: Self|SkipSelf = null;
for (let i = 0; i < metadata.length; ++i) {
const paramMetadata = metadata[i];
@ -246,29 +243,21 @@ function _extractToken(
} else if (paramMetadata instanceof Optional) {
optional = true;
} else if (paramMetadata instanceof Self) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof Host) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof SkipSelf) {
lowerBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
visibility = paramMetadata;
}
}
token = resolveForwardRef(token);
if (token != null) {
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
return _createDependency(token, optional, visibility);
} else {
throw new NoAnnotationError(typeOrFunc, params);
}
}
function _createDependency(
token: any, optional: boolean, lowerBoundVisibility: any, upperBoundVisibility: any,
depProps: any[]): ReflectiveDependency {
return new ReflectiveDependency(
ReflectiveKey.get(token), optional, lowerBoundVisibility, upperBoundVisibility, depProps);
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency {
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
}

View File

@ -7,7 +7,7 @@
*/
import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
import {ReflectiveInjectorDynamicStrategy, ReflectiveInjectorInlineStrategy, ReflectiveInjector_, ReflectiveProtoInjector} from '@angular/core/src/di/reflective_injector';
import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -83,478 +83,440 @@ export function main() {
{provide: 'provider10', useValue: 1}
];
[{strategy: 'inline', providers: [], strategyClass: ReflectiveInjectorInlineStrategy}, {
strategy: 'dynamic',
providers: dynamicProviders,
strategyClass: ReflectiveInjectorDynamicStrategy
}].forEach((context) => {
function createInjector(
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ {
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(context['providers']));
if (isPresent(parent)) {
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);
} else {
return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders);
}
function createInjector(
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ {
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders));
if (isPresent(parent)) {
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);
} else {
return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders);
}
}
describe(`injector ${context['strategy']}`, () => {
it('should use the right strategy', () => {
const injector = createInjector([]);
expect(injector.internalStrategy).toBeAnInstanceOf(context['strategyClass']);
});
describe(`injector`, () => {
it('should instantiate a class without dependencies', () => {
const injector = createInjector([Engine]);
const engine = injector.get(Engine);
expect(engine).toBeAnInstanceOf(Engine);
});
it('should resolve dependencies based on type information', () => {
const injector = createInjector([Engine, Car]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should resolve dependencies based on @Inject annotation', () => {
const injector = createInjector([TurboEngine, Engine, CarWithInject]);
const car = injector.get(CarWithInject);
expect(car).toBeAnInstanceOf(CarWithInject);
expect(car.engine).toBeAnInstanceOf(TurboEngine);
});
it('should throw when no type and not @Inject (class case)', () => {
expect(() => createInjector([NoAnnotations]))
.toThrowError(
'Cannot resolve all parameters for \'NoAnnotations\'(?). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations ' +
'and that \'NoAnnotations\' is decorated with Injectable.');
});
it('should throw when no type and not @Inject (factory case)', () => {
expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}]))
.toThrowError(
'Cannot resolve all parameters for \'factoryFn\'(?). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations ' +
'and that \'factoryFn\' is decorated with Injectable.');
});
it('should cache instances', () => {
const injector = createInjector([Engine]);
const e1 = injector.get(Engine);
const e2 = injector.get(Engine);
expect(e1).toBe(e2);
});
it('should provide to a value', () => {
const injector = createInjector([{provide: Engine, useValue: 'fake engine'}]);
const engine = injector.get(Engine);
expect(engine).toEqual('fake engine');
});
it('should provide to a factory', () => {
function sportsCarFactory(e: any /** TODO #9100 */) { return new SportsCar(e); }
const injector =
createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should throw when using a factory with more than 20 dependencies', () => {
function factoryWithTooManyArgs() { return new Car(null); }
const injector = createInjector([
Engine, {
provide: Car,
useFactory: factoryWithTooManyArgs,
deps: [
Engine, Engine, Engine, Engine, Engine, Engine, Engine,
Engine, Engine, Engine, Engine, Engine, Engine, Engine,
Engine, Engine, Engine, Engine, Engine, Engine, Engine
]
}
]);
try {
injector.get(Car);
throw 'Must throw';
} catch (e) {
expect(e.message).toContain(
`Cannot instantiate 'Car' because it has more than 20 dependencies`);
}
});
it('should supporting provider to null', () => {
const injector = createInjector([{provide: Engine, useValue: null}]);
const engine = injector.get(Engine);
expect(engine).toBeNull();
});
it('should provide to an alias', () => {
const injector = createInjector([
Engine, {provide: SportsCar, useClass: SportsCar},
{provide: Car, useExisting: SportsCar}
]);
const car = injector.get(Car);
const sportsCar = injector.get(SportsCar);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car).toBe(sportsCar);
});
it('should support multiProviders', () => {
const injector = createInjector([
Engine, {provide: Car, useClass: SportsCar, multi: true},
{provide: Car, useClass: CarWithOptionalEngine, multi: true}
]);
const cars = injector.get(Car);
expect(cars.length).toEqual(2);
expect(cars[0]).toBeAnInstanceOf(SportsCar);
expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine);
});
it('should support multiProviders that are created using useExisting', () => {
const injector = createInjector(
[Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]);
const cars = injector.get(Car);
expect(cars.length).toEqual(1);
expect(cars[0]).toBe(injector.get(SportsCar));
});
it('should throw when the aliased provider does not exist', () => {
const injector = createInjector([{provide: 'car', useExisting: SportsCar}]);
const e = `No provider for ${stringify(SportsCar)}! (car -> ${stringify(SportsCar)})`;
expect(() => injector.get('car')).toThrowError(e);
});
it('should handle forwardRef in useExisting', () => {
const injector = createInjector([
{provide: 'originalEngine', useClass: forwardRef(() => Engine)},
{provide: 'aliasedEngine', useExisting: <any>forwardRef(() => 'originalEngine')}
]);
expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine);
});
it('should support overriding factory dependencies', () => {
const injector = createInjector(
[Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should support optional dependencies', () => {
const injector = createInjector([CarWithOptionalEngine]);
const car = injector.get(CarWithOptionalEngine);
expect(car.engine).toEqual(null);
});
it('should flatten passed-in providers', () => {
const injector = createInjector([[[Engine, Car]]]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(Car);
});
it('should use the last provider when there are multiple providers for same token', () => {
const injector = createInjector(
[{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]);
expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine);
});
it('should use non-type tokens', () => {
const injector = createInjector([{provide: 'token', useValue: 'value'}]);
expect(injector.get('token')).toEqual('value');
});
it('should throw when given invalid providers', () => {
expect(() => createInjector(<any>['blah']))
.toThrowError(
'Invalid provider - only instances of Provider and Type are allowed, got: blah');
});
it('should provide itself', () => {
const parent = createInjector([]);
const child = parent.resolveAndCreateChild([]);
expect(child.get(Injector)).toBe(child);
});
it('should throw when no provider defined', () => {
const injector = createInjector([]);
expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!');
});
it('should show the full path when no provider', () => {
const injector = createInjector([CarWithDashboard, Engine, Dashboard]);
expect(() => injector.get(CarWithDashboard))
.toThrowError(
`No provider for DashboardSoftware! (${stringify(CarWithDashboard)} -> ${stringify(Dashboard)} -> DashboardSoftware)`);
});
it('should throw when trying to instantiate a cyclic dependency', () => {
const injector = createInjector([Car, {provide: Engine, useClass: CyclicEngine}]);
expect(() => injector.get(Car))
.toThrowError(
`Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`);
});
it('should show the full path when error happens in a constructor', () => {
const providers =
ReflectiveInjector.resolve([Car, {provide: Engine, useClass: BrokenEngine}]);
const proto = new ReflectiveProtoInjector([providers[0], providers[1]]);
const injector = new ReflectiveInjector_(proto);
try {
injector.get(Car);
throw 'Must throw';
} catch (e) {
expect(e.message).toContain(
`Error during instantiation of Engine! (${stringify(Car)} -> Engine)`);
expect(e.originalError instanceof Error).toBeTruthy();
expect(e.causeKey.token).toEqual(Engine);
}
});
it('should instantiate an object after a failed attempt', () => {
let isBroken = true;
const injector = createInjector([
Car,
{provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())}
]);
expect(() => injector.get(Car))
.toThrowError('Broken Engine: Error during instantiation of Engine! (Car -> Engine).');
isBroken = false;
expect(injector.get(Car)).toBeAnInstanceOf(Car);
});
it('should support null values', () => {
const injector = createInjector([{provide: 'null', useValue: null}]);
expect(injector.get('null')).toBe(null);
});
it('should instantiate a class without dependencies', () => {
const injector = createInjector([Engine]);
const engine = injector.get(Engine);
expect(engine).toBeAnInstanceOf(Engine);
});
it('should resolve dependencies based on type information', () => {
const injector = createInjector([Engine, Car]);
const car = injector.get(Car);
describe('child', () => {
it('should load instances from parent injector', () => {
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should resolve dependencies based on @Inject annotation', () => {
const injector = createInjector([TurboEngine, Engine, CarWithInject]);
const car = injector.get(CarWithInject);
expect(car).toBeAnInstanceOf(CarWithInject);
expect(car.engine).toBeAnInstanceOf(TurboEngine);
});
it('should throw when no type and not @Inject (class case)', () => {
expect(() => createInjector([NoAnnotations]))
.toThrowError(
'Cannot resolve all parameters for \'NoAnnotations\'(?). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations ' +
'and that \'NoAnnotations\' is decorated with Injectable.');
});
it('should throw when no type and not @Inject (factory case)', () => {
expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}]))
.toThrowError(
'Cannot resolve all parameters for \'factoryFn\'(?). ' +
'Make sure that all the parameters are decorated with Inject or have valid type annotations ' +
'and that \'factoryFn\' is decorated with Injectable.');
});
it('should cache instances', () => {
const injector = createInjector([Engine]);
const e1 = injector.get(Engine);
const e2 = injector.get(Engine);
expect(e1).toBe(e2);
});
it('should provide to a value', () => {
const injector = createInjector([{provide: Engine, useValue: 'fake engine'}]);
const engine = injector.get(Engine);
expect(engine).toEqual('fake engine');
});
it('should provide to a factory', () => {
function sportsCarFactory(e: any /** TODO #9100 */) { return new SportsCar(e); }
const injector =
createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should supporting provider to null', () => {
const injector = createInjector([{provide: Engine, useValue: null}]);
const engine = injector.get(Engine);
expect(engine).toBeNull();
});
it('should provide to an alias', () => {
const injector = createInjector([
Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar}
]);
const car = injector.get(Car);
const sportsCar = injector.get(SportsCar);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car).toBe(sportsCar);
});
it('should support multiProviders', () => {
const injector = createInjector([
Engine, {provide: Car, useClass: SportsCar, multi: true},
{provide: Car, useClass: CarWithOptionalEngine, multi: true}
]);
const cars = injector.get(Car);
expect(cars.length).toEqual(2);
expect(cars[0]).toBeAnInstanceOf(SportsCar);
expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine);
});
it('should support multiProviders that are created using useExisting', () => {
const injector =
createInjector([Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]);
const cars = injector.get(Car);
expect(cars.length).toEqual(1);
expect(cars[0]).toBe(injector.get(SportsCar));
});
it('should throw when the aliased provider does not exist', () => {
const injector = createInjector([{provide: 'car', useExisting: SportsCar}]);
const e = `No provider for ${stringify(SportsCar)}! (car -> ${stringify(SportsCar)})`;
expect(() => injector.get('car')).toThrowError(e);
});
it('should handle forwardRef in useExisting', () => {
const injector = createInjector([
{provide: 'originalEngine', useClass: forwardRef(() => Engine)},
{provide: 'aliasedEngine', useExisting: <any>forwardRef(() => 'originalEngine')}
]);
expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine);
});
it('should support overriding factory dependencies', () => {
const injector = createInjector(
[Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(SportsCar);
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should support optional dependencies', () => {
const injector = createInjector([CarWithOptionalEngine]);
const car = injector.get(CarWithOptionalEngine);
expect(car.engine).toEqual(null);
});
it('should flatten passed-in providers', () => {
const injector = createInjector([[[Engine, Car]]]);
const car = injector.get(Car);
expect(car).toBeAnInstanceOf(Car);
});
it('should use the last provider when there are multiple providers for same token', () => {
const injector = createInjector(
[{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]);
expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine);
});
it('should use non-type tokens', () => {
const injector = createInjector([{provide: 'token', useValue: 'value'}]);
expect(injector.get('token')).toEqual('value');
});
it('should throw when given invalid providers', () => {
expect(() => createInjector(<any>['blah']))
.toThrowError(
'Invalid provider - only instances of Provider and Type are allowed, got: blah');
});
it('should provide itself', () => {
const parent = createInjector([]);
const child = parent.resolveAndCreateChild([]);
expect(child.get(Injector)).toBe(child);
});
it('should throw when no provider defined', () => {
const injector = createInjector([]);
expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!');
});
it('should show the full path when no provider', () => {
const injector = createInjector([CarWithDashboard, Engine, Dashboard]);
expect(() => injector.get(CarWithDashboard))
.toThrowError(
`No provider for DashboardSoftware! (${stringify(CarWithDashboard)} -> ${stringify(Dashboard)} -> DashboardSoftware)`);
});
it('should throw when trying to instantiate a cyclic dependency', () => {
const injector = createInjector([Car, {provide: Engine, useClass: CyclicEngine}]);
expect(() => injector.get(Car))
.toThrowError(
`Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`);
});
it('should show the full path when error happens in a constructor', () => {
const providers =
ReflectiveInjector.resolve([Car, {provide: Engine, useClass: BrokenEngine}]);
const injector = new ReflectiveInjector_(providers);
try {
injector.get(Car);
throw 'Must throw';
} catch (e) {
expect(e.message).toContain(
`Error during instantiation of Engine! (${stringify(Car)} -> Engine)`);
expect(e.originalError instanceof Error).toBeTruthy();
expect(e.causeKey.token).toEqual(Engine);
}
});
it('should instantiate an object after a failed attempt', () => {
let isBroken = true;
const injector = createInjector([
Car, {provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())}
]);
expect(() => injector.get(Car))
.toThrowError('Broken Engine: Error during instantiation of Engine! (Car -> Engine).');
isBroken = false;
expect(injector.get(Car)).toBeAnInstanceOf(Car);
});
it('should support null values', () => {
const injector = createInjector([{provide: 'null', useValue: null}]);
expect(injector.get('null')).toBe(null);
});
});
describe('child', () => {
it('should load instances from parent injector', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([]);
const engineFromParent = parent.get(Engine);
const engineFromChild = child.get(Engine);
expect(engineFromChild).toBe(engineFromParent);
});
it('should not use the child providers when resolving the dependencies of a parent provider',
() => {
const parent = ReflectiveInjector.resolveAndCreate([Car, Engine]);
const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]);
const carFromChild = child.get(Car);
expect(carFromChild.engine).toBeAnInstanceOf(Engine);
});
it('should create new instance in a child injector', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]);
const engineFromParent = parent.get(Engine);
const engineFromChild = child.get(Engine);
expect(engineFromParent).not.toBe(engineFromChild);
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
});
it('should give access to parent', () => {
const parent = ReflectiveInjector.resolveAndCreate([]);
const child = parent.resolveAndCreateChild([]);
expect(child.parent).toBe(parent);
});
});
describe('resolveAndInstantiate', () => {
it('should instantiate an object in the context of the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
const car = inj.resolveAndInstantiate(Car);
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBe(inj.get(Engine));
});
it('should not store the instantiated object in the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
inj.resolveAndInstantiate(Car);
expect(() => inj.get(Car)).toThrowError();
});
});
describe('instantiate', () => {
it('should instantiate an object in the context of the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
const car = inj.instantiateResolved(ReflectiveInjector.resolve([Car])[0]);
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBe(inj.get(Engine));
});
});
describe('depedency resolution', () => {
describe('@Self()', () => {
it('should return a dependency from self', () => {
const inj = ReflectiveInjector.resolveAndCreate([
Engine,
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}
]);
expect(inj.get(Car)).toBeAnInstanceOf(Car);
});
it('should throw when not requested provider on self', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([]);
const child = parent.resolveAndCreateChild(
[{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}]);
const engineFromParent = parent.get(Engine);
const engineFromChild = child.get(Engine);
expect(engineFromChild).toBe(engineFromParent);
expect(() => child.get(Car))
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
});
});
it('should not use the child providers when resolving the dependencies of a parent provider',
() => {
const parent = ReflectiveInjector.resolveAndCreate([Car, Engine]);
const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]);
const carFromChild = child.get(Car);
expect(carFromChild.engine).toBeAnInstanceOf(Engine);
});
it('should create new instance in a child injector', () => {
describe('default', () => {
it('should not skip self', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]);
const engineFromParent = parent.get(Engine);
const engineFromChild = child.get(Engine);
expect(engineFromParent).not.toBe(engineFromChild);
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
});
it('should give access to parent', () => {
const parent = ReflectiveInjector.resolveAndCreate([]);
const child = parent.resolveAndCreateChild([]);
expect(child.parent).toBe(parent);
});
});
describe('resolveAndInstantiate', () => {
it('should instantiate an object in the context of the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
const car = inj.resolveAndInstantiate(Car);
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBe(inj.get(Engine));
});
it('should not store the instantiated object in the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
inj.resolveAndInstantiate(Car);
expect(() => inj.get(Car)).toThrowError();
});
});
describe('instantiate', () => {
it('should instantiate an object in the context of the injector', () => {
const inj = ReflectiveInjector.resolveAndCreate([Engine]);
const car = inj.instantiateResolved(ReflectiveInjector.resolve([Car])[0]);
expect(car).toBeAnInstanceOf(Car);
expect(car.engine).toBe(inj.get(Engine));
});
});
describe('depedency resolution', () => {
describe('@Self()', () => {
it('should return a dependency from self', () => {
const inj = ReflectiveInjector.resolveAndCreate([
Engine,
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}
]);
expect(inj.get(Car)).toBeAnInstanceOf(Car);
});
it('should throw when not requested provider on self', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}
]);
expect(() => child.get(Car))
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
});
});
describe('default', () => {
it('should not skip self', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([
{provide: Engine, useClass: TurboEngine},
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [Engine]}
]);
expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine);
});
});
});
describe('resolve', () => {
it('should resolve and flatten', () => {
const providers = ReflectiveInjector.resolve([Engine, [BrokenEngine]]);
providers.forEach(function(b) {
if (!b) return; // the result is a sparse array
expect(b instanceof ResolvedReflectiveProvider_).toBe(true);
});
});
it('should support multi providers', () => {
const provider = ReflectiveInjector.resolve([
{provide: Engine, useClass: BrokenEngine, multi: true},
{provide: Engine, useClass: TurboEngine, multi: true}
])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(2);
});
it('should support providers as hash', () => {
const provider = ReflectiveInjector.resolve([
{provide: Engine, useClass: BrokenEngine, multi: true},
{provide: Engine, useClass: TurboEngine, multi: true}
])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(2);
});
it('should support multi providers with only one provider', () => {
const provider =
ReflectiveInjector.resolve([{provide: Engine, useClass: BrokenEngine, multi: true}])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(1);
});
it('should throw when mixing multi providers with regular providers', () => {
expect(() => {
ReflectiveInjector.resolve(
[{provide: Engine, useClass: BrokenEngine, multi: true}, Engine]);
}).toThrowError(/Cannot mix multi providers and regular providers/);
expect(() => {
ReflectiveInjector.resolve(
[Engine, {provide: Engine, useClass: BrokenEngine, multi: true}]);
}).toThrowError(/Cannot mix multi providers and regular providers/);
});
it('should resolve forward references', () => {
const providers = ReflectiveInjector.resolve([
forwardRef(() => Engine),
[{provide: forwardRef(() => BrokenEngine), useClass: forwardRef(() => Engine)}], {
provide: forwardRef(() => String),
useFactory: () => 'OK',
deps: [forwardRef(() => Engine)]
}
const child = parent.resolveAndCreateChild([
{provide: Engine, useClass: TurboEngine},
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [Engine]}
]);
const engineProvider = providers[0];
const brokenEngineProvider = providers[1];
const stringProvider = providers[2];
expect(engineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true);
expect(brokenEngineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true);
expect(stringProvider.resolvedFactories[0].dependencies[0].key)
.toEqual(ReflectiveKey.get(Engine));
});
it('should support overriding factory dependencies with dependency annotations', () => {
const providers = ReflectiveInjector.resolve([{
provide: 'token',
useFactory: (e: any /** TODO #9100 */) => 'result',
deps: [[new Inject('dep')]]
}]);
const provider = providers[0];
expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep');
});
it('should allow declaring dependencies with flat arrays', () => {
const resolved = ReflectiveInjector.resolve(
[{provide: 'token', useFactory: (e: any) => e, deps: [new Inject('dep')]}]);
const nestedResolved = ReflectiveInjector.resolve(
[{provide: 'token', useFactory: (e: any) => e, deps: [[new Inject('dep')]]}]);
expect(resolved[0].resolvedFactories[0].dependencies[0].key.token)
.toEqual(nestedResolved[0].resolvedFactories[0].dependencies[0].key.token);
});
});
describe('displayName', () => {
it('should work', () => {
expect((<ReflectiveInjector_>ReflectiveInjector.resolveAndCreate([Engine, BrokenEngine]))
.displayName)
.toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])');
expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine);
});
});
});
describe('resolve', () => {
it('should resolve and flatten', () => {
const providers = ReflectiveInjector.resolve([Engine, [BrokenEngine]]);
providers.forEach(function(b) {
if (!b) return; // the result is a sparse array
expect(b instanceof ResolvedReflectiveProvider_).toBe(true);
});
});
it('should support multi providers', () => {
const provider = ReflectiveInjector.resolve([
{provide: Engine, useClass: BrokenEngine, multi: true},
{provide: Engine, useClass: TurboEngine, multi: true}
])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(2);
});
it('should support providers as hash', () => {
const provider = ReflectiveInjector.resolve([
{provide: Engine, useClass: BrokenEngine, multi: true},
{provide: Engine, useClass: TurboEngine, multi: true}
])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(2);
});
it('should support multi providers with only one provider', () => {
const provider =
ReflectiveInjector.resolve([{provide: Engine, useClass: BrokenEngine, multi: true}])[0];
expect(provider.key.token).toBe(Engine);
expect(provider.multiProvider).toEqual(true);
expect(provider.resolvedFactories.length).toEqual(1);
});
it('should throw when mixing multi providers with regular providers', () => {
expect(() => {
ReflectiveInjector.resolve(
[{provide: Engine, useClass: BrokenEngine, multi: true}, Engine]);
}).toThrowError(/Cannot mix multi providers and regular providers/);
expect(() => {
ReflectiveInjector.resolve(
[Engine, {provide: Engine, useClass: BrokenEngine, multi: true}]);
}).toThrowError(/Cannot mix multi providers and regular providers/);
});
it('should resolve forward references', () => {
const providers = ReflectiveInjector.resolve([
forwardRef(() => Engine),
[{provide: forwardRef(() => BrokenEngine), useClass: forwardRef(() => Engine)}], {
provide: forwardRef(() => String),
useFactory: () => 'OK',
deps: [forwardRef(() => Engine)]
}
]);
const engineProvider = providers[0];
const brokenEngineProvider = providers[1];
const stringProvider = providers[2];
expect(engineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true);
expect(brokenEngineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true);
expect(stringProvider.resolvedFactories[0].dependencies[0].key)
.toEqual(ReflectiveKey.get(Engine));
});
it('should support overriding factory dependencies with dependency annotations', () => {
const providers = ReflectiveInjector.resolve([{
provide: 'token',
useFactory: (e: any /** TODO #9100 */) => 'result',
deps: [[new Inject('dep')]]
}]);
const provider = providers[0];
expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep');
});
it('should allow declaring dependencies with flat arrays', () => {
const resolved = ReflectiveInjector.resolve(
[{provide: 'token', useFactory: (e: any) => e, deps: [new Inject('dep')]}]);
const nestedResolved = ReflectiveInjector.resolve(
[{provide: 'token', useFactory: (e: any) => e, deps: [[new Inject('dep')]]}]);
expect(resolved[0].resolvedFactories[0].dependencies[0].key.token)
.toEqual(nestedResolved[0].resolvedFactories[0].dependencies[0].key.token);
});
});
describe('displayName', () => {
it('should work', () => {
expect((<ReflectiveInjector_>ReflectiveInjector.resolveAndCreate([Engine, BrokenEngine]))
.displayName)
.toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])');
});
});
}