| Index: pkg/compiler/lib/src/ssa/builder.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
|
| index a3d3a60b42b5366fcb6017895d90bf4117a723bc..9ec65bc02452bd98d5afb108fe30d854ea0abdf8 100644
|
| --- a/pkg/compiler/lib/src/ssa/builder.dart
|
| +++ b/pkg/compiler/lib/src/ssa/builder.dart
|
| @@ -5526,15 +5526,37 @@ class SsaBuilder extends NewResolvedVisitor {
|
| }
|
|
|
| visitSyncForIn(ast.SyncForIn node) {
|
| + // The 'get iterator' selector for this node has the inferred receiver type.
|
| + // If the receiver supports JavaScript indexing we generate an indexing loop
|
| + // instead of allocating an iterator object.
|
| +
|
| + // This scheme recognizes for-in on direct lists. It does not recognize all
|
| + // uses of ArrayIterator. They still occur when the receiver is an Iterable
|
| + // with a `get iterator` method that delegate to another Iterable and the
|
| + // method is inlined. We would require full scalar replacement in that
|
| + // case.
|
| +
|
| + Selector selector = elements.getIteratorSelector(node);
|
| + TypeMask mask = selector.mask;
|
| +
|
| + ClassWorld classWorld = compiler.world;
|
| + if (mask != null && mask.satisfies(backend.jsIndexableClass, classWorld)) {
|
| + return buildSyncForInIndexable(node, mask);
|
| + }
|
| + buildSyncForInIterator(node);
|
| + }
|
| +
|
| + buildSyncForInIterator(ast.SyncForIn node) {
|
| // Generate a structure equivalent to:
|
| // Iterator<E> $iter = <iterable>.iterator;
|
| // while ($iter.moveNext()) {
|
| - // E <declaredIdentifier> = $iter.current;
|
| + // <declaredIdentifier> = $iter.current;
|
| // <body>
|
| // }
|
|
|
| // The iterator is shared between initializer, condition and body.
|
| HInstruction iterator;
|
| +
|
| void buildInitializer() {
|
| Selector selector = elements.getIteratorSelector(node);
|
| visit(node.expression);
|
| @@ -5542,38 +5564,145 @@ class SsaBuilder extends NewResolvedVisitor {
|
| pushInvokeDynamic(node, selector, [receiver]);
|
| iterator = pop();
|
| }
|
| +
|
| HInstruction buildCondition() {
|
| Selector selector = elements.getMoveNextSelector(node);
|
| pushInvokeDynamic(node, selector, [iterator]);
|
| return popBoolified();
|
| }
|
| +
|
| void buildBody() {
|
| Selector call = elements.getCurrentSelector(node);
|
| pushInvokeDynamic(node, call, [iterator]);
|
| + buildAssignLoopVariable(node, pop());
|
| + visit(node.body);
|
| + }
|
|
|
| - ast.Node identifier = node.declaredIdentifier;
|
| - Element variable = elements.getForInVariable(node);
|
| - Selector selector = elements.getSelector(identifier);
|
| + handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
|
| + }
|
|
|
| - HInstruction value = pop();
|
| - if (identifier.asSend() != null
|
| - && Elements.isInstanceSend(identifier, elements)) {
|
| - HInstruction receiver = generateInstanceSendReceiver(identifier);
|
| - assert(receiver != null);
|
| - generateInstanceSetterWithCompiledReceiver(
|
| - null,
|
| - receiver,
|
| - value,
|
| - selector: selector,
|
| - location: identifier);
|
| - } else {
|
| - generateNonInstanceSetter(null, variable, value, location: identifier);
|
| - }
|
| - pop(); // Pop the value pushed by the setter call.
|
| + buildAssignLoopVariable(ast.ForIn node, HInstruction value) {
|
| + ast.Node identifier = node.declaredIdentifier;
|
| + Element variable = elements.getForInVariable(node);
|
| + Selector selector = elements.getSelector(identifier);
|
| +
|
| + if (identifier.asSend() != null &&
|
| + Elements.isInstanceSend(identifier, elements)) {
|
| + HInstruction receiver = generateInstanceSendReceiver(identifier);
|
| + assert(receiver != null);
|
| + generateInstanceSetterWithCompiledReceiver(
|
| + null,
|
| + receiver,
|
| + value,
|
| + selector: selector,
|
| + location: identifier);
|
| + } else {
|
| + generateNonInstanceSetter(null, variable, value, location: identifier);
|
| + }
|
| + pop(); // Discard the value pushed by the setter call.
|
| + }
|
| +
|
| + buildSyncForInIndexable(ast.ForIn node, TypeMask arrayType) {
|
| + // Generate a structure equivalent to:
|
| + //
|
| + // int end = a.length;
|
| + // for (int i = 0;
|
| + // i < a.length;
|
| + // checkConcurrentModificationError(a.length == end, a), ++i) {
|
| + // <declaredIdentifier> = a[i];
|
| + // <body>
|
| + // }
|
| + Element loopVariable = elements.getForInVariable(node);
|
| + SyntheticLocal indexVariable = new SyntheticLocal('_i', loopVariable);
|
| + TypeMask boolType = backend.boolType;
|
| +
|
| + // These variables are shared by initializer, condition, body and update.
|
| + HInstruction array; // Set in buildInitializer.
|
| + bool isFixed; // Set in buildInitializer.
|
| + HInstruction originalLength = null; // Set for growable lists.
|
| +
|
| + HInstruction buildGetLength() {
|
| + Element lengthElement = backend.jsIndexableLength;
|
| + HFieldGet result = new HFieldGet(
|
| + lengthElement, array, backend.positiveIntType,
|
| + isAssignable: !isFixed);
|
| + add(result);
|
| + return result;
|
| + }
|
| +
|
| + void buildConcurrentModificationErrorCheck() {
|
| + if (originalLength == null) return;
|
| + // The static call checkConcurrentModificationError() is expanded in
|
| + // codegen to:
|
| + //
|
| + // array.length == _end || throwConcurrentModificationError(array)
|
| + //
|
| + HInstruction length = buildGetLength();
|
| + push(new HIdentity(length, originalLength, null, boolType));
|
| + pushInvokeStatic(node,
|
| + backend.getCheckConcurrentModificationError(),
|
| + [pop(), array]);
|
| + pop();
|
| + }
|
| +
|
| + void buildInitializer() {
|
| + visit(node.expression);
|
| + array = pop();
|
| + isFixed = isFixedLength(array.instructionType, compiler);
|
| + localsHandler.updateLocal(indexVariable,
|
| + graph.addConstantInt(0, compiler));
|
| + originalLength = buildGetLength();
|
| + }
|
| +
|
| + HInstruction buildCondition() {
|
| + HInstruction index = localsHandler.readLocal(indexVariable);
|
| + HInstruction length = buildGetLength();
|
| + HInstruction compare = new HLess(index, length, null, boolType);
|
| + add(compare);
|
| + return compare;
|
| + }
|
|
|
| + void buildBody() {
|
| + // If we had mechanically inlined ArrayIterator.moveNext(), it would have
|
| + // inserted the ConcurrentModificationError check as part of the
|
| + // condition. It is not necessary on the first iteration since there is
|
| + // no code between calls to `get iterator` and `moveNext`, so the test is
|
| + // moved to the loop update.
|
| +
|
| + // Find a type for the element. Use the element type of the indexer of the
|
| + // array, as this is stronger than the iterator's `get current` type, for
|
| + // example, `get current` includes null.
|
| + // TODO(sra): The element type of a container type mask might be better.
|
| + Selector selector = new Selector.index();
|
| + Selector refined = new TypedSelector(arrayType, selector, compiler.world);
|
| + TypeMask type =
|
| + TypeMaskFactory.inferredTypeForSelector(refined, compiler);
|
| +
|
| + HInstruction index = localsHandler.readLocal(indexVariable);
|
| + HInstruction value = new HIndex(array, index, null, type);
|
| + add(value);
|
| +
|
| + buildAssignLoopVariable(node, value);
|
| visit(node.body);
|
| }
|
| - handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
|
| +
|
| + void buildUpdate() {
|
| + // See buildBody as to why we check here.
|
| + buildConcurrentModificationErrorCheck();
|
| +
|
| + // TODO(sra): It would be slightly shorter to generate `a[i++]` in the
|
| + // body (and that more closely follows what an inlined iterator would do)
|
| + // but the code is horrible as `i+1` is carried around the loop in an
|
| + // additional variable.
|
| + HInstruction index = localsHandler.readLocal(indexVariable);
|
| + HInstruction one = graph.addConstantInt(1, compiler);
|
| + HInstruction addInstruction =
|
| + new HAdd(index, one, null, backend.positiveIntType);
|
| + add(addInstruction);
|
| + localsHandler.updateLocal(indexVariable, addInstruction);
|
| + }
|
| +
|
| + handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody);
|
| }
|
|
|
| visitLabel(ast.Label node) {
|
|
|