[dart2js] Smaller code by caching values

- Move more that one assignment into var-list
- Move 'this' to a local variable
- Move repeated large 'fast' constants to local variable

Currently under flag --experiment-code-1


Change-Id: I091cab47f498b4ec3759b9ed358bcc0f2e73fdb1
Reviewed-on: https://dart-review.googlesource.com/c/77025
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 2bbd9c2..c68391d 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -26,6 +26,11 @@
       '--experimental-track-allocations';
   static const String experimentalAllocationsPath =
       '--experimental-allocations-path';
+
+  // Temporary experiment for code generation of locals for frequently used
+  // 'this' and constants.
+  static const String experimentLocalNames = '--experiment-code-1';
+
   static const String fastStartup = '--fast-startup';
   static const String fatalWarnings = '--fatal-warnings';
   static const String generateCodeWithCompileTimeErrors =
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 7b6ca85..5555b38 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -376,6 +376,8 @@
     new OptionHandler(Flags.experimentalTrackAllocations, passThrough),
     new OptionHandler("${Flags.experimentalAllocationsPath}=.+", passThrough),
 
+    new OptionHandler(Flags.experimentLocalNames, passThrough),
+
     // The following three options must come last.
     new OptionHandler('-D.+=.*', addInEnvironment),
     new OptionHandler('-.*', (String argument) {
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 46ae073..c0d1ef5 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -679,7 +679,8 @@
 
     List<js.Statement> statements = [
       new js.ExpressionStatement(new js.VariableDeclarationList(
-          holders.map(emitHolderInitialization).toList())),
+          holders.map(emitHolderInitialization).toList(),
+          indentSplits: false)),
       js.js.statement(
           'var holders = #',
           new js.ArrayInitializer(holders
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 9dbf70e..586171e 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -232,6 +232,9 @@
   /// This is an experimental feature.
   bool experimentalTrackAllocations = false;
 
+  /// Expermental optimization.
+  bool experimentLocalNames = false;
+
   /// The path to the file that contains the profiled allocations.
   ///
   /// The file must contain the Map that was produced by using
@@ -286,6 +289,7 @@
           _hasOption(options, Flags.experimentalTrackAllocations)
       ..experimentalAllocationsPath = _extractStringOption(
           options, "${Flags.experimentalAllocationsPath}=", null)
+      ..experimentLocalNames = _hasOption(options, Flags.experimentLocalNames)
       ..generateCodeWithCompileTimeErrors =
           _hasOption(options, Flags.generateCodeWithCompileTimeErrors)
       ..generateSourceMap = !_hasOption(options, Flags.noSourceMaps)
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 2a86ba0..61ed394 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1395,7 +1395,7 @@
     // TODO(efortuna): Source information!
     push(new HInvokeStatic(
         commonElements.loadDeferredLibrary,
-        [graph.addConstantString(loadId, closedWorld)],
+        <HInstruction>[graph.addConstantString(loadId, closedWorld)],
         abstractValueDomain.nonNullType,
         const <DartType>[],
         targetCanThrow: false));
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index fca7000..52cd6c9 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -184,6 +184,10 @@
    * copies to perform on block transitioning.
    */
   VariableNames variableNames;
+
+  /// `true` when we need to generate a `var` declaration at function entry,
+  /// `false` if we can generate a `var` declaration at first assignment in the
+  /// middle of the function.
   bool shouldGroupVarDeclarations = false;
 
   /**
@@ -370,6 +374,7 @@
         .visitGraph(graph);
     new SsaConditionMerger(generateAtUseSite, controlFlowOperators)
         .visitGraph(graph);
+    new SsaShareRegionConstants(_options).visitGraph(graph);
     SsaLiveIntervalBuilder intervalBuilder =
         new SsaLiveIntervalBuilder(generateAtUseSite, controlFlowOperators);
     intervalBuilder.visitGraph(graph);
@@ -384,6 +389,11 @@
   }
 
   void handleDelayedVariableDeclarations(SourceInformation sourceInformation) {
+    if (_options.experimentLocalNames) {
+      handleDelayedVariableDeclarations2(sourceInformation);
+      return;
+    }
+
     // If we have only one variable declaration and the first statement is an
     // assignment to that variable then we can merge the two.  We count the
     // number of variables in the variable allocator to try to avoid this issue,
@@ -426,6 +436,57 @@
     }
   }
 
+  void handleDelayedVariableDeclarations2(SourceInformation sourceInformation) {
+    // Create 'var' list at the start of function.  Move assignment statements
+    // from the top of the body into the variable initializers.
+    if (collectedVariableDeclarations.isEmpty) return;
+
+    List<js.VariableInitialization> declarations = [];
+    List<js.Statement> statements = currentContainer.statements;
+    int nextStatement = 0;
+
+    while (nextStatement < statements.length) {
+      if (collectedVariableDeclarations.isEmpty) break;
+      js.Statement statement = statements[nextStatement];
+      if (statement is js.ExpressionStatement) {
+        js.Expression expression = statement.expression;
+        if (expression is js.Assignment && !expression.isCompound) {
+          js.Expression left = expression.leftHandSide;
+          if (left is js.VariableReference) {
+            String name = left.name;
+            js.Expression value = expression.value;
+            if (_safeInInitializer(value) &&
+                collectedVariableDeclarations.remove(name)) {
+              var initialization = new js.VariableInitialization(
+                      new js.VariableDeclaration(name), value)
+                  .withSourceInformation(expression.sourceInformation);
+              declarations.add(initialization);
+              ++nextStatement;
+              continue;
+            }
+          }
+        }
+      }
+      break;
+    }
+
+    List<js.VariableInitialization> uninitialized = [];
+    for (String name in collectedVariableDeclarations) {
+      uninitialized.add(new js.VariableInitialization(
+          new js.VariableDeclaration(name), null));
+    }
+    var declarationList =
+        new js.VariableDeclarationList(uninitialized + declarations)
+            .withSourceInformation(sourceInformation);
+    statements.replaceRange(
+        0, nextStatement, [new js.ExpressionStatement(declarationList)]);
+  }
+
+  // An expression is safe to be pulled into a 'var' initializer if it does not
+  // contain assignments to locals. We don't generate assignments to locals
+  // inside expressions.
+  bool _safeInInitializer(js.Expression node) => true;
+
   visitGraph(HGraph graph) {
     preGenerateMethod(graph);
     currentGraph = graph;
@@ -1400,6 +1461,10 @@
         .withSourceInformation(sourceInformation));
   }
 
+  visitLateValue(HLateValue node) {
+    use(node.target);
+  }
+
   visitInvokeBinary(HInvokeBinary node, String op) {
     handleInvokeBinary(node, op, node.sourceInformation);
   }
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 6608085..1a9ab2f 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -832,3 +832,188 @@
     }
   }
 }
+
+/// Insert 'caches' for whole-function region-constants when the local minified
+/// name would be shorter than repeated references.  These are caches for 'this'
+/// and constant values.
+class SsaShareRegionConstants extends HBaseVisitor {
+  final CompilerOptions _options;
+
+  SsaShareRegionConstants(this._options);
+
+  visitGraph(HGraph graph) {
+    if (!_options.experimentLocalNames) return;
+    // We need the async rewrite to be smarter about hoisting region constants
+    // before it is worth-while.
+    if (graph.needsAsyncRewrite) return;
+
+    // 'HThis' and constants are in the entry block. No need to walk the rest of
+    // the graph.
+    visitBasicBlock(graph.entry);
+  }
+
+  visitBasicBlock(HBasicBlock block) {
+    HInstruction instruction = block.first;
+    while (instruction != null) {
+      HInstruction next = instruction.next;
+      instruction.accept(this);
+      instruction = next;
+    }
+  }
+
+  // Not all occurences should be replaced with a local variable cache, so we
+  // filter the uses.
+  int _countCacheableUses(
+      HInstruction node, bool Function(HInstruction) cacheable) {
+    return node.usedBy.where(cacheable).length;
+  }
+
+  // Replace cacheable uses with a reference to a HLateValue node.
+  _cache(
+      HInstruction node, bool Function(HInstruction) cacheable, String name) {
+    var users = node.usedBy.toList();
+    var reference = new HLateValue(node);
+    // TODO(sra): The sourceInformation should really be from the function
+    // entry, not the use of `this`.
+    reference.sourceInformation = node.sourceInformation;
+    reference.sourceElement = _ExpressionName(name);
+    node.block.addAfter(node, reference);
+    for (HInstruction user in users) {
+      if (cacheable(user)) {
+        user.changeUse(node, reference);
+      }
+    }
+  }
+
+  void visitThis(HThis node) {
+    int size = 4;
+    // Compare the size of the unchanged minified with the size of the minified
+    // code where 'this' is assigned to a variable. We assume the variable has
+    // minified size 1.
+    //
+    // The size overhead of introducing a variable in the worst case includes
+    // 'var ':
+    //
+    //           1234   // size
+    //     var x=this;  // (minified ';' can be end-of-line)
+    //     123456    7  // additional overhead
+    //
+    // TODO(sra): If there are multiple values that can potentially be cached,
+    // they can share the 'var ' cost, even if none of them are beneficial
+    // individually.
+    int useCount = node.usedBy.length;
+    if (useCount * size <= 7 + size + useCount * 1) return;
+    _cache(node, (_) => true, '_this');
+  }
+
+  void visitConstant(HConstant node) {
+    if (node.usedBy.length <= 1) return;
+    ConstantValue constant = node.constant;
+
+    if (constant.isNull) {
+      _handleNull(node);
+      return;
+    }
+
+    if (constant.isInt) {
+      _handleInt(node, constant);
+      return;
+    }
+
+    if (constant.isString) {
+      _handleString(node, constant);
+      return;
+    }
+  }
+
+  void _handleNull(HConstant node) {
+    int size = 4;
+
+    bool _isCacheableUse(HInstruction instruction) {
+      // One-shot interceptors have `null` as a dummy interceptor.
+      if (instruction is HOneShotInterceptor) return false;
+
+      if (instruction is HInvoke) return true;
+      if (instruction is HCreate) return true;
+      if (instruction is HPhi) return true;
+
+      // We return `null` by removing the return expression or statement.
+      if (instruction is HReturn) return false;
+
+      // JavaScript `x == null` is more efficient than `x == _null`.
+      if (instruction is HIdentity) return false;
+
+      // TODO(sra): Deterimine if other uses result in faster JavaScript code.
+      return false;
+    }
+
+    int useCount = _countCacheableUses(node, _isCacheableUse);
+    if (useCount * size <= 7 + size + useCount * 1) return;
+    _cache(node, _isCacheableUse, '_null');
+    return;
+  }
+
+  void _handleInt(HConstant node, IntConstantValue intConstant) {
+    BigInt value = intConstant.intValue;
+    String text = value.toString();
+    int size = text.length;
+    if (size <= 3) return;
+
+    bool _isCacheableUse(HInstruction instruction) {
+      if (instruction is HInvoke) return true;
+      if (instruction is HCreate) return true;
+      if (instruction is HReturn) return true;
+      if (instruction is HPhi) return true;
+
+      // JavaScript `x === 5` is more efficient than `x === _5`.
+      if (instruction is HIdentity) return false;
+
+      // Foreign code templates may use literals in ways that are beneficial.
+      if (instruction is HForeignCode) return false;
+
+      // TODO(sra): Deterimine if other uses result in faster JavaScript code.
+      return false;
+    }
+
+    int useCount = _countCacheableUses(node, _isCacheableUse);
+    if (useCount * size <= 7 + size + useCount * 1) return;
+    _cache(node, _isCacheableUse, '_${text.replaceFirst("-", "_")}');
+  }
+
+  void _handleString(HConstant node, StringConstantValue stringConstant) {
+    String value = stringConstant.stringValue;
+    int length = value.length;
+    int size = length + 2; // Include quotes.
+    if (size <= 2) return;
+
+    bool _isCacheableUse(HInstruction instruction) {
+      // Foreign code templates may use literals in ways that are beneficial.
+      if (instruction is HForeignCode) return false;
+
+      // Cache larger strings even if unfortunate.
+      if (length >= 16) return true;
+
+      if (instruction is HInvoke) return true;
+      if (instruction is HCreate) return true;
+      if (instruction is HReturn) return true;
+      if (instruction is HPhi) return true;
+
+      // TODO(sra): Check if a.x="s" can avoid or specialize a write barrier.
+      if (instruction is HFieldSet) return true;
+
+      // TODO(sra): Deterimine if other uses result in faster JavaScript code.
+      return false;
+    }
+
+    int useCount = _countCacheableUses(node, _isCacheableUse);
+    if (useCount * size <= 7 + size + useCount * 1) return;
+    _cache(node, _isCacheableUse, '_s${length}_');
+  }
+}
+
+/// A simple Entity to give intermediate values nice names when not generating
+/// minified code.
+class _ExpressionName implements Entity {
+  final String name;
+  _ExpressionName(this.name);
+}
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 32ea8a9..f815b64 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -65,6 +65,7 @@
   R visitInvokeGeneratorBody(HInvokeGeneratorBody node);
   R visitIs(HIs node);
   R visitIsViaInterceptor(HIsViaInterceptor node);
+  R visitLateValue(HLateValue node);
   R visitLazyStatic(HLazyStatic node);
   R visitLess(HLess node);
   R visitLessEqual(HLessEqual node);
@@ -478,6 +479,7 @@
   visitTruncatingDivide(HTruncatingDivide node) => visitBinaryArithmetic(node);
   visitTry(HTry node) => visitControlFlow(node);
   visitIs(HIs node) => visitInstruction(node);
+  visitLateValue(HLateValue node) => visitInstruction(node);
   visitIsViaInterceptor(HIsViaInterceptor node) => visitInstruction(node);
   visitTypeConversion(HTypeConversion node) => visitCheck(node);
   visitTypeKnown(HTypeKnown node) => visitCheck(node);
@@ -3077,6 +3079,22 @@
   }
 }
 
+/// HLateValue is a late-stage instruction that can be used to force a value
+/// into a temporary.
+///
+/// HLateValue is useful for naming values that would otherwise be generated at
+/// use site, for example, if 'this' is used many times, replacing uses of
+/// 'this' with HLateValhe(HThis) will have the effect of copying 'this' to a
+/// temporary will reduce the size of minified code.
+class HLateValue extends HLateInstruction {
+  HLateValue(HInstruction target) : super([target], target.instructionType);
+
+  HInstruction get target => inputs.single;
+
+  accept(HVisitor visitor) => visitor.visitLateValue(this);
+  toString() => 'HLateValue($target)';
+}
+
 class HTypeConversion extends HCheck {
   // Values for [kind].
   static const int CHECKED_MODE_CHECK = 0;
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index f72cc6b..61a56cd 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -158,6 +158,10 @@
     return "$prefix${instruction.id}";
   }
 
+  String visitLateValue(HLateValue node) {
+    return "LateValue: ${temporaryId(node.inputs[0])}";
+  }
+
   String visitBoolify(HBoolify node) {
     return "Boolify: ${temporaryId(node.inputs[0])}";
   }
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 9b933ae..2c44a3c 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -1046,7 +1046,12 @@
 class VariableDeclarationList extends Expression {
   final List<VariableInitialization> declarations;
 
-  VariableDeclarationList(this.declarations);
+  /// When pretty-printing a declaration list with multiple declarations over
+  /// several lines, the declarations are usually indented with respect to the
+  /// `var` keyword. Set [indentSplits] to `false` to suppress the indentation.
+  final bool indentSplits;
+
+  VariableDeclarationList(this.declarations, {this.indentSplits = true});
 
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitVariableDeclarationList(this);
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index 21f94bd..e56cca8 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -678,8 +678,53 @@
   @override
   visitVariableDeclarationList(VariableDeclarationList list) {
     out("var ");
-    visitCommaSeparated(list.declarations, ASSIGNMENT,
-        newInForInit: inForInit, newAtStatementBegin: false);
+    List<Node> nodes = list.declarations;
+    if (inForInit) {
+      visitCommaSeparated(nodes, ASSIGNMENT,
+          newInForInit: inForInit, newAtStatementBegin: false);
+    } else {
+      // Print 'big' declarations on their own line, while keeping adjacent
+      // small and uninitialized declarations on the same line.
+      bool useIndent = nodes.length > 1 && list.indentSplits;
+      if (useIndent) {
+        indentMore();
+      }
+      bool lastWasBig = false;
+      for (int i = 0; i < nodes.length; i++) {
+        Node node = nodes[i];
+        bool thisIsBig = !_isSmallInitialization(node);
+        if (i > 0) {
+          atStatementBegin = false;
+          out(",");
+          if (lastWasBig || thisIsBig) {
+            lineOut();
+            indent();
+          } else {
+            spaceOut();
+          }
+        }
+        visitNestedExpression(node, ASSIGNMENT,
+            newInForInit: inForInit, newAtStatementBegin: false);
+        lastWasBig = thisIsBig;
+      }
+      if (useIndent) {
+        indentLess();
+      }
+    }
+  }
+
+  static bool _isSmallInitialization(Node node) {
+    if (node is VariableInitialization) {
+      Node value = node.value;
+      if (value == null) return true;
+      if (value is This) return true;
+      if (value is LiteralNull) return true;
+      if (value is LiteralNumber) return true;
+      if (value is LiteralString && value.value.length <= 8) return true;
+      if (value is ObjectInitializer && value.properties.isEmpty) return true;
+      if (value is ArrayInitializer && value.elements.isEmpty) return true;
+    }
+    return false;
   }
 
   @override
diff --git a/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
index f86c1f1..d31c169 100644
--- a/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
+++ b/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
@@ -66,7 +66,9 @@
 
           /// 01: ok
           r"""function() {
-  var __goto = 0, __completer = NewCompleter(CompleterType), closures, v0, v1, v2, v3;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    closures, v0, v1, v2, v3;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -102,7 +104,9 @@
   await foo();
 }""", """
 function(a) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __self = this;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __self = this;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -147,7 +151,9 @@
     return 4;
   }""", """
 function(b) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __returnValue, __handler = 2, __currentError, __next = [], __helper;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __returnValue, __handler = 2, __currentError, __next = [], __helper;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1) {
       __currentError = __result;
@@ -257,7 +263,9 @@
   f = --foo1()[await foo2()];
 }""", """
 function(c) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), a, b, c, d, e, f, __temp1;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    a, b, c, d, e, f, __temp1;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -309,7 +317,9 @@
     h = foo1() && foo2();
   }""", """
 function(d2) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), a, b, c, d, e, f, g, h, __temp1;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    a, b, c, d, e, f, g, h, __temp1;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -441,7 +451,8 @@
   }
 }""", """
 function(x, y) {
-  var __goto = 0, __completer = NewCompleter(CompleterType);
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType);
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -525,7 +536,9 @@
   }
   """, """
 function(f) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), a;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    a;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -585,7 +598,9 @@
 }
 """, """
 function(g) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __returnValue, i, __temp1;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __returnValue, i, __temp1;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -665,7 +680,9 @@
   }
   """, """
 function(a, h) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), x, __temp1, __temp2;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    x, __temp1, __temp2;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -741,7 +758,9 @@
 }
 """, """
 function(c, i) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __handler = 1, __currentError, __next = [], x, y, __error, __error1;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __handler = 1, __currentError, __next = [], x, y, __error, __error1;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1) {
       __currentError = __result;
@@ -851,7 +870,9 @@
   }
   """, """
 function(x, y, j) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __temp1, __temp2, __temp3;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __temp1, __temp2, __temp3;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -929,7 +950,9 @@
   }
 }""", """
 function(x, y, k) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __returnValue, __temp1;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __returnValue, __temp1;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -1052,7 +1075,8 @@
     }
   }""", """
 function(l) {
-  var __goto = 0, __completer = NewCompleter(CompleterType);
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType);
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1)
       return rethrowHelper(__result, __completer);
@@ -1099,7 +1123,9 @@
     print(exception);
   }""", """
 function(m) {
-  var __goto = 0, __completer = NewCompleter(CompleterType), __handler = 1, __currentError, __next = [], exception, __exception;
+  var __goto = 0,
+    __completer = NewCompleter(CompleterType),
+    __handler = 1, __currentError, __next = [], exception, __exception;
   var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
     if (__errorCode === 1) {
       __currentError = __result;
diff --git a/tests/compiler/dart2js/sourcemaps/helpers/html_parts.dart b/tests/compiler/dart2js/sourcemaps/helpers/html_parts.dart
index 5724641..77dd7cf 100644
--- a/tests/compiler/dart2js/sourcemaps/helpers/html_parts.dart
+++ b/tests/compiler/dart2js/sourcemaps/helpers/html_parts.dart
@@ -84,7 +84,7 @@
     data['title'] = annotations.map((l) => l.title).join(',');
   }
   if (id != null) {
-    Set ids = annotations.map((l) => l.id).toSet();
+    Set ids = annotations.map<int>((l) => l.id).toSet();
     data['name'] = elementScheme.getName(id, ids);
     data['href'] = elementScheme.getHref(id, ids);
     data['onclick'] = elementScheme.onClick(id, ids);