Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Unified Diff: pkg/polymer_expressions/lib/eval.dart

Issue 207433002: Changes in polymer-expressions to prepare for codegen in polymer: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/polymer_expressions/lib/eval.dart
diff --git a/pkg/polymer_expressions/lib/eval.dart b/pkg/polymer_expressions/lib/eval.dart
index 7b925a724cac8f21b9c04427bfc9def3fa3466cd..1cd12b1156d29e878060b8413cdc0ebcd05981d5 100644
--- a/pkg/polymer_expressions/lib/eval.dart
+++ b/pkg/polymer_expressions/lib/eval.dart
@@ -7,11 +7,6 @@ library polymer_expressions.eval;
import 'dart:async';
import 'dart:collection';
-@MirrorsUsed(
- metaTargets: const [Reflectable, ObservableProperty],
- override: 'smoke.mirrors')
-import 'dart:mirrors' show MirrorsUsed;
-
import 'package:observe/observe.dart';
import 'package:smoke/smoke.dart' as smoke;
@@ -147,74 +142,107 @@ void assign(Expression expr, Object value, Scope scope) {
}
}
+
/**
- * A mapping of names to objects. Scopes contain a set of named [variables] and
- * a single [model] object (which can be thought of as the "this" reference).
- * Names are currently looked up in [variables] first, then the [model].
- *
- * Scopes can be nested by giving them a [parent]. If a name in not found in a
- * Scope, it will look for it in it's parent.
+ * A scope in polymer expressions that can map names to objects. Scopes contain
+ * a set of named variables and a unique model object. The scope structure
+ * is then used to lookup names using the `[]` operator. The lookup first
+ * searches for the name in the variable set (even if these are global
+ * variables), otherwise it looks up the name as a property in the model.
*/
-class Scope {
- final Scope parent;
+abstract class Scope {
+ Scope._();
+
+ /** Create a scope containing a [model] and all of [variables]. */
+ factory Scope({Object model, Map<String, Object> variables}) {
+ var scope = new _ModelScope(model);
+ if (variables == null) return scope;
+ variables.forEach((name, value) {
+ scope = new _VariableScope(name, value, scope);
Jennifer Messerly 2014/03/21 20:08:54 hmmm. Is the Map used for globals? We might want s
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 good point. Added _GlobalsScope for this.
+ });
+ return scope;
+ }
+
+ /** Return the unique model in this scope. */
+ Object get model;
+
+ /**
+ * Lookup the value of [name] in the current scope. If [name] is 'this', then
+ * we return the [model]. For any other name, this finds the first variable
+ * matching [name] or, if none exists, the property [name] in the [model].
+ */
+ Object operator[](String name);
+
+ /**
+ * Return the object that defines the value of [name]. The result may be a
+ * [_VariableScope] if it is a variable on this scope, [model] if it is a
+ * member of the model object, a [_ModelScope] if name is 'this', or null if
+ * the name can't be found and [model] is null.
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 Justin - a general question here: I was wondering
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 BWT - after adding the GlobalsScope and with the d
+ */
+ Object _ownerOf(String name);
+
+ /** Create a new scope extending this scope with an additional variable. */
+ Scope childScope(String name, Object value) =>
+ new _VariableScope(name, value, this);
+}
+
+/**
+ * A scope that looks up names in a model object. This kind of scope has no
+ * parent scope because all our lookup operations stop when we reach the model
+ * object. Any variables added in scope or global variables are added as child
+ * scopes.
+ */
+class _ModelScope extends Scope {
final Object model;
- // TODO(justinfagnani): disallow adding/removing names
- final ObservableMap<String, Object> _variables;
- Scope({this.model, Map<String, Object> variables, this.parent})
- : _variables = new ObservableMap.from(variables == null ? {} : variables);
+ _ModelScope(this.model) : super._();
Object operator[](String name) {
- if (name == 'this') {
- return model;
- } else if (_variables.containsKey(name)) {
- return _convert(_variables[name]);
- } else {
- var symbol = smoke.nameToSymbol(name);
- if (model != null && smoke.hasGetter(model.runtimeType, symbol)) {
- return _convert(smoke.read(model, symbol));
- }
- }
- if (parent != null) {
- return _convert(parent[name]);
- } else {
+ if (name == 'this') return model;
+ var symbol = smoke.nameToSymbol(name);
+ if (model == null || symbol == null) {
throw new EvalException("variable '$name' not found");
}
+ return _convert(smoke.read(model, symbol));
}
- Object ownerOf(String name) {
- if (name == 'this') {
- // we could return the Scope if it were Observable, but since assigning
- // a model to a template destroys and recreates the instance, it doesn't
- // seem neccessary
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 (see comment re _ownerOf)
- return null;
- } else if (_variables.containsKey(name)) {
- return _variables;
- } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) {
- return model;
- }
- if (parent != null) {
- return parent.ownerOf(name);
+ Object _ownerOf(String name) => name == 'this' ? this : model;
+}
+
+/**
+ * A scope that holds a reference to a single variable. Polymer expressions
+ * introduce variables to the scope one at a time. Each time a variable is
+ * added, a new [_VariableScope] is created.
+ */
+class _VariableScope extends Scope {
+ final Scope parent;
+ final String varName;
+ // TODO(sigmund,justinfagnani): make this @observable?
+ final Object value;
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 I had this with @observable first (matching what y
Jennifer Messerly 2014/03/21 20:08:54 Not sure I understand the TODO. Shouldn't values a
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 I was thinking if we were to make those constructs
Jennifer Messerly 2014/03/22 00:37:56 It doesn't currently reuse nodes if the model chan
+
+ _VariableScope(this.varName, this.value, this.parent) : super._() {
+ if (varName == 'this') {
+ throw new EvalException("'this' cannot be used as a variable name.");
}
}
- bool contains(String name) {
- if (_variables.containsKey(name) ||
- smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) {
- return true;
- }
- if (parent != null) {
- return parent.contains(name);
- }
- return false;
+ Object get model => parent != null ? parent.model : null;
+
+ Object operator[](String name) {
+ if (varName == name) return _convert(value);
+ if (parent != null) return parent[name];
+ throw new EvalException("variable '$name' not found");
}
-}
-Object _convert(v) {
- if (v is Stream) return new StreamBinding(v);
- return v;
+ Object _ownerOf(String name) {
+ if (varName == name) return this;
+ if (parent != null) return parent._ownerOf(name);
+ return null;
+ }
}
+Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
+
abstract class ExpressionObserver<E extends Expression> implements Expression {
final E _expr;
ExpressionObserver _parent;
@@ -456,12 +484,11 @@ class IdentifierObserver extends ExpressionObserver<Identifier>
_updateSelf(Scope scope) {
_value = scope[value];
- var owner = scope.ownerOf(value);
+ var owner = scope._ownerOf(value);
if (owner is Observable) {
var symbol = smoke.nameToSymbol(value);
_subscription = (owner as Observable).changes.listen((changes) {
- if (changes.any(
- (c) => c is PropertyChangeRecord && c.name == symbol)) {
+ if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
_invalidate(scope);
}
});

Powered by Google App Engine
This is Rietveld 408576698