diff --git a/angular_static.dart b/angular_static.dart
new file mode 100644
index 000000000..e69de29bb
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 08762dadd..6d13f3cd4 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -4,7 +4,7 @@ packages:
analyzer:
description: analyzer
source: hosted
- version: "0.11.10"
+ version: "0.12.2"
angular:
description:
path: ".."
@@ -15,17 +15,28 @@ packages:
description: args
source: hosted
version: "0.9.0"
+ barback:
+ description: barback
+ source: hosted
+ version: "0.11.1"
browser:
description: browser
source: hosted
version: "0.9.1"
+ code_transformers:
+ description: code_transformers
+ source: hosted
+ version: "0.0.1-dev.2"
collection:
description: collection
source: hosted
version: "0.9.1"
di:
- description: di
- source: hosted
+ description:
+ ref: null
+ resolved-ref: "88e0d48101517e1d3bc84f9d38d4a1b619db65aa"
+ url: "https://github.com/angular/di.dart.git"
+ source: git
version: "0.0.33"
html5lib:
description: html5lib
@@ -34,11 +45,15 @@ packages:
intl:
description: intl
source: hosted
- version: "0.9.7"
+ version: "0.8.10+4"
logging:
description: logging
source: hosted
version: "0.9.1+1"
+ meta:
+ description: meta
+ source: hosted
+ version: "0.8.8"
path:
description: path
source: hosted
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 0aff3ae1d..3e0e3b133 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -5,3 +5,16 @@ dependencies:
path: ../
browser: any
unittest: any
+
+transformers:
+- angular:
+ dart_entries:
+ - web/todo.dart
+ - web/bouncing_balls.dart
+ html_files: # Need to split out to per-entry assets
+ - web/todo.html
+ - web/bouncing_balls.html
+
+dependency_overrides:
+ di:
+ git: https://github.com/angular/di.dart.git
diff --git a/example/web/animation.dart b/example/web/animation.dart
index 7ee1a7eea..210933dca 100644
--- a/example/web/animation.dart
+++ b/example/web/animation.dart
@@ -1,16 +1,9 @@
library animation;
import 'package:angular/angular.dart';
+import 'package:angular/angular_dynamic.dart';
import 'package:angular/animate/module.dart';
-// This annotation allows Dart to shake away any classes
-// not used from Dart code nor listed in another @MirrorsUsed.
-//
-// If you create classes that are referenced from the Angular
-// expressions, you must include a library target in @MirrorsUsed.
-@MirrorsUsed(override: '*')
-import 'dart:mirrors';
-
part 'animation/repeat_demo.dart';
part 'animation/visibility_demo.dart';
part 'animation/stress_demo.dart';
@@ -25,11 +18,13 @@ class AnimationDemoController {
}
main() {
- ngBootstrap(module: new Module()
+ new NgDynamicApp()
+ .addModule(new Module()
..install(new NgAnimateModule())
..type(RepeatDemoComponent)
..type(VisibilityDemoComponent)
..type(StressDemoComponent)
..type(CssDemoComponent)
- ..type(AnimationDemoController));
+ ..type(AnimationDemoController))
+ .run();
}
diff --git a/example/web/bouncing_balls.dart b/example/web/bouncing_balls.dart
index 783769740..c5bdd3437 100644
--- a/example/web/bouncing_balls.dart
+++ b/example/web/bouncing_balls.dart
@@ -1,4 +1,7 @@
+import 'package:perf_api/perf_api.dart';
import 'package:angular/angular.dart';
+import 'package:angular/angular_dynamic.dart';
+import 'package:angular/change_detection/change_detection.dart';
import 'dart:html';
import 'dart:math';
import 'dart:core';
@@ -30,18 +33,17 @@ class BallModel {
publishAs: 'bounce')
class BounceController {
var lastTime = window.performance.now();
- var run = true;
+ var run = false;
var fps = 0;
var digestTime = 0;
var currentDigestTime = 0;
var balls = [];
- final NgZone zone;
final Scope scope;
var ballClassName = 'ball';
- BounceController(this.zone, this.scope) {
+ BounceController(this.scope) {
changeCount(100);
- tick();
+ if (run) tick();
}
void toggleCSS() {
@@ -54,7 +56,7 @@ class BounceController {
}
void requestAnimationFrame(fn) {
- window.requestAnimationFrame((_) => zone.run(fn));
+ window.requestAnimationFrame((_) => fn());
}
void changeCount(count) {
@@ -118,20 +120,12 @@ class MyModule extends Module {
MyModule() {
type(BounceController);
type(BallPositionDirective);
- value(GetterCache, new GetterCache({
- 'x': (o) => o.x,
- 'y': (o) => o.y,
- 'bounce': (o) => o.bounce,
- 'fps': (o) => o.fps,
- 'balls': (o) => o.balls,
- 'length': (o) => o.length,
- 'digestTime': (o) => o.digestTime,
- 'ballClassName': (o) => o.ballClassName
- }));
value(ScopeStats, new ScopeStats(report: true));
}
}
main() {
- ngBootstrap(module: new MyModule());
+ new NgDynamicApp()
+ .addModule(new MyModule())
+ .run();
}
diff --git a/example/web/hello_world.dart b/example/web/hello_world.dart
index 338e33bb6..6b1f3957c 100644
--- a/example/web/hello_world.dart
+++ b/example/web/hello_world.dart
@@ -1,4 +1,5 @@
import 'package:angular/angular.dart';
+import 'package:angular/angular_dynamic.dart';
// This annotation allows Dart to shake away any classes
// not used from Dart code nor listed in another @MirrorsUsed.
@@ -16,5 +17,7 @@ class HelloWorldController {
}
main() {
- ngBootstrap(module: new Module()..type(HelloWorldController));
+ new NgDynamicApp()
+ .addModule(new Module()..type(HelloWorldController))
+ .run();
}
diff --git a/example/web/todo.dart b/example/web/todo.dart
index c480c9636..0da49f35f 100644
--- a/example/web/todo.dart
+++ b/example/web/todo.dart
@@ -1,19 +1,11 @@
library todo;
import 'package:angular/angular.dart';
+import 'package:angular/angular_dynamic.dart';
import 'package:angular/playback/playback_http.dart';
-import 'todo.dart';
import 'dart:html';
-// This annotation allows Dart to shake away any classes
-// not used from Dart code nor listed in another @MirrorsUsed.
-//
-// If you create classes that are referenced from the Angular
-// expressions, you must include a library target in @MirrorsUsed.
-@MirrorsUsed(override: '*')
-import 'dart:mirrors';
-
class Item {
String text;
bool done;
@@ -39,6 +31,7 @@ abstract class ServerController {
// An implementation of ServerController that does nothing.
+@NgInjectableService()
class NoServerController implements ServerController {
init(TodoController todo) { }
}
@@ -46,6 +39,7 @@ class NoServerController implements ServerController {
// An implementation of ServerController that fetches items from
// the server over HTTP.
+@NgInjectableService()
class HttpServerController implements ServerController {
final Http _http;
HttpServerController(this._http);
@@ -128,5 +122,5 @@ main() {
module.type(HttpBackend, implementedBy: PlaybackHttpBackend);
}
- ngBootstrap(module: module);
+ new NgDynamicApp().addModule(module).run();
}
diff --git a/example/web/todo.html b/example/web/todo.html
index aeeed8732..cbd32cc92 100644
--- a/example/web/todo.html
+++ b/example/web/todo.html
@@ -5,7 +5,7 @@
Things To Do
-
+
diff --git a/lib/angular.dart b/lib/angular.dart
index 136a01815..c190d6a7e 100644
--- a/lib/angular.dart
+++ b/lib/angular.dart
@@ -13,46 +13,8 @@ library angular;
import 'dart:html' as dom;
import 'dart:js' as js;
import 'package:di/di.dart';
-import 'package:di/dynamic_injector.dart';
import 'package:intl/date_symbol_data_local.dart';
-/**
- * If you are writing code accessed from Angular expressions, you must include
- * your own @MirrorsUsed annotation or ensure that everything is tagged with
- * the Ng annotations.
- *
- * All programs should also include a @MirrorsUsed(override: '*') which
- * tells the compiler that only the explicitly listed libraries will
- * be reflected over.
- *
- * This is a short-term fix until we implement a transformer-based solution
- * which does not rely on mirrors.
- */
-@MirrorsUsed(targets: const [
- 'angular',
- 'angular.animate',
- 'angular.core',
- 'angular.core.dom',
- 'angular.filter',
- 'angular.perf',
- 'angular.directive',
- 'angular.routing',
- 'angular.core.parser.Parser',
- 'angular.core.parser.dynamic_parser',
- 'angular.core.parser.lexer',
- 'perf_api',
- List,
- dom.NodeTreeSanitizer,
-],
-metaTargets: const [
- NgInjectableService,
- NgDirective,
- NgController,
- NgComponent,
- NgFilter
-])
-import 'dart:mirrors' show MirrorsUsed;
-
import 'package:angular/core/module.dart';
import 'package:angular/core_dom/module.dart';
import 'package:angular/directive/module.dart';
@@ -68,8 +30,6 @@ export 'package:angular/core/parser/lexer.dart';
export 'package:angular/directive/module.dart';
export 'package:angular/filter/module.dart';
export 'package:angular/routing/module.dart';
-export 'package:angular/change_detection/dirty_checking_change_detector.dart'
- show FieldGetter, GetterCache;
part 'bootstrap.dart';
part 'introspection.dart';
diff --git a/lib/angular_dynamic.dart b/lib/angular_dynamic.dart
new file mode 100644
index 000000000..41264e445
--- /dev/null
+++ b/lib/angular_dynamic.dart
@@ -0,0 +1,56 @@
+library angular.dynamic;
+
+import 'package:di/dynamic_injector.dart';
+import "package:angular/angular.dart";
+import 'package:angular/change_detection/change_detection.dart';
+import 'package:angular/change_detection/dirty_checking_change_detector_dynamic.dart';
+import 'package:angular/core/registry_dynamic.dart';
+import 'dart:html' as dom;
+
+/**
+ * If you are writing code accessed from Angular expressions, you must include
+ * your own @MirrorsUsed annotation or ensure that everything is tagged with
+ * the Ng annotations.
+ *
+ * All programs should also include a @MirrorsUsed(override: '*') which
+ * tells the compiler that only the explicitly listed libraries will
+ * be reflected over.
+ *
+ * This is a short-term fix until we implement a transformer-based solution
+ * which does not rely on mirrors.
+ */
+@MirrorsUsed(targets: const [
+ 'angular',
+ 'angular.animate',
+ 'angular.core',
+ 'angular.core.dom',
+ 'angular.filter',
+ 'angular.perf',
+ 'angular.directive',
+ 'angular.routing',
+ 'angular.core.parser.Parser',
+ 'angular.core.parser.dynamic_parser',
+ 'angular.core.parser.lexer',
+ 'perf_api',
+ List,
+ dom.NodeTreeSanitizer,
+],
+metaTargets: const [
+ NgInjectableService,
+ NgDirective,
+ NgController,
+ NgComponent,
+ NgFilter
+])
+import 'dart:mirrors' show MirrorsUsed;
+
+class NgDynamicApp extends NgApp {
+ NgDynamicApp() {
+ ngModule
+ ..type(MetadataExtractor, implementedBy: DynamicMetadataExtractor)
+ ..type(FieldGetterFactory, implementedBy: DynamicFieldGetterFactory);
+ }
+
+ Injector createInjector()
+ => new DynamicInjector(modules: modules);
+}
diff --git a/lib/angular_static.dart b/lib/angular_static.dart
new file mode 100644
index 000000000..d250f7d22
--- /dev/null
+++ b/lib/angular_static.dart
@@ -0,0 +1,28 @@
+library angular.static;
+
+// REMOVE once all mirrors dependencies are gone.
+@MirrorsUsed(override: const ['*'], targets: const [])
+import 'dart:mirrors';
+
+import 'package:di/static_injector.dart';
+import 'package:angular/angular.dart';
+import 'package:angular/core/registry_static.dart';
+import 'package:angular/change_detection/change_detection.dart';
+import 'package:angular/change_detection/dirty_checking_change_detector_static.dart';
+
+class NgStaticApp extends NgApp {
+ final Map typeFactories;
+
+ NgStaticApp(Map this.typeFactories,
+ Map metadata,
+ Map fieldGetters,
+ ClosureMap closureMap) {
+ ngModule
+ ..value(MetadataExtractor, new StaticMetadataExtractor(metadata))
+ ..value(FieldGetterFactory, new StaticFieldGetterFactory(fieldGetters))
+ ..value(ClosureMap, closureMap);
+ }
+
+ Injector createInjector()
+ => new StaticInjector(modules: modules, typeFactories: typeFactories);
+}
diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart
index 6c0172f88..d082691fc 100644
--- a/lib/bootstrap.dart
+++ b/lib/bootstrap.dart
@@ -23,13 +23,9 @@ class AngularModule extends Module {
type(MetadataExtractor);
value(Expando, _elementExpando);
- value(NgApp, new NgApp(dom.window.document.documentElement));
}
}
-Injector _defaultInjectorFactory(List modules) =>
- new DynamicInjector(modules: modules);
-
/**
* This method is the main entry point to an angular application.
*
@@ -62,46 +58,49 @@ Injector _defaultInjectorFactory(List modules) =>
* ....
* Injector injector = ngBootstrap(module: myAppModule);
*/
-Injector ngBootstrap({
- Module module: null,
- List modules: null,
- dom.Element element: null,
- String selector: '[ng-app]',
- Injector injectorFactory(List modules): _defaultInjectorFactory}) {
- _publishToJavaScript();
- var ngModules = [new AngularModule()];
- if (module != null) ngModules.add(module);
- if (modules != null) ngModules.addAll(modules);
- if (element == null) {
- element = dom.querySelector(selector);
- if (element == null) {
- element = dom.window.document.childNodes
- .firstWhere((e) => e is dom.Element);
- }
+abstract class NgApp {
+ static _find(String selector, [dom.Element defaultElement]) {
+ var element = dom.window.document.querySelector(selector);
+ if (element == null) element = defaultElement;
+ if (element == null)throw "Could not find application element '$selector'.";
+ return element;
}
- // The injector must be created inside the zone, so we create the
- // zone manually and give it back to the injector as a value.
- NgZone zone = new NgZone();
- ngModules.add(new Module()
+ final NgZone zone = new NgZone();
+ final AngularModule ngModule = new AngularModule();
+ final List modules = [];
+ dom.Element element;
+
+ dom.Element selector(String selector) => element = _find(selector);
+
+ NgApp(): element = _find('[ng-app]', dom.window.document.documentElement) {
+ modules.add(ngModule);
+ ngModule
..value(NgZone, zone)
- ..value(NgApp, new NgApp(element)));
+ ..value(NgApp, this);
+ }
+
+ Injector injector;
- return zone.run(() {
- var rootElements = [element];
- Injector injector = injectorFactory(ngModules);
- initializeDateFormatting(null, null).then((_) {
- var compiler = injector.get(Compiler);
- var blockFactory = compiler(rootElements, injector.get(DirectiveMap));
- blockFactory(injector, rootElements);
+ NgApp addModule(Module module) {
+ modules.add(module);
+ return this;
+ }
+
+ Injector run() {
+ _publishToJavaScript();
+ return zone.run(() {
+ var rootElements = [element];
+ Injector injector = createInjector();
+ initializeDateFormatting(null, null).then((_) {
+ var compiler = injector.get(Compiler);
+ var blockFactory = compiler(rootElements, injector.get(DirectiveMap));
+ blockFactory(injector, rootElements);
+ });
+ return injector;
});
- return injector;
- });
-}
+ }
-/// Holds a reference to the root of the application used by ngBootstrap.
-class NgApp {
- final dom.Element root;
- NgApp(this.root);
+ Injector createInjector();
}
diff --git a/lib/change_detection/change_detection.dart b/lib/change_detection/change_detection.dart
index c3394708a..de368f258 100644
--- a/lib/change_detection/change_detection.dart
+++ b/lib/change_detection/change_detection.dart
@@ -230,6 +230,12 @@ abstract class RemovedItem extends CollectionChangeItem {
RemovedItem get nextRemovedItem;
}
+typedef FieldGetter(object);
+
+abstract class FieldGetterFactory {
+ FieldGetter call(Object object, String name);
+}
+
class AvgStopwatch extends Stopwatch {
int _count = 0;
diff --git a/lib/change_detection/dirty_checking_change_detector.dart b/lib/change_detection/dirty_checking_change_detector.dart
index 420ece3db..9852d2298 100644
--- a/lib/change_detection/dirty_checking_change_detector.dart
+++ b/lib/change_detection/dirty_checking_change_detector.dart
@@ -1,19 +1,8 @@
library dirty_checking_change_detector;
-import 'dart:mirrors';
import 'dart:collection';
import 'package:angular/change_detection/change_detection.dart';
-typedef FieldGetter(object);
-
-class GetterCache {
- final Map _map;
-
- GetterCache(this._map);
-
- FieldGetter call(String name) => _map[name];
-}
-
/**
* [DirtyCheckingChangeDetector] determines which object properties have changed
* by comparing them to the their previous value.
@@ -47,7 +36,7 @@ class DirtyCheckingChangeDetectorGroup implements ChangeDetectorGroup {
*/
final DirtyCheckingRecord _marker = new DirtyCheckingRecord.marker();
- final GetterCache _getterCache;
+ final FieldGetterFactory _fieldGetterFactory;
/**
* All records for group are kept together and are denoted by head/tail.
@@ -85,7 +74,7 @@ class DirtyCheckingChangeDetectorGroup implements ChangeDetectorGroup {
*/
DirtyCheckingChangeDetectorGroup _parent, _childHead, _childTail, _prev, _next;
- DirtyCheckingChangeDetectorGroup(this._parent, this._getterCache) {
+ DirtyCheckingChangeDetectorGroup(this._parent, this._fieldGetterFactory) {
// we need to insert the marker record at the beginning.
if (_parent == null) {
_recordHead = _marker;
@@ -116,9 +105,8 @@ class DirtyCheckingChangeDetectorGroup implements ChangeDetectorGroup {
WatchRecord watch(Object object, String field, H handler) {
assert(_root != null); // prove that we are not deleted connected;
- var getter = field == null ? null : _getterCache(field);
- return _recordAdd(new DirtyCheckingRecord(this, object, field, getter,
- handler));
+ return _recordAdd(new DirtyCheckingRecord(this, _fieldGetterFactory,
+ handler, field, object));
}
/**
@@ -126,7 +114,7 @@ class DirtyCheckingChangeDetectorGroup implements ChangeDetectorGroup {
*/
DirtyCheckingChangeDetectorGroup newGroup() {
assert(_root._assertRecordsOk());
- var child = new DirtyCheckingChangeDetectorGroup(this, _getterCache);
+ var child = new DirtyCheckingChangeDetectorGroup(this, _fieldGetterFactory);
if (_childHead == null) {
_childHead = _childTail = child;
} else {
@@ -250,7 +238,8 @@ class DirtyCheckingChangeDetector extends DirtyCheckingChangeDetectorGroup
final DirtyCheckingRecord _fakeHead = new DirtyCheckingRecord.marker();
- DirtyCheckingChangeDetector(GetterCache getterCache): super(null, getterCache);
+ DirtyCheckingChangeDetector(FieldGetterFactory fieldGetterFactory)
+ : super(null, fieldGetterFactory);
DirtyCheckingChangeDetector get _root => this;
@@ -349,19 +338,17 @@ class _ChangeIterator implements Iterator>{
*/
class DirtyCheckingRecord implements Record, WatchRecord {
static const List _MODE_NAMES =
- const ['MARKER', 'IDENT', 'REFLECT', 'GETTER', 'MAP[]', 'ITERABLE', 'MAP'];
+ const ['MARKER', 'IDENT', 'GETTER', 'MAP[]', 'ITERABLE', 'MAP'];
static const int _MODE_MARKER_ = 0;
static const int _MODE_IDENTITY_ = 1;
- static const int _MODE_REFLECT_ = 2;
- static const int _MODE_GETTER_ = 3;
- static const int _MODE_MAP_FIELD_ = 4;
- static const int _MODE_ITERABLE_ = 5;
- static const int _MODE_MAP_ = 6;
+ static const int _MODE_GETTER_ = 2;
+ static const int _MODE_MAP_FIELD_ = 3;
+ static const int _MODE_ITERABLE_ = 4;
+ static const int _MODE_MAP_ = 5;
final DirtyCheckingChangeDetectorGroup _group;
+ final FieldGetterFactory _fieldGetterFactory;
final String field;
- final Symbol _symbol;
- final FieldGetter _getter;
final H handler;
int _mode;
@@ -372,20 +359,18 @@ class DirtyCheckingRecord implements Record, WatchRecord {
DirtyCheckingRecord _prevRecord;
Record _nextChange;
var _object;
- InstanceMirror _instanceMirror;
+ FieldGetter _getter;
- DirtyCheckingRecord(this._group, object, fieldName, this._getter, this.handler)
- : field = fieldName,
- _symbol = fieldName == null ? null : new Symbol(fieldName)
- {
+ DirtyCheckingRecord(this._group, this._fieldGetterFactory, this.handler,
+ this.field, object) {
this.object = object;
}
DirtyCheckingRecord.marker()
- : handler = null,
+ : _group = null,
+ _fieldGetterFactory = null,
+ handler = null,
field = null,
- _group = null,
- _symbol = null,
_getter = null,
_mode = _MODE_MARKER_;
@@ -400,11 +385,12 @@ class DirtyCheckingRecord implements Record, WatchRecord {
_object = obj;
if (obj == null) {
_mode = _MODE_IDENTITY_;
+ _getter = null;
return;
}
if (field == null) {
- _instanceMirror = null;
+ _getter = null;
if (obj is Map) {
if (_mode != _MODE_MAP_) {
// Last one was collection as well, don't reset state.
@@ -426,13 +412,10 @@ class DirtyCheckingRecord implements Record, WatchRecord {
if (obj is Map) {
_mode = _MODE_MAP_FIELD_;
- _instanceMirror = null;
- } else if (_getter != null) {
- _mode = _MODE_GETTER_;
- _instanceMirror = null;
+ _getter = null;
} else {
- _mode = _MODE_REFLECT_;
- _instanceMirror = reflect(obj);
+ _mode = _MODE_GETTER_;
+ _getter = _fieldGetterFactory.call(obj, field);
}
}
@@ -442,9 +425,6 @@ class DirtyCheckingRecord implements Record, WatchRecord {
switch (_mode) {
case _MODE_MARKER_:
return false;
- case _MODE_REFLECT_:
- current = _instanceMirror.getField(_symbol).reflectee;
- break;
case _MODE_GETTER_:
current = _getter(object);
break;
diff --git a/lib/change_detection/dirty_checking_change_detector_dynamic.dart b/lib/change_detection/dirty_checking_change_detector_dynamic.dart
new file mode 100644
index 000000000..a34ac6cd2
--- /dev/null
+++ b/lib/change_detection/dirty_checking_change_detector_dynamic.dart
@@ -0,0 +1,19 @@
+library dirty_checking_change_detector_dynamic;
+
+import 'package:angular/change_detection/change_detection.dart';
+
+/**
+ * We are using mirrors, but there is no need to import anything.
+ */
+@MirrorsUsed(targets: const [], metaTargets: const [])
+import 'dart:mirrors';
+
+class DynamicFieldGetterFactory implements FieldGetterFactory {
+ FieldGetter call (Object object, String name) {
+ Symbol symbol = new Symbol(name);
+ InstanceMirror instanceMirror = reflect(object);
+ return (Object object) {
+ return instanceMirror.getField(symbol).reflectee;
+ };
+ }
+}
diff --git a/lib/change_detection/dirty_checking_change_detector_static.dart b/lib/change_detection/dirty_checking_change_detector_static.dart
new file mode 100644
index 000000000..8267f71ea
--- /dev/null
+++ b/lib/change_detection/dirty_checking_change_detector_static.dart
@@ -0,0 +1,15 @@
+library dirty_checking_change_detector_static;
+
+import 'package:angular/change_detection/change_detection.dart';
+
+class StaticFieldGetterFactory implements FieldGetterFactory {
+ Map getters;
+
+ StaticFieldGetterFactory(this.getters);
+
+ FieldGetter call(Object object, String name) {
+ var getter = getters[name];
+ if (getter == null) throw "Missing getter: (o) => o.$name";
+ return getter;
+ }
+}
diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart
index b3085562a..1e4b49547 100644
--- a/lib/change_detection/watch_group.dart
+++ b/lib/change_detection/watch_group.dart
@@ -1,6 +1,5 @@
library angular.watch_group;
-import 'dart:mirrors';
import 'package:angular/change_detection/change_detection.dart';
part 'linked_list.dart';
@@ -206,7 +205,8 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
_EvalWatchRecord _addEvalWatch(AST lhsAST, /* dartbug.com/16401 Function */ fn, String name,
List argsAST, String expression) {
_InvokeHandler invokeHandler = new _InvokeHandler(this, expression);
- var evalWatchRecord = new _EvalWatchRecord(this, invokeHandler, fn, name,
+ var evalWatchRecord = new _EvalWatchRecord(
+ _rootGroup._fieldGetterFactory, this, invokeHandler, fn, name,
argsAST.length);
invokeHandler.watchRecord = evalWatchRecord;
@@ -339,6 +339,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
* [RootWatchGroup]
*/
class RootWatchGroup extends WatchGroup {
+ final FieldGetterFactory _fieldGetterFactory;
Watch _dirtyWatchHead, _dirtyWatchTail;
/**
@@ -351,7 +352,9 @@ class RootWatchGroup extends WatchGroup {
int _removeCount = 0;
- RootWatchGroup(ChangeDetector changeDetector, Object context):
+ RootWatchGroup(FieldGetterFactory this._fieldGetterFactory,
+ ChangeDetector changeDetector,
+ Object context):
super._root(changeDetector, context);
RootWatchGroup get _rootGroup => this;
@@ -676,20 +679,19 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
WatchGroup watchGrp;
final _Handler handler;
final List args;
- final Symbol symbol;
final String name;
int mode;
/* dartbug.com/16401 Function*/ var fn;
- InstanceMirror _instanceMirror;
+ FieldGetterFactory _fieldGetterFactory;
bool dirtyArgs = true;
dynamic currentValue, previousValue, _object;
_EvalWatchRecord _prevEvalWatch, _nextEvalWatch;
- _EvalWatchRecord(this.watchGrp, this.handler, this.fn, name, int arity)
- : args = new List(arity),
- name = name,
- symbol = name == null ? null : new Symbol(name) {
+ _EvalWatchRecord(this._fieldGetterFactory, this.watchGrp, this.handler,
+ this.fn, this.name, int arity)
+ : args = new List(arity)
+ {
if (fn is FunctionApply) {
mode = _MODE_FUNCTION_APPLY_;
} else if (fn is Function) {
@@ -701,21 +703,21 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
_EvalWatchRecord.marker()
: mode = _MODE_MARKER_,
+ _fieldGetterFactory = null,
watchGrp = null,
handler = null,
args = null,
fn = null,
- symbol = null,
name = null;
_EvalWatchRecord.constant(_Handler handler, dynamic constantValue)
: mode = _MODE_MARKER_,
+ _fieldGetterFactory = null,
handler = handler,
currentValue = constantValue,
watchGrp = null,
args = null,
fn = null,
- symbol = null,
name = null;
get field => '()';
@@ -727,7 +729,6 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
assert(mode != _MODE_MARKER_);
assert(mode != _MODE_FUNCTION_);
assert(mode != _MODE_FUNCTION_APPLY_);
- assert(symbol != null);
_object = value;
if (value == null) {
@@ -736,10 +737,21 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
if (value is Map) {
mode = _MODE_MAP_CLOSURE_;
} else {
- _instanceMirror = reflect(value);
- mode = _hasMethod(_instanceMirror, symbol)
- ? _MODE_METHOD_
- : _MODE_FIELD_CLOSURE_;
+ var getter = _fieldGetterFactory.call(value, name);
+ // We need to know if we are referring to method or field which is a
+ // function We can find out by calling it twice and seeing if we get
+ // the same value.
+ var val1 = getter(_object);
+ var val2 = getter(_object);
+ if (identical(val1, val2)) {
+ // It is a field since calling it twice returns same value
+ fn = getter;
+ mode = _MODE_FIELD_CLOSURE_;
+ } else {
+ // It is a method since method closurizes into different instances
+ mode = _MODE_METHOD_;
+ fn = val1;
+ }
}
}
}
@@ -761,7 +773,7 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
dirtyArgs = false;
break;
case _MODE_FIELD_CLOSURE_:
- var closure = _instanceMirror.getField(symbol).reflectee;
+ var closure = fn(_object);
value = closure == null ? null : Function.apply(closure, args);
break;
case _MODE_MAP_CLOSURE_:
@@ -769,7 +781,7 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
value = closure == null ? null : Function.apply(closure, args);
break;
case _MODE_METHOD_:
- value = _instanceMirror.invoke(symbol, args).reflectee;
+ value = Function.apply(fn, args);
break;
default:
assert(false);
@@ -803,8 +815,4 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, Record<_Handler> {
if (mode == _MODE_MARKER_) return 'MARKER[$currentValue]';
return '${watchGrp.id}:${handler.expression}';
}
-
- static bool _hasMethod(InstanceMirror mirror, Symbol symbol) {
- return mirror.type.instanceMembers[symbol] is MethodMirror;
- }
}
diff --git a/lib/change_detection/watch_group_dynamic.dart b/lib/change_detection/watch_group_dynamic.dart
new file mode 100644
index 000000000..78b8f4c52
--- /dev/null
+++ b/lib/change_detection/watch_group_dynamic.dart
@@ -0,0 +1,4 @@
+library watch_group_dynamic;
+
+import 'package:angular/change_detection/watch_group.dart';
+
diff --git a/lib/change_detection/watch_group_static.dart b/lib/change_detection/watch_group_static.dart
new file mode 100644
index 000000000..817d6a984
--- /dev/null
+++ b/lib/change_detection/watch_group_static.dart
@@ -0,0 +1,4 @@
+library watch_group_static;
+
+import 'package:angular/change_detection/watch_group.dart';
+
diff --git a/lib/core/module.dart b/lib/core/module.dart
index 4558a3d5f..846eec44e 100644
--- a/lib/core/module.dart
+++ b/lib/core/module.dart
@@ -2,7 +2,6 @@ library angular.core;
import 'dart:async' as async;
import 'dart:collection';
-import 'dart:mirrors';
import 'package:intl/intl.dart';
import 'package:di/di.dart';
@@ -43,7 +42,6 @@ class NgCoreModule extends Module {
type(RootScope);
factory(Scope, (injector) => injector.get(RootScope));
value(ScopeStats, new ScopeStats());
- value(GetterCache, new GetterCache({}));
value(Object, {}); // RootScope context
type(AstParser);
type(NgZone);
diff --git a/lib/core/parser/parser.dart b/lib/core/parser/parser.dart
index 0e8af1a81..a205cfebb 100644
--- a/lib/core/parser/parser.dart
+++ b/lib/core/parser/parser.dart
@@ -4,8 +4,6 @@ export 'package:angular/core/parser/syntax.dart'
show Visitor, Expression, BoundExpression;
export 'package:angular/core/parser/dynamic_parser.dart'
show DynamicParser, DynamicParserBackend, ClosureMap;
-export 'package:angular/core/parser/static_parser.dart'
- show StaticParser, StaticParserFunctions;
typedef LocalsWrapper(context, locals);
typedef Getter(self);
diff --git a/lib/core/parser/static_parser.dart b/lib/core/parser/static_parser.dart
deleted file mode 100644
index b9c59ee5d..000000000
--- a/lib/core/parser/static_parser.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-library angular.core.parser.static_parser;
-
-import 'package:angular/core/module.dart' show FilterMap, NgInjectableService;
-import 'package:angular/core/parser/parser.dart';
-import 'package:angular/core/parser/utils.dart' show EvalError;
-import 'package:angular/core/parser/syntax.dart';
-
-class StaticParserFunctions {
- final Map eval;
- final Map assign;
- StaticParserFunctions(this.eval, this.assign);
-}
-
-@NgInjectableService()
-class StaticParser implements Parser {
- final StaticParserFunctions _functions;
- final DynamicParser _fallbackParser;
- final Map _cache = {};
- StaticParser(this._functions, this._fallbackParser);
-
- Expression call(String input) {
- if (input == null) input = '';
- return _cache.putIfAbsent(input, () => _construct(input));
- }
-
- Expression _construct(String input) {
- var eval = _functions.eval[input];
- if (eval == null) return _fallbackParser(input);
- if (eval is !Function) throw eval;
- Function assign = _functions.assign[input];
- return new StaticExpression(input, eval, assign);
- }
-}
-
-class StaticExpression extends Expression {
- final String _input;
- final Function _eval;
- final Function _assign;
- StaticExpression(this._input, this._eval, [this._assign]);
-
- bool get isAssignable => _assign != null;
- accept(Visitor visitor) => throw "Cannot visit static expression $this";
- toString() => _input;
-
- eval(scope, [FilterMap filters = defaultFilterMap]) {
- try {
- return _eval(scope, filters);
- } on EvalError catch (e, s) {
- throw e.unwrap("$this", s);
- }
- }
-
- assign(scope, value) {
- try {
- if (_assign == null) throw new EvalError("Cannot assign to $this");
- return _assign(scope, value);
- } on EvalError catch (e, s) {
- throw e.unwrap("$this", s);
- }
- }
-}
\ No newline at end of file
diff --git a/lib/core/registry.dart b/lib/core/registry.dart
index 11ed68651..8bfad16bf 100644
--- a/lib/core/registry.dart
+++ b/lib/core/registry.dart
@@ -70,12 +70,6 @@ abstract class AnnotationsMap {
@NgInjectableService()
-class MetadataExtractor {
- Iterable call(Type type) {
- if (reflectType(type) is TypedefMirror) return [];
- var metadata = reflectClass(type).metadata;
- return metadata == null
- ? []
- : metadata.map((InstanceMirror im) => im.reflectee);
- }
+abstract class MetadataExtractor {
+ Iterable call(Type type);
}
diff --git a/lib/core/registry_dynamic.dart b/lib/core/registry_dynamic.dart
new file mode 100644
index 000000000..49fff9537
--- /dev/null
+++ b/lib/core/registry_dynamic.dart
@@ -0,0 +1,77 @@
+library angular.core_dynamic;
+
+import 'dart:mirrors';
+import 'package:angular/core/module.dart';
+
+class DynamicMetadataExtractor implements MetadataExtractor {
+ final _fieldAnnotations = [
+ reflectType(NgAttr),
+ reflectType(NgOneWay),
+ reflectType(NgOneWayOneTime),
+ reflectType(NgTwoWay),
+ reflectType(NgCallback)
+ ];
+
+ Iterable call(Type type) {
+ if (reflectType(type) is TypedefMirror) return [];
+ var metadata = reflectClass(type).metadata;
+ if (metadata == null) {
+ metadata = [];
+ } else {
+ metadata = metadata.map((InstanceMirror im) => map(type, im.reflectee));
+ }
+ return metadata;
+ }
+
+ map(Type type, obj) {
+ if (obj is NgAnnotation) {
+ return mapDirectiveAnnotation(type, obj);
+ } else {
+ return obj;
+ }
+ }
+
+ NgAnnotation mapDirectiveAnnotation(Type type, NgAnnotation annotation) {
+ var match;
+ var fieldMetadata = fieldMetadataExtractor(type);
+ if (fieldMetadata.isNotEmpty) {
+ var newMap = annotation.map == null ? {} : new Map.from(annotation.map);
+ fieldMetadata.forEach((String fieldName, AttrFieldAnnotation ann) {
+ var attrName = ann.attrName;
+ if (newMap.containsKey(attrName)) {
+ throw 'Mapping for attribute $attrName is already defined (while '
+ 'processing annottation for field $fieldName of $type)';
+ }
+ newMap[attrName] = '${ann.mappingSpec}$fieldName';
+ });
+ annotation = annotation.cloneWithNewMap(newMap);
+ }
+ return annotation;
+ }
+
+
+ Map fieldMetadataExtractor(Type type) {
+ ClassMirror cm = reflectType(type);
+ final fields = {};
+ cm.declarations.forEach((Symbol name, DeclarationMirror decl) {
+ if (decl is VariableMirror ||
+ decl is MethodMirror && (decl.isGetter || decl.isSetter)) {
+ var fieldName = MirrorSystem.getName(name);
+ if (decl is MethodMirror && decl.isSetter) {
+ // Remove "=" from the end of the setter.
+ fieldName = fieldName.substring(0, fieldName.length - 1);
+ }
+ decl.metadata.forEach((InstanceMirror meta) {
+ if (_fieldAnnotations.contains(meta.type)) {
+ if (fields.containsKey(fieldName)) {
+ throw 'Attribute annotation for $fieldName is defined more '
+ 'than once in $type';
+ }
+ fields[fieldName] = meta.reflectee as AttrFieldAnnotation;
+ }
+ });
+ }
+ });
+ return fields;
+ }
+}
diff --git a/lib/core/registry_static.dart b/lib/core/registry_static.dart
new file mode 100644
index 000000000..78ad4e9fa
--- /dev/null
+++ b/lib/core/registry_static.dart
@@ -0,0 +1,17 @@
+library angular.core_static;
+
+import 'package:angular/angular.dart';
+import 'package:angular/core/module.dart';
+
+@NgInjectableService()
+class StaticMetadataExtractor extends MetadataExtractor {
+ Map metadataMap;
+ final List empty = const [];
+
+ StaticMetadataExtractor(this.metadataMap);
+
+ Iterable call(Type type) {
+ Iterable i = metadataMap[type];
+ return i == null ? empty : i;
+ }
+}
diff --git a/lib/core/scope.dart b/lib/core/scope.dart
index 707d59568..0246e6bca 100644
--- a/lib/core/scope.dart
+++ b/lib/core/scope.dart
@@ -389,7 +389,7 @@ class ScopeStats {
}
toString() =>
- 'digest #$_digestLoopNo:'
+ '${_digestLoopNo == 1 ? 'digest' : ' '} #$_digestLoopNo:'
'Field: ${_stat(digestFieldStopwatch)} '
'Eval: ${_stat(digestEvalStopwatch)} '
'Process: ${_stat(digestProcessStopwatch)}';
@@ -417,12 +417,14 @@ class RootScope extends Scope {
String _state;
RootScope(Object context, this._astParser, this._parser,
- GetterCache cacheGetter, FilterMap filterMap,
+ FieldGetterFactory fieldGetterFactory, FilterMap filterMap,
this._exceptionHandler, this._ttl, this._zone,
this._scopeStats)
: super(context, null, null,
- new RootWatchGroup(new DirtyCheckingChangeDetector(cacheGetter), context),
- new RootWatchGroup(new DirtyCheckingChangeDetector(cacheGetter), context))
+ new RootWatchGroup(fieldGetterFactory,
+ new DirtyCheckingChangeDetector(fieldGetterFactory), context),
+ new RootWatchGroup(fieldGetterFactory,
+ new DirtyCheckingChangeDetector(fieldGetterFactory), context))
{
_zone.onTurnDone = apply;
_zone.onError = (e, s, ls) => _exceptionHandler(e, s);
diff --git a/lib/core/zone.dart b/lib/core/zone.dart
index 6d5eb3e39..c1c04ac48 100644
--- a/lib/core/zone.dart
+++ b/lib/core/zone.dart
@@ -108,7 +108,7 @@ class NgZone {
/**
* A function called with any errors from the zone.
*/
- var onError = (e, s, ls) => null;
+ var onError = (e, s, ls) => print('$e\n$s\n$ls');
/**
* A function that is called at the end of each VM turn in which the
diff --git a/lib/core_dom/directive_map.dart b/lib/core_dom/directive_map.dart
index affd2fab2..5a740af3a 100644
--- a/lib/core_dom/directive_map.dart
+++ b/lib/core_dom/directive_map.dart
@@ -9,62 +9,8 @@ class DirectiveMap extends AnnotationsMap {
return _selector = _directiveSelectorFactory.selector(this);
}
- DirectiveMap(Injector injector, MetadataExtractor metadataExtractor,
- FieldMetadataExtractor fieldMetadataExtractor,
- this._directiveSelectorFactory)
- : super(injector, metadataExtractor) {
- final directives = >{};
- forEach((NgAnnotation annotation, Type type) {
- var match;
- var fieldMetadata = fieldMetadataExtractor(type);
- if (fieldMetadata.isNotEmpty) {
- var newMap = annotation.map == null ? {} : new Map.from(annotation.map);
- fieldMetadata.forEach((String fieldName, AttrFieldAnnotation ann) {
- var attrName = ann.attrName;
- if (newMap.containsKey(attrName)) {
- throw 'Mapping for attribute $attrName is already defined (while '
- 'processing annottation for field $fieldName of $type)';
- }
- newMap[attrName] = '${ann.mappingSpec}$fieldName';
- });
- annotation = annotation.cloneWithNewMap(newMap);
- }
- directives.putIfAbsent(annotation, () => []).add(type);
- });
- map
- ..clear()
- ..addAll(directives);
- }
-}
-
-@NgInjectableService()
-class FieldMetadataExtractor implements Function {
- final _fieldAnnotations = [reflectType(NgAttr), reflectType(NgOneWay),
- reflectType(NgOneWayOneTime), reflectType(NgTwoWay),
- reflectType(NgCallback)];
-
- Map call(Type type) {
- ClassMirror cm = reflectType(type);
- final fields = {};
- cm.declarations.forEach((Symbol name, DeclarationMirror decl) {
- if (decl is VariableMirror ||
- decl is MethodMirror && (decl.isGetter || decl.isSetter)) {
- var fieldName = MirrorSystem.getName(name);
- if (decl is MethodMirror && decl.isSetter) {
- // Remove "=" from the end of the setter.
- fieldName = fieldName.substring(0, fieldName.length - 1);
- }
- decl.metadata.forEach((InstanceMirror meta) {
- if (_fieldAnnotations.contains(meta.type)) {
- if (fields.containsKey(fieldName)) {
- throw 'Attribute annotation for $fieldName is defined more '
- 'than once in $type';
- }
- fields[fieldName] = meta.reflectee as AttrFieldAnnotation;
- }
- });
- }
- });
- return fields;
- }
+ DirectiveMap(Injector injector,
+ MetadataExtractor metadataExtractor,
+ this._directiveSelectorFactory)
+ : super(injector, metadataExtractor);
}
diff --git a/lib/core_dom/module.dart b/lib/core_dom/module.dart
index 3fc9e2e89..2316df0f6 100644
--- a/lib/core_dom/module.dart
+++ b/lib/core_dom/module.dart
@@ -3,7 +3,6 @@ library angular.core.dom;
import 'dart:async' as async;
import 'dart:convert' show JSON;
import 'dart:html' as dom;
-import 'dart:mirrors';
import 'package:di/di.dart';
import 'package:perf_api/perf_api.dart';
@@ -50,7 +49,6 @@ class NgCoreDomModule extends Module {
type(BrowserCookies);
type(Cookies);
type(LocationWrapper);
- type(FieldMetadataExtractor);
type(DirectiveMap);
type(DirectiveSelectorFactory);
}
diff --git a/lib/metadata.dart b/lib/metadata.dart
new file mode 100644
index 000000000..e69de29bb
diff --git a/lib/mock/http_backend.dart b/lib/mock/http_backend.dart
index 20a8e97a7..5108b8fb4 100644
--- a/lib/mock/http_backend.dart
+++ b/lib/mock/http_backend.dart
@@ -1,4 +1,12 @@
-part of angular.mock;
+library angular.mock.http_backend;
+
+import 'dart:async' as dart_async;
+import 'dart:convert' show JSON;
+import 'dart:html';
+
+import 'package:angular/angular.dart';
+import 'package:angular/utils.dart' as utils;
+
class _MockXhr {
var $$method, $$url, $$async, $$reqHeaders, $$respHeaders;
diff --git a/lib/mock/module.dart b/lib/mock/module.dart
index 92b7386a6..a56a9acc2 100644
--- a/lib/mock/module.dart
+++ b/lib/mock/module.dart
@@ -2,25 +2,23 @@ library angular.mock;
import 'dart:async' as dart_async;
import 'dart:collection' show ListBase;
-import 'dart:convert' show JSON;
import 'dart:html';
import 'dart:js' as js;
import 'package:angular/angular.dart';
-import 'package:angular/utils.dart' as utils;
import 'package:di/di.dart';
-import 'package:di/dynamic_injector.dart';
import 'package:unittest/mock.dart';
+import 'http_backend.dart';
+
+export 'http_backend.dart';
export 'zone.dart';
part 'debug.dart';
part 'exception_handler.dart';
-part 'http_backend.dart';
part 'log.dart';
part 'probe.dart';
part 'test_bed.dart';
part 'mock_window.dart';
-part 'test_injection.dart';
/**
* Use in addition to [AngularModule] in your tests.
diff --git a/lib/mock/test_injection.dart b/lib/mock/test_injection.dart
index 2ca2b6c2c..0d14668b7 100644
--- a/lib/mock/test_injection.dart
+++ b/lib/mock/test_injection.dart
@@ -1,4 +1,9 @@
-part of angular.mock;
+library angular.mock.test_injection;
+
+import 'package:angular/angular_dynamic.dart';
+import 'package:angular/mock/module.dart';
+import 'package:di/di.dart';
+import 'package:di/dynamic_injector.dart';
_SpecInjector _currentSpecInjector = null;
@@ -127,7 +132,9 @@ module(fnOrModule) {
void setUpInjector() {
_currentSpecInjector = new _SpecInjector();
_currentSpecInjector.module((Module m) {
- m..install(new AngularModule())..install(new AngularMockModule());
+ m
+ ..install(new NgDynamicApp().ngModule)
+ ..install(new AngularMockModule());
});
}
diff --git a/lib/playback/playback_http.dart b/lib/playback/playback_http.dart
index f6253a3d9..8b3d6afaa 100644
--- a/lib/playback/playback_http.dart
+++ b/lib/playback/playback_http.dart
@@ -6,7 +6,7 @@ import 'dart:html';
import 'package:angular/core_dom/module.dart';
import 'package:angular/core/service.dart';
-import 'package:angular/mock/module.dart' as mock;
+import 'package:angular/mock/http_backend.dart' as mock;
import 'package:angular/playback/playback_data.dart' as playback_data;
diff --git a/lib/routing/routing.dart b/lib/routing/routing.dart
index 4a26bf6f8..948c3f26a 100644
--- a/lib/routing/routing.dart
+++ b/lib/routing/routing.dart
@@ -137,7 +137,7 @@ class NgRoutingHelper {
});
});
- router.listen(appRoot: _ngApp.root);
+ router.listen(appRoot: _ngApp.element);
}
_reloadViews({Route startingFrom}) {
diff --git a/lib/tools/parser_getter_setter/generator.dart b/lib/tools/parser_getter_setter/generator.dart
index b33244e10..b58c241c3 100644
--- a/lib/tools/parser_getter_setter/generator.dart
+++ b/lib/tools/parser_getter_setter/generator.dart
@@ -58,7 +58,7 @@ class StaticClosureMap extends ClosureMap {
=> _getters[name];
Setter lookupSetter(String name)
=> _setters[name];
- lookupFunction(String name, int arity)
+ lookupFunction(String name, int arity)
=> (arity < _functions.length) ? _functions[arity][name] : null;
}
''';
@@ -84,7 +84,7 @@ class StaticClosureMap extends ClosureMap {
var maxArity = arities.keys.reduce((x, y) => max(x, y));
- var maps = new Iterable.generate(maxArity, (arity) {
+ var maps = new Iterable.generate(maxArity + 1, (arity) {
var names = arities[arity];
if (names == null) {
return '{\n }';
diff --git a/lib/tools/transformer/expression_generator.dart b/lib/tools/transformer/expression_generator.dart
new file mode 100644
index 000000000..2b3a9b284
--- /dev/null
+++ b/lib/tools/transformer/expression_generator.dart
@@ -0,0 +1,188 @@
+library angular.tools.transformer.expression_generator;
+
+import 'dart:async';
+import 'dart:math' as math;
+import 'package:analyzer/src/generated/element.dart';
+import 'package:angular/core/parser/parser.dart';
+import 'package:angular/tools/html_extractor.dart';
+import 'package:angular/tools/parser_getter_setter/generator.dart';
+import 'package:angular/tools/source_crawler.dart';
+import 'package:angular/tools/source_metadata_extractor.dart';
+import 'package:angular/tools/transformer/options.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:di/di.dart';
+import 'package:di/dynamic_injector.dart';
+import 'package:path/path.dart' as path;
+
+/**
+ * Transformer which gathers all expressions from the HTML source files and
+ * Dart source files of an application and packages them for static evaluation.
+ *
+ * This will also modify the main Dart source file to import the generated
+ * expressions and modify all references to NG_EXPRESSION_MODULE to refer to
+ * the generated expressions.
+ */
+class ExpressionGenerator extends Transformer with ResolverTransformer {
+ final TransformOptions options;
+
+ ExpressionGenerator(this.options, Resolvers resolvers) {
+ this.resolvers = resolvers;
+ }
+
+ Future isPrimary(Asset input) => options.isDartEntry(input.id);
+
+ Future applyResolver(Transform transform, Resolver resolver) {
+ var asset = transform.primaryInput;
+ var outputBuffer = new StringBuffer();
+
+ _writeStaticExpressionHeader(asset.id, outputBuffer);
+
+ var sourceMetadataExtractor = new SourceMetadataExtractor();
+ var directives =
+ sourceMetadataExtractor.gatherDirectiveInfo(null,
+ new _LibrarySourceCrawler(resolver.libraries));
+
+ var htmlExtractor = new HtmlExpressionExtractor(directives);
+ return _getHtmlSources(transform)
+ .forEach(htmlExtractor.parseHtml)
+ .then((_) {
+ var module = new Module()
+ ..type(Parser, implementedBy: DynamicParser)
+ ..type(ParserBackend, implementedBy: DartGetterSetterGen);
+ var injector =
+ new DynamicInjector(modules: [module], allowImplicitInjection: true);
+
+ injector.get(_ParserGetterSetter).generateParser(
+ htmlExtractor.expressions.toList(), outputBuffer);
+
+ var id = transform.primaryInput.id;
+ var outputFilename = '${path.url.basenameWithoutExtension(id.path)}'
+ '_static_expressions.dart';
+ var outputPath = path.url.join(path.url.dirname(id.path), outputFilename);
+ var outputId = new AssetId(id.package, outputPath);
+ transform.addOutput(
+ new Asset.fromString(outputId, outputBuffer.toString()));
+
+ transform.addOutput(asset);
+ });
+ }
+
+ /**
+ * Gets a stream consisting of the contents of all HTML source files to be
+ * scoured for expressions.
+ */
+ Stream _getHtmlSources(Transform transform) {
+ var controller = new StreamController();
+ if (options.htmlFiles == null) {
+ controller.close();
+ return controller.stream;
+ }
+ Future.wait(options.htmlFiles.map((path) {
+ var htmlId = new AssetId(transform.primaryInput.id.package, path);
+ return transform.readInputAsString(htmlId);
+ }).map((future) {
+ return future.then(controller.add).catchError(controller.addError);
+ })).then((_) {
+ controller.close();
+ });
+ return controller.stream;
+ }
+}
+
+void _writeStaticExpressionHeader(AssetId id, StringSink sink) {
+ var libPath = path.withoutExtension(id.path).replaceAll('/', '.');
+ sink.write('''
+library ${id.package}.$libPath.generated_expressions;
+
+import 'package:angular/angular.dart';
+import 'package:angular/core/parser/dynamic_parser.dart' show ClosureMap;
+
+Module get expressionModule => new Module()
+ ..value(ClosureMap, new StaticClosureMap());
+
+''');
+}
+
+class _LibrarySourceCrawler implements SourceCrawler {
+ final List libraries;
+ _LibrarySourceCrawler(this.libraries);
+
+ void crawl(String entryPoint, CompilationUnitVisitor visitor) {
+ libraries.expand((lib) => lib.units)
+ .map((compilationUnitElement) => compilationUnitElement.node)
+ .forEach(visitor);
+ }
+}
+
+class _ParserGetterSetter {
+ final Parser parser;
+ final ParserBackend backend;
+ _ParserGetterSetter(this.parser, this.backend);
+
+ generateParser(List exprs, StringSink sink) {
+ exprs.forEach((expr) {
+ try {
+ parser(expr);
+ } catch (e) {
+ // Ignore exceptions.
+ }
+ });
+
+ DartGetterSetterGen backend = this.backend;
+ sink.write(generateClosureMap(backend.properties, backend.calls));
+ }
+
+ String generateClosureMap(Set properties,
+ Map> calls) {
+ return '''
+class StaticClosureMap extends ClosureMap {
+ Getter lookupGetter(String name) => getters[name];
+ Setter lookupSetter(String name) => setters[name];
+ lookupFunction(String name, int arity)
+ => (arity < functions.length) ? functions[arity][name] : null;
+}
+
+final Map getters = ${generateGetterMap(properties)};
+final Map setters = ${generateSetterMap(properties)};
+final List