Skip to content

Commit c58318a

Browse files
author
John Messerly
committed
fixes #130, accept anything with iterator in for loops
[email protected] Review URL: https://codereview.chromium.org/1126713002
1 parent 1a0efac commit c58318a

File tree

5 files changed

+102
-30
lines changed

5 files changed

+102
-30
lines changed

pkg/dev_compiler/lib/runtime/dart/collection.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ var collection;
109109
let _newSet = Symbol('_newSet');
110110
let SetMixin$ = dart.generic(function(E) {
111111
class SetMixin extends core.Object {
112+
[Symbol.iterator]() {
113+
return new dart.JsIterator(this[core.$iterator]);
114+
}
112115
get [core.$isEmpty]() {
113116
return this[core.$length] == 0;
114117
}
@@ -454,6 +457,9 @@ var collection;
454457
result.add(e);
455458
return result;
456459
}
460+
[Symbol.iterator]() {
461+
return new dart.JsIterator(this[core.$iterator]);
462+
}
457463
}
458464
HashSet[dart.implements] = () => [core.Set$(E)];
459465
dart.defineNamedConstructor(HashSet, 'identity');
@@ -677,6 +683,9 @@ var collection;
677683
toString() {
678684
return IterableBase.iterableToShortString(this, '(', ')');
679685
}
686+
[Symbol.iterator]() {
687+
return new dart.JsIterator(this[core.$iterator]);
688+
}
680689
}
681690
IterableMixin[dart.implements] = () => [core.Iterable$(E)];
682691
return IterableMixin;
@@ -1020,6 +1029,9 @@ var collection;
10201029
parts[core.$add](penultimateString);
10211030
parts[core.$add](ultimateString);
10221031
}
1032+
[Symbol.iterator]() {
1033+
return new dart.JsIterator(this[core.$iterator]);
1034+
}
10231035
}
10241036
IterableBase[dart.implements] = () => [core.Iterable$(E)];
10251037
dart.defineLazyProperties(IterableBase, {
@@ -1185,6 +1197,9 @@ var collection;
11851197
}
11861198
return result;
11871199
}
1200+
[Symbol.iterator]() {
1201+
return new dart.JsIterator(this[core.$iterator]);
1202+
}
11881203
}
11891204
LinkedHashSet[dart.implements] = () => [HashSet$(E)];
11901205
dart.defineNamedConstructor(LinkedHashSet, 'identity');
@@ -1396,6 +1411,9 @@ var collection;
13961411
get [core.$iterator]() {
13971412
return new (_internal.ListIterator$(E))(this);
13981413
}
1414+
[Symbol.iterator]() {
1415+
return new dart.JsIterator(this[core.$iterator]);
1416+
}
13991417
[core.$elementAt](index) {
14001418
return this[core.$get](index);
14011419
}
@@ -2212,6 +2230,9 @@ var collection;
22122230
from(elements) {
22132231
return new ListQueue$(E).from(elements);
22142232
}
2233+
[Symbol.iterator]() {
2234+
return new dart.JsIterator(this[core.$iterator]);
2235+
}
22152236
}
22162237
Queue[dart.implements] = () => [core.Iterable$(E), _internal.EfficientLength];
22172238
dart.defineNamedConstructor(Queue, 'from');

