Skip to content

Commit 308823b

Browse files
committed
perf(view): use pre-resolved bindings for child injector init
Creating a child injector from pre-resolved bindings (if any) is an order of magnitude faster.
1 parent c05bad3 commit 308823b

File tree

8 files changed

+62
-24
lines changed

8 files changed

+62
-24
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import {Type} from 'angular2/src/facade/lang';
2+
import {List} from 'angular2/src/facade/collection';
23
import {Directive} from 'angular2/src/core/annotations/annotations'
4+
import {ResolvedBinding} from 'angular2/di';
35

46
/**
57
* Combination of a type with the Directive annotation
68
*/
79
export class DirectiveMetadata {
810
type:Type;
911
annotation:Directive;
12+
resolvedInjectables:List<ResolvedBinding>;
1013

11-
constructor(type:Type, annotation:Directive) {
14+
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
1215
this.annotation = annotation;
1316
this.type = type;
17+
this.resolvedInjectables = resolvedInjectables;
1418
}
1519
}

modules/angular2/src/core/compiler/directive_metadata_reader.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {Injectable} from 'angular2/di';
1+
import {Injectable, Injector} from 'angular2/di';
22
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
3-
import {Directive} from '../annotations/annotations';
3+
import {Directive, Component} from '../annotations/annotations';
44
import {DirectiveMetadata} from './directive_metadata';
55
import {reflector} from 'angular2/src/reflection/reflection';
66

@@ -13,7 +13,11 @@ export class DirectiveMetadataReader {
1313
var annotation = annotations[i];
1414

1515
if (annotation instanceof Directive) {
16-
return new DirectiveMetadata(type, annotation);
16+
var resolvedInjectables = null;
17+
if (annotation instanceof Component && isPresent(annotation.injectables)) {
18+
resolvedInjectables = Injector.resolve(annotation.injectables);
19+
}
20+
return new DirectiveMetadata(type, annotation, resolvedInjectables);
1721
}
1822
}
1923
}

modules/angular2/src/core/compiler/dynamic_component_loader.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {Key, Injector, Injectable} from 'angular2/di'
1+
import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di'
22
import {Compiler} from './compiler';
33
import {DirectiveMetadataReader} from './directive_metadata_reader';
44
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
5+
import {List} from 'angular2/src/facade/collection';
56
import {Promise} from 'angular2/src/facade/async';
67
import {Component} from 'angular2/src/core/annotations/annotations';
78
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
@@ -34,15 +35,15 @@ export class DynamicComponentLoader {
3435
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
3536
this._assertTypeIsComponent(type);
3637

37-
var annotation = this._directiveMetadataReader.read(type).annotation;
38+
var directiveMetadata = this._directiveMetadataReader.read(type);
3839

39-
var inj = this._componentAppInjector(location, injector, annotation.injectables);
40+
var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables);
4041

4142
var hostEi = location.elementInjector;
4243
var hostView = location.hostView;
4344

4445
return this._compiler.compile(type).then(componentProtoView => {
45-
var context = hostEi.dynamicallyCreateComponent(type, annotation, inj);
46+
var context = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj);
4647
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context);
4748

4849
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
@@ -78,9 +79,9 @@ export class DynamicComponentLoader {
7879
});
7980
}
8081

