Skip to content

Commit 6c8398d

Browse files
committed
fix(di): refactor bindings to support Dart annotations
1 parent ff6e775 commit 6c8398d

File tree

7 files changed

+207
-78
lines changed

7 files changed

+207
-78
lines changed

gulpfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,6 @@ gulp.task('build/packages.dart', function(done) {
756756
'build/transpile.dart', // Creates the folder structure needed by subsequent tasks.
757757
['build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
758758
'build/format.dart',
759-
'build/pubspec.dart',
760759
done
761760
);
762761
});
@@ -765,6 +764,7 @@ gulp.task('build/packages.dart', function(done) {
765764
gulp.task('build.dart', function(done) {
766765
runSequence(
767766
'build/packages.dart',
767+
'build/pubspec.dart',
768768
'build/analyze.dart',
769769
'build/pubbuild.dart',
770770
done

karma-dart.conf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = function(config) {
1313
// Unit test files needs to be included.
1414
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
1515
{pattern: 'modules/*/test/**/*_spec.js', included: true},
16+
{pattern: 'modules/*/test/**/*_spec.dart', included: true},
1617
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
1718

1819
// These files are not included, they are imported by the unit tests above.

modules/angular2/di.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
export {Inject, InjectPromise, InjectLazy, Injectable, Optional, DependencyAnnotation} from './src/di/annotations';
88
export {Injector} from './src/di/injector';
9-
export {Binding, Dependency, bind} from './src/di/binding';
9+
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
1010
export {Key, KeyRegistry} from './src/di/key';
1111
export {KeyMetadataError, NoProviderError, ProviderError, AsyncBindingError, CyclicDependencyError,
1212
InstantiationError, InvalidBindingError, NoAnnotationError} from './src/di/exceptions';

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {isPresent, isBlank, Type, int, BaseException} from 'angular2/src/facade/lang';
22
import {Math} from 'angular2/src/facade/math';
33
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
4-
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
4+
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
55
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
66
import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
77
import * as viewModule from 'angular2/src/core/compiler/view';
@@ -278,7 +278,7 @@ export class DirectiveDependency extends Dependency {
278278
}
279279
}
280280

281-
export class DirectiveBinding extends Binding {
281+
export class DirectiveBinding extends ResolvedBinding {
282282
callOnDestroy:boolean;
283283
callOnChange:boolean;
284284
callOnAllChangesDone:boolean;
@@ -292,13 +292,14 @@ export class DirectiveBinding extends Binding {
292292
this.annotation = annotation;
293293
}
294294

295-
static createFromBinding(b:Binding, annotation:Directive):Binding {
296-
var deps = ListWrapper.map(b.dependencies, DirectiveDependency.createFrom);
297-
return new DirectiveBinding(b.key, b.factory, deps, b.providedAsPromise, annotation);
295+
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
296+
var rb = b.resolve();
297+
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
298+
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, annotation);
298299
}
299300

300-
static createFromType(type:Type, annotation:Directive):Binding {
301-
var binding = bind(type).toClass(type);
301+
static createFromType(type:Type, annotation:Directive):DirectiveBinding {
302+
var binding = new Binding(type, {toClass: type});
302303
return DirectiveBinding.createFromBinding(binding, annotation);
303304
}
304305

@@ -343,16 +344,16 @@ ElementInjector:
343344
*/
344345

345346
export class ProtoElementInjector {
346-
_binding0:Binding;
347-
_binding1:Binding;
348-
_binding2:Binding;
349-
_binding3:Binding;
350-
_binding4:Binding;
351-
_binding5:Binding;
352-
_binding6:Binding;
353-
_binding7:Binding;
354-
_binding8:Binding;
355-
_binding9:Binding;
347+
_binding0:ResolvedBinding;
348+
_binding1:ResolvedBinding;
349+
_binding2:ResolvedBinding;
350+
_binding3:ResolvedBinding;
351+
_binding4:ResolvedBinding;
352+
_binding5:ResolvedBinding;
353+
_binding6:ResolvedBinding;
354+
_binding7:ResolvedBinding;
355+
_binding8:ResolvedBinding;
356+
_binding9:ResolvedBinding;
356357
_binding0IsComponent:boolean;
357358
_keyId0:int;
358359
_keyId1:int;
@@ -642,7 +643,7 @@ export class ElementInjector extends TreeNode {
642643
this._dynamicallyCreatedComponentBinding.key.id;
643644
}
644645

645-
_new(binding:Binding) {
646+
_new(binding:ResolvedBinding) {
646647
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
647648
throw new CyclicDependencyError(binding.key);
648649
}

modules/angular2/src/di/binding.js

Lines changed: 115 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
1+
import {Type, isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
22
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
33
import {reflector} from 'angular2/src/reflection/reflection';
44
import {Key} from './key';
55
import {Inject, InjectLazy, InjectPromise, Optional, DependencyAnnotation} from './annotations';
6-
import {NoAnnotationError} from './exceptions';
6+
import {NoAnnotationError, InvalidBindingError} from './exceptions';
77

88
export class Dependency {
99
key:Key;
1010
asPromise:boolean;
1111
lazy:boolean;
1212
optional:boolean;
1313
properties:List;
14+
1415
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List) {
1516
this.key = key;
1617
this.asPromise = asPromise;
@@ -24,80 +25,155 @@ export class Dependency {
2425
}
2526
}
2627

28+
var _EMPTY_LIST = []; // TODO: make const when supported
29+
30+
/**
31+
* Declaration of a dependency binding.
32+
*/
2733
export class Binding {
34+
token;
35+
toClass:Type;
36+
toValue;
37+
toAlias;
38+
toFactory:Function;
39+
toAsyncFactory:Function;
40+
dependencies:List;
41+
42+
@CONST()
43+
constructor(
44+
token,
45+
{
46+
toClass,
47+
toValue,
48+
toAlias,
49+
toFactory,
50+
toAsyncFactory,
51+
deps
52+
}) {
53+
this.token = token;
54+
this.toClass = toClass;
55+
this.toValue = toValue;
56+
this.toAlias = toAlias;
57+
this.toFactory = toFactory;
58+
this.toAsyncFactory = toAsyncFactory;
59+
this.dependencies = deps;
60+
}
61+
62+
resolve(): ResolvedBinding {
63+
var factoryFn:Function;
64+
var resolvedDeps;
65+
var isAsync = false;
66+
if (isPresent(this.toClass)) {
67+
factoryFn = reflector.factory(this.toClass);
68+
resolvedDeps = _dependenciesFor(this.toClass);
69+
} else if (isPresent(this.toAlias)) {
70+
factoryFn = (aliasInstance) => aliasInstance;
71+
resolvedDeps = [Dependency.fromKey(Key.get(this.toAlias))];
72+
} else if (isPresent(this.toFactory)) {
73+
factoryFn = this.toFactory;
74+
resolvedDeps = _constructDependencies(this.toFactory, this.dependencies);
75+
} else if (isPresent(this.toAsyncFactory)) {
76+
factoryFn = this.toAsyncFactory;
77+
resolvedDeps = _constructDependencies(this.toAsyncFactory, this.dependencies);
78+
isAsync = true;
79+
} else {
80+
factoryFn = () => this.toValue;
81+
resolvedDeps = _EMPTY_LIST;
82+
}
83+
84+
return new ResolvedBinding(
85+
Key.get(this.token),
86+
factoryFn,
87+
resolvedDeps,
88+
isAsync
89+
);
90+
}
91+
92+
static resolveAll(bindings:List): List {
93+
var resolvedList = ListWrapper.createFixedSize(bindings.length);
94+
for (var i = 0; i < bindings.length; i++) {
95+
var unresolved = bindings[i];
96+
var resolved;
97+
if (unresolved instanceof Type) {
98+
resolved = bind(unresolved).toClass(unresolved).resolve();
99+
} else if (unresolved instanceof Binding) {
100+
resolved = unresolved.resolve();
101+
} else if (unresolved instanceof List) {
102+
resolved = Binding.resolveAll(unresolved);
103+
} else if (unresolved instanceof BindingBuilder) {
104+
throw new InvalidBindingError(unresolved.token);
105+
} else {
106+
throw new InvalidBindingError(unresolved);
107+
}
108+
resolvedList[i] = resolved;
109+
}
110+
return resolvedList;
111+
}
112+
}
113+
114+
/// Dependency binding with resolved keys and dependencies.
115+
export class ResolvedBinding {
28116
key:Key;
29117
factory:Function;
30-
dependencies:List;
118+
dependencies:List<Dependency>;
31119
providedAsPromise:boolean;
32120

33-
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean) {
121+
constructor(key:Key, factory:Function, dependencies:List<Dependency>, providedAsPromise:boolean) {
34122
this.key = key;
35123
this.factory = factory;
36124
this.dependencies = dependencies;
37125
this.providedAsPromise = providedAsPromise;
38126
}
39127
}
40128

129+
/**
130+
* Provides fluent API for imperative construction of [Binding] objects.
131+
*/
41132
export function bind(token):BindingBuilder {
42133
return new BindingBuilder(token);
43134
}
44135

136+
/**
137+
* Helper class for [bind] function.
138+
*/
45139
export class BindingBuilder {
46140
token;
141+
47142
constructor(token) {
48143
this.token = token;
49144
}
50145

51146
toClass(type:Type):Binding {
52-
return new Binding(
53-
Key.get(this.token),
54-
reflector.factory(type),
55-
_dependenciesFor(type),
56-
false
57-
);
147+
return new Binding(this.token, {toClass: type});
58148
}
59149

60150
toValue(value):Binding {
61-
return new Binding(
62-
Key.get(this.token),
63-
() => value,
64-
[],
65-
false
66-
);
151+
return new Binding(this.token, {toValue: value});
67152
}
68153

69154
toAlias(aliasToken):Binding {
70-
return new Binding(
71-
Key.get(this.token),
72-
(aliasInstance) => aliasInstance,
73-
[Dependency.fromKey(Key.get(aliasToken))],
74-
false
75-
);
155+
return new Binding(this.token, {toAlias: aliasToken});
76156
}
77157

78158
toFactory(factoryFunction:Function, dependencies:List = null):Binding {
79-
return new Binding(
80-
Key.get(this.token),
81-
factoryFunction,
82-
this._constructDependencies(factoryFunction, dependencies),
83-
false
84-
);
159+
return new Binding(this.token, {
160+
toFactory: factoryFunction,
161+
deps: dependencies
162+
});
85163
}
86164

87165
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding {
88-
return new Binding(
89-
Key.get(this.token),
90-
factoryFunction,
91-
this._constructDependencies(factoryFunction, dependencies),
92-
true
93-
);
166+
return new Binding(this.token, {
167+
toAsyncFactory: factoryFunction,
168+
deps: dependencies
169+
});
94170
}
171+
}
95172

96-
_constructDependencies(factoryFunction:Function, dependencies:List) {
97-
return isBlank(dependencies) ?
98-
_dependenciesFor(factoryFunction) :
99-
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
100-
}
173+
function _constructDependencies(factoryFunction:Function, dependencies:List) {
174+
return isBlank(dependencies) ?
175+
_dependenciesFor(factoryFunction) :
176+
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
101177
}
102178

103179
function _dependenciesFor(typeOrFunc):List {

modules/angular2/src/di/injector.js

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Map, List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
2-
import {Binding, BindingBuilder, bind} from './binding';
3-
import {ProviderError, NoProviderError, InvalidBindingError,
2+
import {ResolvedBinding, Binding, BindingBuilder, bind} from './binding';
3+
import {ProviderError, NoProviderError,
44
AsyncBindingError, CyclicDependencyError, InstantiationError} from './exceptions';
55
import {FunctionWrapper, Type, isPresent, isBlank} from 'angular2/src/facade/lang';
66
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
@@ -28,7 +28,7 @@ export class Injector {
2828
_asyncStrategy: _AsyncInjectorStrategy;
2929
_syncStrategy:_SyncInjectorStrategy;
3030
constructor(bindings:List, {parent=null, defaultBindings=false}={}) {
31-
var flatten = _flattenBindings(bindings, MapWrapper.create());
31+
var flatten = _flattenBindings(Binding.resolveAll(bindings), MapWrapper.create());
3232
this._bindings = this._createListOfBindings(flatten);
3333
this._instances = this._createInstances();
3434
this._parent = parent;
@@ -89,7 +89,7 @@ export class Injector {
8989
}
9090
}
9191

92-
_resolveDependencies(key:Key, binding:Binding, forceAsync:boolean):List {
92+
_resolveDependencies(key:Key, binding:ResolvedBinding, forceAsync:boolean):List {
9393
try {
9494
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional);
9595
return ListWrapper.map(binding.dependencies, getDependency);
@@ -115,7 +115,7 @@ export class Injector {
115115
ListWrapper.get(this._bindings, key.id);
116116

117117
if (isBlank(binding) && this._defaultBindings) {
118-
return bind(key.token).toClass(key.token);
118+
return bind(key.token).toClass(key.token).resolve();
119119
} else {
120120
return binding;
121121
}
@@ -166,7 +166,7 @@ class _SyncInjectorStrategy {
166166
return this._createInstance(key, binding, deps);
167167
}
168168

169-
_createInstance(key:Key, binding:Binding, deps:List) {
169+
_createInstance(key:Key, binding:ResolvedBinding, deps:List) {
170170
try {
171171
var instance = FunctionWrapper.apply(binding.factory, deps);
172172
this.injector._setInstance(key, instance);
@@ -227,7 +227,7 @@ class _AsyncInjectorStrategy {
227227
return PromiseWrapper.reject(e);
228228
}
229229

230-
_findOrCreate(key:Key, binding:Binding, deps:List) {
230+
_findOrCreate(key:Key, binding:ResolvedBinding, deps:List) {
231231
try {
232232
var instance = this.injector._getInstance(key);
233233
if (!_isWaiting(instance)) return instance;
@@ -246,21 +246,10 @@ class _AsyncInjectorStrategy {
246246

247247
function _flattenBindings(bindings:List, res:Map) {
248248
ListWrapper.forEach(bindings, function (b) {
249-
if (b instanceof Binding) {
249+
if (b instanceof ResolvedBinding) {
250250
MapWrapper.set(res, b.key.id, b);
251-
252-
} else if (b instanceof Type) {
253-
var s = bind(b).toClass(b);
254-
MapWrapper.set(res, s.key.id, s);
255-
256251
} else if (b instanceof List) {
257252
_flattenBindings(b, res);
258-
259-
} else if (b instanceof BindingBuilder) {
260-
throw new InvalidBindingError(b.token);
261-
262-
} else {
263-
throw new InvalidBindingError(b);
264253
}
265254
});
266255
return res;

0 commit comments

Comments
 (0)