| Index: pkg/compiler/lib/src/ssa/builder_kernel.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
|
| index 2d4726d25d3cc488632310f9cf854ec57ee0ae7e..807c1fcfaf80b62144225e8d3a6c3b962f29c827 100644
|
| --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
|
| +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
|
| @@ -6,6 +6,7 @@ import 'package:kernel/ast.dart' as ir;
|
|
|
| import '../common.dart';
|
| import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
|
| +import '../common/names.dart';
|
| import '../common/tasks.dart' show CompilerTask;
|
| import '../compiler.dart';
|
| import '../dart_types.dart';
|
| @@ -17,7 +18,6 @@ import '../resolution/tree_elements.dart';
|
| import '../tree/dartstring.dart';
|
| import '../types/masks.dart';
|
| import '../universe/selector.dart';
|
| -
|
| import 'graph_builder.dart';
|
| import 'kernel_ast_adapter.dart';
|
| import 'kernel_string_builder.dart';
|
| @@ -242,6 +242,164 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
|
| }
|
|
|
| @override
|
| + void visitForInStatement(ir.ForInStatement forInStatement) {
|
| + if (forInStatement.isAsync) {
|
| + compiler.reporter.internalError(astAdapter.getNode(forInStatement),
|
| + "Cannot compile async for-in using kernel.");
|
| + }
|
| + // If the expression being iterated over is a JS indexable type, we can
|
| + // generate an optimized version of for-in that uses indexing.
|
| + if (astAdapter.isJsIndexableIterator(forInStatement)) {
|
| + _buildForInIndexable(forInStatement);
|
| + } else {
|
| + _buildForInIterator(forInStatement);
|
| + }
|
| + }
|
| +
|
| + /// Builds the graph for a for-in node with an indexable expression.
|
| + ///
|
| + /// In this case we build:
|
| + ///
|
| + /// int end = a.length;
|
| + /// for (int i = 0;
|
| + /// i < a.length;
|
| + /// checkConcurrentModificationError(a.length == end, a), ++i) {
|
| + /// <declaredIdentifier> = a[i];
|
| + /// <body>
|
| + /// }
|
| + _buildForInIndexable(ir.ForInStatement forInStatement) {
|
| + SyntheticLocal indexVariable = new SyntheticLocal('_i', targetElement);
|
| +
|
| + // 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() {
|
| + HFieldGet result = new HFieldGet(
|
| + astAdapter.jsIndexableLength, 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, backend.boolType));
|
| + _pushStaticInvocation(
|
| + astAdapter.checkConcurrentModificationError,
|
| + [pop(), array],
|
| + astAdapter.checkConcurrentModificationErrorReturnType);
|
| + pop();
|
| + }
|
| +
|
| + void buildInitializer() {
|
| + forInStatement.iterable.accept(this);
|
| + array = pop();
|
| + isFixed = astAdapter.isFixedLength(array.instructionType);
|
| + 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, backend.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.
|
| + TypeMask type = astAdapter.inferredIndexType(forInStatement);
|
| +
|
| + HInstruction index = localsHandler.readLocal(indexVariable);
|
| + HInstruction value = new HIndex(array, index, null, type);
|
| + add(value);
|
| +
|
| + localsHandler.updateLocal(
|
| + astAdapter.getLocal(forInStatement.variable), value);
|
| +
|
| + forInStatement.body.accept(this);
|
| + }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + loopHandler.handleLoop(forInStatement, buildInitializer, buildCondition,
|
| + buildUpdate, buildBody);
|
| + }
|
| +
|
| + _buildForInIterator(ir.ForInStatement forInStatement) {
|
| + // Generate a structure equivalent to:
|
| + // Iterator<E> $iter = <iterable>.iterator;
|
| + // while ($iter.moveNext()) {
|
| + // <declaredIdentifier> = $iter.current;
|
| + // <body>
|
| + // }
|
| +
|
| + // The iterator is shared between initializer, condition and body.
|
| + HInstruction iterator;
|
| +
|
| + void buildInitializer() {
|
| + TypeMask mask = astAdapter.typeOfIterator(forInStatement);
|
| + forInStatement.iterable.accept(this);
|
| + HInstruction receiver = pop();
|
| + _pushDynamicInvocation(forInStatement, mask, <HInstruction>[receiver],
|
| + selector: Selectors.iterator);
|
| + iterator = pop();
|
| + }
|
| +
|
| + HInstruction buildCondition() {
|
| + TypeMask mask = astAdapter.typeOfIteratorMoveNext(forInStatement);
|
| + _pushDynamicInvocation(forInStatement, mask, <HInstruction>[iterator],
|
| + selector: Selectors.moveNext);
|
| + return popBoolified();
|
| + }
|
| +
|
| + void buildBody() {
|
| + TypeMask mask = astAdapter.typeOfIteratorCurrent(forInStatement);
|
| + _pushDynamicInvocation(forInStatement, mask, [iterator],
|
| + selector: Selectors.current);
|
| + localsHandler.updateLocal(
|
| + astAdapter.getLocal(forInStatement.variable), pop());
|
| + forInStatement.body.accept(this);
|
| + }
|
| +
|
| + loopHandler.handleLoop(
|
| + forInStatement, buildInitializer, buildCondition, () {}, buildBody);
|
| + }
|
| +
|
| + @override
|
| void visitWhileStatement(ir.WhileStatement whileStatement) {
|
| assert(isReachable);
|
| HInstruction buildCondition() {
|
| @@ -330,7 +488,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
|
| // TODO(het): set runtime type info
|
| }
|
|
|
| - // TODO(het): Set the instruction type to the list type given by inference
|
| + TypeMask type = astAdapter.typeOfNewList(targetElement, listLiteral);
|
| + if (!type.containsAll(compiler.closedWorld)) {
|
| + listInstruction.instructionType = type;
|
| + }
|
| stack.add(listInstruction);
|
| }
|
|
|
| @@ -412,23 +573,13 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
|
| propertyGet.receiver.accept(this);
|
| HInstruction receiver = pop();
|
|
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - bool isIntercepted = astAdapter.isIntercepted(propertyGet);
|
| - if (isIntercepted) {
|
| - HInterceptor interceptor = _interceptorFor(receiver);
|
| - inputs.add(interceptor);
|
| - }
|
| - inputs.add(receiver);
|
| -
|
| - TypeMask type = astAdapter.selectorGetterTypeOf(propertyGet);
|
| -
|
| - push(new HInvokeDynamicGetter(astAdapter.getGetterSelector(propertyGet),
|
| - astAdapter.typeOfGet(propertyGet), null, inputs, type));
|
| + _pushDynamicInvocation(propertyGet, astAdapter.typeOfGet(propertyGet),
|
| + <HInstruction>[receiver]);
|
| }
|
|
|
| @override
|
| void visitVariableGet(ir.VariableGet variableGet) {
|
| - LocalElement local = astAdapter.getElement(variableGet.variable);
|
| + Local local = astAdapter.getLocal(variableGet.variable);
|
| stack.add(localsHandler.readLocal(local));
|
| }
|
|
|
| @@ -441,7 +592,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
|
|
|
| @override
|
| void visitVariableDeclaration(ir.VariableDeclaration declaration) {
|
| - LocalElement local = astAdapter.getElement(declaration);
|
| + Local local = astAdapter.getLocal(declaration);
|
| if (declaration.initializer == null) {
|
| HInstruction initialValue = graph.addConstantNull(compiler);
|
| localsHandler.updateLocal(local, initialValue);
|
| @@ -508,28 +659,43 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
|
| push(instruction);
|
| }
|
|
|
| - // TODO(het): Decide when to inline
|
| - @override
|
| - void visitMethodInvocation(ir.MethodInvocation invocation) {
|
| - invocation.receiver.accept(this);
|
| - HInstruction receiver = pop();
|
| -
|
| - List<HInstruction> arguments = <HInstruction>[receiver]
|
| - ..addAll(_visitArguments(invocation.arguments));
|
| -
|
| + void _pushDynamicInvocation(
|
| + ir.Node node, TypeMask mask, List<HInstruction> arguments,
|
| + {Selector selector}) {
|
| + HInstruction receiver = arguments.first;
|
| List<HInstruction> inputs = <HInstruction>[];
|
|
|
| - bool isIntercepted = astAdapter.isIntercepted(invocation);
|
| + selector ??= astAdapter.getSelector(node);
|
| + bool isIntercepted = astAdapter.isInterceptedSelector(selector);
|
| +
|
| if (isIntercepted) {
|
| HInterceptor interceptor = _interceptorFor(receiver);
|
| inputs.add(interceptor);
|
| }
|
| inputs.addAll(arguments);
|
|
|
| - TypeMask type = astAdapter.selectorTypeOf(invocation);
|
| + TypeMask type = astAdapter.selectorTypeOf(selector, mask);
|
| + if (selector.isGetter) {
|
| + push(new HInvokeDynamicGetter(selector, mask, null, inputs, type));
|
| + } else if (selector.isSetter) {
|
| + push(new HInvokeDynamicSetter(selector, mask, null, inputs, type));
|
| + } else {
|
| + push(new HInvokeDynamicMethod(
|
| + selector, mask, inputs, type, isIntercepted));
|
| + }
|
| + }
|
| +
|
| + // TODO(het): Decide when to inline
|
| + @override
|
| + void visitMethodInvocation(ir.MethodInvocation invocation) {
|
| + invocation.receiver.accept(this);
|
| + HInstruction receiver = pop();
|
|
|
| - push(new HInvokeDynamicMethod(astAdapter.getSelector(invocation),
|
| - astAdapter.typeOfInvocation(invocation), inputs, type, isIntercepted));
|
| + _pushDynamicInvocation(
|
| + invocation,
|
| + astAdapter.typeOfInvocation(invocation),
|
| + <HInstruction>[receiver]
|
| + ..addAll(_visitArguments(invocation.arguments)));
|
| }
|
|
|
| HInterceptor _interceptorFor(HInstruction intercepted) {
|
|
|