81-
_componentAppInjector(location, injector, services) {
82+
_componentAppInjector(location, injector:Injector, resolvedBindings:List<ResolvedBinding>) {
8283
var inj = isPresent(injector) ? injector : location.injector;
83-
return isPresent(services) ? inj.resolveAndCreateChild(services) : inj;
84+
return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj;
8485
}
8586

8687
_instantiateAndHydrateView(protoView, injector, hostElementInjector, context) {

modules/angular2/src/core/compiler/element_injector.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/
77
import * as viewModule from 'angular2/src/core/compiler/view';
88
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
99
import {NgElement} from 'angular2/src/core/compiler/ng_element';
10-
import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
10+
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
1111
import {BindingPropagationConfig} from 'angular2/change_detection';
1212
import {QueryList} from './query_list';
1313

@@ -283,13 +283,17 @@ export class DirectiveBinding extends ResolvedBinding {
283283
callOnChange:boolean;
284284
callOnAllChangesDone:boolean;
285285
annotation:Directive;
286+
resolvedInjectables:List<ResolvedBinding>;
286287

287288
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
288289
super(key, factory, dependencies, providedAsPromise);
289290
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
290291
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
291292
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
292293
this.annotation = annotation;
294+
if (annotation instanceof Component && isPresent(annotation.injectables)) {
295+
this.resolvedInjectables = Injector.resolve(annotation.injectables);
296+
}
293297
}
294298

295299
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {

modules/angular2/src/core/compiler/view.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ export class AppView {
151151

152152
// shadowDomAppInjector
153153
if (isPresent(componentDirective)) {
154-
var injectables = componentDirective.annotation.injectables;
154+
var injectables = componentDirective.resolvedInjectables;
155155
if (isPresent(injectables))
156-
shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables);
156+
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
157157
else {
158158
shadowDomAppInjector = appInjector;
159159
}

modules/angular2/src/di/injector.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,12 @@ class _AsyncInjectorStrategy {
267267
}
268268
}
269269

270-
function _createListOfBindings(flattenBindings):List {
270+
function _createListOfBindings(flattenedBindings):List {
271271
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
272-
MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v);
272+
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
273273
return bindings;
274274
}
275275

276-
277-
278276
function _flattenBindings(bindings:List, res:Map) {
279277
ListWrapper.forEach(bindings, function (b) {
280278
if (b instanceof ResolvedBinding) {

modules/angular2/test/core/compiler/directive_metadata_reader_spec.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
import {isPresent} from 'angular2/src/facade/lang';
2+
import {ListWrapper} from 'angular2/src/facade/collection';
13
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
24
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
35
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
46
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
7+
import {Injectable, Injector} from 'angular2/di';
58

9+
@Injectable()
10+
class SomeInjectable {}
611

712
@Decorator({selector: 'someDecorator'})
813
class SomeDecorator {}
914

10-
@Component({selector: 'someComponent'})
15+
@Component({selector: 'someComponent', injectables: [SomeInjectable]})
1116
class SomeComponent {}
1217

1318
@Viewport({selector: 'someViewport'})
@@ -27,19 +32,30 @@ export function main() {
2732
it('should read out the Decorator annotation', () => {
2833
var directiveMetadata = reader.read(SomeDecorator);
2934
expect(directiveMetadata).toEqual(
30-
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'})));
35+
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'}), null));
3136
});
3237

3338
it('should read out the Viewport annotation', () => {
3439
var directiveMetadata = reader.read(SomeViewport);
3540
expect(directiveMetadata).toEqual(
36-
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'})));
41+
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'}), null));
3742
});
3843

3944
it('should read out the Component annotation', () => {
40-
var directiveMetadata = reader.read(SomeComponent);
41-
expect(directiveMetadata).toEqual(
42-
new DirectiveMetadata(SomeComponent, new Component({selector: 'someComponent'})));
45+
var m = reader.read(SomeComponent);
46+
// For some reason `toEqual` fails to compare ResolvedBinding objects.
47+
// Have to decompose and compare.
48+
expect(m.type).toEqual(SomeComponent);
49+
expect(m.annotation)
50+
.toEqual(new Component({selector: 'someComponent', injectables: [SomeInjectable]}));
51+
var resolvedList = ListWrapper.reduce(m.resolvedInjectables, function(prev, elem) {
52+
if (isPresent(elem)) {
53+
ListWrapper.push(prev, elem);
54+
}
55+
return prev;
56+
}, []);
57+
expect(resolvedList.length).toBe(1);
58+
expect(resolvedList[0].key.token).toBe(SomeInjectable);
4359
});
4460

4561
it('should throw if not matching annotation is found', () => {

modules/angular2/test/di/injector_spec.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import {isBlank} from 'angular2/src/facade/lang';
12
import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib';
2-
import {Injector, Inject, InjectLazy, Optional, bind} from 'angular2/di';
3+
import {Injector, Inject, InjectLazy, Optional, bind, ResolvedBinding} from 'angular2/di';
34

45
class Engine {
56
}
@@ -364,5 +365,15 @@ export function main() {
364365
expect(e1).toBe(e2);
365366
});
366367
});
368+
369+
describe('resolve', function() {
370+
it('should resolve and flatten', function() {
371+
var bindings = Injector.resolve([Engine, [BrokenEngine]]);
372+
bindings.forEach(function(b) {
373+
if (isBlank(b)) return; // the result is a sparse array
374+
expect(b instanceof ResolvedBinding).toBe(true);
375+
});
376+
});
377+
});
367378
});
368379
}

0 commit comments

Comments
 (0)