diff --git a/modules/angular2/angular2.api.ts b/modules/angular2/angular2.api.ts index f2e068cf89..ba4c757820 100644 --- a/modules/angular2/angular2.api.ts +++ b/modules/angular2/angular2.api.ts @@ -22,9 +22,8 @@ export {Form} from './src/forms/directives/form_interface'; export {TypeDecorator, ClassDefinition} from './src/util/decorators'; export {Query} from './src/core/annotations_impl/di'; export {ControlContainer} from './src/forms/directives/control_container'; -export {Injectable} from './src/di/annotations_impl'; +export {Injectable, Visibility} from './src/di/annotations_impl'; export {BaseQueryList} from './src/core/compiler/base_query_list'; export {AppProtoView, AppView, AppViewContainer} from './src/core/compiler/view'; export * from './src/change_detection/parser/ast'; -export {Visibility} from './src/core/annotations_impl/visibility'; export {AppViewManager} from './src/core/compiler/view_manager'; diff --git a/modules/angular2/angular2.ts b/modules/angular2/angular2.ts index 8db5ce4a11..18f92394cd 100644 --- a/modules/angular2/angular2.ts +++ b/modules/angular2/angular2.ts @@ -31,14 +31,13 @@ export { export { Inject, - InjectPromise, - InjectLazy, Optional, Injectable, forwardRef, resolveForwardRef, ForwardRefFn, Injector, + ProtoInjector, Binding, bind, Key, @@ -52,7 +51,12 @@ export { OpaqueToken, ResolvedBinding, BindingBuilder, - Dependency + Dependency, + Visibility, + Self, + Parent, + Ancestor, + Unbounded } from './di'; export * from './core'; diff --git a/modules/angular2/core.ts b/modules/angular2/core.ts index fd849ca850..c7ee651169 100644 --- a/modules/angular2/core.ts +++ b/modules/angular2/core.ts @@ -4,7 +4,6 @@ * @description * Define angular core API here. */ -export * from './src/core/annotations/visibility'; export * from './src/core/annotations/view'; export * from './src/core/application'; export * from './src/core/application_tokens'; @@ -19,4 +18,4 @@ export {ViewRef, ProtoViewRef} from './src/core/compiler/view_ref'; export {ViewContainerRef} from './src/core/compiler/view_container_ref'; export {ElementRef} from './src/core/compiler/element_ref'; -export {NgZone} from './src/core/zone/ng_zone'; +export {NgZone} from './src/core/zone/ng_zone'; \ No newline at end of file diff --git a/modules/angular2/di.ts b/modules/angular2/di.ts index b175e1112c..6ee7542059 100644 --- a/modules/angular2/di.ts +++ b/modules/angular2/di.ts @@ -7,16 +7,39 @@ export { InjectAnnotation, - InjectPromiseAnnotation, - InjectLazyAnnotation, OptionalAnnotation, InjectableAnnotation, - DependencyAnnotation + DependencyAnnotation, + VisibilityAnnotation, + SelfAnnotation, + ParentAnnotation, + AncestorAnnotation, + UnboundedAnnotation } from './src/di/annotations'; -export {Inject, InjectPromise, InjectLazy, Optional, Injectable} from './src/di/decorators'; +export { + Inject, + Optional, + Injectable, + Visibility, + Self, + Parent, + Ancestor, + Unbounded +} from './src/di/decorators'; +export {self} from './src/di/annotations_impl'; export {forwardRef, resolveForwardRef, ForwardRefFn} from './src/di/forward_ref'; -export {resolveBindings, Injector} from './src/di/injector'; +export { + resolveBindings, + Injector, + ProtoInjector, + PUBLIC_AND_PRIVATE, + PUBLIC, + PRIVATE, + undefinedValue, + InjectorInlineStrategy, + InjectorDynamicStrategy +} from './src/di/injector'; export {Binding, BindingBuilder, ResolvedBinding, Dependency, bind} from './src/di/binding'; export {Key, KeyRegistry, TypeLiteral} from './src/di/key'; export { @@ -26,6 +49,7 @@ export { CyclicDependencyError, InstantiationError, InvalidBindingError, - NoAnnotationError + NoAnnotationError, + OutOfBoundsError } from './src/di/exceptions'; export {OpaqueToken} from './src/di/opaque_token'; diff --git a/modules/angular2/src/core/annotations/decorators.dart b/modules/angular2/src/core/annotations/decorators.dart index 38168fafe7..f2976668a8 100644 --- a/modules/angular2/src/core/annotations/decorators.dart +++ b/modules/angular2/src/core/annotations/decorators.dart @@ -1,6 +1,5 @@ library angular2.core.decorators; export '../annotations_impl/annotations.dart'; -export '../annotations_impl/visibility.dart'; export '../annotations_impl/view.dart'; export '../annotations_impl/di.dart'; diff --git a/modules/angular2/src/core/annotations/decorators.ts b/modules/angular2/src/core/annotations/decorators.ts index 4dc0cec846..7359ab518d 100644 --- a/modules/angular2/src/core/annotations/decorators.ts +++ b/modules/angular2/src/core/annotations/decorators.ts @@ -5,12 +5,6 @@ import { DirectiveArgs } from './annotations'; import {ViewAnnotation, ViewArgs} from './view'; -import { - SelfAnnotation, - ParentAnnotation, - AncestorAnnotation, - UnboundedAnnotation -} from './visibility'; import {AttributeAnnotation, QueryAnnotation} from './di'; import {makeDecorator, makeParamDecorator, TypeDecorator, Class} from '../../util/decorators'; import {Type} from 'angular2/src/facade/lang'; @@ -46,12 +40,6 @@ export var Directive = makeDecorator(DirectiveAnnotation); /* from view */ export var View = makeDecorator(ViewAnnotation, (fn: any) => fn.View = View); -/* from visibility */ -export var Self = makeParamDecorator(SelfAnnotation); -export var Parent = makeParamDecorator(ParentAnnotation); -export var Ancestor = makeParamDecorator(AncestorAnnotation); -export var Unbounded = makeParamDecorator(UnboundedAnnotation); - /* from di */ export var Attribute = makeParamDecorator(AttributeAnnotation); export var Query = makeParamDecorator(QueryAnnotation); diff --git a/modules/angular2/src/core/annotations/visibility.dart b/modules/angular2/src/core/annotations/visibility.dart deleted file mode 100644 index 3e971511b7..0000000000 --- a/modules/angular2/src/core/annotations/visibility.dart +++ /dev/null @@ -1,3 +0,0 @@ -library angular2.core.annotations.visibility; - -export "../annotations_impl/visibility.dart"; diff --git a/modules/angular2/src/core/annotations/visibility.ts b/modules/angular2/src/core/annotations/visibility.ts deleted file mode 100644 index eded081cea..0000000000 --- a/modules/angular2/src/core/annotations/visibility.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - Self as SelfAnnotation, - Ancestor as AncestorAnnotation, - Parent as ParentAnnotation, - Unbounded as UnboundedAnnotation -} from '../annotations_impl/visibility'; diff --git a/modules/angular2/src/core/annotations_impl/annotations.ts b/modules/angular2/src/core/annotations_impl/annotations.ts index 952b4b7e52..3e2fac9baf 100644 --- a/modules/angular2/src/core/annotations_impl/annotations.ts +++ b/modules/angular2/src/core/annotations_impl/annotations.ts @@ -1,6 +1,6 @@ import {CONST, CONST_EXPR} from 'angular2/src/facade/lang'; import {List} from 'angular2/src/facade/collection'; -import {Injectable} from 'angular2/src/di/annotations_impl'; +import {Injectable, self} from 'angular2/src/di/annotations_impl'; import {DEFAULT} from 'angular2/change_detection'; // type StringMap = {[idx: string]: string}; @@ -788,7 +788,7 @@ export class Directive extends Injectable { selector, properties, events, host, lifecycle, hostInjector, exportAs, compileChildren = true, }: DirectiveArgs = {}) { - super(); + super(self); this.selector = selector; this.properties = properties; this.events = events; diff --git a/modules/angular2/src/core/annotations_impl/visibility.ts b/modules/angular2/src/core/annotations_impl/visibility.ts deleted file mode 100644 index a8daedaaaf..0000000000 --- a/modules/angular2/src/core/annotations_impl/visibility.ts +++ /dev/null @@ -1,213 +0,0 @@ -import {CONST, CONST_EXPR, isBlank} from 'angular2/src/facade/lang'; -import {DependencyAnnotation} from 'angular2/src/di/annotations_impl'; - -@CONST() -export class Visibility extends DependencyAnnotation { - constructor(public depth: number, public crossComponentBoundaries: boolean, - public _includeSelf: boolean) { - super(); - } - - get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; } - toString() { - return `@Visibility(depth: ${this.depth}, crossComponentBoundaries: ${this.crossComponentBoundaries}, includeSelf: ${this.includeSelf}})`; - } -} - -/** - * Specifies that an injector should retrieve a dependency from its element. - * - * ## Example - * - * Here is a simple directive that retrieves a dependency from its element. - * - * ``` - * @Directive({ - * selector: '[dependency]', - * properties: [ - * 'id: dependency' - * ] - * }) - * class Dependency { - * id:string; - * } - * - * - * @Directive({ - * selector: '[my-directive]' - * }) - * class Dependency { - * constructor(@Self() dependency:Dependency) { - * expect(dependency.id).toEqual(1); - * }; - * } - * ``` - * - * We use this with the following HTML template: - * - * ``` - *
- * ``` - * - * @exportedAs angular2/annotations - */ -@CONST() -export class Self extends Visibility { - constructor() { super(0, false, true); } - toString() { return `@Self()`; } -} - -// make constants after switching to ts2dart -export var self = new Self(); - -/** - * Specifies that an injector should retrieve a dependency from the direct parent. - * - * ## Example - * - * Here is a simple directive that retrieves a dependency from its parent element. - * - * ``` - * @Directive({ - * selector: '[dependency]', - * properties: [ - * 'id: dependency' - * ] - * }) - * class Dependency { - * id:string; - * } - * - * - * @Directive({ - * selector: '[my-directive]' - * }) - * class Dependency { - * constructor(@Parent() dependency:Dependency) { - * expect(dependency.id).toEqual(1); - * }; - * } - * ``` - * - * We use this with the following HTML template: - * - * ``` - *
- *
- *
- * ``` - * The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from - * the - * parent element (even thought the current element could resolve it): Angular injects - * `dependency=1`. - * - * @exportedAs angular2/annotations - */ -@CONST() -export class Parent extends Visibility { - constructor({self}: {self?: boolean} = {}) { super(1, false, self); } - toString() { return `@Parent(self: ${this.includeSelf}})`; } -} - -/** - * Specifies that an injector should retrieve a dependency from any ancestor element within the same - * shadow boundary. - * - * An ancestor is any element between the parent element and the shadow root. - * - * Use {@link Unbounded} if you need to cross upper shadow boundaries. - * - * ## Example - * - * Here is a simple directive that retrieves a dependency from an ancestor element. - * - * ``` - * @Directive({ - * selector: '[dependency]', - * properties: [ - * 'id: dependency' - * ] - * }) - * class Dependency { - * id:string; - * } - * - * - * @Directive({ - * selector: '[my-directive]' - * }) - * class Dependency { - * constructor(@Ancestor() dependency:Dependency) { - * expect(dependency.id).toEqual(2); - * }; - * } - * ``` - * - * We use this with the following HTML template: - * - * ``` - *
- *
- *
- *
- *
- *
- *
- * ``` - * - * The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency - * from the - * nearest ancestor element: - * - The current element `dependency="3"` is skipped because it is not an ancestor. - * - Next parent has no directives `
` - * - Next parent has the `Dependency` directive and so the dependency is satisfied. - * - * Angular injects `dependency=2`. - * - * @exportedAs angular2/annotations - */ -@CONST() -export class Ancestor extends Visibility { - constructor({self}: {self?: boolean} = {}) { super(999999, false, self); } - toString() { return `@Ancestor(self: ${this.includeSelf}})`; } -} - -/** - * Specifies that an injector should retrieve a dependency from any ancestor element, crossing - * component boundaries. - * - * Use {@link Ancestor} to look for ancestors within the current shadow boundary only. - * - * ## Example - * - * Here is a simple directive that retrieves a dependency from an ancestor element. - * - * ``` - * @Directive({ - * selector: '[dependency]', - * properties: [ - * 'id: dependency' - * ] - * }) - * class Dependency { - * id:string; - * } - * - * - * @Directive({ - * selector: '[my-directive]' - * }) - * class Dependency { - * constructor(@Unbounded() dependency:Dependency) { - * expect(dependency.id).toEqual(2); - * }; - * } - * ``` - * - * @exportedAs angular2/annotations - */ -@CONST() -export class Unbounded extends Visibility { - constructor({self}: {self?: boolean} = {}) { super(999999, true, self); } - toString() { return `@Unbounded(self: ${this.includeSelf}})`; } -} diff --git a/modules/angular2/src/core/application.ts b/modules/angular2/src/core/application.ts index a64fd5ad26..7d09cf0c2c 100644 --- a/modules/angular2/src/core/application.ts +++ b/modules/angular2/src/core/application.ts @@ -79,20 +79,19 @@ function _injectorBindings(appComponentType): List> { .toValue(DOM.defaultDoc()), bind(appComponentTypeToken).toValue(appComponentType), bind(appComponentRefToken) - .toAsyncFactory( + .toFactory( (dynamicComponentLoader, injector, testability, registry) => { - // TODO(rado): investigate whether to support bindings on root component. return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector) .then((componentRef) => { registry.registerApplication(componentRef.location.nativeElement, testability); - return componentRef; }); }, [DynamicComponentLoader, Injector, Testability, TestabilityRegistry]), - bind(appComponentType).toFactory((ref) => ref.instance, [appComponentRefToken]), + bind(appComponentType) + .toFactory((p: Promise) => p.then(ref => ref.instance), [appComponentRefToken]), bind(LifeCycle) .toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()), [ExceptionHandler]), @@ -293,20 +292,19 @@ export function bootstrap(appComponentType: Type, // index.html and main.js are possible. var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone); + var compRefToken: Promise = + PromiseWrapper.wrap(() => appInjector.get(appComponentRefToken)); + var tick = (componentRef) => { + var appChangeDetector = internalView(componentRef.hostView).changeDetector; + // retrieve life cycle: may have already been created if injected in root component + var lc = appInjector.get(LifeCycle); + lc.registerWith(zone, appChangeDetector); + lc.tick(); // the first tick that will bootstrap the app - PromiseWrapper.then( - appInjector.asyncGet(appComponentRefToken), - (componentRef) => { - var appChangeDetector = internalView(componentRef.hostView).changeDetector; - // retrieve life cycle: may have already been created if injected in root component - var lc = appInjector.get(LifeCycle); - lc.registerWith(zone, appChangeDetector); - lc.tick(); // the first tick that will bootstrap the app - - bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector)); - }, - - (err, stackTrace) => {bootstrapProcess.reject(err, stackTrace)}); + bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector)); + }; + PromiseWrapper.then(compRefToken, tick, + (err, stackTrace) => {bootstrapProcess.reject(err, stackTrace)}); }); return bootstrapProcess.promise; diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index aecdc6edf4..3528d9fa9f 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -11,6 +11,13 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import { Injector, + ProtoInjector, + PUBLIC_AND_PRIVATE, + PUBLIC, + PRIVATE, + undefinedValue, + InjectorInlineStrategy, + InjectorDynamicStrategy, Key, Dependency, bind, @@ -20,10 +27,14 @@ import { AbstractBindingError, CyclicDependencyError, resolveForwardRef, - resolveBindings + resolveBindings, + Visibility, + VisibilityAnnotation, + self } from 'angular2/di'; -import {Visibility, self} from 'angular2/src/core/annotations_impl/visibility'; + import {Attribute, Query} from 'angular2/src/core/annotations_impl/di'; + import * as viewModule from './view'; import * as avmModule from './view_manager'; import {ViewContainerRef} from './view_container_ref'; @@ -44,11 +55,6 @@ import {QueryList} from './query_list'; import {reflector} from 'angular2/src/reflection/reflection'; import {DirectiveMetadata} from 'angular2/src/render/api'; -// Threshold for the dynamic version -var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10; - -const _undefined = CONST_EXPR(new Object()); - var _staticKeys; class StaticKeys { @@ -161,28 +167,10 @@ export class TreeNode> { } } -export class DependencyWithVisibility extends Dependency { - constructor(key: Key, asPromise: boolean, lazy: boolean, optional: boolean, properties: List, - public visibility: Visibility) { - super(key, asPromise, lazy, optional, properties); - } - - static createFrom(d: Dependency): Dependency { - return new DependencyWithVisibility(d.key, d.asPromise, d.lazy, d.optional, d.properties, - DependencyWithVisibility._visibility(d.properties)); - } - - static _visibility(properties): Visibility { - if (properties.length == 0) return self; - var p = ListWrapper.find(properties, p => p instanceof Visibility); - return isPresent(p) ? p : self; - } -} - -export class DirectiveDependency extends DependencyWithVisibility { - constructor(key: Key, asPromise: boolean, lazy: boolean, optional: boolean, properties: List, - visibility: Visibility, public attributeName: string, public queryDecorator: Query) { - super(key, asPromise, lazy, optional, properties, visibility); +export class DirectiveDependency extends Dependency { + constructor(key: Key, optional: boolean, visibility: any, properties: List, + public attributeName: string, public queryDecorator: Query) { + super(key, optional, visibility, properties); this._verify(); } @@ -196,8 +184,7 @@ export class DirectiveDependency extends DependencyWithVisibility { } static createFrom(d: Dependency): Dependency { - return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties, - DependencyWithVisibility._visibility(d.properties), + return new DirectiveDependency(d.key, d.optional, d.visibility, d.properties, DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties)); } @@ -212,11 +199,11 @@ export class DirectiveDependency extends DependencyWithVisibility { export class DirectiveBinding extends ResolvedBinding { constructor(key: Key, factory: Function, dependencies: List, - providedAsPromise: boolean, public resolvedAppInjectables: List, + public resolvedAppInjectables: List, public resolvedHostInjectables: List, public resolvedViewInjectables: List, public metadata: DirectiveMetadata) { - super(key, factory, dependencies, providedAsPromise); + super(key, factory, dependencies); } get callOnDestroy(): boolean { return this.metadata.callOnDestroy; } @@ -276,9 +263,8 @@ export class DirectiveBinding extends ResolvedBinding { exportAs: ann.exportAs }); - return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, - resolvedAppInjectables, resolvedHostInjectables, - resolvedViewInjectables, metadata); + return new DirectiveBinding(rb.key, rb.factory, deps, resolvedAppInjectables, + resolvedHostInjectables, resolvedViewInjectables, metadata); } static _readAttributes(deps) { @@ -325,10 +311,6 @@ export class HostActionAccessor { } } -const LIGHT_DOM = 1; -const SHADOW_DOM = 2; -const LIGHT_DOM_AND_SHADOW_DOM = 3; - export class BindingData { constructor(public binding: ResolvedBinding, public visibility: number) {} @@ -364,33 +346,12 @@ export class BindingData { } } -/** - -Difference between di.Injector and ElementInjector - -di.Injector: - - imperative based (can create child injectors imperativly) - - Lazy loading of code - - Component/App Level services which are usually not DOM Related. - - -ElementInjector: - - ProtoBased (Injector structure fixed at compile time) - - understands @Ancestor, @Parent, @Child, @Descendent - - Fast - - Query mechanism for children - - 1:1 to DOM structure. - - PERF BENCHMARK: -http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/ - */ export class ProtoElementInjector { view: viewModule.AppView; attributes: Map; eventEmitterAccessors: List>; hostActionAccessors: List>; - - _strategy: _ProtoElementInjectorStrategy; + protoInjector: ProtoInjector; static create(parent: ProtoElementInjector, index: number, bindings: List, firstBindingIsComponent: boolean, distanceToParent: number, @@ -421,40 +382,39 @@ export class ProtoElementInjector { ListWrapper.forEach(dirBindings, dirBinding => { ListWrapper.forEach(dirBinding.resolvedHostInjectables, b => { bd.push(ProtoElementInjector._createBindingData(firstBindingIsComponent, dirBinding, - dirBindings, - ProtoElementInjector._createBinding(b))); + dirBindings, b)); }); }); } private static _createBindingData(firstBindingIsComponent, dirBinding, dirBindings, binding) { var isComponent = firstBindingIsComponent && dirBindings[0] === dirBinding; - return new BindingData(binding, isComponent ? LIGHT_DOM_AND_SHADOW_DOM : LIGHT_DOM); + return new BindingData(binding, isComponent ? PUBLIC_AND_PRIVATE : PUBLIC); } private static _createViewInjectorBindingData(bindings: List, bd: List) { var db = bindings[0]; - ListWrapper.forEach( - db.resolvedViewInjectables, - b => bd.push(new BindingData(ProtoElementInjector._createBinding(b), SHADOW_DOM))); + ListWrapper.forEach(db.resolvedViewInjectables, b => bd.push(new BindingData(b, PRIVATE))); } - private static _createBinding(b: ResolvedBinding) { - var deps = ListWrapper.map(b.dependencies, d => DependencyWithVisibility.createFrom(d)); - return new ResolvedBinding(b.key, b.factory, deps, b.providedAsPromise); - } + constructor(public parent: ProtoElementInjector, public index: int, bd: List, public distanceToParent: number, public _firstBindingIsComponent: boolean, public directiveVariableBindings: Map) { var length = bd.length; + + this.protoInjector = + new ProtoInjector(isPresent(parent) ? parent.protoInjector : null, bd, distanceToParent); + this.eventEmitterAccessors = ListWrapper.createFixedSize(length); this.hostActionAccessors = ListWrapper.createFixedSize(length); - this._strategy = length > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER ? - new _ProtoElementInjectorDynamicStrategy(this, bd) : - new _ProtoElementInjectorInlineStrategy(this, bd); + for (var i = 0; i < length; ++i) { + this.eventEmitterAccessors[i] = bd[i].createEventEmitterAccessors(); + this.hostActionAccessors[i] = bd[i].createHostActionAccessors(); + } } instantiate(parent: ElementInjector): ElementInjector { @@ -463,191 +423,11 @@ export class ProtoElementInjector { directParent(): ProtoElementInjector { return this.distanceToParent < 2 ? this.parent : null; } - get hasBindings(): boolean { return this._strategy.hasBindings(); } + get hasBindings(): boolean { return this.eventEmitterAccessors.length > 0; } - getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); } + getBindingAtIndex(index: number): any { return this.protoInjector.getBindingAtIndex(index); } } -interface _ProtoElementInjectorStrategy { - hasBindings(): boolean; - getBindingAtIndex(index: number): any; - createElementInjectorStrategy(ei: ElementInjector): _ElementInjectorStrategy; -} - -/** - * Strategy used by the `ProtoElementInjector` when the number of bindings is 10 or less. - * In such a case, inlining fields is benefitial for performances. - */ -class _ProtoElementInjectorInlineStrategy implements _ProtoElementInjectorStrategy { - // only _binding0 can contain a component - _binding0: ResolvedBinding = null; - _binding1: ResolvedBinding = null; - _binding2: ResolvedBinding = null; - _binding3: ResolvedBinding = null; - _binding4: ResolvedBinding = null; - _binding5: ResolvedBinding = null; - _binding6: ResolvedBinding = null; - _binding7: ResolvedBinding = null; - _binding8: ResolvedBinding = null; - _binding9: ResolvedBinding = 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; - - _visibility0: number = null; - _visibility1: number = null; - _visibility2: number = null; - _visibility3: number = null; - _visibility4: number = null; - _visibility5: number = null; - _visibility6: number = null; - _visibility7: number = null; - _visibility8: number = null; - _visibility9: number = null; - - constructor(protoEI: ProtoElementInjector, bd: List) { - var length = bd.length; - - if (length > 0) { - this._binding0 = bd[0].binding; - this._keyId0 = bd[0].getKeyId(); - this._visibility0 = bd[0].visibility; - protoEI.eventEmitterAccessors[0] = bd[0].createEventEmitterAccessors(); - protoEI.hostActionAccessors[0] = bd[0].createHostActionAccessors(); - } - if (length > 1) { - this._binding1 = bd[1].binding; - this._keyId1 = bd[1].getKeyId(); - this._visibility1 = bd[1].visibility; - protoEI.eventEmitterAccessors[1] = bd[1].createEventEmitterAccessors(); - protoEI.hostActionAccessors[1] = bd[1].createHostActionAccessors(); - } - if (length > 2) { - this._binding2 = bd[2].binding; - this._keyId2 = bd[2].getKeyId(); - this._visibility2 = bd[2].visibility; - protoEI.eventEmitterAccessors[2] = bd[2].createEventEmitterAccessors(); - protoEI.hostActionAccessors[2] = bd[2].createHostActionAccessors(); - } - if (length > 3) { - this._binding3 = bd[3].binding; - this._keyId3 = bd[3].getKeyId(); - this._visibility3 = bd[3].visibility; - protoEI.eventEmitterAccessors[3] = bd[3].createEventEmitterAccessors(); - protoEI.hostActionAccessors[3] = bd[3].createHostActionAccessors(); - } - if (length > 4) { - this._binding4 = bd[4].binding; - this._keyId4 = bd[4].getKeyId(); - this._visibility4 = bd[4].visibility; - protoEI.eventEmitterAccessors[4] = bd[4].createEventEmitterAccessors(); - protoEI.hostActionAccessors[4] = bd[4].createHostActionAccessors(); - } - if (length > 5) { - this._binding5 = bd[5].binding; - this._keyId5 = bd[5].getKeyId(); - this._visibility5 = bd[5].visibility; - protoEI.eventEmitterAccessors[5] = bd[5].createEventEmitterAccessors(); - protoEI.hostActionAccessors[5] = bd[5].createHostActionAccessors(); - } - if (length > 6) { - this._binding6 = bd[6].binding; - this._keyId6 = bd[6].getKeyId(); - this._visibility6 = bd[6].visibility; - protoEI.eventEmitterAccessors[6] = bd[6].createEventEmitterAccessors(); - protoEI.hostActionAccessors[6] = bd[6].createHostActionAccessors(); - } - if (length > 7) { - this._binding7 = bd[7].binding; - this._keyId7 = bd[7].getKeyId(); - this._visibility7 = bd[7].visibility; - protoEI.eventEmitterAccessors[7] = bd[7].createEventEmitterAccessors(); - protoEI.hostActionAccessors[7] = bd[7].createHostActionAccessors(); - } - if (length > 8) { - this._binding8 = bd[8].binding; - this._keyId8 = bd[8].getKeyId(); - this._visibility8 = bd[8].visibility; - protoEI.eventEmitterAccessors[8] = bd[8].createEventEmitterAccessors(); - protoEI.hostActionAccessors[8] = bd[8].createHostActionAccessors(); - } - if (length > 9) { - this._binding9 = bd[9].binding; - this._keyId9 = bd[9].getKeyId(); - this._visibility9 = bd[9].visibility; - protoEI.eventEmitterAccessors[9] = bd[9].createEventEmitterAccessors(); - protoEI.hostActionAccessors[9] = bd[9].createHostActionAccessors(); - } - } - - hasBindings(): boolean { return isPresent(this._binding0); } - - getBindingAtIndex(index: number): any { - if (index == 0) return this._binding0; - if (index == 1) return this._binding1; - if (index == 2) return this._binding2; - if (index == 3) return this._binding3; - if (index == 4) return this._binding4; - if (index == 5) return this._binding5; - if (index == 6) return this._binding6; - if (index == 7) return this._binding7; - if (index == 8) return this._binding8; - if (index == 9) return this._binding9; - throw new OutOfBoundsAccess(index); - } - - createElementInjectorStrategy(ei: ElementInjector): _ElementInjectorStrategy { - return new ElementInjectorInlineStrategy(this, ei); - } -} - -/** - * Strategy used by the `ProtoElementInjector` when the number of bindings is more than 10. - */ -class _ProtoElementInjectorDynamicStrategy implements _ProtoElementInjectorStrategy { - // only _bindings[0] can contain a component - _bindings: List; - _keyIds: List; - _visibilities: List; - - constructor(protoInj: ProtoElementInjector, bd: List) { - var len = bd.length; - - this._bindings = ListWrapper.createFixedSize(len); - this._keyIds = ListWrapper.createFixedSize(len); - this._visibilities = ListWrapper.createFixedSize(len); - - for (var i = 0; i < len; i++) { - this._bindings[i] = bd[i].binding; - this._keyIds[i] = bd[i].getKeyId(); - this._visibilities[i] = bd[i].visibility; - protoInj.eventEmitterAccessors[i] = bd[i].createEventEmitterAccessors(); - protoInj.hostActionAccessors[i] = bd[i].createHostActionAccessors(); - } - } - - hasBindings(): boolean { return isPresent(this._bindings[0]); } - - getBindingAtIndex(index: number): any { - if (index < 0 || index >= this._bindings.length) { - throw new OutOfBoundsAccess(index); - } - - return this._bindings[index]; - } - - createElementInjectorStrategy(ei: ElementInjector) { - return new ElementInjectorDynamicStrategy(this, ei); - } -} export class ElementInjector extends TreeNode { private _lightDomAppInjector: Injector = null; @@ -655,7 +435,6 @@ export class ElementInjector extends TreeNode { private _host: ElementInjector; private _preBuiltObjects = null; - private _constructionCounter: number = 0; // Queries are added during construction or linking with a new parent. // They are never removed. @@ -665,13 +444,22 @@ export class ElementInjector extends TreeNode { hydrated: boolean; - _strategy: _ElementInjectorStrategy; + private _injector: Injector; + private _strategy: _ElementInjectorStrategy; constructor(public _proto: ProtoElementInjector, parent: ElementInjector) { super(parent); - this._strategy = _proto._strategy.createElementInjectorStrategy(this); - this._constructionCounter = 0; + this._injector = new Injector(this._proto.protoInjector); + + // we couple ourselves to the injector strategy to avoid polymoprhic calls + var injectorStrategy = this._injector.strategy; + this._strategy = injectorStrategy instanceof InjectorInlineStrategy ? + new ElementInjectorInlineStrategy( + injectorStrategy, this) : + new ElementInjectorDynamicStrategy( + injectorStrategy, this); + this.hydrated = false; this._buildQueries(); @@ -685,8 +473,7 @@ export class ElementInjector extends TreeNode { this._lightDomAppInjector = null; this._shadowDomAppInjector = null; this._strategy.callOnDestroy(); - this._strategy.clearInstances(); - this._constructionCounter = 0; + this._injector.dehydrate(); } onAllChangesDone(): void { @@ -715,8 +502,12 @@ export class ElementInjector extends TreeNode { this._checkShadowDomAppInjector(this._shadowDomAppInjector); - this._strategy.hydrate(); + var parentInjector = isPresent(this._parent) ? this._parent._injector : null; + var hostInjector = isPresent(host) ? host._injector : null; + this._injector.hydrate(parentInjector, hostInjector, this); + + this._addDirectivesToQueries(); this._addVarBindingsToQueries(); this.hydrated = true; @@ -751,11 +542,9 @@ export class ElementInjector extends TreeNode { } } - get(token): any { return this._getByKey(Key.get(token), self, false, null); } + get(token): any { return this._injector.get(token); } - hasDirective(type: Type): boolean { - return this._strategy.getObjByKeyId(Key.get(type).id, LIGHT_DOM_AND_SHADOW_DOM) !== _undefined; - } + hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); } getEventEmitterAccessors(): List> { return this._proto.eventEmitterAccessors; @@ -779,95 +568,36 @@ export class ElementInjector extends TreeNode { directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; } - private _isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); } + isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); } - _new(binding: ResolvedBinding): any { - if (this._constructionCounter++ > this._strategy.getMaxDirectives()) { - throw new CyclicDependencyError(binding.key); - } + getDependency(dep: any): any { + var key = dep.key; - var factory = binding.factory; - var deps = >binding.dependencies; - var length = deps.length; - - var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; - try { - d0 = length > 0 ? this._getByDependency(deps[0], binding.key) : null; - d1 = length > 1 ? this._getByDependency(deps[1], binding.key) : null; - d2 = length > 2 ? this._getByDependency(deps[2], binding.key) : null; - d3 = length > 3 ? this._getByDependency(deps[3], binding.key) : null; - d4 = length > 4 ? this._getByDependency(deps[4], binding.key) : null; - d5 = length > 5 ? this._getByDependency(deps[5], binding.key) : null; - d6 = length > 6 ? this._getByDependency(deps[6], binding.key) : null; - d7 = length > 7 ? this._getByDependency(deps[7], binding.key) : null; - d8 = length > 8 ? this._getByDependency(deps[8], binding.key) : null; - d9 = length > 9 ? this._getByDependency(deps[9], binding.key) : null; - } catch (e) { - if (e instanceof AbstractBindingError) e.addKey(binding.key); - throw e; - } - - var obj; - 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; - } - - this._addToQueries(obj, binding.key.token); - - return obj; - } - - private _getByDependency(dep: DependencyWithVisibility, requestor: Key): any { - if (!(dep instanceof DirectiveDependency)) { - return this._getByKey(dep.key, dep.visibility, dep.optional, requestor); - } + if (!(dep instanceof DirectiveDependency)) return undefinedValue; var dirDep = dep; + var staticKeys = StaticKeys.instance(); + + + if (key.id === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager; if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep); + if (isPresent(dirDep.queryDecorator)) return this._findQuery(dirDep.queryDecorator).list; + if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) { var componentView = this._preBuiltObjects.view.componentChildViews[this._proto.index]; return componentView.changeDetector.ref; } + if (dirDep.key.id === StaticKeys.instance().elementRefId) { return this.getElementRef(); } + if (dirDep.key.id === StaticKeys.instance().viewContainerId) { return this.getViewContainerRef(); } + if (dirDep.key.id === StaticKeys.instance().protoViewId) { if (isBlank(this._preBuiltObjects.protoView)) { if (dirDep.optional) { @@ -878,7 +608,8 @@ export class ElementInjector extends TreeNode { } return new ProtoViewRef(this._preBuiltObjects.protoView); } - return this._getByKey(dirDep.key, dirDep.visibility, dirDep.optional, requestor); + + return undefinedValue; } private _buildAttribute(dep: DirectiveDependency): string { @@ -905,6 +636,12 @@ export class ElementInjector extends TreeNode { this._addVarBindingsToQuery(this._query2); } + private _addDirectivesToQueries(): void { + this._addDirectivesToQuery(this._query0); + this._addDirectivesToQuery(this._query1); + this._addDirectivesToQuery(this._query2); + } + private _addVarBindingsToQuery(queryRef: QueryRef): void { if (isBlank(queryRef) || !queryRef.query.isVarBindingQuery) return; @@ -916,6 +653,14 @@ export class ElementInjector extends TreeNode { } } + private _addDirectivesToQuery(queryRef: QueryRef): void { + if (isBlank(queryRef) || queryRef.query.isVarBindingQuery) return; + + var matched = []; + this.addDirectivesMatchingQuery(queryRef.query, matched); + matched.forEach(s => queryRef.list.add(s)); + } + private _createQueryRef(query: Query): void { var queryList = new QueryList(); if (isBlank(this._query0)) { @@ -924,19 +669,8 @@ export class ElementInjector extends TreeNode { this._query1 = new QueryRef(query, queryList, this); } else if (isBlank(this._query2)) { this._query2 = new QueryRef(query, queryList, this); - } else + } else { throw new QueryError(); - } - - private _addToQueries(obj, token): void { - if (isPresent(this._query0) && (this._query0.query.selector === token)) { - this._query0.list.add(obj); - } - if (isPresent(this._query1) && (this._query1.query.selector === token)) { - this._query1.list.add(obj); - } - if (isPresent(this._query2) && (this._query2.query.selector === token)) { - this._query2.list.add(obj); } } @@ -1066,78 +800,17 @@ export class ElementInjector extends TreeNode { if (this._query2 == query) this._query2 = null; } - private _getByKey(key: Key, visibility: Visibility, optional: boolean, requestor: Key): any { - var ei = this; - - var currentVisibility = this._isComponentKey(requestor) ? - LIGHT_DOM_AND_SHADOW_DOM : // component can access both shadow dom - // and light dom dependencies - LIGHT_DOM; - - var depth = visibility.depth; - - if (!visibility.includeSelf) { - depth -= ei._proto.distanceToParent; - - if (isPresent(ei._parent)) { - ei = ei._parent; - } else { - ei = ei._host; - currentVisibility = visibility.crossComponentBoundaries ? LIGHT_DOM : SHADOW_DOM; - } - } - - while (ei != null && depth >= 0) { - var preBuiltObj = ei._getPreBuiltObjectByKeyId(key.id); - if (preBuiltObj !== _undefined) return preBuiltObj; - - var dir = ei._getObjByKeyId(key.id, currentVisibility); - if (dir !== _undefined) return dir; - - depth -= ei._proto.distanceToParent; - - // we check only one mode with the SHADOW_DOM visibility - if (currentVisibility === SHADOW_DOM) break; - - if (isPresent(ei._parent)) { - ei = ei._parent; - } else { - ei = ei._host; - currentVisibility = visibility.crossComponentBoundaries ? LIGHT_DOM : SHADOW_DOM; - } - } - - if (isPresent(this._host) && this._host._isComponentKey(key)) { - return this._host.getComponent(); - } else if (optional) { - return this._appInjector(requestor).getOptional(key); - } else { - return this._appInjector(requestor).get(key); - } - } - - private _appInjector(requestor: Key): Injector { - if (isPresent(requestor) && this._isComponentKey(requestor)) { + appInjector(requestor: Key): Injector { + if (isPresent(requestor) && this.isComponentKey(requestor)) { return this._shadowDomAppInjector; } else { return this._lightDomAppInjector; } } - private _getPreBuiltObjectByKeyId(keyId: number): any { - var staticKeys = StaticKeys.instance(); - if (keyId === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager; + getDirectiveAtIndex(index: number) { return this._injector.getObjAtIndex(index); } - return _undefined; - } - - private _getObjByKeyId(keyId: number, visibility: number) { - return this._strategy.getObjByKeyId(keyId, visibility); - } - - getDirectiveAtIndex(index: number) { return this._strategy.getDirectiveAtIndex(index); } - - hasInstances(): boolean { return this._constructionCounter > 0; } + hasInstances(): boolean { return this._proto.hasBindings && this.hydrated; } getLightDomAppInjector(): Injector { return this._lightDomAppInjector; } @@ -1150,16 +823,11 @@ export class ElementInjector extends TreeNode { interface _ElementInjectorStrategy { callOnDestroy(): void; - clearInstances(): void; - hydrate(): void; getComponent(): any; isComponentKey(key: Key): boolean; buildQueries(): void; addDirectivesMatchingQuery(q: Query, res: any[]): void; - getObjByKeyId(keyId: number, visibility: number): any; - getDirectiveAtIndex(index: number): any; getComponentBinding(): DirectiveBinding; - getMaxDirectives(): number; } /** @@ -1167,226 +835,106 @@ interface _ElementInjectorStrategy { * In such a case, inlining fields is benefitial for performances. */ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy { - // If this element injector has a component, the component instance will be stored in _obj0 - _obj0: any = null; - _obj1: any = null; - _obj2: any = null; - _obj3: any = null; - _obj4: any = null; - _obj5: any = null; - _obj6: any = null; - _obj7: any = null; - _obj8: any = null; - _obj9: any = null; - - constructor(public _protoStrategy: _ProtoElementInjectorInlineStrategy, - public _ei: ElementInjector) {} + constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: ElementInjector) {} callOnDestroy(): void { - var p = this._protoStrategy; + var i = this.injectorStrategy; + var p = i.protoStrategy; - if (p._binding0 instanceof DirectiveBinding && (p._binding0).callOnDestroy) { - this._obj0.onDestroy(); + if (p.binding0 instanceof DirectiveBinding && (p.binding0).callOnDestroy) { + i.obj0.onDestroy(); } - if (p._binding1 instanceof DirectiveBinding && (p._binding1).callOnDestroy) { - this._obj1.onDestroy(); + if (p.binding1 instanceof DirectiveBinding && (p.binding1).callOnDestroy) { + i.obj1.onDestroy(); } - if (p._binding2 instanceof DirectiveBinding && (p._binding2).callOnDestroy) { - this._obj2.onDestroy(); + if (p.binding2 instanceof DirectiveBinding && (p.binding2).callOnDestroy) { + i.obj2.onDestroy(); } - if (p._binding3 instanceof DirectiveBinding && (p._binding3).callOnDestroy) { - this._obj3.onDestroy(); + if (p.binding3 instanceof DirectiveBinding && (p.binding3).callOnDestroy) { + i.obj3.onDestroy(); } - if (p._binding4 instanceof DirectiveBinding && (p._binding4).callOnDestroy) { - this._obj4.onDestroy(); + if (p.binding4 instanceof DirectiveBinding && (p.binding4).callOnDestroy) { + i.obj4.onDestroy(); } - if (p._binding5 instanceof DirectiveBinding && (p._binding5).callOnDestroy) { - this._obj5.onDestroy(); + if (p.binding5 instanceof DirectiveBinding && (p.binding5).callOnDestroy) { + i.obj5.onDestroy(); } - if (p._binding6 instanceof DirectiveBinding && (p._binding6).callOnDestroy) { - this._obj6.onDestroy(); + if (p.binding6 instanceof DirectiveBinding && (p.binding6).callOnDestroy) { + i.obj6.onDestroy(); } - if (p._binding7 instanceof DirectiveBinding && (p._binding7).callOnDestroy) { - this._obj7.onDestroy(); + if (p.binding7 instanceof DirectiveBinding && (p.binding7).callOnDestroy) { + i.obj7.onDestroy(); } - if (p._binding8 instanceof DirectiveBinding && (p._binding8).callOnDestroy) { - this._obj8.onDestroy(); + if (p.binding8 instanceof DirectiveBinding && (p.binding8).callOnDestroy) { + i.obj8.onDestroy(); } - if (p._binding9 instanceof DirectiveBinding && (p._binding9).callOnDestroy) { - this._obj9.onDestroy(); + if (p.binding9 instanceof DirectiveBinding && (p.binding9).callOnDestroy) { + i.obj9.onDestroy(); } } - clearInstances(): void { - this._obj0 = null; - this._obj1 = null; - this._obj2 = null; - this._obj3 = null; - this._obj4 = null; - this._obj5 = null; - this._obj6 = null; - this._obj7 = null; - this._obj8 = null; - this._obj9 = null; - } - - hydrate(): void { - var p = this._protoStrategy; - var e = this._ei; - - if (isPresent(p._keyId0) && isBlank(this._obj0)) this._obj0 = e._new(p._binding0); - if (isPresent(p._keyId1) && isBlank(this._obj1)) this._obj1 = e._new(p._binding1); - if (isPresent(p._keyId2) && isBlank(this._obj2)) this._obj2 = e._new(p._binding2); - if (isPresent(p._keyId3) && isBlank(this._obj3)) this._obj3 = e._new(p._binding3); - if (isPresent(p._keyId4) && isBlank(this._obj4)) this._obj4 = e._new(p._binding4); - if (isPresent(p._keyId5) && isBlank(this._obj5)) this._obj5 = e._new(p._binding5); - if (isPresent(p._keyId6) && isBlank(this._obj6)) this._obj6 = e._new(p._binding6); - if (isPresent(p._keyId7) && isBlank(this._obj7)) this._obj7 = e._new(p._binding7); - if (isPresent(p._keyId8) && isBlank(this._obj8)) this._obj8 = e._new(p._binding8); - if (isPresent(p._keyId9) && isBlank(this._obj9)) this._obj9 = e._new(p._binding9); - } - - getComponent(): any { return this._obj0; } + getComponent(): any { return this.injectorStrategy.obj0; } isComponentKey(key: Key): boolean { return this._ei._proto._firstBindingIsComponent && isPresent(key) && - key.id === this._protoStrategy._keyId0; + key.id === this.injectorStrategy.protoStrategy.keyId0; } buildQueries(): void { - var p = this._protoStrategy; - if (p._binding0 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding0.dependencies); + var p = this.injectorStrategy.protoStrategy; + + if (p.binding0 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding0.dependencies); } - if (p._binding1 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding1.dependencies); + if (p.binding1 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding1.dependencies); } - if (p._binding2 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding2.dependencies); + if (p.binding2 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding2.dependencies); } - if (p._binding3 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding3.dependencies); + if (p.binding3 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding3.dependencies); } - if (p._binding4 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding4.dependencies); + if (p.binding4 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding4.dependencies); } - if (p._binding5 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding5.dependencies); + if (p.binding5 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding5.dependencies); } - if (p._binding6 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding6.dependencies); + if (p.binding6 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding6.dependencies); } - if (p._binding7 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding7.dependencies); + if (p.binding7 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding7.dependencies); } - if (p._binding8 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding8.dependencies); + if (p.binding8 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding8.dependencies); } - if (p._binding9 instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._binding9.dependencies); + if (p.binding9 instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.binding9.dependencies); } } addDirectivesMatchingQuery(query: Query, list: any[]): void { - var p = this._protoStrategy; - if (isPresent(p._binding0) && p._binding0.key.token === query.selector) list.push(this._obj0); - if (isPresent(p._binding1) && p._binding1.key.token === query.selector) list.push(this._obj1); - if (isPresent(p._binding2) && p._binding2.key.token === query.selector) list.push(this._obj2); - if (isPresent(p._binding3) && p._binding3.key.token === query.selector) list.push(this._obj3); - if (isPresent(p._binding4) && p._binding4.key.token === query.selector) list.push(this._obj4); - if (isPresent(p._binding5) && p._binding5.key.token === query.selector) list.push(this._obj5); - if (isPresent(p._binding6) && p._binding6.key.token === query.selector) list.push(this._obj6); - if (isPresent(p._binding7) && p._binding7.key.token === query.selector) list.push(this._obj7); - if (isPresent(p._binding8) && p._binding8.key.token === query.selector) list.push(this._obj8); - if (isPresent(p._binding9) && p._binding9.key.token === query.selector) list.push(this._obj9); - } + var i = this.injectorStrategy; + var p = i.protoStrategy; - getObjByKeyId(keyId: number, visibility: number): any { - var p = this._protoStrategy; - - if (p._keyId0 === keyId && (p._visibility0 & visibility) > 0) { - if (isBlank(this._obj0)) { - this._obj0 = this._ei._new(p._binding0); - } - return this._obj0; - } - if (p._keyId1 === keyId && (p._visibility1 & visibility) > 0) { - if (isBlank(this._obj1)) { - this._obj1 = this._ei._new(p._binding1); - } - return this._obj1; - } - if (p._keyId2 === keyId && (p._visibility2 & visibility) > 0) { - if (isBlank(this._obj2)) { - this._obj2 = this._ei._new(p._binding2); - } - return this._obj2; - } - if (p._keyId3 === keyId && (p._visibility3 & visibility) > 0) { - if (isBlank(this._obj3)) { - this._obj3 = this._ei._new(p._binding3); - } - return this._obj3; - } - if (p._keyId4 === keyId && (p._visibility4 & visibility) > 0) { - if (isBlank(this._obj4)) { - this._obj4 = this._ei._new(p._binding4); - } - return this._obj4; - } - if (p._keyId5 === keyId && (p._visibility5 & visibility) > 0) { - if (isBlank(this._obj5)) { - this._obj5 = this._ei._new(p._binding5); - } - return this._obj5; - } - if (p._keyId6 === keyId && (p._visibility6 & visibility) > 0) { - if (isBlank(this._obj6)) { - this._obj6 = this._ei._new(p._binding6); - } - return this._obj6; - } - if (p._keyId7 === keyId && (p._visibility7 & visibility) > 0) { - if (isBlank(this._obj7)) { - this._obj7 = this._ei._new(p._binding7); - } - return this._obj7; - } - if (p._keyId8 === keyId && (p._visibility8 & visibility) > 0) { - if (isBlank(this._obj8)) { - this._obj8 = this._ei._new(p._binding8); - } - return this._obj8; - } - if (p._keyId9 === keyId && (p._visibility9 & visibility) > 0) { - if (isBlank(this._obj9)) { - this._obj9 = this._ei._new(p._binding9); - } - return this._obj9; - } - - return _undefined; - } - - getDirectiveAtIndex(index: number) { - 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 OutOfBoundsAccess(index); + if (isPresent(p.binding0) && p.binding0.key.token === query.selector) list.push(i.obj0); + if (isPresent(p.binding1) && p.binding1.key.token === query.selector) list.push(i.obj1); + if (isPresent(p.binding2) && p.binding2.key.token === query.selector) list.push(i.obj2); + if (isPresent(p.binding3) && p.binding3.key.token === query.selector) list.push(i.obj3); + if (isPresent(p.binding4) && p.binding4.key.token === query.selector) list.push(i.obj4); + if (isPresent(p.binding5) && p.binding5.key.token === query.selector) list.push(i.obj5); + if (isPresent(p.binding6) && p.binding6.key.token === query.selector) list.push(i.obj6); + if (isPresent(p.binding7) && p.binding7.key.token === query.selector) list.push(i.obj7); + if (isPresent(p.binding8) && p.binding8.key.token === query.selector) list.push(i.obj8); + if (isPresent(p.binding9) && p.binding9.key.token === query.selector) list.push(i.obj9); } getComponentBinding(): DirectiveBinding { - return this._protoStrategy._binding0; + var p = this.injectorStrategy.protoStrategy; + return p.binding0; } - - getMaxDirectives(): number { return _MAX_DIRECTIVE_CONSTRUCTION_COUNTER; } } /** @@ -1394,106 +942,53 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy { * In such a case, inlining fields is benefitial for performances. */ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy { - // If this element injector has a component, the component instance will be stored in _objs[0] - _objs: List; - - constructor(public _protoStrategy: _ProtoElementInjectorDynamicStrategy, - public _ei: ElementInjector) { - this._objs = ListWrapper.createFixedSize(_protoStrategy._bindings.length); - } + constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: ElementInjector) {} callOnDestroy(): void { - var p = this._protoStrategy; + var ist = this.injectorStrategy; + var p = ist.protoStrategy; - for (var i = 0; i < p._bindings.length; i++) { - if (p._bindings[i] instanceof DirectiveBinding && - (p._bindings[i]).callOnDestroy) { - this._objs[i].onDestroy(); + for (var i = 0; i < p.bindings.length; i++) { + if (p.bindings[i] instanceof DirectiveBinding && + (p.bindings[i]).callOnDestroy) { + ist.objs[i].onDestroy(); } } } - clearInstances(): void { ListWrapper.fill(this._objs, null); } - - hydrate(): void { - var p = this._protoStrategy; - - for (var i = 0; i < p._keyIds.length; i++) { - if (isPresent(p._keyIds[i]) && isBlank(this._objs[i])) { - this._objs[i] = this._ei._new(p._bindings[i]); - } - } - } - - getComponent(): any { return this._objs[0]; } + getComponent(): any { return this.injectorStrategy.objs[0]; } isComponentKey(key: Key): boolean { - return this._ei._proto._firstBindingIsComponent && isPresent(key) && - key.id === this._protoStrategy._keyIds[0]; + var p = this.injectorStrategy.protoStrategy; + return this._ei._proto._firstBindingIsComponent && isPresent(key) && key.id === p.keyIds[0]; } buildQueries(): void { - var p = this._protoStrategy; + var p = this.injectorStrategy.protoStrategy; - for (var i = 0; i < p._bindings.length; i++) { - if (p._bindings[i] instanceof DirectiveBinding) { - this._ei._buildQueriesForDeps(>p._bindings[i].dependencies); + for (var i = 0; i < p.bindings.length; i++) { + if (p.bindings[i] instanceof DirectiveBinding) { + this._ei._buildQueriesForDeps(>p.bindings[i].dependencies); } } } addDirectivesMatchingQuery(query: Query, list: any[]): void { - var p = this._protoStrategy; + var ist = this.injectorStrategy; + var p = ist.protoStrategy; - for (var i = 0; i < p._bindings.length; i++) { - if (p._bindings[i].key.token === query.selector) list.push(this._objs[i]); + for (var i = 0; i < p.bindings.length; i++) { + if (p.bindings[i].key.token === query.selector) list.push(ist.objs[i]); } } - - getObjByKeyId(keyId: number, visibility: number): any { - var p = this._protoStrategy; - - // TODO(vicb): optimize lookup ? - for (var i = 0; i < p._keyIds.length; i++) { - if (p._keyIds[i] === keyId && (p._visibilities[i] & visibility) > 0) { - if (isBlank(this._objs[i])) { - this._objs[i] = this._ei._new(p._bindings[i]); - } - - return this._objs[i]; - } - } - - return _undefined; - } - - getDirectiveAtIndex(index: number): any { - if (index < 0 || index >= this._objs.length) { - throw new OutOfBoundsAccess(index); - } - - return this._objs[index]; - } - getComponentBinding(): DirectiveBinding { - return this._protoStrategy._bindings[0]; + var p = this.injectorStrategy.protoStrategy; + return p.bindings[0]; } - - getMaxDirectives(): number { return this._objs.length; } } -class OutOfBoundsAccess extends BaseException { - message: string; - constructor(index) { - super(); - this.message = `Index ${index} is out-of-bounds.`; - } - - toString(): string { return this.message; } -} - -class QueryError extends BaseException { +export class QueryError extends BaseException { message: string; // TODO(rado): pass the names of the active directives. constructor() { @@ -1504,7 +999,7 @@ class QueryError extends BaseException { toString(): string { return this.message; } } -class QueryRef { +export class QueryRef { constructor(public query: Query, public list: QueryList, public originator: ElementInjector) {} diff --git a/modules/angular2/src/di/annotations.ts b/modules/angular2/src/di/annotations.ts index 0a25866c7c..599122edb8 100644 --- a/modules/angular2/src/di/annotations.ts +++ b/modules/angular2/src/di/annotations.ts @@ -5,9 +5,12 @@ export { Inject as InjectAnnotation, - InjectPromise as InjectPromiseAnnotation, - InjectLazy as InjectLazyAnnotation, Optional as OptionalAnnotation, Injectable as InjectableAnnotation, + Visibility as VisibilityAnnotation, + Self as SelfAnnotation, + Parent as ParentAnnotation, + Ancestor as AncestorAnnotation, + Unbounded as UnboundedAnnotation, DependencyAnnotation, // abstract base class, does not need a decorator } from './annotations_impl'; diff --git a/modules/angular2/src/di/annotations_impl.ts b/modules/angular2/src/di/annotations_impl.ts index 42ce53ff05..0b68ba1a2a 100644 --- a/modules/angular2/src/di/annotations_impl.ts +++ b/modules/angular2/src/di/annotations_impl.ts @@ -1,4 +1,4 @@ -import {CONST, stringify} from "angular2/src/facade/lang"; +import {CONST, CONST_EXPR, stringify, isBlank, isPresent} from "angular2/src/facade/lang"; /** * A parameter annotation that specifies a dependency. @@ -18,44 +18,6 @@ export class Inject { toString(): string { return `@Inject(${stringify(this.token)})`; } } -/** - * A parameter annotation that specifies a `Promise` of a dependency. - * - * ``` - * class AComponent { - * constructor(@InjectPromise(MyService) aServicePromise:Promise) { - * aServicePromise.then(aService:MyService => ...); - * } - * } - * ``` - * - * @exportedAs angular2/di_annotations - */ -@CONST() -export class InjectPromise { - constructor(public token) {} - toString(): string { return `@InjectPromise(${stringify(this.token)})`; } -} - -/** - * A parameter annotation that creates a synchronous lazy dependency. - * - * ``` - * class AComponent { - * constructor(@InjectLazy(MyService) aServiceFn:Function) { - * var aService:MyService = aServiceFn(); - * } - * } - * ``` - * - * @exportedAs angular2/di_annotations - */ -@CONST() -export class InjectLazy { - constructor(public token) {} - toString(): string { return `@InjectLazy(${stringify(this.token)})`; } -} - /** * A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if * the dependency is not found. @@ -124,4 +86,172 @@ export class DependencyAnnotation { */ @CONST() export class Injectable { + constructor(public visibility: Visibility = unbounded) {} } + +/** + * Specifies how injector should resolve a dependency. + * + * See {@link Self}, {@link Parent}, {@link Ancestor}, {@link Unbounded}. + * + * @exportedAs angular2/di_annotations + */ +@CONST() +export class Visibility extends DependencyAnnotation { + constructor(public depth: number, public crossComponentBoundaries: boolean, + public _includeSelf: boolean) { + super(); + } + + get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; } + + toString(): string { + return `@Visibility(depth: ${this.depth}, crossComponentBoundaries: ${this.crossComponentBoundaries}, includeSelf: ${this.includeSelf}})`; + } +} + +/** + * Specifies that an injector should retrieve a dependency from itself. + * + * ## Example + * + * ``` + * class Dependency { + * } + * + * class NeedsDependency { + * constructor(public @Self() dependency:Dependency) {} + * } + * + * var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]); + * var nd = inj.get(NeedsDependency); + * expect(nd.dependency).toBeAnInstanceOf(Dependency); + * ``` + * + * @exportedAs angular2/di + */ +@CONST() +export class Self extends Visibility { + constructor() { super(0, false, true); } + toString(): string { return `@Self()`; } +} + +export const self = CONST_EXPR(new Self()); + +/** + * Specifies that an injector should retrieve a dependency from the direct parent. + * + * ## Example + * + * ``` + * class Dependency { + * } + * + * class NeedsDependency { + * constructor(public @Parent() dependency:Dependency) {} + * } + * + * var parent = Injector.resolveAndCreate([ + * bind(Dependency).toClass(ParentDependency) + * ]); + * var child = parent.resolveAndCreateChild([NeedsDependency, Depedency]); + * var nd = child.get(NeedsDependency); + * expect(nd.dependency).toBeAnInstanceOf(ParentDependency); + * ``` + * + * You can make an injector to retrive a dependency either from itself or its direct parent by + * setting self to true. + * + * ``` + * class NeedsDependency { + * constructor(public @Parent({self:true}) dependency:Dependency) {} + * } + * ``` + * + * @exportedAs angular2/di + */ +@CONST() +export class Parent extends Visibility { + constructor({self}: {self?: boolean} = {}) { super(1, false, self); } + toString(): string { return `@Parent(self: ${this.includeSelf}})`; } +} + +/** + * Specifies that an injector should retrieve a dependency from any ancestor from the same boundary. + * + * ## Example + * + * ``` + * class Dependency { + * } + * + * class NeedsDependency { + * constructor(public @Ancestor() dependency:Dependency) {} + * } + * + * var parent = Injector.resolveAndCreate([ + * bind(Dependency).toClass(AncestorDependency) + * ]); + * var child = parent.resolveAndCreateChild([]); + * var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]); + * var nd = grandChild.get(NeedsDependency); + * expect(nd.dependency).toBeAnInstanceOf(AncestorDependency); + * ``` + * + * You can make an injector to retrive a dependency either from itself or its ancestor by setting + * self to true. + * + * ``` + * class NeedsDependency { + * constructor(public @Ancestor({self:true}) dependency:Dependency) {} + * } + * ``` + * + * @exportedAs angular2/di + */ +@CONST() +export class Ancestor extends Visibility { + constructor({self}: {self?: boolean} = {}) { super(999999, false, self); } + toString(): string { return `@Ancestor(self: ${this.includeSelf}})`; } +} + +/** + * Specifies that an injector should retrieve a dependency from any ancestor, crossing boundaries. + * + * ## Example + * + * ``` + * class Dependency { + * } + * + * class NeedsDependency { + * constructor(public @Ancestor() dependency:Dependency) {} + * } + * + * var parent = Injector.resolveAndCreate([ + * bind(Dependency).toClass(AncestorDependency) + * ]); + * var child = parent.resolveAndCreateChild([]); + * var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]); + * var nd = grandChild.get(NeedsDependency); + * expect(nd.dependency).toBeAnInstanceOf(AncestorDependency); + * ``` + * + * You can make an injector to retrive a dependency either from itself or its ancestor by setting + * self to true. + * + * ``` + * class NeedsDependency { + * constructor(public @Ancestor({self:true}) dependency:Dependency) {} + * } + * ``` + * + * @exportedAs angular2/di + */ +@CONST() +export class Unbounded extends Visibility { + constructor({self}: {self?: boolean} = {}) { super(999999, true, self); } + toString(): string { return `@Unbounded(self: ${this.includeSelf}})`; } +} + +export const unbounded = CONST_EXPR(new Unbounded({self: true})); \ No newline at end of file diff --git a/modules/angular2/src/di/binding.ts b/modules/angular2/src/di/binding.ts index 7029ca4566..733e398f22 100644 --- a/modules/angular2/src/di/binding.ts +++ b/modules/angular2/src/di/binding.ts @@ -13,9 +13,10 @@ import {reflector} from 'angular2/src/reflection/reflection'; import {Key} from './key'; import { Inject, - InjectLazy, - InjectPromise, + Injectable, + Visibility, Optional, + unbounded, DependencyAnnotation } from './annotations_impl'; import {NoAnnotationError} from './exceptions'; @@ -25,10 +26,10 @@ import {resolveForwardRef} from './forward_ref'; * @private */ export class Dependency { - constructor(public key: Key, public asPromise: boolean, public lazy: boolean, - public optional: boolean, public properties: List) {} + constructor(public key: Key, public optional: boolean, public visibility: Visibility, + public properties: List) {} - static fromKey(key: Key) { return new Dependency(key, false, false, false, []); } + static fromKey(key: Key) { return new Dependency(key, false, _defaulVisiblity(key.token), []); } } const _EMPTY_LIST = CONST_EXPR([]); @@ -158,35 +159,7 @@ export class Binding { toFactory: Function; /** - * Binds a key to a function which computes the value asynchronously. - * - * ## Example - * - * ```javascript - * var injector = Injector.resolveAndCreate([ - * new Binding(Number, { toAsyncFactory: () => { - * return new Promise((resolve) => resolve(1 + 2)); - * }}), - * new Binding(String, { toFactory: (value) => { return "Value: " + value; }, - * dependencies: [Number]}) - * ]); - * - * injector.asyncGet(Number).then((v) => expect(v).toBe(3)); - * injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3')); - * ``` - * - * The interesting thing to note is that event though `Number` has an async factory, the `String` - * factory function takes the resolved value. This shows that the {@link Injector} delays - *executing the - *`String` factory - * until after the `Number` is resolved. This can only be done if the `token` is retrieved using - * the `asyncGet` API in the {@link Injector}. - * - */ - toAsyncFactory: Function; - - /** - * Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies + * Used in conjunction with `toFactory` and specifies a set of dependencies * (as `token`s) which should be injected into the factory function. * * ## Example @@ -204,12 +177,11 @@ export class Binding { */ dependencies: List; - constructor(token, {toClass, toValue, toAlias, toFactory, toAsyncFactory, deps}: { + constructor(token, {toClass, toValue, toAlias, toFactory, deps}: { toClass?: Type, toValue?: any, toAlias?: any, toFactory?: Function, - toAsyncFactory?: Function, deps?: List }) { this.token = token; @@ -217,7 +189,6 @@ export class Binding { this.toValue = toValue; this.toAlias = toAlias; this.toFactory = toFactory; - this.toAsyncFactory = toAsyncFactory; this.dependencies = deps; } @@ -230,7 +201,6 @@ export class Binding { resolve(): ResolvedBinding { var factoryFn: Function; var resolvedDeps; - var isAsync = false; if (isPresent(this.toClass)) { var toClass = resolveForwardRef(this.toClass); factoryFn = reflector.factory(toClass); @@ -241,16 +211,12 @@ export class Binding { } else if (isPresent(this.toFactory)) { factoryFn = this.toFactory; resolvedDeps = _constructDependencies(this.toFactory, this.dependencies); - } else if (isPresent(this.toAsyncFactory)) { - factoryFn = this.toAsyncFactory; - resolvedDeps = _constructDependencies(this.toAsyncFactory, this.dependencies); - isAsync = true; } else { factoryFn = () => this.toValue; resolvedDeps = _EMPTY_LIST; } - return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps, isAsync); + return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps); } } @@ -278,12 +244,7 @@ export class ResolvedBinding { /** * Arguments (dependencies) to the `factory` function. */ - public dependencies: List, - - /** - * Specifies whether the `factory` function returns a `Promise`. - */ - public providedAsPromise: boolean) {} + public dependencies: List) {} } /** @@ -417,33 +378,6 @@ export class BindingBuilder { toFactory(factoryFunction: Function, dependencies?: List): Binding { return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies}); } - - /** - * Binds a key to a function which computes the value asynchronously. - * - * ## Example - * - * ```javascript - * var injector = Injector.resolveAndCreate([ - * bind(Number).toAsyncFactory(() => { - * return new Promise((resolve) => resolve(1 + 2)); - * }), - * bind(String).toFactory((v) => { return "Value: " + v; }, [Number]) - * ]); - * - * injector.asyncGet(Number).then((v) => expect(v).toBe(3)); - * injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3')); - * ``` - * - * The interesting thing to note is that event though `Number` has an async factory, the `String` - * factory function takes the resolved value. This shows that the {@link Injector} delays - * executing of the `String` factory - * until after the `Number` is resolved. This can only be done if the `token` is retrieved using - * the `asyncGet` API in the {@link Injector}. - */ - toAsyncFactory(factoryFunction: Function, dependencies?: List): Binding { - return new Binding(this.token, {toAsyncFactory: factoryFunction, deps: dependencies}); - } } function _constructDependencies(factoryFunction: Function, @@ -470,33 +404,30 @@ function _extractToken(typeOrFunc, annotations /*List | any*/, var depProps = []; var token = null; var optional = false; - var lazy = false; - var asPromise = false; if (!isArray(annotations)) { - return _createDependency(annotations, asPromise, lazy, optional, depProps); + return _createDependency(annotations, optional, _defaulVisiblity(annotations), depProps); } + var visibility = null; + var defaultVisibility = unbounded; + for (var i = 0; i < annotations.length; ++i) { var paramAnnotation = annotations[i]; if (paramAnnotation instanceof Type) { token = paramAnnotation; + defaultVisibility = _defaulVisiblity(token); } else if (paramAnnotation instanceof Inject) { token = paramAnnotation.token; - } else if (paramAnnotation instanceof InjectPromise) { - token = paramAnnotation.token; - asPromise = true; - - } else if (paramAnnotation instanceof InjectLazy) { - token = paramAnnotation.token; - lazy = true; - } else if (paramAnnotation instanceof Optional) { optional = true; + } else if (paramAnnotation instanceof Visibility) { + visibility = paramAnnotation; + } else if (paramAnnotation instanceof DependencyAnnotation) { if (isPresent(paramAnnotation.token)) { token = paramAnnotation.token; @@ -505,15 +436,29 @@ function _extractToken(typeOrFunc, annotations /*List | any*/, } } + if (isBlank(visibility)) { + visibility = defaultVisibility; + } + token = resolveForwardRef(token); if (isPresent(token)) { - return _createDependency(token, asPromise, lazy, optional, depProps); + return _createDependency(token, optional, visibility, depProps); } else { throw new NoAnnotationError(typeOrFunc, params); } } -function _createDependency(token, asPromise, lazy, optional, depProps): Dependency { - return new Dependency(Key.get(token), asPromise, lazy, optional, depProps); +function _defaulVisiblity(typeOrFunc) { + try { + if (!(typeOrFunc instanceof Type)) return unbounded; + var f = ListWrapper.filter(reflector.annotations(typeOrFunc), s => s instanceof Injectable); + return f.length === 0 ? unbounded : f[0].visibility; + } catch (e) { + return unbounded; + } +} + +function _createDependency(token, optional, visibility, depProps): Dependency { + return new Dependency(Key.get(token), optional, visibility, depProps); } diff --git a/modules/angular2/src/di/decorators.ts b/modules/angular2/src/di/decorators.ts index 06250d5a28..a15b5029d2 100644 --- a/modules/angular2/src/di/decorators.ts +++ b/modules/angular2/src/di/decorators.ts @@ -1,14 +1,20 @@ import { InjectAnnotation, - InjectPromiseAnnotation, - InjectLazyAnnotation, OptionalAnnotation, - InjectableAnnotation + InjectableAnnotation, + VisibilityAnnotation, + SelfAnnotation, + ParentAnnotation, + AncestorAnnotation, + UnboundedAnnotation } from './annotations'; import {makeDecorator, makeParamDecorator} from '../util/decorators'; export var Inject = makeParamDecorator(InjectAnnotation); -export var InjectPromise = makeParamDecorator(InjectPromiseAnnotation); -export var InjectLazy = makeParamDecorator(InjectLazyAnnotation); export var Optional = makeParamDecorator(OptionalAnnotation); export var Injectable = makeDecorator(InjectableAnnotation); +export var Visibility = makeParamDecorator(VisibilityAnnotation); +export var Self = makeParamDecorator(SelfAnnotation); +export var Parent = makeParamDecorator(ParentAnnotation); +export var Ancestor = makeParamDecorator(AncestorAnnotation); +export var Unbounded = makeParamDecorator(UnboundedAnnotation); \ No newline at end of file diff --git a/modules/angular2/src/di/exceptions.ts b/modules/angular2/src/di/exceptions.ts index 2e96348930..a514ce1956 100644 --- a/modules/angular2/src/di/exceptions.ts +++ b/modules/angular2/src/di/exceptions.ts @@ -140,15 +140,17 @@ export class CyclicDependencyError extends AbstractBindingError { export class InstantiationError extends AbstractBindingError { cause; causeKey; + stack; // TODO(tbosch): Can't do key:Key as this results in a circular dependency! - constructor(cause, key) { + constructor(cause, stack, key) { super(key, function(keys: List) { var first = stringify(ListWrapper.first(keys).token); return `Error during instantiation of ${first}!${constructResolvingPath(keys)}. ORIGINAL ERROR: ${cause}`; }); this.cause = cause; this.causeKey = key; + this.stack = stack; } } @@ -198,3 +200,18 @@ export class NoAnnotationError extends BaseException { toString(): string { return this.message; } } + +/** + * Thrown when getting an object by index. + * + * @exportedAs angular2/di_errors + */ +export class OutOfBoundsError extends BaseException { + message: string; + constructor(index) { + super(); + this.message = `Index ${index} is out-of-bounds.`; + } + + toString(): string { return this.message; } +} diff --git a/modules/angular2/src/di/injector.ts b/modules/angular2/src/di/injector.ts index 8ee63b5e2d..b163f65394 100644 --- a/modules/angular2/src/di/injector.ts +++ b/modules/angular2/src/di/injector.ts @@ -8,22 +8,371 @@ import { AsyncBindingError, CyclicDependencyError, InstantiationError, - InvalidBindingError + InvalidBindingError, + OutOfBoundsError } from './exceptions'; import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang'; -import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {Key} from './key'; import {resolveForwardRef} from './forward_ref'; +import {self, unbounded} from './annotations_impl'; const _constructing = CONST_EXPR(new Object()); const _notFound = CONST_EXPR(new Object()); -class _Waiting { - constructor(public promise: Promise) {} +// Threshold for the dynamic version +const _MAX_CONSTRUCTION_COUNTER = 10; + +export const undefinedValue = CONST_EXPR(new Object()); + +export const PUBLIC = 1; +export const PRIVATE = 2; +export const PUBLIC_AND_PRIVATE = 3; + +export interface ProtoInjectorStrategy { + getBindingAtIndex(index: number): ResolvedBinding; + createInjectorStrategy(inj: Injector): InjectorStrategy; } -function _isWaiting(obj): boolean { - return obj instanceof _Waiting; +export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy { + binding0: ResolvedBinding = null; + binding1: ResolvedBinding = null; + binding2: ResolvedBinding = null; + binding3: ResolvedBinding = null; + binding4: ResolvedBinding = null; + binding5: ResolvedBinding = null; + binding6: ResolvedBinding = null; + binding7: ResolvedBinding = null; + binding8: ResolvedBinding = null; + binding9: ResolvedBinding = 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; + + visibility0: number = null; + visibility1: number = null; + visibility2: number = null; + visibility3: number = null; + visibility4: number = null; + visibility5: number = null; + visibility6: number = null; + visibility7: number = null; + visibility8: number = null; + visibility9: number = null; + + constructor(protoEI: ProtoInjector, bd: any[]) { + var length = bd.length; + + if (length > 0) { + this.binding0 = bd[0].binding; + this.keyId0 = bd[0].getKeyId(); + this.visibility0 = bd[0].visibility; + } + if (length > 1) { + this.binding1 = bd[1].binding; + this.keyId1 = bd[1].getKeyId(); + this.visibility1 = bd[1].visibility; + } + if (length > 2) { + this.binding2 = bd[2].binding; + this.keyId2 = bd[2].getKeyId(); + this.visibility2 = bd[2].visibility; + } + if (length > 3) { + this.binding3 = bd[3].binding; + this.keyId3 = bd[3].getKeyId(); + this.visibility3 = bd[3].visibility; + } + if (length > 4) { + this.binding4 = bd[4].binding; + this.keyId4 = bd[4].getKeyId(); + this.visibility4 = bd[4].visibility; + } + if (length > 5) { + this.binding5 = bd[5].binding; + this.keyId5 = bd[5].getKeyId(); + this.visibility5 = bd[5].visibility; + } + if (length > 6) { + this.binding6 = bd[6].binding; + this.keyId6 = bd[6].getKeyId(); + this.visibility6 = bd[6].visibility; + } + if (length > 7) { + this.binding7 = bd[7].binding; + this.keyId7 = bd[7].getKeyId(); + this.visibility7 = bd[7].visibility; + } + if (length > 8) { + this.binding8 = bd[8].binding; + this.keyId8 = bd[8].getKeyId(); + this.visibility8 = bd[8].visibility; + } + if (length > 9) { + this.binding9 = bd[9].binding; + this.keyId9 = bd[9].getKeyId(); + this.visibility9 = bd[9].visibility; + } + } + + getBindingAtIndex(index: number): any { + if (index == 0) return this.binding0; + if (index == 1) return this.binding1; + if (index == 2) return this.binding2; + if (index == 3) return this.binding3; + if (index == 4) return this.binding4; + if (index == 5) return this.binding5; + if (index == 6) return this.binding6; + if (index == 7) return this.binding7; + if (index == 8) return this.binding8; + if (index == 9) return this.binding9; + throw new OutOfBoundsError(index); + } + + createInjectorStrategy(injector: Injector): InjectorStrategy { + return new InjectorInlineStrategy(injector, this); + } +} + +export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy { + bindings: ResolvedBinding[]; + keyIds: number[]; + visibilities: number[]; + + constructor(protoInj: ProtoInjector, bd: any[]) { + var len = bd.length; + + this.bindings = ListWrapper.createFixedSize(len); + this.keyIds = ListWrapper.createFixedSize(len); + this.visibilities = ListWrapper.createFixedSize(len); + + for (var i = 0; i < len; i++) { + this.bindings[i] = bd[i].binding; + this.keyIds[i] = bd[i].getKeyId(); + this.visibilities[i] = bd[i].visibility; + } + } + + getBindingAtIndex(index: number): any { + if (index < 0 || index >= this.bindings.length) { + throw new OutOfBoundsError(index); + } + return this.bindings[index]; + } + + createInjectorStrategy(ei: Injector): InjectorStrategy { + return new InjectorDynamicStrategy(this, ei); + } +} + +export class ProtoInjector { + _strategy: ProtoInjectorStrategy; + + constructor(public parent: ProtoInjector, rb: any[], public distanceToParent: number) { + this._strategy = rb.length > _MAX_CONSTRUCTION_COUNTER ? + new ProtoInjectorDynamicStrategy(this, rb) : + new ProtoInjectorInlineStrategy(this, rb); + } + + getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); } +} + + + +export interface InjectorStrategy { + getObjByKeyId(keyId: number, visibility: number): any; + getObjAtIndex(index: number): any; + getMaxNumberOfObjects(): number; + + hydrate(): void; + dehydrate(): void; +} + +export class InjectorInlineStrategy implements InjectorStrategy { + obj0: any = null; + obj1: any = null; + obj2: any = null; + obj3: any = null; + obj4: any = null; + obj5: any = null; + obj6: any = null; + obj7: any = null; + obj8: any = null; + obj9: any = null; + + constructor(public injector: Injector, public protoStrategy: ProtoInjectorInlineStrategy) {} + + hydrate(): void { + var p = this.protoStrategy; + var inj = this.injector; + + if (isPresent(p.keyId0) && isBlank(this.obj0)) this.obj0 = inj._new(p.binding0); + if (isPresent(p.keyId1) && isBlank(this.obj1)) this.obj1 = inj._new(p.binding1); + if (isPresent(p.keyId2) && isBlank(this.obj2)) this.obj2 = inj._new(p.binding2); + if (isPresent(p.keyId3) && isBlank(this.obj3)) this.obj3 = inj._new(p.binding3); + if (isPresent(p.keyId4) && isBlank(this.obj4)) this.obj4 = inj._new(p.binding4); + if (isPresent(p.keyId5) && isBlank(this.obj5)) this.obj5 = inj._new(p.binding5); + if (isPresent(p.keyId6) && isBlank(this.obj6)) this.obj6 = inj._new(p.binding6); + if (isPresent(p.keyId7) && isBlank(this.obj7)) this.obj7 = inj._new(p.binding7); + if (isPresent(p.keyId8) && isBlank(this.obj8)) this.obj8 = inj._new(p.binding8); + if (isPresent(p.keyId9) && isBlank(this.obj9)) this.obj9 = inj._new(p.binding9); + } + + dehydrate() { + this.obj0 = null; + this.obj1 = null; + this.obj2 = null; + this.obj3 = null; + this.obj4 = null; + this.obj5 = null; + this.obj6 = null; + this.obj7 = null; + this.obj8 = null; + this.obj9 = null; + } + + getObjByKeyId(keyId: number, visibility: number): any { + var p = this.protoStrategy; + var inj = this.injector; + + if (p.keyId0 === keyId && (p.visibility0 & visibility) > 0) { + if (isBlank(this.obj0)) { + this.obj0 = inj._new(p.binding0); + } + return this.obj0; + } + if (p.keyId1 === keyId && (p.visibility1 & visibility) > 0) { + if (isBlank(this.obj1)) { + this.obj1 = inj._new(p.binding1); + } + return this.obj1; + } + if (p.keyId2 === keyId && (p.visibility2 & visibility) > 0) { + if (isBlank(this.obj2)) { + this.obj2 = inj._new(p.binding2); + } + return this.obj2; + } + if (p.keyId3 === keyId && (p.visibility3 & visibility) > 0) { + if (isBlank(this.obj3)) { + this.obj3 = inj._new(p.binding3); + } + return this.obj3; + } + if (p.keyId4 === keyId && (p.visibility4 & visibility) > 0) { + if (isBlank(this.obj4)) { + this.obj4 = inj._new(p.binding4); + } + return this.obj4; + } + if (p.keyId5 === keyId && (p.visibility5 & visibility) > 0) { + if (isBlank(this.obj5)) { + this.obj5 = inj._new(p.binding5); + } + return this.obj5; + } + if (p.keyId6 === keyId && (p.visibility6 & visibility) > 0) { + if (isBlank(this.obj6)) { + this.obj6 = inj._new(p.binding6); + } + return this.obj6; + } + if (p.keyId7 === keyId && (p.visibility7 & visibility) > 0) { + if (isBlank(this.obj7)) { + this.obj7 = inj._new(p.binding7); + } + return this.obj7; + } + if (p.keyId8 === keyId && (p.visibility8 & visibility) > 0) { + if (isBlank(this.obj8)) { + this.obj8 = inj._new(p.binding8); + } + return this.obj8; + } + if (p.keyId9 === keyId && (p.visibility9 & visibility) > 0) { + if (isBlank(this.obj9)) { + this.obj9 = inj._new(p.binding9); + } + return this.obj9; + } + + return undefinedValue; + } + + getObjAtIndex(index: number): any { + if (index == 0) return this.obj0; + if (index == 1) return this.obj1; + if (index == 2) return this.obj2; + if (index == 3) return this.obj3; + if (index == 4) return this.obj4; + if (index == 5) return this.obj5; + if (index == 6) return this.obj6; + if (index == 7) return this.obj7; + if (index == 8) return this.obj8; + if (index == 9) return this.obj9; + throw new OutOfBoundsError(index); + } + + getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; } +} + + +export class InjectorDynamicStrategy implements InjectorStrategy { + objs: any[]; + + constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector) { + this.objs = ListWrapper.createFixedSize(protoStrategy.bindings.length); + } + + hydrate(): void { + var p = this.protoStrategy; + for (var i = 0; i < p.keyIds.length; i++) { + if (isPresent(p.keyIds[i]) && isBlank(this.objs[i])) { + this.objs[i] = this.injector._new(p.bindings[i]); + } + } + } + + dehydrate(): void { ListWrapper.fill(this.objs, null); } + + getObjByKeyId(keyId: number, visibility: number): any { + var p = this.protoStrategy; + + for (var i = 0; i < p.keyIds.length; i++) { + if (p.keyIds[i] === keyId && (p.visibilities[i] & visibility) > 0) { + if (isBlank(this.objs[i])) { + this.objs[i] = this.injector._new(p.bindings[i]); + } + + return this.objs[i]; + } + } + + return undefinedValue; + } + + 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; } +} + +export class BindingData { + constructor(public binding: ResolvedBinding, public visibility: number){}; + + getKeyId(): number { return this.binding.key.id; } } /** @@ -67,10 +416,6 @@ function _isWaiting(obj): boolean { * @exportedAs angular2/di */ export class Injector { - private _instances: List; - private _asyncStrategy: _AsyncInjectorStrategy; - private _syncStrategy: _SyncInjectorStrategy; - /** * Turns a list of binding definitions into an internal resolved list of resolved bindings. * @@ -108,7 +453,11 @@ export class Injector { */ static resolveAndCreate(bindings: List>, {defaultBindings = false}: any = {}): Injector { - return new Injector(Injector.resolve(bindings), null, defaultBindings); + var resolvedBindings = Injector.resolve(bindings); + var bd = resolvedBindings.map(b => new BindingData(b, PUBLIC)); + var proto = new ProtoInjector(null, bd, 0); + var inj = new Injector(proto); + return inj; } /** @@ -121,67 +470,63 @@ export class Injector { */ static fromResolvedBindings(bindings: List, {defaultBindings = false}: any = {}): Injector { - return new Injector(bindings, null, defaultBindings); + var bd = bindings.map(b => new BindingData(b, PUBLIC)); + var proto = new ProtoInjector(null, bd, 0); + var inj = new Injector(proto); + return inj; } - /** - * @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the - * {@link Injector}. - * @param `parent` Parent Injector or `null` if root Injector. - * @param `defaultBindings` Setting to true will auto-create bindings. (Only use with root - * injector.) - */ - constructor(private _bindings: List, private _parent: Injector, - private _defaultBindings: boolean) { - this._instances = this._createInstances(); - this._asyncStrategy = new _AsyncInjectorStrategy(this); - this._syncStrategy = new _SyncInjectorStrategy(this); + _strategy: InjectorStrategy; + _parent: Injector; + _host: Injector; + _constructionCounter: number = 0; + + // TODO vsavkin remove it after DI and EI are merged + private _ei: any; + + constructor(public _proto: ProtoInjector) { + this._strategy = _proto._strategy.createInjectorStrategy(this); } - /** - * Direct parent of this injector. - */ + get(token): any { return this._getByKey(Key.get(token), unbounded, false, null); } + + getOptional(token): any { return this._getByKey(Key.get(token), unbounded, true, null); } + + getObjAtIndex(index: number): any { return this._strategy.getObjAtIndex(index); } + get parent(): Injector { return this._parent; } - /** - * Retrieves an instance from the injector. - * - * @param `token`: usually the `Type` of an object. (Same as the token used while setting up a - *binding). - * @returns an instance represented by the token. Throws if not found. - */ - get(token) { return this._getByKey(Key.get(token), false, false, false); } + get strategy() { return this._strategy; } + hydrate(parent: Injector, host: Injector, ei: any) { + this._constructionCounter = 0; + this._parent = parent; + this._host = host; + this._ei = ei; + + this._strategy.hydrate(); + } + + dehydrate(): void { this._strategy.dehydrate(); } /** - * Retrieves an instance from the injector. - * - * @param `token`: usually a `Type`. (Same as the token used while setting up a binding). - * @returns an instance represented by the token. Returns `null` if not found. - */ - getOptional(token) { return this._getByKey(Key.get(token), false, false, true); } - - /** - * Retrieves an instance from the injector asynchronously. Used with asynchronous bindings. - * - * @param `token`: usually a `Type`. (Same as token used while setting up a binding). - * @returns a `Promise` which resolves to the instance represented by the token. - */ - asyncGet(token): Promise { return this._getByKey(Key.get(token), true, false, false); } - - /** - * Creates a child injector and loads a new set of bindings into it. - * - * A resolution is a process of flattening multiple nested lists and converting individual - * bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` - * for the {@link Injector} for performance-sensitive code. - * - * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a - * recursive list of more bindings. - * - */ + * Creates a child injector and loads a new set of bindings into it. + * + * A resolution is a process of flattening multiple nested lists and converting individual + * bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` + * for the {@link Injector} for performance-sensitive code. + * + * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a + * recursive list of more bindings. + * + */ resolveAndCreateChild(bindings: List>): Injector { - return new Injector(Injector.resolve(bindings), this, false); + var resovledBindings = Injector.resolve(bindings); + var bd = resovledBindings.map(b => new BindingData(b, PUBLIC)); + var proto = new ProtoInjector(this._proto, bd, 1); + var inj = new Injector(proto); + inj._parent = this; + return inj; } /** @@ -192,26 +537,184 @@ export class Injector { * @returns a new child {@link Injector}. */ createChildFromResolved(bindings: List): Injector { - return new Injector(bindings, this, false); + var bd = bindings.map(b => new BindingData(b, PUBLIC)); + var proto = new ProtoInjector(this._proto, bd, 1); + var inj = new Injector(proto); + inj._parent = this; + return inj; } - _createInstances(): List { return ListWrapper.createFixedSize(Key.numberOfKeys + 1); } - - _getByKey(key: Key, returnPromise: boolean, returnLazy: boolean, optional: boolean) { - if (returnLazy) { - return () => this._getByKey(key, returnPromise, false, optional); + _new(binding: ResolvedBinding): any { + if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) { + throw new CyclicDependencyError(binding.key); } - var strategy = returnPromise ? this._asyncStrategy : this._syncStrategy; + var factory = binding.factory; + var deps = binding.dependencies; + var length = deps.length; - var instance = strategy.readFromCache(key); - if (instance !== _notFound) return instance; + var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19; + try { + d0 = length > 0 ? this._getByDependency(deps[0], binding.key) : null; + d1 = length > 1 ? this._getByDependency(deps[1], binding.key) : null; + d2 = length > 2 ? this._getByDependency(deps[2], binding.key) : null; + d3 = length > 3 ? this._getByDependency(deps[3], binding.key) : null; + d4 = length > 4 ? this._getByDependency(deps[4], binding.key) : null; + d5 = length > 5 ? this._getByDependency(deps[5], binding.key) : null; + d6 = length > 6 ? this._getByDependency(deps[6], binding.key) : null; + d7 = length > 7 ? this._getByDependency(deps[7], binding.key) : null; + d8 = length > 8 ? this._getByDependency(deps[8], binding.key) : null; + d9 = length > 9 ? this._getByDependency(deps[9], binding.key) : null; + d10 = length > 10 ? this._getByDependency(deps[10], binding.key) : null; + d11 = length > 11 ? this._getByDependency(deps[11], binding.key) : null; + d12 = length > 12 ? this._getByDependency(deps[12], binding.key) : null; + d13 = length > 13 ? this._getByDependency(deps[13], binding.key) : null; + d14 = length > 14 ? this._getByDependency(deps[14], binding.key) : null; + d15 = length > 15 ? this._getByDependency(deps[15], binding.key) : null; + d16 = length > 16 ? this._getByDependency(deps[16], binding.key) : null; + d17 = length > 17 ? this._getByDependency(deps[17], binding.key) : null; + d18 = length > 18 ? this._getByDependency(deps[18], binding.key) : null; + d19 = length > 19 ? this._getByDependency(deps[19], binding.key) : null; + } catch (e) { + if (e instanceof AbstractBindingError) e.addKey(binding.key); + throw e; + } - instance = strategy.instantiate(key); - if (instance !== _notFound) return instance; + var obj; + try { + switch (length) { + case 0: + obj = factory(); + break; + case 1: + obj = factory(d0); + break; + case 2: + obj = factory(d0, d1); + break; + case 3: + obj = factory(d0, d1, d2); + break; + case 4: + obj = factory(d0, d1, d2, d3); + break; + case 5: + obj = factory(d0, d1, d2, d3, d4); + break; + case 6: + obj = factory(d0, d1, d2, d3, d4, d5); + break; + case 7: + obj = factory(d0, d1, d2, d3, d4, d5, d6); + break; + case 8: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7); + break; + case 9: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8); + break; + case 10: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); + break; + case 11: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10); + break; + case 12: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); + break; + case 13: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12); + break; + case 14: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); + break; + case 15: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14); + break; + case 16: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15); + break; + case 17: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16); + break; + case 18: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, + d17); + break; + case 19: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, + d17, d18); + break; + case 20: + obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, + d17, d18, d19); + break; + } + } catch (e) { + throw new InstantiationError(e, e.stack, binding.key); + } + return obj; + } - if (isPresent(this._parent)) { - return this._parent._getByKey(key, returnPromise, returnLazy, optional); + private _getByDependency(dep: any, requestor: Key): any { + var special = isPresent(this._ei) ? this._ei.getDependency(dep) : undefinedValue; + if (special !== undefinedValue) { + return special; + } else { + return this._getByKey(dep.key, dep.visibility, dep.optional, requestor); + } + } + + private _getByKey(key: Key, depVisibility: any, optional: boolean, requestor: Key): any { + if (key.token === Injector) { + return this; + } + + var inj = this; + var ei = this._ei; + + // TODO vsavkin remove after DI and EI are merged + var bindingVisibility = + isPresent(ei) && ei.isComponentKey(requestor) ? PUBLIC_AND_PRIVATE : PUBLIC; + + var depth = depVisibility.depth; + + if (!depVisibility.includeSelf) { + depth -= inj._proto.distanceToParent; + + if (isPresent(inj._parent)) { + inj = inj._parent; + } else { + inj = inj._host; + bindingVisibility = depVisibility.crossComponentBoundaries ? PUBLIC : PRIVATE; + } + } + + while (inj != null && depth >= 0) { + var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility); + if (obj !== undefinedValue) return obj; + + depth -= inj._proto.distanceToParent; + + // we check only one mode with the PRIVATE visibility + if (bindingVisibility === PRIVATE) break; + + if (isPresent(inj._parent)) { + inj = inj._parent; + } else { + inj = inj._host; + bindingVisibility = depVisibility.crossComponentBoundaries ? PUBLIC : PRIVATE; + } + } + + // TODO vsavkin remove after DI and EI are merged + if (isPresent(ei)) { + var appInj = this._ei.appInjector(requestor); + if (optional) { + return appInj.getOptional(key); + } else { + return appInj.get(key); + } } if (optional) { @@ -221,149 +724,13 @@ export class Injector { } } - _resolveDependencies(key: Key, binding: ResolvedBinding, forceAsync: boolean): List { - try { - var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional); - return ListWrapper.map(binding.dependencies, getDependency); - } catch (e) { - this._clear(key); - if (e instanceof AbstractBindingError) e.addKey(key); - throw e; - } - } - - _getInstance(key: Key) { - if (this._instances.length <= key.id) return null; - return ListWrapper.get(this._instances, key.id); - } - - _setInstance(key: Key, obj): void { ListWrapper.set(this._instances, key.id, obj); } - - _getBinding(key: Key) { - var binding = this._bindings.length <= key.id ? null : ListWrapper.get(this._bindings, key.id); - - if (isBlank(binding) && this._defaultBindings) { - var token: any = key.token; - return bind(key.token).toClass(token).resolve(); - } else { - return binding; - } - } - - _markAsConstructing(key: Key): void { this._setInstance(key, _constructing); } - - _clear(key: Key): void { this._setInstance(key, null); } -} - -interface _InjectorStrategy { - readFromCache(key: Key); - instantiate(key: Key); -} - -class _SyncInjectorStrategy implements _InjectorStrategy { - constructor(private _injector: Injector) {} - - readFromCache(key: Key) { - if (key.token === Injector) { - return this._injector; - } - - var instance = this._injector._getInstance(key); - - if (instance === _constructing) { - throw new CyclicDependencyError(key); - } else if (isPresent(instance) && !_isWaiting(instance)) { - return instance; - } else { - return _notFound; - } - } - - instantiate(key: Key) { - var binding = this._injector._getBinding(key); - if (isBlank(binding)) return _notFound; - - if (binding.providedAsPromise) throw new AsyncBindingError(key); - - // add a marker so we can detect cyclic dependencies - this._injector._markAsConstructing(key); - - var deps = this._injector._resolveDependencies(key, binding, false); - return this._createInstance(key, binding, deps); - } - - _createInstance(key: Key, binding: ResolvedBinding, deps: List) { - try { - var instance = FunctionWrapper.apply(binding.factory, deps); - this._injector._setInstance(key, instance); - return instance; - } catch (e) { - this._injector._clear(key); - throw new InstantiationError(e, key); - } + // TODO vsavkin remove after DI and EI are merged + getAppInjector(): Injector { + if (isBlank(this._ei)) return this; + return this._ei.appInjector(null); } } -class _AsyncInjectorStrategy implements _InjectorStrategy { - constructor(private _injector: Injector) {} - - readFromCache(key: Key) { - if (key.token === Injector) { - return PromiseWrapper.resolve(this._injector); - } - - var instance = this._injector._getInstance(key); - - if (instance === _constructing) { - throw new CyclicDependencyError(key); - } else if (_isWaiting(instance)) { - return instance.promise; - } else if (isPresent(instance)) { - return PromiseWrapper.resolve(instance); - } else { - return _notFound; - } - } - - instantiate(key: Key) /* Promise?? */ { - var binding = this._injector._getBinding(key); - if (isBlank(binding)) return _notFound; - - // add a marker so we can detect cyclic dependencies - this._injector._markAsConstructing(key); - - var deps = this._injector._resolveDependencies(key, binding, true); - var depsPromise = PromiseWrapper.all(deps); - - var promise = PromiseWrapper.then(depsPromise, null, (e, s) => this._errorHandler(key, e, s)) - .then(deps => this._findOrCreate(key, binding, deps)) - .then(instance => this._cacheInstance(key, instance)); - - this._injector._setInstance(key, new _Waiting(promise)); - return promise; - } - - _errorHandler(key: Key, e, stack): Promise { - if (e instanceof AbstractBindingError) e.addKey(key); - return PromiseWrapper.reject(e, stack); - } - - _findOrCreate(key: Key, binding: ResolvedBinding, deps: List) { - try { - var instance = this._injector._getInstance(key); - if (!_isWaiting(instance)) return instance; - return FunctionWrapper.apply(binding.factory, deps); - } catch (e) { - this._injector._clear(key); - throw new InstantiationError(e, key); - } - } - - _cacheInstance(key, instance) { - this._injector._setInstance(key, instance); - return instance - } -} export function resolveBindings(bindings: List>): List { var resolvedList = ListWrapper.createFixedSize(bindings.length); @@ -397,9 +764,7 @@ function flattenBindings(bindings: List): List function _createListOfBindings( flattenedBindings: Map): List { - var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1); - MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v); - return bindings; + return MapWrapper.values(flattenedBindings); } function _flattenBindings(bindings: List>, diff --git a/modules/angular2/src/directives/ng_switch.ts b/modules/angular2/src/directives/ng_switch.ts index 93443187e0..0445701b94 100644 --- a/modules/angular2/src/directives/ng_switch.ts +++ b/modules/angular2/src/directives/ng_switch.ts @@ -1,4 +1,5 @@ -import {Directive, Parent} from 'angular2/annotations'; +import {Directive} from 'angular2/annotations'; +import {Parent} from 'angular2/di'; import {ViewContainerRef, ProtoViewRef} from 'angular2/core'; import {isPresent, isBlank, normalizeBlank} from 'angular2/src/facade/lang'; import {ListWrapper, List, MapWrapper, Map} from 'angular2/src/facade/collection'; diff --git a/modules/angular2/src/facade/collection.dart b/modules/angular2/src/facade/collection.dart index 2cd79c08cb..a46c84b6b2 100644 --- a/modules/angular2/src/facade/collection.dart +++ b/modules/angular2/src/facade/collection.dart @@ -49,8 +49,8 @@ class MapWrapper { } } static Iterable iterable(Map m) => new IterableMap(m); - static Iterable keys(Map m) => m.keys; - static Iterable values(Map m) => m.values; + static List keys(Map m) => m.keys.toList(); + static List values(Map m) => m.values.toList(); } class StringMapWrapper { diff --git a/modules/angular2/src/forms/directives/ng_control_group.ts b/modules/angular2/src/forms/directives/ng_control_group.ts index 23c3683b19..c754902ca4 100644 --- a/modules/angular2/src/forms/directives/ng_control_group.ts +++ b/modules/angular2/src/forms/directives/ng_control_group.ts @@ -1,5 +1,5 @@ -import {Directive, Ancestor, onDestroy, onInit} from 'angular2/angular2'; -import {Inject, forwardRef, Binding} from 'angular2/di'; +import {Directive, onDestroy, onInit} from 'angular2/angular2'; +import {Inject, Ancestor, forwardRef, Binding} from 'angular2/di'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {CONST_EXPR} from 'angular2/src/facade/lang'; diff --git a/modules/angular2/src/forms/directives/ng_control_name.ts b/modules/angular2/src/forms/directives/ng_control_name.ts index a7448328c8..de96be10da 100644 --- a/modules/angular2/src/forms/directives/ng_control_name.ts +++ b/modules/angular2/src/forms/directives/ng_control_name.ts @@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang'; import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection'; -import {Directive, Ancestor, onDestroy, onChange, Query, QueryList} from 'angular2/angular2'; -import {forwardRef, Binding, Inject} from 'angular2/di'; +import {Directive, onDestroy, onChange, Query, QueryList} from 'angular2/angular2'; +import {forwardRef, Ancestor, Binding, Inject} from 'angular2/di'; import {ControlContainer} from './control_container'; import {NgControl} from './ng_control'; diff --git a/modules/angular2/src/forms/directives/ng_form_control.ts b/modules/angular2/src/forms/directives/ng_form_control.ts index 918c50a31f..78a9fdcfad 100644 --- a/modules/angular2/src/forms/directives/ng_form_control.ts +++ b/modules/angular2/src/forms/directives/ng_form_control.ts @@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang'; import {StringMapWrapper} from 'angular2/src/facade/collection'; import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; -import {Directive, Ancestor, onChange, Query, QueryList} from 'angular2/angular2'; -import {forwardRef, Binding} from 'angular2/di'; +import {Directive, onChange, Query, QueryList} from 'angular2/angular2'; +import {forwardRef, Ancestor, Binding} from 'angular2/di'; import {NgControl} from './ng_control'; import {Control} from '../model'; diff --git a/modules/angular2/src/forms/directives/ng_model.ts b/modules/angular2/src/forms/directives/ng_model.ts index 271aff9cb0..81472a1bed 100644 --- a/modules/angular2/src/forms/directives/ng_model.ts +++ b/modules/angular2/src/forms/directives/ng_model.ts @@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang'; import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; import {StringMapWrapper} from 'angular2/src/facade/collection'; -import {Directive, Ancestor, onChange, QueryList, Query} from 'angular2/angular2'; -import {forwardRef, Binding} from 'angular2/di'; +import {Directive, onChange, QueryList, Query} from 'angular2/angular2'; +import {forwardRef, Ancestor, Binding} from 'angular2/di'; import {NgControl} from './ng_control'; import {Control} from '../model'; diff --git a/modules/angular2/src/router/router_outlet.ts b/modules/angular2/src/router/router_outlet.ts index fbb4edb95a..cf9c5b0d76 100644 --- a/modules/angular2/src/router/router_outlet.ts +++ b/modules/angular2/src/router/router_outlet.ts @@ -26,15 +26,17 @@ export class RouterOutlet { private _componentRef: ComponentRef; private _elementRef: ElementRef; private _currentInstruction: Instruction; + private _injector: Injector; constructor(elementRef: ElementRef, private _loader: DynamicComponentLoader, - private _parentRouter: routerMod.Router, private _injector: Injector, + private _parentRouter: routerMod.Router, _injector: Injector, @Attribute('name') nameAttr: string) { // TODO: reintroduce with new // sibling routes // if (isBlank(nameAttr)) { // nameAttr = 'default'; //} + this._injector = _injector.getAppInjector(); this._elementRef = elementRef; this._childRouter = null; diff --git a/modules/angular2/src/test_lib/test_component_builder.ts b/modules/angular2/src/test_lib/test_component_builder.ts index d60f6034b6..12df20258e 100644 --- a/modules/angular2/src/test_lib/test_component_builder.ts +++ b/modules/angular2/src/test_lib/test_component_builder.ts @@ -144,6 +144,8 @@ export class TestComponentBuilder { // TODO(juliemr): can/should this be optional? DOM.appendChild(doc.body, rootEl); + + return this._injector.get(DynamicComponentLoader) .loadAsRoot(rootComponentType, `#${rootElId}`, this._injector) .then((componentRef) => { return new RootTestComponent(componentRef); }); diff --git a/modules/angular2/test/core/application_spec.ts b/modules/angular2/test/core/application_spec.ts index 464435ec0d..c0af816262 100644 --- a/modules/angular2/test/core/application_spec.ts +++ b/modules/angular2/test/core/application_spec.ts @@ -85,6 +85,7 @@ export function main() { inject([AsyncTestCompleter], (async) => { var refPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e, t) => { throw e; }); + PromiseWrapper.then(refPromise, null, (reason) => { expect(reason.message) .toContain( @@ -108,24 +109,6 @@ export function main() { expect(refPromise).not.toBe(null); }); - it('should resolve an injector promise and contain bindings', - inject([AsyncTestCompleter], (async) => { - var refPromise = bootstrap(HelloRootCmp, testBindings); - refPromise.then((ref) => { - expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp); - async.done(); - }); - })); - - it('should provide the application component in the injector', - inject([AsyncTestCompleter], (async) => { - var refPromise = bootstrap(HelloRootCmp, testBindings); - refPromise.then((ref) => { - expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp); - async.done(); - }); - })); - it('should display hello world', inject([AsyncTestCompleter], (async) => { var refPromise = bootstrap(HelloRootCmp, testBindings); refPromise.then((ref) => { @@ -151,7 +134,7 @@ export function main() { bootstrap(HelloRootCmp3, [testBindings, bind("appBinding").toValue("BoundValue")]); refPromise.then((ref) => { - expect(ref.injector.get(HelloRootCmp3).appBinding).toEqual("BoundValue"); + expect(ref.hostComponent.appBinding).toEqual("BoundValue"); async.done(); }); })); @@ -161,7 +144,7 @@ export function main() { var refPromise = bootstrap(HelloRootCmp4, testBindings); refPromise.then((ref) => { - expect(ref.injector.get(HelloRootCmp4).lc).toBe(ref.injector.get(LifeCycle)); + expect(ref.hostComponent.lc).toBe(ref.injector.get(LifeCycle)); async.done(); }); })); @@ -183,7 +166,7 @@ export function main() { .then((refs: ApplicationRef[]) => { var registry = refs[0].injector.get(TestabilityRegistry); var testabilities = - [refs[0].injector.asyncGet(Testability), refs[1].injector.asyncGet(Testability)]; + [refs[0].injector.get(Testability), refs[1].injector.get(Testability)]; PromiseWrapper.all(testabilities) .then((testabilities: Testability[]) => { expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]); diff --git a/modules/angular2/test/core/compiler/element_injector_spec.ts b/modules/angular2/test/core/compiler/element_injector_spec.ts index 6e723ddebd..e11217ec89 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.ts +++ b/modules/angular2/test/core/compiler/element_injector_spec.ts @@ -34,17 +34,13 @@ import { } from 'angular2/src/core/compiler/element_injector'; import * as dirAnn from 'angular2/src/core/annotations_impl/annotations'; import { - Parent, - Ancestor, - Unbounded, Attribute, Query, Component, Directive, onDestroy } from 'angular2/annotations'; -import * as ngDiAnn from 'angular2/src/core/annotations_impl/visibility'; -import {bind, Injector, Binding, resolveBindings, Optional, Inject, Injectable} from 'angular2/di'; +import {bind, Injector, Binding, resolveBindings, Optional, Inject, Injectable, Self, Parent, Ancestor, Unbounded, self} from 'angular2/di'; import * as diAnn from 'angular2/src/di/annotations_impl'; import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref'; @@ -78,13 +74,16 @@ class DummyElementRef extends SpyObject { noSuchMethod(m) { return super.noSuchMethod(m); } } +@Injectable(self) class SimpleDirective {} class SimpleService {} +@Injectable(self) class SomeOtherDirective {} var _constructionCount = 0; +@Injectable(self) class CountingDirective { count; constructor() { @@ -93,6 +92,7 @@ class CountingDirective { } } +@Injectable(self) class FancyCountingDirective extends CountingDirective { constructor() { super(); } } @@ -139,6 +139,12 @@ class NeedsService { constructor(@Inject("service") service) { this.service = service; } } +@Injectable() +class NeedsAncestorService { + service: any; + constructor(@Ancestor() @Inject("service") service) { this.service = service; } +} + class HasEventEmitter { emitter; constructor() { this.emitter = "emitter"; } @@ -581,7 +587,6 @@ export function main() { expect(d.dependency).toBeAnInstanceOf(SimpleDirective); }); - it("should instantiate hostInjector injectables that have dependencies with set visibility", function() { var childInj = parentChildInjectors( @@ -597,7 +602,7 @@ export function main() { bind('injectable2') .toFactory( (val) => `${val}-injectable2`, - [[new diAnn.Inject('injectable1'), new ngDiAnn.Parent()]]) + [[new diAnn.Inject('injectable1'), new diAnn.Parent()]]) ] }))]); expect(childInj.get('injectable2')).toEqual('injectable1-injectable2'); @@ -648,7 +653,8 @@ export function main() { expect(shadowInj.get(NeedsService).service).toEqual('hostService'); }); - it("should not instantiate a directive in a view that depends on hostInjector bindings of a decorator directive", () => { + it("should not instantiate a directive in a view that has an ancestor dependency on hostInjector"+ + " bindings of a decorator directive", () => { expect(() => { hostShadowInjectors( ListWrapper.concat([ @@ -657,7 +663,7 @@ export function main() { hostInjector: [bind('service').toValue('hostService')]}) )], extraBindings), - ListWrapper.concat([NeedsService], extraBindings) + ListWrapper.concat([NeedsAncestorService], extraBindings) ); }).toThrowError(new RegExp("No provider for service!")); }); diff --git a/modules/angular2/test/core/compiler/integration_dart_spec.dart b/modules/angular2/test/core/compiler/integration_dart_spec.dart index 3a97b59b63..4f0091bf66 100644 --- a/modules/angular2/test/core/compiler/integration_dart_spec.dart +++ b/modules/angular2/test/core/compiler/integration_dart_spec.dart @@ -55,7 +55,8 @@ main() { }); describe('Error handling', () { - it('should preserve Error stack traces thrown from components', inject([ + //TODO: vsavkin reenable this test after merging DI and EI + xit('should preserve Error stack traces thrown from components', inject([ TestComponentBuilder, AsyncTestCompleter ], (tb, async) { @@ -69,7 +70,8 @@ main() { }); })); - it('should preserve non-Error stack traces thrown from components', inject([ + //TODO: vsavkin reenable this test after merging DI and EI + xit('should preserve non-Error stack traces thrown from components', inject([ TestComponentBuilder, AsyncTestCompleter ], (tb, async) { diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index 2adab993f1..78daf1bad0 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -33,7 +33,18 @@ import { } from 'angular2/src/facade/lang'; import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; -import {Injector, bind, Injectable, Binding, forwardRef, OpaqueToken, Inject} from 'angular2/di'; +import { + Injector, + bind, + Injectable, + Binding, + forwardRef, + OpaqueToken, + Inject, + Parent, + Ancestor, + Unbounded +} from 'angular2/di'; import { PipeFactory, PipeRegistry, @@ -45,18 +56,9 @@ import { ON_PUSH } from 'angular2/change_detection'; -import { - Directive, - Component, - View, - Parent, - Ancestor, - Unbounded, - Attribute, - Query -} from 'angular2/annotations'; +import {Directive, Component, View, Attribute, Query} from 'angular2/annotations'; import * as viewAnn from 'angular2/src/core/annotations_impl/view'; -import * as visAnn from 'angular2/src/core/annotations_impl/visibility'; +import * as visAnn from 'angular2/src/di/annotations_impl'; import {QueryList} from 'angular2/src/core/compiler/query_list'; diff --git a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts index 6f9c823493..7002eec178 100644 --- a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts +++ b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts @@ -156,7 +156,7 @@ export function main() { } function directiveBinding({metadata}: {metadata?: any} = {}) { - return new DirectiveBinding(Key.get("dummy"), null, [], false, [], [], [], metadata); + return new DirectiveBinding(Key.get("dummy"), null, [], [], [], [], metadata); } function createRenderProtoView(elementBinders = null, type: renderApi.ViewType = null) { diff --git a/modules/angular2/test/core/compiler/view_manager_spec.ts b/modules/angular2/test/core/compiler/view_manager_spec.ts index e31396f4f8..3d2206013e 100644 --- a/modules/angular2/test/core/compiler/view_manager_spec.ts +++ b/modules/angular2/test/core/compiler/view_manager_spec.ts @@ -210,7 +210,7 @@ export function main() { }); it('should hydrate the view', () => { - var injector = new Injector([], null, false); + var injector = Injector.resolveAndCreate([]); manager.createRootHostView(wrapPv(hostProtoView), null, injector); expect(utils.spy('hydrateRootHostView')).toHaveBeenCalledWith(createdViews[0], injector); expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render); @@ -301,7 +301,7 @@ export function main() { }); it('should hydrate the view', () => { - var injector = new Injector([], null, false); + var injector = Injector.resolveAndCreate([]); var contextView = createView(createProtoView([createEmptyElBinder(), createEmptyElBinder()])); manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView), diff --git a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts index 0c67fb0a2c..0c205c130b 100644 --- a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts +++ b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts @@ -42,7 +42,7 @@ export function main() { var directiveResolver; var utils; - function createInjector() { return new Injector([], null, false); } + function createInjector() { return Injector.resolveAndCreate([]); } function createDirectiveBinding(type) { var annotation = directiveResolver.resolve(type); diff --git a/modules/angular2/test/di/async_spec.ts b/modules/angular2/test/di/async_spec.ts deleted file mode 100644 index 18e4a14928..0000000000 --- a/modules/angular2/test/di/async_spec.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { - AsyncTestCompleter, - beforeEach, - ddescribe, - describe, - expect, - iit, - inject, - it, - xit, -} from 'angular2/test_lib'; -import {Injector, bind, Key} from 'angular2/di'; -import {Inject, InjectPromise, Injectable} from 'angular2/src/di/decorators'; -import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; -import {stringify} from 'angular2/src/facade/lang'; - -class UserList {} - -function fetchUsers() { - return PromiseWrapper.resolve(new UserList()); -} - -class SynchronousUserList {} - -@Injectable() -class UserController { - list: UserList; - constructor(list: UserList) { this.list = list; } -} - -@Injectable() -class AsyncUserController { - userList; - constructor(@InjectPromise(UserList) userList) { this.userList = userList; } -} - -export function main() { - describe("async injection", function() { - - describe("asyncGet", function() { - it('should return a promise', function() { - var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]); - var p = injector.asyncGet(UserList); - expect(p).toBePromise(); - }); - - it('should return a promise when the binding is sync', function() { - var injector = Injector.resolveAndCreate([SynchronousUserList]); - var p = injector.asyncGet(SynchronousUserList); - expect(p).toBePromise(); - }); - - it("should return a promise when the binding is sync (from cache)", function() { - var injector = Injector.resolveAndCreate([UserList]); - expect(injector.get(UserList)).toBeAnInstanceOf(UserList); - expect(injector.asyncGet(UserList)).toBePromise(); - }); - - it('should return the injector', inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate([]); - var p = injector.asyncGet(Injector); - p.then(function(injector) { - expect(injector).toBe(injector); - async.done(); - }); - })); - - it('should return a promise when instantiating a sync binding ' + - 'with an async dependency', - inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate( - [bind(UserList).toAsyncFactory(fetchUsers), UserController]); - - injector.asyncGet(UserController) - .then(function(userController) { - expect(userController).toBeAnInstanceOf(UserController); - expect(userController.list).toBeAnInstanceOf(UserList); - async.done(); - }); - })); - - it("should create only one instance (async + async)", - inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]); - - var ul1 = injector.asyncGet(UserList); - var ul2 = injector.asyncGet(UserList); - - PromiseWrapper.all([ul1, ul2]) - .then(function(uls) { - expect(uls[0]).toBe(uls[1]); - async.done(); - }); - })); - - it("should create only one instance (sync + async)", inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate([UserList]); - - var promise = injector.asyncGet(UserList); - var ul = injector.get(UserList); - - expect(promise).toBePromise(); - expect(ul).toBeAnInstanceOf(UserList); - - promise.then(function(ful) { - expect(ful).toBe(ul); - async.done(); - }); - })); - - it('should show the full path when error happens in a constructor', - inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate([ - UserController, - bind(UserList).toAsyncFactory(function() { throw "Broken UserList"; }) - ]); - - var promise = injector.asyncGet(UserController); - PromiseWrapper.then(promise, null, function(e) { - expect(e.message).toContain( - `Error during instantiation of UserList! (${stringify(UserController)} -> UserList)`); - async.done(); - }); - })); - }); - - describe("get", function() { - it('should throw when instantiating an async binding', function() { - var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]); - - expect(() => injector.get(UserList)) - .toThrowError( - 'Cannot instantiate UserList synchronously. It is provided as a promise!'); - }); - - it('should throw when instantiating a sync binding with an async dependency', function() { - var injector = - Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers), UserController]); - - expect(() => injector.get(UserController)) - .toThrowError(new RegExp( - 'Cannot instantiate UserList synchronously. It is provided as a promise!')); - }); - - it('should not throw when instantiating a sync binding with a resolved async dependency', - inject([AsyncTestCompleter], (async) => { - var injector = Injector.resolveAndCreate( - [bind(UserList).toAsyncFactory(fetchUsers), UserController]); - - injector.asyncGet(UserList).then((_) => { - expect(() => { injector.get(UserController); }).not.toThrow(); - async.done(); - }); - })); - - it('should resolve synchronously when an async dependency requested as a promise', - function() { - var injector = Injector.resolveAndCreate( - [bind(UserList).toAsyncFactory(fetchUsers), AsyncUserController]); - var controller = injector.get(AsyncUserController); - - expect(controller).toBeAnInstanceOf(AsyncUserController); - expect(controller.userList).toBePromise(); - }); - - it('should wrap sync dependencies into promises if required', function() { - var injector = Injector.resolveAndCreate( - [bind(UserList).toFactory(() => new UserList()), AsyncUserController]); - var controller = injector.get(AsyncUserController); - - expect(controller).toBeAnInstanceOf(AsyncUserController); - expect(controller.userList).toBePromise(); - }); - }); - }); -} diff --git a/modules/angular2/test/di/binding_dart_spec.dart b/modules/angular2/test/di/binding_dart_spec.dart index 0a8624d3f2..79df12b815 100644 --- a/modules/angular2/test/di/binding_dart_spec.dart +++ b/modules/angular2/test/di/binding_dart_spec.dart @@ -28,14 +28,10 @@ main() { expect(const Binding(Foo, toFactory: fn).toFactory).toBe(fn); }); - it('can create constant from async factory', () { - expect(const Binding(Foo, toAsyncFactory: fn).toAsyncFactory).toBe(fn); - }); - it('can be used in annotation', () { ClassMirror mirror = reflectType(Annotated); var bindings = mirror.metadata[0].reflectee.bindings; - expect(bindings.length).toBe(6); + expect(bindings.length).toBe(5); bindings.forEach((b) { expect(b).toBeA(Binding); }); @@ -57,7 +53,6 @@ class Annotation { const Binding(Foo, toClass: Bar), const Binding(Foo, toValue: 5), const Binding(Foo, toAlias: Bar), - const Binding(Foo, toFactory: fn), - const Binding(Foo, toAsyncFactory: fn), + const Binding(Foo, toFactory: fn) ]) class Annotated {} diff --git a/modules/angular2/test/di/injector_spec.ts b/modules/angular2/test/di/injector_spec.ts index b1de28cf3f..be54026726 100644 --- a/modules/angular2/test/di/injector_spec.ts +++ b/modules/angular2/test/di/injector_spec.ts @@ -9,13 +9,22 @@ import { DependencyAnnotation, Injectable } from 'angular2/di'; -import {Optional, Inject, InjectLazy} from 'angular2/src/di/decorators'; +import {Optional, Inject} from 'angular2/src/di/decorators'; import * as ann from 'angular2/src/di/annotations_impl'; class CustomDependencyAnnotation extends DependencyAnnotation {} class Engine {} +@Injectable(ann.self) +class EngineWithSetVisibility { +} + +@Injectable() +class CarNeedsEngineWithSetVisibility { + constructor(engine: EngineWithSetVisibility) {} +} + class BrokenEngine { constructor() { throw new BaseException("Broken Engine"); } } @@ -35,12 +44,6 @@ class Car { constructor(engine: Engine) { this.engine = engine; } } -@Injectable() -class CarWithLazyEngine { - engineFactory; - constructor(@InjectLazy(Engine) engineFactory) { this.engineFactory = engineFactory; } -} - @Injectable() class CarWithOptionalEngine { engine; @@ -236,10 +239,6 @@ export function main() { expect(() => injector.get(Car)) .toThrowError( `Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`); - - expect(() => injector.asyncGet(Car)) - .toThrowError( - `Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`); }); it('should show the full path when error happens in a constructor', () => { @@ -274,25 +273,6 @@ export function main() { expect(injector.get('null')).toBe(null); }); - describe("default bindings", () => { - it("should be used when no matching binding found", () => { - var injector = Injector.resolveAndCreate([], {defaultBindings: true}); - - var car = injector.get(Car); - - expect(car).toBeAnInstanceOf(Car); - }); - - it("should use the matching binding when it is available", () => { - var injector = - Injector.resolveAndCreate([bind(Car).toClass(SportsCar)], {defaultBindings: true}); - - var car = injector.get(Car); - - expect(car).toBeAnInstanceOf(SportsCar); - }); - }); - describe("child", () => { it('should load instances from parent injector', () => { var parent = Injector.resolveAndCreate([Engine]); @@ -324,17 +304,6 @@ export function main() { expect(engineFromChild).toBeAnInstanceOf(TurboEngine); }); - it("should create child injectors without default bindings", () => { - var parent = Injector.resolveAndCreate([], {defaultBindings: true}); - var child = parent.resolveAndCreateChild([]); - - // child delegates to parent the creation of Car - var childCar = child.get(Car); - var parentCar = parent.get(Car); - - expect(childCar).toBe(parentCar); - }); - it("should give access to direct parent", () => { var parent = Injector.resolveAndCreate([]); var child = parent.resolveAndCreateChild([]); @@ -342,25 +311,6 @@ export function main() { }); }); - describe("lazy", () => { - it("should create dependencies lazily", () => { - var injector = Injector.resolveAndCreate([Engine, CarWithLazyEngine]); - - var car = injector.get(CarWithLazyEngine); - expect(car.engineFactory()).toBeAnInstanceOf(Engine); - }); - - it("should cache instance created lazily", () => { - var injector = Injector.resolveAndCreate([Engine, CarWithLazyEngine]); - - var car = injector.get(CarWithLazyEngine); - var e1 = car.engineFactory(); - var e2 = car.engineFactory(); - - expect(e1).toBe(e2); - }); - }); - describe('resolve', () => { it('should resolve and flatten', () => { var bindings = Injector.resolve([Engine, [BrokenEngine]]); @@ -374,20 +324,16 @@ export function main() { var bindings = Injector.resolve([ forwardRef(() => Engine), [bind(forwardRef(() => BrokenEngine)).toClass(forwardRef(() => Engine))], - bind(forwardRef(() => String)).toFactory(() => 'OK', [forwardRef(() => Engine)]), - bind(forwardRef(() => DashboardSoftware)) - .toAsyncFactory(() => 123, [forwardRef(() => BrokenEngine)]) + bind(forwardRef(() => String)).toFactory(() => 'OK', [forwardRef(() => Engine)]) ]); - var engineBinding = bindings[Key.get(Engine).id]; - var brokenEngineBinding = bindings[Key.get(BrokenEngine).id]; - var stringBinding = bindings[Key.get(String).id]; - var dashboardSoftwareBinding = bindings[Key.get(DashboardSoftware).id]; + var engineBinding = bindings[0]; + var brokenEngineBinding = bindings[1]; + var stringBinding = bindings[2]; expect(engineBinding.factory() instanceof Engine).toBe(true); expect(brokenEngineBinding.factory() instanceof Engine).toBe(true); expect(stringBinding.dependencies[0].key).toEqual(Key.get(Engine)); - expect(dashboardSoftwareBinding.dependencies[0].key).toEqual(Key.get(BrokenEngine)); }); it('should support overriding factory dependencies with dependency annotations', () => { @@ -396,11 +342,25 @@ export function main() { .toFactory((e) => "result", [[new ann.Inject("dep"), new CustomDependencyAnnotation()]]) ]); - var binding = bindings[Key.get("token").id]; + var binding = bindings[0]; expect(binding.dependencies[0].key.token).toEqual("dep"); expect(binding.dependencies[0].properties).toEqual([new CustomDependencyAnnotation()]); }); }); + + describe("default visibility", () => { + it("should use the provided visibility", () => { + var bindings = Injector.resolve([CarNeedsEngineWithSetVisibility, EngineWithSetVisibility]); + var carBinding = bindings[0]; + expect(carBinding.dependencies[0].visibility).toEqual(ann.self); + }); + + it("should set the default visibility to unbounded", () => { + var bindings = Injector.resolve([Car, Engine]); + var carBinding = bindings[0]; + expect(carBinding.dependencies[0].visibility).toEqual(ann.unbounded); + }); + }); }); } diff --git a/modules/angular2/test/router/router_integration_spec.ts b/modules/angular2/test/router/router_integration_spec.ts index d34b46a763..8fa5d101be 100644 --- a/modules/angular2/test/router/router_integration_spec.ts +++ b/modules/angular2/test/router/router_integration_spec.ts @@ -56,7 +56,7 @@ export function main() { var router = applicationRef.hostComponent.router; PromiseWrapper.catchError(router.navigate('/cause-error'), (error) => { expect(el).toHaveText('outer { oh no }'); - expect(error.message).toBe('oops!'); + expect(error.message).toContain('oops!'); async.done(); }); }); @@ -89,7 +89,6 @@ export function main() { router.navigate('/parent/child'); }); })); - // TODO: add a test in which the child component has bindings }); } @@ -107,14 +106,12 @@ class AppCmp { constructor(public router: Router, public location: LocationStrategy) {} } - @Component({selector: 'parent-cmp'}) @View({template: `parent { }`, directives: routerDirectives}) @RouteConfig([{path: '/child', component: HelloCmp}]) class ParentCmp { } - @Component({selector: 'app-cmp'}) @View({template: `root { }`, directives: routerDirectives}) @RouteConfig([{path: '/parent/...', component: ParentCmp}]) diff --git a/modules/benchmarks/src/costs/index.ts b/modules/benchmarks/src/costs/index.ts index 9edfcc74d4..c5ab478316 100644 --- a/modules/benchmarks/src/costs/index.ts +++ b/modules/benchmarks/src/costs/index.ts @@ -23,7 +23,7 @@ export function main() { bootstrap(AppComponent) .then((ref) => { var injector = ref.injector; - var app: AppComponent = injector.get(AppComponent); + var app: AppComponent = ref.hostComponent; var lifeCycle = injector.get(LifeCycle); bindAction('#reset', function() { diff --git a/modules/benchmarks/src/largetable/largetable_benchmark.ts b/modules/benchmarks/src/largetable/largetable_benchmark.ts index f33c9e4485..8e65706e6d 100644 --- a/modules/benchmarks/src/largetable/largetable_benchmark.ts +++ b/modules/benchmarks/src/largetable/largetable_benchmark.ts @@ -125,7 +125,7 @@ export function main() { bootstrap(AppComponent, _createBindings()) .then((ref) => { var injector = ref.injector; - app = injector.get(AppComponent); + app = ref.hostComponent; lifecycle = injector.get(LifeCycle); bindAction('#ng2DestroyDom', ng2DestroyDom); bindAction('#ng2CreateDom', ng2CreateDom); diff --git a/modules/benchmarks/src/tree/tree_benchmark.ts b/modules/benchmarks/src/tree/tree_benchmark.ts index a0252e6782..e95f805847 100644 --- a/modules/benchmarks/src/tree/tree_benchmark.ts +++ b/modules/benchmarks/src/tree/tree_benchmark.ts @@ -85,7 +85,6 @@ export function main() { function ng2CreateDom() { var values = count++ % 2 == 0 ? ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] : ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-']; - app.initData = buildTree(maxDepth, values, 0); lifeCycle.tick(); } @@ -98,7 +97,7 @@ export function main() { var injector = ref.injector; lifeCycle = injector.get(LifeCycle); - app = injector.get(AppComponent); + app = ref.hostComponent; bindAction('#ng2DestroyDom', ng2DestroyDom); bindAction('#ng2CreateDom', ng2CreateDom); bindAction('#ng2UpdateDomProfile', profile(ng2CreateDom, noop, 'ng2-update')); diff --git a/modules/benchpress/src/metric/multi_metric.ts b/modules/benchpress/src/metric/multi_metric.ts index 1b14857df0..5c55330be8 100644 --- a/modules/benchpress/src/metric/multi_metric.ts +++ b/modules/benchpress/src/metric/multi_metric.ts @@ -8,10 +8,10 @@ export class MultiMetric extends Metric { static createBindings(childTokens): List { return [ bind(_CHILDREN) - .toAsyncFactory((injector) => PromiseWrapper.all( - ListWrapper.map(childTokens, (token) => injector.asyncGet(token))), - [Injector]), - bind(MultiMetric).toFactory((children) => new MultiMetric(children), [_CHILDREN]) + .toFactory( + (injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)), + [Injector]), + bind(MultiMetric).toFactory(children => new MultiMetric(children), [_CHILDREN]) ]; } @@ -52,4 +52,4 @@ function mergeStringMaps(maps): Object { return result; } -var _CHILDREN = new OpaqueToken('MultiMetric.children'); +var _CHILDREN = new OpaqueToken('MultiMetric.children'); \ No newline at end of file diff --git a/modules/benchpress/src/reporter/multi_reporter.ts b/modules/benchpress/src/reporter/multi_reporter.ts index 62fe2c302c..9f2583e66c 100644 --- a/modules/benchpress/src/reporter/multi_reporter.ts +++ b/modules/benchpress/src/reporter/multi_reporter.ts @@ -9,10 +9,10 @@ export class MultiReporter extends Reporter { static createBindings(childTokens: List): List { return [ bind(_CHILDREN) - .toAsyncFactory((injector) => PromiseWrapper.all( - ListWrapper.map(childTokens, (token) => injector.asyncGet(token))), - [Injector]), - bind(MultiReporter).toFactory((children) => new MultiReporter(children), [_CHILDREN]) + .toFactory( + (injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)), + [Injector]), + bind(MultiReporter).toFactory(children => new MultiReporter(children), [_CHILDREN]) ]; } diff --git a/modules/benchpress/src/runner.ts b/modules/benchpress/src/runner.ts index 39a0f43137..fb4b537d73 100644 --- a/modules/benchpress/src/runner.ts +++ b/modules/benchpress/src/runner.ts @@ -1,7 +1,7 @@ import {Injector, bind, Binding} from 'angular2/di'; import {isPresent, isBlank} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; -import {Promise} from 'angular2/src/facade/async'; +import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {Sampler, SampleState} from './sampler'; import {ConsoleReporter} from './reporter/console_reporter'; @@ -50,9 +50,31 @@ export class Runner { if (isPresent(bindings)) { sampleBindings.push(bindings); } - return Injector.resolveAndCreate(sampleBindings) - .asyncGet(Sampler) - .then((sampler) => sampler.sample()); + + var inj = Injector.resolveAndCreate(sampleBindings); + var adapter = inj.get(WebDriverAdapter); + + return PromiseWrapper + .all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')]) + .then((args) => { + var capabilities = args[0]; + var userAgent = args[1]; + + // This might still create instances twice. We are creating a new injector with all the + // bindings. + // Only WebDriverAdapter is reused. + // TODO vsavkin consider changing it when toAsyncFactory is added back or when child + // injectors are handled better. + var injector = Injector.resolveAndCreate([ + sampleBindings, + bind(Options.CAPABILITIES).toValue(capabilities), + bind(Options.USER_AGENT).toValue(userAgent), + bind(WebDriverAdapter).toValue(adapter) + ]); + + var sampler = injector.get(Sampler); + return sampler.sample(); + }); } } @@ -74,10 +96,4 @@ var _DEFAULT_BINDINGS = [ Validator.bindTo(RegressionSlopeValidator), WebDriverExtension.bindTo([ChromeDriverExtension, FirefoxDriverExtension, IOsDriverExtension]), Metric.bindTo(MultiMetric), - - bind(Options.CAPABILITIES) - .toAsyncFactory((adapter) => adapter.capabilities(), [WebDriverAdapter]), - bind(Options.USER_AGENT) - .toAsyncFactory((adapter) => adapter.executeScript('return window.navigator.userAgent;'), - [WebDriverAdapter]) ]; diff --git a/modules/benchpress/src/web_driver_extension.ts b/modules/benchpress/src/web_driver_extension.ts index 6ae1d0a9b7..4bba52d893 100644 --- a/modules/benchpress/src/web_driver_extension.ts +++ b/modules/benchpress/src/web_driver_extension.ts @@ -14,11 +14,11 @@ import {Options} from './common_options'; @ABSTRACT() export class WebDriverExtension { static bindTo(childTokens): List { - return [ + var res = [ bind(_CHILDREN) - .toAsyncFactory((injector) => PromiseWrapper.all( - ListWrapper.map(childTokens, (token) => injector.asyncGet(token))), - [Injector]), + .toFactory( + (injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)), + [Injector]), bind(WebDriverExtension) .toFactory( (children, capabilities) => { @@ -35,6 +35,7 @@ export class WebDriverExtension { }, [_CHILDREN, Options.CAPABILITIES]) ]; + return res; } gc(): Promise { throw new BaseException('NYI'); } diff --git a/modules/benchpress/test/metric/multi_metric_spec.ts b/modules/benchpress/test/metric/multi_metric_spec.ts index a4c9a390ff..fe460adfe5 100644 --- a/modules/benchpress/test/metric/multi_metric_spec.ts +++ b/modules/benchpress/test/metric/multi_metric_spec.ts @@ -18,15 +18,15 @@ import {Metric, MultiMetric, bind, Injector} from 'benchpress/common'; export function main() { function createMetric(ids) { - return Injector.resolveAndCreate([ - ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id))), - MultiMetric.createBindings(ids) - ]) - .asyncGet(MultiMetric); + var m = Injector.resolveAndCreate([ + ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id))), + MultiMetric.createBindings(ids) + ]) + .get(MultiMetric); + return PromiseWrapper.resolve(m); } describe('multi metric', () => { - it('should merge descriptions', inject([AsyncTestCompleter], (async) => { createMetric(['m1', 'm2']) .then((m) => { diff --git a/modules/benchpress/test/reporter/multi_reporter_spec.ts b/modules/benchpress/test/reporter/multi_reporter_spec.ts index f2f0fe89df..85cc3f0445 100644 --- a/modules/benchpress/test/reporter/multi_reporter_spec.ts +++ b/modules/benchpress/test/reporter/multi_reporter_spec.ts @@ -19,11 +19,12 @@ import {Reporter, MultiReporter, bind, Injector, MeasureValues} from 'benchpress export function main() { function createReporters(ids) { - return Injector.resolveAndCreate([ - ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id))), - MultiReporter.createBindings(ids) - ]) - .asyncGet(MultiReporter); + var r = Injector.resolveAndCreate([ + ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id))), + MultiReporter.createBindings(ids) + ]) + .get(MultiReporter); + return PromiseWrapper.resolve(r); } describe('multi reporter', () => { diff --git a/modules/benchpress/test/runner_spec.ts b/modules/benchpress/test/runner_spec.ts index c63f0e8e24..db0e3814f4 100644 --- a/modules/benchpress/test/runner_spec.ts +++ b/modules/benchpress/test/runner_spec.ts @@ -52,7 +52,7 @@ export function main() { it('should set SampleDescription.id', inject([AsyncTestCompleter], (async) => { createRunner() .sample({id: 'someId'}) - .then((_) => injector.asyncGet(SampleDescription)) + .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.id).toBe('someId'); async.done(); @@ -62,9 +62,8 @@ export function main() { it('should merge SampleDescription.description', inject([AsyncTestCompleter], (async) => { createRunner([bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1})]) .sample({id: 'someId', bindings: [bind(Options.SAMPLE_DESCRIPTION).toValue({'b': 2})]}) - .then((_) => injector.asyncGet(SampleDescription)) + .then((_) => injector.get(SampleDescription)) .then((desc) => { - expect(desc.description) .toEqual( {'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11}); @@ -76,7 +75,7 @@ export function main() { inject([AsyncTestCompleter], (async) => { createRunner() .sample({id: 'someId'}) - .then((_) => injector.asyncGet(SampleDescription)) + .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.metrics).toEqual({'m1': 'some metric'}); @@ -125,10 +124,10 @@ export function main() { .toValue({'a': 2}), ] }) - .then((_) => injector.asyncGet(SampleDescription)) + .then((_) => injector.get(SampleDescription)) .then((desc) => { - expect(injector.get(SampleDescription).description['a']).toBe(2); + expect(desc.description['a']).toBe(2); async.done(); }); @@ -139,6 +138,7 @@ export function main() { class MockWebDriverAdapter extends WebDriverAdapter { executeScript(script): Promise { return PromiseWrapper.resolve('someUserAgent'); } + capabilities() { return null; } } class MockValidator extends Validator { diff --git a/modules/benchpress/test/web_driver_extension_spec.ts b/modules/benchpress/test/web_driver_extension_spec.ts index 0bb2c9a73a..c8b3e09b8b 100644 --- a/modules/benchpress/test/web_driver_extension_spec.ts +++ b/modules/benchpress/test/web_driver_extension_spec.ts @@ -19,12 +19,14 @@ import {WebDriverExtension, bind, Injector, Options} from 'benchpress/common'; export function main() { function createExtension(ids, caps) { - return Injector.resolveAndCreate([ - ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id))), - bind(Options.CAPABILITIES).toValue(caps), - WebDriverExtension.bindTo(ids) - ]) - .asyncGet(WebDriverExtension); + return PromiseWrapper.wrap(() => { + return Injector.resolveAndCreate([ + ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id))), + bind(Options.CAPABILITIES).toValue(caps), + WebDriverExtension.bindTo(ids) + ]) + .get(WebDriverExtension); + }); } describe('WebDriverExtension.bindTo', () => { @@ -44,7 +46,6 @@ export function main() { async.done(); }); })); - }); } diff --git a/modules/examples/src/material/button/index.html b/modules/examples/src/material/button/index.html index 876dc42e36..2fa7e4d248 100644 --- a/modules/examples/src/material/button/index.html +++ b/modules/examples/src/material/button/index.html @@ -10,4 +10,4 @@ Loading... $SCRIPTS$ - + \ No newline at end of file