From c37af2af5adadc9469522103d32514de35fdfab1 Mon Sep 17 00:00:00 2001 From: Jason Aden Date: Wed, 25 Jan 2017 17:21:54 -0800 Subject: [PATCH] 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 --- .../core/src/di/reflective_injector.ts | 513 ++-------- .../core/src/di/reflective_provider.ts | 31 +- .../core/test/di/reflective_injector_spec.ts | 878 +++++++++--------- 3 files changed, 490 insertions(+), 932 deletions(-) diff --git a/modules/@angular/core/src/di/reflective_injector.ts b/modules/@angular/core/src/di/reflective_injector.ts index d64fb0f0ca..7ea356bfce 100644 --- a/modules/@angular/core/src/di/reflective_injector.ts +++ b/modules/@angular/core/src/di/reflective_injector.ts @@ -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_ = 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; } diff --git a/modules/@angular/core/src/di/reflective_provider.ts b/modules/@angular/core/src/di/reflective_provider.ts index fdc8505479..6bfd16a653 100644 --- a/modules/@angular/core/src/di/reflective_provider.ts +++ b/modules/@angular/core/src/di/reflective_provider.ts @@ -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); } diff --git a/modules/@angular/core/test/di/reflective_injector_spec.ts b/modules/@angular/core/test/di/reflective_injector_spec.ts index b96688103f..159cac1a66 100644 --- a/modules/@angular/core/test/di/reflective_injector_spec.ts +++ b/modules/@angular/core/test/di/reflective_injector_spec.ts @@ -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 parent.createChildFromResolved(resolvedProviders); - } else { - return ReflectiveInjector.fromResolvedProviders(resolvedProviders); - } + function createInjector( + providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ { + const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders)); + if (isPresent(parent)) { + return parent.createChildFromResolved(resolvedProviders); + } else { + return 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: 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(['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: 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(['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.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.resolveAndCreate([Engine, BrokenEngine])) + .displayName) + .toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])'); + }); + }); }