feat(ElementInjector): implement ElementInjector
This commit is contained in:
parent
ea0df352be
commit
e3548b497f
56
gulpfile.js
56
gulpfile.js
|
@ -1,7 +1,9 @@
|
|||
var shell = require('gulp-shell');
|
||||
var gulp = require('gulp');
|
||||
var rename = require('gulp-rename');
|
||||
var watch = require('gulp-watch');
|
||||
var mergeStreams = require('event-stream').merge;
|
||||
var es = require('event-stream');
|
||||
var connect = require('gulp-connect');
|
||||
var clean = require('gulp-rimraf');
|
||||
var runSequence = require('run-sequence');
|
||||
|
@ -225,22 +227,58 @@ gulp.task('analyze/dartanalyzer', function(done) {
|
|||
|
||||
|
||||
// ------------------
|
||||
// BENCHMARKS
|
||||
// BENCHMARKS JS
|
||||
|
||||
var benchmarksBuildPath = 'build/benchpress';
|
||||
var benchmarksCompiledJsPath = 'build/js/benchmarks/lib';
|
||||
|
||||
gulp.task('benchmarks/build.benchpress', function () {
|
||||
gulp.task('benchmarks/build.benchpress.js', function () {
|
||||
benchpress.build({
|
||||
benchmarksPath: benchmarksCompiledJsPath,
|
||||
buildPath: benchmarksBuildPath
|
||||
benchmarksPath: 'build/js/benchmarks/lib',
|
||||
buildPath: 'build/benchpress/js'
|
||||
})
|
||||
});
|
||||
|
||||
gulp.task('benchmarks/build', function() {
|
||||
gulp.task('benchmarks/build.js', function() {
|
||||
runSequence(
|
||||
['jsRuntime/build', 'modules/build.prod.js'],
|
||||
'benchmarks/build.benchpress'
|
||||
'benchmarks/build.benchpress.js'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// ------------------
|
||||
// BENCHMARKS DART
|
||||
|
||||
gulp.task('benchmarks/build.dart2js.dart', function () {
|
||||
return gulp.src([
|
||||
"build/dart/benchmarks/lib/**/benchmark.dart"
|
||||
]).pipe(shell(['dart2js --package-root="build/dart/benchmarks/packages" -o "<%= file.path %>.js" <%= file.path %>']));
|
||||
});
|
||||
|
||||
gulp.task('benchmarks/create-bpconf.dart', function () {
|
||||
var bpConfContent = "module.exports = function(c) {c.set({scripts: [{src: 'benchmark.dart.js'}]});}";
|
||||
var createBpConfJs = es.map(function(file, cb) {
|
||||
var dir = path.dirname(file.path);
|
||||
fs.writeFileSync(path.join(dir, "bp.conf.js"), bpConfContent);
|
||||
cb();
|
||||
});
|
||||
|
||||
return gulp.src([
|
||||
"build/dart/benchmarks/lib/**/benchmark.dart"
|
||||
]).pipe(createBpConfJs);
|
||||
});
|
||||
|
||||
gulp.task('benchmarks/build.benchpress.dart', function () {
|
||||
benchpress.build({
|
||||
benchmarksPath: 'build/dart/benchmarks/lib',
|
||||
buildPath: 'build/benchpress/dart'
|
||||
})
|
||||
});
|
||||
|
||||
gulp.task('benchmarks/build.dart', function() {
|
||||
runSequence(
|
||||
'modules/build.dart',
|
||||
'benchmarks/build.dart2js.dart',
|
||||
'benchmarks/create-bpconf.dart',
|
||||
'benchmarks/build.benchpress.dart'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
name: bnehcmarks
|
||||
environment:
|
||||
sdk: '>=1.4.0'
|
||||
dependencies:
|
||||
facade:
|
||||
path: ../facade
|
||||
di:
|
||||
path: ../di
|
||||
core:
|
||||
path: ../core
|
|
@ -0,0 +1,11 @@
|
|||
library injector_get_benchmark;
|
||||
|
||||
import './injector_instantiate_benchmark.dart' as b;
|
||||
import 'dart:js' as js;
|
||||
|
||||
main () {
|
||||
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
|
||||
"name": "Injector.instantiate",
|
||||
"fn": new js.JsFunction.withThis((_) => b.run())
|
||||
}));
|
||||
}
|
|
@ -10,8 +10,6 @@ export function run () {
|
|||
var child = injector.createChild([E]);
|
||||
child.get(E);
|
||||
}
|
||||
|
||||
console.log(count)
|
||||
}
|
||||
|
||||
class A {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
library element_injector_benchmark;
|
||||
|
||||
import './instantiate_benchmark.dart' as ib;
|
||||
import 'dart:js' as js;
|
||||
|
||||
main () {
|
||||
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
|
||||
"name": "ElementInjector.instantiate + instantiateDirectives",
|
||||
"fn": new js.JsFunction.withThis((_) => ib.run())
|
||||
}));
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
System.import('benchmarks/element_injector/instantiate_benchmark').then(function (bm) {
|
||||
window.benchmarkSteps.push({name: 'ElementInjector.instantiate + instantiateDirectives', fn: bm.run});
|
||||
}, console.log.bind(console));
|
||||
|
||||
System.import('benchmarks/element_injector/instantiate_directive_benchmark').then(function (bm) {
|
||||
window.benchmarkSteps.push({name: 'ElementInjector.instantiateDirectives', fn: bm.run});
|
||||
}, console.log.bind(console));
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [
|
||||
{src: '/js/traceur-runtime.js'},
|
||||
{src: '/js/es6-module-loader-sans-promises.src.js'},
|
||||
{src: '/js/extension-register.js'},
|
||||
{src: 'register_system.js'},
|
||||
{src: 'benchmark.js'}
|
||||
]
|
||||
});
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import {Injector} from 'di/di';
|
||||
import {ProtoElementInjector} from 'core/compiler/element_injector';
|
||||
|
||||
var count = 0;
|
||||
|
||||
export function run () {
|
||||
var appInjector = new Injector([]);
|
||||
|
||||
var bindings = [A, B, C];
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
for (var i = 0; i < 20000; ++i) {
|
||||
var ei = proto.instantiate();
|
||||
ei.instantiateDirectives(appInjector);
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
constructor() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
constructor(a:A, b:B) {
|
||||
count++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import {Injector} from 'di/di';
|
||||
import {ProtoElementInjector} from 'core/compiler/element_injector';
|
||||
|
||||
var count = 0;
|
||||
|
||||
export function run () {
|
||||
var appInjector = new Injector([]);
|
||||
|
||||
var bindings = [A, B, C];
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
var ei = proto.instantiate();
|
||||
|
||||
for (var i = 0; i < 20000; ++i) {
|
||||
ei.clearDirectives();
|
||||
ei.instantiateDirectives(appInjector);
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
constructor() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
constructor() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
constructor(a:A, b:B) {
|
||||
count++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
System.paths = {
|
||||
'core/*': '/js/core/lib/*.js',
|
||||
'change_detection/*': '/js/change_detection/lib/*.js',
|
||||
'facade/*': '/js/facade/lib/*.js',
|
||||
'di/*': '/js/di/lib/*.js',
|
||||
'rtts_assert/*': '/js/rtts_assert/lib/*.js',
|
||||
'test_lib/*': '/js/test_lib/lib/*.js',
|
||||
'benchmarks/*': '/js/benchmarks/lib/*.js'
|
||||
};
|
||||
register(System);
|
|
@ -0,0 +1,22 @@
|
|||
import {CONST} from 'facade/lang';
|
||||
import {DependencyAnnotation} from 'di/di';
|
||||
|
||||
/**
|
||||
* The directive can only be injected from the current element
|
||||
* or from its parent.
|
||||
*/
|
||||
export class Parent extends DependencyAnnotation {
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The directive can only be injected from the current element
|
||||
* or from its ancestor.
|
||||
*/
|
||||
export class Ancestor extends DependencyAnnotation {
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
}
|
|
@ -1,4 +1,72 @@
|
|||
import {FIELD} from 'facade/lang';
|
||||
import {FIELD, isPresent, isBlank, Type, int} from 'facade/lang';
|
||||
import {Math} from 'facade/math';
|
||||
import {List, ListWrapper} from 'facade/collection';
|
||||
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError} from 'di/di';
|
||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||
|
||||
var MAX_DEPTH = Math.pow(2, 30) - 1;
|
||||
|
||||
class TreeNode {
|
||||
@FIELD('_parent:TreeNode')
|
||||
@FIELD('_head:TreeNode')
|
||||
@FIELD('_tail:TreeNode')
|
||||
@FIELD('_next:TreeNode')
|
||||
@FIELD('_prev:TreeNode')
|
||||
constructor(parent:TreeNode) {
|
||||
this._parent = parent;
|
||||
this._head = null;
|
||||
this._tail = null;
|
||||
this._next = null;
|
||||
this._prev = null;
|
||||
if (isPresent(parent)) parent._addChild(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child to the parent node. The child MUST NOT be a part of a tree.
|
||||
*/
|
||||
_addChild(child:TreeNode) {
|
||||
if (isPresent(this._tail)) {
|
||||
this._tail._next = child;
|
||||
child._prev = this._tail;
|
||||
this._tail = child;
|
||||
} else {
|
||||
this._tail = this._head = child;
|
||||
}
|
||||
}
|
||||
|
||||
get parent() {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
get children() {
|
||||
var res = [];
|
||||
var child = this._head;
|
||||
while (child != null) {
|
||||
ListWrapper.push(res, child);
|
||||
child = child._next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
class DirectiveDependency extends Dependency {
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, properties:List, depth:int) {
|
||||
super(key, asPromise, lazy, properties);
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
static createFrom(d:Dependency):Dependency {
|
||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy,
|
||||
d.properties, DirectiveDependency._depth(d.properties));
|
||||
}
|
||||
|
||||
static _depth(properties):int {
|
||||
if (properties.length == 0) return 0;
|
||||
if (ListWrapper.any(properties, p => p instanceof Parent)) return 1;
|
||||
if (ListWrapper.any(properties, p => p instanceof Ancestor)) return MAX_DEPTH;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
|
@ -18,35 +86,34 @@ ElementInjector (ElementModule):
|
|||
- 1:1 to DOM structure.
|
||||
|
||||
PERF BENCHMARK: http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
|
||||
|
||||
*/
|
||||
|
||||
export class ProtoElementInjector {
|
||||
export class ProtoElementInjector extends TreeNode {
|
||||
/**
|
||||
parent:ProtoDirectiveInjector;
|
||||
next:ProtoDirectiveInjector;
|
||||
prev:ProtoDirectiveInjector;
|
||||
head:ProtoDirectiveInjector;
|
||||
tail:ProtoDirectiveInjector;
|
||||
DirectiveInjector cloneingInstance;
|
||||
DirectiveInjector cloningInstance;
|
||||
KeyMap keyMap;
|
||||
/// Because DI tree is sparse, this shows how far away is the Parent DI
|
||||
parentDistance:int = 1; /// 1 for non-sparse/normal depth.
|
||||
|
||||
cKey:int; cFactory:Function; cParams:List<int>;
|
||||
keyId0:int; factory0:Function; params0:List<int>;
|
||||
keyId1:int; factory1:Function; params1:List<int>;
|
||||
keyId2:int; factory2:Function; params2:List<int>;
|
||||
keyId3:int; factory3:Function; params3:List<int>;
|
||||
keyId4:int; factory4:Function; params4:List<int>;
|
||||
keyId5:int; factory5:Function; params5:List<int>;
|
||||
keyId6:int; factory6:Function; params6:List<int>;
|
||||
keyId7:int; factory7:Function; params7:List<int>;
|
||||
keyId8:int; factory8:Function; params8:List<int>;
|
||||
keyId9:int; factory9:Function; params9:List<int>;
|
||||
_keyId0:int; factory0:Function; params0:List<int>;
|
||||
_keyId1:int; factory1:Function; params1:List<int>;
|
||||
_keyId2:int; factory2:Function; params2:List<int>;
|
||||
_keyId3:int; factory3:Function; params3:List<int>;
|
||||
_keyId4:int; factory4:Function; params4:List<int>;
|
||||
_keyId5:int; factory5:Function; params5:List<int>;
|
||||
_keyId6:int; factory6:Function; params6:List<int>;
|
||||
_keyId7:int; factory7:Function; params7:List<int>;
|
||||
_keyId8:int; factory8:Function; params8:List<int>;
|
||||
_keyId9:int; factory9:Function; params9:List<int>;
|
||||
|
||||
queryKeyId0:int;
|
||||
queryKeyId1:int;
|
||||
query_keyId0:int;
|
||||
query_keyId1:int;
|
||||
|
||||
textNodes:List<int>;
|
||||
hasProperties:boolean;
|
||||
|
@ -54,17 +121,91 @@ export class ProtoElementInjector {
|
|||
|
||||
elementInjector:ElementInjector;
|
||||
*/
|
||||
constructor(parent:ProtoElementInjector) {
|
||||
@FIELD('_elementInjector:ElementInjector')
|
||||
@FIELD('_binding0:Binding')
|
||||
@FIELD('_binding1:Binding')
|
||||
@FIELD('_binding2:Binding')
|
||||
@FIELD('_binding3:Binding')
|
||||
@FIELD('_binding4:Binding')
|
||||
@FIELD('_binding5:Binding')
|
||||
@FIELD('_binding6:Binding')
|
||||
@FIELD('_binding7:Binding')
|
||||
@FIELD('_binding8:Binding')
|
||||
@FIELD('_binding9:Binding')
|
||||
@FIELD('_key0:int')
|
||||
@FIELD('_key1:int')
|
||||
@FIELD('_key2:int')
|
||||
@FIELD('_key3:int')
|
||||
@FIELD('_key4:int')
|
||||
@FIELD('_key5:int')
|
||||
@FIELD('_key6:int')
|
||||
@FIELD('_key7:int')
|
||||
@FIELD('_key8:int')
|
||||
@FIELD('_key9:int')
|
||||
@FIELD('textNodes:List<int>')
|
||||
constructor(parent:ProtoElementInjector, directiveTypes:List, textNodes:List) {
|
||||
super(parent);
|
||||
|
||||
this._elementInjector = null;
|
||||
|
||||
this._binding0 = null; this._keyId0 = null;
|
||||
this._binding1 = null; this._keyId1 = null;
|
||||
this._binding2 = null; this._keyId2 = null;
|
||||
this._binding3 = null; this._keyId3 = null;
|
||||
this._binding4 = null; this._keyId4 = null;
|
||||
this._binding5 = null; this._keyId5 = null;
|
||||
this._binding6 = null; this._keyId6 = null;
|
||||
this._binding7 = null; this._keyId7 = null;
|
||||
this._binding8 = null; this._keyId8 = null;
|
||||
this._binding9 = null; this._keyId9 = null;
|
||||
|
||||
var length = directiveTypes.length;
|
||||
|
||||
if (length > 0) {this._binding0 = this._createBinding(directiveTypes[0]); this._keyId0 = this._binding0.key.id;}
|
||||
if (length > 1) {this._binding1 = this._createBinding(directiveTypes[1]); this._keyId1 = this._binding1.key.id;}
|
||||
if (length > 2) {this._binding2 = this._createBinding(directiveTypes[2]); this._keyId2 = this._binding2.key.id;}
|
||||
if (length > 3) {this._binding3 = this._createBinding(directiveTypes[3]); this._keyId3 = this._binding3.key.id;}
|
||||
if (length > 4) {this._binding4 = this._createBinding(directiveTypes[4]); this._keyId4 = this._binding4.key.id;}
|
||||
if (length > 5) {this._binding5 = this._createBinding(directiveTypes[5]); this._keyId5 = this._binding5.key.id;}
|
||||
if (length > 6) {this._binding6 = this._createBinding(directiveTypes[6]); this._keyId6 = this._binding6.key.id;}
|
||||
if (length > 7) {this._binding7 = this._createBinding(directiveTypes[7]); this._keyId7 = this._binding7.key.id;}
|
||||
if (length > 8) {this._binding8 = this._createBinding(directiveTypes[8]); this._keyId8 = this._binding8.key.id;}
|
||||
if (length > 9) {this._binding9 = this._createBinding(directiveTypes[9]); this._keyId9 = this._binding9.key.id;}
|
||||
if (length > 10) {
|
||||
throw 'Maximum number of directives per element has been reached.';
|
||||
}
|
||||
|
||||
// dummy fields to make analyzer happy
|
||||
this.textNodes = [];
|
||||
this.hasProperties = false;
|
||||
this.textNodes = null;
|
||||
}
|
||||
|
||||
instantiate():ElementInjector {
|
||||
return new ElementInjector(this);
|
||||
var p = this._parent;
|
||||
var parentElementInjector = p == null ? null : p._elementInjector;
|
||||
this._elementInjector = new ElementInjector({
|
||||
proto: this,
|
||||
parent: parentElementInjector
|
||||
});
|
||||
return this._elementInjector;
|
||||
}
|
||||
|
||||
_createBinding(directiveType:Type) {
|
||||
var b = bind(directiveType).toClass(directiveType);
|
||||
var deps = ListWrapper.map(b.dependencies, DirectiveDependency.createFrom);
|
||||
return new Binding(b.key, b.factory, deps, b.providedAsPromise);
|
||||
}
|
||||
|
||||
clearElementInjector() {
|
||||
this._elementInjector = null;
|
||||
}
|
||||
|
||||
get hasBindings():boolean {
|
||||
return isPresent(this._binding0);
|
||||
}
|
||||
}
|
||||
|
||||
export class ElementInjector {
|
||||
export class ElementInjector extends TreeNode {
|
||||
/*
|
||||
_protoInjector:ProtoElementInjector;
|
||||
injector:Injector;
|
||||
|
@ -107,10 +248,141 @@ export class ElementInjector {
|
|||
_query1:Query;
|
||||
|
||||
*/
|
||||
@FIELD('final protoInjector:ProtoElementInjector')
|
||||
constructor(protoInjector:ProtoElementInjector) {
|
||||
this.protoInjector = protoInjector;
|
||||
|
||||
@FIELD('_proto:ProtoElementInjector')
|
||||
@FIELD('_appInjector:Injector')
|
||||
@FIELD('_obj0:Object')
|
||||
@FIELD('_obj1:Object')
|
||||
@FIELD('_obj2:Object')
|
||||
@FIELD('_obj3:Object')
|
||||
@FIELD('_obj4:Object')
|
||||
@FIELD('_obj5:Object')
|
||||
@FIELD('_obj6:Object')
|
||||
@FIELD('_obj7:Object')
|
||||
@FIELD('_obj8:Object')
|
||||
@FIELD('_obj9:Object')
|
||||
constructor({proto, parent}) {
|
||||
super(parent);
|
||||
this._proto = proto;
|
||||
|
||||
//we cannot call clearDirectives because fields won't be detected
|
||||
this._appInjector = null;
|
||||
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;
|
||||
}
|
||||
|
||||
clearDirectives() {
|
||||
this._appInjector = null;
|
||||
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;
|
||||
}
|
||||
|
||||
instantiateDirectives(appInjector:Injector) {
|
||||
this._appInjector = appInjector;
|
||||
|
||||
var p = this._proto;
|
||||
if (isPresent(p._keyId0)) this._obj0 = this._new(p._binding0);
|
||||
if (isPresent(p._keyId1)) this._obj1 = this._new(p._binding1);
|
||||
if (isPresent(p._keyId2)) this._obj2 = this._new(p._binding2);
|
||||
if (isPresent(p._keyId3)) this._obj3 = this._new(p._binding3);
|
||||
if (isPresent(p._keyId4)) this._obj4 = this._new(p._binding4);
|
||||
if (isPresent(p._keyId5)) this._obj5 = this._new(p._binding5);
|
||||
if (isPresent(p._keyId6)) this._obj6 = this._new(p._binding6);
|
||||
if (isPresent(p._keyId7)) this._obj7 = this._new(p._binding7);
|
||||
if (isPresent(p._keyId8)) this._obj8 = this._new(p._binding8);
|
||||
if (isPresent(p._keyId9)) this._obj9 = this._new(p._binding9);
|
||||
}
|
||||
|
||||
get(token) {
|
||||
return this._getByKey(Key.get(token), 0);
|
||||
}
|
||||
|
||||
_new(binding:Binding) {
|
||||
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]) : null;
|
||||
d1 = length > 1 ? this._getByDependency(deps[1]) : null;
|
||||
d2 = length > 2 ? this._getByDependency(deps[2]) : null;
|
||||
d3 = length > 3 ? this._getByDependency(deps[3]) : null;
|
||||
d4 = length > 4 ? this._getByDependency(deps[4]) : null;
|
||||
d5 = length > 5 ? this._getByDependency(deps[5]) : null;
|
||||
d6 = length > 6 ? this._getByDependency(deps[6]) : null;
|
||||
d7 = length > 7 ? this._getByDependency(deps[7]) : null;
|
||||
d8 = length > 8 ? this._getByDependency(deps[8]) : null;
|
||||
d9 = length > 9 ? this._getByDependency(deps[9]) : null;
|
||||
} catch(e) {
|
||||
if (e instanceof ProviderError) 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;
|
||||
default: throw `Directive ${binding.key.token} can only have up to 10 dependencies.`;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
_getByDependency(dep:DirectiveDependency) {
|
||||
return this._getByKey(dep.key, dep.depth);
|
||||
}
|
||||
|
||||
_getByKey(key:Key, depth:int) {
|
||||
var ei = this;
|
||||
while (ei != null && depth >= 0) {
|
||||
var obj = ei._getDirectiveByKey(key);
|
||||
if (isPresent(obj)) return obj;
|
||||
ei = ei._parent;
|
||||
depth -= 1;
|
||||
}
|
||||
return this._appInjector.get(key);
|
||||
}
|
||||
|
||||
_getDirectiveByKey(key:Key) {
|
||||
var p = this._proto;
|
||||
var keyId= key.id;
|
||||
if (p._keyId0 === keyId) return this._obj0;
|
||||
if (p._keyId1 === keyId) return this._obj1;
|
||||
if (p._keyId2 === keyId) return this._obj2;
|
||||
if (p._keyId3 === keyId) return this._obj3;
|
||||
if (p._keyId4 === keyId) return this._obj4;
|
||||
if (p._keyId5 === keyId) return this._obj5;
|
||||
if (p._keyId6 === keyId) return this._obj6;
|
||||
if (p._keyId7 === keyId) return this._obj7;
|
||||
if (p._keyId8 === keyId) return this._obj8;
|
||||
if (p._keyId9 === keyId) return this._obj9;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib';
|
||||
import {isBlank, FIELD} from 'facade/lang';
|
||||
import {ListWrapper, MapWrapper, List} from 'facade/collection';
|
||||
import {ProtoElementInjector} from 'core/compiler/element_injector';
|
||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||
import {Injector, Inject, bind} from 'di/di';
|
||||
|
||||
class Directive {
|
||||
}
|
||||
|
||||
class NeedsDirective {
|
||||
@FIELD("dependency:Directive")
|
||||
constructor(dependency:Directive){
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
class NeedDirectiveFromParent {
|
||||
@FIELD("dependency:Directive")
|
||||
constructor(@Parent() dependency:Directive){
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
class NeedDirectiveFromAncestor {
|
||||
@FIELD("dependency:Directive")
|
||||
constructor(@Ancestor() dependency:Directive){
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
class NeedsService {
|
||||
@FIELD("service:Object")
|
||||
constructor(@Inject("service") service) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
function humanize(tree, names:List) {
|
||||
var lookupName = (item) =>
|
||||
ListWrapper.last(
|
||||
ListWrapper.find(names, (pair) => pair[0] === item));
|
||||
|
||||
if (tree.children.length == 0) return lookupName(tree);
|
||||
var children = tree.children.map(m => humanize(m, names));
|
||||
return [lookupName(tree), children];
|
||||
}
|
||||
|
||||
describe("ElementInjector", function () {
|
||||
describe("proto injectors", function () {
|
||||
it("should construct a proto tree", function () {
|
||||
var p = new ProtoElementInjector(null, [], []);
|
||||
var c1 = new ProtoElementInjector(p, [], []);
|
||||
var c2 = new ProtoElementInjector(p, [], []);
|
||||
|
||||
expect(humanize(p, [
|
||||
[p, 'parent'],
|
||||
[c1, 'child1'],
|
||||
[c2, 'child2']
|
||||
])).toEqual(["parent", ["child1", "child2"]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("instantiate", function () {
|
||||
it("should create an element injector", function () {
|
||||
var protoParent = new ProtoElementInjector(null, [], []);
|
||||
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
|
||||
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
|
||||
|
||||
var p = protoParent.instantiate();
|
||||
var c1 = protoChild1.instantiate();
|
||||
var c2 = protoChild2.instantiate();
|
||||
|
||||
expect(humanize(p, [
|
||||
[p, 'parent'],
|
||||
[c1, 'child1'],
|
||||
[c2, 'child2']
|
||||
])).toEqual(["parent", ["child1", "child2"]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasBindings", function () {
|
||||
it("should be true when there are bindings", function () {
|
||||
var p = new ProtoElementInjector(null, [Directive], []);
|
||||
expect(p.hasBindings).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should be false otherwise", function () {
|
||||
var p = new ProtoElementInjector(null, [], []);
|
||||
expect(p.hasBindings).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("instantiateDirectives", function () {
|
||||
function injector(bindings, appInjector = null) {
|
||||
var proto = new ProtoElementInjector(null, bindings, []);
|
||||
var inj = proto.instantiate();
|
||||
|
||||
if (isBlank(appInjector)) appInjector = new Injector([]);
|
||||
inj.instantiateDirectives(appInjector);
|
||||
return inj;
|
||||
}
|
||||
|
||||
function parentChildInjectors(parentBindings, childBindings) {
|
||||
var inj = new Injector([]);
|
||||
|
||||
var protoParent = new ProtoElementInjector(null, parentBindings, []);
|
||||
var parent = protoParent.instantiate();
|
||||
parent.instantiateDirectives(inj);
|
||||
|
||||
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
|
||||
var child = protoChild.instantiate();
|
||||
child.instantiateDirectives(inj);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
it("should instantiate directives that have no dependencies", function () {
|
||||
var inj = injector([Directive]);
|
||||
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on other directives", function () {
|
||||
var inj = injector([Directive, NeedsDirective]);
|
||||
|
||||
var d = inj.get(NeedsDirective);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedsDirective);
|
||||
expect(d.dependency).toBeAnInstanceOf(Directive);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on app services", function () {
|
||||
var appInjector = new Injector([
|
||||
bind("service").toValue("service")
|
||||
]);
|
||||
var inj = injector([NeedsService], appInjector);
|
||||
|
||||
var d = inj.get(NeedsService);
|
||||
expect(d).toBeAnInstanceOf(NeedsService);
|
||||
expect(d.service).toEqual("service");
|
||||
});
|
||||
|
||||
it("should return app services", function () {
|
||||
var appInjector = new Injector([
|
||||
bind("service").toValue("service")
|
||||
]);
|
||||
var inj = injector([], appInjector);
|
||||
|
||||
expect(inj.get('service')).toEqual('service');
|
||||
});
|
||||
|
||||
it("should get directives from parent", function () {
|
||||
var child = parentChildInjectors([Directive], [NeedDirectiveFromParent]);
|
||||
|
||||
var d = child.get(NeedDirectiveFromParent);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedDirectiveFromParent);
|
||||
expect(d.dependency).toBeAnInstanceOf(Directive);
|
||||
});
|
||||
|
||||
it("should get directives from ancestor", function () {
|
||||
var child = parentChildInjectors([Directive], [NeedDirectiveFromAncestor]);
|
||||
|
||||
var d = child.get(NeedDirectiveFromAncestor);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||
expect(d.dependency).toBeAnInstanceOf(Directive);
|
||||
});
|
||||
|
||||
it("should throw when no directive found", function () {
|
||||
expect(() => injector([NeedDirectiveFromParent])).
|
||||
toThrowError('No provider for Directive! (NeedDirectiveFromParent -> Directive)');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -25,10 +25,10 @@ export function main() {
|
|||
'</div>' +
|
||||
'</section>');
|
||||
var module:Module = null;
|
||||
var sectionPI = new ProtoElementInjector(null);
|
||||
var sectionPI = new ProtoElementInjector(null, null, null);
|
||||
sectionPI.textNodes = [0];
|
||||
var divPI = new ProtoElementInjector(null);
|
||||
var spanPI = new ProtoElementInjector(null);
|
||||
var divPI = new ProtoElementInjector(null, null, null);
|
||||
var spanPI = new ProtoElementInjector(null, null, null);
|
||||
spanPI.hasProperties = true;
|
||||
var protoElementInjector:List<ProtoElementInjector> = [sectionPI, divPI, spanPI];
|
||||
var protoWatchGroup:ProtoWatchGroup = null;
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
import {CONST} from "facade/lang";
|
||||
|
||||
/**
|
||||
* A parameter annotation that creates a synchronous eager dependency.
|
||||
*
|
||||
* class AComponent {
|
||||
* constructor(@Inject('aServiceToken') aService) {}
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export class Inject {
|
||||
@CONST()
|
||||
constructor(token) {
|
||||
|
@ -7,6 +15,16 @@ export class Inject {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that creates an asynchronous eager dependency.
|
||||
*
|
||||
* class AComponent {
|
||||
* constructor(@InjectPromise('aServiceToken') aServicePromise) {
|
||||
* aServicePromise.then(aService => ...);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export class InjectPromise {
|
||||
@CONST()
|
||||
constructor(token) {
|
||||
|
@ -14,9 +32,48 @@ export class InjectPromise {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that creates a synchronous lazy dependency.
|
||||
*
|
||||
* class AComponent {
|
||||
* constructor(@InjectLazy('aServiceToken') aServiceFn) {
|
||||
* aService = aServiceFn();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
export class InjectLazy {
|
||||
@CONST()
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `DependencyAnnotation` is used by the framework to extend DI.
|
||||
*
|
||||
* Only annotations implementing `DependencyAnnotation` will be added
|
||||
* to the list of dependency properties.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* class Parent extends DependencyAnnotation {}
|
||||
* class NotDependencyProperty {}
|
||||
*
|
||||
* class AComponent {
|
||||
* constructor(@Parent @NotDependencyProperty aService:AService) {}
|
||||
* }
|
||||
*
|
||||
* will create the following dependency:
|
||||
*
|
||||
* new Dependency(Key.get(AService), [new Parent()])
|
||||
*
|
||||
* The framework can use `new Parent()` to handle the `aService` dependency
|
||||
* in a specific way.
|
||||
*
|
||||
*/
|
||||
export class DependencyAnnotation {
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
}
|
|
@ -7,10 +7,11 @@ export class Dependency {
|
|||
@FIELD('final key:Key')
|
||||
@FIELD('final asPromise:bool')
|
||||
@FIELD('final lazy:bool')
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean) {
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, properties:List) {
|
||||
this.key = key;
|
||||
this.asPromise = asPromise;
|
||||
this.lazy = lazy;
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +45,7 @@ export class BindingBuilder {
|
|||
toValue(value):Binding {
|
||||
return new Binding(
|
||||
Key.get(this.token),
|
||||
(_) => value,
|
||||
() => value,
|
||||
[],
|
||||
false
|
||||
);
|
||||
|
@ -53,7 +54,7 @@ export class BindingBuilder {
|
|||
toFactory(factoryFunction:Function, dependencies:List = null):Binding {
|
||||
return new Binding(
|
||||
Key.get(this.token),
|
||||
reflector.convertToFactory(factoryFunction),
|
||||
factoryFunction,
|
||||
this._constructDependencies(factoryFunction, dependencies),
|
||||
false
|
||||
);
|
||||
|
@ -62,7 +63,7 @@ export class BindingBuilder {
|
|||
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding {
|
||||
return new Binding(
|
||||
Key.get(this.token),
|
||||
reflector.convertToFactory(factoryFunction),
|
||||
factoryFunction,
|
||||
this._constructDependencies(factoryFunction, dependencies),
|
||||
true
|
||||
);
|
||||
|
@ -71,6 +72,6 @@ export class BindingBuilder {
|
|||
_constructDependencies(factoryFunction:Function, dependencies:List) {
|
||||
return isBlank(dependencies) ?
|
||||
reflector.dependencies(factoryFunction) :
|
||||
ListWrapper.map(dependencies, (t) => new Dependency(Key.get(t), false, false));
|
||||
ListWrapper.map(dependencies, (t) => new Dependency(Key.get(t), false, false, []));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,3 +3,4 @@ export * from './injector';
|
|||
export * from './binding';
|
||||
export * from './key';
|
||||
export * from './module';
|
||||
export * from './exceptions';
|
||||
|
|
|
@ -5,6 +5,7 @@ import {ProviderError, NoProviderError, InvalidBindingError,
|
|||
import {Type, isPresent, isBlank} from 'facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'facade/async';
|
||||
import {Key} from './key';
|
||||
import {reflector} from './reflector';
|
||||
|
||||
var _constructing = new Object();
|
||||
|
||||
|
@ -150,7 +151,7 @@ class _SyncInjectorStrategy {
|
|||
|
||||
_createInstance(key:Key, binding:Binding, deps:List) {
|
||||
try {
|
||||
var instance = binding.factory(deps);
|
||||
var instance = reflector.invoke(binding.factory, deps);
|
||||
this.injector._setInstance(key, instance);
|
||||
return instance;
|
||||
} catch (e) {
|
||||
|
@ -211,7 +212,7 @@ class _AsyncInjectorStrategy {
|
|||
try {
|
||||
var instance = this.injector._getInstance(key);
|
||||
if (!_isWaiting(instance)) return instance;
|
||||
return binding.factory(deps);
|
||||
return reflector.invoke(binding.factory, deps);
|
||||
} catch (e) {
|
||||
this.injector._clear(key);
|
||||
throw new InstantiationError(e, key);
|
||||
|
|
|
@ -1,26 +1,49 @@
|
|||
library facade.di.reflector;
|
||||
|
||||
import 'dart:mirrors';
|
||||
import 'annotations.dart' show Inject, InjectPromise, InjectLazy;
|
||||
import 'annotations.dart' show Inject, InjectPromise, InjectLazy, DependencyAnnotation;
|
||||
import 'key.dart' show Key;
|
||||
import 'binding.dart' show Dependency;
|
||||
import 'exceptions.dart' show NoAnnotationError;
|
||||
|
||||
class Reflector {
|
||||
Function factoryFor(Type type) {
|
||||
return _generateFactory(type);
|
||||
}
|
||||
|
||||
Function convertToFactory(Function factory) {
|
||||
return (args) => Function.apply(factory, args);
|
||||
}
|
||||
|
||||
Function _generateFactory(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||
Function create = classMirror.newInstance;
|
||||
Symbol name = ctor.constructorName;
|
||||
return (args) => create(name, args).reflectee;
|
||||
int length = ctor.parameters.length;
|
||||
|
||||
switch (length) {
|
||||
case 0: return () =>
|
||||
create(name, []).reflectee;
|
||||
case 1: return (a1) =>
|
||||
create(name, [a1]).reflectee;
|
||||
case 2: return (a1, a2) =>
|
||||
create(name, [a1, a2]).reflectee;
|
||||
case 3: return (a1, a2, a3) =>
|
||||
create(name, [a1, a2, a3]).reflectee;
|
||||
case 4: return (a1, a2, a3, a4) =>
|
||||
create(name, [a1, a2, a3, a4]).reflectee;
|
||||
case 5: return (a1, a2, a3, a4, a5) =>
|
||||
create(name, [a1, a2, a3, a4, a5]).reflectee;
|
||||
case 6: return (a1, a2, a3, a4, a5, a6) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6]).reflectee;
|
||||
case 7: return (a1, a2, a3, a4, a5, a6, a7) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7]).reflectee;
|
||||
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8]).reflectee;
|
||||
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9]).reflectee;
|
||||
case 10: return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]).reflectee;
|
||||
};
|
||||
|
||||
throw "Factory cannot take more than 10 arguments";
|
||||
}
|
||||
|
||||
invoke(Function factory, List args) {
|
||||
return Function.apply(factory, args);
|
||||
}
|
||||
|
||||
List<Dependency> dependencies(typeOrFunc) {
|
||||
|
@ -38,16 +61,17 @@ class Reflector {
|
|||
var injectLazy = metadata.firstWhere((m) => m is InjectLazy, orElse: () => null);
|
||||
|
||||
if (inject != null) {
|
||||
return new Dependency(Key.get(inject.token), false, false);
|
||||
return new Dependency(Key.get(inject.token), false, false, []);
|
||||
|
||||
} else if (injectPromise != null) {
|
||||
return new Dependency(Key.get(injectPromise.token), true, false);
|
||||
return new Dependency(Key.get(injectPromise.token), true, false, []);
|
||||
|
||||
} else if (injectLazy != null) {
|
||||
return new Dependency(Key.get(injectLazy.token), false, true);
|
||||
return new Dependency(Key.get(injectLazy.token), false, true, []);
|
||||
|
||||
} else if (p.type.qualifiedName != #dynamic) {
|
||||
return new Dependency(Key.get(p.type.reflectedType), false, false);
|
||||
var depProps = metadata.where((m) => m is DependencyAnnotation).toList();
|
||||
return new Dependency(Key.get(p.type.reflectedType), false, false, depProps);
|
||||
|
||||
} else {
|
||||
throw new NoAnnotationError(typeOrFunc);
|
||||
|
|
|
@ -1,17 +1,43 @@
|
|||
import {Type, isPresent} from 'facade/lang';
|
||||
import {List} from 'facade/collection';
|
||||
import {Inject, InjectPromise, InjectLazy} from './annotations';
|
||||
import {Inject, InjectPromise, InjectLazy, DependencyAnnotation} from './annotations';
|
||||
import {Key} from './key';
|
||||
import {Dependency} from './binding';
|
||||
import {NoAnnotationError} from './exceptions';
|
||||
|
||||
class Reflector {
|
||||
factoryFor(type:Type):Function {
|
||||
return (args) => new type(...args);
|
||||
var length = type.parameters ? type.parameters.length : 0;
|
||||
switch (length) {
|
||||
case 0: return () =>
|
||||
new type();
|
||||
case 1: return (a1) =>
|
||||
new type(a1);
|
||||
case 2: return (a1, a2) =>
|
||||
new type(a1, a2);
|
||||
case 3: return (a1, a2, a3) =>
|
||||
new type(a1, a2, a3);
|
||||
case 4: return (a1, a2, a3, a4) =>
|
||||
new type(a1, a2, a3, a4);
|
||||
case 5: return (a1, a2, a3, a4, a5) =>
|
||||
new type(a1, a2, a3, a4, a5);
|
||||
case 6: return (a1, a2, a3, a4, a5, a6) =>
|
||||
new type(a1, a2, a3, a4, a5, a6);
|
||||
case 7: return (a1, a2, a3, a4, a5, a6, a7) =>
|
||||
new type(a1, a2, a3, a4, a5, a6, a7);
|
||||
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) =>
|
||||
new type(a1, a2, a3, a4, a5, a6, a7, a8);
|
||||
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
|
||||
new type(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||
case 10: return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) =>
|
||||
new type(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
||||
};
|
||||
|
||||
throw "Factory cannot take more than 10 arguments";
|
||||
}
|
||||
|
||||
convertToFactory(factoryFunction:Function):Function {
|
||||
return (args) => factoryFunction(...args);
|
||||
invoke(factory:Function, args:List) {
|
||||
return factory(...args);
|
||||
}
|
||||
|
||||
dependencies(typeOrFunc):List {
|
||||
|
@ -23,31 +49,35 @@ class Reflector {
|
|||
|
||||
_extractToken(typeOrFunc, annotations) {
|
||||
var type;
|
||||
var depProps = [];
|
||||
|
||||
for (var paramAnnotation of annotations) {
|
||||
if (paramAnnotation instanceof Type) {
|
||||
type = paramAnnotation;
|
||||
|
||||
} else if (paramAnnotation instanceof Inject) {
|
||||
return this._createDependency(paramAnnotation.token, false, false);
|
||||
return this._createDependency(paramAnnotation.token, false, false, []);
|
||||
|
||||
} else if (paramAnnotation instanceof InjectPromise) {
|
||||
return this._createDependency(paramAnnotation.token, true, false);
|
||||
return this._createDependency(paramAnnotation.token, true, false, []);
|
||||
|
||||
} else if (paramAnnotation instanceof InjectLazy) {
|
||||
return this._createDependency(paramAnnotation.token, false, true);
|
||||
return this._createDependency(paramAnnotation.token, false, true, []);
|
||||
|
||||
} else if (paramAnnotation instanceof DependencyAnnotation) {
|
||||
depProps.push(paramAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(type)) {
|
||||
return this._createDependency(type, false, false);
|
||||
return this._createDependency(type, false, false, depProps);
|
||||
} else {
|
||||
throw new NoAnnotationError(typeOrFunc);
|
||||
}
|
||||
}
|
||||
|
||||
_createDependency(token, asPromise, lazy):Dependency {
|
||||
return new Dependency(Key.get(token), asPromise, lazy);
|
||||
_createDependency(token, asPromise, lazy, depProps):Dependency {
|
||||
return new Dependency(Key.get(token), asPromise, lazy, depProps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import {ddescribe, describe, it, iit, expect} from 'test_lib/test_lib';
|
||||
import {Key, Inject, DependencyAnnotation} from 'di/di';
|
||||
import {CONST} from 'facade/lang';
|
||||
import {reflector, Token} from 'di/reflector';
|
||||
|
||||
class Parent extends DependencyAnnotation {
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe("reflector", function () {
|
||||
describe("dependencies", function () {
|
||||
it('should collect annotations implementing DependencyAnnotation as properties', function () {
|
||||
function f(@Parent() arg:Function) {}
|
||||
|
||||
var dep = reflector.dependencies(f)[0];
|
||||
expect(dep.properties[0]).toBeAnInstanceOf(Parent);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -22,6 +22,8 @@ class ListWrapper {
|
|||
static void set(m, k, v) { m[k] = v; }
|
||||
static contains(m, k) => m.containsKey(k);
|
||||
static map(list, fn) => list.map(fn).toList();
|
||||
static find(List list, fn) => list.firstWhere(fn, orElse:() => null);
|
||||
static any(List list, fn) => list.any(fn);
|
||||
static forEach(list, fn) {
|
||||
list.forEach(fn);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,18 @@ export class ListWrapper {
|
|||
if (!array || array.length == 0) return null;
|
||||
return array[array.length - 1];
|
||||
}
|
||||
static find(list:List, pred:Function) {
|
||||
for (var i = 0 ; i < list.length; ++i) {
|
||||
if (pred(list[i])) return list[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
static any(list:List, pred:Function) {
|
||||
for (var i = 0 ; i < list.length; ++i) {
|
||||
if (pred(list[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static reversed(array) {
|
||||
var a = ListWrapper.clone(array);
|
||||
return a.reverse();
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
library angular.core.facade.math;
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
class Math {
|
||||
static num pow(num x, num exponent) {
|
||||
return math.pow(x, exponent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export var Math = window.Math;
|
|
@ -14,6 +14,7 @@
|
|||
"gulp": "^3.8.8",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-watch": "^1.0.3",
|
||||
"gulp-shell": "^0.2.10",
|
||||
"karma-cli": "^0.0.4",
|
||||
"karma": "^0.12.23",
|
||||
"karma-chrome-launcher": "^0.1.4",
|
||||
|
|
Loading…
Reference in New Issue