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:
parent
da41a954b5
commit
c37af2af5a
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type} from '../type';
|
|
||||||
|
|
||||||
import {Injector, THROW_IF_NOT_FOUND} from './injector';
|
import {Injector, THROW_IF_NOT_FOUND} from './injector';
|
||||||
import {Self, SkipSelf} from './metadata';
|
import {Self, SkipSelf} from './metadata';
|
||||||
import {Provider} from './provider';
|
import {Provider} from './provider';
|
||||||
@ -16,308 +14,8 @@ import {ReflectiveKey} from './reflective_key';
|
|||||||
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
|
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
|
||||||
|
|
||||||
// Threshold for the dynamic version
|
// Threshold for the dynamic version
|
||||||
const _MAX_CONSTRUCTION_COUNTER = 10;
|
|
||||||
const UNDEFINED = new Object();
|
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
|
* A ReflectiveDependency injection container used for instantiating objects and resolving
|
||||||
* dependencies.
|
* dependencies.
|
||||||
@ -447,8 +145,7 @@ export abstract class ReflectiveInjector implements Injector {
|
|||||||
*/
|
*/
|
||||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
|
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
|
||||||
ReflectiveInjector {
|
ReflectiveInjector {
|
||||||
return new ReflectiveInjector_(
|
return new ReflectiveInjector_(providers, parent);
|
||||||
ReflectiveProtoInjector.fromResolvedProviders(providers), parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -580,45 +277,46 @@ export abstract class ReflectiveInjector implements Injector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ReflectiveInjector_ implements ReflectiveInjector {
|
export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||||
private _strategy: ReflectiveInjectorStrategy;
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_constructionCounter: number = 0;
|
_constructionCounter: number = 0;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public _proto: any /* ProtoInjector */;
|
public _providers: ResolvedReflectiveProvider[];
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public _parent: Injector;
|
public _parent: Injector;
|
||||||
|
|
||||||
|
keyIds: number[];
|
||||||
|
objs: any[];
|
||||||
/**
|
/**
|
||||||
* Private
|
* Private
|
||||||
*/
|
*/
|
||||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null) {
|
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) {
|
||||||
this._proto = _proto;
|
this._providers = _providers;
|
||||||
this._parent = _parent;
|
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 {
|
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; }
|
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 {
|
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
|
||||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||||
return this.createChildFromResolved(ResolvedReflectiveProviders);
|
return this.createChildFromResolved(ResolvedReflectiveProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
||||||
const proto = new ReflectiveProtoInjector(providers);
|
const inj = new ReflectiveInjector_(providers);
|
||||||
const inj = new ReflectiveInjector_(proto);
|
|
||||||
inj._parent = this;
|
inj._parent = this;
|
||||||
return inj;
|
return inj;
|
||||||
}
|
}
|
||||||
@ -631,14 +329,23 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
return this._instantiateProvider(provider);
|
return this._instantiateProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
|
||||||
|
if (index < 0 || index >= this._providers.length) {
|
||||||
|
throw new OutOfBoundsError(index);
|
||||||
|
}
|
||||||
|
return this._providers[index];
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_new(provider: ResolvedReflectiveProvider): any {
|
_new(provider: ResolvedReflectiveProvider): any {
|
||||||
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
|
||||||
throw new CyclicDependencyError(this, provider.key);
|
throw new CyclicDependencyError(this, provider.key);
|
||||||
}
|
}
|
||||||
return this._instantiateProvider(provider);
|
return this._instantiateProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||||
|
|
||||||
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||||
if (provider.multiProvider) {
|
if (provider.multiProvider) {
|
||||||
const res = new Array(provider.resolvedFactories.length);
|
const res = new Array(provider.resolvedFactories.length);
|
||||||
@ -655,50 +362,11 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
provider: ResolvedReflectiveProvider,
|
provider: ResolvedReflectiveProvider,
|
||||||
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
|
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
|
||||||
const factory = ResolvedReflectiveFactory.factory;
|
const factory = ResolvedReflectiveFactory.factory;
|
||||||
const deps = ResolvedReflectiveFactory.dependencies;
|
|
||||||
const length = deps.length;
|
|
||||||
|
|
||||||
let d0: any;
|
let deps: 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;
|
|
||||||
try {
|
try {
|
||||||
d0 = length > 0 ? this._getByReflectiveDependency(provider, deps[0]) : null;
|
deps =
|
||||||
d1 = length > 1 ? this._getByReflectiveDependency(provider, deps[1]) : null;
|
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
|
||||||
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) {
|
} catch (e) {
|
||||||
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
|
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
|
||||||
e.addKey(this, provider.key);
|
e.addKey(this, provider.key);
|
||||||
@ -708,106 +376,45 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
|
|
||||||
let obj: any;
|
let obj: any;
|
||||||
try {
|
try {
|
||||||
switch (length) {
|
obj = factory(...deps);
|
||||||
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`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new InstantiationError(this, e, e.stack, provider.key);
|
throw new InstantiationError(this, e, e.stack, provider.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getByReflectiveDependency(
|
private _getByReflectiveDependency(dep: ReflectiveDependency): any {
|
||||||
provider: ResolvedReflectiveProvider, dep: ReflectiveDependency): any {
|
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
|
||||||
return this._getByKey(
|
|
||||||
dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
|
|
||||||
dep.optional ? null : THROW_IF_NOT_FOUND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getByKey(
|
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any {
|
||||||
key: ReflectiveKey, lowerBoundVisibility: Object, upperBoundVisibility: Object,
|
|
||||||
notFoundValue: any): any {
|
|
||||||
if (key === INJECTOR_KEY) {
|
if (key === INJECTOR_KEY) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upperBoundVisibility instanceof Self) {
|
if (visibility instanceof Self) {
|
||||||
return this._getByKeySelf(key, notFoundValue);
|
return this._getByKeySelf(key, notFoundValue);
|
||||||
|
|
||||||
} else {
|
} 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 */
|
/** @internal */
|
||||||
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
|
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
|
||||||
if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
||||||
@ -819,15 +426,15 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
|
_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);
|
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, lowerBoundVisibility: Object): any {
|
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any {
|
||||||
let inj: Injector;
|
let inj: Injector;
|
||||||
|
|
||||||
if (lowerBoundVisibility instanceof SkipSelf) {
|
if (visibility instanceof SkipSelf) {
|
||||||
inj = this._parent;
|
inj = this._parent;
|
||||||
} else {
|
} else {
|
||||||
inj = this;
|
inj = this;
|
||||||
@ -835,7 +442,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
|
|
||||||
while (inj instanceof ReflectiveInjector_) {
|
while (inj instanceof ReflectiveInjector_) {
|
||||||
const inj_ = <ReflectiveInjector_>inj;
|
const inj_ = <ReflectiveInjector_>inj;
|
||||||
const obj = inj_._strategy.getObjByKeyId(key.id);
|
const obj = inj_._getObjByKeyId(key.id);
|
||||||
if (obj !== UNDEFINED) return obj;
|
if (obj !== UNDEFINED) return obj;
|
||||||
inj = inj_._parent;
|
inj = inj_._parent;
|
||||||
}
|
}
|
||||||
@ -859,9 +466,9 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
const INJECTOR_KEY = ReflectiveKey.get(Injector);
|
const INJECTOR_KEY = ReflectiveKey.get(Injector);
|
||||||
|
|
||||||
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
|
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
|
||||||
const res: any[] = new Array(injector._proto.numberOfProviders);
|
const res: any[] = new Array(injector._providers.length);
|
||||||
for (let i = 0; i < injector._proto.numberOfProviders; ++i) {
|
for (let i = 0; i < injector._providers.length; ++i) {
|
||||||
res[i] = fn(injector._proto.getProviderAtIndex(i));
|
res[i] = fn(injector.getProviderAtIndex(i));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,10 @@ interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider,
|
|||||||
*/
|
*/
|
||||||
export class ReflectiveDependency {
|
export class ReflectiveDependency {
|
||||||
constructor(
|
constructor(
|
||||||
public key: ReflectiveKey, public optional: boolean, public lowerBoundVisibility: any,
|
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {}
|
||||||
public upperBoundVisibility: any, public properties: any[]) {}
|
|
||||||
|
|
||||||
static fromKey(key: ReflectiveKey): ReflectiveDependency {
|
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(
|
function _extractToken(
|
||||||
typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency {
|
typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency {
|
||||||
const depProps: any[] = [];
|
|
||||||
let token: any = null;
|
let token: any = null;
|
||||||
let optional = false;
|
let optional = false;
|
||||||
|
|
||||||
if (!Array.isArray(metadata)) {
|
if (!Array.isArray(metadata)) {
|
||||||
if (metadata instanceof Inject) {
|
if (metadata instanceof Inject) {
|
||||||
return _createDependency(metadata.token, optional, null, null, depProps);
|
return _createDependency(metadata.token, optional, null);
|
||||||
} else {
|
} else {
|
||||||
return _createDependency(metadata, optional, null, null, depProps);
|
return _createDependency(metadata, optional, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lowerBoundVisibility: any = null;
|
let visibility: Self|SkipSelf = null;
|
||||||
let upperBoundVisibility: any = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < metadata.length; ++i) {
|
for (let i = 0; i < metadata.length; ++i) {
|
||||||
const paramMetadata = metadata[i];
|
const paramMetadata = metadata[i];
|
||||||
@ -246,29 +243,21 @@ function _extractToken(
|
|||||||
} else if (paramMetadata instanceof Optional) {
|
} else if (paramMetadata instanceof Optional) {
|
||||||
optional = true;
|
optional = true;
|
||||||
|
|
||||||
} else if (paramMetadata instanceof Self) {
|
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
|
||||||
upperBoundVisibility = paramMetadata;
|
visibility = paramMetadata;
|
||||||
|
|
||||||
} else if (paramMetadata instanceof Host) {
|
|
||||||
upperBoundVisibility = paramMetadata;
|
|
||||||
|
|
||||||
} else if (paramMetadata instanceof SkipSelf) {
|
|
||||||
lowerBoundVisibility = paramMetadata;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
token = resolveForwardRef(token);
|
token = resolveForwardRef(token);
|
||||||
|
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
|
return _createDependency(token, optional, visibility);
|
||||||
} else {
|
} else {
|
||||||
throw new NoAnnotationError(typeOrFunc, params);
|
throw new NoAnnotationError(typeOrFunc, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createDependency(
|
function _createDependency(
|
||||||
token: any, optional: boolean, lowerBoundVisibility: any, upperBoundVisibility: any,
|
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency {
|
||||||
depProps: any[]): ReflectiveDependency {
|
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
|
||||||
return new ReflectiveDependency(
|
|
||||||
ReflectiveKey.get(token), optional, lowerBoundVisibility, upperBoundVisibility, depProps);
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
|
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 {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
@ -83,478 +83,440 @@ export function main() {
|
|||||||
{provide: 'provider10', useValue: 1}
|
{provide: 'provider10', useValue: 1}
|
||||||
];
|
];
|
||||||
|
|
||||||
[{strategy: 'inline', providers: [], strategyClass: ReflectiveInjectorInlineStrategy}, {
|
function createInjector(
|
||||||
strategy: 'dynamic',
|
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ {
|
||||||
providers: dynamicProviders,
|
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders));
|
||||||
strategyClass: ReflectiveInjectorDynamicStrategy
|
if (isPresent(parent)) {
|
||||||
}].forEach((context) => {
|
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);
|
||||||
function createInjector(
|
} else {
|
||||||
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ {
|
return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders);
|
||||||
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(context['providers']));
|
|
||||||
if (isPresent(parent)) {
|
|
||||||
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);
|
|
||||||
} else {
|
|
||||||
return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe(`injector ${context['strategy']}`, () => {
|
describe(`injector`, () => {
|
||||||
it('should use the right strategy', () => {
|
|
||||||
const injector = createInjector([]);
|
|
||||||
expect(injector.internalStrategy).toBeAnInstanceOf(context['strategyClass']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should instantiate a class without dependencies', () => {
|
it('should instantiate a class without dependencies', () => {
|
||||||
const injector = createInjector([Engine]);
|
const injector = createInjector([Engine]);
|
||||||
const engine = injector.get(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);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
expect(engine).toBeAnInstanceOf(Engine);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should resolve dependencies based on type information', () => {
|
||||||
|
const injector = createInjector([Engine, Car]);
|
||||||
|
const car = injector.get(Car);
|
||||||
|
|
||||||
describe('child', () => {
|
expect(car).toBeAnInstanceOf(Car);
|
||||||
it('should load instances from parent injector', () => {
|
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 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);
|
expect(() => child.get(Car))
|
||||||
const engineFromChild = child.get(Engine);
|
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
|
||||||
|
|
||||||
expect(engineFromChild).toBe(engineFromParent);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not use the child providers when resolving the dependencies of a parent provider',
|
describe('default', () => {
|
||||||
() => {
|
it('should not skip self', () => {
|
||||||
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 parent = ReflectiveInjector.resolveAndCreate([Engine]);
|
||||||
const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]);
|
const child = parent.resolveAndCreateChild([
|
||||||
|
{provide: Engine, useClass: TurboEngine},
|
||||||
const engineFromParent = parent.get(Engine);
|
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [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 engineProvider = providers[0];
|
expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine);
|
||||||
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" ])');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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" ])');
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user