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' | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										10
									
								
								modules/benchmarks/pubspec.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								modules/benchmarks/pubspec.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| name: bnehcmarks | ||||
| environment: | ||||
|   sdk: '>=1.4.0' | ||||
| dependencies: | ||||
|   facade: | ||||
|     path: ../facade | ||||
|   di: | ||||
|     path: ../di | ||||
|   core: | ||||
|     path: ../core | ||||
							
								
								
									
										11
									
								
								modules/benchmarks/src/di/benchmark.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								modules/benchmarks/src/di/benchmark.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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 { | ||||
|  | ||||
							
								
								
									
										11
									
								
								modules/benchmarks/src/element_injector/benchmark.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								modules/benchmarks/src/element_injector/benchmark.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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()) | ||||
|   })); | ||||
| } | ||||
							
								
								
									
										7
									
								
								modules/benchmarks/src/element_injector/benchmark.es5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								modules/benchmarks/src/element_injector/benchmark.es5
									
									
									
									
									
										Normal file
									
								
							| @ -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)); | ||||
							
								
								
									
										11
									
								
								modules/benchmarks/src/element_injector/bp.conf.es5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								modules/benchmarks/src/element_injector/bp.conf.es5
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								modules/benchmarks/src/element_injector/main.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								modules/benchmarks/src/element_injector/main.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										10
									
								
								modules/benchmarks/src/element_injector/register_system.es5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								modules/benchmarks/src/element_injector/register_system.es5
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										22
									
								
								modules/core/src/annotations/visibility.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/core/src/annotations/visibility.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										177
									
								
								modules/core/test/compiler/element_injector_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								modules/core/test/compiler/element_injector_spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										23
									
								
								modules/di/test/di/reflector_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								modules/di/test/di/reflector_spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
|  | ||||
							
								
								
									
										9
									
								
								modules/facade/src/math.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								modules/facade/src/math.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1
									
								
								modules/facade/src/math.es6
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								modules/facade/src/math.es6
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user