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

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

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 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
« no previous file with comments | « packages/polymer_expressions/lib/async.dart ('k') | packages/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/polymer_expressions/lib/eval.dart
diff --git a/packages/polymer_expressions/lib/eval.dart b/packages/polymer_expressions/lib/eval.dart
deleted file mode 100644
index 8f7988b662808aac3ea97ceeb8aeeab37d122729..0000000000000000000000000000000000000000
--- a/packages/polymer_expressions/lib/eval.dart
+++ /dev/null
@@ -1,823 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library polymer_expressions.eval;
-
-import 'dart:async';
-import 'dart:collection';
-
-import 'package:observe/observe.dart';
-import 'package:smoke/smoke.dart' as smoke;
-
-import 'async.dart';
-import 'expression.dart';
-import 'filter.dart';
-import 'visitor.dart';
-
-final _BINARY_OPERATORS = {
- '+': (a, b) => a + b,
- '-': (a, b) => a - b,
- '*': (a, b) => a * b,
- '/': (a, b) => a / b,
- '%': (a, b) => a % b,
- '==': (a, b) => a == b,
- '!=': (a, b) => a != b,
- '===': (a, b) => identical(a, b),
- '!==': (a, b) => !identical(a, b),
- '>': (a, b) => a > b,
- '>=': (a, b) => a >= b,
- '<': (a, b) => a < b,
- '<=': (a, b) => a <= b,
- '||': (a, b) => a || b,
- '&&': (a, b) => a && b,
- '|': (a, f) {
- if (f is Transformer) return f.forward(a);
- if (f is Filter) return f(a);
- throw new EvalException("Filters must be a one-argument function.");
- }
-};
-
-final _UNARY_OPERATORS = {
- '+': (a) => a,
- '-': (a) => -a,
- '!': (a) => !a,
-};
-
-final _BOOLEAN_OPERATORS = ['!', '||', '&&'];
-
-/**
- * Evaluation [expr] in the context of [scope].
- */
-Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr);
-
-/**
- * Returns an [ExpressionObserver] that evaluates [expr] in the context of
- * scope] and listens for any changes on [Observable] values that are
- * returned from sub-expressions. When a value changes the expression is
- * reevaluated and the new result is sent to the [onUpdate] stream of the
- * [ExpressionObsserver].
- */
-ExpressionObserver observe(Expression expr, Scope scope) {
- var observer = new ObserverBuilder().visit(expr);
- return observer;
-}
-
-/**
- * Causes [expr] to be reevaluated a returns it's value.
- */
-Object update(ExpressionObserver expr, Scope scope, {skipChanges: false}) {
- new Updater(scope, skipChanges).visit(expr);
- return expr.currentValue;
-}
-
-/**
- * Assign [value] to the variable or field referenced by [expr] in the context
- * of [scope].
- *
- * [expr] must be an /assignable/ expression, it must not contain
- * operators or function invocations, and any index operations must use a
- * literal index.
- */
-Object assign(Expression expr, Object value, Scope scope,
- {bool checkAssignability: true}) {
-
- Expression expression;
- var property;
- bool isIndex = false;
- var filters = <Expression>[]; // reversed order for assignment
-
- while (expr is BinaryOperator) {
- BinaryOperator op = expr;
- if (op.operator != '|') {
- break;
- }
- filters.add(op.right);
- expr = op.left;
- }
-
- if (expr is Identifier) {
- expression = empty();
- property = expr.value;
- } else if (expr is Index) {
- expression = expr.receiver;
- property = expr.argument;
- isIndex = true;
- } else if (expr is Getter) {
- expression = expr.receiver;
- property = expr.name;
- } else {
- if (checkAssignability) {
- throw new EvalException("Expression is not assignable: $expr");
- }
- return null;
- }
-
- // transform the values backwards through the filters
- for (var filterExpr in filters) {
- var filter = eval(filterExpr, scope);
- if (filter is! Transformer) {
- if (checkAssignability) {
- throw new EvalException("filter must implement Transformer to be "
- "assignable: $filterExpr");
- } else {
- return null;
- }
- }
- value = filter.reverse(value);
- }
- // evaluate the receiver
- var o = eval(expression, scope);
-
- // can't assign to a property on a null LHS object. Silently fail.
- if (o == null) return null;
-
- if (isIndex) {
- var index = eval(property, scope);
- o[index] = value;
- } else {
- smoke.write(o, smoke.nameToSymbol(property), value);
- }
- return value;
-}
-
-
-/**
- * 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 local variables, then in global variables,
- * and then finally looks up the name as a property in the model.
- */
-abstract class Scope implements Indexable<String, Object> {
- Scope._();
-
- /** Create a scope containing a [model] and all of [variables]. */
- factory Scope({Object model, Map<String, Object> variables}) {
- var scope = new _ModelScope(model);
- return variables == null ? scope
- : new _GlobalsScope(new Map<String, Object>.from(variables), 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);
-
- operator []=(String name, Object value) {
- throw new UnsupportedError('[]= is not supported in Scope.');
- }
-
- /**
- * Returns whether [name] is defined in [model], that is, a lookup
- * would not find a variable with that name, but there is a non-null model
- * where we can look it up as a property.
- */
- bool _isModelProperty(String name);
-
- /** Create a new scope extending this scope with an additional variable. */
- Scope childScope(String name, Object value) =>
- new _LocalVariableScope(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;
-
- _ModelScope(this.model) : super._();
-
- Object operator[](String name) {
- 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 _isModelProperty(String name) => name != 'this';
-
- String toString() => "[model: $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 [_LocalVariableScope] is created.
- */
-class _LocalVariableScope extends Scope {
- final Scope parent;
- final String varName;
- // TODO(sigmund,justinfagnani): make this @observable?
- final Object value;
-
- _LocalVariableScope(this.varName, this.value, this.parent) : super._() {
- if (varName == 'this') {
- throw new EvalException("'this' cannot be used as a variable name.");
- }
- }
-
- 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");
- }
-
- bool _isModelProperty(String name) {
- if (varName == name) return false;
- return parent == null ? false : parent._isModelProperty(name);
- }
-
- String toString() => "$parent > [local: $varName]";
-}
-
-/** A scope that holds a reference to a global variables. */
-class _GlobalsScope extends Scope {
- final _ModelScope parent;
- final Map<String, Object> variables;
-
- _GlobalsScope(this.variables, this.parent) : super._() {
- if (variables.containsKey('this')) {
- throw new EvalException("'this' cannot be used as a variable name.");
- }
- }
-
- Object get model => parent != null ? parent.model : null;
-
- Object operator[](String name) {
- if (variables.containsKey(name)) return _convert(variables[name]);
- if (parent != null) return parent[name];
- throw new EvalException("variable '$name' not found");
- }
-
- bool _isModelProperty(String name) {
- if (variables.containsKey(name)) return false;
- return parent == null ? false : parent._isModelProperty(name);
- }
-
- String toString() => "$parent > [global: ${variables.keys}]";
-}
-
-Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
-
-abstract class ExpressionObserver<E extends Expression> implements Expression {
- final E _expr;
- ExpressionObserver _parent;
-
- StreamSubscription _subscription;
- Object _value;
-
- StreamController _controller = new StreamController.broadcast();
- Stream get onUpdate => _controller.stream;
-
- ExpressionObserver(this._expr);
-
- Expression get expression => _expr;
-
- Object get currentValue => _value;
-
- update(Scope scope) => _updateSelf(scope);
-
- _updateSelf(Scope scope) {}
-
- _invalidate(Scope scope) {
- _observe(scope, false);
- if (_parent != null) {
- _parent._invalidate(scope);
- }
- }
-
- _unobserve() {
- if (_subscription != null) {
- _subscription.cancel();
- _subscription = null;
- }
- }
-
- _observe(Scope scope, skipChanges) {
- _unobserve();
-
- var _oldValue = _value;
-
- // evaluate
- _updateSelf(scope);
-
- if (!skipChanges && !identical(_value, _oldValue)) {
- _controller.add(_value);
- }
- }
-
- String toString() => _expr.toString();
-}
-
-class Updater extends RecursiveVisitor {
- final Scope scope;
- final bool skipChanges;
-
- Updater(this.scope, [this.skipChanges = false]);
-
- visitExpression(ExpressionObserver e) {
- e._observe(scope, skipChanges);
- }
-}
-
-class Closer extends RecursiveVisitor {
- static final _instance = new Closer._();
- factory Closer() => _instance;
- Closer._();
-
- visitExpression(ExpressionObserver e) {
- e._unobserve();
- }
-}
-
-class EvalVisitor extends Visitor {
- final Scope scope;
-
- EvalVisitor(this.scope);
-
- visitEmptyExpression(EmptyExpression e) => scope.model;
-
- visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
-
- visitGetter(Getter g) {
- var receiver = visit(g.receiver);
- if (receiver == null) return null;
- var symbol = smoke.nameToSymbol(g.name);
- return smoke.read(receiver, symbol);
- }
-
- visitIndex(Index i) {
- var receiver = visit(i.receiver);
- if (receiver == null) return null;
- var key = visit(i.argument);
- return receiver[key];
- }
-
- visitInvoke(Invoke i) {
- var receiver = visit(i.receiver);
- if (receiver == null) return null;
- var args = (i.arguments == null)
- ? null
- : i.arguments.map(visit).toList(growable: false);
-
- if (i.method == null) {
- assert(receiver is Function);
- return Function.apply(receiver, args);
- }
-
- var symbol = smoke.nameToSymbol(i.method);
- return smoke.invoke(receiver, symbol, args);
- }
-
- visitLiteral(Literal l) => l.value;
-
- visitListLiteral(ListLiteral l) => l.items.map(visit).toList();
-
- visitMapLiteral(MapLiteral l) {
- var map = {};
- for (var entry in l.entries) {
- var key = visit(entry.key);
- var value = visit(entry.entryValue);
- map[key] = value;
- }
- return map;
- }
-
- visitMapLiteralEntry(MapLiteralEntry e) =>
- throw new UnsupportedError("should never be called");
-
- visitIdentifier(Identifier i) => scope[i.value];
-
- visitBinaryOperator(BinaryOperator o) {
- var operator = o.operator;
- var left = visit(o.left);
- var right = visit(o.right);
-
- var f = _BINARY_OPERATORS[operator];
- if (operator == '&&' || operator == '||') {
- // TODO: short-circuit
- return f(_toBool(left), _toBool(right));
- } else if (operator == '==' || operator == '!=') {
- return f(left, right);
- } else if (left == null || right == null) {
- return null;
- }
- return f(left, right);
- }
-
- visitUnaryOperator(UnaryOperator o) {
- var expr = visit(o.child);
- var f = _UNARY_OPERATORS[o.operator];
- if (o.operator == '!') {
- return f(_toBool(expr));
- }
- return (expr == null) ? null : f(expr);
- }
-
- visitTernaryOperator(TernaryOperator o) =>
- visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr);
-
- visitInExpression(InExpression i) =>
- throw new UnsupportedError("can't eval an 'in' expression");
-
- visitAsExpression(AsExpression i) =>
- throw new UnsupportedError("can't eval an 'as' expression");
-}
-
-class ObserverBuilder extends Visitor {
- final Queue parents = new Queue();
-
- ObserverBuilder();
-
- visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
-
- visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
-
- visitGetter(Getter g) {
- var receiver = visit(g.receiver);
- var getter = new GetterObserver(g, receiver);
- receiver._parent = getter;
- return getter;
- }
-
- visitIndex(Index i) {
- var receiver = visit(i.receiver);
- var arg = visit(i.argument);
- var index = new IndexObserver(i, receiver, arg);
- receiver._parent = index;
- arg._parent = index;
- return index;
- }
-
- visitInvoke(Invoke i) {
- var receiver = visit(i.receiver);
- var args = (i.arguments == null)
- ? null
- : i.arguments.map(visit).toList(growable: false);
- var invoke = new InvokeObserver(i, receiver, args);
- receiver._parent = invoke;
- if (args != null) args.forEach((a) => a._parent = invoke);
- return invoke;
- }
-
- visitLiteral(Literal l) => new LiteralObserver(l);
-
- visitListLiteral(ListLiteral l) {
- var items = l.items.map(visit).toList(growable: false);
- var list = new ListLiteralObserver(l, items);
- items.forEach((e) => e._parent = list);
- return list;
- }
-
- visitMapLiteral(MapLiteral l) {
- var entries = l.entries.map(visit).toList(growable: false);
- var map = new MapLiteralObserver(l, entries);
- entries.forEach((e) => e._parent = map);
- return map;
- }
-
- visitMapLiteralEntry(MapLiteralEntry e) {
- var key = visit(e.key);
- var value = visit(e.entryValue);
- var entry = new MapLiteralEntryObserver(e, key, value);
- key._parent = entry;
- value._parent = entry;
- return entry;
- }
-
- visitIdentifier(Identifier i) => new IdentifierObserver(i);
-
- visitBinaryOperator(BinaryOperator o) {
- var left = visit(o.left);
- var right = visit(o.right);
- var binary = new BinaryObserver(o, left, right);
- left._parent = binary;
- right._parent = binary;
- return binary;
- }
-
- visitUnaryOperator(UnaryOperator o) {
- var expr = visit(o.child);
- var unary = new UnaryObserver(o, expr);
- expr._parent = unary;
- return unary;
- }
-
- visitTernaryOperator(TernaryOperator o) {
- var condition = visit(o.condition);
- var trueExpr = visit(o.trueExpr);
- var falseExpr = visit(o.falseExpr);
- var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr);
- condition._parent = ternary;
- trueExpr._parent = ternary;
- falseExpr._parent = ternary;
- return ternary;
- }
-
- visitInExpression(InExpression i) {
- throw new UnsupportedError("can't eval an 'in' expression");
- }
-
- visitAsExpression(AsExpression i) {
- throw new UnsupportedError("can't eval an 'as' expression");
- }
-}
-
-class EmptyObserver extends ExpressionObserver<EmptyExpression>
- implements EmptyExpression {
-
- EmptyObserver(EmptyExpression value) : super(value);
-
- _updateSelf(Scope scope) {
- _value = scope.model;
- // TODO(justin): listen for scope.model changes?
- }
-
- accept(Visitor v) => v.visitEmptyExpression(this);
-}
-
-class LiteralObserver extends ExpressionObserver<Literal> implements Literal {
-
- LiteralObserver(Literal value) : super(value);
-
- dynamic get value => _expr.value;
-
- _updateSelf(Scope scope) {
- _value = _expr.value;
- }
-
- accept(Visitor v) => v.visitLiteral(this);
-}
-
-class ListLiteralObserver extends ExpressionObserver<ListLiteral>
- implements ListLiteral {
-
- final List<ExpressionObserver> items;
-
- ListLiteralObserver(ListLiteral value, this.items) : super(value);
-
- _updateSelf(Scope scope) {
- _value = items.map((i) => i._value).toList();
- }
-
- accept(Visitor v) => v.visitListLiteral(this);
-}
-
-class MapLiteralObserver extends ExpressionObserver<MapLiteral>
- implements MapLiteral {
-
- final List<MapLiteralEntryObserver> entries;
-
- MapLiteralObserver(MapLiteral value, this.entries) : super(value);
-
- _updateSelf(Scope scope) {
- _value = entries.fold(new Map(),
- (m, e) => m..[e.key._value] = e.entryValue._value);
- }
-
- accept(Visitor v) => v.visitMapLiteral(this);
-}
-
-class MapLiteralEntryObserver extends ExpressionObserver<MapLiteralEntry>
- implements MapLiteralEntry {
-
- final LiteralObserver key;
- final ExpressionObserver entryValue;
-
- MapLiteralEntryObserver(MapLiteralEntry value, this.key, this.entryValue)
- : super(value);
-
- accept(Visitor v) => v.visitMapLiteralEntry(this);
-}
-
-class IdentifierObserver extends ExpressionObserver<Identifier>
- implements Identifier {
-
- IdentifierObserver(Identifier value) : super(value);
-
- String get value => _expr.value;
-
- _updateSelf(Scope scope) {
- _value = scope[value];
- if (!scope._isModelProperty(value)) return;
- var model = scope.model;
- if (model is! Observable) return;
- var symbol = smoke.nameToSymbol(value);
- _subscription = (model as Observable).changes.listen((changes) {
- if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
- _invalidate(scope);
- }
- });
- }
-
- accept(Visitor v) => v.visitIdentifier(this);
-}
-
-class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
- implements ParenthesizedExpression {
- final ExpressionObserver child;
-
- ParenthesizedObserver(ParenthesizedExpression expr, this.child) : super(expr);
-
-
- _updateSelf(Scope scope) {
- _value = child._value;
- }
-
- accept(Visitor v) => v.visitParenthesizedExpression(this);
-}
-
-class UnaryObserver extends ExpressionObserver<UnaryOperator>
- implements UnaryOperator {
- final ExpressionObserver child;
-
- UnaryObserver(UnaryOperator expr, this.child) : super(expr);
-
- String get operator => _expr.operator;
-
- _updateSelf(Scope scope) {
- var f = _UNARY_OPERATORS[_expr.operator];
- if (operator == '!') {
- _value = f(_toBool(child._value));
- } else {
- _value = (child._value == null) ? null : f(child._value);
- }
- }
-
- accept(Visitor v) => v.visitUnaryOperator(this);
-}
-
-class BinaryObserver extends ExpressionObserver<BinaryOperator>
- implements BinaryOperator {
-
- final ExpressionObserver left;
- final ExpressionObserver right;
-
- BinaryObserver(BinaryOperator expr, this.left, this.right)
- : super(expr);
-
- String get operator => _expr.operator;
-
- _updateSelf(Scope scope) {
- var f = _BINARY_OPERATORS[operator];
- if (operator == '&&' || operator == '||') {
- _value = f(_toBool(left._value), _toBool(right._value));
- } else if (operator == '==' || operator == '!=') {
- _value = f(left._value, right._value);
- } else if (left._value == null || right._value == null) {
- _value = null;
- } else {
- if (operator == '|' && left._value is ObservableList) {
- _subscription = (left._value as ObservableList).listChanges
- .listen((_) => _invalidate(scope));
- }
- _value = f(left._value, right._value);
- }
- }
-
- accept(Visitor v) => v.visitBinaryOperator(this);
-
-}
-
-class TernaryObserver extends ExpressionObserver<TernaryOperator>
- implements TernaryOperator {
-
- final ExpressionObserver condition;
- final ExpressionObserver trueExpr;
- final ExpressionObserver falseExpr;
-
- TernaryObserver(TernaryOperator expr, this.condition, this.trueExpr,
- this.falseExpr) : super(expr);
-
- _updateSelf(Scope scope) {
- _value = _toBool(condition._value) ? trueExpr._value : falseExpr._value;
- }
-
- accept(Visitor v) => v.visitTernaryOperator(this);
-
-}
-
-class GetterObserver extends ExpressionObserver<Getter> implements Getter {
- final ExpressionObserver receiver;
-
- GetterObserver(Expression expr, this.receiver) : super(expr);
-
- String get name => _expr.name;
-
- _updateSelf(Scope scope) {
- var receiverValue = receiver._value;
- if (receiverValue == null) {
- _value = null;
- return;
- }
- var symbol = smoke.nameToSymbol(_expr.name);
- _value = smoke.read(receiverValue, symbol);
-
- if (receiverValue is Observable) {
- _subscription = (receiverValue as Observable).changes.listen((changes) {
- if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
- _invalidate(scope);
- }
- });
- }
- }
-
- accept(Visitor v) => v.visitGetter(this);
-}
-
-class IndexObserver extends ExpressionObserver<Index> implements Index {
- final ExpressionObserver receiver;
- final ExpressionObserver argument;
-
- IndexObserver(Expression expr, this.receiver, this.argument) : super(expr);
-
- _updateSelf(Scope scope) {
- var receiverValue = receiver._value;
- if (receiverValue == null) {
- _value = null;
- return;
- }
- var key = argument._value;
- _value = receiverValue[key];
-
- if (receiverValue is ObservableList) {
- _subscription = (receiverValue as ObservableList).listChanges
- .listen((changes) {
- if (changes.any((c) => c.indexChanged(key))) _invalidate(scope);
- });
- } else if (receiverValue is Observable) {
- _subscription = (receiverValue as Observable).changes.listen((changes) {
- if (changes.any((c) => c is MapChangeRecord && c.key == key)) {
- _invalidate(scope);
- }
- });
- }
- }
-
- accept(Visitor v) => v.visitIndex(this);
-}
-
-class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke {
- final ExpressionObserver receiver;
- final List<ExpressionObserver> arguments;
-
- InvokeObserver(Expression expr, this.receiver, this.arguments)
- : super(expr) {
- assert(arguments != null);
- }
-
- String get method => _expr.method;
-
- _updateSelf(Scope scope) {
- var args = arguments.map((a) => a._value).toList();
- var receiverValue = receiver._value;
- if (receiverValue == null) {
- _value = null;
- return;
- }
- if (_expr.method == null) {
- // top-level function or model method
- // TODO(justin): listen to model changes to see if the method has
- // changed? listen to the scope to see if the top-level method has
- // changed?
- assert(receiverValue is Function);
- _value = _convert(Function.apply(receiverValue, args));
- } else {
- var symbol = smoke.nameToSymbol(_expr.method);
- _value = smoke.invoke(receiverValue, symbol, args);
-
- if (receiverValue is Observable) {
- _subscription = (receiverValue as Observable).changes.listen(
- (List<ChangeRecord> changes) {
- if (changes.any(
- (c) => c is PropertyChangeRecord && c.name == symbol)) {
- _invalidate(scope);
- }
- });
- }
- }
- }
-
- accept(Visitor v) => v.visitInvoke(this);
-}
-
-_toBool(v) => (v == null) ? false : v;
-
-class EvalException implements Exception {
- final String message;
- EvalException(this.message);
- String toString() => "EvalException: $message";
-}
« no previous file with comments | « packages/polymer_expressions/lib/async.dart ('k') | packages/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698