pkg/dev_compiler/lib/runtime/dart/core.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ var core;
10851085
return dart.notNull(this.isGetter) || dart.notNull(this.isSetter);
10861086
}
10871087
}
1088+
let $iterator = dart.JsSymbol('$iterator');
10881089
let $join = dart.JsSymbol('$join');
10891090
let Iterable$ = dart.generic(function(E) {
10901091
class Iterable extends Object {
@@ -1097,22 +1098,16 @@ var core;
10971098
return new (_internal.EmptyIterable$(E))();
10981099
return new (exports._GeneratorIterable$(E))(count, generator);
10991100
}
1101+
[dart.JsSymbol.iterator]() {
1102+
return new dart.JsIterator(this[$iterator]);
1103+
}
11001104
[$join](separator) {
11011105
if (separator === void 0)
11021106
separator = "";
11031107
let buffer = new StringBuffer();
11041108
buffer.writeAll(this, separator);
11051109
return dart.toString(buffer);
11061110
}
1107-
[dart.JsSymbol.iterator]() {
1108-
var iterator = this.iterator;
1109-
return {
1110-
next() {
1111-
var done = iterator.moveNext();
1112-
return {done: done, current: done ? void 0 : iterator.current};
1113-
}
1114-
};
1115-
}
11161111
}
11171112
dart.defineNamedConstructor(Iterable, 'generate');
11181113
return Iterable;
@@ -1126,7 +1121,6 @@ var core;
11261121
let _end = dart.JsSymbol('_end');
11271122
let _start = dart.JsSymbol('_start');
11281123
let _generator = dart.JsSymbol('_generator');
1129-
let $iterator = dart.JsSymbol('$iterator');
11301124
let $skip = dart.JsSymbol('$skip');
11311125
let $take = dart.JsSymbol('$take');
11321126
let _GeneratorIterable$ = dart.generic(function(E) {
@@ -3412,10 +3406,10 @@ var core;
34123406
exports.identityHashCode = identityHashCode;
34133407
exports.int = int;
34143408
exports.Invocation = Invocation;
3409+
exports.$iterator = $iterator;
34153410
exports.$join = $join;
34163411
exports.Iterable$ = Iterable$;
34173412
exports.Iterable = Iterable;
3418-
exports.$iterator = $iterator;
34193413
exports.$skip = $skip;
34203414
exports.$take = $take;
34213415
exports.BidirectionalIterator$ = BidirectionalIterator$;

pkg/dev_compiler/lib/runtime/dart_runtime.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,18 @@ var dart, _js_helper, _js_primitives;
945945
}
946946
dart.noSuchMethod = noSuchMethod;
947947

948+
class JsIterator {
949+
constructor(dartIterator) {
950+
this.dartIterator = dartIterator;
951+
}
952+
next() {
953+
let i = this.dartIterator;
954+
var done = !i.moveNext();
955+
return { done: done, value: done ? void 0 : i.current };
956+
}
957+
}
958+
dart.JsIterator = JsIterator;
959+
948960
// TODO(jmesserly): right now this is a sentinel. It should be a type object
949961
// of some sort, assuming we keep around `dynamic` at runtime.
950962
dart.dynamic = { toString() { return 'dynamic'; } };

pkg/dev_compiler/lib/src/codegen/js_codegen.dart

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -573,32 +573,64 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
573573
jsMethods.add(_emitImplicitConstructor(node, name, fields));
574574
}
575575

576-
for (var member in node.members) {
577-
if (member is ConstructorDeclaration) {
578-
jsMethods.add(_emitConstructor(member, name, fields, isObject));
579-
} else if (member is MethodDeclaration) {
580-
jsMethods.add(_emitMethodDeclaration(type, member));
576+
bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null;
577+
578+
bool hasIterator = false;
579+
for (var m in node.members) {
580+
if (m is ConstructorDeclaration) {
581+
jsMethods.add(_emitConstructor(m, name, fields, isObject));
582+
} else if (m is MethodDeclaration) {
583+
jsMethods.add(_emitMethodDeclaration(type, m));
584+
585+
if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') {
586+
hasIterator = true;
587+
jsMethods.add(_emitIterable(type));
588+
}
581589
}
582590
}
583591

584-
// Support for adapting dart:core Iterator/Iterable to ES6 versions.
585-
// This lets them use for-of loops transparently.
586-
// https://github.com/lukehoban/es6features#iterators--forof
587-
if (element.library.isDartCore && element.name == 'Iterable') {
588-
JS.Fun body = js.call('''function() {
589-
var iterator = this.iterator;
590-
return {
591-
next() {
592-
var done = iterator.moveNext();
593-
return { done: done, current: done ? void 0 : iterator.current };
594-
}
595-
};
596-
}''');
597-
jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body));
592+
// If the type doesn't have an `iterator`, but claims to implement Iterable,
593+
// we inject the adaptor method here, as it's less code size to put the
594+
// helper on a parent class. This pattern is common in the core libraries
595+
// (e.g. IterableMixin<E> and IterableBase<E>).
596+
//
597+
// (We could do this same optimization for any interface with an `iterator`
598+
// method, but that's more expensive to check for, so it doesn't seem worth
599+
// it. The above case for an explicit `iterator` method will catch those.)
600+
if (!hasJsPeer && !hasIterator && _implementsIterable(type)) {
601+
jsMethods.add(_emitIterable(type));
598602
}
603+
599604
return jsMethods.where((m) => m != null).toList(growable: false);
600605
}
601606

607+
bool _implementsIterable(InterfaceType t) =>
608+
t.interfaces.any((i) => i.element.type == types.iterableType);
609+
610+
/// Support for adapting dart:core Iterable to ES6 versions.
611+
///
612+
/// This lets them use for-of loops transparently:
613+
/// <https://github.com/lukehoban/es6features#iterators--forof>
614+
///
615+
/// This will return `null` if the adapter was already added on a super type,
616+
/// otherwise it returns the adapter code.
617+
// TODO(jmesserly): should we adapt `Iterator` too?
618+
JS.Method _emitIterable(InterfaceType t) {
619+
// If a parent had an `iterator` (concrete or abstract) or implements
620+
// Iterable, we know the adapter is already there, so we can skip it as a
621+
// simple code size optimization.
622+
var parent = t.lookUpGetterInSuperclass('iterator', t.element.library);
623+
if (parent != null) return null;
624+
parent = findSupertype(t, _implementsIterable);
625+
if (parent != null) return null;
626+
627+
// Otherwise, emit the adapter method, which wraps the Dart iterator in
628+
// an ES6 iterator.
629+
return new JS.Method(js.call('$_SYMBOL.iterator'), js.call(
630+
'function() { return new dart.JsIterator(this.#); }',
631+
[_emitMemberName('iterator', type: t)]));
632+
}
633+
602634
/// Emit class members that need to come after the class declaration, such
603635
/// as static fields. See [_emitClassMethods] for things that are emitted
604636
/// inside the ES6 `class { ... }` node.

pkg/dev_compiler/lib/src/utils.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,16 @@ Map<String, DartType> getObjectMemberMap(TypeProvider typeProvider) {
424424
}
425425
return map;
426426
}
427+
428+
/// Searches all supertype, in order of most derived members, to see if any
429+
/// [match] a condition. If so, returns the first match, otherwise returns null.
430+
InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
431+
for (var m in type.mixins.reversed) {
432+
if (match(m)) return m;
433+
}
434+
var s = type.superclass;
435+
if (s == null) return null;
436+
437+
if (match(s)) return type;
438+
return findSupertype(s, match);
439+
}

0 commit comments

Comments
 (0)