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 gulp = require('gulp'); | ||||||
| var rename = require('gulp-rename'); | var rename = require('gulp-rename'); | ||||||
| var watch = require('gulp-watch'); | var watch = require('gulp-watch'); | ||||||
| var mergeStreams = require('event-stream').merge; | var mergeStreams = require('event-stream').merge; | ||||||
|  | var es = require('event-stream'); | ||||||
| var connect = require('gulp-connect'); | var connect = require('gulp-connect'); | ||||||
| var clean = require('gulp-rimraf'); | var clean = require('gulp-rimraf'); | ||||||
| var runSequence = require('run-sequence'); | var runSequence = require('run-sequence'); | ||||||
| @ -225,22 +227,58 @@ gulp.task('analyze/dartanalyzer', function(done) { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // ------------------
 | // ------------------
 | ||||||
| // BENCHMARKS
 | // BENCHMARKS JS
 | ||||||
| 
 | 
 | ||||||
| var benchmarksBuildPath = 'build/benchpress'; | gulp.task('benchmarks/build.benchpress.js', function () { | ||||||
| var benchmarksCompiledJsPath = 'build/js/benchmarks/lib'; |  | ||||||
| 
 |  | ||||||
| gulp.task('benchmarks/build.benchpress', function () { |  | ||||||
|   benchpress.build({ |   benchpress.build({ | ||||||
|     benchmarksPath: benchmarksCompiledJsPath, |     benchmarksPath: 'build/js/benchmarks/lib', | ||||||
|     buildPath: benchmarksBuildPath |     buildPath: 'build/benchpress/js' | ||||||
|   }) |   }) | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| gulp.task('benchmarks/build', function() { | gulp.task('benchmarks/build.js', function() { | ||||||
|   runSequence( |   runSequence( | ||||||
|     ['jsRuntime/build', 'modules/build.prod.js'], |     ['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]); |     var child = injector.createChild([E]); | ||||||
|     child.get(E); |     child.get(E); | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   console.log(count) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class A { | 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. |   - 1:1 to DOM structure. | ||||||
| 
 | 
 | ||||||
|  PERF BENCHMARK: http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
 |  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; |   parent:ProtoDirectiveInjector; | ||||||
|   next:ProtoDirectiveInjector; |   next:ProtoDirectiveInjector; | ||||||
|   prev:ProtoDirectiveInjector; |   prev:ProtoDirectiveInjector; | ||||||
|   head:ProtoDirectiveInjector; |   head:ProtoDirectiveInjector; | ||||||
|   tail:ProtoDirectiveInjector; |   tail:ProtoDirectiveInjector; | ||||||
|   DirectiveInjector cloneingInstance; |   DirectiveInjector cloningInstance; | ||||||
|   KeyMap keyMap; |   KeyMap keyMap; | ||||||
|   /// Because DI tree is sparse, this shows how far away is the Parent DI
 |   /// Because DI tree is sparse, this shows how far away is the Parent DI
 | ||||||
|   parentDistance:int = 1; /// 1 for non-sparse/normal depth.
 |   parentDistance:int = 1; /// 1 for non-sparse/normal depth.
 | ||||||
| 
 | 
 | ||||||
|   cKey:int; cFactory:Function; cParams:List<int>; |   cKey:int; cFactory:Function; cParams:List<int>; | ||||||
|   keyId0:int; factory0:Function; params0:List<int>; |   _keyId0:int; factory0:Function; params0:List<int>; | ||||||
|   keyId1:int; factory1:Function; params1:List<int>; |   _keyId1:int; factory1:Function; params1:List<int>; | ||||||
|   keyId2:int; factory2:Function; params2:List<int>; |   _keyId2:int; factory2:Function; params2:List<int>; | ||||||
|   keyId3:int; factory3:Function; params3:List<int>; |   _keyId3:int; factory3:Function; params3:List<int>; | ||||||
|   keyId4:int; factory4:Function; params4:List<int>; |   _keyId4:int; factory4:Function; params4:List<int>; | ||||||
|   keyId5:int; factory5:Function; params5:List<int>; |   _keyId5:int; factory5:Function; params5:List<int>; | ||||||
|   keyId6:int; factory6:Function; params6:List<int>; |   _keyId6:int; factory6:Function; params6:List<int>; | ||||||
|   keyId7:int; factory7:Function; params7:List<int>; |   _keyId7:int; factory7:Function; params7:List<int>; | ||||||
|   keyId8:int; factory8:Function; params8:List<int>; |   _keyId8:int; factory8:Function; params8:List<int>; | ||||||
|   keyId9:int; factory9:Function; params9:List<int>; |   _keyId9:int; factory9:Function; params9:List<int>; | ||||||
| 
 | 
 | ||||||
|   queryKeyId0:int; |   query_keyId0:int; | ||||||
|   queryKeyId1:int; |   query_keyId1:int; | ||||||
| 
 | 
 | ||||||
|   textNodes:List<int>; |   textNodes:List<int>; | ||||||
|   hasProperties:boolean; |   hasProperties:boolean; | ||||||
| @ -54,17 +121,91 @@ export class ProtoElementInjector { | |||||||
| 
 | 
 | ||||||
|   elementInjector:ElementInjector; |   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.hasProperties = false; | ||||||
|     this.textNodes = null; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   instantiate():ElementInjector { |   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; |    _protoInjector:ProtoElementInjector; | ||||||
|    injector:Injector; |    injector:Injector; | ||||||
| @ -107,10 +248,141 @@ export class ElementInjector { | |||||||
|   _query1:Query; |   _query1:Query; | ||||||
| 
 | 
 | ||||||
|    */ |    */ | ||||||
|   @FIELD('final protoInjector:ProtoElementInjector') | 
 | ||||||
|   constructor(protoInjector:ProtoElementInjector) { |   @FIELD('_proto:ProtoElementInjector') | ||||||
|     this.protoInjector = protoInjector; |   @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>' + |                '</div>' + | ||||||
|              '</section>'); |              '</section>'); | ||||||
|         var module:Module = null; |         var module:Module = null; | ||||||
|         var sectionPI = new ProtoElementInjector(null); |         var sectionPI = new ProtoElementInjector(null, null, null); | ||||||
|         sectionPI.textNodes = [0]; |         sectionPI.textNodes = [0]; | ||||||
|         var divPI = new ProtoElementInjector(null); |         var divPI = new ProtoElementInjector(null, null, null); | ||||||
|         var spanPI = new ProtoElementInjector(null); |         var spanPI = new ProtoElementInjector(null, null, null); | ||||||
|         spanPI.hasProperties = true; |         spanPI.hasProperties = true; | ||||||
|         var protoElementInjector:List<ProtoElementInjector> = [sectionPI, divPI, spanPI]; |         var protoElementInjector:List<ProtoElementInjector> = [sectionPI, divPI, spanPI]; | ||||||
|         var protoWatchGroup:ProtoWatchGroup = null; |         var protoWatchGroup:ProtoWatchGroup = null; | ||||||
|  | |||||||
| @ -1,5 +1,13 @@ | |||||||
| import {CONST} from "facade/lang"; | import {CONST} from "facade/lang"; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * A parameter annotation that creates a synchronous eager dependency. | ||||||
|  |  * | ||||||
|  |  *    class AComponent { | ||||||
|  |  *      constructor(@Inject('aServiceToken') aService) {} | ||||||
|  |  *    } | ||||||
|  |  * | ||||||
|  |  */ | ||||||
| export class Inject { | export class Inject { | ||||||
|   @CONST() |   @CONST() | ||||||
|   constructor(token) { |   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 { | export class InjectPromise { | ||||||
|   @CONST() |   @CONST() | ||||||
|   constructor(token) { |   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 { | export class InjectLazy { | ||||||
|   @CONST() |   @CONST() | ||||||
|   constructor(token) { |   constructor(token) { | ||||||
|     this.token = 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 key:Key') | ||||||
|   @FIELD('final asPromise:bool') |   @FIELD('final asPromise:bool') | ||||||
|   @FIELD('final lazy: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.key = key; | ||||||
|     this.asPromise = asPromise; |     this.asPromise = asPromise; | ||||||
|     this.lazy = lazy; |     this.lazy = lazy; | ||||||
|  |     this.properties = properties; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -44,7 +45,7 @@ export class BindingBuilder { | |||||||
|   toValue(value):Binding { |   toValue(value):Binding { | ||||||
|     return new Binding( |     return new Binding( | ||||||
|       Key.get(this.token), |       Key.get(this.token), | ||||||
|       (_) => value, |       () => value, | ||||||
|       [], |       [], | ||||||
|       false |       false | ||||||
|     ); |     ); | ||||||
| @ -53,7 +54,7 @@ export class BindingBuilder { | |||||||
|   toFactory(factoryFunction:Function, dependencies:List = null):Binding { |   toFactory(factoryFunction:Function, dependencies:List = null):Binding { | ||||||
|     return new Binding( |     return new Binding( | ||||||
|       Key.get(this.token), |       Key.get(this.token), | ||||||
|       reflector.convertToFactory(factoryFunction), |       factoryFunction, | ||||||
|       this._constructDependencies(factoryFunction, dependencies), |       this._constructDependencies(factoryFunction, dependencies), | ||||||
|       false |       false | ||||||
|     ); |     ); | ||||||
| @ -62,7 +63,7 @@ export class BindingBuilder { | |||||||
|   toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding { |   toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding { | ||||||
|     return new Binding( |     return new Binding( | ||||||
|       Key.get(this.token), |       Key.get(this.token), | ||||||
|       reflector.convertToFactory(factoryFunction), |       factoryFunction, | ||||||
|       this._constructDependencies(factoryFunction, dependencies), |       this._constructDependencies(factoryFunction, dependencies), | ||||||
|       true |       true | ||||||
|     ); |     ); | ||||||
| @ -71,6 +72,6 @@ export class BindingBuilder { | |||||||
|   _constructDependencies(factoryFunction:Function, dependencies:List) { |   _constructDependencies(factoryFunction:Function, dependencies:List) { | ||||||
|     return isBlank(dependencies) ? |     return isBlank(dependencies) ? | ||||||
|       reflector.dependencies(factoryFunction) : |       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 './binding'; | ||||||
| export * from './key'; | export * from './key'; | ||||||
| export * from './module'; | export * from './module'; | ||||||
|  | export * from './exceptions'; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import {ProviderError, NoProviderError, InvalidBindingError, | |||||||
| import {Type, isPresent, isBlank} from 'facade/lang'; | import {Type, isPresent, isBlank} from 'facade/lang'; | ||||||
| import {Promise, PromiseWrapper} from 'facade/async'; | import {Promise, PromiseWrapper} from 'facade/async'; | ||||||
| import {Key} from './key'; | import {Key} from './key'; | ||||||
|  | import {reflector} from './reflector'; | ||||||
| 
 | 
 | ||||||
| var _constructing = new Object(); | var _constructing = new Object(); | ||||||
| 
 | 
 | ||||||
| @ -150,7 +151,7 @@ class _SyncInjectorStrategy { | |||||||
| 
 | 
 | ||||||
|   _createInstance(key:Key, binding:Binding, deps:List) { |   _createInstance(key:Key, binding:Binding, deps:List) { | ||||||
|     try { |     try { | ||||||
|       var instance = binding.factory(deps); |       var instance = reflector.invoke(binding.factory, deps); | ||||||
|       this.injector._setInstance(key, instance); |       this.injector._setInstance(key, instance); | ||||||
|       return instance; |       return instance; | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
| @ -211,7 +212,7 @@ class _AsyncInjectorStrategy { | |||||||
|     try { |     try { | ||||||
|       var instance = this.injector._getInstance(key); |       var instance = this.injector._getInstance(key); | ||||||
|       if (!_isWaiting(instance)) return instance; |       if (!_isWaiting(instance)) return instance; | ||||||
|       return binding.factory(deps); |       return reflector.invoke(binding.factory, deps); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       this.injector._clear(key); |       this.injector._clear(key); | ||||||
|       throw new InstantiationError(e, key); |       throw new InstantiationError(e, key); | ||||||
|  | |||||||
| @ -1,26 +1,49 @@ | |||||||
| library facade.di.reflector; | library facade.di.reflector; | ||||||
| 
 | 
 | ||||||
| import 'dart:mirrors'; | import 'dart:mirrors'; | ||||||
| import 'annotations.dart' show Inject, InjectPromise, InjectLazy; | import 'annotations.dart' show Inject, InjectPromise, InjectLazy, DependencyAnnotation; | ||||||
| import 'key.dart' show Key; | import 'key.dart' show Key; | ||||||
| import 'binding.dart' show Dependency; | import 'binding.dart' show Dependency; | ||||||
| import 'exceptions.dart' show NoAnnotationError; | import 'exceptions.dart' show NoAnnotationError; | ||||||
| 
 | 
 | ||||||
| class Reflector { | class Reflector { | ||||||
|   Function factoryFor(Type type) { |   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); |     ClassMirror classMirror = reflectType(type); | ||||||
|     MethodMirror ctor = classMirror.declarations[classMirror.simpleName]; |     MethodMirror ctor = classMirror.declarations[classMirror.simpleName]; | ||||||
|     Function create = classMirror.newInstance; |     Function create = classMirror.newInstance; | ||||||
|     Symbol name = ctor.constructorName; |     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) { |   List<Dependency> dependencies(typeOrFunc) { | ||||||
| @ -38,16 +61,17 @@ class Reflector { | |||||||
|       var injectLazy = metadata.firstWhere((m) => m is InjectLazy, orElse: () => null); |       var injectLazy = metadata.firstWhere((m) => m is InjectLazy, orElse: () => null); | ||||||
| 
 | 
 | ||||||
|       if (inject != 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) { |       } 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) { |       } 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) { |       } 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 { |       } else { | ||||||
|         throw new NoAnnotationError(typeOrFunc); |         throw new NoAnnotationError(typeOrFunc); | ||||||
|  | |||||||
| @ -1,17 +1,43 @@ | |||||||
| import {Type, isPresent} from 'facade/lang'; | import {Type, isPresent} from 'facade/lang'; | ||||||
| import {List} from 'facade/collection'; | import {List} from 'facade/collection'; | ||||||
| import {Inject, InjectPromise, InjectLazy} from './annotations'; | import {Inject, InjectPromise, InjectLazy, DependencyAnnotation} from './annotations'; | ||||||
| import {Key} from './key'; | import {Key} from './key'; | ||||||
| import {Dependency} from './binding'; | import {Dependency} from './binding'; | ||||||
| import {NoAnnotationError} from './exceptions'; | import {NoAnnotationError} from './exceptions'; | ||||||
| 
 | 
 | ||||||
| class Reflector { | class Reflector { | ||||||
|   factoryFor(type:Type):Function { |   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 { |   invoke(factory:Function, args:List) { | ||||||
|     return (args) => factoryFunction(...args); |     return factory(...args); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   dependencies(typeOrFunc):List { |   dependencies(typeOrFunc):List { | ||||||
| @ -23,31 +49,35 @@ class Reflector { | |||||||
| 
 | 
 | ||||||
|   _extractToken(typeOrFunc, annotations) { |   _extractToken(typeOrFunc, annotations) { | ||||||
|     var type; |     var type; | ||||||
|  |     var depProps = []; | ||||||
| 
 | 
 | ||||||
|     for (var paramAnnotation of annotations) { |     for (var paramAnnotation of annotations) { | ||||||
|       if (paramAnnotation instanceof Type) { |       if (paramAnnotation instanceof Type) { | ||||||
|         type = paramAnnotation; |         type = paramAnnotation; | ||||||
| 
 | 
 | ||||||
|       } else if (paramAnnotation instanceof Inject) { |       } else if (paramAnnotation instanceof Inject) { | ||||||
|         return this._createDependency(paramAnnotation.token, false, false); |         return this._createDependency(paramAnnotation.token, false, false, []); | ||||||
| 
 | 
 | ||||||
|       } else if (paramAnnotation instanceof InjectPromise) { |       } else if (paramAnnotation instanceof InjectPromise) { | ||||||
|         return this._createDependency(paramAnnotation.token, true, false); |         return this._createDependency(paramAnnotation.token, true, false, []); | ||||||
| 
 | 
 | ||||||
|       } else if (paramAnnotation instanceof InjectLazy) { |       } 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)) { |     if (isPresent(type)) { | ||||||
|       return this._createDependency(type, false, false); |       return this._createDependency(type, false, false, depProps); | ||||||
|     } else { |     } else { | ||||||
|       throw new NoAnnotationError(typeOrFunc); |       throw new NoAnnotationError(typeOrFunc); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _createDependency(token, asPromise, lazy):Dependency { |   _createDependency(token, asPromise, lazy, depProps):Dependency { | ||||||
|     return new Dependency(Key.get(token), asPromise, lazy); |     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 void set(m, k, v) { m[k] = v; } | ||||||
|   static contains(m, k) => m.containsKey(k); |   static contains(m, k) => m.containsKey(k); | ||||||
|   static map(list, fn) => list.map(fn).toList(); |   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) { |   static forEach(list, fn) { | ||||||
|     list.forEach(fn); |     list.forEach(fn); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -41,6 +41,18 @@ export class ListWrapper { | |||||||
|     if (!array || array.length == 0) return null; |     if (!array || array.length == 0) return null; | ||||||
|     return array[array.length - 1]; |     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) { |   static reversed(array) { | ||||||
|     var a = ListWrapper.clone(array); |     var a = ListWrapper.clone(array); | ||||||
|     return a.reverse(); |     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": "^3.8.8", | ||||||
|     "gulp-rename": "^1.2.0", |     "gulp-rename": "^1.2.0", | ||||||
|     "gulp-watch": "^1.0.3", |     "gulp-watch": "^1.0.3", | ||||||
|  |     "gulp-shell": "^0.2.10", | ||||||
|     "karma-cli": "^0.0.4", |     "karma-cli": "^0.0.4", | ||||||
|     "karma": "^0.12.23", |     "karma": "^0.12.23", | ||||||
|     "karma-chrome-launcher": "^0.1.4", |     "karma-chrome-launcher": "^0.1.4", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user