Move frog/leg to lib/compiler/implementation.
Review URL: https://chromiumcodereview.appspot.com//9873021
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@5926 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/compiler/compiler.dart b/lib/compiler/compiler.dart
new file mode 100644
index 0000000..31152fd
--- /dev/null
+++ b/lib/compiler/compiler.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2012, 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('compiler');
+
+#import('../../lib/uri/uri.dart');
+#import('implementation/apiimpl.dart');
+
+// Unless explicitly allowed, passing null for any argument to the
+// methods of library will result in a NullPointerException being
+// thrown.
+
+/**
+ * Returns the source corresponding to [uri]. If no such source exists
+ * or if an error occur while fetching it, this method must throw an
+ * exception.
+ */
+typedef Future<String> ReadUriFromString(Uri uri);
+
+/**
+ * Invoked by the compiler to report diagnostics. If [uri] is null, so
+ * is [begin] and [end]. No other arguments may be null. If [uri] is
+ * not null, neither are [begin] and [end]. [uri] indicates the
+ * compilation unit from where the diagnostic originates. [begin] and
+ * [end] are zero-based character offsets from the beginning of the
+ * compilaton unit. [message] is the diagnostic message, and [fatal]
+ * indicates whether or not this diagnostic will prevent the compiler
+ * from returning null.
+ */
+typedef void DiagnosticHandler(Uri uri, int begin, int end,
+                               String message, bool fatal);
+
+/**
+ * Returns [script] compiled to JavaScript. If the compilation fails,
+ * null is returned and [handler] will have been invoked at least once
+ * with [:fatal == true:].
+ */
+Future<String> compile(Uri script,
+                       Uri libraryRoot,
+                       ReadUriFromString provider,
+                       DiagnosticHandler handler,
+                       [List<String> options = const []]) {
+  Compiler compiler = new Compiler(provider, handler, libraryRoot, options);
+  compiler.run(script);
+  String code = compiler.assembledCode;
+  Completer<String> completer = new Completer<String>();
+  completer.complete(code);
+  return completer.future;
+}
diff --git a/lib/compiler/implementation/README.txt b/lib/compiler/implementation/README.txt
new file mode 100644
index 0000000..f6afd6b
--- /dev/null
+++ b/lib/compiler/implementation/README.txt
@@ -0,0 +1,25 @@
+Leg is an experimental add-on to the Frog compiler that aims to
+explore areas of long-term interest:
+
+   * high-performance extensible scanner and parser
+   * concrete type inferencing
+   * fancy language tool support
+   * programming environment integration
+   * SSA-based intermediate representation
+   * adaptive compilation on the client
+   * ...
+
+Some of the things listed above are very experimental of nature and
+may prove themselves infeasible or unnecesary, so we want to work on
+them in a way that guarantees we will not disrupt the work being done
+to make Frog a fantastic compiler for Dart in its default setup.
+
+To keep things simple and allow quick experimentation, Leg will only
+support a subset of Dart and fall back on using the default Frog
+compiler for the remaining parts of the language. We expect Leg to be
+a complete and correct implementation of the supported subset
+throughout the implementation and experimentation so you should always
+be able to try Leg on any Dart project.
+
+The plan is to share code between the default Frog compiler and Leg
+where it makes sense and to learn from both code bases along the way.
diff --git a/lib/compiler/implementation/apiimpl.dart b/lib/compiler/implementation/apiimpl.dart
new file mode 100644
index 0000000..3b9b531
--- /dev/null
+++ b/lib/compiler/implementation/apiimpl.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2012, 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('leg_apiimpl');
+
+#import('leg.dart', prefix: 'leg');
+#import('elements/elements.dart', prefix: 'leg');
+#import('tree/tree.dart', prefix: 'leg');
+#import('ssa/tracer.dart', prefix: 'ssa');
+// TODO(ahe): Remove dependency on frog.
+#import('../../../frog/lang.dart', prefix: 'frog');
+#import('../compiler.dart');
+#import('../../uri/uri.dart');
+
+class Compiler extends leg.Compiler {
+  ReadUriFromString provider;
+  DiagnosticHandler handler;
+  Uri libraryRoot;
+  List<String> options;
+  bool mockableLibraryUsed = false;
+
+  Compiler(this.provider, this.handler, this.libraryRoot, this.options)
+    : super.withCurrentDirectory(null, tracer: new ssa.HTracer());
+
+  leg.LibraryElement scanBuiltinLibrary(String filename) {
+    Uri uri = libraryRoot.resolve(filename);
+    leg.LibraryElement library = scanner.loadLibrary(uri, null);
+    return library;
+  }
+
+  void log(message) {
+    handler(null, null, null, message, false);
+  }
+
+  leg.Script readScript(Uri uri, [leg.ScriptTag node]) {
+    if (uri.scheme == 'dart') {
+      uri = translateDartUri(uri, node);
+    }
+    String text = "";
+    try {
+      // TODO(ahe): We expect the future to be complete and call value
+      // directly. In effect, we don't support truly asynchronous API.
+      text = provider(uri).value;
+    } catch (var exception) {
+      cancel("${uri}: $exception", node: node);
+    }
+    frog.SourceFile sourceFile = new frog.SourceFile(uri.toString(), text);
+    return new leg.Script(uri, sourceFile);
+  }
+
+  translateDartUri(Uri uri, leg.ScriptTag node) {
+    String uriName = uri.toString();
+    // TODO(ahe): Clean this up.
+    if (uriName == 'dart:dom') {
+      mockableLibraryUsed = true;
+      return libraryRoot.resolve('../../../../lib/dom/frog/dom_frog.dart');
+    } else if (uriName == 'dart:html') {
+      mockableLibraryUsed = true;
+      return libraryRoot.resolve('../../../../lib/html/frog/html_frog.dart');
+    } else if (uriName == 'dart:json') {
+      return libraryRoot.resolve('../../../../lib/json/json.dart');
+    } else if (uriName == 'dart:isolate') {
+      return libraryRoot.resolve('../../../../lib/isolate/isolate_leg.dart');
+    } else if (uriName == 'dart:io') {
+      mockableLibraryUsed = true;
+      return libraryRoot.resolve('io.dart');
+    } else if (uriName == 'dart:utf') {
+      return libraryRoot.resolve('../../../../lib/utf/utf.dart');
+    } else if (uriName == 'dart:uri') {
+      return libraryRoot.resolve('../../../../lib/uri/uri.dart');
+    }
+    reportError(node, "library not found $uriName");
+  }
+
+  bool run(Uri uri) {
+    bool success = super.run(uri);
+    for (final task in tasks) {
+      log('${task.name} took ${task.timing}msec');
+    }
+    return success;
+  }
+
+  void reportDiagnostic(leg.SourceSpan span, String message, bool fatal) {
+    if (span === null) {
+      handler(null, null, null, message, fatal);
+    } else {
+      handler(span.uri, span.begin, span.end, message, fatal);
+    }
+  }
+
+  bool get isMockCompilation() {
+    return mockableLibraryUsed
+      && (options.indexOf('--allow-mock-compilation') !== -1);
+  }
+}
diff --git a/lib/compiler/implementation/colors.dart b/lib/compiler/implementation/colors.dart
new file mode 100644
index 0000000..c39f081
--- /dev/null
+++ b/lib/compiler/implementation/colors.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2012, 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('colors');
+
+final String GREEN_COLOR = '\u001b[32m';
+final String RED_COLOR = '\u001b[31m';
+final String MAGENTA_COLOR = '\u001b[35m';
+final String NO_COLOR = '\u001b[0m';
+
+String green(String string) => "${GREEN_COLOR}$string${NO_COLOR}";
+String red(String string) => "${RED_COLOR}$string${NO_COLOR}";
+String magenta(String string) => "${MAGENTA_COLOR}$string${NO_COLOR}";
diff --git a/lib/compiler/implementation/compile_time_constants.dart b/lib/compiler/implementation/compile_time_constants.dart
new file mode 100644
index 0000000..94c98c5
--- /dev/null
+++ b/lib/compiler/implementation/compile_time_constants.dart
@@ -0,0 +1,1115 @@
+// Copyright (c) 2012, 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.
+
+class Constant implements Hashable {
+  const Constant();
+
+  bool isNull() => false;
+  bool isBool() => false;
+  bool isTrue() => false;
+  bool isFalse() => false;
+  bool isInt() => false;
+  bool isDouble() => false;
+  bool isNum() => false;
+  bool isString() => false;
+  bool isList() => false;
+  bool isMap() => false;
+  bool isConstructedObject() => false;
+  /** Returns true if the constant is null, a bool, a number or a string. */
+  bool isPrimitive() => false;
+  /** Returns true if the constant is a list, a map or a constructed object. */
+  bool isObject() => false;
+
+  abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler);
+  /**
+    * Unless the constant can be emitted multiple times (as for numbers and
+    * strings) adds its canonical name to the buffer.
+    */
+  abstract void writeCanonicalizedJsCode(StringBuffer buffer,
+                                         ConstantHandler handler);
+  abstract List<Constant> getDependencies();
+}
+
+class PrimitiveConstant extends Constant {
+  abstract get value();
+  const PrimitiveConstant();
+  bool isPrimitive() => true;
+
+  bool operator ==(var other) {
+    if (other is !PrimitiveConstant) return false;
+    PrimitiveConstant otherPrimitive = other;
+    // We use == instead of === so that DartStrings compare correctly.
+    return value == otherPrimitive.value;
+  }
+
+  String toString() => value.toString();
+  // Primitive constants don't have dependencies.
+  List<Constant> getDependencies() => const <Constant>[];
+  abstract DartString toDartString();
+
+  void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) {
+    writeJsCode(buffer, handler);
+  }
+}
+
+class NullConstant extends PrimitiveConstant {
+  factory NullConstant() => const NullConstant._internal();
+  const NullConstant._internal();
+  bool isNull() => true;
+  get value() => null;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("(void 0)");
+  }
+
+  // The magic constant has no meaning. It is just a random value.
+  int hashCode() => 785965825;
+  DartString toDartString() => const LiteralDartString("null");
+}
+
+class NumConstant extends PrimitiveConstant {
+  abstract num get value();
+  const NumConstant();
+  bool isNum() => true;
+}
+
+class IntConstant extends NumConstant {
+  final int value;
+  factory IntConstant(int value) {
+    switch(value) {
+      case 0: return const IntConstant._internal(0);
+      case 1: return const IntConstant._internal(1);
+      case 2: return const IntConstant._internal(2);
+      case 3: return const IntConstant._internal(3);
+      case 4: return const IntConstant._internal(4);
+      case 5: return const IntConstant._internal(5);
+      case 6: return const IntConstant._internal(6);
+      case 7: return const IntConstant._internal(7);
+      case 8: return const IntConstant._internal(8);
+      case 9: return const IntConstant._internal(9);
+      case 10: return const IntConstant._internal(10);
+      case -1: return const IntConstant._internal(-1);
+      case -2: return const IntConstant._internal(-2);
+      default: return new IntConstant._internal(value);
+    }
+  }
+  const IntConstant._internal(this.value);
+  bool isInt() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("$value");
+  }
+
+  // We have to override the equality operator so that ints and doubles are
+  // treated as separate constants.
+  // The is [:!IntConstant:] check at the beginning of the function makes sure
+  // that we compare only equal to integer constants.
+  bool operator ==(var other) {
+    if (other is !IntConstant) return false;
+    IntConstant otherInt = other;
+    return value == otherInt.value;
+  }
+
+  int hashCode() => value.hashCode();
+  DartString toDartString() => new DartString.literal(value.toString());
+}
+
+class DoubleConstant extends NumConstant {
+  final double value;
+  factory DoubleConstant(double value) {
+    if (value.isNaN()) {
+      return const DoubleConstant._internal(double.NAN);
+    } else if (value == double.INFINITY) {
+      return const DoubleConstant._internal(double.INFINITY);
+    } else if (value == -double.INFINITY) {
+      return const DoubleConstant._internal(-double.INFINITY);
+    } else if (value == 0.0 && !value.isNegative()) {
+      return const DoubleConstant._internal(0.0);
+    } else if (value == 1.0) {
+      return const DoubleConstant._internal(1.0);
+    } else {
+      return new DoubleConstant._internal(value);
+    }
+  }
+  const DoubleConstant._internal(this.value);
+  bool isDouble() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    if (value.isNaN()) {
+      buffer.add("(0/0)");
+    } else if (value == double.INFINITY) {
+      buffer.add("(1/0)");
+    } else if (value == -double.INFINITY) {
+      buffer.add("(-1/0)");
+    } else {
+      buffer.add("$value");
+    }
+  }
+
+  bool operator ==(var other) {
+    if (other is !DoubleConstant) return false;
+    DoubleConstant otherDouble = other;
+    double otherValue = otherDouble.value;
+    if (value == 0.0 && otherValue == 0.0) {
+      return value.isNegative() == otherValue.isNegative();
+    } else if (value.isNaN()) {
+      return otherValue.isNaN();
+    } else {
+      return value == otherValue;
+    }
+  }
+
+  int hashCode() => value.hashCode();
+  DartString toDartString() => new DartString.literal(value.toString());
+}
+
+class BoolConstant extends PrimitiveConstant {
+  factory BoolConstant(value) {
+    return value ? new TrueConstant() : new FalseConstant();
+  }
+  const BoolConstant._internal();
+  bool isBool() => true;
+
+  BoolConstant unaryFold(String op) {
+    if (op == "!") return new BoolConstant(!value);
+    return null;
+  }
+
+  abstract BoolConstant negate();
+}
+
+class TrueConstant extends BoolConstant {
+  final bool value = true;
+
+  factory TrueConstant() => const TrueConstant._internal();
+  const TrueConstant._internal() : super._internal();
+  bool isTrue() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("true");
+  }
+
+  FalseConstant negate() => new FalseConstant();
+
+  bool operator ==(var other) => this === other;
+  // The magic constant is just a random value. It does not have any
+  // significance.
+  int hashCode() => 499;
+  DartString toDartString() => const LiteralDartString("true");
+}
+
+class FalseConstant extends BoolConstant {
+  final bool value = false;
+
+  factory FalseConstant() => const FalseConstant._internal();
+  const FalseConstant._internal() : super._internal();
+  bool isFalse() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("false");
+  }
+
+  TrueConstant negate() => new TrueConstant();
+
+  bool operator ==(var other) => this === other;
+  // The magic constant is just a random value. It does not have any
+  // significance.
+  int hashCode() => 536555975;
+  DartString toDartString() => const LiteralDartString("false");
+}
+
+class StringConstant extends PrimitiveConstant {
+  final DartString value;
+  int _hashCode;
+
+  StringConstant(this.value) {
+    // TODO(floitsch): cache StringConstants.
+    // TODO(floitsch): compute hashcode without calling toString() on the
+    // DartString.
+    _hashCode = value.slowToString().hashCode();
+  }
+  bool isString() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("'");
+    ConstantHandler.writeEscapedString(value, buffer, (reason) {
+      throw new CompilerCancelledException(reason);
+    });
+    buffer.add("'");
+  }
+
+  bool operator ==(var other) {
+    if (other is !StringConstant) return false;
+    StringConstant otherString = other;
+    return (_hashCode == otherString._hashCode) && (value == otherString.value);
+  }
+
+  int hashCode() => _hashCode;
+  DartString toDartString() => value;
+}
+
+class ObjectConstant extends Constant {
+  final Type type;
+
+  ObjectConstant(this.type);
+  bool isObject() => true;
+
+  // TODO(1603): The class should be marked as abstract, but the VM doesn't
+  // currently allow this.
+  abstract int hashCode();
+
+  void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) {
+    String name = handler.getNameForConstant(this);
+    String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype";
+    buffer.add("$isolatePrototype.$name");
+  }
+}
+
+class ListConstant extends ObjectConstant {
+  final List<Constant> entries;
+  int _hashCode;
+
+  ListConstant(Type type, this.entries) : super(type) {
+    // TODO(floitsch): create a better hash.
+    int hash = 0;
+    for (Constant input in entries) hash ^= input.hashCode();
+    _hashCode = hash;
+  }
+  bool isList() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    // TODO(floitsch): we should not need to go through the compiler to make
+    // the list constant.
+    String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype";
+    buffer.add("$isolatePrototype.makeConstantList");
+    buffer.add("([");
+    for (int i = 0; i < entries.length; i++) {
+      if (i != 0) buffer.add(", ");
+      Constant entry = entries[i];
+      entry.writeCanonicalizedJsCode(buffer, handler);
+    }
+    buffer.add("])");
+  }
+
+  bool operator ==(var other) {
+    if (other is !ListConstant) return false;
+    ListConstant otherList = other;
+    if (hashCode() != otherList.hashCode()) return false;
+    // TODO(floitsch): verify that the generic types are the same.
+    if (entries.length != otherList.entries.length) return false;
+    for (int i = 0; i < entries.length; i++) {
+      if (entries[i] != otherList.entries[i]) return false;
+    }
+    return true;
+  }
+
+  int hashCode() => _hashCode;
+
+  List<Constant> getDependencies() => entries;
+}
+
+class MapConstant extends ObjectConstant {
+  /** The dart class implementing constant map literals. */
+  static final SourceString DART_CLASS = const SourceString("ConstantMap");
+  static final SourceString LENGTH_NAME = const SourceString("length");
+  static final SourceString JS_OBJECT_NAME = const SourceString("_jsObject");
+  static final SourceString KEYS_NAME = const SourceString("_keys");
+
+  final ListConstant keys;
+  final List<Constant> values;
+  int _hashCode;
+
+  MapConstant(Type type, this.keys, this.values) : super(type) {
+    // TODO(floitsch): create a better hash.
+    int hash = 0;
+    for (Constant value in values) hash ^= value.hashCode();
+    _hashCode = hash;
+  }
+  bool isMap() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+
+    void writeJsMap() {
+      buffer.add("{");
+      for (int i = 0; i < keys.entries.length; i++) {
+        if (i != 0) buffer.add(", ");
+
+        StringConstant key = keys.entries[i];
+        key.writeJsCode(buffer, handler);
+        buffer.add(": ");
+        Constant value = values[i];
+        value.writeCanonicalizedJsCode(buffer, handler);
+      }
+      buffer.add("}");
+    }
+
+    void badFieldCountError() {
+      handler.compiler.internalError(
+          "Compiler and ConstantMap disagree on number of fields.");
+    }
+
+    ClassElement classElement = type.element;
+    buffer.add("new ");
+    buffer.add(handler.getJsConstructor(classElement));
+    buffer.add("(");
+    // The arguments of the JavaScript constructor for any given Dart class
+    // are in the same order as the members of the class element.
+    int emittedArgumentCount = 0;
+    for (Element element in classElement.members) {
+      if (element.name == LENGTH_NAME) {
+        buffer.add(keys.entries.length);
+      } else if (element.name == JS_OBJECT_NAME) {
+        writeJsMap();
+      } else if (element.name == KEYS_NAME) {
+        keys.writeCanonicalizedJsCode(buffer, handler);
+      } else {
+        // Skip methods.
+        if (element.kind == ElementKind.FIELD) badFieldCountError();
+        continue;
+      }
+      emittedArgumentCount++;
+      if (emittedArgumentCount == 3) {
+        break;  // All arguments have been emitted.
+      } else {
+        buffer.add(", ");
+      }
+    }
+    if (emittedArgumentCount != 3) badFieldCountError();
+    buffer.add(")");
+  }
+
+  bool operator ==(var other) {
+    if (other is !MapConstant) return false;
+    MapConstant otherMap = other;
+    if (hashCode() != otherMap.hashCode()) return false;
+    // TODO(floitsch): verify that the generic types are the same.
+    if (keys != otherMap.keys) return false;
+    for (int i = 0; i < values.length; i++) {
+      if (values[i] != otherMap.values[i]) return false;
+    }
+    return true;
+  }
+
+  int hashCode() => _hashCode;
+
+  List<Constant> getDependencies() {
+    List<Constant> result = <Constant>[keys];
+    result.addAll(values);
+    return result;
+  }
+}
+
+class ConstructedConstant extends ObjectConstant {
+  final List<Constant> fields;
+  int _hashCode;
+
+  ConstructedConstant(Type type, this.fields) : super(type) {
+    assert(type !== null);
+    // TODO(floitsch): create a better hash.
+    int hash = 0;
+    for (Constant field in fields) {
+      hash ^= field.hashCode();
+    }
+    hash ^= type.element.hashCode();
+    _hashCode = hash;
+  }
+  bool isConstructedObject() => true;
+
+  void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
+    buffer.add("new ");
+    buffer.add(handler.getJsConstructor(type.element));
+    buffer.add("(");
+    for (int i = 0; i < fields.length; i++) {
+      if (i != 0) buffer.add(", ");
+      Constant field = fields[i];
+      field.writeCanonicalizedJsCode(buffer, handler);
+    }
+    buffer.add(")");
+  }
+
+  bool operator ==(var otherVar) {
+    if (otherVar is !ConstructedConstant) return false;
+    ConstructedConstant other = otherVar;
+    if (hashCode() != other.hashCode()) return false;
+    // TODO(floitsch): verify that the (generic) types are the same.
+    if (type.element != other.type.element) return false;
+    if (fields.length != other.fields.length) return false;
+    for (int i = 0; i < fields.length; i++) {
+      if (fields[i] != other.fields[i]) return false;
+    }
+    return true;
+  }
+
+  int hashCode() => _hashCode;
+  List<Constant> getDependencies() => fields;
+}
+
+/**
+ * The [ConstantHandler] keeps track of compile-time constants,
+ * initializations of global and static fields, and default values of
+ * optional parameters.
+ */
+class ConstantHandler extends CompilerTask {
+  // Contains the initial value of fields. Must contain all static and global
+  // initializations of used fields. May contain caches for instance fields.
+  final Map<VariableElement, Constant> initialVariableValues;
+
+  // Map from compile-time constants to their JS name.
+  final Map<Constant, String> compiledConstants;
+
+  // The set of variable elements that are in the process of being computed.
+  final Set<VariableElement> pendingVariables;
+
+  ConstantHandler(Compiler compiler)
+      : initialVariableValues = new Map<VariableElement, Dynamic>(),
+        compiledConstants = new Map<Constant, String>(),
+        pendingVariables = new Set<VariableElement>(),
+        super(compiler);
+  String get name() => 'ConstantHandler';
+
+  void registerCompileTimeConstant(Constant constant) {
+    Function ifAbsentThunk = (() => compiler.namer.getFreshGlobalName("CTC"));
+    compiledConstants.putIfAbsent(constant, ifAbsentThunk);
+  }
+
+  /**
+   * Compiles the initial value of the given field and stores it in an internal
+   * map.
+   *
+   * [WorkItem] must contain a [VariableElement] refering to a global or
+   * static field.
+   */
+  void compileWorkItem(WorkItem work) {
+    measure(() {
+      assert(work.element.kind == ElementKind.FIELD
+             || work.element.kind == ElementKind.PARAMETER
+             || work.element.kind == ElementKind.FIELD_PARAMETER);
+      VariableElement element = work.element;
+      // Shortcut if it has already been compiled.
+      if (initialVariableValues.containsKey(element)) return;
+      compileVariableWithDefinitions(element, work.resolutionTree);
+      assert(pendingVariables.isEmpty());
+    });
+  }
+
+  Constant compileVariable(VariableElement element) {
+    return measure(() {
+      if (initialVariableValues.containsKey(element)) {
+        Constant result = initialVariableValues[element];
+        return result;
+      }
+      TreeElements definitions = compiler.analyzeElement(element);
+      Constant constant =  compileVariableWithDefinitions(element, definitions);
+      return constant;
+    });
+  }
+
+  Constant compileVariableWithDefinitions(VariableElement element,
+                                          TreeElements definitions) {
+    return measure(() {
+      Node node = element.parseNode(compiler);
+      if (pendingVariables.contains(element)) {
+        MessageKind kind = MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS;
+        compiler.reportError(node,
+                             new CompileTimeConstantError(kind, const []));
+      }
+      pendingVariables.add(element);
+
+      SendSet assignment = node.asSendSet();
+      Constant value;
+      if (assignment === null) {
+        // No initial value.
+        value = new NullConstant();
+      } else {
+        Node right = assignment.arguments.head;
+        value = compileNodeWithDefinitions(right, definitions);
+      }
+      initialVariableValues[element] = value;
+      pendingVariables.remove(element);
+      return value;
+    });
+  }
+
+  Constant compileNodeWithDefinitions(Node node, TreeElements definitions) {
+    return measure(() {
+      assert(node !== null);
+      CompileTimeConstantEvaluator evaluator =
+          new CompileTimeConstantEvaluator(definitions, compiler);
+      return evaluator.evaluate(node);
+    });
+  }
+
+  /**
+   * Returns a [List] of static non final fields that need to be initialized.
+   * The list must be evaluated in order since the fields might depend on each
+   * other.
+   */
+  List<VariableElement> getStaticNonFinalFieldsForEmission() {
+    return initialVariableValues.getKeys().filter((element) {
+      return element.kind == ElementKind.FIELD
+          && !element.isInstanceMember()
+          && !element.modifiers.isFinal();
+    });
+  }
+
+  /**
+   * Returns a [List] of static final fields that need to be initialized. The
+   * list must be evaluated in order since the fields might depend on each
+   * other.
+   */
+  List<VariableElement> getStaticFinalFieldsForEmission() {
+    return initialVariableValues.getKeys().filter((element) {
+      return element.kind == ElementKind.FIELD
+          && !element.isInstanceMember()
+          && element.modifiers.isFinal();
+    });
+  }
+
+  List<Constant> getConstantsForEmission() {
+    // We must emit dependencies before their uses.
+    Set<Constant> seenConstants = new Set<Constant>();
+    List<Constant> result = new List<Constant>();
+
+    void addConstant(Constant constant) {
+      if (!seenConstants.contains(constant)) {
+        constant.getDependencies().forEach(addConstant);
+        assert(!seenConstants.contains(constant));
+        result.add(constant);
+        seenConstants.add(constant);
+      }
+    }
+
+    compiledConstants.forEach((Constant key, ignored) => addConstant(key));
+    return result;
+  }
+
+  String getNameForConstant(Constant constant) {
+    return compiledConstants[constant];
+  }
+
+  StringBuffer writeJsCode(StringBuffer buffer, Constant value) {
+    value.writeJsCode(buffer, this);
+    return buffer;
+  }
+
+  StringBuffer writeJsCodeForVariable(StringBuffer buffer,
+                                      VariableElement element) {
+    if (!initialVariableValues.containsKey(element)) {
+      compiler.internalError("No initial value for given element",
+                             element: element);
+    }
+    Constant constant = initialVariableValues[element];
+    if (constant.isObject()) {
+      String name = compiledConstants[constant];
+      buffer.add("${compiler.namer.ISOLATE}.prototype.$name");
+    } else {
+      writeJsCode(buffer, constant);
+    }
+    return buffer;
+  }
+
+  /**
+   * Write the contents of the quoted string to a [StringBuffer] in
+   * a form that is valid as JavaScript string literal content.
+   * The string is assumed quoted by single quote characters.
+   */
+  static void writeEscapedString(DartString string,
+                                 StringBuffer buffer,
+                                 void cancel(String reason)) {
+    Iterator<int> iterator = string.iterator();
+    while (iterator.hasNext()) {
+      int code = iterator.next();
+      if (code === $SQ) {
+        buffer.add(@"\'");
+      } else if (code === $LF) {
+        buffer.add(@'\n');
+      } else if (code === $CR) {
+        buffer.add(@'\r');
+      } else if (code === $LS) {
+        // This Unicode line terminator and $PS are invalid in JS string
+        // literals.
+        buffer.add(@'\u2028');
+      } else if (code === $PS) {
+        buffer.add(@'\u2029');
+      } else if (code === $BACKSLASH) {
+        buffer.add(@'\\');
+      } else {
+        if (code > 0xffff) {
+          cancel('Unhandled non-BMP character: U+${code.toRadixString(16)}');
+        }
+        // TODO(lrn): Consider whether all codes above 0x7f really need to
+        // be escaped. We build a Dart string here, so it should be a literal
+        // stage that converts it to, e.g., UTF-8 for a JS interpreter.
+        if (code < 0x20) {
+          buffer.add(@'\x');
+          if (code < 0x10) buffer.add('0');
+          buffer.add(code.toRadixString(16));
+        } else if (code >= 0x80) {
+          if (code < 0x100) {
+            buffer.add(@'\x');
+            buffer.add(code.toRadixString(16));
+          } else {
+            buffer.add(@'\u');
+            if (code < 0x1000) {
+              buffer.add('0');
+            }
+            buffer.add(code.toRadixString(16));
+          }
+        } else {
+          buffer.add(new String.fromCharCodes(<int>[code]));
+        }
+      }
+    }
+  }
+
+  String getJsConstructor(ClassElement element) {
+    return compiler.namer.isolatePropertyAccess(element);
+  }
+}
+
+class CompileTimeConstantEvaluator extends AbstractVisitor {
+  final TreeElements elements;
+  final Compiler compiler;
+
+  CompileTimeConstantEvaluator(this.elements, this.compiler);
+
+  Constant evaluate(Node node) {
+    return node.accept(this);
+  }
+
+  visitNode(Node node) {
+    error(node);
+  }
+
+  Constant visitLiteralBool(LiteralBool node) {
+    return new BoolConstant(node.value);
+  }
+
+  Constant visitLiteralDouble(LiteralDouble node) {
+    return new DoubleConstant(node.value);
+  }
+
+  Constant visitLiteralInt(LiteralInt node) {
+    return new IntConstant(node.value);
+  }
+
+  Constant visitLiteralList(LiteralList node) {
+    if (!node.isConst()) error(node);
+    List<Constant> arguments = <Constant>[];
+    for (Link<Node> link = node.elements.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      arguments.add(evaluate(link.head));
+    }
+    // TODO(floitsch): get type from somewhere.
+    Type type = null;
+    Constant constant = new ListConstant(type, arguments);
+    compiler.constantHandler.registerCompileTimeConstant(constant);
+    return constant;
+  }
+
+  Constant visitLiteralMap(LiteralMap node) {
+    // TODO(floitsch): check for isConst, once the parser adds it into the node.
+    // if (!node.isConst()) error(node);
+    List<StringConstant> keys = <StringConstant>[];
+    List<Constant> values = <Constant>[];
+    bool hasProtoKey = false;
+    for (Link<Node> link = node.entries.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      LiteralMapEntry entry = link.head;
+      Constant key = evaluate(entry.key);
+      if (!key.isString() || entry.key.asLiteralString() === null) {
+        MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL;
+        compiler.reportError(entry.key, new ResolutionError(kind, const []));
+      }
+      // TODO(floitsch): make this faster.
+      StringConstant keyConstant = key;
+      if (keyConstant.value == new LiteralDartString("__proto__")) {
+        hasProtoKey = true;
+      }
+      keys.add(key);
+      values.add(evaluate(entry.value));
+    }
+    if (hasProtoKey) {
+      compiler.unimplemented("visitLiteralMap with __proto__ key",
+                             node: node);
+    }
+    // TODO(floitsch): this should be a List<String> type.
+    Type keysType = null;
+    ListConstant keysList = new ListConstant(keysType, keys);
+    compiler.constantHandler.registerCompileTimeConstant(keysList);
+    ClassElement classElement =
+        compiler.jsHelperLibrary.find(MapConstant.DART_CLASS);
+    classElement.ensureResolved(compiler);
+    // TODO(floitsch): copy over the generic type.
+    Type type = new SimpleType(classElement.name, classElement);
+    compiler.registerInstantiatedClass(classElement);
+    Constant constant = new MapConstant(type, keysList, values);
+    compiler.constantHandler.registerCompileTimeConstant(constant);
+    return constant;
+  }
+
+  Constant visitLiteralNull(LiteralNull node) {
+    return new NullConstant();
+  }
+
+  Constant visitLiteralString(LiteralString node) {
+    return new StringConstant(node.dartString);
+  }
+
+  Constant visitStringJuxtaposition(StringJuxtaposition node) {
+    StringConstant left = evaluate(node.first);
+    StringConstant right = evaluate(node.second);
+    return new StringConstant(new DartString.concat(left.value, right.value));
+  }
+
+  Constant visitStringInterpolation(StringInterpolation node) {
+    StringConstant initialString = evaluate(node.string);
+    DartString accumulator = initialString.value;
+    for (StringInterpolationPart part in node.parts) {
+      Constant expression = evaluate(part.expression);
+      DartString expressionString;
+      if (expression.isNum() || expression.isBool()) {
+        Object value = expression.value;
+        expressionString = new DartString.literal(value.toString());
+      } else if (expression.isString()) {
+        expressionString = expression.value;
+      } else {
+        error(part.expression);
+      }
+      accumulator = new DartString.concat(accumulator, expressionString);
+      StringConstant partString = evaluate(part.string);
+      accumulator = new DartString.concat(accumulator, partString.value);
+    };
+    return new StringConstant(accumulator);
+  }
+
+  // TODO(floitsch): provide better error-messages.
+  Constant visitSend(Send send) {
+    Element element = elements[send];
+    if (Elements.isStaticOrTopLevelField(element)) {
+      if (element.modifiers === null ||
+          !element.modifiers.isFinal()) {
+        error(send);
+      }
+      return compiler.compileVariable(element);
+    } else if (send.isPrefix) {
+      assert(send.isOperator);
+      Constant receiverConstant = evaluate(send.receiver);
+      Operator op = send.selector;
+      Constant folded;
+      switch (op.source.stringValue) {
+        case "!":
+          folded = const NotOperation().fold(receiverConstant);
+          break;
+        case "-":
+          folded = const NegateOperation().fold(receiverConstant);
+          break;
+        case "~":
+          folded = const BitNotOperation().fold(receiverConstant);
+          break;
+        default:
+          compiler.internalError("Unexpected operator.", node: op);
+          break;
+      }
+      if (folded === null) error(send);
+      return folded;
+    } else if (send.isOperator && !send.isPostfix) {
+      assert(send.argumentCount() == 1);
+      Constant left = evaluate(send.receiver);
+      Constant right = evaluate(send.argumentsNode.nodes.head);
+      Operator op = send.selector.asOperator();
+      Constant folded;
+      switch (op.source.stringValue) {
+        case "+":
+          if (left.isString() && !right.isString()) {
+            // At the moment only compile-time concatenation of two strings is
+            // allowed.
+            error(send);
+          }
+          folded = const AddOperation().fold(left, right);
+          break;
+        case "-":
+          folded = const SubtractOperation().fold(left, right);
+          break;
+        case "*":
+          folded = const MultiplyOperation().fold(left, right);
+          break;
+        case "/":
+          folded = const DivideOperation().fold(left, right);
+          break;
+        case "%":
+          folded = const ModuloOperation().fold(left, right);
+          break;
+        case "~/":
+          folded = const TruncatingDivideOperation().fold(left, right);
+          break;
+        case "|":
+          folded = const BitOrOperation().fold(left, right);
+          break;
+        case "&":
+          folded = const BitAndOperation().fold(left, right);
+          break;
+        case "^":
+          folded = const BitXorOperation().fold(left, right);
+          break;
+        case "||":
+          folded = const BooleanOr().fold(left, right);
+          break;
+        case "&&":
+          folded = const BooleanAnd().fold(left, right);
+          break;
+        case "<<":
+          folded = const ShiftLeftOperation().fold(left, right);
+          break;
+        case ">>":
+          folded = const ShiftRightOperation().fold(left, right);
+          break;
+        case "<":
+          folded = const LessOperation().fold(left, right);
+          break;
+        case "<=":
+          folded = const LessEqualOperation().fold(left, right);
+          break;
+        case ">":
+          folded = const GreaterOperation().fold(left, right);
+          break;
+        case ">=":
+          folded = const GreaterEqualOperation().fold(left, right);
+          break;
+        case "==":
+          if (left.isPrimitive() && right.isPrimitive()) { 
+            folded = const EqualsOperation().fold(left, right);
+          }
+          break;
+        case "===":
+          if (left.isPrimitive() && right.isPrimitive()) { 
+            folded = const IdentityOperation().fold(left, right);
+          }
+          break;
+        case "!=":
+          if (left.isPrimitive() && right.isPrimitive()) { 
+            BoolConstant areEquals = const EqualsOperation().fold(left, right);
+            if (areEquals === null) {
+              folded = null;
+            } else {
+              folded = areEquals.negate();
+            }
+          }
+          break;
+        case "!==":
+          if (left.isPrimitive() && right.isPrimitive()) { 
+            BoolConstant areIdentical =
+                const IdentityOperation().fold(left, right);
+            if (areIdentical === null) {
+              folded = null;
+            } else {
+              folded = areIdentical.negate();
+            }
+          }
+          break;
+        default:
+          compiler.internalError("Unexpected operator.", node: op);
+          break;
+      }
+      if (folded === null) error(send);
+      return folded;
+    }
+    return super.visitSend(send);
+  }
+
+  visitSendSet(SendSet node) {
+    error(node);
+  }
+
+  /** Returns the list of constants that are passed to the static function. */
+  List<Constant> evaluateArgumentsToConstructor(Send send,
+                                                FunctionElement target) {
+    FunctionParameters parameters = target.computeParameters(compiler);
+    List<Constant> arguments = <Constant>[];
+    Selector selector = elements.getSelector(send);
+
+    Function compileArgument = evaluate;
+    Function compileConstant = compiler.compileVariable;
+    bool succeeded = selector.addSendArgumentsToList(
+        send, arguments, parameters, compileArgument, compileConstant);
+    if (!succeeded) error(send);
+    return arguments;
+  }
+
+  Constant visitNewExpression(NewExpression node) {
+    if (!node.isConst()) error(node);
+
+    FunctionElement constructor = elements[node.send];
+    ClassElement classElement = constructor.enclosingElement;
+    if (classElement.isInterface()) {
+      compiler.resolver.resolveMethodElement(constructor);
+      constructor = constructor.defaultImplementation;
+      classElement = constructor.enclosingElement;
+    }
+
+    List<Constant> arguments =
+        evaluateArgumentsToConstructor(node.send, constructor);
+    ConstructorEvaluator evaluator =
+        new ConstructorEvaluator(constructor, compiler);
+    evaluator.evaluateConstructorFieldValues(arguments);
+    List<Constant>jsNewArguments = evaluator.buildJsNewArguments(classElement);
+
+    compiler.registerInstantiatedClass(classElement);
+    // TODO(floitsch): take generic types into account.
+    Type type = classElement.computeType(compiler);
+    Constant constant = new ConstructedConstant(type, jsNewArguments);
+    compiler.constantHandler.registerCompileTimeConstant(constant);
+    return constant;
+  }
+
+  Constant visitParenthesizedExpression(ParenthesizedExpression node) {
+    return node.expression.accept(this);
+  }
+
+  error(Node node) {
+    // TODO(floitsch): get the list of constants that are currently compiled
+    // and present some kind of stack-trace.
+    MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT;
+    compiler.reportError(node, new CompileTimeConstantError(kind, const []));
+  }
+}
+
+class ConstructorEvaluator extends CompileTimeConstantEvaluator {
+  FunctionElement constructor;
+  final Map<Element, Constant> definitions;
+  final Map<Element, Constant> fieldValues;
+
+  ConstructorEvaluator(FunctionElement constructor, Compiler compiler)
+      : this.constructor = constructor,
+        this.definitions = new Map<Element, Constant>(),
+        this.fieldValues = new Map<Element, Constant>(),
+        super(compiler.resolver.resolveMethodElement(constructor),
+              compiler);
+
+  Constant visitSend(Send send) {
+    Element element = elements[send];
+    if (Elements.isLocal(element)) {
+      Constant constant = definitions[element];
+      if (constant === null) {
+        compiler.internalError("Local variable without value", node: send);
+      }
+      return constant;
+    }
+    return super.visitSend(send);
+  }
+
+  /**
+   * Given the arguments (a list of constants) assigns them to the parameters,
+   * updating the definitions map. If the constructor has field-initializer
+   * parameters (like [:this.x:]), also updates the [fieldValues] map.
+   */ 
+  void assignArgumentsToParameters(List<Constant> arguments) {
+    // Assign arguments to parameters.
+    FunctionParameters parameters = constructor.computeParameters(compiler);
+    int index = 0;
+    parameters.forEachParameter((Element parameter) {
+      Constant argument = arguments[index++];
+      definitions[parameter] = argument;
+      if (parameter.kind == ElementKind.FIELD_PARAMETER) {
+        FieldParameterElement fieldParameterElement = parameter;
+        fieldValues[fieldParameterElement.fieldElement] = argument;
+      }
+    });    
+  }
+
+  void evaluateSuperOrRedirectSend(FunctionElement targetConstructor,
+                                   List<Constant> targetArguments) {
+    ConstructorEvaluator evaluator =
+        new ConstructorEvaluator(targetConstructor, compiler);
+    evaluator.evaluateConstructorFieldValues(targetArguments);
+    // Copy over the fieldValues from the super/redirect-constructor.
+    evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value);
+  }
+
+  /**
+   * Runs through the initializers of the given [constructor] and updates
+   * the [fieldValues] map.
+   */
+  void evaluateConstructorInitializers() {
+    FunctionExpression functionNode = constructor.parseNode(compiler);
+    NodeList initializerList = functionNode.initializers;
+
+    bool foundSuperOrRedirect = false;
+
+    if (initializerList !== null) {
+      for (Link<Node> link = initializerList.nodes;
+           !link.isEmpty();
+           link = link.tail) {
+        assert(link.head is Send);
+        if (link.head is !SendSet) {
+          // A super initializer or constructor redirection.
+          Send call = link.head;
+          FunctionElement targetConstructor = elements[call];
+          List<Constant> targetArguments =
+              evaluateArgumentsToConstructor(call, targetConstructor);
+          evaluateSuperOrRedirectSend(targetConstructor, targetArguments);
+          foundSuperOrRedirect = true;
+        } else {
+          // A field initializer.
+          SendSet init = link.head;
+          Link<Node> initArguments = init.arguments;
+          assert(!initArguments.isEmpty() && initArguments.tail.isEmpty());
+          Constant fieldValue = evaluate(initArguments.head);
+          fieldValues[elements[init]] = fieldValue;
+        }
+      }
+    }
+
+    if (!foundSuperOrRedirect) {
+      // No super initializer found. Try to find the default constructor if
+      // the class is not Object.
+      ClassElement enclosingClass = constructor.enclosingElement;
+      ClassElement superClass = enclosingClass.superclass;
+      if (enclosingClass != compiler.objectClass) {
+        assert(superClass !== null);
+        assert(superClass.isResolved);
+        FunctionElement targetConstructor =
+            superClass.lookupConstructor(superClass.name);
+        if (targetConstructor === null) {
+          compiler.internalError("no default constructor available");
+        }
+        evaluateSuperOrRedirectSend(targetConstructor, const <Constant>[]);
+      }
+    }
+  }
+
+  /**
+   * Simulates the execution of the [constructor] with the given
+   * [arguments] to obtain the field values that need to be passed to the
+   * native JavaScript constructor.
+   */
+  void evaluateConstructorFieldValues(List<Constant> arguments) {
+    compiler.withCurrentElement(constructor, () {
+      assignArgumentsToParameters(arguments);
+      evaluateConstructorInitializers();
+    });
+  }
+
+  List<Constant> buildJsNewArguments(ClassElement classElement) {
+    List<Constant> jsNewArguments = <Constant>[];
+    // TODO(floitsch): share this code with the emitter, so that we don't
+    // need to care about the order of fields here.
+    while (classElement != compiler.objectClass) {
+      for (Element member in classElement.members) {
+        if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
+          Constant fieldValue = fieldValues[member];
+          if (fieldValue === null) {
+            // Use the default value.
+            fieldValue = compiler.compileVariable(member);
+          }
+          jsNewArguments.add(fieldValue);
+        }
+      }
+      classElement = classElement.superclass;
+    } 
+    return jsNewArguments;
+  }
+}
diff --git a/lib/compiler/implementation/compiler.dart b/lib/compiler/implementation/compiler.dart
new file mode 100644
index 0000000..cf51d9c
--- /dev/null
+++ b/lib/compiler/implementation/compiler.dart
@@ -0,0 +1,552 @@
+// Copyright (c) 2012, 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.
+
+class WorkItem {
+  final Element element;
+  TreeElements resolutionTree;
+  Function run;
+  bool allowSpeculativeOptimization = true;
+  List<HTypeGuard> guards = const <HTypeGuard>[];
+
+  WorkItem.toCompile(this.element) : resolutionTree = null {
+    run = this.compile;
+  }
+
+  WorkItem.toCodegen(this.element, this.resolutionTree) {
+    run = this.codegen;
+  }
+
+  bool isAnalyzed() => resolutionTree != null;
+
+  int hashCode() => element.hashCode();
+
+  String compile(Compiler compiler) {
+    return compiler.compile(this);
+  }
+
+  String codegen(Compiler compiler) {
+    return compiler.codegen(this);
+  }
+}
+
+class Compiler implements DiagnosticListener {
+  Queue<WorkItem> worklist;
+  Universe universe;
+  String assembledCode;
+  Namer namer;
+  Types types;
+  final String currentDirectory;
+
+  final Tracer tracer;
+
+  CompilerTask measuredTask;
+  Element _currentElement;
+  LibraryElement coreLibrary;
+  LibraryElement coreImplLibrary;
+  LibraryElement isolateLibrary;
+  LibraryElement jsHelperLibrary;
+  LibraryElement mainApp;
+  ClassElement objectClass;
+  ClassElement closureClass;
+  ClassElement dynamicClass;
+  ClassElement boolClass;
+  ClassElement numClass;
+  ClassElement intClass;
+  ClassElement doubleClass;
+  ClassElement stringClass;
+  ClassElement functionClass;
+  ClassElement nullClass;
+  ClassElement listClass;
+
+  Element get currentElement() => _currentElement;
+  withCurrentElement(Element element, f()) {
+    Element old = currentElement;
+    _currentElement = element;
+    try {
+      return f();
+    } finally {
+      _currentElement = old;
+    }
+  }
+
+  List<CompilerTask> tasks;
+  ScannerTask scanner;
+  DietParserTask dietParser;
+  ParserTask parser;
+  TreeValidatorTask validator;
+  ResolverTask resolver;
+  TypeCheckerTask checker;
+  SsaBuilderTask builder;
+  SsaOptimizerTask optimizer;
+  SsaCodeGeneratorTask generator;
+  CodeEmitterTask emitter;
+  ConstantHandler constantHandler;
+  EnqueueTask enqueuer;
+
+  static final SourceString MAIN = const SourceString('main');
+  static final SourceString NO_SUCH_METHOD = const SourceString('noSuchMethod');
+  static final SourceString NO_SUCH_METHOD_EXCEPTION =
+      const SourceString('NoSuchMethodException');
+  static final SourceString START_ROOT_ISOLATE =
+      const SourceString('startRootIsolate');
+  bool enabledNoSuchMethod = false;
+
+  bool workListIsClosed = false;
+
+  Stopwatch codegenProgress;
+
+  Compiler.withCurrentDirectory(String this.currentDirectory,
+                                [this.tracer = const Tracer()])
+      : types = new Types(),
+        universe = new Universe(),
+        worklist = new Queue<WorkItem>(),
+        codegenProgress = new Stopwatch.start() {
+    namer = new Namer(this);
+    constantHandler = new ConstantHandler(this);
+    scanner = new ScannerTask(this);
+    dietParser = new DietParserTask(this);
+    parser = new ParserTask(this);
+    validator = new TreeValidatorTask(this);
+    resolver = new ResolverTask(this);
+    checker = new TypeCheckerTask(this);
+    builder = new SsaBuilderTask(this);
+    optimizer = new SsaOptimizerTask(this);
+    generator = new SsaCodeGeneratorTask(this);
+    emitter = new CodeEmitterTask(this);
+    enqueuer = new EnqueueTask(this);
+    tasks = [scanner, dietParser, parser, resolver, checker,
+             builder, optimizer, generator,
+             emitter, constantHandler, enqueuer];
+  }
+
+  void ensure(bool condition) {
+    if (!condition) cancel('failed assertion in leg');
+  }
+
+  void unimplemented(String methodName,
+                     [Node node, Token token, HInstruction instruction,
+                      Element element]) {
+    internalError("$methodName not implemented",
+                  node, token, instruction, element);
+  }
+
+  void internalError(String message,
+                     [Node node, Token token, HInstruction instruction,
+                      Element element]) {
+    cancel("${red('internal error:')} $message",
+           node, token, instruction, element);
+  }
+
+  void internalErrorOnElement(Element element, String message) {
+    withCurrentElement(element, () {
+      internalError(message, element: element);
+    });
+  }
+
+  void cancel([String reason, Node node, Token token,
+               HInstruction instruction, Element element]) {
+    SourceSpan span = const SourceSpan(null, null, null);
+    if (node !== null) {
+      span = spanFromNode(node);
+    } else if (token !== null) {
+      span = spanFromTokens(token, token);
+    } else if (instruction !== null) {
+      span = spanFromElement(currentElement);
+    } else if (element !== null) {
+      span = spanFromElement(element);
+    }
+    reportDiagnostic(span, red(reason), true);
+    throw new CompilerCancelledException(reason);
+  }
+
+  void log(message) {
+    reportDiagnostic(null, message, false);
+  }
+
+  void enqueue(WorkItem work) {
+    if (workListIsClosed) {
+      internalErrorOnElement(work.element, "work list is closed");
+    }
+    worklist.add(work);
+  }
+
+  bool run(Uri uri) {
+    try {
+      runCompiler(uri);
+    } catch (CompilerCancelledException exception) {
+      log(exception.toString());
+      log('compilation failed');
+      return false;
+    }
+    tracer.close();
+    log('compilation succeeded');
+    return true;
+  }
+
+  void enableNoSuchMethod(Element element) {
+    if (enabledNoSuchMethod) return;
+    if (element.enclosingElement == objectClass) return;
+    enabledNoSuchMethod = true;
+    enqueuer.registerInvocation(NO_SUCH_METHOD, new Invocation(2));
+  }
+
+  void enableIsolateSupport(LibraryElement element) {
+    isolateLibrary = element;
+    addToWorkList(element.find(START_ROOT_ISOLATE));
+  }
+
+  bool hasIsolateSupport() => isolateLibrary !== null;
+
+  void onLibraryLoaded(LibraryElement library, Uri uri) {
+    if (uri.toString() == 'dart:isolate') {
+      enableIsolateSupport(library);
+    }
+    if (dynamicClass !== null) {
+      // When loading the built-in libraries, dynamicClass is null. We
+      // take advantage of this as core and coreimpl import js_helper
+      // and see Dynamic this way.
+      withCurrentElement(dynamicClass, () {
+        library.define(dynamicClass, this);
+      });
+    }
+  }
+
+  abstract LibraryElement scanBuiltinLibrary(String filename);
+
+  void initializeSpecialClasses() {
+    objectClass = coreLibrary.find(const SourceString('Object'));
+    boolClass = coreLibrary.find(const SourceString('bool'));
+    numClass = coreLibrary.find(const SourceString('num'));
+    intClass = coreLibrary.find(const SourceString('int'));
+    doubleClass = coreLibrary.find(const SourceString('double'));
+    stringClass = coreLibrary.find(const SourceString('String'));
+    functionClass = coreLibrary.find(const SourceString('Function'));
+    listClass = coreLibrary.find(const SourceString('List'));
+    closureClass = jsHelperLibrary.find(const SourceString('Closure'));
+    dynamicClass = jsHelperLibrary.find(const SourceString('Dynamic'));
+    nullClass = jsHelperLibrary.find(const SourceString('Null'));
+  }
+
+  void scanBuiltinLibraries() {
+    coreImplLibrary = scanBuiltinLibrary('coreimpl.dart');
+    jsHelperLibrary = scanBuiltinLibrary('js_helper.dart');
+    coreLibrary = scanBuiltinLibrary('core.dart');
+
+    // Since coreLibrary import the libraries "coreimpl", and
+    // "js_helper", coreLibrary is null when they are being built. So
+    // we add the implicit import of coreLibrary now. This can be
+    // cleaned up when we have proper support for "dart:core" and
+    // don't need to access it through the field "coreLibrary".
+    // TODO(ahe): Clean this up as described above.
+    scanner.importLibrary(coreImplLibrary, coreLibrary, null);
+    scanner.importLibrary(jsHelperLibrary, coreLibrary, null);
+    addForeignFunctions(jsHelperLibrary);
+
+    universe.libraries['dart:core'] = coreLibrary;
+    universe.libraries['dart:coreimpl'] = coreImplLibrary;
+
+    initializeSpecialClasses();
+  }
+
+  /** Define the JS helper functions in the given library. */
+  void addForeignFunctions(LibraryElement library) {
+    library.define(new ForeignElement(
+        const SourceString('JS'), library), this);
+    library.define(new ForeignElement(
+        const SourceString('UNINTERCEPTED'), library), this);
+    library.define(new ForeignElement(
+        const SourceString('JS_HAS_EQUALS'), library), this);
+    library.define(new ForeignElement(
+        const SourceString('JS_CURRENT_ISOLATE'), library), this);
+    library.define(new ForeignElement(
+        const SourceString('JS_CALL_IN_ISOLATE'), library), this);
+    library.define(new ForeignElement(
+        const SourceString('DART_CLOSURE_TO_JS'), library), this);
+  }
+
+  void runCompiler(Uri uri) {
+    scanBuiltinLibraries();
+    mainApp = scanner.loadLibrary(uri, null);
+    final Element mainMethod = mainApp.find(MAIN);
+    if (mainMethod === null) {
+      withCurrentElement(mainApp, () => cancel('Could not find $MAIN'));
+    } else {
+      withCurrentElement(mainMethod, () {
+        if (!mainMethod.isFunction()) {
+          cancel('main is not a function', element: mainMethod);
+        }
+        FunctionParameters parameters = mainMethod.computeParameters(this);
+        if (parameters.parameterCount > 0) {
+          cancel('main cannot have parameters', element: mainMethod);
+        }
+      });
+    }
+    native.processNativeClasses(this, universe.libraries.getValues());
+    enqueue(new WorkItem.toCompile(mainMethod));
+    codegenProgress.reset();
+    while (!worklist.isEmpty()) {
+      WorkItem work = worklist.removeLast();
+      withCurrentElement(work.element, () => (work.run)(this));
+    }
+    workListIsClosed = true;
+    assert(enqueuer.checkNoEnqueuedInvokedInstanceMethods());
+    enqueuer.registerFieldClosureInvocations();
+    emitter.assembleProgram();
+    if (!worklist.isEmpty()) {
+      internalErrorOnElement(worklist.first().element,
+                             "work list is not empty");
+    }
+  }
+
+  TreeElements analyzeElement(Element element) {
+    assert(parser !== null);
+    Node tree = parser.parse(element);
+    validator.validate(tree);
+    TreeElements elements = resolver.resolve(element);
+    checker.check(tree, elements);
+    return elements;
+  }
+
+  TreeElements analyze(WorkItem work) {
+    work.resolutionTree = analyzeElement(work.element);
+    return work.resolutionTree;
+  }
+
+  String codegen(WorkItem work) {
+    if (codegenProgress.elapsedInMs() > 500) {
+      // TODO(ahe): Add structured diagnostics to the compiler API and
+      // use it to separate this from the --verbose option.
+      log('compiled ${universe.generatedCode.length} methods');
+      codegenProgress.reset();
+    }
+    if (work.element.kind.category == ElementCategory.VARIABLE) {
+      constantHandler.compileWorkItem(work);
+      return null;
+    } else {
+      HGraph graph = builder.build(work);
+      optimizer.optimize(work, graph);
+      if (work.allowSpeculativeOptimization
+          && optimizer.trySpeculativeOptimizations(work, graph)) {
+        String code = generator.generateBailoutMethod(work, graph);
+        universe.addBailoutCode(work, code);
+        optimizer.prepareForSpeculativeOptimizations(work, graph);
+        optimizer.optimize(work, graph);
+        code = generator.generateMethod(work, graph);
+        universe.addGeneratedCode(work, code);
+        return code;
+      } else {
+        String code = generator.generateMethod(work, graph);
+        universe.addGeneratedCode(work, code);
+        return code;
+      }
+    }
+  }
+
+  String compile(WorkItem work) {
+    String code = universe.generatedCode[work.element];
+    if (code !== null) return code;
+    analyze(work);
+    return codegen(work);
+  }
+
+  void addToWorkList(Element element) {
+    if (workListIsClosed) {
+      internalErrorOnElement(element, "work list is closed");
+    }
+    if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) {
+      registerInstantiatedClass(element.enclosingElement);
+    }
+    worklist.add(new WorkItem.toCompile(element));
+  }
+
+  void registerStaticUse(Element element) {
+    addToWorkList(element);
+  }
+
+  void registerGetOfStaticFunction(FunctionElement element) {
+    registerStaticUse(element);
+    universe.staticFunctionsNeedingGetter.add(element);
+  }
+
+  void registerDynamicInvocation(SourceString methodName, Selector selector) {
+    assert(selector !== null);
+    enqueuer.registerInvocation(methodName, selector);
+  }
+
+  void registerDynamicGetter(SourceString methodName) {
+    enqueuer.registerGetter(methodName);
+  }
+
+  void registerDynamicSetter(SourceString methodName) {
+    enqueuer.registerSetter(methodName);
+  }
+
+  void registerInstantiatedClass(ClassElement element) {
+    universe.instantiatedClasses.add(element);
+    enqueuer.onRegisterInstantiatedClass(element);
+  }
+
+  // TODO(ngeoffray): This should get a type.
+  void registerIsCheck(Element element) {
+    universe.isChecks.add(element);
+  }
+
+  Type resolveType(ClassElement element) {
+    return withCurrentElement(element, () => resolver.resolveType(element));
+  }
+
+  FunctionParameters resolveSignature(FunctionElement element) {
+    return withCurrentElement(element,
+                              () => resolver.resolveSignature(element));
+  }
+
+  Constant compileVariable(VariableElement element) {
+    return withCurrentElement(element, () {
+      return constantHandler.compileVariable(element);
+    });
+  }
+
+  reportWarning(Node node, var message) {
+    if (message is ResolutionWarning) {
+      // TODO(ahe): Don't supress this warning when we support type variables.
+      if (message.message.kind === MessageKind.CANNOT_RESOLVE_TYPE) return;
+    } else if (message is TypeWarning) {
+      // TODO(ahe): Don't supress these warning when the type checker
+      // is more complete.
+      if (message.message.kind === MessageKind.NOT_ASSIGNABLE) return;
+      if (message.message.kind === MessageKind.MISSING_RETURN) return;
+      if (message.message.kind === MessageKind.ADDITIONAL_ARGUMENT) return;
+      if (message.message.kind === MessageKind.METHOD_NOT_FOUND) return;
+    }
+    SourceSpan span = spanFromNode(node);
+    reportDiagnostic(span, "${magenta('warning:')} $message", false);
+  }
+
+  reportError(Node node, var message) {
+    SourceSpan span = spanFromNode(node);
+    reportDiagnostic(span, "${red('error:')} $message", true);
+    throw new CompilerCancelledException(message.toString());
+  }
+
+  abstract void reportDiagnostic(SourceSpan span, String message, bool fatal);
+
+  SourceSpan spanFromTokens(Token begin, Token end) {
+    if (begin === null || end === null) {
+      // TODO(ahe): We can almost always do better. Often it is only
+      // end that is null. Otherwise, we probably know the current
+      // URI.
+      throw 'cannot find tokens to produce error message';
+    }
+    final startOffset = begin.charOffset;
+    // TODO(ahe): Compute proper end offset in token. Right now we use
+    // the position of the next token. We want to preserve the
+    // invariant that endOffset > startOffset, but for EOF the
+    // charoffset of the next token may be [startOffset]. This can
+    // also happen for synthetized tokens that are produced during
+    // error handling.
+    final endOffset =
+      Math.max((end.next !== null) ? end.next.charOffset : 0, startOffset + 1);
+    assert(endOffset > startOffset);
+    Uri uri = currentElement.getCompilationUnit().script.uri;
+    return new SourceSpan(uri, startOffset, endOffset);
+  }
+
+  SourceSpan spanFromNode(Node node) {
+    return spanFromTokens(node.getBeginToken(), node.getEndToken());
+  }
+
+  SourceSpan spanFromElement(Element element) {
+    if (element.position() === null) {
+      // Sometimes, the backend fakes up elements that have no
+      // position. So we use the enclosing element instead. It is
+      // not a good error location, but cancel really is "internal
+      // error" or "not implemented yet", so the vicinity is good
+      // enough for now.
+      element = element.enclosingElement;
+      // TODO(ahe): I plan to overhaul this infrastructure anyways.
+    }
+    if (element === null) {
+      element = currentElement;
+    }
+    Token position = element.position();
+    if (position === null) {
+      // TODO(ahe): Find the enclosing library.
+      return const SourceSpan(null, null, null);
+    }
+    return spanFromTokens(position, position);
+  }
+
+  Script readScript(Uri uri, [ScriptTag node]) {
+    unimplemented('Compiler.readScript');
+  }
+
+  String get legDirectory() {
+    unimplemented('Compiler.legDirectory');
+  }
+
+  Element findHelper(SourceString name) => jsHelperLibrary.find(name);
+
+  bool get isMockCompilation() => false;
+}
+
+class CompilerTask {
+  final Compiler compiler;
+  final Stopwatch watch;
+
+  CompilerTask(this.compiler) : watch = new Stopwatch();
+
+  String get name() => 'Unknown task';
+  int get timing() => watch.elapsedInMs();
+
+  measure(Function action) {
+    // TODO(kasperl): Do we have to worry about exceptions here?
+    CompilerTask previous = compiler.measuredTask;
+    compiler.measuredTask = this;
+    if (previous !== null) previous.watch.stop();
+    watch.start();
+    var result = action();
+    watch.stop();
+    if (previous !== null) previous.watch.start();
+    compiler.measuredTask = previous;
+    return result;
+  }
+}
+
+class CompilerCancelledException implements Exception {
+  final String reason;
+  CompilerCancelledException(this.reason);
+
+  String toString() {
+    String banner = 'compiler cancelled';
+    return (reason !== null) ? '$banner: $reason' : '$banner';
+  }
+}
+
+interface Tracer default LTracer {
+  const Tracer();
+  final bool enabled;
+  void traceCompilation(String methodName);
+  void traceGraph(String name, var graph);
+  void close();
+}
+
+// TODO(ahe): Remove when the VM supports implicit interfaces.
+class LTracer implements Tracer {
+  const LTracer();
+  final bool enabled = false;
+  void traceCompilation(String methodName) {
+  }
+  void traceGraph(String name, var graph) {
+  }
+  void close() {
+  }
+}
+
+class SourceSpan {
+  final Uri uri;
+  final int begin;
+  final int end;
+
+  const SourceSpan(this.uri, this.begin, this.end);
+}
diff --git a/lib/compiler/implementation/dart2js.dart b/lib/compiler/implementation/dart2js.dart
new file mode 100644
index 0000000..094a203
--- /dev/null
+++ b/lib/compiler/implementation/dart2js.dart
@@ -0,0 +1,153 @@
+// Copyright (c) 2012, 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('dart2js');
+
+#import('dart:io');
+#import('dart:utf');
+
+#import('../../uri/uri.dart');
+#import('../compiler.dart', prefix: 'api');
+#import('io/io.dart', prefix: 'io');
+#import('colors.dart');
+#import('source_file.dart');
+
+String relativize(Uri base, Uri uri) {
+  if (base.scheme == 'file' &&
+      base.scheme == uri.scheme &&
+      base.userInfo == uri.userInfo &&
+      base.domain == uri.domain &&
+      base.port == uri.port &&
+      uri.query == "" && uri.fragment == "") {
+    if (uri.path.startsWith(base.path)) {
+      return uri.path.substring(base.path.length);
+    }
+    List<String> uriParts = uri.path.split('/');
+    List<String> baseParts = base.path.split('/');
+    int common = 0;
+    int length = Math.min(uriParts.length, baseParts.length);
+    while (common < length && uriParts[common] == baseParts[common]) {
+      common++;
+    }
+    StringBuffer sb = new StringBuffer();
+    for (int i = common + 1; i < baseParts.length; i++) {
+      sb.add('../');
+    }
+    for (int i = common; i < uriParts.length - 1; i++) {
+      sb.add('${uriParts[i]}/');
+    }
+    sb.add('${uriParts.last()}');
+    return sb.toString();
+  }
+  return uri.toString();
+}
+
+void compile(List<String> argv) {
+  Uri cwd = new Uri(scheme: 'file', path: io.getCurrentDirectory());
+  bool throwOnError = false;
+  bool showWarnings = true;
+  bool verbose = false;
+  Uri libraryRoot = cwd;
+  Uri out = cwd.resolve('out.js');
+
+  List<String> arguments = <String>[];
+  for (String argument in argv) {
+    if ('--throw-on-error' == argument) {
+      throwOnError = true;
+    } else if ('--suppress-warnings' == argument) {
+      showWarnings = false;
+    } else if ('--verbose' == argument) {
+      verbose = true;
+    } else if (argument.startsWith('--library-root=')) {
+      String path = argument.substring(argument.indexOf('=') + 1);
+      if (!path.endsWith("/")) path = "$path/";
+      libraryRoot = cwd.resolve(path);
+    } else if (argument.startsWith('--out=')) {
+      String path = argument.substring(argument.indexOf('=') + 1);
+      out = cwd.resolve(path);
+    } else if (argument.startsWith('-')) {
+      throw new AbortLeg('unknown option $argument');
+    } else {
+      arguments.add(argument);
+    }
+  }
+  if (arguments.isEmpty()) {
+    throw new AbortLeg('no files to compile');
+  }
+  if (arguments.length > 1) {
+    var extra = arguments.getRange(1, arguments.length - 1);
+    throw new AbortLeg('extra arguments: $extra');
+  }
+
+  Map<String, SourceFile> sourceFiles = <SourceFile>{};
+  int dartBytesRead = 0;
+
+  Future<String> provider(Uri uri) {
+    if (uri.scheme != 'file') {
+      throw new IllegalArgumentException(uri);
+    }
+    String source = readAll(uri.path);
+    dartBytesRead += source.length;
+    sourceFiles[uri.toString()] =
+      new SourceFile(relativize(cwd, uri), source);
+    Completer<String> completer = new Completer<String>();
+    completer.complete(source);
+    return completer.future;
+  }
+
+  void info(var message) {
+    if (verbose) print('${green("info:")} $message');
+  }
+
+  void handler(Uri uri, int begin, int end, String message, bool fatal) {
+    if (uri === null && !fatal) {
+      info(message);
+      return;
+    }
+    if (uri === null) {
+      assert(fatal);
+      print(message);
+    } else if (fatal || showWarnings) {
+      SourceFile file = sourceFiles[uri.toString()];
+      print(file.getLocationMessage(message, begin, end, true));
+    }
+    if (fatal && throwOnError) throw new AbortLeg(message);
+  }
+
+  Uri uri = cwd.resolve(arguments[0]);
+  info('compiling $uri');
+
+  // TODO(ahe): We expect the future to be complete and call value
+  // directly. In effect, we don't support truly asynchronous API.
+  String code = api.compile(uri, libraryRoot, provider, handler).value;
+  if (code === null) throw new AbortLeg('compilation failed');
+  writeString(out, code);
+  int jsBytesWritten = code.length;
+  info('compiled $dartBytesRead bytes Dart -> $jsBytesWritten bytes JS '
+       + 'in ${relativize(cwd, out)}');
+}
+
+class AbortLeg {
+  final message;
+  AbortLeg(this.message);
+  toString() => 'Aborted due to --throw-on-error: $message';
+}
+
+void writeString(Uri uri, String text) {
+  if (uri.scheme != 'file') {
+    throw new AbortLeg('unhandled scheme ${uri.scheme}');
+  }
+  var file = new File(uri.path).openSync(FileMode.WRITE);
+  file.writeStringSync(text);
+  file.closeSync();
+}
+
+String readAll(String filename) {
+  var file = (new File(filename)).openSync();
+  var length = file.lengthSync();
+  var buffer = new List<int>(length);
+  var bytes = file.readListSync(buffer, 0, length);
+  file.closeSync();
+  return new String.fromCharCodes(new Utf8Decoder(buffer).decodeRest());
+}
diff --git a/lib/compiler/implementation/diagnostic_listener.dart b/lib/compiler/implementation/diagnostic_listener.dart
new file mode 100644
index 0000000..0d4df79
--- /dev/null
+++ b/lib/compiler/implementation/diagnostic_listener.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2012, 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.
+
+interface DiagnosticListener {
+  // TODO(karlklose): replace cancel with better error reporting mechanism.
+  void cancel([String reason, node, token, instruction, element]);
+  // TODO(karlklose): rename log to something like reportInfo.
+  void log(message);
+  // TODO(karlklose): add reportWarning and reportError to this interface.
+}
diff --git a/lib/compiler/implementation/elements/elements.dart b/lib/compiler/implementation/elements/elements.dart
new file mode 100644
index 0000000..2db8b5c
--- /dev/null
+++ b/lib/compiler/implementation/elements/elements.dart
@@ -0,0 +1,969 @@
+// Copyright (c) 2012, 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('elements');
+
+#import('../tree/tree.dart');
+#import('../scanner/scannerlib.dart');
+#import('../leg.dart');  // TODO(karlklose): we only need type.
+#import('../util/util.dart');
+
+class ElementCategory {
+  /**
+   * Represents things that we don't expect to find when looking in a
+   * scope.
+   */
+  static final int NONE = 0;
+
+  /** Field, parameter, or variable. */
+  static final int VARIABLE = 1;
+
+  /** Function, method, or foreign function. */
+  static final int FUNCTION = 2;
+
+  static final int CLASS = 4;
+
+  static final int PREFIX = 8;
+
+  /** Constructor or factory. */
+  static final int FACTORY = 16;
+
+  static final int ALIAS = 32;
+
+  static final int SUPER = 64;
+
+  /** Type variable */
+  static final int TYPE_VARIABLE = 128;
+
+  static final int IMPLIES_TYPE = CLASS | ALIAS | TYPE_VARIABLE;
+
+  static final int IS_EXTENDABLE = CLASS | ALIAS;
+}
+
+class ElementKind {
+  final String id;
+  final int category;
+
+  const ElementKind(String this.id, this.category);
+
+  static final ElementKind VARIABLE =
+    const ElementKind('variable', ElementCategory.VARIABLE);
+  static final ElementKind PARAMETER =
+    const ElementKind('parameter', ElementCategory.VARIABLE);
+  // Parameters in constructors that directly initialize fields. For example:
+  // [:A(this.field):].
+  static final ElementKind FIELD_PARAMETER =
+    const ElementKind('field_parameter', ElementCategory.VARIABLE);
+  static final ElementKind FUNCTION =
+    const ElementKind('function', ElementCategory.FUNCTION);
+  static final ElementKind CLASS =
+    const ElementKind('class', ElementCategory.CLASS);
+  static final ElementKind FOREIGN =
+    const ElementKind('foreign', ElementCategory.FUNCTION);
+  static final ElementKind GENERATIVE_CONSTRUCTOR =
+      const ElementKind('generative_constructor', ElementCategory.FACTORY);
+  static final ElementKind FIELD =
+    const ElementKind('field', ElementCategory.VARIABLE);
+  static final ElementKind VARIABLE_LIST =
+    const ElementKind('variable_list', ElementCategory.NONE);
+  static final ElementKind FIELD_LIST =
+    const ElementKind('field_list', ElementCategory.NONE);
+  static final ElementKind GENERATIVE_CONSTRUCTOR_BODY =
+      const ElementKind('generative_constructor_body', ElementCategory.NONE);
+  static final ElementKind COMPILATION_UNIT =
+      const ElementKind('compilation_unit', ElementCategory.NONE);
+  static final ElementKind GETTER =
+    const ElementKind('getter', ElementCategory.NONE);
+  static final ElementKind SETTER =
+    const ElementKind('setter', ElementCategory.NONE);
+  static final ElementKind TYPE_VARIABLE =
+    const ElementKind('type_variable', ElementCategory.TYPE_VARIABLE);
+  static final ElementKind ABSTRACT_FIELD =
+    const ElementKind('abstract_field', ElementCategory.VARIABLE);
+  static final ElementKind LIBRARY =
+    const ElementKind('library', ElementCategory.NONE);
+  static final ElementKind PREFIX =
+    const ElementKind('prefix', ElementCategory.PREFIX);
+  static final ElementKind TYPEDEF =
+    const ElementKind('typedef', ElementCategory.ALIAS);
+
+  static final ElementKind STATEMENT =
+    const ElementKind('statement', ElementCategory.NONE);
+  static final ElementKind LABEL =
+    const ElementKind('label', ElementCategory.NONE);
+
+  toString() => id;
+}
+
+class Element implements Hashable {
+  final SourceString name;
+  final ElementKind kind;
+  final Element enclosingElement;
+  Modifiers get modifiers() => null;
+
+  Node parseNode(DiagnosticListener listener) {
+    listener.cancel("Internal Error: $this.parseNode", token: position());
+  }
+
+  Type computeType(Compiler compiler) {
+    compiler.internalError("$this.computeType.", token: position());
+  }
+
+  bool isFunction() => kind === ElementKind.FUNCTION;
+  bool isMember() =>
+      enclosingElement !== null && enclosingElement.kind === ElementKind.CLASS;
+  bool isInstanceMember() => false;
+  bool isFactoryConstructor() => modifiers !== null && modifiers.isFactory();
+  bool isGenerativeConstructor() => kind === ElementKind.GENERATIVE_CONSTRUCTOR;
+  bool isCompilationUnit() {
+    return kind === ElementKind.COMPILATION_UNIT ||
+           kind === ElementKind.LIBRARY;
+  }
+  bool isClass() => kind === ElementKind.CLASS;
+  bool isVariable() => kind === ElementKind.VARIABLE;
+  bool isParameter() => kind === ElementKind.PARAMETER;
+  bool isStatement() => kind === ElementKind.STATEMENT;
+  bool isTypedef() => kind === ElementKind.TYPEDEF;
+  bool isTypeVariable() => kind === ElementKind.TYPE_VARIABLE;
+  bool isGetter() => kind === ElementKind.GETTER;
+  bool isSetter() => kind === ElementKind.SETTER;
+  bool impliesType() => (kind.category & ElementCategory.IMPLIES_TYPE) != 0;
+  bool isExtendable() => (kind.category & ElementCategory.IS_EXTENDABLE) != 0;
+
+  bool isAssignable() {
+    if (modifiers != null && modifiers.isFinal()) return false;
+    if (isFunction() || isGenerativeConstructor()) return false;
+    return true;
+  }
+
+  Token position() => null;
+
+  Token findMyName(Token token) {
+    for (Token t = token; t !== EOF_TOKEN; t = t.next) {
+      if (t.value == name) return t;
+    }
+    return token;
+  }
+
+  Element(this.name, this.kind, this.enclosingElement) {
+    assert(getLibrary() !== null);
+  }
+
+  // TODO(kasperl): This is a very bad hash code for the element and
+  // there's no reason why two elements with the same name should have
+  // the same hash code. Replace this with a simple id in the element?
+  int hashCode() => name.hashCode();
+
+  CompilationUnitElement getCompilationUnit() {
+    Element element = this;
+    while (element !== null && !element.isCompilationUnit()) {
+      element = element.enclosingElement;
+    }
+    return element;
+  }
+
+  LibraryElement getLibrary() {
+    Element element = this;
+    while (element.kind !== ElementKind.LIBRARY) {
+      element = element.enclosingElement;
+    }
+    return element;
+  }
+
+  ClassElement getEnclosingClass() {
+    for (Element e = this; e !== null; e = e.enclosingElement) {
+      if (e.kind === ElementKind.CLASS) return e;
+    }
+    return null;
+  }
+
+  toString() => '$kind(${name.slowToString()})';
+
+  bool _isNative = false;
+  void setNative() => _isNative = true;
+  bool isNative() => _isNative;
+}
+
+class ContainerElement extends Element {
+  ContainerElement(name, kind, enclosingElement) :
+    super(name, kind, enclosingElement);
+
+  abstract void addMember(Element element, DiagnosticListener listener);
+
+  void addGetterOrSetter(Element element,
+                         Element existing,
+                         DiagnosticListener listener) {
+    void reportError(Element other) {
+      listener.cancel('duplicate definition of ${element.name.slowToString()}',
+                      element: element);
+      listener.cancel('existing definition', element: other);
+    }
+
+    if (existing != null) {
+      if (existing.kind !== ElementKind.ABSTRACT_FIELD) {
+        reportError(existing);
+      } else {
+        AbstractFieldElement field = existing;
+        if (element.kind == ElementKind.GETTER) {
+          if (field.getter != null && field.getter != element) {
+            reportError(field.getter);
+          }
+          field.getter = element;
+        } else {
+          if (field.setter != null && field.setter != element) {
+            reportError(field.setter);
+          }
+          field.setter = element;
+        }
+      }
+    } else {
+      AbstractFieldElement field = new AbstractFieldElement(element.name, this);
+      addMember(field, listener);
+      if (element.kind == ElementKind.GETTER) {
+        field.getter = element;
+      } else {
+        field.setter = element;
+      }
+    }
+  }
+}
+
+class CompilationUnitElement extends ContainerElement {
+  final Script script;
+  Link<Element> topLevelElements = const EmptyLink<Element>();
+
+  CompilationUnitElement(Script script, Element enclosing)
+    : this.script = script,
+      super(new SourceString(script.name),
+            ElementKind.COMPILATION_UNIT,
+            enclosing);
+
+  CompilationUnitElement.library(Script script)
+    : this.script = script,
+      super(new SourceString(script.name), ElementKind.LIBRARY, null);
+
+  void addMember(Element element, DiagnosticListener listener) {
+    LibraryElement library = enclosingElement;
+    library.addMember(element, listener);
+    topLevelElements = topLevelElements.prepend(element);
+  }
+
+  void define(Element element, DiagnosticListener listener) {
+    LibraryElement library = enclosingElement;
+    library.define(element, listener);
+  }
+
+  void addTag(ScriptTag tag, DiagnosticListener listener) {
+    listener.cancel("script tags not allowed here", node: tag);
+  }
+}
+
+class LibraryElement extends CompilationUnitElement {
+  // TODO(ahe): Library element should not be a subclass of
+  // CompilationUnitElement.
+
+  Link<CompilationUnitElement> compilationUnits =
+    const EmptyLink<CompilationUnitElement>();
+  Link<ScriptTag> tags = const EmptyLink<ScriptTag>();
+  ScriptTag libraryTag;
+  Map<SourceString, Element> elements;
+  bool canUseNative = false;
+
+  LibraryElement(Script script)
+      : elements = new Map<SourceString, Element>(),
+        super.library(script);
+
+  void addCompilationUnit(CompilationUnitElement element) {
+    compilationUnits = compilationUnits.prepend(element);
+  }
+
+  void addTag(ScriptTag tag, DiagnosticListener listener) {
+    tags = tags.prepend(tag);
+  }
+
+  void addMember(Element element, DiagnosticListener listener) {
+    topLevelElements = topLevelElements.prepend(element);
+    define(element, listener);
+  }
+
+  void define(Element element, DiagnosticListener listener) {
+    if (element.kind == ElementKind.GETTER
+        || element.kind == ElementKind.SETTER) {
+      addGetterOrSetter(element, elements[element.name], listener);
+    } else {
+      Element existing = elements.putIfAbsent(element.name, () => element);
+      if (existing !== element) {
+        listener.cancel('duplicate definition', token: element.position());
+        listener.cancel('existing definition', token: existing.position());
+      }
+    }
+  }
+
+  Element find(SourceString elementName) {
+    return elements[elementName];
+  }
+
+  void forEachExport(f(Element element)) {
+    elements.forEach((SourceString _, Element e) {
+      if (this === e.getLibrary()
+          && e.kind !== ElementKind.PREFIX
+          && e.kind !== ElementKind.FOREIGN) {
+        if (!e.name.isPrivate()) f(e);
+      }
+    });
+  }
+
+  bool hasLibraryName() => libraryTag !== null;
+}
+
+class PrefixElement extends Element {
+  Map<SourceString, Element> imported;
+  Token firstPosition;
+
+  PrefixElement(SourceString prefix, Element enclosing, this.firstPosition)
+    : imported = new Map<SourceString, Element>(),
+      super(prefix, ElementKind.PREFIX, enclosing);
+
+  lookupLocalMember(SourceString memberName) => imported[memberName];
+
+  Type computeType(Compiler compiler) => compiler.types.dynamicType;
+
+  Token position() => firstPosition;
+}
+
+class TypedefElement extends Element {
+  Token token;
+  TypedefElement(SourceString name, Element enclosing, this.token)
+    : super(name, ElementKind.TYPEDEF, enclosing);
+
+  position() => findMyName(token);
+}
+
+class VariableElement extends Element {
+  final VariableListElement variables;
+  Expression cachedNode; // The send or the identifier in the variables list.
+
+  Modifiers get modifiers() => variables.modifiers;
+
+  VariableElement(SourceString name,
+                  VariableListElement this.variables,
+                  ElementKind kind,
+                  Element enclosing,
+                  [Node node])
+    : super(name, kind, enclosing), cachedNode = node;
+
+  Node parseNode(DiagnosticListener listener) {
+    if (cachedNode !== null) return cachedNode;
+    VariableDefinitions definitions = variables.parseNode(listener);
+    for (Link<Node> link = definitions.definitions.nodes;
+         !link.isEmpty(); link = link.tail) {
+      Expression initializedIdentifier = link.head;
+      Identifier identifier = initializedIdentifier.asIdentifier();
+      if (identifier === null) {
+        identifier = initializedIdentifier.asSendSet().selector.asIdentifier();
+      }
+      if (name === identifier.source) {
+        cachedNode = initializedIdentifier;
+        return cachedNode;
+      }
+    }
+    listener.cancel('internal error: could not find $name', node: variables);
+  }
+
+  Type computeType(Compiler compiler) {
+    return variables.computeType(compiler);
+  }
+
+  Type get type() => variables.type;
+
+  bool isInstanceMember() {
+    return isMember() && !modifiers.isStatic();
+  }
+
+  // Note: cachedNode.getBeginToken() will not be correct in all
+  // cases, for example, for function typed parameters.
+  Token position() => findMyName(variables.position());
+}
+
+/**
+ * Parameters in constructors that directly initialize fields. For example:
+ * [:A(this.field):].
+ */
+class FieldParameterElement extends VariableElement {
+  VariableElement fieldElement;
+
+  FieldParameterElement(SourceString name,
+                        this.fieldElement,
+                        VariableListElement variables,
+                        Element enclosing,
+                        Node node)
+      : super(name, variables, ElementKind.FIELD_PARAMETER, enclosing, node);
+}
+
+// This element represents a list of variable or field declaration.
+// It contains the node, and the type. A [VariableElement] always
+// references its [VariableListElement]. It forwards its
+// [computeType] and [parseNode] methods to this element.
+class VariableListElement extends Element {
+  VariableDefinitions cachedNode;
+  Type type;
+  final Modifiers modifiers;
+
+  VariableListElement(ElementKind kind,
+                      Modifiers this.modifiers,
+                      Element enclosing)
+    : super(null, kind, enclosing);
+
+  VariableListElement.node(VariableDefinitions node,
+                           ElementKind kind,
+                           Element enclosing)
+    : super(null, kind, enclosing),
+      this.cachedNode = node,
+      this.modifiers = node.modifiers;
+
+  VariableDefinitions parseNode(DiagnosticListener listener) {
+    return cachedNode;
+  }
+
+  Type computeType(Compiler compiler) {
+    if (type != null) return type;
+    type = getType(parseNode(compiler).type, compiler, getLibrary());
+    return type;
+  }
+
+  Token position() => cachedNode.getBeginToken();
+}
+
+class ForeignElement extends Element {
+  ForeignElement(SourceString name, ContainerElement enclosingElement)
+    : super(name, ElementKind.FOREIGN, enclosingElement);
+
+  Type computeType(Compiler compiler) {
+    return compiler.types.dynamicType;
+  }
+
+  parseNode(DiagnosticListener listener) {
+    throw "internal error: ForeignElement has no node";
+  }
+}
+
+class AbstractFieldElement extends Element {
+  FunctionElement getter;
+  FunctionElement setter;
+  Modifiers modifiers;
+
+  AbstractFieldElement(SourceString name, Element enclosing)
+      : super(name, ElementKind.ABSTRACT_FIELD, enclosing),
+        modifiers = new Modifiers.empty();
+
+  Type computeType(Compiler compiler) {
+    throw "internal error: AbstractFieldElement has no type";
+  }
+
+  Node parseNode(DiagnosticListener listener) {
+    throw "internal error: AbstractFieldElement has no node";
+  }
+
+  position() {
+    // The getter and setter may be defined in two different
+    // compilation units.  However, we know that one of them is
+    // non-null and defined in the same compilation unit as the
+    // abstract element.
+    //
+    // We need to make sure that the position returned is relative to
+    // the compilation unit of the abstract element.
+    if (getter !== null && getter.enclosingElement === enclosingElement) {
+      return getter.position();
+    } else if (setter != null) {
+      // TODO(ahe): checking for null should not be necessary.
+      return setter.position();
+    }
+  }
+}
+
+/** DEPRECATED. */
+Type getType(TypeAnnotation typeAnnotation,
+             Compiler compiler,
+             LibraryElement library) {
+  // TODO(karlklose,ngeoffray): This method should be removed and the
+  // information should be computed by the resolver.
+
+  if (typeAnnotation == null || typeAnnotation.typeName == null) {
+    return compiler.types.dynamicType;
+  }
+  Identifier identifier = typeAnnotation.typeName.asIdentifier();
+  if (identifier === null) {
+    compiler.reportWarning(typeAnnotation.typeName,
+                           'library prefixes not handled');
+    return compiler.types.dynamicType;
+  }
+  SourceString name = identifier.source;
+  Element element = library.find(name);
+  if (element !== null) {
+    if (element.isTypedef()) {
+      // TODO(ngeoffray): This is a hack to help us get support for the
+      // DOM library.
+      // TODO(ngeoffray): The list of types for the argument is wrong.
+      return new FunctionType(compiler.types.dynamicType,
+                              const EmptyLink<Type>(),
+                              element);
+    }
+    if (element.isClass()) {
+      // TODO(karlklose): substitute type parameters.
+      return element.computeType(compiler);
+    }
+  }
+  Type type = compiler.types.lookup(name);
+  if (type === null) {
+    type = compiler.types.dynamicType;
+  }
+  return type;
+}
+
+class FunctionParameters {
+  Link<Element> requiredParameters;
+  Link<Element> optionalParameters;
+  int requiredParameterCount;
+  int optionalParameterCount;
+  FunctionParameters(this.requiredParameters,
+                     this.optionalParameters,
+                     this.requiredParameterCount,
+                     this.optionalParameterCount);
+
+  void forEachParameter(void function(Element parameter)) {
+    for (Link<Element> link = requiredParameters;
+         !link.isEmpty();
+         link = link.tail) {
+      function(link.head);
+    }
+    for (Link<Element> link = optionalParameters;
+         !link.isEmpty();
+         link = link.tail) {
+      function(link.head);
+    }
+  }
+
+  int get parameterCount() => requiredParameterCount + optionalParameterCount;
+}
+
+class FunctionElement extends Element {
+  FunctionExpression cachedNode;
+  Type type;
+  final Modifiers modifiers;
+
+  FunctionParameters functionParameters;
+
+  /**
+   * If this is an interface constructor, [defaultImplementation] will
+   * changed by the resolver to point to the default
+   * implementation. Otherwise, [:defaultImplementation === this:].
+   */
+  FunctionElement defaultImplementation;
+
+  FunctionElement(SourceString name,
+                  ElementKind kind,
+                  Modifiers modifiers,
+                  Element enclosing)
+    : this.tooMuchOverloading(name, null, kind, modifiers, enclosing, null);
+
+  FunctionElement.node(SourceString name,
+                       FunctionExpression node,
+                       ElementKind kind,
+                       Modifiers modifiers,
+                       Element enclosing)
+    : this.tooMuchOverloading(name, node, kind, modifiers, enclosing, null);
+
+  FunctionElement.from(SourceString name,
+                       FunctionElement other,
+                       Element enclosing)
+    : this.tooMuchOverloading(name, other.cachedNode, other.kind,
+                              other.modifiers, enclosing,
+                              other.functionParameters);
+
+  FunctionElement.tooMuchOverloading(SourceString name,
+                                     FunctionExpression this.cachedNode,
+                                     ElementKind kind,
+                                     Modifiers this.modifiers,
+                                     Element enclosing,
+                                     FunctionParameters this.functionParameters)
+    : super(name, kind, enclosing)
+  {
+    defaultImplementation = this;
+  }
+
+  bool isInstanceMember() {
+    return isMember()
+           && kind != ElementKind.GENERATIVE_CONSTRUCTOR
+           && !modifiers.isFactory()
+           && !modifiers.isStatic();
+  }
+
+  FunctionParameters computeParameters(Compiler compiler) {
+    if (functionParameters !== null) return functionParameters;
+    functionParameters = compiler.resolveSignature(this);
+    return functionParameters;
+  }
+
+  int requiredParameterCount(Compiler compiler) {
+    return computeParameters(compiler).requiredParameterCount;
+  }
+
+  int optionalParameterCount(Compiler compiler) {
+    return computeParameters(compiler).optionalParameterCount;
+  }
+
+  int parameterCount(Compiler compiler) {
+    return computeParameters(compiler).parameterCount;
+  }
+
+  FunctionType computeType(Compiler compiler) {
+    if (type != null) return type;
+    FunctionParameters parameters = computeParameters(compiler);
+    Types types = compiler.types;
+    FunctionExpression node =
+        compiler.parser.measure(() => parseNode(compiler));
+    Type returnType = getType(node.returnType, compiler, getLibrary());
+    if (returnType === null) returnType = types.dynamicType;
+
+    LinkBuilder<Type> parameterTypes = new LinkBuilder<Type>();
+    for (Link<Element> link = parameters.requiredParameters;
+         !link.isEmpty();
+         link = link.tail) {
+      parameterTypes.addLast(link.head.computeType(compiler));
+    }
+    type = new FunctionType(returnType, parameterTypes.toLink(), this);
+    return type;
+  }
+
+  Node parseNode(DiagnosticListener listener) => cachedNode;
+
+  Token position() => cachedNode.getBeginToken();
+}
+
+class ConstructorBodyElement extends FunctionElement {
+  FunctionElement constructor;
+
+  ConstructorBodyElement(FunctionElement constructor)
+      : this.constructor = constructor,
+        super(constructor.name,
+              ElementKind.GENERATIVE_CONSTRUCTOR_BODY,
+              null,
+              constructor.enclosingElement) {
+    functionParameters = constructor.functionParameters;
+  }
+
+  bool isInstanceMember() => true;
+
+  FunctionType computeType(Compiler compiler) { unreachable(); }
+
+  Node parseNode(DiagnosticListener listener) {
+    if (cachedNode !== null) return cachedNode;
+    cachedNode = constructor.parseNode(listener);
+    assert(cachedNode !== null);
+    return cachedNode;
+  }
+
+  Token position() => constructor.position();
+}
+
+class SynthesizedConstructorElement extends FunctionElement {
+  SynthesizedConstructorElement(Element enclosing)
+    : super(enclosing.name, ElementKind.GENERATIVE_CONSTRUCTOR,
+            null, enclosing);
+
+  Token position() => enclosingElement.position();
+}
+
+class ClassElement extends ContainerElement {
+  Type type;
+  Type supertype;
+  Type defaultClass;
+  Link<Element> members = const EmptyLink<Element>();
+  Map<SourceString, Element> localMembers;
+  Map<SourceString, Element> constructors;
+  Link<Type> interfaces = const EmptyLink<Type>();
+  Map<SourceString, TypeVariableElement> typeParameters;
+  bool isResolved = false;
+  bool isBeingResolved = false;
+  // backendMembers are members that have been added by the backend to simplify
+  // compilation. They don't have any user-side counter-part.
+  Link<Element> backendMembers = const EmptyLink<Element>();
+
+  Link<Type> allSupertypes;
+
+  ClassElement(SourceString name, CompilationUnitElement enclosing)
+    : localMembers = new Map<SourceString, Element>(),
+      constructors = new Map<SourceString, Element>(),
+      typeParameters = new Map<SourceString, TypeVariableElement>(),
+      super(name, ElementKind.CLASS, enclosing);
+
+  void addMember(Element element, DiagnosticListener listener) {
+    members = members.prepend(element);
+    if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
+        element.modifiers.isFactory()) {
+      constructors[element.name] = element;
+    } else if (element.kind == ElementKind.GETTER
+               || element.kind == ElementKind.SETTER) {
+      addGetterOrSetter(element, localMembers[element.name], listener);
+    } else {
+      localMembers[element.name] = element;
+    }
+  }
+
+  Type computeType(compiler) {
+    if (type === null) {
+      type = new SimpleType(name, this);
+    }
+    return type;
+  }
+
+  ClassElement ensureResolved(Compiler compiler) {
+    if (!isResolved && !isBeingResolved) {
+      isBeingResolved = true;
+      compiler.resolveType(this);
+      isBeingResolved = false;
+      isResolved = true;
+    }
+    return this;
+  }
+
+  Element lookupTypeParameter(SourceString parameterName) {
+    Element result = typeParameters[parameterName];
+    return result;
+  }
+
+  Element lookupLocalMember(SourceString memberName) {
+    return localMembers[memberName];
+  }
+
+  Element lookupSuperMember(SourceString memberName) {
+    for (ClassElement s = superclass; s != null; s = s.superclass) {
+      Element e = s.lookupLocalMember(memberName);
+      if (e !== null) {
+        if (!memberName.isPrivate() || getLibrary() === e.getLibrary()) {
+          return e;
+        }
+      }
+    }
+    return null;
+  }
+
+  Element lookupConstructor(SourceString className,
+                            [SourceString constructorName =
+                                 const SourceString(''),
+                            Element noMatch(Element)]) {
+    // TODO(karlklose): have a map from class names to a map of constructors
+    //                  instead of creating the name here?
+    SourceString normalizedName;
+    if (constructorName !== const SourceString('')) {
+      normalizedName = Elements.constructConstructorName(className,
+                                                         constructorName);
+    } else {
+      normalizedName = className;
+    }
+    Element result = constructors[normalizedName];
+    if (result === null && noMatch !== null) {
+      result = noMatch(lookupLocalMember(constructorName));
+    }
+    return result;
+  }
+
+  /**
+   * Returns the super class, if any.
+   *
+   * The returned element may not be resolved yet.
+   */
+  ClassElement get superclass() {
+    assert(isResolved);
+    return supertype === null ? null : supertype.element;
+  }
+
+  bool isInterface() => false;
+  bool isNative() => nativeName != null;
+  SourceString nativeName;
+}
+
+class Elements {
+  static bool isLocal(Element element) {
+    return ((element !== null)
+            && !element.isInstanceMember()
+            && !isStaticOrTopLevelField(element)
+            && !isStaticOrTopLevelFunction(element)
+            && (element.kind === ElementKind.VARIABLE ||
+                element.kind === ElementKind.PARAMETER ||
+                element.kind === ElementKind.FUNCTION));
+  }
+
+  static bool isInstanceField(Element element) {
+    return (element !== null)
+           && element.isInstanceMember()
+           && (element.kind === ElementKind.FIELD
+               || element.kind === ElementKind.GETTER
+               || element.kind === ElementKind.SETTER);
+  }
+
+  static bool isStaticOrTopLevel(Element element) {
+    return (element != null)
+           && !element.isInstanceMember()
+           && element.enclosingElement !== null
+           && (element.enclosingElement.kind == ElementKind.CLASS ||
+               element.enclosingElement.kind == ElementKind.COMPILATION_UNIT ||
+               element.enclosingElement.kind == ElementKind.LIBRARY);
+  }
+
+  static bool isStaticOrTopLevelField(Element element) {
+    return isStaticOrTopLevel(element)
+           && (element.kind === ElementKind.FIELD
+               || element.kind === ElementKind.GETTER
+               || element.kind === ElementKind.SETTER);
+  }
+
+  static bool isStaticOrTopLevelFunction(Element element) {
+    return isStaticOrTopLevel(element)
+           && (element.kind === ElementKind.FUNCTION);
+  }
+
+  static bool isInstanceMethod(Element element) {
+    return (element != null)
+           && element.isInstanceMember()
+           && (element.kind === ElementKind.FUNCTION);
+  }
+
+  static bool isInstanceSend(Send send, TreeElements elements) {
+    Element element = elements[send];
+    if (element === null) return !isClosureSend(send, elements);
+    return isInstanceMethod(element) || isInstanceField(element);
+  }
+
+  static bool isClosureSend(Send send, TreeElements elements) {
+    if (send.isPropertyAccess) return false;
+    if (send.receiver !== null) return false;
+    Element element = elements[send];
+    // (o)() or foo()().
+    if (element === null && send.selector.asIdentifier() === null) return true;
+    if (element === null) return false;
+    // foo() with foo a local or a parameter.
+    return isLocal(element);
+  }
+
+  static SourceString constructConstructorName(SourceString receiver,
+                                               SourceString selector) {
+    String r = receiver.slowToString();
+    String s = selector.slowToString();
+    return new SourceString('$r\$$s');
+  }
+
+  static SourceString constructOperatorName(SourceString receiver,
+                                            SourceString selector,
+                                            [bool isPrefix = false]) {
+    String str = selector.stringValue;
+    if (str === '==' || str === '!=') return Namer.OPERATOR_EQUALS;
+
+    if (str === '~') str = 'not';
+    else if (str === 'negate' || (str === '-' && isPrefix)) str = 'negate';
+    else if (str === '[]') str = 'index';
+    else if (str === '[]=') str = 'indexSet';
+    else if (str === '*' || str === '*=') str = 'mul';
+    else if (str === '/' || str === '/=') str = 'div';
+    else if (str === '%' || str === '%=') str = 'mod';
+    else if (str === '~/' || str === '~/=') str = 'tdiv';
+    else if (str === '+' || str === '+=') str = 'add';
+    else if (str === '-' || str === '-=') str = 'sub';
+    else if (str === '<<' || str === '<<=') str = 'shl';
+    else if (str === '>>' || str === '>>=') str = 'shr';
+    else if (str === '>=') str = 'ge';
+    else if (str === '>') str = 'gt';
+    else if (str === '<=') str = 'le';
+    else if (str === '<') str = 'lt';
+    else if (str === '&' || str === '&=') str = 'and';
+    else if (str === '^' || str === '^=') str = 'xor';
+    else if (str === '|' || str === '|=') str = 'or';
+    else {
+      throw new Exception('Unhandled selector: ${selector.slowToString()}');
+    }
+    return new SourceString('$receiver\$$str');
+  }
+
+  static bool isStringSupertype(Element element, Compiler compiler) {
+    LibraryElement coreLibrary = compiler.coreLibrary;
+    return (element == coreLibrary.find(const SourceString('Comparable')))
+        || (element == coreLibrary.find(const SourceString('Hashable')))
+        || (element == coreLibrary.find(const SourceString('Pattern')));
+  }
+
+  static bool isListSupertype(Element element, Compiler compiler) {
+    LibraryElement coreLibrary = compiler.coreLibrary;
+    return (element == coreLibrary.find(const SourceString('Collection')))
+        || (element == coreLibrary.find(const SourceString('Iterable')));
+  }
+}
+
+
+class LabelElement extends Element {
+  final Identifier label;
+  final String labelName;
+  final TargetElement target;
+  bool isBreakTarget = false;
+  bool isContinueTarget = false;
+  LabelElement(Identifier label, this.labelName, this.target,
+               Element enclosingElement)
+      : this.label = label,
+        super(label.source, ElementKind.LABEL, enclosingElement);
+
+  void setBreakTarget() {
+    isBreakTarget = true;
+    target.isBreakTarget = true;
+  }
+  void setContinueTarget() {
+    isContinueTarget = true;
+    target.isContinueTarget = true;
+  }
+
+  bool get isTarget() => isBreakTarget || isContinueTarget;
+  Node parseNode(DiagnosticListener l) => label;
+
+  Token position() => label.token;
+  String toString() => "${labelName}:";
+}
+
+// Represents a reference to a statement, either a label or the
+// default target of a break or continue.
+class TargetElement extends Element {
+  // TODO(lrn): StatementElement is not just referencing statements anymore.
+  final Node statement;
+  final int nestingLevel;
+  Link<LabelElement> labels = const EmptyLink<LabelElement>();
+  bool isBreakTarget = false;
+  bool isContinueTarget = false;
+
+  TargetElement(this.statement, this.nestingLevel, Element enclosingElement)
+      : super(const SourceString(""), ElementKind.STATEMENT, enclosingElement);
+  bool get isTarget() => isBreakTarget || isContinueTarget;
+
+  LabelElement addLabel(Identifier label, String labelName) {
+    LabelElement result = new LabelElement(label, labelName, this,
+                                           enclosingElement);
+    labels = labels.prepend(result);
+    return result;
+  }
+
+  Node parseNode(DiagnosticListener l) => statement;
+
+  bool get isSwitch() => statement is SwitchStatement;
+
+  Token position() => statement.getBeginToken();
+  String toString() => statement.toString();
+}
+
+class TypeVariableElement extends Element {
+  final Node node;
+  Type bound;
+  Type type;
+  TypeVariableElement(name, Element enclosing, this.node, this.type,
+                      [this.bound])
+    : super(name, ElementKind.TYPE_VARIABLE, enclosing);
+  Type computeType(compiler) => type;
+  Node parseNode(compiler) => node;
+  toString() => "${enclosingElement.toString()}.${name.slowToString()}";
+}
diff --git a/lib/compiler/implementation/emitter.dart b/lib/compiler/implementation/emitter.dart
new file mode 100644
index 0000000..d471443
--- /dev/null
+++ b/lib/compiler/implementation/emitter.dart
@@ -0,0 +1,728 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * A function element that represents a closure call. The signature is copied
+ * from the given element.
+ */
+class ClosureInvocationElement extends FunctionElement {
+  ClosureInvocationElement(SourceString name,
+                           FunctionElement other)
+      : super.from(name, other, other.enclosingElement);
+
+  isInstanceMember() => true;
+}
+
+/**
+ * Generates the code for all used classes in the program. Static fields (even
+ * in classes) are ignored, since they can be treated as non-class elements.
+ *
+ * The code for the containing (used) methods must exist in the [:universe:].
+ */
+class CodeEmitterTask extends CompilerTask {
+  static final String INHERIT_FUNCTION = '''
+function(child, parent) {
+  if (child.prototype.__proto__) {
+    child.prototype.__proto__ = parent.prototype;
+  } else {
+    function tmp() {};
+    tmp.prototype = parent.prototype;
+    child.prototype = new tmp();
+    child.prototype.constructor = child;
+  }
+}''';
+
+  bool addedInheritFunction = false;
+  final Namer namer;
+  final NativeEmitter nativeEmitter;
+  Set<ClassElement> generatedClasses;
+  StringBuffer mainBuffer;
+
+  CodeEmitterTask(Compiler compiler)
+      : namer = compiler.namer,
+        nativeEmitter = new NativeEmitter(compiler),
+        generatedClasses = new Set<ClassElement>(),
+        mainBuffer = new StringBuffer(),
+        super(compiler);
+
+  String get name() => 'CodeEmitter';
+
+  String get inheritsName() => '${namer.ISOLATE}.\$inherits';
+
+  String get objectClassName() {
+    ClassElement objectClass =
+        compiler.coreLibrary.find(const SourceString('Object'));
+    return namer.isolatePropertyAccess(objectClass);
+  }
+
+  void addInheritFunctionIfNecessary() {
+    if (addedInheritFunction) return;
+    addedInheritFunction = true;
+    mainBuffer.add('$inheritsName = ');
+    mainBuffer.add(INHERIT_FUNCTION);
+    mainBuffer.add(';\n');
+  }
+
+  void addParameterStub(FunctionElement member,
+                        String attachTo(String invocationName),
+                        StringBuffer buffer,
+                        Selector selector,
+                        bool isNative) {
+    FunctionParameters parameters = member.computeParameters(compiler);
+    int positionalArgumentCount = selector.positionalArgumentCount;
+    if (positionalArgumentCount == parameters.parameterCount) {
+      assert(selector.namedArgumentCount == 0);
+      return;
+    }
+    ConstantHandler handler = compiler.constantHandler;
+    List<SourceString> names = selector.getOrderedNamedArguments();
+
+    String invocationName =
+        namer.instanceMethodInvocationName(member.getLibrary(), member.name,
+                                           selector);
+    buffer.add('${attachTo(invocationName)} = function(');
+
+    // The parameters that this stub takes.
+    List<String> parametersBuffer = new List<String>(selector.argumentCount);
+    // The arguments that will be passed to the real method.
+    List<String> argumentsBuffer = new List<String>(parameters.parameterCount);
+
+    // We fill the lists depending on the selector. For example,
+    // take method foo:
+    //    foo(a, b, [c, d]);
+    //
+    // We may have multiple ways of calling foo:
+    // (1) foo(1, 2, 3, 4)
+    // (2) foo(1, 2);
+    // (3) foo(1, 2, 3);
+    // (4) foo(1, 2, c: 3);
+    // (5) foo(1, 2, d: 4);
+    // (6) foo(1, 2, c: 3, d: 4);
+    // (7) foo(1, 2, d: 4, c: 3);
+    //
+    // What we generate at the call sites are:
+    // (1) foo$4(1, 2, 3, 4)
+    // (2) foo$2(1, 2);
+    // (3) foo$3(1, 2, 3);
+    // (4) foo$3$c(1, 2, 3);
+    // (5) foo$3$d(1, 2, 4);
+    // (6) foo$4$c$d(1, 2, 3, 4);
+    // (7) foo$4$c$d(1, 2, 3, 4);
+    //
+    // The stubs we generate are (expressed in Dart):
+    // (1) No stub generated, call is direct.
+    // (2) foo$2(a, b) => foo$4(a, b, null, null)
+    // (3) foo$3(a, b, c) => foo$4(a, b, c, null)
+    // (4) foo$3$c(a, b, c) => foo$4(a, b, c, null);
+    // (5) foo$3$d(a, b, d) => foo$4(a, b, null, d);
+    // (6) foo$4$c$d(a, b, c, d) => foo$4(a, b, c, d);
+    // (7) Same as (5).
+    //
+    // We need to generate a stub for (5) because the order of the
+    // stub arguments and the real method may be different.
+
+    int count = 0;
+    int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1;
+    parameters.forEachParameter((Element element) {
+      String jsName = JsNames.getValid(element.name.slowToString());
+      if (count < positionalArgumentCount) {
+        parametersBuffer[count] = jsName;
+        argumentsBuffer[count] = jsName;
+      } else {
+        int index = names.indexOf(element.name);
+        if (index != -1) {
+          indexOfLastOptionalArgumentInParameters = count;
+          // The order of the named arguments is not the same as the
+          // one in the real method (which is in Dart source order).
+          argumentsBuffer[count] = jsName;
+          parametersBuffer[selector.positionalArgumentCount + index] = jsName;
+        } else {
+          Constant value = handler.initialVariableValues[element];
+          if (value == null) {
+            argumentsBuffer[count] = '(void 0)';
+          } else {
+            if (!value.isNull()) {
+              // If the value is the null constant, we should not pass it
+              // down to the native method.
+              indexOfLastOptionalArgumentInParameters = count;
+            }
+            argumentsBuffer[count] =
+                handler.writeJsCode(new StringBuffer(), value).toString();
+          }
+        }
+      }
+      count++;
+    });
+    String parametersString = Strings.join(parametersBuffer, ",");
+    buffer.add('$parametersString) {\n');
+
+    if (isNative) {
+      nativeEmitter.emitParameterStub(
+          member, invocationName, parametersString, argumentsBuffer,
+          indexOfLastOptionalArgumentInParameters);
+    } else {
+      String arguments = Strings.join(argumentsBuffer, ",");
+      buffer.add('  return this.${namer.getName(member)}($arguments)');
+  }
+    buffer.add('\n};\n');
+  }
+
+  void addParameterStubs(FunctionElement member,
+                         String attachTo(String invocationName),
+                         StringBuffer buffer,
+                         [bool isNative = false]) {
+    Set<Selector> selectors = compiler.universe.invokedNames[member.name];
+    if (selectors == null) return;
+    FunctionParameters parameters = member.computeParameters(compiler);
+    for (Selector selector in selectors) {
+      if (!selector.applies(parameters)) continue;
+      addParameterStub(member, attachTo, buffer, selector, isNative);
+    }
+  }
+
+  void addInstanceMember(Element member,
+                         String attachTo(String name),
+                         StringBuffer buffer,
+                         [bool isNative = false]) {
+    // TODO(floitsch): we don't need to deal with members of
+    // uninstantiated classes, that have been overwritten by subclasses.
+
+    if (member.kind === ElementKind.FUNCTION
+        || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY
+        || member.kind === ElementKind.GETTER
+        || member.kind === ElementKind.SETTER) {
+      if (member.modifiers !== null && member.modifiers.isAbstract()) return;
+      String codeBlock = compiler.universe.generatedCode[member];
+      if (codeBlock == null) return;
+      buffer.add('${attachTo(namer.getName(member))} = $codeBlock;\n');
+      codeBlock = compiler.universe.generatedBailoutCode[member];
+      if (codeBlock !== null) {
+        String name = compiler.namer.getBailoutName(member);
+        buffer.add('${attachTo(name)} = $codeBlock;\n');
+      }
+      FunctionElement function = member;
+      FunctionParameters parameters = function.computeParameters(compiler);
+      if (!parameters.optionalParameters.isEmpty()) {
+        addParameterStubs(member, attachTo, buffer, isNative: isNative);
+      }
+    } else if (member.kind === ElementKind.FIELD) {
+      // TODO(ngeoffray): Have another class generate the code for the
+      // fields.
+      if ((member.modifiers === null || !member.modifiers.isFinal()) &&
+          compiler.universe.invokedSetters.contains(member.name)) {
+        String setterName = namer.setterName(member.getLibrary(), member.name);
+        String name =
+            isNative ? member.name.slowToString() : namer.getName(member);
+        buffer.add('${attachTo(setterName)} = function(v){\n');
+        buffer.add('  this.$name = v;\n};\n');
+      }
+      if (compiler.universe.invokedGetters.contains(member.name)) {
+        String getterName = namer.getterName(member.getLibrary(), member.name);
+        String name =
+            isNative ? member.name.slowToString() : namer.getName(member);
+        buffer.add('${attachTo(getterName)} = function(){\n');
+        buffer.add('  return this.$name;\n};\n');
+      }
+    } else {
+      compiler.internalError('unexpected kind: "${member.kind}"',
+                             element: member);
+    }
+    emitExtraAccessors(member, attachTo, buffer);
+  }
+
+  bool generateFieldInits(ClassElement classElement,
+                          StringBuffer argumentsBuffer,
+                          StringBuffer bodyBuffer) {
+    bool isFirst = true;
+    do {
+      // TODO(floitsch): make sure there are no name clashes.
+      String className = namer.getName(classElement);
+
+      void generateFieldInit(Element member) {
+        if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
+          if (!isFirst) argumentsBuffer.add(', ');
+          isFirst = false;
+          String memberName = namer.instanceFieldName(member.getLibrary(),
+                                                      member.name);
+          argumentsBuffer.add('${className}_$memberName');
+          bodyBuffer.add('  this.$memberName = ${className}_$memberName;\n');
+        }
+      }
+
+      for (Element element in classElement.members) {
+        generateFieldInit(element);
+      }
+      for (Element element in classElement.backendMembers) {
+        generateFieldInit(element);
+      }
+
+      classElement = classElement.superclass;
+    } while(classElement !== null);
+  }
+
+  void emitInherits(ClassElement cls, StringBuffer buffer) {
+    ClassElement superclass = cls.superclass;
+    if (superclass !== null) {
+      addInheritFunctionIfNecessary();
+      String className = namer.isolatePropertyAccess(cls);
+      String superName = namer.isolatePropertyAccess(superclass);
+      buffer.add('${inheritsName}($className, $superName);\n');
+    }
+  }
+
+  void ensureGenerated(ClassElement classElement, StringBuffer buffer) {
+    if (classElement == null) return;
+    if (generatedClasses.contains(classElement)) return;
+    generatedClasses.add(classElement);
+    generateClass(classElement, buffer);
+  }
+
+  void generateClass(ClassElement classElement, StringBuffer buffer) {
+    ensureGenerated(classElement.superclass, buffer);
+
+    if (classElement.isNative()) {
+      nativeEmitter.generateNativeClass(classElement);
+      return;
+    } else {
+      // TODO(ngeoffray): Instead of switching between buffer, we
+      // should create code sections, and decide where to emit them at
+      // the end.
+      buffer = mainBuffer;
+    }
+
+    String className = namer.isolatePropertyAccess(classElement);
+    String constructorName = namer.safeName(classElement.name.slowToString());
+    buffer.add('$className = function $constructorName(');
+    StringBuffer bodyBuffer = new StringBuffer();
+    // If the class is never instantiated we still need to set it up for
+    // inheritance purposes, but we can leave its JavaScript constructor empty.
+    if (compiler.universe.instantiatedClasses.contains(classElement)) {
+      generateFieldInits(classElement, buffer, bodyBuffer);
+    }
+    buffer.add(') {\n');
+    buffer.add(bodyBuffer);
+    buffer.add('};\n');
+
+    emitInherits(classElement, buffer);
+
+    String attachTo(String name) => '$className.prototype.$name';
+    for (Element member in classElement.members) {
+      if (member.isInstanceMember()) {
+        addInstanceMember(member, attachTo, buffer);
+      }
+    }
+    for (Element member in classElement.backendMembers) {
+      if (member.isInstanceMember()) {
+        addInstanceMember(member, attachTo, buffer);
+      }
+    }
+    generateTypeTests(classElement, (Element other) {
+      buffer.add('${attachTo(namer.operatorIs(other))} = ');
+      if (nativeEmitter.requiresNativeIsCheck(other)) {
+        buffer.add('function() { return true; }');
+      } else {
+        buffer.add('true');
+      }
+      buffer.add(';\n');
+    });
+
+    if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) {
+      // Emit the noSuchMethods on the Object prototype now, so that
+      // the code in the dynamicMethod can find them. Note that the
+      // code in dynamicMethod is invoked before analyzing the full JS
+      // script.
+      emitNoSuchMethodCalls(buffer);
+    }
+  }
+
+  void generateTypeTests(ClassElement cls,
+                         void generateTypeTest(ClassElement element)) {
+    if (compiler.universe.isChecks.contains(cls)) {
+      generateTypeTest(cls);
+    }
+    generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>());
+  }
+
+  void generateInterfacesIsTests(ClassElement cls,
+                                 void generateTypeTest(ClassElement element),
+                                 Set<Element> alreadyGenerated) {
+    for (Type interfaceType in cls.interfaces) {
+      Element element = interfaceType.element;
+      if (!alreadyGenerated.contains(element) &&
+          compiler.universe.isChecks.contains(element)) {
+        alreadyGenerated.add(element);
+        generateTypeTest(element);
+      }
+      generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated);
+    }
+  }
+
+  void emitClasses(StringBuffer buffer) {
+    for (ClassElement element in compiler.universe.instantiatedClasses) {
+      ensureGenerated(element, buffer);
+    }
+  }
+
+  void emitStaticFunctionsWithNamer(StringBuffer buffer,
+                                    Map<Element, String> generatedCode,
+                                    String functionNamer(Element element)) {
+    generatedCode.forEach((Element element, String codeBlock) {
+      if (!element.isInstanceMember()) {
+        buffer.add('${functionNamer(element)} = ');
+        buffer.add(codeBlock);
+        buffer.add(';\n\n');
+      }
+    });
+  }
+
+  void emitStaticFunctions(StringBuffer buffer) {
+    emitStaticFunctionsWithNamer(buffer,
+                                 compiler.universe.generatedCode,
+                                 namer.isolatePropertyAccess);
+    emitStaticFunctionsWithNamer(buffer,
+                                 compiler.universe.generatedBailoutCode,
+                                 namer.isolateBailoutPropertyAccess);
+  }
+
+  void emitStaticFunctionGetters(StringBuffer buffer) {
+    Set<FunctionElement> functionsNeedingGetter =
+        compiler.universe.staticFunctionsNeedingGetter;
+    for (FunctionElement element in functionsNeedingGetter) {
+      // The static function does not have the correct name. Since
+      // [addParameterStubs] use the name to create its stubs we simply
+      // create a fake element with the correct name.
+      // Note: the callElement will not have any enclosingElement.
+      FunctionElement callElement =
+          new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, element);
+      String staticName = namer.isolatePropertyAccess(element);
+      int parameterCount = element.parameterCount(compiler);
+      String invocationName =
+          namer.instanceMethodName(element.getLibrary(), callElement.name,
+                                   parameterCount);
+      buffer.add("$staticName.$invocationName = $staticName;\n");
+      addParameterStubs(callElement, (name) => '$staticName.$name', buffer);
+    }
+  }
+
+  void emitDynamicFunctionGetter(StringBuffer buffer,
+                                 String attachTo(String invocationName),
+                                 FunctionElement member) {
+    // For every method that has the same name as a property-get we create a
+    // getter that returns a bound closure. Say we have a class 'A' with method
+    // 'foo' and somewhere in the code there is a dynamic property get of
+    // 'foo'. Then we generate the following code (in pseudo Dart):
+    //
+    // class A {
+    //    foo(x, y, z) { ... } // Original function.
+    //    get foo() { return new BoundClosure499(this); }
+    // }
+    // class BoundClosure499 extends Closure {
+    //   var self;
+    //   BoundClosure499(this.self);
+    //   $call3(x, y, z) { return self.foo(x, y, z); }
+    // }
+
+    // TODO(floitsch): share the closure classes with other classes
+    // if they share methods with the same signature.
+
+    // The closure class.
+    SourceString name = const SourceString("BoundClosure");
+    ClassElement closureClassElement =
+        new ClosureClassElement(compiler, member.getCompilationUnit());
+    String isolateAccess = namer.isolatePropertyAccess(closureClassElement);
+    ensureGenerated(closureClassElement.superclass, buffer);
+
+    // Define the constructor with a name so that Object.toString can
+    // find the class name of the closure class.
+    buffer.add("$isolateAccess = function $name(self) ");
+    buffer.add("{ this.self = self; };\n");
+    emitInherits(closureClassElement, buffer);
+
+    String prototype = "$isolateAccess.prototype";
+
+    // Now add the methods on the closure class. The instance method does not
+    // have the correct name. Since [addParameterStubs] use the name to create
+    // its stubs we simply create a fake element with the correct name.
+    // Note: the callElement will not have any enclosingElement.
+    FunctionElement callElement =
+        new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member);
+
+    int parameterCount = member.parameterCount(compiler);
+    String invocationName =
+        namer.instanceMethodName(member.getLibrary(),
+                                 callElement.name, parameterCount);
+    String targetName = namer.instanceMethodName(member.getLibrary(),
+                                                 member.name, parameterCount);
+    List<String> arguments = new List<String>(parameterCount);
+    for (int i = 0; i < parameterCount; i++) {
+      arguments[i] = "arg$i";
+    }
+    String joinedArgs = Strings.join(arguments, ", ");
+    buffer.add("$prototype.$invocationName = function($joinedArgs) {\n");
+    buffer.add("  return this.self.$targetName($joinedArgs);\n");
+    buffer.add("};\n");
+    addParameterStubs(callElement,
+                      (invocationName) => '$prototype.$invocationName',
+                      buffer);
+
+    // And finally the getter.
+    String getterName = namer.getterName(member.getLibrary(), member.name);
+    String closureClass = namer.isolateAccess(closureClassElement);
+    buffer.add("${attachTo(getterName)} = function() {\n");
+    buffer.add("  return new $closureClass(this);\n");
+    buffer.add("};\n");
+  }
+
+  void emitCallStubForGetter(StringBuffer buffer,
+                             String attachTo(String name),
+                             Element member,
+                             Set<Selector> selectors) {
+    String getter;
+    if (member.kind == ElementKind.GETTER) {
+      getter = "this.${namer.getterName(member.getLibrary(), member.name)}()";
+    } else {
+      getter =
+          "this.${namer.instanceFieldName(member.getLibrary(), member.name)}";
+    }
+    for (Selector selector in selectors) {
+      String invocationName =
+          namer.instanceMethodInvocationName(member.getLibrary(), member.name,
+                                             selector);
+      SourceString callName = Namer.CLOSURE_INVOCATION_NAME;
+      String closureCallName =
+          namer.instanceMethodInvocationName(member.getLibrary(), callName,
+                                             selector);
+      List<String> arguments = <String>[];
+      for (int i = 0; i < selector.argumentCount; i++) {
+        arguments.add("arg$i");
+      }
+      String joined = Strings.join(arguments, ", ");
+      buffer.add("${attachTo(invocationName)} = function($joined) {\n");
+      buffer.add("  return $getter.$closureCallName($joined);\n");
+      buffer.add("};\n");
+    }
+  }
+
+  void emitStaticNonFinalFieldInitializations(StringBuffer buffer) {
+    // Adds initializations inside the Isolate constructor.
+    // Example:
+    //    function Isolate() {
+    //       this.staticNonFinal = Isolate.prototype.someVal;
+    //       ...
+    //    }
+    ConstantHandler handler = compiler.constantHandler;
+    List<VariableElement> staticNonFinalFields =
+        handler.getStaticNonFinalFieldsForEmission();
+    if (!staticNonFinalFields.isEmpty()) buffer.add('\n');
+    for (Element element in staticNonFinalFields) {
+      buffer.add('  this.${namer.getName(element)} = ');
+      compiler.withCurrentElement(element, () {
+          handler.writeJsCodeForVariable(buffer, element);
+        });
+      buffer.add(';\n');
+    }
+  }
+
+  void emitCompileTimeConstants(StringBuffer buffer) {
+    ConstantHandler handler = compiler.constantHandler;
+    List<Constant> constants = handler.getConstantsForEmission();
+    String prototype = "${namer.ISOLATE}.prototype";
+    bool addedMakeConstantList = false;
+    for (Constant constant in constants) {
+      if (!addedMakeConstantList && constant.isList()) {
+        addedMakeConstantList = true;
+        emitMakeConstantList(prototype, buffer);
+      }
+      String name = handler.getNameForConstant(constant);
+      buffer.add('$prototype.$name = ');
+      handler.writeJsCode(buffer, constant);
+      buffer.add(';\n');
+    }
+  }
+
+  void emitMakeConstantList(String prototype, StringBuffer buffer) {
+    buffer.add(prototype);
+    buffer.add(@'''.makeConstantList = function(list) {
+  list.immutable$list = true;
+  list.fixed$length = true;
+  return list;
+};
+''');
+  }
+
+  void emitStaticFinalFieldInitializations(StringBuffer buffer) {
+    ConstantHandler handler = compiler.constantHandler;
+    List<VariableElement> staticFinalFields =
+        handler.getStaticFinalFieldsForEmission();
+    for (VariableElement element in staticFinalFields) {
+      buffer.add('${namer.isolatePropertyAccess(element)} = ');
+      compiler.withCurrentElement(element, () {
+          handler.writeJsCodeForVariable(buffer, element);
+        });
+      buffer.add(';\n');
+    }
+  }
+
+  void emitExtraAccessors(Element member,
+                          String attachTo(String name),
+                          StringBuffer buffer) {
+    if (member.kind == ElementKind.GETTER || member.kind == ElementKind.FIELD) {
+      Set<Selector> selectors = compiler.universe.invokedNames[member.name];
+      if (selectors !== null && !selectors.isEmpty()) {
+        compiler.emitter.emitCallStubForGetter(
+            buffer, attachTo, member, selectors);
+      }
+    } else if (member.kind == ElementKind.FUNCTION) {
+      if (compiler.universe.invokedGetters.contains(member.name)) {
+        compiler.emitter.emitDynamicFunctionGetter(
+            buffer, attachTo, member);
+      }
+    }
+  }
+
+  void emitNoSuchMethodCalls(StringBuffer buffer) {
+    // Do not generate no such method calls if there is no class.
+    if (compiler.universe.instantiatedClasses.isEmpty()) return;
+
+    ClassElement objectClass =
+        compiler.coreLibrary.find(const SourceString('Object'));
+    String className = namer.isolatePropertyAccess(objectClass);
+    String prototype = '$className.prototype';
+    String noSuchMethodName =
+        namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2);
+    Collection<LibraryElement> libraries =
+        compiler.universe.libraries.getValues();
+
+    void generateMethod(String methodName, String jsName, Selector selector) {
+      buffer.add('$prototype.$jsName = function');
+      StringBuffer args = new StringBuffer();
+      for (int i = 0; i < selector.argumentCount; i++) {
+        if (i != 0) args.add(', ');
+        args.add('arg$i');
+      }
+      // We need to check if the object has a noSuchMethod. If not, it
+      // means the object is a native object, and we can just call our
+      // generic noSuchMethod. Note that when calling this method, the
+      // 'this' object is not a Dart object.
+      buffer.add(' ($args) {\n');
+      buffer.add('  return this.$noSuchMethodName\n');
+      buffer.add("      ? this.$noSuchMethodName('$methodName', [$args])\n");
+      buffer.add("      : $objectClassName.prototype.$noSuchMethodName.call(");
+      buffer.add("this, '$methodName', [$args])\n");
+      buffer.add('}\n');
+    }
+
+    compiler.universe.invokedNames.forEach((SourceString methodName,
+                                            Set<Selector> selectors) {
+      if (objectClass.lookupLocalMember(methodName) === null
+          && methodName != Namer.OPERATOR_EQUALS) {
+        for (Selector selector in selectors) {
+          if (methodName.isPrivate()) {
+            for (LibraryElement lib in libraries) {
+              String jsName =
+                namer.instanceMethodInvocationName(lib, methodName, selector);
+              generateMethod(methodName.slowToString(), jsName, selector);
+            }
+          } else {
+            String jsName =
+              namer.instanceMethodInvocationName(null, methodName, selector);
+            generateMethod(methodName.slowToString(), jsName, selector);
+          }
+        }
+      }
+    });
+
+    compiler.universe.invokedGetters.forEach((SourceString getterName) {
+      if (getterName.isPrivate()) {
+        for (LibraryElement lib in libraries) {
+          String jsName = namer.getterName(lib, getterName);
+          generateMethod('get ${getterName.slowToString()}', jsName,
+                         Selector.GETTER);
+        }
+      } else {
+        String jsName = namer.getterName(null, getterName);
+        generateMethod('get ${getterName.slowToString()}', jsName,
+                       Selector.GETTER);
+      }
+    });
+
+    compiler.universe.invokedSetters.forEach((SourceString setterName) {
+      if (setterName.isPrivate()) {
+        for (LibraryElement lib in libraries) {
+          String jsName = namer.setterName(lib, setterName);
+          generateMethod('set ${setterName.slowToString()}', jsName,
+                         Selector.SETTER);
+        }
+      } else {
+        String jsName = namer.setterName(null, setterName);
+        generateMethod('set ${setterName.slowToString()}', jsName,
+                       Selector.SETTER);
+      }
+    });
+  }
+
+  String buildIsolateSetup(Element appMain, Element isolateMain) {
+    String mainAccess = "${namer.isolateAccess(appMain)}";
+    String currentIsolate = "${namer.CURRENT_ISOLATE}";
+    String mainEnsureGetter = '';
+    // Since we pass the closurized version of the main method to
+    // the isolate method, we must make sure that it exists.
+    if (!compiler.universe.staticFunctionsNeedingGetter.contains(appMain)) {
+      String invocationName =
+          "${namer.closureInvocationName(Selector.INVOCATION_0)}";
+      mainEnsureGetter = "$mainAccess.$invocationName = $mainAccess";
+    }
+
+    // TODO(ngeoffray): These globals are currently required by the isolate
+    // library, but since leg already generates code on an Isolate object, they
+    // are not really needed. We should remove them once Leg replaces Frog.
+    return """
+var \$globalThis = $currentIsolate;
+var \$globalState;
+var \$globals;
+function \$static_init(){};
+
+function \$initGlobals(context) {
+  context.isolateStatics = new ${namer.ISOLATE}();
+}
+function \$setGlobals(context) {
+  $currentIsolate = context.isolateStatics;
+  \$globalThis = $currentIsolate;
+}
+$mainEnsureGetter
+${namer.isolateAccess(isolateMain)}($mainAccess);""";
+  }
+
+  emitMain(StringBuffer buffer) {
+    if (compiler.isMockCompilation) return;
+    Element main = compiler.mainApp.find(Compiler.MAIN);
+    if (compiler.isolateLibrary != null) {
+      Element isolateMain =
+        compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE);
+      buffer.add(buildIsolateSetup(main, isolateMain));
+    } else {
+      buffer.add('${namer.isolateAccess(main)}();\n');
+    }
+  }
+
+  String assembleProgram() {
+    measure(() {
+      mainBuffer.add('function ${namer.ISOLATE}() {');
+      emitStaticNonFinalFieldInitializations(mainBuffer);
+      mainBuffer.add('}\n\n');
+      emitClasses(mainBuffer);
+      emitStaticFunctions(mainBuffer);
+      emitStaticFunctionGetters(mainBuffer);
+      emitCompileTimeConstants(mainBuffer);
+      emitStaticFinalFieldInitializations(mainBuffer);
+      nativeEmitter.emitDynamicDispatchMetadata();
+      mainBuffer.add(
+          'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n');
+      nativeEmitter.assembleCode(mainBuffer);
+      emitMain(mainBuffer);
+      compiler.assembledCode = mainBuffer.toString();
+    });
+    return compiler.assembledCode;
+  }
+}
diff --git a/lib/compiler/implementation/enqueue.dart b/lib/compiler/implementation/enqueue.dart
new file mode 100644
index 0000000..02d5f96
--- /dev/null
+++ b/lib/compiler/implementation/enqueue.dart
@@ -0,0 +1,208 @@
+// Copyright (c) 2012, 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.
+
+class EnqueueTask extends CompilerTask {
+  final Map<String, Link<Element>> instanceMembersByName;
+  final Set<ClassElement> seenClasses;
+
+  String get name() => 'Enqueue';
+
+  EnqueueTask(Compiler compiler)
+    : instanceMembersByName = new Map<String, Link<Element>>(),
+      seenClasses = new Set<ClassElement>(),
+      super(compiler);
+
+  bool checkNoEnqueuedInvokedInstanceMethods() {
+    measure(() {
+      // Run through the classes and see if we need to compile methods.
+      for (ClassElement classElement in compiler.universe.instantiatedClasses) {
+        for (ClassElement currentClass = classElement;
+             currentClass !== null;
+             currentClass = currentClass.superclass) {
+          processInstantiatedClass(currentClass);
+        }
+      }
+    });
+    return true;
+  }
+
+  void processInstantiatedClass(ClassElement cls) {
+    cls.members.forEach(processInstantiatedClassMember);
+  }
+
+  void registerFieldClosureInvocations() {
+    measure(() {
+      // Make sure that the closure understands a call with the given
+      // selector. For a method-invocation of the form o.foo(a: 499), we
+      // need to make sure that closures can handle the optional argument if
+      // there exists a field or getter 'foo'.
+      var names = compiler.universe.instantiatedClassInstanceFields;
+      // TODO(ahe): Might be enough to use invokedGetters.
+      for (SourceString name in names) {
+        Set<Selector> invokedSelectors = compiler.universe.invokedNames[name];
+        if (invokedSelectors != null) {
+          for (Selector selector in invokedSelectors) {
+            compiler.registerDynamicInvocation(Namer.CLOSURE_INVOCATION_NAME,
+                                               selector);
+          }
+        }
+      }
+    });
+  }
+
+  void processInstantiatedClassMember(Element member) {
+    if (compiler.universe.generatedCode.containsKey(member)) return;
+
+    if (!member.isInstanceMember()) return;
+
+    String memberName = member.name.slowToString();
+    Link<Element> members = instanceMembersByName.putIfAbsent(
+        memberName, () => const EmptyLink<Element>());
+    instanceMembersByName[memberName] = members.prepend(member);
+
+    if (member.kind === ElementKind.GETTER ||
+        member.kind === ElementKind.FIELD) {
+      compiler.universe.instantiatedClassInstanceFields.add(member.name);
+    }
+
+    if (member.kind == ElementKind.FUNCTION) {
+      if (member.name == Compiler.NO_SUCH_METHOD) {
+        compiler.enableNoSuchMethod(member);
+      }
+      Set<Selector> selectors = compiler.universe.invokedNames[member.name];
+      if (selectors != null) {
+        FunctionElement functionMember = member;
+        FunctionParameters parameters =
+            functionMember.computeParameters(compiler);
+        for (Selector selector in selectors) {
+          if (selector.applies(parameters)) {
+            return compiler.addToWorkList(member);
+          }
+        }
+      }
+      // If there is a property access with the same name as a method we
+      // need to emit the method.
+      if (compiler.universe.invokedGetters.contains(member.name)) {
+        // We will emit a closure, so make sure the closure class is
+        // generated.
+        compiler.closureClass.ensureResolved(compiler);
+        compiler.registerInstantiatedClass(compiler.closureClass);
+        return compiler.addToWorkList(member);
+      }
+    } else if (member.kind == ElementKind.GETTER) {
+      if (compiler.universe.invokedGetters.contains(member.name)) {
+        return compiler.addToWorkList(member);
+      }
+      // A method invocation like in o.foo(x, y) might actually be an
+      // invocation of the getter foo followed by an invocation of the
+      // returned closure.
+      Set<Selector> invokedSelectors =
+        compiler.universe.invokedNames[member.name];
+      // We don't know what selectors the returned closure accepts. If
+      // the set contains any selector we have to assume that it matches.
+      if (invokedSelectors !== null && !invokedSelectors.isEmpty()) {
+        return compiler.addToWorkList(member);
+      }
+    } else if (member.kind === ElementKind.SETTER) {
+      if (compiler.universe.invokedSetters.contains(member.name)) {
+        return compiler.addToWorkList(member);
+      }
+    }
+  }
+
+  void onRegisterInstantiatedClass(ClassElement cls) => measure(() {
+    while (cls !== null) {
+      if (seenClasses.contains(cls)) return;
+      seenClasses.add(cls);
+      // TODO(ahe): Don't call resolveType, instead, call this method
+      // when resolveType is called.
+      compiler.resolveType(cls);
+      cls.members.forEach(processInstantiatedClassMember);
+      cls = cls.superclass;
+    }
+  });
+
+  void registerInvocation(SourceString methodName, Selector selector) {
+    measure(() {
+      Map<SourceString, Set<Selector>> invokedNames =
+        compiler.universe.invokedNames;
+      Set<Selector> selectors =
+        invokedNames.putIfAbsent(methodName, () => new Set<Selector>());
+      if (!selectors.contains(selector)) {
+        selectors.add(selector);
+        handleUnseenInvocation(methodName, selector);
+      }
+    });
+  }
+
+  void registerGetter(SourceString methodName) {
+    measure(() {
+      if (!compiler.universe.invokedGetters.contains(methodName)) {
+        compiler.universe.invokedGetters.add(methodName);
+        handleUnseenGetter(methodName);
+      }
+    });
+  }
+
+  void registerSetter(SourceString methodName) {
+    measure(() {
+      if (!compiler.universe.invokedSetters.contains(methodName)) {
+        compiler.universe.invokedSetters.add(methodName);
+        handleUnseenSetter(methodName);
+      }
+    });
+  }
+
+  processInstanceMembers(SourceString n, bool f(Element e)) {
+    String memberName = n.slowToString();
+    Link<Element> members = instanceMembersByName[memberName];
+    if (members !== null) {
+      LinkBuilder<Element> remaining = new LinkBuilder<Element>();
+      for (; !members.isEmpty(); members = members.tail) {
+        if (!f(members.head)) remaining.addLast(members.head);
+      }
+      instanceMembersByName[memberName] = remaining.toLink();
+    }
+  }
+
+  void handleUnseenInvocation(SourceString methodName, Selector selector) {
+    processInstanceMembers(methodName, (Element member) {
+      if (member.isGetter()) {
+        compiler.addToWorkList(member);
+        return true;
+      } else if (member.isFunction()) {
+        FunctionElement functionMember = member;
+        FunctionParameters parameters =
+          functionMember.computeParameters(compiler);
+        if (selector.applies(parameters)) {
+          compiler.addToWorkList(member);
+          return true;
+        }
+      }
+      return false;
+    });
+  }
+
+  void handleUnseenGetter(SourceString methodName) {
+    processInstanceMembers(methodName, (Element member) {
+      if (member.isGetter() || member.isFunction()) {
+        compiler.addToWorkList(member);
+        return true;
+      } else {
+        return false;
+      }
+    });
+  }
+
+  void handleUnseenSetter(SourceString methodName) {
+    processInstanceMembers(methodName, (Element member) {
+      if (member.isSetter()) {
+        compiler.addToWorkList(member);
+        return true;
+      } else {
+        return false;
+      }
+    });
+  }
+}
diff --git a/lib/compiler/implementation/frog_leg.dart b/lib/compiler/implementation/frog_leg.dart
new file mode 100644
index 0000000..42ea0cc
--- /dev/null
+++ b/lib/compiler/implementation/frog_leg.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2012, 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('frog_leg');
+
+#import('../../uri/uri.dart');
+#import('source_file.dart');
+#import('../../../frog/lang.dart', prefix: 'frog');
+#import('../compiler.dart', prefix: 'compiler');
+#import('io/io.dart', prefix: 'io');
+
+String relativize(Uri base, Uri uri) {
+  if (base.scheme == 'file' &&
+      base.scheme == uri.scheme &&
+      base.userInfo == uri.userInfo &&
+      base.domain == uri.domain &&
+      base.port == uri.port &&
+      uri.query == "" && uri.fragment == "") {
+    if (uri.path.startsWith(base.path)) {
+      return uri.path.substring(base.path.length);
+    }
+    List<String> uriParts = uri.path.split('/');
+    List<String> baseParts = base.path.split('/');
+    int common = 0;
+    int length = Math.min(uriParts.length, baseParts.length);
+    while (common < length && uriParts[common] == baseParts[common]) {
+      common++;
+    }
+    StringBuffer sb = new StringBuffer();
+    for (int i = common + 1; i < baseParts.length; i++) {
+      sb.add('../');
+    }
+    for (int i = common; i < uriParts.length - 1; i++) {
+      sb.add('${uriParts[i]}/');
+    }
+    sb.add('${uriParts.last()}');
+    return sb.toString();
+  }
+  return uri.toString();
+}
+
+bool compile(frog.World world) {
+  final throwOnError = frog.options.throwOnErrors;
+  final showWarnings = frog.options.showWarnings;
+  final allowMockCompilation = frog.options.allowMockCompilation;
+  Uri cwd = new Uri(scheme: 'file', path: io.getCurrentDirectory());
+  Uri uri = cwd.resolve(frog.options.dartScript);
+  String frogLibDir = frog.options.libDir;
+  if (!frogLibDir.endsWith("/")) frogLibDir = "$frogLibDir/";
+  Uri frogLib = new Uri(scheme: 'file', path: frogLibDir);
+  Uri libraryRoot = frogLib.resolve('../../lib/compiler/implementation/lib/');
+  Map<String, SourceFile> sourceFiles = <SourceFile>{};
+
+  Future<String> provider(Uri uri) {
+    if (uri.scheme != 'file') {
+      throw new IllegalArgumentException(uri);
+    }
+    String source = world.files.readAll(uri.path);
+    world.dartBytesRead += source.length;
+    sourceFiles[uri.toString()] =
+      new SourceFile(relativize(cwd, uri), source);
+    Completer<String> completer = new Completer<String>();
+    completer.complete(source);
+    return completer.future;
+  }
+
+  void handler(Uri uri, int begin, int end, String message, bool fatal) {
+    if (uri === null && !fatal) {
+      world.info('[leg] $message');
+      return;
+    }
+    if (uri === null) {
+      assert(fatal);
+      print(message);
+    } else if (fatal || showWarnings) {
+      SourceFile file = sourceFiles[uri.toString()];
+      print(file.getLocationMessage(message, begin, end, true));
+    }
+    if (fatal && throwOnError) throw new AbortLeg(message);
+  }
+
+  List<String> options = new List<String>();
+  if (allowMockCompilation) options.add('--allow-mock-compilation');
+
+  // TODO(ahe): We expect the future to be complete and call value
+  // directly. In effect, we don't support truly asynchronous API.
+  String code =
+    compiler.compile(uri, libraryRoot, provider, handler, options).value;
+  if (code === null) return false;
+  world.legCode = code;
+  world.jsBytesWritten = code.length;
+  return true;
+}
+
+class AbortLeg {
+  final message;
+  AbortLeg(this.message);
+  toString() => 'Aborted due to --throw-on-error: $message';
+}
diff --git a/lib/compiler/implementation/io/io.dart b/lib/compiler/implementation/io/io.dart
new file mode 100644
index 0000000..541ef82
--- /dev/null
+++ b/lib/compiler/implementation/io/io.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2011, 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("io");
+
+#import("dart:io");
+
+String join(List<String> strings) => Strings.join(strings, '/');
+
+String getCurrentDirectory() {
+  String dir = new File(".").fullPathSync();
+  if (dir.endsWith("/")) return dir;
+  return "$dir/";
+}
diff --git a/lib/compiler/implementation/leg.dart b/lib/compiler/implementation/leg.dart
new file mode 100644
index 0000000..0b6344d
--- /dev/null
+++ b/lib/compiler/implementation/leg.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2011, 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('leg');
+
+#import('../../uri/uri.dart');
+
+#import('colors.dart');
+#import('elements/elements.dart');
+#import('native_handler.dart', prefix: 'native');
+#import('scanner/scanner_implementation.dart');
+#import('scanner/scannerlib.dart');
+#import('ssa/ssa.dart');
+#import('string_validator.dart');
+#import('tree/tree.dart');
+#import('util/characters.dart');
+#import('util/util.dart');
+
+#source('compile_time_constants.dart');
+#source('compiler.dart');
+#source('diagnostic_listener.dart');
+#source('emitter.dart');
+#source('enqueue.dart');
+#source('namer.dart');
+#source('native_emitter.dart');
+#source('operations.dart');
+#source('resolver.dart');
+#source('script.dart');
+#source('tree_validator.dart');
+#source('typechecker.dart');
+#source('universe.dart');
+#source('warnings.dart');
+
+void unreachable() {
+  throw const Exception("Internal Error (Leg): UNREACHABLE");
+}
diff --git a/lib/compiler/implementation/lib/clock.dart b/lib/compiler/implementation/lib/clock.dart
new file mode 100644
index 0000000..c9a1edd
--- /dev/null
+++ b/lib/compiler/implementation/lib/clock.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * The class [Clock] provides access to a monotonically incrementing clock
+ * device.
+ */
+class Clock {
+
+  /**
+   * Returns the current clock tick.
+   */
+  static int now() => Primitives.dateNow();
+
+  /**
+   * Returns the frequency of clock ticks in Hz.
+   */
+  static int frequency() => 1000;
+}
diff --git a/lib/compiler/implementation/lib/constant_map.dart b/lib/compiler/implementation/lib/constant_map.dart
new file mode 100644
index 0000000..0b12f21
--- /dev/null
+++ b/lib/compiler/implementation/lib/constant_map.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2012, 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.
+
+// This class has no constructor. This is on purpose since the instantiation
+// is shortcut by the compiler.
+class ConstantMap<V> implements Map<String, V> {
+  final int length;
+  // A constant map is backed by a JavaScript object.
+  final _jsObject;
+  final List<String> _keys;
+
+  bool containsValue(V needle) {
+    return getValues().some((V value) => value == needle);
+  }
+
+  bool containsKey(String key) {
+    if (key == '__proto__') return false;
+    return jsHasOwnProperty(_jsObject, key);
+  }
+
+  V operator [](String key) {
+    if (!containsKey(key)) return null;
+    return jsPropertyAccess(_jsObject, key);
+  }
+
+  void forEach(void f(String key, V value)) {
+    _keys.forEach((String key) => f(key, this[key]));
+  }
+
+  Collection<String> getKeys() => _keys;
+
+  Collection<V> getValues() {
+    List<V> result = <V>[];
+    _keys.forEach((String key) => result.add(this[key]));
+    return result;
+  }
+
+  bool isEmpty() => length == 0;
+
+  String toString() => Maps.mapToString(this);
+
+  _throwImmutable() {
+    throw const IllegalAccessException();
+  }
+  void operator []=(String key, V val) => _throwImmutable();
+  V putIfAbsent(String key, V ifAbsent()) => _throwImmutable();
+  V remove(String key) => _throwImmutable();
+  void clear() => _throwImmutable();
+}
diff --git a/lib/compiler/implementation/lib/core.dart b/lib/compiler/implementation/lib/core.dart
new file mode 100644
index 0000000..62ef9dbe
--- /dev/null
+++ b/lib/compiler/implementation/lib/core.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2012, 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('core');
+
+#import('coreimpl.dart');
+
+#import('js_helper.dart'); // TODO(ahe): remove this import.
+
+#source('../../../../corelib/src/bool.dart');
+#source('../../../../corelib/src/collection.dart');
+#source('../../../../corelib/src/comparable.dart');
+#source('../../../../corelib/src/date.dart');
+#source('../../../../corelib/src/double.dart');
+#source('../../../../corelib/src/duration.dart');
+#source('../../../../corelib/src/exceptions.dart');
+#source('../../../../corelib/src/expect.dart');
+#source('../../../../corelib/src/function.dart');
+#source('../../../../corelib/src/future.dart');
+#source('../../../../corelib/src/hashable.dart');
+#source('../../../../corelib/src/int.dart');
+#source('../../../../corelib/src/iterable.dart');
+#source('../../../../corelib/src/iterator.dart');
+#source('../../../../corelib/src/list.dart');
+#source('../../../../corelib/src/map.dart');
+#source('../../../../corelib/src/math.dart');
+#source('../../../../corelib/src/num.dart');
+#source('../../../../corelib/src/options.dart');
+#source('../../../../corelib/src/pattern.dart');
+#source('../../../../corelib/src/queue.dart');
+#source('../../../../corelib/src/regexp.dart');
+#source('../../../../corelib/src/set.dart');
+#source('../../../../corelib/src/stopwatch.dart');
+#source('../../../../corelib/src/string.dart');
+#source('../../../../corelib/src/string_buffer.dart');
+#source('../../../../corelib/src/strings.dart');
+#source('../../../../corelib/src/time_zone.dart');
+#source('mock.dart');
+#source('clock.dart');
+
+void print(var obj) => Primitives.printString(obj.toString());
+
+class Object {
+  String toString() => Primitives.objectToString(this);
+
+  void noSuchMethod(String name, List args) {
+    throw new NoSuchMethodException(this, name, args);
+  }
+}
diff --git a/lib/compiler/implementation/lib/coreimpl.dart b/lib/compiler/implementation/lib/coreimpl.dart
new file mode 100644
index 0000000..11dd7ae
--- /dev/null
+++ b/lib/compiler/implementation/lib/coreimpl.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2012, 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('coreimpl');
+
+#import('js_helper.dart');
+
+#source('../../../../corelib/src/implementation/collections.dart');
+#source('../../../../corelib/src/implementation/dual_pivot_quicksort.dart');
+#source('../../../../corelib/src/implementation/duration_implementation.dart');
+#source('../../../../corelib/src/implementation/exceptions.dart');
+#source('../../../../corelib/src/implementation/future_implementation.dart');
+#source('../../../../corelib/src/implementation/hash_map_set.dart');
+#source('../../../../corelib/src/implementation/linked_hash_map.dart');
+#source('../../../../corelib/src/implementation/maps.dart');
+#source('../../../../corelib/src/implementation/options.dart');
+#source('../../../../corelib/src/implementation/queue.dart');
+#source('../../../../corelib/src/implementation/splay_tree.dart');
+#source('../../../../corelib/src/implementation/stopwatch_implementation.dart');
+
+#source('../../../../runtime/lib/arrays.dart');
+#source('../../../../runtime/lib/string_buffer.dart');
+
+#source('mockimpl.dart');
diff --git a/lib/compiler/implementation/lib/io.dart b/lib/compiler/implementation/lib/io.dart
new file mode 100644
index 0000000..bdd4eea
--- /dev/null
+++ b/lib/compiler/implementation/lib/io.dart
@@ -0,0 +1,126 @@
+// Copyright (c) 2012, 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.
+
+// This is a copy of the VM's dart:io library. This API is not usable
+// when running inside a web browser. Nevertheless, Leg provides a
+// mock version of the dart:io library so that it can statically
+// analyze programs that use dart:io.
+
+// TODO(ahe): Separate API from implementation details.
+
+#library("io");
+#import("dart:coreimpl");
+#import("dart:isolate");
+// TODO(ahe): Should Leg support this library?
+// #import("dart:nativewrappers");
+#import("dart:uri");
+#source('../../../../runtime/bin/buffer_list.dart');
+#source('../../../../runtime/bin/common.dart');
+#source('../../../../runtime/bin/chunked_stream.dart');
+#source('../../../../runtime/bin/directory.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/directory_impl.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/eventhandler.dart');
+#source('../../../../runtime/bin/file.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/file_impl.dart');
+#source('../../../../runtime/bin/http.dart');
+#source('../../../../runtime/bin/http_impl.dart');
+#source('../../../../runtime/bin/http_parser.dart');
+#source('../../../../runtime/bin/http_utils.dart');
+#source('../../../../runtime/bin/input_stream.dart');
+#source('../../../../runtime/bin/list_stream.dart');
+#source('../../../../runtime/bin/list_stream_impl.dart');
+#source('../../../../runtime/bin/output_stream.dart');
+#source('../../../../runtime/bin/stream_util.dart');
+#source('../../../../runtime/bin/string_stream.dart');
+#source('../../../../runtime/bin/platform.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/platform_impl.dart');
+#source('../../../../runtime/bin/process.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/process_impl.dart');
+#source('../../../../runtime/bin/socket.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/socket_impl.dart');
+#source('../../../../runtime/bin/socket_stream.dart');
+#source('../../../../runtime/bin/socket_stream_impl.dart');
+// Uses native keyword.
+// #source('../../../../runtime/bin/stdio.dart');
+#source('../../../../runtime/bin/timer.dart');
+#source('../../../../runtime/bin/timer_impl.dart');
+
+class _File implements File {
+  factory File(arg) {
+    throw new UnsupportedOperationException('new File($arg)');
+  }
+}
+
+class _Platform implements Platform {
+  factory Platform() {
+    throw new UnsupportedOperationException('new Platform()');
+  }
+}
+
+class _Directory implements Directory {
+  factory Directory(arg) {
+    throw new UnsupportedOperationException('new Directory($arg)');
+  }
+
+  factory Directory.current() {
+    throw new UnsupportedOperationException('new Directory.current()');
+  }
+}
+
+class _Process implements Process {
+  factory Process.start(String executable,
+                        List<String> arguments,
+                        [String workingDirectory]) {
+    var msg = 'new Process.start($executable, $arguments, $workingDirectory';
+    throw new UnsupportedOperationException(msg);
+  }
+}
+
+class _ServerSocket implements ServerSocket {
+  factory _ServerSocket(String bindAddress, int port, int backlog) {
+    throw new UnsupportedOperationException(
+        'new ServerSocket($bindAddress, $port, $backlog)');
+  }
+}
+
+class _Socket implements Socket {
+  factory Socket(String host, int port) {
+    throw new UnsupportedOperationException('new Socket($host, $port)');
+  }
+}
+
+class _EventHandler {
+  factory _EventHandler() {
+    throw new UnsupportedOperationException('new _EventHandler()');
+  }
+
+  static void _start() {
+    throw new UnsupportedOperationException('_EventHandler._start()');
+  }
+
+  static _sendData(int id, ReceivePort receivePort, int data) {
+    var msg = '_EventHandler._sendData($id, $receivePort, $data)';
+    throw new UnsupportedOperationException(msg);
+  }
+
+  static _EventHandler get _eventHandler() {
+    throw new UnsupportedOperationException('_EventHandler._eventhandler');
+  }
+
+  static void set _eventHandler(_EventHandler e) {
+    throw new UnsupportedOperationException('_EventHandler._eventhandler = $e');
+  }
+}
+
+final InputStream stdin = null;
+
+final OutputStream stdout = null;
+
+final OutputStream stderr = null;
diff --git a/lib/compiler/implementation/lib/js_helper.dart b/lib/compiler/implementation/lib/js_helper.dart
new file mode 100644
index 0000000..b6f7fb9
--- /dev/null
+++ b/lib/compiler/implementation/lib/js_helper.dart
@@ -0,0 +1,1376 @@
+// Copyright (c) 2012, 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('js_helper');
+
+#import('coreimpl.dart');
+
+#source('constant_map.dart');
+#source('native_helper.dart');
+#source('regexp_helper.dart');
+#source('string_helper.dart');
+
+/**
+ * Returns true if both arguments are numbers.
+ *
+ * If only the first argument is a number, an
+ * [IllegalArgumentException] with the other argument is thrown.
+ */
+bool checkNumbers(var a, var b) {
+  if (a is num) {
+    if (b is num) {
+      return true;
+    } else {
+      checkNull(b);
+      throw new IllegalArgumentException(b);
+    }
+  }
+  return false;
+}
+
+
+bool isJsArray(var value) {
+  return value !== null && JS('bool', @'#.constructor === Array', value);
+}
+
+
+add(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# + #', a, b);
+  } else if (a is String) {
+    // TODO(lrn): Remove when we disable String.operator+
+    b = b.toString();
+    if (b is String) {
+      return JS('String', @'# + #', a, b);
+    }
+    checkNull(b);
+    throw new IllegalArgumentException(b);
+  }
+  return UNINTERCEPTED(a + b);
+}
+
+div(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# / #', a, b);
+  }
+  return UNINTERCEPTED(a / b);
+}
+
+mul(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# * #', a, b);
+  }
+  return UNINTERCEPTED(a * b);
+}
+
+sub(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# - #', a, b);
+  }
+  return UNINTERCEPTED(a - b);
+}
+
+mod(var a, var b) {
+  if (checkNumbers(a, b)) {
+    // Euclidean Modulo.
+    int result = JS('num', @'# % #', a, b);
+    if (result == 0) return 0;  // Make sure we don't return -0.0.
+    if (result > 0) return result;
+    if (b < 0) {
+      return result - b;
+    } else {
+      return result + b;
+    }
+  }
+  return UNINTERCEPTED(a % b);
+}
+
+tdiv(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return (a / b).truncate();
+  }
+  return UNINTERCEPTED(a ~/ b);
+}
+
+eq(var a, var b) {
+  if (JS('bool', @'typeof # === "object"', a)) {
+    if (JS_HAS_EQUALS(a)) {
+      return UNINTERCEPTED(a == b) === true;
+    } else {
+      return JS('bool', @'# === #', a, b);
+    }
+  }
+  // TODO(lrn): is NaN === NaN ? Is -0.0 === 0.0 ?
+  return JS('bool', @'# === #', a, b);
+}
+
+eqq(var a, var b) {
+  return JS('bool', @'# === #', a, b);
+}
+
+eqNull(var a) {
+  if (JS('bool', @'typeof # === "object"', a)) {
+    if (JS_HAS_EQUALS(a)) {
+      return UNINTERCEPTED(a == null) === true;
+    } else {
+      return false;
+    }
+  } else {
+    return JS('bool', @'typeof # === "undefined"', a);
+  }
+}
+
+gt(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('bool', @'# > #', a, b);
+  }
+  return UNINTERCEPTED(a > b);
+}
+
+ge(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('bool', @'# >= #', a, b);
+  }
+  return UNINTERCEPTED(a >= b);
+}
+
+lt(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('bool', @'# < #', a, b);
+  }
+  return UNINTERCEPTED(a < b);
+}
+
+le(var a, var b) {
+  if (checkNumbers(a, b)) {
+    return JS('bool', @'# <= #', a, b);
+  }
+  return UNINTERCEPTED(a <= b);
+}
+
+shl(var a, var b) {
+  // TODO(floitsch): inputs must be integers.
+  if (checkNumbers(a, b)) {
+    if (b < 0) throw new IllegalArgumentException(b);
+    return JS('num', @'# << #', a, b);
+  }
+  return UNINTERCEPTED(a << b);
+}
+
+shr(var a, var b) {
+  // TODO(floitsch): inputs must be integers.
+  if (checkNumbers(a, b)) {
+    if (b < 0) throw new IllegalArgumentException(b);
+    return JS('num', @'# >> #', a, b);
+  }
+  return UNINTERCEPTED(a >> b);
+}
+
+and(var a, var b) {
+  // TODO(floitsch): inputs must be integers.
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# & #', a, b);
+  }
+  return UNINTERCEPTED(a & b);
+}
+
+or(var a, var b) {
+  // TODO(floitsch): inputs must be integers.
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# | #', a, b);
+  }
+  return UNINTERCEPTED(a | b);
+}
+
+xor(var a, var b) {
+  // TODO(floitsch): inputs must be integers.
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# ^ #', a, b);
+  }
+  return UNINTERCEPTED(a ^ b);
+}
+
+not(var a) {
+  if (JS('bool', @'typeof # === "number"', a)) return JS('num', @'~#', a);
+  return UNINTERCEPTED(~a);
+}
+
+neg(var a) {
+  if (JS('bool', @'typeof # === "number"', a)) return JS('num', @'-#', a);
+  return UNINTERCEPTED(-a);
+}
+
+index(var a, var index) {
+  if (a is String || isJsArray(a)) {
+    if (index is !int) {
+      if (index is !num) throw new IllegalArgumentException(index);
+      if (index.truncate() !== index) throw new IllegalArgumentException(index);
+    }
+    if (index < 0 || index >= a.length) {
+      throw new IndexOutOfRangeException(index);
+    }
+    return JS('Object', @'#[#]', a, index);
+  }
+  return UNINTERCEPTED(a[index]);
+}
+
+void indexSet(var a, var index, var value) {
+  if (isJsArray(a)) {
+    if (!(index is int)) {
+      throw new IllegalArgumentException(index);
+    }
+    if (index < 0 || index >= a.length) {
+      throw new IndexOutOfRangeException(index);
+    }
+    checkMutable(a, 'indexed set');
+    JS('Object', @'#[#] = #', a, index, value);
+    return;
+  }
+  UNINTERCEPTED(a[index] = value);
+}
+
+checkMutable(list, reason) {
+  if (JS('bool', @'!!(#.immutable$list)', list)) {
+    throw new UnsupportedOperationException(reason);
+  }
+}
+
+builtin$add$1(var receiver, var value) {
+  if (isJsArray(receiver)) {
+    checkGrowable(receiver, 'add');
+    JS('Object', @'#.push(#)', receiver, value);
+    return;
+  }
+  return UNINTERCEPTED(receiver.add(value));
+}
+
+builtin$removeLast$0(var receiver) {
+  if (isJsArray(receiver)) {
+    checkGrowable(receiver, 'removeLast');
+    if (receiver.length === 0) throw new IndexOutOfRangeException(-1);
+    return JS('Object', @'#.pop()', receiver);
+  }
+  return UNINTERCEPTED(receiver.removeLast());
+}
+
+builtin$filter$1(var receiver, var predicate) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.filter(predicate));
+  } else {
+    return Collections.filter(receiver, [], predicate);
+  }
+}
+
+
+builtin$get$length(var receiver) {
+  if (receiver is String || isJsArray(receiver)) {
+    return JS('num', @'#.length', receiver);
+  } else {
+    return UNINTERCEPTED(receiver.length);
+  }
+}
+
+builtin$set$length(receiver, newLength) {
+  if (isJsArray(receiver)) {
+    checkNull(newLength); // TODO(ahe): This is not specified but co19 tests it.
+    if (newLength is !int) throw new IllegalArgumentException(newLength);
+    if (newLength < 0) throw new IndexOutOfRangeException(newLength);
+    checkGrowable(receiver, 'set length');
+    JS('void', @'#.length = #', receiver, newLength);
+  } else {
+    UNINTERCEPTED(receiver.length = newLength);
+  }
+  return newLength;
+}
+
+checkGrowable(list, reason) {
+  if (JS('bool', @'!!(#.fixed$length)', list)) {
+    throw new UnsupportedOperationException(reason);
+  }
+}
+
+builtin$toString$0(var value) {
+  if (JS('bool', @'typeof # == "object"', value)) {
+    if (isJsArray(value)) {
+      return Collections.collectionToString(value);
+    } else {
+      return UNINTERCEPTED(value.toString());
+    }
+  }
+  if (JS('bool', @'# === 0 && (1 / #) < 0', value, value)) {
+    return '-0.0';
+  }
+  if (value === null) return 'null';
+  if (JS('bool', @'typeof # == "function"', value)) {
+    return 'Closure';
+  }
+  return JS('string', @'String(#)', value);
+}
+
+
+builtin$iterator$0(receiver) {
+  if (isJsArray(receiver)) {
+    return new ListIterator(receiver);
+  }
+  return UNINTERCEPTED(receiver.iterator());
+}
+
+class ListIterator<T> implements Iterator<T> {
+  int i;
+  List<T> list;
+  ListIterator(List<T> this.list) : i = 0;
+  bool hasNext() => i < JS('int', @'#.length', list);
+  T next() {
+    if (!hasNext()) throw new NoMoreElementsException();
+    var value = JS('Object', @'#[#]', list, i);
+    i += 1;
+    return value;
+  }
+}
+
+builtin$charCodeAt$1(var receiver, int index) {
+  if (receiver is String) {
+    if (index is !num) throw new IllegalArgumentException(index);
+    if (index < 0) throw new IndexOutOfRangeException(index);
+    if (index >= receiver.length) throw new IndexOutOfRangeException(index);
+    return JS('int', @'#.charCodeAt(#)', receiver, index);
+  } else {
+    return UNINTERCEPTED(receiver.charCodeAt(index));
+  }
+}
+
+builtin$isEmpty$0(receiver) {
+  if (receiver is String || isJsArray(receiver)) {
+    return JS('bool', @'#.length === 0', receiver);
+  }
+  return UNINTERCEPTED(receiver.isEmpty());
+}
+
+class Primitives {
+  static void printString(String string) {
+    var hasConsole = JS('bool', @'typeof console == "object"');
+    if (hasConsole) {
+      JS('void', @'console.log(#)', string);
+    } else {
+      JS('void', @'write(#)', string);
+      JS('void', @'write("\n")');
+    }
+  }
+
+  /** [: @"$".charCodeAt(0) :] */
+  static final int DOLLAR_CHAR_VALUE = 36;
+
+  static String objectToString(Object object) {
+    String name = JS('String', @'#.constructor.name', object);
+    if (name === null) {
+      name = JS('String', @'#.match(/^\s*function\s*\$?(\S*)\s*\(/)[1]',
+                JS('String', @'#.constructor.toString()', object));
+    } else {
+      if (name.charCodeAt(0) === DOLLAR_CHAR_VALUE) name = name.substring(1);
+    }
+    return "Instance of '$name'";
+  }
+
+  static List newList(length) {
+    if (length === null) return JS('Object', @'new Array()');
+    if ((length is !int) || (length < 0)) {
+      throw new IllegalArgumentException(length);
+    }
+    var result = JS('Object', @'new Array(#)', length);
+    JS('void', @'#.fixed$length = #', result, true);
+    return result;
+  }
+
+  static num dateNow() => JS('num', @'Date.now()');
+
+  static String stringFromCharCodes(charCodes) {
+    for (var i in charCodes) {
+      if (i is !int) throw new IllegalArgumentException(i);
+    }
+    return JS('String', @'String.fromCharCode.apply(#, #)', null, charCodes);
+  }
+
+  static valueFromDecomposedDate(years, month, day, hours, minutes, seconds,
+                                 milliseconds, isUtc) {
+    checkInt(years);
+    checkInt(month);
+    if (month < 1 || 12 < month) throw new IllegalArgumentException(month);
+    checkInt(day);
+    if (day < 1 || 31 < day) throw new IllegalArgumentException(day);
+    checkInt(hours);
+    if (hours < 0 || 24 < hours) throw new IllegalArgumentException(hours);
+    checkInt(minutes);
+    if (minutes < 0 || 59 < minutes) {
+      throw new IllegalArgumentException(minutes);
+    }
+    checkInt(seconds);
+    if (seconds < 0 || 59 < seconds) {
+      // TODO(ahe): Leap seconds?
+      throw new IllegalArgumentException(seconds);
+    }
+    checkInt(milliseconds);
+    if (milliseconds < 0 || 999 < milliseconds) {
+      throw new IllegalArgumentException(milliseconds);
+    }
+    checkBool(isUtc);
+    var jsMonth = month - 1;
+    var value;
+    if (isUtc) {
+      value = JS('num', @'Date.UTC(#, #, #, #, #, #, #)',
+                 years, jsMonth, day, hours, minutes, seconds, milliseconds);
+    } else {
+      value = JS('num', @'new Date(#, #, #, #, #, #, #).valueOf()',
+                 years, jsMonth, day, hours, minutes, seconds, milliseconds);
+    }
+    if (value.isNaN()) throw new IllegalArgumentException('');
+    if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc);
+    return value;
+  }
+
+  static patchUpY2K(value, years, isUtc) {
+    var date = JS('Object', @'new Date(#)', value);
+    if (isUtc) {
+      JS('num', @'#.setUTCFullYear(#)', date, years);
+    } else {
+      JS('num', @'#.setFullYear(#)', date, years);
+    }
+    return JS('num', @'#.valueOf()', date);
+  }
+
+  // Lazily keep a JS Date stored in the JS object.
+  static lazyAsJsDate(receiver) {
+    if (JS('bool', @'#.date === (void 0)', receiver)) {
+      JS('void', @'#.date = new Date(#)', receiver, receiver.value);
+    }
+    return JS('Date', @'#.date', receiver);
+  }
+
+  static getYear(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCFullYear()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getFullYear()', lazyAsJsDate(receiver));
+  }
+
+  static getMonth(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCMonth()', lazyAsJsDate(receiver)) + 1
+      : JS('int', @'#.getMonth()', lazyAsJsDate(receiver)) + 1;
+  }
+
+  static getDay(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCDate()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getDate()', lazyAsJsDate(receiver));
+  }
+
+  static getHours(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCHours()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getHours()', lazyAsJsDate(receiver));
+  }
+
+  static getMinutes(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCMinutes()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getMinutes()', lazyAsJsDate(receiver));
+  }
+
+  static getSeconds(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCSeconds()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getSeconds()', lazyAsJsDate(receiver));
+  }
+
+  static getMilliseconds(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCMilliseconds()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getMilliseconds()', lazyAsJsDate(receiver));
+  }
+
+  static getWeekday(receiver) {
+    return (receiver.timeZone.isUtc)
+      ? JS('int', @'#.getUTCDay()', lazyAsJsDate(receiver))
+      : JS('int', @'#.getDay()', lazyAsJsDate(receiver));
+  }
+
+  static valueFromDateString(str) {
+    checkNull(str);
+    if (str is !String) throw new IllegalArgumentException(str);
+    var value = JS('num', @'Date.parse(#)', str);
+    if (value.isNaN()) throw new IllegalArgumentException(str);
+    return value;
+  }
+}
+
+builtin$compareTo$1(a, b) {
+  if (checkNumbers(a, b)) {
+    if (a < b) {
+      return -1;
+    } else if (a > b) {
+      return 1;
+    } else if (a == b) {
+      if (a == 0) {
+        bool aIsNegative = a.isNegative();
+        bool bIsNegative = b.isNegative();
+        if (aIsNegative == bIsNegative) return 0;
+        if (aIsNegative) return -1;
+        return 1;
+      }
+      return 0;
+    } else if (a.isNaN()) {
+      if (b.isNaN()) {
+        return 0;
+      }
+      return 1;
+    } else {
+      return -1;
+    }
+  } else if (a is String) {
+    if (b is !String) throw new IllegalArgumentException(b);
+    return JS('bool', @'# == #', a, b) ? 0
+      : JS('bool', @'# < #', a, b) ? -1 : 1;
+  } else {
+    return UNINTERCEPTED(a.compareTo(b));
+  }
+}
+
+/**
+ * Called by generated code to throw an illegal-argument exception,
+ * for example, if a non-integer index is given to an optimized
+ * indexed access.
+ */
+iae(argument) {
+  throw new IllegalArgumentException(argument);
+}
+
+/**
+ * Called by generated code to throw an index-out-of-range exception,
+ * for example, if a bounds check fails in an optimized indexed
+ * access.
+ */
+ioore(index) {
+  throw new IndexOutOfRangeException(index);
+}
+
+builtin$addAll$1(receiver, collection) {
+  if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addAll(collection));
+
+  // TODO(ahe): Use for-in when it is implemented correctly.
+  var iterator = collection.iterator();
+  while (iterator.hasNext()) {
+    receiver.add(iterator.next());
+  }
+}
+
+builtin$addLast$1(receiver, value) {
+  if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addLast(value));
+
+  checkGrowable(receiver, 'addLast');
+  JS('Object', @'#.push(#)', receiver, value);
+}
+
+builtin$clear$0(receiver) {
+  if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.clear());
+  receiver.length = 0;
+}
+
+builtin$forEach$1(receiver, f) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.forEach(f));
+  } else {
+    return Collections.forEach(receiver, f);
+  }
+}
+
+builtin$map$1(receiver, f) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.map(f));
+  } else {
+    return Collections.map(receiver, [], f);
+  }
+}
+
+builtin$getRange$2(receiver, start, length) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.getRange(start, length));
+  }
+  if (0 === length) return [];
+  checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
+  if (start is !int) throw new IllegalArgumentException(start);
+  if (length is !int) throw new IllegalArgumentException(length);
+  if (length < 0) throw new IllegalArgumentException(length);
+  if (start < 0) throw new IndexOutOfRangeException(start);
+  var end = start + length;
+  if (end > receiver.length) {
+    throw new IndexOutOfRangeException(length);
+  }
+  if (length < 0) throw new IllegalArgumentException(length);
+  return JS('Object', @'#.slice(#, #)', receiver, start, end);
+}
+
+builtin$indexOf$1(receiver, element) {
+  if (isJsArray(receiver) || receiver is String) {
+    return builtin$indexOf$2(receiver, element, 0);
+  }
+  return UNINTERCEPTED(receiver.indexOf(element));
+}
+
+builtin$indexOf$2(receiver, element, start) {
+  if (isJsArray(receiver)) {
+    if (start is !int) throw new IllegalArgumentException(start);
+    var length = JS('num', @'#.length', receiver);
+    return Arrays.indexOf(receiver, element, start, length);
+  } else if (receiver is String) {
+    checkNull(element);
+    if (start is !int) throw new IllegalArgumentException(start);
+    if (element is !String) throw new IllegalArgumentException(element);
+    if (start < 0) return -1; // TODO(ahe): Is this correct?
+    return JS('int', @'#.indexOf(#, #)', receiver, element, start);
+  }
+  return UNINTERCEPTED(receiver.indexOf(element, start));
+}
+
+builtin$insertRange$2(receiver, start, length) {
+  if (isJsArray(receiver)) {
+    return builtin$insertRange$3(receiver, start, length, null);
+  }
+  return UNINTERCEPTED(receiver.insertRange(start, length));
+}
+
+builtin$insertRange$3(receiver, start, length, initialValue) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.insertRange(start, length, initialValue));
+  }
+  return listInsertRange(receiver, start, length, initialValue);
+}
+
+listInsertRange(receiver, start, length, initialValue) {
+  if (length === 0) {
+    return;
+  }
+  checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
+  if (length is !int) throw new IllegalArgumentException(length);
+  if (length < 0) throw new IllegalArgumentException(length);
+  if (start is !int) throw new IllegalArgumentException(start);
+
+  var receiverLength = JS('num', @'#.length', receiver);
+  if (start < 0 || start > receiverLength) {
+    throw new IndexOutOfRangeException(start);
+  }
+  receiver.length = receiverLength + length;
+  Arrays.copy(receiver,
+              start,
+              receiver,
+              start + length,
+              receiverLength - start);
+  if (initialValue !== null) {
+    for (int i = start; i < start + length; i++) {
+      receiver[i] = initialValue;
+    }
+  }
+  receiver.length = receiverLength + length;
+}
+
+builtin$last$0(receiver) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.last());
+  }
+  return receiver[receiver.length - 1];
+}
+
+builtin$lastIndexOf$1(receiver, element) {
+  if (isJsArray(receiver)) {
+    var start = JS('num', @'#.length', receiver);
+    return Arrays.lastIndexOf(receiver, element, start);
+  } else if (receiver is String) {
+    checkNull(element);
+    if (element is !String) throw new IllegalArgumentException(element);
+    return JS('int', @'#.lastIndexOf(#)', receiver, element);
+  }
+  return UNINTERCEPTED(receiver.lastIndexOf(element));
+}
+
+builtin$lastIndexOf$2(receiver, element, start) {
+  if (isJsArray(receiver)) {
+    return Arrays.lastIndexOf(receiver, element, start);
+  } else if (receiver is String) {
+    checkNull(element);
+    if (element is !String) throw new IllegalArgumentException(element);
+    if (start !== null) {
+      if (start is !num) throw new IllegalArgumentException(start);
+      if (start < 0) return -1;
+      if (start >= receiver.length) {
+        if (element == "") return receiver.length;
+        start = receiver.length - 1;
+      }
+    }
+    return stringLastIndexOfUnchecked(receiver, element, start);
+  }
+  return UNINTERCEPTED(receiver.lastIndexOf(element, start));
+}
+
+stringLastIndexOfUnchecked(receiver, element, start)
+  => JS('int', @'#.lastIndexOf(#, #)', receiver, element, start);
+
+builtin$removeRange$2(receiver, start, length) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.removeRange(start, length));
+  }
+  checkGrowable(receiver, 'removeRange');
+  if (length == 0) {
+    return;
+  }
+  checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
+  if (start is !int) throw new IllegalArgumentException(start);
+  if (length is !int) throw new IllegalArgumentException(length);
+  if (length < 0) throw new IllegalArgumentException(length);
+  var receiverLength = JS('num', @'#.length', receiver);
+  if (start < 0 || start >= receiverLength) {
+    throw new IndexOutOfRangeException(start);
+  }
+  if (start + length > receiverLength) {
+    throw new IndexOutOfRangeException(start + length);
+  }
+  Arrays.copy(receiver,
+              start + length,
+              receiver,
+              start,
+              receiverLength - length - start);
+  receiver.length = receiverLength - length;
+}
+
+builtin$setRange$3(receiver, start, length, from) {
+  if (isJsArray(receiver)) {
+    return builtin$setRange$4(receiver, start, length, from, 0);
+  }
+  return UNINTERCEPTED(receiver.setRange(start, length, from));
+}
+
+builtin$setRange$4(receiver, start, length, from, startFrom) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.setRange(start, length, from, startFrom));
+  }
+
+  checkMutable(receiver, 'indexed set');
+  if (length === 0) return;
+  checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(from); // TODO(ahe): This is not specified but co19 tests it.
+  checkNull(startFrom); // TODO(ahe): This is not specified but co19 tests it.
+  if (start is !int) throw new IllegalArgumentException(start);
+  if (length is !int) throw new IllegalArgumentException(length);
+  if (startFrom is !int) throw new IllegalArgumentException(startFrom);
+  if (length < 0) throw new IllegalArgumentException(length);
+  if (start < 0) throw new IndexOutOfRangeException(start);
+  if (start + length > receiver.length) {
+    throw new IndexOutOfRangeException(start + length);
+  }
+
+  Arrays.copy(from, startFrom, receiver, start, length);
+}
+
+builtin$some$1(receiver, f) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.some(f));
+  } else {
+    return Collections.some(receiver, f);
+  }
+}
+
+builtin$every$1(receiver, f) {
+  if (!isJsArray(receiver)) {
+    return UNINTERCEPTED(receiver.every(f));
+  } else {
+    return Collections.every(receiver, f);
+  }
+}
+
+builtin$sort$1(receiver, compare) {
+  if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.sort(compare));
+
+  checkMutable(receiver, 'sort');
+  DualPivotQuicksort.sort(receiver, compare);
+}
+
+checkNull(object) {
+  if (object === null) throw new NullPointerException();
+  return object;
+}
+
+checkNum(value) {
+  if (value is !num) {
+    checkNull(value);
+    throw new IllegalArgumentException(value);
+  }
+  return value;
+}
+
+checkInt(value) {
+  if (value is !int) {
+    checkNull(value);
+    throw new IllegalArgumentException(value);
+  }
+  return value;
+}
+
+checkBool(value) {
+  if (value is !bool) {
+    checkNull(value);
+    throw new IllegalArgumentException(value);
+  }
+  return value;
+}
+
+checkString(value) {
+  if (value is !String) {
+    checkNull(value);
+    throw new IllegalArgumentException(value);
+  }
+  return value;
+}
+
+builtin$isNegative$0(receiver) {
+  if (receiver is num) {
+    return (receiver === 0) ? (1 / receiver) < 0 : receiver < 0;
+  } else {
+    return UNINTERCEPTED(receiver.isNegative());
+  }
+}
+
+builtin$isNaN$0(receiver) {
+  if (receiver is num) {
+    return JS('bool', @'isNaN(#)', receiver);
+  } else {
+    return UNINTERCEPTED(receiver.isNegative());
+  }
+}
+
+builtin$remainder$1(a, b) {
+  if (checkNumbers(a, b)) {
+    return JS('num', @'# % #', a, b);
+  } else {
+    return UNINTERCEPTED(a.remainder(b));
+  }
+}
+
+builtin$abs$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.abs());
+
+  return JS('num', @'Math.abs(#)', receiver);
+}
+
+builtin$toInt$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.toInt());
+
+  if (receiver.isNaN()) throw new BadNumberFormatException('NaN');
+
+  if (receiver.isInfinite()) throw new BadNumberFormatException('Infinity');
+
+  var truncated = receiver.truncate();
+  return JS('bool', @'# == -0.0', truncated) ? 0 : truncated;
+}
+
+builtin$ceil$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.ceil());
+
+  return JS('num', @'Math.ceil(#)', receiver);
+}
+
+builtin$floor$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.floor());
+
+  return JS('num', @'Math.floor(#)', receiver);
+}
+
+builtin$isInfinite$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.isInfinite());
+
+  return JS('bool', @'# == Infinity', receiver)
+    || JS('bool', @'# == -Infinity', receiver);
+}
+
+builtin$negate$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.negate());
+
+  return JS('num', @'-#', receiver);
+}
+
+builtin$round$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.round());
+
+  if (JS('bool', @'# < 0', receiver)) {
+    return JS('num', @'-Math.round(-#)', receiver);
+  } else {
+    return JS('num', @'Math.round(#)', receiver);
+  }
+}
+
+builtin$toDouble$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.toDouble());
+
+  // TODO(ahe): Just return receiver?
+  return JS('double', @'# + 0', receiver);
+}
+
+builtin$truncate$0(receiver) {
+  if (receiver is !num) return UNINTERCEPTED(receiver.truncate());
+
+  return receiver < 0 ? receiver.ceil() : receiver.floor();
+}
+
+builtin$toStringAsFixed$1(receiver, fractionDigits) {
+  if (receiver is !num) {
+    return UNINTERCEPTED(receiver.toStringAsFixed(fractionDigits));
+  }
+  checkNum(fractionDigits);
+
+  String result = JS('String', @'#.toFixed(#)', receiver, fractionDigits);
+  if (receiver == 0 && receiver.isNegative()) return "-$result";
+  return result;
+}
+
+builtin$toStringAsExponential$1(receiver, fractionDigits) {
+  if (receiver is !num) {
+    return UNINTERCEPTED(receiver.toStringAsExponential(fractionDigits));
+  }
+  if (fractionDigits !== null) checkNum(fractionDigits);
+
+  String result = JS('String', @'#.toExponential(#)',
+                     receiver, fractionDigits);
+  if (receiver == 0 && receiver.isNegative()) return "-$result";
+  return result;
+}
+
+builtin$toStringAsPrecision$1(receiver, fractionDigits) {
+  if (receiver is !num) {
+    return UNINTERCEPTED(receiver.toStringAsPrecision(fractionDigits));
+  }
+  checkNum(fractionDigits);
+
+  String result = JS('String', @'#.toPrecision(#)',
+                     receiver, fractionDigits);
+  if (receiver == 0 && receiver.isNegative()) return "-$result";
+  return result;
+}
+
+builtin$toRadixString$1(receiver, radix) {
+  if (receiver is !num) {
+    return UNINTERCEPTED(receiver.toRadixString(radix));
+  }
+  checkNum(radix);
+
+  return JS('String', @'#.toString(#)', receiver, radix);
+}
+
+builtin$allMatches$1(receiver, str) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.allMatches(str));
+  checkString(str);
+  return allMatchesInStringUnchecked(receiver, str);
+}
+
+builtin$concat$1(receiver, other) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.concat(other));
+
+  if (other is !String) throw new IllegalArgumentException(other);
+  return JS('String', @'# + #', receiver, other);
+}
+
+builtin$contains$1(receiver, other) {
+  if (receiver is !String) {
+    return UNINTERCEPTED(receiver.contains(other));
+  }
+  return builtin$contains$2(receiver, other, 0);
+}
+
+builtin$contains$2(receiver, other, startIndex) {
+  if (receiver is !String) {
+    return UNINTERCEPTED(receiver.contains(other, startIndex));
+  }
+  checkNull(other);
+  return stringContainsUnchecked(receiver, other, startIndex);
+}
+
+builtin$endsWith$1(receiver, other) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.endsWith(other));
+
+  checkString(other);
+  int receiverLength = receiver.length;
+  int otherLength = other.length;
+  if (otherLength > receiverLength) return false;
+  return other == receiver.substring(receiverLength - otherLength);
+}
+
+builtin$replaceAll$2(receiver, from, to) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.replaceAll(from, to));
+
+  checkString(to);
+  return stringReplaceAllUnchecked(receiver, from, to);
+}
+
+builtin$replaceFirst$2(receiver, from, to) {
+  if (receiver is !String) {
+    return UNINTERCEPTED(receiver.replaceFirst(from, to));
+  }
+  checkString(to);
+  return stringReplaceFirstUnchecked(receiver, from, to);
+}
+
+builtin$split$1(receiver, pattern) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.split(pattern));
+  checkNull(pattern);
+  return stringSplitUnchecked(receiver, pattern);
+}
+
+builtin$splitChars$0(receiver) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.splitChars());
+
+  return JS('List', @'#.split("")', receiver);
+}
+
+builtin$startsWith$1(receiver, other) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.startsWith(other));
+  checkString(other);
+
+  int length = other.length;
+  if (length > receiver.length) return false;
+  return JS('bool', @'# == #', other,
+            JS('String', @'#.substring(0, #)', receiver, length));
+}
+
+builtin$substring$1(receiver, startIndex) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.substring(startIndex));
+
+  return builtin$substring$2(receiver, startIndex, null);
+}
+
+builtin$substring$2(receiver, startIndex, endIndex) {
+  if (receiver is !String) {
+    return UNINTERCEPTED(receiver.substring(startIndex, endIndex));
+  }
+  checkNum(startIndex);
+  var length = receiver.length;
+  if (endIndex === null) endIndex = length;
+  checkNum(endIndex);
+  if (startIndex < 0 ) throw new IndexOutOfRangeException(startIndex);
+  if (startIndex > endIndex) throw new IndexOutOfRangeException(startIndex);
+  if (endIndex > length) throw new IndexOutOfRangeException(endIndex);
+  return substringUnchecked(receiver, startIndex, endIndex);
+}
+
+substringUnchecked(receiver, startIndex, endIndex)
+  => JS('String', @'#.substring(#, #)', receiver, startIndex, endIndex);
+
+
+builtin$toLowerCase$0(receiver) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.toLowerCase());
+
+  return JS('String', @'#.toLowerCase()', receiver);
+}
+
+builtin$toUpperCase$0(receiver) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.toUpperCase());
+
+  return JS('String', @'#.toUpperCase()', receiver);
+}
+
+builtin$trim$0(receiver) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.trim());
+
+  return JS('String', @'#.trim()', receiver);
+}
+
+class MathNatives {
+  static int parseInt(str) {
+    checkString(str);
+    if (!JS('bool',
+            @'/^\s*[+-]?(?:0[xX][abcdefABCDEF0-9]+|\d+)\s*$/.test(#)',
+            str)) {
+      throw new BadNumberFormatException(str);
+    }
+    var trimmed = str.trim();
+    var base = 10;;
+    if ((trimmed.length > 2 && (trimmed[1] == 'x' || trimmed[1] == 'X')) ||
+        (trimmed.length > 3 && (trimmed[2] == 'x' || trimmed[2] == 'X'))) {
+      base = 16;
+    }
+    var ret = JS('num', @'parseInt(#, #)', trimmed, base);
+    if (ret.isNaN()) throw new BadNumberFormatException(str);
+    return ret;
+  }
+
+  static double parseDouble(String str) {
+    checkString(str);
+    var ret = JS('num', @'parseFloat(#)', str);
+    if (ret == 0 && (str.startsWith("0x") || str.startsWith("0X"))) {
+      // TODO(ahe): This is unspecified, but tested by co19.
+      ret = JS('num', @'parseInt(#)', str);
+    }
+    if (ret.isNaN() && str != 'NaN' && str != '-NaN') {
+      throw new BadNumberFormatException(str);
+    }
+    return ret;
+  }
+
+  static double sqrt(num value)
+    => JS('double', @'Math.sqrt(#)', checkNum(value));
+
+  static double sin(num value)
+    => JS('double', @'Math.sin(#)', checkNum(value));
+
+  static double cos(num value)
+    => JS('double', @'Math.cos(#)', checkNum(value));
+
+  static double tan(num value)
+    => JS('double', @'Math.tan(#)', checkNum(value));
+
+  static double acos(num value)
+    => JS('double', @'Math.acos(#)', checkNum(value));
+
+  static double asin(num value)
+    => JS('double', @'Math.asin(#)', checkNum(value));
+
+  static double atan(num value)
+    => JS('double', @'Math.atan(#)', checkNum(value));
+
+  static double atan2(num a, num b)
+    => JS('double', @'Math.atan2(#, #)', checkNum(a), checkNum(b));
+
+  static double exp(num value)
+    => JS('double', @'Math.exp(#)', checkNum(value));
+
+  static double log(num value)
+    => JS('double', @'Math.log(#)', checkNum(value));
+
+  static num pow(num value, num exponent) {
+    checkNum(value);
+    checkNum(exponent);
+    return JS('num', @'Math.pow(#, #)', value, exponent);
+  }
+
+  static double random() => JS('double', @'Math.random()');
+}
+
+/**
+ * This is the [Jenkins hash function][1] but using masking to keep
+ * values in SMI range. This was inspired by jmesserly's work in
+ * Frog.
+ *
+ * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
+ */
+builtin$hashCode$0(receiver) {
+  // TODO(ahe): This method shouldn't have to use JS. Update when our
+  // optimizations are smarter.
+  if (receiver is num) return JS('int', @'# & 0x1FFFFFFF', receiver);
+  if (receiver is !String) return UNINTERCEPTED(receiver.hashCode());
+  int hash = 0;
+  int length = JS('int', @'#.length', receiver);
+  for (int i = 0; i < length; i++) {
+    hash = 0x1fffffff & (hash + JS('int', @'#.charCodeAt(#)', receiver, i));
+    hash = 0x1fffffff & (hash + JS('int', @'# << #', 0x0007ffff & hash, 10));
+    hash ^= hash >> 6;
+  }
+  hash = 0x1fffffff & (hash + JS('int', @'# << #', 0x03ffffff & hash, 3));
+  hash ^= hash >> 11;
+  return 0x1fffffff & (hash + JS('int', @'# << #', 0x00003fff & hash, 15));
+}
+
+// TODO(ahe): Dynamic may be overridden.
+builtin$get$dynamic(receiver) => receiver;
+
+/**
+ * Called by generated code to capture the stacktrace before throwing
+ * an exception.
+ */
+captureStackTrace(ex) {
+  var jsError = JS('Object', @'new Error()');
+  JS('void', @'#.dartException = #', jsError, ex);
+  JS('void', @'''#.toString = #''', jsError, DART_CLOSURE_TO_JS(toStringWrapper));
+  return jsError;
+}
+
+/**
+ * This method is installed as JavaScript toString method on exception
+ * objects in [captureStackTrace]. So JavaScript 'this' binds to an
+ * instance of JavaScript Error to which we have added a property
+ * 'dartException' which holds a Dart object.
+ */
+toStringWrapper() => JS('Object', @'this.dartException').toString();
+
+builtin$charCodes$0(receiver) {
+  if (receiver is !String) return UNINTERCEPTED(receiver.charCodes());
+  int len = receiver.length;
+  List<int> result = new List<int>(len);
+  for (int i = 0; i < len; i++) {
+    result[i] = receiver.charCodeAt(i);
+  }
+  return result;
+}
+
+makeLiteralListConst(list) {
+  JS('bool', @'#.immutable$list = #', list, true);
+  JS('bool', @'#.fixed$length = #', list, true);
+  return list;
+}
+
+/**
+ * Called from catch blocks in generated code to extract the Dart
+ * exception from the thrown value. The thrown value may have been
+ * created by [captureStackTrace] or it may be a 'native' JS
+ * exception.
+ *
+ * Some native exceptions are mapped to new Dart instances, others are
+ * returned unmodified.
+ */
+unwrapException(ex) {
+  // Note that we are checking if the object has the property. If it
+  // has, it could be set to null if the thrown value is null.
+  if (JS('bool', @'"dartException" in #', ex)) {
+    return JS('Object', @'#.dartException', ex);
+  } else if (JS('bool', @'# instanceof TypeError', ex)) {
+    // TODO(ahe): ex.type is Chrome specific.
+    var type = JS('String', @'#.type', ex);
+    var jsArguments = JS('Object', @'#.arguments', ex);
+    var name = jsArguments[0];
+    if (type == 'property_not_function' ||
+        type == 'called_non_callable' ||
+        type == 'non_object_property_call' ||
+        type == 'non_object_property_load') {
+      if (name !== null && name.startsWith(@'$call$')) {
+        return new ObjectNotClosureException();
+      } else {
+        return new NullPointerException();
+      }
+    } else if (type == 'undefined_method') {
+      if (name is String && name.startsWith(@'$call$')) {
+        return new ObjectNotClosureException();
+      } else {
+        return new NoSuchMethodException('', name, []);
+      }
+    }
+  } else if (JS('bool', @'# instanceof RangeError', ex)) {
+    var message = JS('String', @'#.message', ex);
+    if (message.contains('call stack')) {
+      return new StackOverflowException();
+    }
+  }
+  return ex;
+}
+
+/**
+ * Called by generated code to fetch the stack trace from an
+ * exception.
+ */
+StackTrace getTraceFromException(exception) {
+  return new StackTrace(JS("var", @"#.stack", exception));
+}
+
+class StackTrace {
+  var stack;
+  StackTrace(this.stack);
+  String toString() => stack != null ? stack : '';
+}
+
+
+/**
+ * Called by generated code to build a map literal. [keyValuePairs] is
+ * a list of key, value, key, value, ..., etc.
+ */
+makeLiteralMap(List keyValuePairs) {
+  Iterator iterator = keyValuePairs.iterator();
+  Map result = new LinkedHashMap();
+  while (iterator.hasNext()) {
+    String key = iterator.next();
+    var value = iterator.next();
+    result[key] = value;
+  }
+  return result;
+}
+
+invokeClosure(Function closure,
+              var isolate,
+              int numberOfArguments,
+              var arg1,
+              var arg2) {
+  if (numberOfArguments == 0) {
+    return JS_CALL_IN_ISOLATE(isolate, () => closure());
+  } else if (numberOfArguments == 1) {
+    return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1));
+  } else if (numberOfArguments == 2) {
+    return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2));
+  } else {
+    throw new Exception(
+        'Unsupported number of arguments for wrapped closure');
+  }
+}
+
+/**
+ * Called by generated code to convert a Dart closure to a JS
+ * closure when the Dart closure is passed to the DOM.
+ */
+convertDartClosureToJS(closure) {
+  if (closure === null) return null;
+  var function = JS('var', @'#.$identity', closure);
+  if (JS('bool', @'!!#', function)) return function;
+
+  function = JS("var", @"""function() {
+    return #(#, #, arguments.length, arguments[0], arguments[1]);
+  }""",
+  DART_CLOSURE_TO_JS(invokeClosure),
+  closure,
+  JS_CURRENT_ISOLATE());
+
+  JS('void', @'#.$identity = #', closure, function);
+  return function;
+}
+
+/**
+ * Super class for Dart closures.
+ */
+class Closure implements Function {
+  String toString() => "Closure";
+}
+
+bool jsHasOwnProperty(var jsObject, String property) {
+  return JS('bool', @'#.hasOwnProperty(#)', jsObject, property);
+}
+
+jsPropertyAccess(var jsObject, String property) {
+  return JS('var', @'#[#]', jsObject, property);
+}
+
+/**
+ * Called at the end of unaborted switch cases to get the singleton
+ * FallThroughError exception that will be thrown.
+ */
+getFallThroughError() => const FallThroughError();
+
+builtin$isEven$0(receiver) {
+  if (receiver is !int) return UNINTERCEPTED(receiver.isEven());
+  return (receiver & 1) === 0;
+}
+
+builtin$isOdd$0(receiver) {
+  if (receiver is !int) return UNINTERCEPTED(receiver.isOdd());
+  return (receiver & 1) === 1;
+}
+
+/**
+ * Represents the type Dynamic. The compiler treats this specially.
+ */
+interface Dynamic {
+}
+
+/**
+ * Represents the type of Null. The compiler treats this specially.
+ */
+class Null {
+  factory Null() {
+    throw new UnsupportedOperationException('new Null()');
+  }
+}
+
+builtin$get$toString(receiver) => () => builtin$toString$0(receiver);
diff --git a/lib/compiler/implementation/lib/mock.dart b/lib/compiler/implementation/lib/mock.dart
new file mode 100644
index 0000000..f61ef40
--- /dev/null
+++ b/lib/compiler/implementation/lib/mock.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2012, 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.
+
+// Mocks of things that Leg cannot read directly.
+
+// TODO(ahe): Remove this file.
+
+class AssertionError {}
+class TypeError extends AssertionError {}
+
+class FallThroughError {
+  const FallThroughError();
+  String toString() => "Switch case fall-through.";
+}
+
+// TODO(ahe): VM specfic exception?
+class InternalError {
+  const InternalError(this._msg);
+  String toString() => "InternalError: '${_msg}'";
+  final String _msg;
+}
+
+// TODO(ahe): VM specfic exception?
+class StaticResolutionException implements Exception {}
+
+void assert(condition) {}
+
+// TODO(ahe): Not sure ByteArray belongs in the core library.
+interface ByteArray extends List default _InternalByteArray {
+  ByteArray(int length);
+}
+
+class _InternalByteArray {
+  factory _InternalByteArray(int length) {
+    throw new UnsupportedOperationException("new ByteArray($length)");
+  }
+}
+
+// TODO(ahe): This definitely does not belong in the core library.
+void exit(int exitCode) {
+  throw new UnsupportedOperationException("exit($exitCode)");
+}
diff --git a/lib/compiler/implementation/lib/mockimpl.dart b/lib/compiler/implementation/lib/mockimpl.dart
new file mode 100644
index 0000000..0182b37
--- /dev/null
+++ b/lib/compiler/implementation/lib/mockimpl.dart
@@ -0,0 +1,402 @@
+// Copyright (c) 2012, 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.
+
+// Mocks of classes and interfaces that Leg cannot read directly.
+
+// TODO(ahe): Remove this file.
+
+class JSSyntaxRegExp implements RegExp {
+  final String pattern;
+  final bool multiLine;
+  final bool ignoreCase;
+
+  const JSSyntaxRegExp(String pattern,
+                       [bool multiLine = false, bool ignoreCase = false])
+    : this.pattern = pattern,
+      this.multiLine = multiLine,
+      this.ignoreCase = ignoreCase;
+
+  JSSyntaxRegExp._globalVersionOf(JSSyntaxRegExp other)
+      : this.pattern = other.pattern,
+        this.multiLine = other.multiLine,
+        this.ignoreCase = other.ignoreCase {
+    regExpAttachGlobalNative(this);
+  }
+
+  Match firstMatch(String str) {
+    List<String> m = regExpExec(this, checkString(str));
+    if (m === null) return null;
+    var matchStart = regExpMatchStart(m);
+    // m.lastIndex only works with flag 'g'.
+    var matchEnd = matchStart + m[0].length;
+    return new MatchImplementation(pattern, str, matchStart, matchEnd, m);
+  }
+
+  bool hasMatch(String str) => regExpTest(this, checkString(str));
+
+  String stringMatch(String str) {
+    var match = firstMatch(str);
+    return match === null ? null : match.group(0);
+  }
+
+  Iterable<Match> allMatches(String str) {
+    checkString(str);
+    return new _AllMatchesIterable(this, str);
+  }
+
+  _getNative() => regExpGetNative(this);
+}
+
+class MatchImplementation implements Match {
+  const MatchImplementation(
+      String this.pattern,
+      String this.str,
+      int this._start,
+      int this._end,
+      List<String> this._groups);
+
+  final String pattern;
+  final String str;
+  final int _start;
+  final int _end;
+  final List<String> _groups;
+
+  int start() => _start;
+  int end() => _end;
+  String group(int index) => _groups[index];
+  String operator [](int index) => group(index);
+  int groupCount() => _groups.length - 1;
+
+  List<String> groups(List<int> groups) {
+    List<String> out = [];
+    for (int i in groups) {
+      out.add(group(i));
+    }
+    return out;
+  }
+}
+
+class _AllMatchesIterable implements Iterable<Match> {
+  final JSSyntaxRegExp _re;
+  final String _str;
+
+  const _AllMatchesIterable(this._re, this._str);
+
+  Iterator<Match> iterator() => new _AllMatchesIterator(_re, _str);
+}
+
+class _AllMatchesIterator implements Iterator<Match> {
+  final RegExp _re;
+  final String _str;
+  Match _next;
+  bool _done;
+
+  _AllMatchesIterator(JSSyntaxRegExp re, String this._str)
+    : _done = false, _re = new JSSyntaxRegExp._globalVersionOf(re);
+
+  Match next() {
+    if (!hasNext()) {
+      throw const NoMoreElementsException();
+    }
+
+    // _next is set by #hasNext
+    var next = _next;
+    _next = null;
+    return next;
+  }
+
+  bool hasNext() {
+    if (_done) {
+      return false;
+    } else if (_next != null) {
+      return true;
+    }
+
+    _next = _re.firstMatch(_str);
+    if (_next == null) {
+      _done = true;
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
+
+class ReceivePortFactory {
+  factory ReceivePort() {
+    throw 'factory ReceivePort is not implemented';
+  }
+}
+
+class StringBase {
+  static String createFromCharCodes(List<int> charCodes) {
+    checkNull(charCodes);
+    if (!isJsArray(charCodes)) {
+      if (charCodes is !List) throw new IllegalArgumentException(charCodes);
+      charCodes = new List.from(charCodes);
+    }
+    return Primitives.stringFromCharCodes(charCodes);
+  }
+
+  static String join(List<String> strings, String separator) {
+    checkNull(strings);
+    checkNull(separator);
+    var result = "";
+    var first = true;
+    for (var string in strings) {
+      checkNull(string);
+      if (string is !String) throw new IllegalArgumentException(string);
+      if (!first) result += separator; // TODO(ahe): Use string buffer.
+      result += string; // TODO(ahe): Use string buffer.
+      first = false;
+    }
+    return result;
+  }
+
+  static String concatAll(List<String> strings) {
+    checkNull(strings);
+    var result = "";
+    for (var string in strings) {
+      checkNull(string);
+      if (string is !String) throw new IllegalArgumentException(string);
+      result += string; // TODO(ahe): Use string buffer.
+    }
+    return result;
+  }
+}
+
+class TimeZoneImplementation implements TimeZone {
+  const TimeZoneImplementation.utc() : isUtc = true;
+  TimeZoneImplementation.local() : isUtc = false;
+
+  bool operator ==(Object other) {
+    if (!(other is TimeZoneImplementation)) return false;
+    return isUtc == other.isUtc;
+  }
+
+  final bool isUtc;
+}
+
+class DateImplementation implements Date {
+  final int value;
+  final TimeZoneImplementation timeZone;
+
+  factory DateImplementation(int years,
+                             int month,
+                             int day,
+                             int hours,
+                             int minutes,
+                             int seconds,
+                             int milliseconds) {
+    return new DateImplementation.withTimeZone(
+        years, month, day,
+        hours, minutes, seconds, milliseconds,
+        new TimeZoneImplementation.local());
+  }
+
+  DateImplementation.withTimeZone(int years,
+                                  int month,
+                                  int day,
+                                  int hours,
+                                  int minutes,
+                                  int seconds,
+                                  int milliseconds,
+                                  TimeZoneImplementation timeZone)
+      : this.timeZone = checkNull(timeZone),
+        value = Primitives.valueFromDecomposedDate(years, month, day,
+                                                   hours, minutes, seconds,
+                                                   milliseconds,
+                                                   timeZone.isUtc) {
+    _asJs();
+  }
+
+  DateImplementation.now()
+      : timeZone = new TimeZone.local(),
+        value = Primitives.dateNow() {
+    _asJs();
+  }
+
+  factory DateImplementation.fromString(String formattedString) {
+    // Read in (a subset of) ISO 8601.
+    // Examples:
+    //    - "2012-02-27 13:27:00"
+    //    - "2012-02-27 13:27:00.423z"
+    //    - "20120227 13:27:00"
+    //    - "20120227T132700"
+    //    - "20120227"
+    //    - "2012-02-27T14Z"
+    //    - "-123450101 00:00:00 Z"  // In the year -12345.
+    final RegExp re = const RegExp(
+        @'^([+-]?\d?\d\d\d\d)-?(\d\d)-?(\d\d)' // The day part.
+        @'(?:[ T](\d\d)(?::?(\d\d)(?::?(\d\d)(?:.(\d{1,5}))?)?)? ?([zZ])?)?$');
+    Match match = re.firstMatch(formattedString);
+    if (match !== null) {
+      int parseIntOrZero(String matched) {
+        // TODO(floitsch): we should not need to test against the empty string.
+        if (matched === null || matched == "") return 0;
+        return Math.parseInt(matched);
+      }
+
+      int years = Math.parseInt(match[1]);
+      int month = Math.parseInt(match[2]);
+      int day = Math.parseInt(match[3]);
+      int hours = parseIntOrZero(match[4]);
+      int minutes = parseIntOrZero(match[5]);
+      int seconds = parseIntOrZero(match[6]);
+      bool addOneMillisecond = false;
+      int milliseconds = parseIntOrZero(match[7]);
+      if (milliseconds != 0) {
+        if (match[7].length == 1) {
+          milliseconds *= 100;
+        } else if (match[7].length == 2) {
+          milliseconds *= 10;
+        } else if (match[7].length == 3) {
+          // Do nothing.
+        } else if (match[7].length == 4) {
+          addOneMillisecond = ((milliseconds % 10) >= 5);
+          milliseconds ~/= 10;
+        } else {
+          assert(match[7].length == 5);
+          addOneMillisecond = ((milliseconds %100) >= 50);
+          milliseconds ~/= 100;
+        }
+        if (addOneMillisecond && milliseconds < 999) {
+          addOneMillisecond = false;
+          milliseconds++;
+        }
+      }
+      // TODO(floitsch): we should not need to test against the empty string.
+      bool isUtc = (match[8] !== null) && (match[8] != "");
+      TimeZone timezone = isUtc ? const TimeZone.utc() : new TimeZone.local();
+      int epochValue = Primitives.valueFromDecomposedDate(
+          years, month, day, hours, minutes, seconds, milliseconds, isUtc);
+      if (epochValue === null) {
+        throw new IllegalArgumentException(formattedString);
+      }
+      if (addOneMillisecond) epochValue++;
+      return new DateImplementation.fromEpoch(epochValue, timezone);
+    } else {
+      throw new IllegalArgumentException(formattedString);
+    }
+  }
+
+  const DateImplementation.fromEpoch(this.value, this.timeZone);
+
+  bool operator ==(other) {
+    if (!(other is DateImplementation)) return false;
+    return (value == other.value) && (timeZone == other.timeZone);
+  }
+
+  int compareTo(Date other) {
+    checkNull(other);
+    return value.compareTo(other.value);
+  }
+
+  Date changeTimeZone(TimeZone targetTimeZone) {
+    if (targetTimeZone == null) {
+      targetTimeZone = new TimeZoneImplementation.local();
+    }
+    return new Date.fromEpoch(value, targetTimeZone);
+  }
+
+  int get year() => Primitives.getYear(this);
+
+  int get month() => Primitives.getMonth(this);
+
+  int get day() => Primitives.getDay(this);
+
+  int get hours() => Primitives.getHours(this);
+
+  int get minutes() => Primitives.getMinutes(this);
+
+  int get seconds() => Primitives.getSeconds(this);
+
+  int get milliseconds() => Primitives.getMilliseconds(this);
+
+  int get weekday() {
+    // Adjust by one because JS weeks start on Sunday.
+    var day = Primitives.getWeekday(this);
+    return (day + 6) % 7;
+  }
+
+  bool isLocalTime() {
+    return !timeZone.isUtc;
+  }
+
+  bool isUtc() {
+    return timeZone.isUtc;
+  }
+
+  String toString() {
+    String fourDigits(int n) {
+      int absN = n.abs();
+      String sign = n < 0 ? "-" : "";
+      if (absN >= 1000) return "$n";
+      if (absN >= 100) return "${sign}0$absN";
+      if (absN >= 10) return "${sign}00$absN";
+      if (absN >= 1) return "${sign}000$absN";
+      throw new IllegalArgumentException(n);
+    }
+
+    String threeDigits(int n) {
+      if (n >= 100) return "${n}";
+      if (n > 10) return "0${n}";
+      return "00${n}";
+    }
+
+    String twoDigits(int n) {
+      if (n >= 10) return "${n}";
+      return "0${n}";
+    }
+
+    String y = fourDigits(year);
+    String m = twoDigits(month);
+    String d = twoDigits(day);
+    String h = twoDigits(hours);
+    String min = twoDigits(minutes);
+    String sec = twoDigits(seconds);
+    String ms = threeDigits(milliseconds);
+    if (timeZone.isUtc) {
+      return "$y-$m-$d $h:$min:$sec.${ms}Z";
+    } else {
+      return "$y-$m-$d $h:$min:$sec.$ms";
+    }
+  }
+
+  // Adds the [duration] to this Date instance.
+  Date add(Duration duration) {
+    checkNull(duration);
+    return new DateImplementation.fromEpoch(value + duration.inMilliseconds,
+                                            timeZone);
+  }
+
+  // Subtracts the [duration] from this Date instance.
+  Date subtract(Duration duration) {
+    checkNull(duration);
+    return new DateImplementation.fromEpoch(value - duration.inMilliseconds,
+                                            timeZone);
+  }
+
+  // Returns a [Duration] with the difference of [this] and [other].
+  Duration difference(Date other) {
+    checkNull(other);
+    return new Duration(milliseconds: value - other.value);
+  }
+
+  // Lazily keep a JS Date stored in the dart object.
+  var _asJs() => Primitives.lazyAsJsDate(this);
+}
+
+class ListFactory<E> implements List<E> {
+  factory List([int length]) => Primitives.newList(length);
+  factory List.from(Iterable<E> other) {
+    List<E> result = new List<E>();
+    // TODO(ahe): Use for-in when it is implemented correctly.
+    Iterator<E> iterator = other.iterator();
+    while (iterator.hasNext()) {
+      result.add(iterator.next());
+    }
+    return result;
+  }
+}
diff --git a/lib/compiler/implementation/lib/native_helper.dart b/lib/compiler/implementation/lib/native_helper.dart
new file mode 100644
index 0000000..fd0a652
--- /dev/null
+++ b/lib/compiler/implementation/lib/native_helper.dart
@@ -0,0 +1,263 @@
+// Copyright (c) 2012, 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.
+
+String typeNameInChrome(obj) {
+  String name = JS('String', "#.constructor.name", obj);
+  if (name == 'Window') return 'DOMWindow';
+  return name;
+}
+
+String typeNameInFirefox(obj) {
+  String name = constructorNameFallback(obj);
+  if (name == 'Window') return 'DOMWindow';
+  if (name == 'Document') return 'HTMLDocument';
+  if (name == 'XMLDocument') return 'Document';
+  return name;
+}
+
+String typeNameInIE(obj) {
+  String name = constructorNameFallback(obj);
+  if (name == 'Window') return 'DOMWindow';
+  // IE calls both HTML and XML documents 'Document', so we check for the
+  // xmlVersion property, which is the empty string on HTML documents.
+  if (name == 'Document' && JS('bool', '!!#.xmlVersion', obj)) return 'Document';
+  if (name == 'Document') return 'HTMLDocument';
+  return name;
+}
+
+String constructorNameFallback(obj) {
+  var constructor = JS('var', "#.constructor", obj);
+  if (JS('String', "typeof(#)", constructor) === 'function') {
+    // The constructor isn't null or undefined at this point. Try
+    // to grab hold of its name.
+    var name = JS('var', '#.name', constructor);
+    // If the name is a non-empty string, we use that as the type
+    // name of this object. On Firefox, we often get 'Object' as
+    // the constructor name even for more specialized objects so
+    // we have to fall through to the toString() based implementation
+    // below in that case.
+    if (JS('String', "typeof(#)", name) === 'string'
+        && !name.isEmpty()
+        && name !== 'Object') {
+      return name;
+    }
+  }
+  String string = JS('String', 'Object.prototype.toString.call(#)', obj);
+  return string.substring(8, string.length - 1);
+}
+
+
+/**
+ * Returns the function to use to get the type name of an object.
+ */
+Function getTypeNameOfFunction() {
+  // If we're not in the browser, we're almost certainly running on v8.
+  if (JS('String', 'typeof(navigator)') !== 'object') return typeNameInChrome;
+
+  String userAgent = JS('String', "navigator.userAgent");
+  if (userAgent.contains(const RegExp('Chrome|DumpRenderTree'))) {
+    return typeNameInChrome;
+  } else if (userAgent.contains('Firefox')) {
+    return typeNameInFirefox;
+  } else if (userAgent.contains('MSIE')) {
+    return typeNameInIE;
+  } else {
+    return constructorNameFallback;
+  }
+}
+
+
+/**
+ * Cached value for the function to use to get the type name of an
+ * object.
+ */
+Function _getTypeNameOf;
+
+/**
+ * Returns the type name of [obj].
+ */
+String getTypeNameOf(var obj) {
+  if (_getTypeNameOf === null) _getTypeNameOf = getTypeNameOfFunction();
+  return _getTypeNameOf(obj);
+}
+
+String toStringForNativeObject(var obj) {
+  return 'Instance of ${getTypeNameOf(obj)}';
+}
+
+/**
+ * Sets a JavaScript property on an object.
+ */
+void defineProperty(var obj, String property, var value) {
+  JS('void', """Object.defineProperty(#, #,
+      {value: #, enumerable: false, writable: false, configurable: true});""",
+      obj,
+      property,
+      value);
+}
+
+/**
+ * Helper method to throw a [NoSuchMethodException] for a invalid call
+ * on a native object.
+ */
+void throwNoSuchMethod(obj, name, arguments) {
+  throw new NoSuchMethodException(obj, name, arguments);
+}
+
+/**
+ * This method looks up the type name of [obj] in [methods]. If it
+ * cannot find it, it looks into the [_dynamicMetadata] array. If the
+ * method can still not be found, it creates a method that will throw
+ * a [NoSuchMethodException].
+ *
+ * Once it has a method, the prototype of [obj] is patched with that
+ * method, on the property [name]. The method is then invoked.
+ *
+ * This method returns the result of invoking the found method.
+ */
+dynamicBind(var obj,
+            String name,
+            var methods,
+            List arguments) {
+  String tag = getTypeNameOf(obj);
+  var method = JS('var', '#[#]', methods, tag);
+
+  if (method === null && _dynamicMetadata !== null) {
+    for (int i = 0; i < _dynamicMetadata.length; i++) {
+      MetaInfo entry = _dynamicMetadata[i];
+      if (entry.set.contains(tag)) {
+        method = JS('var', '#[#]', methods, entry.tag);
+        if (method !== null) break;
+      }
+    }
+  }
+
+  if (method === null) {
+    method = JS('var', "#['Object']", methods);
+  }
+
+  if (method === null) {
+    method = JS('var',
+        'function () {'
+          '#(#, #, Array.prototype.slice.call(arguments));'
+        '}',
+      DART_CLOSURE_TO_JS(throwNoSuchMethod), obj, name);
+  }
+
+  var nullCheckMethod = JS('var',
+      'function() {'
+        'var res = #.apply(this, Array.prototype.slice.call(arguments));'
+        'return res === null ? (void 0) : res;'
+      '}',
+    method);
+
+  var proto = JS('var', 'Object.getPrototypeOf(#)', obj);
+  if (JS('bool', '!#.hasOwnProperty(#)', proto, name)) {
+    defineProperty(proto, name, nullCheckMethod);
+  }
+
+  return JS('var', '#.apply(#, #)', nullCheckMethod, obj, arguments);
+}
+
+/**
+ * Code for doing the dynamic dispatch on JavaScript prototypes that are not
+ * available at compile-time. Each property of a native Dart class
+ * is registered through this function, which is called with the
+ * following pattern:
+ *
+ * dynamicFunction('propertyName').prototypeName = // JS code
+ *
+ * What this function does is:
+ * - Creates a map of { prototypeName: JS code }.
+ * - Attaches 'propertyName' to the JS Object prototype that will
+ *   intercept at runtime all calls to propertyName.
+ * - Sets the value of 'propertyName' to the returned method from
+ *   [dynamicBind].
+ *
+ */
+dynamicFunction(name) {
+  var f = JS('var', 'Object.prototype[#]', name);
+  if (f !== null && JS('bool', '!!#.methods', f)) {
+    return JS('var', '#.methods', f);
+  }
+
+  // TODO(ngeoffray): We could make this a map if the code we
+  // generate plays well with a Dart map.
+  var methods = JS('var', '{}');
+  // If there is a method attached to the Dart Object class, use it as
+  // the method to call in case no method is registered for that type.
+  var dartMethod = JS('var', 'Object.getPrototypeOf(#)[#]', new Object(), name);
+  if (dartMethod !== null) JS('void', "#['Object'] = #", methods, dartMethod);
+
+  var bind = JS('var',
+      'function() {'
+        'return #(this, #, #, Array.prototype.slice.call(arguments));'
+      '}',
+    DART_CLOSURE_TO_JS(dynamicBind), name, methods);
+
+  JS('void', '#.methods = #', bind, methods);
+  defineProperty(JS('var', 'Object.prototype'), name, bind);
+  return methods;
+}
+
+/**
+ * This class encodes the class hierarchy when we need it for dynamic
+ * dispatch.
+ */
+class MetaInfo {
+  /**
+   * The type name this [MetaInfo] relates to.
+   */
+  String tag;
+
+  /**
+   * A string containing the names of subtypes of [tag], separated by
+   * '|'.
+   */
+  String tags;
+
+  /**
+   * A list of names of subtypes of [tag].
+   */
+  Set<String> set;
+
+  MetaInfo(this.tag, this.tags, this.set);
+}
+
+List<MetaInfo> get _dynamicMetadata() {
+  if (JS('var', 'typeof(\$dynamicMetadata)') === 'undefined') {
+    _dynamicMetadata = <MetaInfo>[];
+  }
+  return JS('var', '\$dynamicMetadata');
+}
+
+void set _dynamicMetadata(List<String> table) {
+  JS('void', '\$dynamicMetadata = #', table);
+}
+
+/**
+ * Builds the metadata used for encoding the class hierarchy of native
+ * classes. The following example:
+ *
+ * class A native "*A" {}
+ * class B native "*B" {}
+ *
+ * Will generate:
+ * ['A', 'A|B']
+ *
+ * This method turns the array into a list of [MetaInfo] objects.
+ */
+void dynamicSetMetadata(List<List<String>> inputTable) {
+  _dynamicMetadata = <MetaInfo>[];
+  for (int i = 0; i < inputTable.length; i++) {
+    String tag = inputTable[i][0];
+    String tags = inputTable[i][1];
+    Set<String> set = new Set<String>();
+    List<String> tagNames = tags.split('|');
+    for (int j = 0; j < tagNames.length; j++) {
+      set.add(tagNames[j]);
+    }
+    _dynamicMetadata.add(new MetaInfo(tag, tags, set));
+  }
+}
diff --git a/lib/compiler/implementation/lib/regexp_helper.dart b/lib/compiler/implementation/lib/regexp_helper.dart
new file mode 100644
index 0000000..7227cd1
--- /dev/null
+++ b/lib/compiler/implementation/lib/regexp_helper.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2012, 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.
+
+List regExpExec(JSSyntaxRegExp regExp, String str) {
+  var nativeRegExp = regExpGetNative(regExp);
+  var result = JS('List', @'#.exec(#)', nativeRegExp, str);
+  if (JS('bool', @'# === null', result)) return null;
+  return result; 
+}
+
+bool regExpTest(JSSyntaxRegExp regExp, String str) {
+  var nativeRegExp = regExpGetNative(regExp);
+  return JS('bool', @'#.test(#)', nativeRegExp, str);
+}
+
+regExpGetNative(JSSyntaxRegExp regExp) {
+  var r = JS('var', @'#._re', regExp);
+  if (r === null) {
+    r = JS('var', @'#._re = #', regExp, regExpMakeNative(regExp));
+  }
+  return r; 
+}
+
+regExpAttachGlobalNative(JSSyntaxRegExp regExp) {
+  JS('var', @'#._re = #', regExp, regExpMakeNative(regExp, global: true)); 
+}
+
+regExpMakeNative(JSSyntaxRegExp regExp, [bool global = false]) {
+  String pattern = regExp.pattern;
+  bool multiLine = regExp.multiLine;
+  bool ignoreCase = regExp.ignoreCase;
+  checkString(pattern);
+  StringBuffer sb = new StringBuffer();
+  if (multiLine) sb.add('m');
+  if (ignoreCase) sb.add('i');
+  if (global) sb.add('g');
+  try {
+    return JS('Object', @'new RegExp(#, #)', pattern, sb.toString());
+  } catch (var e) {
+    throw new IllegalJSRegExpException(pattern,
+                                       JS('String', @'String(#)', e));
+  }
+}
+
+int regExpMatchStart(m) => JS('int', @'#.index', m);
diff --git a/lib/compiler/implementation/lib/string_helper.dart b/lib/compiler/implementation/lib/string_helper.dart
new file mode 100644
index 0000000..e234b2b
--- /dev/null
+++ b/lib/compiler/implementation/lib/string_helper.dart
@@ -0,0 +1,127 @@
+// Copyright (c) 2012, 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.
+
+class StringMatch implements Match {
+  const StringMatch(int this._start,
+                    String this.str,
+                    String this.pattern);
+
+  int start() => _start;
+  int end() => _start + pattern.length;
+  String operator[](int g) => group(g);
+  int groupCount() => 0;
+
+  String group(int group_) {
+    if (group_ != 0) {
+      throw new IndexOutOfRangeException(group_);
+    }
+    return pattern;
+  }
+
+  List<String> groups(List<int> groups_) {
+    List<String> result = new List<String>();
+    for (int g in groups_) {
+      result.add(group(g));
+    }
+    return result;
+  }
+
+  final int _start;
+  final String str;
+  final String pattern;
+}
+
+List<Match> allMatchesInStringUnchecked(String needle, String haystack) {
+  // Copied from StringBase.allMatches in
+  // ../../../runtime/lib/string.dart
+  List<Match> result = new List<Match>();
+  int length = haystack.length;
+  int patternLength = needle.length;
+  int startIndex = 0;
+  while (true) {
+    int position = haystack.indexOf(needle, startIndex);
+    if (position == -1) {
+      break;
+    }
+    result.add(new StringMatch(position, haystack, needle));
+    int endIndex = position + patternLength;
+    if (endIndex == length) {
+      break;
+    } else if (position == endIndex) {
+      ++startIndex;  // empty match, advance and restart
+    } else {
+      startIndex = endIndex;
+    }
+  }
+  return result;
+}
+
+stringContainsUnchecked(receiver, other, startIndex) {
+  if (other is String) {
+    return receiver.indexOf(other, startIndex) !== -1;
+  } else if (other is JSSyntaxRegExp) {
+    return other.hasMatch(receiver.substring(startIndex));
+  } else {
+    var substr = receiver.substring(startIndex);
+    return other.allMatches(substr).iterator().hasNext();
+  }
+}
+
+stringReplaceAllUnchecked(receiver, from, to) {
+  if (from is String) {
+    if (from == "") {
+      if (receiver == "") {
+        return to;
+      } else {
+        StringBuffer result = new StringBuffer();
+        int length = receiver.length;
+        result.add(to);
+        for (int i = 0; i < length; i++) {
+          result.add(receiver[i]);
+          result.add(to);
+        }
+        return result.toString();
+      }
+    } else {
+      RegExp quoteRegExp =
+          const JSSyntaxRegExp(@'[-[\]{}()*+?.,\\^$|#\s]', false, false);
+      var quoter = regExpMakeNative(quoteRegExp, global: true);
+      var quoted = JS('String', @'#.replace(#, "\\$&")', from, quoter);
+      RegExp replaceRegExp = new JSSyntaxRegExp(quoted, false, false);
+      var replacer = regExpMakeNative(replaceRegExp, global: true);    
+      return JS('String', @'#.replace(#, #)', receiver, replacer, to);
+    }
+  } else if (from is JSSyntaxRegExp) {
+    var re = regExpMakeNative(from, global: true);
+    return JS('String', @'#.replace(#, #)', receiver, re, to);
+  } else {
+    checkNull(from);
+    // TODO(floitsch): implement generic String.replace (with patterns).
+    throw "StringImplementation.replaceAll(Pattern) UNIMPLEMENTED";
+  }
+}
+
+stringReplaceFirstUnchecked(receiver, from, to) {
+  if (from is String) {
+    return JS('String', @'#.replace(#, #)', receiver, from, to);
+  } else if (from is JSSyntaxRegExp) {
+    var re = regExpGetNative(from);
+    return JS('String', @'#.replace(#, #)', receiver, re, to);
+  } else {
+    checkNull(from);
+    // TODO(floitsch): implement generic String.replace (with patterns).
+    throw "StringImplementation.replace(Pattern) UNIMPLEMENTED";
+  }
+}
+
+stringSplitUnchecked(receiver, pattern) {
+  if (pattern is String) {
+    return JS('List', @'#.split(#)', receiver, pattern);
+  } else if (pattern is JSSyntaxRegExp) {
+    var re = regExpGetNative(pattern);
+    return JS('List', @'#.split(#)', receiver, re);
+  } else {
+    throw "StringImplementation.split(Pattern) UNIMPLEMENTED";
+  }
+}
diff --git a/lib/compiler/implementation/namer.dart b/lib/compiler/implementation/namer.dart
new file mode 100644
index 0000000..9f0946f
--- /dev/null
+++ b/lib/compiler/implementation/namer.dart
@@ -0,0 +1,218 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * Assigns JavaScript identifiers to Dart variables, class-names and members.
+ */
+class Namer {
+  final Compiler compiler;
+
+  static final CLOSURE_INVOCATION_NAME = const SourceString('\$call');
+  static final OPERATOR_EQUALS = const SourceString('operator\$eq');
+
+  static Set<String> _jsReserved = null;
+  Set<String> get jsReserved() {
+    if (_jsReserved === null) {
+      _jsReserved = new Set<String>();
+      _jsReserved.addAll(JsNames.javaScriptKeywords);
+      _jsReserved.addAll(JsNames.reservedPropertySymbols);
+    }
+    return _jsReserved;
+  }
+
+  Map<Element, String> globals;
+  Map<String, int> usedGlobals;
+
+  Namer(this.compiler)
+      : globals = new Map<Element, String>(),
+        usedGlobals = new Map<String, int>();
+
+  final String CURRENT_ISOLATE = "\$";
+  final String ISOLATE = "Isolate";
+
+
+  String closureInvocationName(Selector selector) {
+    // TODO(floitsch): mangle, while not conflicting with instance names.
+    return instanceMethodInvocationName(null, CLOSURE_INVOCATION_NAME,
+                                        selector);
+  }
+
+  String privateName(LibraryElement lib, SourceString name) {
+    if (name.isPrivate()) {
+      return '_${getName(lib)}${name.slowToString()}';
+    } else {
+      return '${name.slowToString()}';
+    }
+  }
+
+  String instanceMethodName(LibraryElement lib, SourceString name, int arity) {
+    return '${privateName(lib, name)}\$$arity';
+  }
+
+  String instanceMethodInvocationName(LibraryElement lib, SourceString name,
+                                      Selector selector) {
+    // TODO(floitsch): mangle, while preserving uniqueness.
+    StringBuffer buffer = new StringBuffer();
+    List<SourceString> names = selector.getOrderedNamedArguments();
+    for (SourceString argumentName in names) {
+      buffer.add(@'$');
+      argumentName.printOn(buffer);
+    }
+    return '${privateName(lib, name)}\$${selector.argumentCount}$buffer';
+  }
+
+  String instanceFieldName(LibraryElement lib, SourceString name) {
+    return privateName(lib, name);
+  }
+
+  String setterName(LibraryElement lib, SourceString name) {
+    return 'set\$${privateName(lib, name)}';
+  }
+
+  String getterName(LibraryElement lib, SourceString name) {
+    return 'get\$${privateName(lib, name)}';
+  }
+
+  String getFreshGlobalName(String proposedName) {
+    int usedCount = usedGlobals[proposedName];
+    if (usedCount === null) {
+      // No element with this name has been used before.
+      usedGlobals[proposedName] = 1;
+      return proposedName;
+    } else {
+      // Not the first time we see this name. Append a number to make it unique.
+      String name;
+      do {
+        usedCount++;
+        name = '$proposedName$usedCount';
+      } while (usedGlobals[name] !== null);
+      usedGlobals[proposedName] = usedCount;
+      return name;
+    }
+  }
+
+  /**
+   * Returns a preferred JS-id for the given top-level or static element.
+   * The returned id is guaranteed to be a valid JS-id.
+   */
+  String _computeGuess(Element element) {
+    assert(!element.isInstanceMember());
+    LibraryElement lib = element.getLibrary();
+    if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+      FunctionElement functionElement = element;
+      return instanceMethodName(lib, element.name,
+                                functionElement.parameterCount(compiler));
+    } else {
+      // TODO(floitsch): deal with named constructors.
+      String name;
+      if (Elements.isStaticOrTopLevel(element)) {
+        name = element.name.slowToString();
+      } else if (element.kind == ElementKind.GETTER) {
+        name = getterName(lib, element.name);
+      } else if (element.kind == ElementKind.SETTER) {
+        name = setterName(lib, element.name);
+      } else if (element.kind == ElementKind.FUNCTION) {
+        FunctionElement functionElement = element;
+        name = element.name.slowToString();
+        name = '$name\$${functionElement.parameterCount(compiler)}';
+      } else if (element.kind === ElementKind.LIBRARY) {
+        name = 'lib';
+      } else {
+        name = element.name.slowToString();
+      }
+      // Prefix the name with '$' if it is reserved.
+      return safeName(name);
+    }
+  }
+
+  String getBailoutName(Element element) {
+    return '${getName(element)}\$bailout';
+  }
+
+  /**
+   * Returns a preferred JS-id for the given element. The returned id is
+   * guaranteed to be a valid JS-id. Globals and static fields are furthermore
+   * guaranteed to be unique.
+   *
+   * For accessing statics consider calling
+   * [isolateAccess]/[isolateBailoutAccess] or [isolatePropertyAccess] instead.
+   */
+  String getName(Element element) {
+    if (element.isInstanceMember()) {
+      if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+        ConstructorBodyElement bodyElement = element;
+        SourceString name = bodyElement.constructor.name;
+        return instanceMethodName(element.getLibrary(),
+                                  name, bodyElement.parameterCount(compiler));
+      } else if (element.kind == ElementKind.FUNCTION) {
+        FunctionElement functionElement = element;
+        return instanceMethodName(element.getLibrary(),
+                                  element.name,
+                                  functionElement.parameterCount(compiler));
+      } else if (element.kind == ElementKind.GETTER) {
+        return getterName(element.getLibrary(), element.name);
+      } else if (element.kind == ElementKind.SETTER) {
+        return setterName(element.getLibrary(), element.name);
+      } else {
+        return instanceFieldName(element.getLibrary(), element.name);
+      }
+    } else {
+      // Dealing with a top-level or static element.
+      String cached = globals[element];
+      if (cached !== null) return cached;
+
+      String guess = _computeGuess(element);
+      switch (element.kind) {
+        case ElementKind.VARIABLE:
+        case ElementKind.PARAMETER:
+          // The name is not guaranteed to be unique.
+          return guess;
+
+        case ElementKind.GENERATIVE_CONSTRUCTOR:
+        case ElementKind.FUNCTION:
+        case ElementKind.CLASS:
+        case ElementKind.FIELD:
+        case ElementKind.GETTER:
+        case ElementKind.SETTER:
+        case ElementKind.TYPEDEF:
+        case ElementKind.LIBRARY:
+          String result = getFreshGlobalName(guess);
+          globals[element] = result;
+          return result;
+
+        default:
+          compiler.internalError('getName for unknown kind: ${element.kind}',
+                                 node: element.parseNode(compiler));
+      }
+    }
+  }
+
+  String isolateAccess(Element element) {
+    return "$CURRENT_ISOLATE.${getName(element)}";
+  }
+
+  String isolatePropertyAccess(Element element) {
+    return "$ISOLATE.prototype.${getName(element)}";
+  }
+
+  String isolateBailoutPropertyAccess(Element element) {
+    return '${isolatePropertyAccess(element)}\$bailout';
+  }
+
+  String isolateBailoutAccess(Element element) {
+    return '${isolateAccess(element)}\$bailout';
+  }
+
+  String operatorIs(Element element) {
+    return 'is\$${getName(element)}';
+  }
+
+  String safeName(String name) {
+    if (jsReserved.contains(name) || name.startsWith('\$')) {
+      name = "\$$name";
+      assert(!jsReserved.contains(name));
+    }
+    return name;
+  }
+}
diff --git a/lib/compiler/implementation/native_emitter.dart b/lib/compiler/implementation/native_emitter.dart
new file mode 100644
index 0000000..25a72fe
--- /dev/null
+++ b/lib/compiler/implementation/native_emitter.dart
@@ -0,0 +1,356 @@
+// Copyright (c) 2012, 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.
+
+class NativeEmitter {
+
+  Compiler compiler;
+  StringBuffer buffer;
+
+  // Classes that participate in dynamic dispatch. These are the
+  // classes that contain used members.
+  Set<ClassElement> classesWithDynamicDispatch;
+
+  // Native classes found in the application.
+  Set<ClassElement> nativeClasses;
+
+  // Caches the direct native subtypes of a native class.
+  Map<ClassElement, List<ClassElement>> subtypes;
+
+  // Caches the native methods that are overridden by a native class.
+  // Note that the method that overrides does not have to be native:
+  // it's the overridden method that must make sure it will dispatch
+  // to its subclass if it sees an instance whose class is a subclass.
+  Set<FunctionElement> overriddenMethods;
+
+  NativeEmitter(this.compiler)
+      : classesWithDynamicDispatch = new Set<ClassElement>(),
+        nativeClasses = new Set<ClassElement>(),
+        subtypes = new Map<ClassElement, List<ClassElement>>(),
+        overriddenMethods = new Set<FunctionElement>(),
+        buffer = new StringBuffer();
+
+  String get dynamicName() {
+    Element element = compiler.findHelper(
+        const SourceString('dynamicFunction'));
+    return compiler.namer.isolateAccess(element);
+  }
+
+  String get dynamicSetMetadataName() {
+    Element element = compiler.findHelper(
+        const SourceString('dynamicSetMetadata'));
+    return compiler.namer.isolateAccess(element);
+  }
+
+  String get typeNameOfName() {
+    Element element = compiler.findHelper(
+        const SourceString('getTypeNameOf'));
+    return compiler.namer.isolateAccess(element);
+  }
+
+  String get defPropName() {
+    Element element = compiler.findHelper(
+        const SourceString('defineProperty'));
+    return compiler.namer.isolateAccess(element);
+  }
+
+  String get toStringHelperName() {
+    Element element = compiler.findHelper(
+        const SourceString('toStringForNativeObject'));
+    return compiler.namer.isolateAccess(element);
+  }
+
+  void generateNativeLiteral(ClassElement classElement) {
+    String quotedNative = classElement.nativeName.slowToString();
+    String nativeCode = quotedNative.substring(2, quotedNative.length - 1);
+    String className = compiler.namer.getName(classElement);
+    buffer.add(className);
+    buffer.add(' = ');
+    buffer.add(nativeCode);
+    buffer.add(';\n');
+
+    String attachTo(name) => "$className.$name";
+
+    for (Element member in classElement.members) {
+      if (member.isInstanceMember()) {
+        compiler.emitter.addInstanceMember(
+            member, attachTo, buffer, isNative: true);
+      }
+    }
+  }
+
+  bool isNativeLiteral(String quotedName) {
+    return quotedName[1] === '=';
+  }
+
+  bool isNativeGlobal(String quotedName) {
+    return quotedName[1] === '@';
+  }
+
+  String toNativeName(ClassElement cls) {
+    String quotedName = cls.nativeName.slowToString();
+    if (isNativeGlobal(quotedName)) {
+      // Global object, just be like the other types for now.
+      return quotedName.substring(3, quotedName.length - 1);
+    } else {
+      return quotedName.substring(2, quotedName.length - 1);
+    }
+  }
+
+  void generateNativeClass(ClassElement classElement) {
+    nativeClasses.add(classElement);
+
+    assert(classElement.backendMembers.isEmpty());
+    String quotedName = classElement.nativeName.slowToString();
+    if (isNativeLiteral(quotedName)) {
+      generateNativeLiteral(classElement);
+      // The native literal kind needs to be dealt with specially when
+      // generating code for it.
+      return;
+    }
+
+    String nativeName = toNativeName(classElement);
+    bool hasUsedSelectors = false;
+
+    String attachTo(String name) {
+      hasUsedSelectors = true;
+      return "$dynamicName('$name').$nativeName";
+    }
+
+    for (Element member in classElement.members) {
+      if (member.isInstanceMember()) {
+        compiler.emitter.addInstanceMember(
+            member, attachTo, buffer, isNative: true);
+      }
+    }
+
+    compiler.emitter.generateTypeTests(classElement, (Element other) {
+      assert(requiresNativeIsCheck(other));
+      buffer.add('${attachTo(compiler.namer.operatorIs(other))} = ');
+      buffer.add('function() { return true; };\n');
+    });
+
+    if (hasUsedSelectors) classesWithDynamicDispatch.add(classElement);
+  }
+
+  List<ClassElement> getDirectSubclasses(ClassElement cls) {
+    List<ClassElement> result = subtypes[cls];
+    if (result === null) result = const<ClassElement>[];
+    return result;
+  }
+
+  void emitParameterStub(Element member,
+                         String invocationName,
+                         String stubParameters,
+                         List<String> argumentsBuffer,
+                         int indexOfLastOptionalArgumentInParameters) {
+    // The target JS function may check arguments.length so we need to
+    // make sure not to pass any unspecified optional arguments to it.
+    // For example, for the following Dart method:
+    //   foo([x, y, z]);
+    // The call:
+    //   foo(y: 1)
+    // must be turned into a JS call to:
+    //   foo(null, y).
+
+    List<String> nativeArgumentsBuffer = argumentsBuffer.getRange(
+        0, indexOfLastOptionalArgumentInParameters + 1);
+
+    ClassElement classElement = member.enclosingElement;
+    String nativeName = classElement.nativeName.slowToString();
+    String nativeArguments = Strings.join(nativeArgumentsBuffer, ",");
+
+    if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) {
+      // Call the method directly.
+      buffer.add('    return this.${member.name.slowToString()}');
+      buffer.add('($nativeArguments)');
+      return;
+    }
+
+    // If the method is overridden, we must check if the prototype of
+    // 'this' has the method available. Otherwise, we may end up
+    // calling the method from the super class. If the method is not
+    // available, we make a direct call to
+    // Object.prototype.$invocationName. This method will patch the
+    // prototype of 'this' to the real method.
+
+    buffer.add('  if (Object.getPrototypeOf(this).hasOwnProperty(');
+    buffer.add("'$invocationName')) {\n");
+    buffer.add('    return this.${member.name.slowToString()}');
+    buffer.add('($nativeArguments)');
+    buffer.add('\n  }\n');
+    buffer.add('  return Object.prototype.$invocationName.call(this');
+    buffer.add(stubParameters == '' ? '' : ', $stubParameters');
+    buffer.add(');');
+  }
+
+  void emitDynamicDispatchMetadata() {
+    if (classesWithDynamicDispatch.isEmpty()) return;
+    buffer.add('// ${classesWithDynamicDispatch.length} dynamic classes.\n');
+
+    // Build a pre-order traversal over all the classes and their subclasses.
+    Set<ClassElement> seen = new Set<ClassElement>();
+    List<ClassElement> classes = <ClassElement>[];
+    void visit(ClassElement cls) {
+      if (seen.contains(cls)) return;
+      seen.add(cls);
+      for (final ClassElement subclass in getDirectSubclasses(cls)) {
+        visit(subclass);
+      }
+      classes.add(cls);
+    }
+    for (final ClassElement cls in classesWithDynamicDispatch) {
+      visit(cls);
+    }
+
+    Collection<ClassElement> dispatchClasses = classes.filter(
+        (cls) => !getDirectSubclasses(cls).isEmpty() &&
+                  classesWithDynamicDispatch.contains(cls));
+
+    buffer.add('// ${classes.length} classes\n');
+    Collection<ClassElement> classesThatHaveSubclasses = classes.filter(
+        (ClassElement t) => !getDirectSubclasses(t).isEmpty());
+    buffer.add('// ${classesThatHaveSubclasses.length} !leaf\n');
+
+    // Generate code that builds the map from cls tags used in dynamic dispatch
+    // to the set of cls tags of classes that extend (TODO: or implement) those
+    // classes.  The set is represented as a string of tags joined with '|'.
+    // This is easily split into an array of tags, or converted into a regexp.
+    //
+    // To reduce the size of the sets, subsets are CSE-ed out into variables.
+    // The sets could be much smaller if we could make assumptions about the
+    // cls tags of other classes (which are constructor names or part of the
+    // result of Object.protocls.toString).  For example, if objects that are
+    // Dart objects could be easily excluded, then we might be able to simplify
+    // the test, replacing dozens of HTMLxxxElement classes with the regexp
+    // /HTML.*Element/.
+
+    // Temporary variables for common substrings.
+    List<String> varNames = <String>[];
+    // var -> expression
+    Map<String, String> varDefns = <String>{};
+    // tag -> expression (a string or a variable)
+    Map<ClassElement, String> tagDefns = new Map<ClassElement, String>();
+
+    String makeExpression(ClassElement cls) {
+      // Expression fragments for this set of cls keys.
+      List<String> expressions = <String>[];
+      // TODO: Remove if cls is abstract.
+      List<String> subtags = [toNativeName(cls)];
+      void walk(ClassElement cls) {
+        for (final ClassElement subclass in getDirectSubclasses(cls)) {
+          ClassElement tag = subclass;
+          String existing = tagDefns[tag];
+          if (existing == null) {
+            subtags.add(toNativeName(tag));
+            walk(subclass);
+          } else {
+            if (varDefns.containsKey(existing)) {
+              expressions.add(existing);
+            } else {
+              String varName = 'v${varNames.length}/*${tag}*/';
+              varNames.add(varName);
+              varDefns[varName] = existing;
+              tagDefns[tag] = varName;
+              expressions.add(varName);
+            }
+          }
+        }
+      }
+      walk(cls);
+      String constantPart = "'${Strings.join(subtags, '|')}'";
+      if (constantPart != "''") expressions.add(constantPart);
+      String expression;
+      if (expressions.length == 1) {
+        expression = expressions[0];
+      } else {
+        expression = "[${Strings.join(expressions, ',')}].join('|')";
+      }
+      return expression;
+    }
+
+    for (final ClassElement cls in dispatchClasses) {
+      tagDefns[cls] = makeExpression(cls);
+    }
+
+    // Write out a thunk that builds the metadata.
+
+    if (!tagDefns.isEmpty()) {
+      buffer.add('(function(){\n');
+
+      for (final String varName in varNames) {
+        buffer.add('  var ${varName} = ${varDefns[varName]};\n');
+      }
+
+      buffer.add('  var table = [\n');
+      buffer.add(
+          '    // [dynamic-dispatch-tag, '
+          'tags of classes implementing dynamic-dispatch-tag]');
+      bool needsComma = false;
+      List<String> entries = <String>[];
+      for (final ClassElement cls in dispatchClasses) {
+        String clsName = toNativeName(cls);
+        entries.add("\n    ['$clsName', ${tagDefns[cls]}]");
+      }
+      buffer.add(Strings.join(entries, ','));
+      buffer.add('];\n');
+      buffer.add('$dynamicSetMetadataName(table);\n');
+
+      buffer.add('})();\n');
+    }
+  }
+
+  bool isSupertypeOfNativeClass(Element element) {
+    if (element.isTypeVariable()) {
+      compiler.cancel("Is check for type variable", element: work.element);
+      return false;
+    }
+    if (element.computeType(compiler) is FunctionType) return false;
+
+    if (!element.isClass()) {
+      compiler.cancel("Is check does not handle element", element: element);
+      return false;
+    }
+
+    return subtypes[element] !== null;
+  }
+
+  bool requiresNativeIsCheck(Element element) {
+    if (!element.isClass()) return false;
+    ClassElement cls = element;
+    if (cls.isNative()) return true;
+    return isSupertypeOfNativeClass(element);
+  }
+
+  void emitIsChecks(StringBuffer buffer) {
+    for (Element type in compiler.universe.isChecks) {
+      if (!requiresNativeIsCheck(type)) continue;
+      String name = compiler.namer.operatorIs(type);
+      buffer.add("$defPropName(Object.prototype, '$name', ");
+      buffer.add('function() { return false; });\n');
+    }
+  }
+
+  void assembleCode(StringBuffer other) {
+    if (nativeClasses.isEmpty()) return;
+
+    // Because of native classes, we have to generate some is checks
+    // by calling a method, instead of accessing a property. So we
+    // attach to the JS Object prototype these methods that return
+    // false, and will be overridden by subclasses when they have to
+    // return true.
+    StringBuffer objectProperties = new StringBuffer();
+    emitIsChecks(objectProperties);
+
+    // In order to have the toString method on every native class,
+    // we must patch the JS Object prototype with a helper method.
+    String toStringName = compiler.namer.instanceMethodName(
+        null, const SourceString('toString'), 0);
+    objectProperties.add("$defPropName(Object.prototype, '$toStringName', ");
+    objectProperties.add(
+        'function() { return $toStringHelperName(this); });\n');
+
+    // Finally, emit the code in the main buffer.
+    other.add('(function() {\n$objectProperties$buffer\n})();\n');
+  }
+}
diff --git a/lib/compiler/implementation/native_handler.dart b/lib/compiler/implementation/native_handler.dart
new file mode 100644
index 0000000..724f3a2
--- /dev/null
+++ b/lib/compiler/implementation/native_handler.dart
@@ -0,0 +1,351 @@
+// Copyright (c) 2012, 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('native');
+#import('../../uri/uri.dart');
+#import('leg.dart');
+#import('elements/elements.dart');
+#import('scanner/scannerlib.dart');
+#import('ssa/ssa.dart');
+#import('tree/tree.dart');
+#import('util/util.dart');
+
+void processNativeClasses(Compiler compiler,
+                          Collection<LibraryElement> libraries) {
+  for (LibraryElement library in libraries) {
+    processNativeClassesInLibrary(compiler, library);
+  }
+}
+
+void addSubtypes(ClassElement cls,
+                 NativeEmitter emitter) {
+  for (Type type in cls.allSupertypes) {
+    List<Element> subtypes = emitter.subtypes.putIfAbsent(
+        type.element,
+        () => <ClassElement>[]);
+    subtypes.add(cls);
+  }
+}
+
+void processNativeClassesInLibrary(Compiler compiler,
+                                   LibraryElement library) {
+  bool hasNativeClass = false;
+  for (Link<Element> link = library.topLevelElements;
+       !link.isEmpty(); link = link.tail) {
+    Element element = link.head;
+    if (element.kind == ElementKind.CLASS) {
+      ClassElement classElement = element;
+      if (classElement.isNative()) {
+        hasNativeClass = true;
+        compiler.registerInstantiatedClass(classElement);
+        // Also parse the node to know all its methods because
+        // otherwise it will only be parsed if there is a call to
+        // one of its constructor.
+        classElement.parseNode(compiler);
+        // Resolve to setup the inheritance.
+        classElement.ensureResolved(compiler);
+        // Add the information that this class is a subtype of
+        // its supertypes. The code emitter and the ssa builder use that
+        // information.
+        NativeEmitter emitter = compiler.emitter.nativeEmitter;
+        addSubtypes(classElement, emitter);
+      }
+    }
+  }
+  if (hasNativeClass) {
+    compiler.registerStaticUse(compiler.findHelper(
+        const SourceString('dynamicFunction')));
+    compiler.registerStaticUse(compiler.findHelper(
+        const SourceString('dynamicSetMetadata')));
+    compiler.registerStaticUse(compiler.findHelper(
+        const SourceString('defineProperty')));
+    compiler.registerStaticUse(compiler.findHelper(
+        const SourceString('toStringForNativeObject')));
+  }
+}
+
+void maybeEnableNative(Compiler compiler,
+                       LibraryElement library,
+                       Uri uri) {
+  String libraryName = uri.toString();
+  if (library.script.name.contains('dart/frog/tests/native/src')
+      || libraryName == 'dart:dom'
+      || libraryName == 'dart:isolate'
+      || libraryName == 'dart:html') {
+    library.define(new ForeignElement(
+        const SourceString('native'), library), compiler);
+    library.canUseNative = true;
+  }
+
+  // Additionaly, if this is a test, we allow access to foreign functions.
+  if (library.script.name.contains('dart/frog/tests/native/src')) {
+    library.define(compiler.findHelper(const SourceString('JS')), compiler);
+  }
+}
+
+void checkAllowedLibrary(ElementListener listener, Token token) {
+  LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary();
+  if (!currentLibrary.canUseNative) {
+    listener.recoverableError("Unexpected token", token: token);
+  }
+}
+
+Token handleNativeBlockToSkip(Listener listener, Token token) {
+  checkAllowedLibrary(listener, token);
+  token = token.next;
+  if (token.kind === STRING_TOKEN) {
+    token = token.next;
+  }
+  if (token.stringValue === '{') {
+    BeginGroupToken beginGroupToken = token;
+    token = beginGroupToken.endGroup;
+  }
+  return token;
+}
+
+Token handleNativeClassBodyToSkip(Listener listener, Token token) {
+  checkAllowedLibrary(listener, token);
+  listener.handleIdentifier(token);
+  token = token.next;
+  if (token.kind !== STRING_TOKEN) {
+    return listener.unexpected(token);
+  }
+  token = token.next;
+  if (token.stringValue !== '{') {
+    return listener.unexpected(token);
+  }
+  BeginGroupToken beginGroupToken = token;
+  token = beginGroupToken.endGroup;
+  return token;
+}
+
+Token handleNativeClassBody(Listener listener, Token token) {
+  checkAllowedLibrary(listener, token);
+  token = token.next;
+  if (token.kind !== STRING_TOKEN) {
+    listener.unexpected(token);
+  } else {
+    token = token.next;
+  }
+  return token;
+}
+
+Token handleNativeFunctionBody(ElementListener listener, Token token) {
+  checkAllowedLibrary(listener, token);
+  Token begin = token;
+  listener.beginExpressionStatement(token);
+  listener.handleIdentifier(token);
+  token = token.next;
+  if (token.kind === STRING_TOKEN) {
+    listener.beginLiteralString(token);
+    listener.endLiteralString(0);
+    listener.pushNode(new NodeList.singleton(listener.popNode()));
+    listener.endSend(token);
+    token = token.next;
+    listener.endExpressionStatement(token);
+  } else {
+    listener.pushNode(new NodeList.empty());
+    listener.endSend(token);
+    listener.endReturnStatement(true, begin, token);
+  }
+  listener.endFunctionBody(1, begin, token);
+  // TODO(ngeoffray): expect a ';'.
+  return token.next;
+}
+
+SourceString checkForNativeClass(ElementListener listener) {
+  SourceString nativeName;
+  Node node = listener.nodes.head;
+  if (node != null
+      && node.asIdentifier() != null
+      && node.asIdentifier().source.stringValue == 'native') {
+    nativeName = node.asIdentifier().token.next.value;
+    listener.popNode();
+  }
+  return nativeName;
+}
+
+bool isOverriddenMethod(FunctionElement element,
+                        ClassElement cls,
+                        NativeEmitter nativeEmitter) {
+  List<ClassElement> subtypes = nativeEmitter.subtypes[cls];
+  if (subtypes == null) return false;
+  for (ClassElement subtype in subtypes) {
+    if (subtype.lookupLocalMember(element.name) != null) return true;
+  }
+  return false;
+}
+
+void handleSsaNative(SsaBuilder builder, Send node) {
+  // Register NoSuchMethodException and captureStackTrace in the compiler
+  // because the dynamic dispatch for native classes may use them.
+  Compiler compiler = builder.compiler;
+  ClassElement cls = compiler.coreLibrary.find(
+      Compiler.NO_SUCH_METHOD_EXCEPTION);
+  cls.ensureResolved(compiler);
+  compiler.addToWorkList(cls.lookupConstructor(cls.name));
+  compiler.registerStaticUse(
+      compiler.findHelper(new SourceString('captureStackTrace')));
+
+  FunctionElement element = builder.work.element;
+  element.setNative();
+  NativeEmitter nativeEmitter = compiler.emitter.nativeEmitter;
+  // If what we're compiling is a getter named 'typeName' and the native
+  // class is named 'DOMType', we generate a call to the typeNameOf
+  // function attached on the isolate.
+  // The DOM classes assume that their 'typeName' property, which is
+  // not a JS property on the DOM types, returns the type name.
+  if (element.name == const SourceString('typeName')
+      && element.isGetter()
+      && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') {
+    DartString jsCode = new DartString.literal(
+        '${nativeEmitter.typeNameOfName}(#)');
+    List<HInstruction> inputs =
+        <HInstruction>[builder.localsHandler.readThis()];
+    builder.push(new HForeign(
+        jsCode, const LiteralDartString('String'), inputs));
+    return;
+  }
+
+  HInstruction convertDartClosure(Element parameter) {
+    HInstruction local = builder.localsHandler.readLocal(parameter);
+    // TODO(ngeoffray): by better analyzing the function type and
+    // its formal parameters, we could just pass, eg closure.$call$0.
+    builder.push(new HStatic(builder.interceptors.getClosureConverter()));
+    List<HInstruction> callInputs = <HInstruction>[builder.pop(), local];
+    HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs);
+    builder.add(closure);
+    return closure;
+  }
+
+  FunctionParameters parameters = element.computeParameters(builder.compiler);
+  if (node.arguments.isEmpty()) {
+    List<String> arguments = <String>[];
+    List<HInstruction> inputs = <HInstruction>[];
+    String receiver = '';
+    if (element.isInstanceMember()) {
+      receiver = '#.';
+      inputs.add(builder.localsHandler.readThis());
+    }
+    parameters.forEachParameter((Element parameter) {
+      Type type = parameter.computeType(compiler);
+      HInstruction input = builder.localsHandler.readLocal(parameter);
+      if (type is FunctionType) input = convertDartClosure(parameter);
+      inputs.add(input);
+      arguments.add('#');
+    });
+    String foreignParameters = Strings.join(arguments, ',');
+
+    String dartMethodName;
+    String nativeMethodName = element.name.slowToString();
+    String nativeMethodCall;
+
+    if (element.kind == ElementKind.FUNCTION) {
+      dartMethodName = builder.compiler.namer.instanceMethodName(
+          element.getLibrary(), element.name, parameters.parameterCount);
+      nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)';
+    } else if (element.kind == ElementKind.GETTER) {
+      dartMethodName = builder.compiler.namer.getterName(
+          element.getLibrary(), element.name);
+      nativeMethodCall = '$receiver$nativeMethodName';
+    } else if (element.kind == ElementKind.SETTER) {
+      dartMethodName = builder.compiler.namer.setterName(
+          element.getLibrary(), element.name);
+      nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters';
+    } else {
+      builder.compiler.internalError('unexpected kind: "${element.kind}"',
+                                     element: element);
+    }
+
+    HInstruction thenInstruction;
+    void visitThen() {
+      DartString jsCode = new DartString.literal(nativeMethodCall);
+      thenInstruction =
+          new HForeign(jsCode, const LiteralDartString('Object'), inputs);
+      builder.add(thenInstruction);
+    }
+
+    bool isNativeLiteral = false;
+    bool isOverridden = false;
+    NativeEmitter nativeEmitter = builder.compiler.emitter.nativeEmitter;
+    if (element.enclosingElement.kind == ElementKind.CLASS) {
+      ClassElement classElement = element.enclosingElement;
+      String nativeName = classElement.nativeName.slowToString();
+      isNativeLiteral = nativeEmitter.isNativeLiteral(nativeName);
+      isOverridden = isOverriddenMethod(element, classElement, nativeEmitter);
+    }
+    if (!element.isInstanceMember() || isNativeLiteral || !isOverridden) {
+      // We generate a direct call to the native method.
+      visitThen();
+      builder.stack.add(thenInstruction);
+    } else {
+      // Record that this method is overridden. In case of optional
+      // arguments, the emitter will generate stubs to handle them,
+      // and needs to know if the method is overridden.
+      nativeEmitter.overriddenMethods.add(element);
+
+      // If the method is an instance method that is overridden, we
+      // generate the following code:
+      // function(params) {
+      //   return Object.getPrototypeOf(this).hasOwnProperty(dartMethodName))
+      //      ? this.methodName(params)
+      //      : Object.prototype.methodName.call(this, params);
+      // }
+      //
+      // The property check at the beginning is to make sure we won't
+      // call the method from the super class, in case the prototype of
+      // 'this' does not have the method yet.
+      HInstruction elseInstruction;
+      void visitElse() {
+        String params = arguments.isEmpty() ? '' : ', $foreignParameters';
+        DartString jsCode = new DartString.literal(
+            'Object.prototype.$dartMethodName.call(#$params)');
+        elseInstruction =
+            new HForeign(jsCode, const LiteralDartString('Object'), inputs);
+        builder.add(elseInstruction);
+      }
+
+      HConstant constant = builder.graph.addConstantString(
+          new DartString.literal('$dartMethodName'));
+      DartString jsCode = new DartString.literal(
+          'Object.getPrototypeOf(#).hasOwnProperty(#)');
+      builder.push(new HForeign(
+          jsCode, const LiteralDartString('Object'),
+          <HInstruction>[builder.localsHandler.readThis(), constant]));
+
+      builder.handleIf(visitThen, visitElse);
+
+      HPhi phi = new HPhi.manyInputs(
+          null, <HInstruction>[thenInstruction, elseInstruction]);
+      builder.current.addPhi(phi);
+      builder.stack.add(phi);
+    }
+
+  } else if (!node.arguments.tail.isEmpty()) {
+    builder.compiler.cancel('More than one argument to native');
+  } else {
+    // This is JS code written in a Dart file with the construct
+    // native """ ... """;. It does not work well with mangling,
+    // but there should currently be no clash between leg mangling
+    // and the library where this construct is being used. This
+    // mangling problem will go away once we switch these libraries
+    // to use Leg's 'JS' function.
+    parameters.forEachParameter((Element parameter) {
+      Type type = parameter.computeType(compiler);
+      if (type is FunctionType) {
+        HInstruction jsClosure = convertDartClosure(parameter);
+        // Because the JS code references the argument name directly,
+        // we must keep the name and assign the JS closure to it.
+        builder.add(new HForeign(
+            new DartString.literal('${parameter.name.slowToString()} = #'),
+            const LiteralDartString('void'),
+            <HInstruction>[jsClosure]));
+      }
+    });
+    LiteralString jsCode = node.arguments.head;
+    builder.push(new HForeign(jsCode.dartString,
+                              const LiteralDartString('Object'),
+                              <HInstruction>[]));
+  }
+}
diff --git a/lib/compiler/implementation/operations.dart b/lib/compiler/implementation/operations.dart
new file mode 100644
index 0000000..ff66987
--- /dev/null
+++ b/lib/compiler/implementation/operations.dart
@@ -0,0 +1,284 @@
+// Copyright (c) 2012, 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.
+
+interface Operation {
+  
+}
+
+interface UnaryOperation extends Operation {
+  /** Returns [:null:] if it was unable to fold the operation. */
+  Constant fold(Constant constant);
+}
+
+class BitNotOperation implements UnaryOperation {
+  const BitNotOperation();
+  Constant fold(Constant constant) {
+    if (constant.isInt()) {
+      IntConstant intConstant = constant;
+      return new IntConstant(~intConstant.value);
+    }
+    return null;
+  }
+}
+
+class NegateOperation implements UnaryOperation {
+  const NegateOperation();
+  Constant fold(Constant constant) {
+    if (constant.isInt()) {
+      IntConstant intConstant = constant;
+      return new IntConstant(-intConstant.value);
+    }
+    if (constant.isDouble()) {
+      DoubleConstant doubleConstant = constant;
+      return new DoubleConstant(-doubleConstant.value);
+    }
+    return null;
+  }
+}
+
+class NotOperation implements UnaryOperation {
+  const NotOperation();
+  Constant fold(Constant constant) {
+    if (constant.isBool()) {
+      BoolConstant boolConstant = constant;
+      return boolConstant.negate();
+    }
+    return null;
+  }
+}
+
+interface BinaryOperation extends Operation {
+  /** Returns [:null:] if it was unable to fold the operation. */
+  Constant fold(Constant left, Constant right);
+}
+
+/**
+ * Operations that only work if both arguments are integers.
+ */
+class BinaryIntOperation implements BinaryOperation {
+  const BinaryIntOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isInt() && right.isInt()) {
+      IntConstant leftInt = left;
+      IntConstant rightInt = right;
+      int resultValue = foldInts(leftInt.value, rightInt.value);
+      if (resultValue === null) return null;
+      return new IntConstant(resultValue);
+    }
+    return null;
+  }
+
+  abstract int foldInts(int left, int right); 
+}
+
+class BitOrOperation extends BinaryIntOperation {
+  const BitOrOperation();
+  int foldInts(int left, int right)  => left | right;
+}
+
+class BitAndOperation extends BinaryIntOperation {
+  const BitAndOperation();
+  int foldInts(int left, int right) => left & right;
+}
+
+class BitXorOperation extends BinaryIntOperation {
+  const BitXorOperation();
+  int foldInts(int left, int right) => left ^ right;
+}
+
+class ShiftLeftOperation extends BinaryIntOperation {
+  const ShiftLeftOperation();
+  int foldInts(int left, int right) {
+    // TODO(floitsch): find a better way to guard against excessive shifts to
+    // the left.
+    if (right > 100 || right < 0) return null;
+    return left << right;
+  }
+}
+
+class ShiftRightOperation extends BinaryIntOperation {
+  const ShiftRightOperation();
+  int foldInts(int left, int right) {
+    if (right < 0) return null;
+    return left >> right;
+  }
+}
+
+class BinaryBoolOperation implements BinaryOperation {
+  const BinaryBoolOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isBool() && right.isBool()) {
+      BoolConstant leftBool = left;
+      BoolConstant rightBool = right;
+      bool resultValue = foldBools(leftBool.value, rightBool.value);
+      return new BoolConstant(resultValue);
+    }
+    return null;
+  }
+
+  abstract bool foldBools(bool left, bool right);
+}
+
+class BooleanAnd extends BinaryBoolOperation {
+  const BooleanAnd();
+  bool foldBools(bool left, bool right) => left && right;
+}
+
+class BooleanOr extends BinaryBoolOperation {
+  const BooleanOr();
+  bool foldBools(bool left, bool right) => left || right;
+}
+
+class ArithmeticNumOperation implements BinaryOperation {
+  const ArithmeticNumOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isNum() && right.isNum()) {
+      NumConstant leftNum = left;
+      NumConstant rightNum = right;
+      num foldedValue;
+      if (left.isInt() && right.isInt()) {
+        foldedValue = foldInts(leftNum.value, rightNum.value);
+      } else {
+        foldedValue = foldNums(leftNum.value, rightNum.value);
+      }
+      // A division by 0 means that we might not have a folded value.
+      if (foldedValue === null) return null;
+      if (left.isInt() && right.isInt() && !isDivide()) {
+        assert(foldedValue is int);
+        return new IntConstant(foldedValue);
+      } else {
+        return new DoubleConstant(foldedValue);
+      }
+    }
+  }
+
+  bool isDivide() => false;
+  num foldInts(int left, int right) => foldNums(left, right);
+  abstract num foldNums(num left, num right);
+}
+
+class SubtractOperation extends ArithmeticNumOperation {
+  const SubtractOperation();
+  num foldNums(num left, num right) => left - right;
+}
+
+class MultiplyOperation extends ArithmeticNumOperation {
+  const MultiplyOperation();
+  num foldNums(num left, num right) => left * right;
+}
+
+class ModuloOperation extends ArithmeticNumOperation {
+  const ModuloOperation();
+  int foldInts(int left, int right) {
+    if (right == 0) return null;
+    return left % right;
+  }
+  num foldNums(num left, num right) => left % right;
+}
+
+class TruncatingDivideOperation extends ArithmeticNumOperation {
+  const TruncatingDivideOperation();
+  int foldInts(int left, int right) {
+    if (right == 0) return null;
+    return left ~/ right;
+  }
+  num foldNums(num left, num right) => left ~/ right;
+}
+
+class DivideOperation extends ArithmeticNumOperation {
+  const DivideOperation();
+  num foldNums(num left, num right) => left / right;
+  bool isDivide() => true;
+}
+
+class AddOperation implements BinaryOperation {
+  const AddOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isInt() && right.isInt()) {
+      IntConstant leftInt = left;
+      IntConstant rightInt = right;
+      return new IntConstant(leftInt.value + rightInt.value);
+    } else if (left.isNum() && right.isNum()) {
+      NumConstant leftNum = left;
+      NumConstant rightNum = right;
+      return new DoubleConstant(leftNum.value + rightNum.value);
+    } else if (left.isString() && !right.isObject()) {
+      PrimitiveConstant primitiveRight = right;
+      DartString rightDartString = primitiveRight.toDartString();
+      StringConstant leftString = left;
+      if (rightDartString.isEmpty()) {
+        return left;
+      } else if (leftString.value.isEmpty()) {
+        return new StringConstant(rightDartString);
+      } else {
+        DartString concatenated =
+            new ConsDartString(leftString.value, rightDartString);
+        return new StringConstant(concatenated);        
+      }
+    } else {
+      return null;
+    }
+  }
+}
+
+class RelationalNumOperation implements BinaryOperation {
+  const RelationalNumOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isNum() && right.isNum()) {
+      NumConstant leftNum = left;
+      NumConstant rightNum = right;
+      bool foldedValue = foldNums(leftNum.value, rightNum.value);
+      assert(foldedValue != null);
+      return new BoolConstant(foldedValue);
+    }
+  }
+
+  abstract bool foldNums(num left, num right);
+}
+
+class LessOperation extends RelationalNumOperation {
+  const LessOperation();
+  bool foldNums(num left, num right) => left < right;
+}
+
+class LessEqualOperation extends RelationalNumOperation {
+  const LessEqualOperation();
+  bool foldNums(num left, num right) => left <= right;
+}
+
+class GreaterOperation extends RelationalNumOperation {
+  const GreaterOperation();
+  bool foldNums(num left, num right) => left > right;
+}
+
+class GreaterEqualOperation extends RelationalNumOperation {
+  const GreaterEqualOperation();
+  bool foldNums(num left, num right) => left >= right;
+}
+
+class EqualsOperation implements BinaryOperation {
+  const EqualsOperation();
+  Constant fold(Constant left, Constant right) {
+    if (left.isNum() && right.isNum()) {
+      // Numbers need to be treated specially because: NaN != NaN, -0.0 == 0.0,
+      // and 1 == 1.0.
+      NumConstant leftNum = left;
+      NumConstant rightNum = right;
+      return new BoolConstant(leftNum.value == rightNum.value);
+    }
+    if (left.isConstructedObject()) {
+      // Unless we know that the user-defined object does not implement the
+      // equality operator we cannot fold here.
+      return null;
+    }
+    return new BoolConstant(left == right);
+  }
+}
+
+class IdentityOperation implements BinaryOperation {
+  const IdentityOperation();
+  Constant fold(Constant left, Constant right) {
+    return new BoolConstant(left == right);
+  }
+}
diff --git a/lib/compiler/implementation/resolver.dart b/lib/compiler/implementation/resolver.dart
new file mode 100644
index 0000000..d50efa9
--- /dev/null
+++ b/lib/compiler/implementation/resolver.dart
@@ -0,0 +1,1800 @@
+// Copyright (c) 2012, 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.
+
+interface TreeElements {
+  Element operator[](Node node);
+  Selector getSelector(Send send);
+}
+
+class TreeElementMapping implements TreeElements {
+  Map<Node, Element> map;
+  Map<Send, Selector> selectors;
+  TreeElementMapping()
+    : map = new LinkedHashMap<Node, Element>(),
+      selectors = new LinkedHashMap<Send, Selector>();
+
+  operator []=(Node node, Element element) => map[node] = element;
+  operator [](Node node) => map[node];
+  void remove(Node node) { map.remove(node); }
+
+  void setSelector(Send send, Selector selector) {
+    selectors[send] = selector;
+  }
+
+  Selector getSelector(Send send) => selectors[send];
+}
+
+class ResolverTask extends CompilerTask {
+  Queue<ClassElement> toResolve;
+
+  // Caches the elements of analyzed constructors to make them available
+  // for inlining in later tasks.
+  Map<FunctionElement, TreeElements> constructorElements;
+
+  ResolverTask(Compiler compiler)
+    : super(compiler), toResolve = new Queue<ClassElement>(),
+      constructorElements = new Map<FunctionElement, TreeElements>();
+
+  String get name() => 'Resolver';
+
+  TreeElements resolve(Element element) {
+    return measure(() {
+      switch (element.kind) {
+        case ElementKind.GENERATIVE_CONSTRUCTOR:
+        case ElementKind.FUNCTION:
+        case ElementKind.GETTER:
+        case ElementKind.SETTER:
+          return resolveMethodElement(element);
+
+        case ElementKind.FIELD:
+          return resolveField(element);
+
+        case ElementKind.PARAMETER:
+        case ElementKind.FIELD_PARAMETER:
+          return resolveParameter(element);
+
+        default:
+          compiler.unimplemented(
+              "resolver", node: element.parseNode(compiler));
+      }
+    });
+  }
+
+  SourceString getConstructorName(Send node) {
+    if (node.receiver !== null) {
+      return node.selector.asIdentifier().source;
+    } else {
+      return const SourceString('');
+    }
+  }
+
+  FunctionElement lookupConstructor(ClassElement classElement, Send send,
+                                    [noConstructor(Element)]) {
+    final SourceString constructorName = getConstructorName(send);
+    final SourceString className = classElement.name;
+    return classElement.lookupConstructor(className,
+                                          constructorName,
+                                          noConstructor);
+  }
+
+  FunctionElement resolveConstructorRedirection(FunctionElement constructor) {
+    FunctionExpression node = constructor.parseNode(compiler);
+    // A synthetic constructor does not have a node.
+    if (node === null) return null;
+    if (node.initializers === null) return null;
+    Link<Node> initializers = node.initializers.nodes;
+    if (!initializers.isEmpty() &&
+        Initializers.isConstructorRedirect(initializers.head)) {
+      return lookupConstructor(constructor.enclosingElement, initializers.head);
+    }
+    return null;
+  }
+
+  void resolveRedirectingConstructor(InitializerResolver resolver,
+                                     Node node,
+                                     FunctionElement constructor,
+                                     FunctionElement redirection) {
+    Set<FunctionElement> seen = new Set<FunctionElement>();
+    seen.add(constructor);
+    while (redirection !== null) {
+      if (seen.contains(redirection)) {
+        resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
+        return;
+      }
+      seen.add(redirection);
+      redirection = resolveConstructorRedirection(redirection);
+    }
+  }
+
+  TreeElements resolveMethodElement(FunctionElement element) {
+    return compiler.withCurrentElement(element, () {
+      bool isConstructor = element.kind === ElementKind.GENERATIVE_CONSTRUCTOR;
+      if (constructorElements.containsKey(element)) {
+        assert(isConstructor);
+        TreeElements elements = constructorElements[element];
+        if (elements !== null) return elements;
+      }
+      FunctionExpression tree = element.parseNode(compiler);
+      if (isConstructor) {
+        resolveConstructorImplementation(element, tree);
+      }
+      ResolverVisitor visitor = new ResolverVisitor(compiler, element);
+      visitor.useElement(tree, element);
+      visitor.setupFunction(tree, element);
+
+      if (tree.initializers != null) {
+        if (!isConstructor) {
+          error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
+        }
+        InitializerResolver resolver = new InitializerResolver(visitor);
+        FunctionElement redirection =
+            resolver.resolveInitializers(element, tree);
+        if (redirection !== null) {
+          resolveRedirectingConstructor(resolver, tree, element, redirection);
+        }
+      }
+      visitor.visit(tree.body);
+
+      // Resolve the type annotations encountered in the method.
+      while (!toResolve.isEmpty()) {
+        ClassElement classElement = toResolve.removeFirst();
+        classElement.ensureResolved(compiler);
+      }
+      if (isConstructor) {
+        constructorElements[element] = visitor.mapping;
+      }
+      return visitor.mapping;
+    });
+  }
+
+  void resolveConstructorImplementation(FunctionElement constructor,
+                                        FunctionExpression node) {
+    assert(constructor.defaultImplementation === constructor);
+    ClassElement intrface = constructor.enclosingElement;
+    if (!intrface.isInterface()) return;
+    Type defaultType = intrface.defaultClass;
+    if (defaultType === null) {
+      error(node, MessageKind.NO_DEFAULT_CLASS, [intrface.name]);
+    }
+    ClassElement defaultClass = defaultType.element;
+    defaultClass.ensureResolved(compiler);
+    if (defaultClass.isInterface()) {
+      error(node, MessageKind.CANNOT_INSTANTIATE_INTERFACE,
+            [defaultClass.name]);
+    }
+    // We have now established the following:
+    // [intrface] is an interface, let's say "MyInterface".
+    // [defaultClass] is a class, let's say "MyClass".
+
+    // First look up the constructor named "MyInterface.name".
+    constructor.defaultImplementation =
+      defaultClass.lookupConstructor(constructor.name);
+
+    // If that fails, try looking up "MyClass.name".
+    if (constructor.defaultImplementation === null) {
+      SourceString name =
+          new SourceString(constructor.name.slowToString().replaceFirst(
+              intrface.name.slowToString(),
+              defaultClass.name.slowToString()));
+      constructor.defaultImplementation = defaultClass.lookupConstructor(name);
+
+      if (constructor.defaultImplementation === null) {
+        // We failed find a constrcutor named either
+        // "MyInterface.name" or "MyClass.name".
+        error(node, MessageKind.CANNOT_FIND_CONSTRUCTOR2,
+              [constructor.name, name]);
+      }
+    }
+  }
+
+  TreeElements resolveField(Element element) {
+    Node tree = element.parseNode(compiler);
+    ResolverVisitor visitor = new ResolverVisitor(compiler, element);
+    initializerDo(tree, visitor.visit);
+    return visitor.mapping;
+  }
+
+  TreeElements resolveParameter(Element element) {
+    Node tree = element.parseNode(compiler);
+    ResolverVisitor visitor =
+        new ResolverVisitor(compiler, element.enclosingElement);
+    initializerDo(tree, visitor.visit);
+    return visitor.mapping;
+  }
+
+  Type resolveType(ClassElement element) {
+    if (element.isResolved) return element.type;
+    return measure(() {
+      ClassNode tree = element.parseNode(compiler);
+      ClassResolverVisitor visitor =
+        new ClassResolverVisitor(compiler, element.getLibrary(), element);
+      visitor.visit(tree);
+      element.isResolved = true;
+      return element.type;
+    });
+  }
+
+  FunctionParameters resolveSignature(FunctionElement element) {
+    return measure(() => SignatureResolver.analyze(compiler, element));
+  }
+
+  error(Node node, MessageKind kind, [arguments = const []]) {
+    ResolutionError message = new ResolutionError(kind, arguments);
+    compiler.reportError(node, message);
+  }
+}
+
+class InitializerResolver {
+  final ResolverVisitor visitor;
+  final Map<SourceString, Node> initialized;
+  Link<Node> initializers;
+  bool hasSuper;
+
+  InitializerResolver(this.visitor)
+    : initialized = new Map<SourceString, Node>(), hasSuper = false;
+
+  error(Node node, MessageKind kind, [arguments = const []]) {
+    visitor.error(node, kind, arguments);
+  }
+
+  warning(Node node, MessageKind kind, [arguments = const []]) {
+    visitor.warning(node, kind, arguments);
+  }
+
+  bool isFieldInitializer(SendSet node) {
+    if (node.selector.asIdentifier() == null) return false;
+    if (node.receiver == null) return true;
+    if (node.receiver.asIdentifier() == null) return false;
+    return node.receiver.asIdentifier().isThis();
+  }
+
+  void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
+    // init is of the form [this.]field = value.
+    final Node selector = init.selector;
+    final SourceString name = selector.asIdentifier().source;
+    // Lookup target field.
+    Element target;
+    if (isFieldInitializer(init)) {
+      final ClassElement classElement = constructor.enclosingElement;
+      target = classElement.lookupLocalMember(name);
+      if (target === null) {
+        error(selector, MessageKind.CANNOT_RESOLVE, [name]);
+      } else if (target.kind != ElementKind.FIELD) {
+        error(selector, MessageKind.NOT_A_FIELD, [name]);
+      } else if (!target.isInstanceMember()) {
+        error(selector, MessageKind.INIT_STATIC_FIELD, [name]);
+      }
+    } else {
+      error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER);
+    }
+    visitor.useElement(init, target);
+    // Check for duplicate initializers.
+    if (initialized.containsKey(name)) {
+      error(init, MessageKind.DUPLICATE_INITIALIZER, [name]);
+      warning(initialized[name], MessageKind.ALREADY_INITIALIZED, [name]);
+    }
+    initialized[name] = init;
+    // Resolve initializing value.
+    visitor.visitInStaticContext(init.arguments.head);
+  }
+
+  Element resolveSuperOrThis(FunctionElement constructor,
+                             FunctionExpression functionNode,
+                             Send call) {
+    noConstructor(e) {
+      if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]);
+    }
+
+    ClassElement lookupTarget = constructor.enclosingElement;
+    bool validTarget = true;
+    FunctionElement result;
+    if (Initializers.isSuperConstructorCall(call)) {
+      // Check for invalid initializers.
+      if (hasSuper) {
+        error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
+      }
+      hasSuper = true;
+      // Calculate correct lookup target and constructor name.
+      if (lookupTarget.name == Types.OBJECT) {
+        error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
+      } else {
+        lookupTarget = lookupTarget.supertype.element;
+      }
+    } else if (Initializers.isConstructorRedirect(call)) {
+      // Check that there is no body (Language specification 7.5.1).
+      if (functionNode.hasBody()) {
+        error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY);
+      }
+      // Check that there are no other initializers.
+      if (!initializers.tail.isEmpty()) {
+        error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER);
+      }
+    } else {
+      visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
+      validTarget = false;
+    }
+
+    if (validTarget) {
+      // Resolve the arguments, and make sure the call gets a selector
+      // by calling handleArguments.
+      visitor.inStaticContext( () => visitor.handleArguments(call) );
+      // Lookup constructor and try to match it to the selector.
+      ResolverTask resolver = visitor.compiler.resolver;
+      result = resolver.lookupConstructor(lookupTarget, call);
+      if (result === null) {
+        SourceString constructorName = resolver.getConstructorName(call);
+        String className = lookupTarget.name.slowToString();
+        String name = (constructorName === const SourceString(''))
+                          ? className
+                          : "$className.${constructorName.slowToString()}";
+        error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]);
+      } else {
+        final Compiler compiler = visitor.compiler;
+        Selector selector = visitor.mapping.getSelector(call);
+        FunctionParameters parameters = result.computeParameters(compiler);
+        // TODO(karlklose): support optional arguments.
+        if (!selector.applies(parameters)) {
+          error(call, MessageKind.NO_MATCHING_CONSTRUCTOR);
+        }
+      }
+      visitor.useElement(call, result);
+    }
+    return result;
+  }
+
+  FunctionElement resolveRedirection(FunctionElement constructor,
+                                     FunctionExpression functionNode) {
+    if (functionNode.initializers === null) return null;
+    Link<Node> link = functionNode.initializers.nodes;
+    if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) {
+      return resolveSuperOrThis(constructor, functionNode, link.head);
+    }
+    return null;
+  }
+
+  /**
+   * Resolve all initializers of this constructor. In the case of a redirecting
+   * constructor, the resolved constructor's function element is returned.
+   */
+  FunctionElement resolveInitializers(FunctionElement constructor,
+                                      FunctionExpression functionNode) {
+    if (functionNode.initializers === null) return null;
+    initializers = functionNode.initializers.nodes;
+    FunctionElement result;
+    for (Link<Node> link = initializers;
+         !link.isEmpty();
+         link = link.tail) {
+      if (link.head.asSendSet() != null) {
+        final SendSet init = link.head.asSendSet();
+        resolveFieldInitializer(constructor, init);
+      } else if (link.head.asSend() !== null) {
+        final Send call = link.head.asSend();
+        result = resolveSuperOrThis(constructor, functionNode, call);
+      } else {
+        error(link.head, MessageKind.INVALID_INITIALIZER);
+      }
+    }
+    return result;
+  }
+}
+
+class CommonResolverVisitor<R> extends AbstractVisitor<R> {
+  final Compiler compiler;
+
+  CommonResolverVisitor(Compiler this.compiler);
+
+  R visitNode(Node node) {
+    cancel(node, 'internal error');
+  }
+
+  R visitEmptyStatement(Node node) => null;
+
+  /** Convenience method for visiting nodes that may be null. */
+  R visit(Node node) => (node == null) ? null : node.accept(this);
+
+  void error(Node node, MessageKind kind, [arguments = const []]) {
+    ResolutionError message  = new ResolutionError(kind, arguments);
+    compiler.reportError(node, message);
+  }
+
+  void warning(Node node, MessageKind kind, [arguments = const []]) {
+    ResolutionWarning message  = new ResolutionWarning(kind, arguments);
+    compiler.reportWarning(node, message);
+  }
+
+  void cancel(Node node, String message) {
+    compiler.cancel(message, node: node);
+  }
+
+  void internalError(Node node, String message) {
+    compiler.internalError(message, node: node);
+  }
+
+  void unimplemented(Node node, String message) {
+    compiler.unimplemented(message, node: node);
+  }
+}
+
+interface LabelScope {
+  LabelScope get outer();
+  LabelElement lookup(String label);
+}
+
+class LabeledStatementLabelScope implements LabelScope {
+  final LabelScope outer;
+  final LabelElement label;
+  LabeledStatementLabelScope(this.outer, this.label);
+  LabelElement lookup(String labelName) {
+    if (this.label.labelName == labelName) return label;
+    return outer.lookup(labelName);
+  }
+}
+
+class SwitchLabelScope implements LabelScope {
+  final LabelScope outer;
+  final Map<String, LabelElement> caseLabels;
+
+  SwitchLabelScope(this.outer, this.caseLabels);
+
+  LabelElement lookup(String labelName) {
+    LabelElement result = caseLabels[labelName];
+    if (result !== null) return result;
+    return outer.lookup(labelName);
+  }
+}
+
+class EmptyLabelScope implements LabelScope {
+  const EmptyLabelScope();
+  LabelElement lookup(String label) => null;
+  LabelScope get outer() {
+    throw 'internal error: empty label scope has no outer';
+  }
+}
+
+class StatementScope {
+  LabelScope labels;
+  Link<TargetElement> breakTargetStack;
+  Link<TargetElement> continueTargetStack;
+  // Used to provide different numbers to statements if one is inside the other.
+  // Can be used to make otherwise duplicate labels unique.
+  int nestingLevel = 0;
+
+  StatementScope()
+      : labels = const EmptyLabelScope(),
+        breakTargetStack = const EmptyLink<TargetElement>(),
+        continueTargetStack = const EmptyLink<TargetElement>();
+
+  LabelElement lookupLabel(String label) {
+    return labels.lookup(label);
+  }
+  TargetElement currentBreakTarget() =>
+    breakTargetStack.isEmpty() ? null : breakTargetStack.head;
+
+  TargetElement currentContinueTarget() =>
+    continueTargetStack.isEmpty() ? null : continueTargetStack.head;
+
+  void enterLabelScope(LabelElement element) {
+    labels = new LabeledStatementLabelScope(labels, element);
+    nestingLevel++;
+  }
+
+  void exitLabelScope() {
+    nestingLevel--;
+    labels = labels.outer;
+  }
+
+  void enterLoop(TargetElement element) {
+    breakTargetStack = breakTargetStack.prepend(element);
+    continueTargetStack = continueTargetStack.prepend(element);
+    nestingLevel++;
+  }
+
+  void exitLoop() {
+    nestingLevel--;
+    breakTargetStack = breakTargetStack.tail;
+    continueTargetStack = continueTargetStack.tail;
+  }
+
+  void enterSwitch(TargetElement breakElement,
+                   Map<String, LabelElement> continueElements) {
+    breakTargetStack = breakTargetStack.prepend(breakElement);
+    labels = new SwitchLabelScope(labels, continueElements);
+    nestingLevel++;
+  }
+
+  void exitSwitch() {
+    nestingLevel--;
+    breakTargetStack = breakTargetStack.tail;
+    labels = labels.outer;
+  }
+}
+
+class ResolverVisitor extends CommonResolverVisitor<Element> {
+  final TreeElementMapping mapping;
+  final Element enclosingElement;
+  bool inInstanceContext;
+  Scope context;
+  ClassElement currentClass;
+  bool typeRequired = false;
+  StatementScope statementScope;
+  int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION;
+
+  ResolverVisitor(Compiler compiler, Element element)
+    : this.mapping  = new TreeElementMapping(),
+      this.enclosingElement = element,
+      inInstanceContext = element.isInstanceMember()
+          || element.isGenerativeConstructor(),
+      this.context  = element.isMember()
+        ? new ClassScope(element.enclosingElement, element.getLibrary())
+        : new TopScope(element.getLibrary()),
+      this.currentClass = element.isMember() ? element.enclosingElement : null,
+      this.statementScope = new StatementScope(),
+      super(compiler);
+
+  Element lookup(Node node, SourceString name) {
+    Element result = context.lookup(name);
+    if (!inInstanceContext && result != null && result.isInstanceMember()) {
+      error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]);
+    }
+    return result;
+  }
+
+  // Create, or reuse an already created, statement element for a statement.
+  TargetElement getOrCreateTargetElement(Node statement) {
+    TargetElement element = mapping[statement];
+    if (element === null) {
+      element = new TargetElement(statement,
+                                     statementScope.nestingLevel,
+                                     enclosingElement);
+      mapping[statement] = element;
+    }
+    return element;
+  }
+
+  inStaticContext(action()) {
+    bool wasInstanceContext = inInstanceContext;
+    inInstanceContext = false;
+    var result = action();
+    inInstanceContext = wasInstanceContext;
+    return result;
+  }
+
+  visitInStaticContext(Node node) {
+    inStaticContext(() => visit(node));
+  }
+
+  Element visitIdentifier(Identifier node) {
+    if (node.isThis()) {
+      if (!inInstanceContext) {
+        error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]);
+      }
+      return null;
+    } else if (node.isSuper()) {
+      if (!inInstanceContext) error(node, MessageKind.NO_SUPER_IN_STATIC);
+      if ((ElementCategory.SUPER & allowedCategory) == 0) {
+        error(node, MessageKind.INVALID_USE_OF_SUPER);
+      }
+      return null;
+    } else {
+      Element element = lookup(node, node.source);
+      if (element === null) {
+        if (!inInstanceContext) error(node, MessageKind.CANNOT_RESOLVE, [node]);
+      } else {
+        if ((element.kind.category & allowedCategory) == 0) {
+          // TODO(ahe): Improve error message. Need UX input.
+          error(node, MessageKind.GENERIC, ["is not an expression $element"]);
+        }
+      }
+      return useElement(node, element);
+    }
+  }
+
+  visitTypeAnnotation(TypeAnnotation node) {
+    Send send = node.typeName.asSend();
+    Element element;
+    if (send !== null) {
+      if (typeRequired) {
+        element = resolveSend(send);
+      } else {
+        // Not calling resolveSend as it will emit an error instead of
+        // a warning if the type is bogus.
+        // TODO(ahe): Change resolveSend so it can emit a warning when needed.
+        return null;
+      }
+    } else {
+      element = context.lookup(node.typeName.asIdentifier().source);
+    }
+    if (element === null) {
+      if (typeRequired) {
+        error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]);
+      } else {
+        warning(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]);
+      }
+    } else if (!element.impliesType()) {
+      if (typeRequired) {
+        error(node, MessageKind.NOT_A_TYPE, [node.typeName]);
+      } else {
+        warning(node, MessageKind.NOT_A_TYPE, [node.typeName]);
+      }
+    } else {
+      if (element.isClass()) {
+        // TODO(ngeoffray): Should we also resolve typedef?
+        ClassElement cls = element;
+        compiler.resolver.toResolve.add(element);
+      }
+      // TODO(ahe): This should be a Type.
+      useElement(node, element);
+    }
+    return element;
+  }
+
+  Element defineElement(Node node, Element element,
+                        [bool doAddToScope = true]) {
+    compiler.ensure(element !== null);
+    mapping[node] = element;
+    if (doAddToScope) {
+      Element existing = context.add(element);
+      if (existing != element) {
+        error(node, MessageKind.DUPLICATE_DEFINITION, [node]);
+      }
+    }
+    return element;
+  }
+
+  Element useElement(Node node, Element element) {
+    if (element === null) return null;
+    return mapping[node] = element;
+  }
+
+  void setupFunction(FunctionExpression node, FunctionElement function) {
+    context = new MethodScope(context, function);
+    // Put the parameters in scope.
+    FunctionParameters functionParameters =
+        function.computeParameters(compiler);
+    Link<Node> parameterNodes = node.parameters.nodes;
+    functionParameters.forEachParameter((Element element) {
+      if (element == functionParameters.optionalParameters.head) {
+        NodeList nodes = parameterNodes.head;
+        parameterNodes = nodes.nodes;
+      }
+      VariableDefinitions variableDefinitions = parameterNodes.head;
+      Node parameterNode = variableDefinitions.definitions.nodes.head;
+      initializerDo(parameterNode, (n) => n.accept(this));
+      // Field parameters (this.x) are not visible inside the constructor. The
+      // fields they reference are visible, but must be resolved independently.
+      if (element.kind == ElementKind.FIELD_PARAMETER) {
+        useElement(parameterNode, element);
+      } else {
+        defineElement(variableDefinitions.definitions.nodes.head, element);
+      }
+      parameterNodes = parameterNodes.tail;
+    });
+  }
+
+  Element visitClassNode(ClassNode node) {
+    cancel(node, "shouldn't be called");
+  }
+
+  visitIn(Node node, Scope scope) {
+    context = scope;
+    Element element = visit(node);
+    context = context.parent;
+    return element;
+  }
+
+  /**
+   * Introduces new default targets for break and continue
+   * before visiting the body of the loop
+   */
+  visitLoopBodyIn(Node loop, Node body, Scope scope) {
+    TargetElement element = getOrCreateTargetElement(loop);
+    statementScope.enterLoop(element);
+    visitIn(body, scope);
+    statementScope.exitLoop();
+    if (!element.isTarget) {
+      mapping.remove(loop);
+    }
+  }
+
+  visitBlock(Block node) {
+    visitIn(node.statements, new BlockScope(context));
+  }
+
+  visitDoWhile(DoWhile node) {
+    visitLoopBodyIn(node, node.body, new BlockScope(context));
+    visit(node.condition);
+  }
+
+  visitEmptyStatement(EmptyStatement node) { }
+
+  visitExpressionStatement(ExpressionStatement node) {
+    visit(node.expression);
+  }
+
+  visitFor(For node) {
+    Scope scope = new BlockScope(context);
+    visitIn(node.initializer, scope);
+    visitIn(node.condition, scope);
+    visitIn(node.update, scope);
+    visitLoopBodyIn(node, node.body, scope);
+  }
+
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    assert(node.function.name !== null);
+    visit(node.function);
+    FunctionElement functionElement = mapping[node.function];
+    // TODO(floitsch): this might lead to two errors complaining about
+    // shadowing.
+    defineElement(node, functionElement);
+  }
+
+  visitFunctionExpression(FunctionExpression node) {
+    visit(node.returnType);
+    SourceString name;
+    if (node.name === null) {
+      name = const SourceString("");
+    } else {
+      name = node.name.asIdentifier().source;
+    }
+    FunctionElement enclosing = new FunctionElement.node(
+        name, node, ElementKind.FUNCTION, new Modifiers.empty(),
+        context.element);
+    setupFunction(node, enclosing);
+    defineElement(node, enclosing, doAddToScope: node.name !== null);
+
+    // Run the body in a fresh statement scope.
+    StatementScope oldScope = statementScope;
+    statementScope = new StatementScope();
+    visit(node.body);
+    statementScope = oldScope;
+
+    context = context.parent;
+  }
+
+  visitIf(If node) {
+    visit(node.condition);
+    visit(node.thenPart);
+    visit(node.elsePart);
+  }
+
+  static bool isLogicalOperator(Identifier op) {
+    String str = op.source.stringValue;
+    return (str === '&&' || str == '||' || str == '!');
+  }
+
+  Element resolveSend(Send node) {
+    if (node.receiver === null) {
+      return node.selector.accept(this);
+    }
+    var oldCategory = allowedCategory;
+    allowedCategory |=
+      ElementCategory.CLASS | ElementCategory.PREFIX | ElementCategory.SUPER;
+    Element resolvedReceiver = visit(node.receiver);
+    allowedCategory = oldCategory;
+
+    Element target;
+    SourceString name = node.selector.asIdentifier().source;
+    if (name.stringValue === 'this') {
+      error(node.selector, MessageKind.GENERIC, ["expected an identifier"]);
+    } else if (node.isSuperCall) {
+      if (node.isOperator) {
+        if (isUserDefinableOperator(name.stringValue)) {
+          name = Elements.constructOperatorName(const SourceString('operator'),
+                                                name);
+        } else {
+          error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, [name]);
+        }
+      }
+      if (!inInstanceContext) {
+        error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, [name]);
+        return null;
+      }
+      if (currentClass.supertype === null) {
+        // This is just to guard against internal errors, so no need
+        // for a real error message.
+        error(node.receiver, MessageKind.GENERIC, ["Object has no superclass"]);
+      }
+      target = currentClass.lookupSuperMember(name);
+      // [target] may be null which means invoking noSuchMethod on
+      // super.
+    } else if (resolvedReceiver === null) {
+      return null;
+    } else if (resolvedReceiver.kind === ElementKind.CLASS) {
+      ClassElement receiverClass = resolvedReceiver;
+      target = receiverClass.ensureResolved(compiler).lookupLocalMember(name);
+      if (target === null) {
+        error(node, MessageKind.METHOD_NOT_FOUND, [receiverClass.name, name]);
+      } else if (target.isInstanceMember()) {
+        error(node, MessageKind.MEMBER_NOT_STATIC, [receiverClass.name, name]);
+      }
+    } else if (resolvedReceiver.kind === ElementKind.PREFIX) {
+      PrefixElement prefix = resolvedReceiver;
+      target = prefix.lookupLocalMember(name);
+      if (target == null) {
+        error(node, MessageKind.NO_SUCH_LIBRARY_MEMBER, [prefix.name, name]);
+      }
+    }
+    return target;
+  }
+
+  resolveTypeTest(Node argument) {
+    TypeAnnotation node = argument.asTypeAnnotation();
+    if (node == null) {
+      node = argument.asSend().receiver;
+    }
+    resolveTypeRequired(node);
+  }
+
+  void handleArguments(Send node) {
+    int count = 0;
+    List<SourceString> namedArguments = <SourceString>[];
+    bool seenNamedArgument = false;
+    for (Link<Node> link = node.argumentsNode.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      count++;
+      Expression argument = link.head;
+      visit(argument);
+      if (argument.asNamedArgument() != null) {
+        seenNamedArgument = true;
+        NamedArgument named = argument;
+        namedArguments.add(named.name.source);
+      } else if (seenNamedArgument) {
+        error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
+      }
+    }
+    mapping.setSelector(node, new Invocation(count, namedArguments));
+  }
+
+  visitSend(Send node) {
+    Element target = resolveSend(node);
+    if (node.isOperator) {
+      Operator op = node.selector.asOperator();
+      if (op.source.stringValue === 'is') {
+        resolveTypeTest(node.arguments.head);
+        assert(node.arguments.tail.isEmpty());
+        mapping.setSelector(node, Selector.BINARY_OPERATOR);
+      } else if (node.arguments.isEmpty()) {
+        assert(op.token.kind !== PLUS_TOKEN);
+        mapping.setSelector(node, Selector.UNARY_OPERATOR);
+      } else {
+        visit(node.argumentsNode);
+        mapping.setSelector(node, Selector.BINARY_OPERATOR);
+      }
+    } else if (node.isIndex) {
+      visit(node.argumentsNode);
+      assert(node.arguments.tail.isEmpty());
+      mapping.setSelector(node, Selector.INDEX);
+    } else if (node.isPropertyAccess) {
+      mapping.setSelector(node, Selector.GETTER);
+    } else {
+      handleArguments(node);
+    }
+    if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) {
+      AbstractFieldElement field = target;
+      target = field.getter;
+    }
+    // TODO(ngeoffray): Warn if target is null and the send is
+    // unqualified.
+    useElement(node, target);
+    if (node.isPropertyAccess) return target;
+  }
+
+  visitSendSet(SendSet node) {
+    Element target = resolveSend(node);
+    Element setter = null;
+    Element getter = null;
+    if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) {
+      AbstractFieldElement field = target;
+      setter = field.setter;
+      getter = field.getter;
+    } else {
+      setter = target;
+      getter = target;
+    }
+    // TODO(ngeoffray): Check if the target can be assigned.
+    Identifier op = node.assignmentOperator;
+    bool needsGetter = op.source.stringValue !== '=';
+    Selector selector;
+    if (needsGetter) {
+      if (node.isIndex) {
+        selector = Selector.INDEX_AND_INDEX_SET;
+      } else {
+        selector = Selector.GETTER_AND_SETTER;
+      }
+      useElement(node.selector, getter);
+    } else if (node.isIndex) {
+      selector = Selector.INDEX_SET;
+    } else {
+      selector = Selector.SETTER;
+    }
+    visit(node.argumentsNode);
+    mapping.setSelector(node, selector);
+    // TODO(ngeoffray): Warn if target is null and the send is
+    // unqualified.
+    return useElement(node, setter);
+  }
+
+  visitLiteralInt(LiteralInt node) {
+  }
+
+  visitLiteralDouble(LiteralDouble node) {
+  }
+
+  visitLiteralBool(LiteralBool node) {
+  }
+
+  visitLiteralString(LiteralString node) {
+  }
+
+  visitLiteralNull(LiteralNull node) {
+  }
+
+  visitStringJuxtaposition(StringJuxtaposition node) {
+    node.visitChildren(this);
+  }
+
+  visitNodeList(NodeList node) {
+    for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
+      visit(link.head);
+    }
+  }
+
+  visitOperator(Operator node) {
+    unimplemented(node, 'operator');
+  }
+
+  visitReturn(Return node) {
+    visit(node.expression);
+  }
+
+  visitThrow(Throw node) {
+    visit(node.expression);
+  }
+
+  visitVariableDefinitions(VariableDefinitions node) {
+    visit(node.type);
+    VariableDefinitionsVisitor visitor =
+        new VariableDefinitionsVisitor(compiler, node, this,
+                                       ElementKind.VARIABLE);
+    visitor.visit(node.definitions);
+  }
+
+  visitWhile(While node) {
+    visit(node.condition);
+    visitLoopBodyIn(node, node.body, new BlockScope(context));
+  }
+
+  visitParenthesizedExpression(ParenthesizedExpression node) {
+    visit(node.expression);
+  }
+
+  visitNewExpression(NewExpression node) {
+    Node selector = node.send.selector;
+
+    FunctionElement constructor = resolveConstructor(node);
+    handleArguments(node.send);
+    if (constructor === null) return null;
+    // TODO(karlklose): handle optional arguments.
+    if (node.send.argumentCount() != constructor.parameterCount(compiler)) {
+      // TODO(ngeoffray): resolution error with wrong number of
+      // parameters. We cannot do this rigth now because of the
+      // List constructor.
+    }
+    useElement(node.send, constructor);
+    return null;
+  }
+
+  FunctionElement resolveConstructor(NewExpression node) {
+    FunctionElement constructor =
+        node.accept(new ConstructorResolver(compiler, this));
+    if (constructor === null) {
+      Element resolved = resolveTypeRequired(node.send.selector);
+      if (resolved !== null && resolved.kind === ElementKind.TYPE_VARIABLE) {
+        error(node, WarningKind.TYPE_VARIABLE_AS_CONSTRUCTOR);
+        return null;
+      } else {
+        error(node.send, MessageKind.CANNOT_FIND_CONSTRUCTOR, [node.send]);
+      }
+    }
+    return constructor;
+  }
+
+  Element resolveTypeRequired(Node node) {
+    bool old = typeRequired;
+    typeRequired = true;
+    Element element = visit(node);
+    typeRequired = old;
+    return element;
+  }
+
+  visitModifiers(Modifiers node) {
+    // TODO(ngeoffray): Implement this.
+    unimplemented(node, 'modifiers');
+  }
+
+  visitLiteralList(LiteralList node) {
+    visit(node.elements);
+  }
+
+  visitConditional(Conditional node) {
+    node.visitChildren(this);
+  }
+
+  visitStringInterpolation(StringInterpolation node) {
+    node.visitChildren(this);
+  }
+
+  visitStringInterpolationPart(StringInterpolationPart node) {
+    node.visitChildren(this);
+  }
+
+  visitBreakStatement(BreakStatement node) {
+    TargetElement target;
+    if (node.target === null) {
+      target = statementScope.currentBreakTarget();
+      if (target === null) {
+        error(node, MessageKind.NO_BREAK_TARGET);
+        return;
+      }
+      target.isBreakTarget = true;
+    } else {
+      String labelName = node.target.source.slowToString();
+      LabelElement label = statementScope.lookupLabel(labelName);
+      if (label === null) {
+        error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
+        return;
+      }
+      target = label.target;
+      if (!target.statement.isValidBreakTarget()) {
+        error(node.target, MessageKind.INVALID_BREAK, [labelName]);
+        return;
+      }
+      label.setBreakTarget();
+      mapping[node.target] = label;
+    }
+    mapping[node] = target;
+  }
+
+  visitContinueStatement(ContinueStatement node) {
+    TargetElement target;
+    if (node.target === null) {
+      target = statementScope.currentContinueTarget();
+      if (target === null) {
+        error(node, MessageKind.NO_CONTINUE_TARGET);
+        return;
+      }
+      target.isContinueTarget = true;
+    } else {
+      String labelName = node.target.source.slowToString();
+      LabelElement label = statementScope.lookupLabel(labelName);
+      if (label === null) {
+        error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
+        return;
+      }
+      target = label.target;
+      if (!target.statement.isValidContinueTarget()) {
+        error(node.target, MessageKind.INVALID_CONTINUE, [labelName]);
+      }
+      label.setContinueTarget();
+    }
+    mapping[node] = target;
+  }
+
+  visitForInStatement(ForInStatement node) {
+    visit(node.expression);
+    Scope scope = new BlockScope(context);
+    Node declaration = node.declaredIdentifier;
+    visitIn(declaration, scope);
+    visitLoopBodyIn(node, node.body, scope);
+
+    // TODO(lrn): Also allow a single identifier.
+    if ((declaration is !Send || declaration.asSend().selector is !Identifier)
+        && (declaration is !VariableDefinitions ||
+        !declaration.asVariableDefinitions().definitions.nodes.tail.isEmpty()))
+    {
+      // The variable declaration is either not an identifier, not a
+      // declaration, or it's declaring more than one variable.
+      error(node.declaredIdentifier, MessageKind.INVALID_FOR_IN, []);
+    }
+  }
+
+  visitLabeledStatement(LabeledStatement node) {
+    String labelName = node.label.source.slowToString();
+    LabelElement existingElement = statementScope.lookupLabel(labelName);
+    if (existingElement !== null) {
+      warning(node.label, MessageKind.DUPLICATE_LABEL, [labelName]);
+      warning(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
+    }
+    Node body = node.getBody();
+    TargetElement targetElement = getOrCreateTargetElement(body);
+
+    LabelElement element = targetElement.addLabel(node.label, labelName);
+    statementScope.enterLabelScope(element);
+    visit(node.statement);
+    statementScope.exitLabelScope();
+    if (element.isTarget) {
+      mapping[node.label] = element;
+    } else {
+      warning(node.label, MessageKind.UNUSED_LABEL, [labelName]);
+    }
+    if (!targetElement.isTarget && mapping[body] === targetElement) {
+      // If the body is itself a break or continue for another target, it
+      // might have updated its mapping to the target it actually does target.
+      mapping.remove(body);
+    }
+  }
+
+  visitLiteralMap(LiteralMap node) {
+    node.visitChildren(this);
+  }
+
+  visitLiteralMapEntry(LiteralMapEntry node) {
+    node.visitChildren(this);
+  }
+
+  visitNamedArgument(NamedArgument node) {
+    visit(node.expression);
+  }
+
+  visitSwitchStatement(SwitchStatement node) {
+    node.expression.accept(this);
+
+    TargetElement breakElement = getOrCreateTargetElement(node);
+    Map<String, LabelElement> continueLabels = <LabelElement>{};
+    Link<Node> cases = node.cases.nodes;
+    while (!cases.isEmpty()) {
+      SwitchCase switchCase = cases.head;
+      if (switchCase.label !== null) {
+        Identifier labelIdentifier = switchCase.label;
+        String labelName = labelIdentifier.source.slowToString();
+
+        LabelElement existingElement = continueLabels[labelName];
+        if (existingElement !== null) {
+          // It's an error if the same label occurs twice in the same switch.
+          warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
+          error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
+        } else {
+          // It's only a warning if it shadows another label.
+          existingElement = statementScope.lookupLabel(labelName);
+          if (existingElement !== null) {
+            warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
+            warning(existingElement.label,
+                    MessageKind.EXISTING_LABEL, [labelName]);
+          }
+        }
+
+        TargetElement TargetElement =
+            new TargetElement(switchCase,
+                                 statementScope.nestingLevel,
+                                 enclosingElement);
+        mapping[switchCase] = TargetElement;
+
+        LabelElement label =
+            new LabelElement(labelIdentifier, labelName,
+                             TargetElement, enclosingElement);
+        mapping[labelIdentifier] = label;
+        continueLabels[labelName] = label;
+      }
+      cases = cases.tail;
+      if (switchCase.defaultKeyword !== null && !cases.isEmpty()) {
+        error(switchCase, MessageKind.INVALID_CASE_DEFAULT);
+      }
+    }
+    statementScope.enterSwitch(breakElement, continueLabels);
+    node.cases.accept(this);
+    statementScope.exitSwitch();
+
+    // Clean-up unused labels
+    continueLabels.forEach((String key, LabelElement label) {
+      TargetElement TargetElement = label.target;
+      SwitchCase switchCase = TargetElement.statement;
+      if (!label.isContinueTarget) {
+        mapping.remove(switchCase);
+        mapping.remove(label.label);
+      }
+    });
+  }
+
+  visitSwitchCase(SwitchCase node) {
+    // The label was handled in [visitSwitchStatement(SwitchStatement)].
+    node.expressions.accept(this);
+    visitIn(node.statements, new BlockScope(context));
+  }
+
+  visitTryStatement(TryStatement node) {
+    visit(node.tryBlock);
+    if (node.catchBlocks.isEmpty() && node.finallyBlock == null) {
+      // TODO(ngeoffray): The precise location is
+      // node.getEndtoken.next. Adjust when issue #1581 is fixed.
+      error(node, MessageKind.NO_CATCH_NOR_FINALLY);
+    }
+    visit(node.catchBlocks);
+    visit(node.finallyBlock);
+  }
+
+  visitCatchBlock(CatchBlock node) {
+    Scope scope = new BlockScope(context);
+    if (node.formals.isEmpty()) {
+      error(node, MessageKind.EMPTY_CATCH_DECLARATION);
+    } else if (!node.formals.nodes.tail.isEmpty()
+               && !node.formals.nodes.tail.tail.isEmpty()) {
+      for (Node extra in node.formals.nodes.tail.tail) {
+        error(extra, MessageKind.EXTRA_CATCH_DECLARATION);
+      }
+    }
+    visitIn(node.formals, scope);
+    visitIn(node.block, scope);
+  }
+
+  visitTypedef(Typedef node) {
+    unimplemented(node, 'typedef');
+  }
+}
+
+class ClassResolverVisitor extends CommonResolverVisitor<Type> {
+  Scope context;
+  ClassElement classElement;
+
+  ClassResolverVisitor(Compiler compiler, LibraryElement library,
+                       ClassElement this.classElement)
+    : context = new TopScope(library),
+      super(compiler);
+
+  Type visitClassNode(ClassNode node) {
+    compiler.ensure(classElement !== null);
+    compiler.ensure(!classElement.isResolved);
+    final Link<Node> parameters =
+        node.typeParameters !== null ? node.typeParameters.nodes
+                                     : const EmptyLink<TypeVariable>();
+    // Create types and elements for type variable.
+    for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) {
+      TypeVariable typeNode = link.head;
+      SourceString variableName = typeNode.name.source;
+      TypeVariableType variableType = new TypeVariableType(variableName);
+      TypeVariableElement variableElement =
+          new TypeVariableElement(variableName, classElement, node,
+                                  variableType);
+      variableType.element = variableElement;
+      classElement.typeParameters[variableName] = variableElement;
+      context = new TypeVariablesScope(context, classElement);
+    }
+    // Resolve the bounds of type variables.
+    for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) {
+      TypeVariable typeNode = link.head;
+      SourceString variableName = typeNode.name.source;
+      TypeVariableElement variableElement =
+          classElement.typeParameters[variableName];
+      if (typeNode.bound !== null) {
+        Type boundType = visit(typeNode.bound);
+        if (boundType !== null && boundType.element == variableElement) {
+          warning(node, MessageKind.CYCLIC_TYPE_VARIABLE,
+                  [variableElement.name]);
+        } else if (boundType !== null) {
+          variableElement.bound = boundType;
+        } else {
+          variableElement.bound = compiler.objectClass.computeType(compiler);
+        }
+      }
+    }
+    // Find super type.
+    Type supertype = visit(node.superclass);
+    if (supertype !== null && supertype.element.isExtendable()) {
+      classElement.supertype = supertype;
+      if (isBlackListed(supertype)) {
+        error(node.superclass, MessageKind.CANNOT_EXTEND, [supertype]);
+      }
+    } else if (supertype !== null) {
+      error(node.superclass, MessageKind.TYPE_NAME_EXPECTED);
+    }
+    if (classElement.name != Types.OBJECT && classElement.supertype === null) {
+      ClassElement objectElement = context.lookup(Types.OBJECT);
+      if (objectElement !== null && !objectElement.isResolved) {
+        compiler.resolver.toResolve.add(objectElement);
+      } else if (objectElement === null){
+        error(node, MessageKind.CANNOT_RESOLVE_TYPE, [Types.OBJECT]);
+      }
+      classElement.supertype = new SimpleType(Types.OBJECT, objectElement);
+    }
+    if (node.defaultClause !== null) {
+      classElement.defaultClass = visit(node.defaultClause);
+    }
+    for (Link<Node> link = node.interfaces.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      Type interfaceType = visit(link.head);
+      if (interfaceType !== null && interfaceType.element.isExtendable()) {
+        classElement.interfaces =
+            classElement.interfaces.prepend(interfaceType);
+        if (isBlackListed(interfaceType)) {
+          error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]);
+        }
+      } else {
+        error(link.head, MessageKind.TYPE_NAME_EXPECTED);
+      }
+    }
+    calculateAllSupertypes(classElement, new Set<ClassElement>());
+    addDefaultConstructorIfNeeded(classElement);
+    return classElement.computeType(compiler);
+  }
+
+  Type visitTypeAnnotation(TypeAnnotation node) {
+    return visit(node.typeName);
+  }
+
+  Type visitIdentifier(Identifier node) {
+    Element element = context.lookup(node.source);
+    if (element === null) {
+      error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node]);
+      return null;
+    } else if (!element.impliesType() && !element.isTypeVariable()) {
+      error(node, MessageKind.NOT_A_TYPE, [node]);
+      return null;
+    } else {
+      if (element.isClass()) {
+        compiler.resolver.toResolve.add(element);
+      }
+      if (element.isTypeVariable()) {
+        TypeVariableElement variableElement = element;
+        return variableElement.type;
+      } else if (element.isTypedef()) {
+        compiler.unimplemented('visitIdentifier for typedefs');
+      } else {
+        // TODO(ngeoffray): Use type variables.
+        return element.computeType(compiler);
+      }
+    }
+    return null;
+  }
+
+  Type visitSend(Send node) {
+    Identifier prefix = node.receiver.asIdentifier();
+    if (prefix === null) {
+      error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]);
+      return null;
+    }
+    Element element = context.lookup(prefix.source);
+    if (element === null || element.kind !== ElementKind.PREFIX) {
+      error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]);
+      return null;
+    }
+    PrefixElement prefixElement = element;
+    Identifier selector = node.selector.asIdentifier();
+    var e = prefixElement.lookupLocalMember(selector.source);
+    if (e === null || !e.impliesType()) {
+      error(node.selector, MessageKind.CANNOT_RESOLVE_TYPE, [node.selector]);
+      return null;
+    }
+    return e.computeType(compiler);
+  }
+
+  Link<Type> getOrCalculateAllSupertypes(ClassElement classElement,
+                                         [Set<ClassElement> seen]) {
+    Link<Type> allSupertypes = classElement.allSupertypes;
+    if (allSupertypes !== null) return allSupertypes;
+    if (seen === null) {
+      seen = new Set<ClassElement>();
+    }
+    if (seen.contains(classElement)) {
+      error(classElement.parseNode(compiler),
+            MessageKind.CYCLIC_CLASS_HIERARCHY,
+            [classElement.name]);
+      classElement.allSupertypes = const EmptyLink<Type>();
+    } else {
+      classElement.ensureResolved(compiler);
+      calculateAllSupertypes(classElement, seen);
+    }
+    return classElement.allSupertypes;
+  }
+
+  void calculateAllSupertypes(ClassElement classElement,
+                              Set<ClassElement> seen) {
+    // TODO(karlklose): substitute type variables.
+    // TODO(karlklose): check if type arguments match, if a classelement occurs
+    //                  more than once in the supertypes.
+    if (classElement.allSupertypes !== null) return;
+    final Type supertype = classElement.supertype;
+    if (seen.contains(classElement)) {
+      error(classElement.parseNode(compiler),
+            MessageKind.CYCLIC_CLASS_HIERARCHY,
+            [classElement.name]);
+      classElement.allSupertypes = const EmptyLink<Type>();
+    } else if (supertype != null) {
+      seen.add(classElement);
+      Link<Type> superSupertypes =
+          getOrCalculateAllSupertypes(supertype.element, seen);
+      Link<Type> supertypes = new Link<Type>(supertype, superSupertypes);
+      for (Link<Type> interfaces = classElement.interfaces;
+           !interfaces.isEmpty();
+           interfaces = interfaces.tail) {
+        Element element = interfaces.head.element;
+        Link<Type> interfaceSupertypes =
+            getOrCalculateAllSupertypes(element, seen);
+        supertypes = supertypes.reversePrependAll(interfaceSupertypes);
+        supertypes = supertypes.prepend(interfaces.head);
+      }
+      seen.remove(classElement);
+      classElement.allSupertypes = supertypes;
+    } else {
+      classElement.allSupertypes = const EmptyLink<Type>();
+    }
+  }
+
+  /**
+   * Add a synthetic nullary constructor if there are no other
+   * constructors.
+   */
+  void addDefaultConstructorIfNeeded(ClassElement element) {
+    if (element.constructors.length != 0) return;
+    SynthesizedConstructorElement constructor =
+      new SynthesizedConstructorElement(element);
+    element.constructors[element.name] = constructor;
+    Type returnType = compiler.types.voidType;
+    constructor.type = new FunctionType(returnType, const EmptyLink<Type>(),
+                                        constructor);
+    constructor.cachedNode =
+      new FunctionExpression(new Identifier(element.position()),
+                             new NodeList.empty(),
+                             new Block(new NodeList.empty()),
+                             null, null, null, null);
+  }
+
+  isBlackListed(Type type) {
+    LibraryElement lib = classElement.getLibrary();
+    return
+      lib !== compiler.coreLibrary &&
+      lib !== compiler.coreImplLibrary &&
+      lib !== compiler.jsHelperLibrary &&
+      (type.element === compiler.dynamicClass ||
+       type.element === compiler.boolClass ||
+       type.element === compiler.numClass ||
+       type.element === compiler.intClass ||
+       type.element === compiler.doubleClass ||
+       type.element === compiler.stringClass ||
+       type.element === compiler.nullClass ||
+       type.element === compiler.functionClass);
+  }
+}
+
+class VariableDefinitionsVisitor extends CommonResolverVisitor<SourceString> {
+  VariableDefinitions definitions;
+  ResolverVisitor resolver;
+  ElementKind kind;
+  VariableListElement variables;
+
+  VariableDefinitionsVisitor(Compiler compiler,
+                             this.definitions, this.resolver, this.kind)
+    : super(compiler)
+  {
+    variables = new VariableListElement.node(
+        definitions, ElementKind.VARIABLE_LIST, resolver.context.element);
+  }
+
+  SourceString visitSendSet(SendSet node) {
+    assert(node.arguments.tail.isEmpty()); // Sanity check
+    resolver.visit(node.arguments.head);
+    return visit(node.selector);
+  }
+
+  SourceString visitIdentifier(Identifier node) => node.source;
+
+  visitNodeList(NodeList node) {
+    for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
+      SourceString name = visit(link.head);
+      VariableElement element = new VariableElement(
+          name, variables, kind, resolver.context.element, node: link.head);
+      resolver.defineElement(link.head, element);
+    }
+  }
+}
+
+class SignatureResolver extends CommonResolverVisitor<Element> {
+  final Element enclosingElement;
+  Link<Element> optionalParameters = const EmptyLink<Element>();
+  int optionalParameterCount = 0;
+  Node currentDefinitions;
+
+  SignatureResolver(Compiler compiler, this.enclosingElement) : super(compiler);
+
+  Element visitNodeList(NodeList node) {
+    // This must be a list of optional arguments.
+    if (node.beginToken.stringValue !== '[') {
+      internalError(node, "expected optional parameters");
+    }
+    LinkBuilder<Element> elements = analyzeNodes(node.nodes);
+    optionalParameterCount = elements.length;
+    optionalParameters = elements.toLink();
+    return null;
+  }
+
+  Element visitVariableDefinitions(VariableDefinitions node) {
+    resolveType(node.type);
+
+    Link<Node> definitions = node.definitions.nodes;
+    if (definitions.isEmpty()) {
+      cancel(node, 'internal error: no parameter definition');
+      return null;
+    }
+    if (!definitions.tail.isEmpty()) {
+      cancel(definitions.tail.head, 'internal error: extra definition');
+      return null;
+    }
+    Node definition = definitions.head;
+    if (definition is NodeList) {
+      cancel(node, 'optional parameters are not implemented');
+    }
+
+    if (currentDefinitions != null) {
+      cancel(node, 'function type parameters not supported');
+    }
+    currentDefinitions = node;
+    Element element = definition.accept(this);
+    currentDefinitions = null;
+    return element;
+  }
+
+  Element visitIdentifier(Identifier node) {
+    Element variables = new VariableListElement.node(currentDefinitions,
+        ElementKind.VARIABLE_LIST, enclosingElement);
+    return new VariableElement(node.source, variables,
+        ElementKind.PARAMETER, enclosingElement, node: node);
+  }
+
+  // The only valid [Send] can be in constructors and must be of the form
+  // [:this.x:] (where [:x:] represents an instance field).
+  FieldParameterElement visitSend(Send node) {
+    FieldParameterElement element;
+    if (node.receiver.asIdentifier() === null ||
+        !node.receiver.asIdentifier().isThis()) {
+      error(node, MessageKind.INVALID_PARAMETER, []);
+    } else if (enclosingElement.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) {
+      error(node, MessageKind.FIELD_PARAMETER_NOT_ALLOWED, []);
+    } else {
+      if (node.selector.asIdentifier() == null) {
+        cancel(node,
+               'internal error: unimplemented receiver on parameter send');
+      }
+      SourceString name = node.selector.asIdentifier().source;
+      Element fieldElement = currentClass.lookupLocalMember(name);
+      if (fieldElement === null || fieldElement.kind !== ElementKind.FIELD) {
+        error(node, MessageKind.NOT_A_FIELD, [name]);
+      } else if (!fieldElement.isInstanceMember()) {
+        error(node, MessageKind.NOT_INSTANCE_FIELD, [name]);
+      }
+      Element variables = new VariableListElement.node(currentDefinitions,
+          ElementKind.VARIABLE_LIST, enclosingElement);
+      element = new FieldParameterElement(node.selector.asIdentifier().source,
+          fieldElement, variables, enclosingElement, node);
+    }
+    return element;
+  }
+
+  Element visitSendSet(SendSet node) {
+    Element element;
+    if (node.receiver != null) {
+      element = visitSend(node);
+    } else if (node.selector.asIdentifier() != null) {
+      Element variables = new VariableListElement.node(currentDefinitions,
+          ElementKind.VARIABLE_LIST, enclosingElement);
+      element = new VariableElement(node.selector.asIdentifier().source,
+          variables, ElementKind.PARAMETER, enclosingElement, node: node);
+    }
+    // Visit the value. The compile time constant handler will
+    // make sure it's a compile time constant.
+    resolveExpression(node.arguments.head);
+    compiler.enqueue(new WorkItem.toCompile(element));
+    return element;
+  }
+
+  Element visitFunctionExpression(FunctionExpression node) {
+    // This is a function typed parameter.
+    // TODO(ahe): Resolve the function type.
+    return visit(node.name);
+  }
+
+  LinkBuilder<Element> analyzeNodes(Link<Node> link) {
+    LinkBuilder<Element> elements = new LinkBuilder<Element>();
+    for (; !link.isEmpty(); link = link.tail) {
+      Element element = link.head.accept(this);
+      if (element != null) {
+        elements.addLast(element);
+      } else {
+        // If parameter is null, the current node should be the last,
+        // and a list of optional named parameters.
+        if (!link.tail.isEmpty() || (link.head is !NodeList)) {
+          internalError(link.head, "expected optional parameters");
+        }
+      }
+    }
+    return elements;
+  }
+
+  static FunctionParameters analyze(Compiler compiler,
+                                    FunctionElement element) {
+    FunctionExpression node = element.parseNode(compiler);
+    SignatureResolver visitor = new SignatureResolver(compiler, element);
+    Link<Node> nodes = node.parameters.nodes;
+    LinkBuilder<Element> parameters = visitor.analyzeNodes(nodes);
+    return new FunctionParameters(parameters.toLink(),
+                                  visitor.optionalParameters,
+                                  parameters.length,
+                                  visitor.optionalParameterCount);
+  }
+
+  // TODO(ahe): This is temporary.
+  void resolveExpression(Node node) {
+    if (node == null) return;
+    node.accept(new ResolverVisitor(compiler, enclosingElement));
+  }
+
+  // TODO(ahe): This is temporary.
+  void resolveType(Node node) {
+    if (node == null) return;
+    node.accept(new ResolverVisitor(compiler, enclosingElement));
+  }
+
+  // TODO(ahe): This is temporary.
+  ClassElement get currentClass() {
+    return enclosingElement.isMember()
+      ? enclosingElement.enclosingElement : null;
+  }
+}
+
+class ConstructorResolver extends CommonResolverVisitor<Element> {
+  final ResolverVisitor resolver;
+  ConstructorResolver(Compiler compiler, this.resolver) : super(compiler);
+
+  visitNode(Node node) {
+    throw 'not supported';
+  }
+
+  visitNewExpression(NewExpression node) {
+    Node selector = node.send.selector;
+    Element e = visit(selector);
+    if (e !== null && e.kind === ElementKind.CLASS) {
+      ClassElement cls = e;
+      cls.ensureResolved(compiler);
+      compiler.resolver.toResolve.add(cls);
+      if (cls.isInterface() && (cls.defaultClass === null)) {
+        error(selector, MessageKind.CANNOT_INSTANTIATE_INTERFACE, [cls.name]);
+      }
+      e = cls.lookupConstructor(cls.name);
+    }
+    return e;
+  }
+
+  visitTypeAnnotation(TypeAnnotation node) {
+    // TODO(ahe): Do not ignore type arguments.
+    return visit(node.typeName);
+  }
+
+  visitSend(Send node) {
+    Element e = visit(node.receiver);
+    if (e === null) return null; // TODO(ahe): Return erroneous element.
+
+    Identifier name = node.selector.asIdentifier();
+    if (name === null) internalError(node.selector, 'unexpected node');
+
+    if (e.kind === ElementKind.CLASS) {
+      ClassElement cls = e;
+      cls.ensureResolved(compiler);
+      compiler.resolver.toResolve.add(cls);
+      if (cls.isInterface() && (cls.defaultClass === null)) {
+        error(node.receiver, MessageKind.CANNOT_INSTANTIATE_INTERFACE,
+              [cls.name]);
+      }
+      SourceString constructorName =
+        Elements.constructConstructorName(cls.name, name.source);
+      FunctionElement constructor = cls.lookupConstructor(constructorName);
+      if (constructor === null) {
+        error(name, MessageKind.CANNOT_FIND_CONSTRUCTOR, [name]);
+      }
+      e = constructor;
+    } else if (e.kind === ElementKind.PREFIX) {
+      PrefixElement prefix = e;
+      e = prefix.lookupLocalMember(name.source);
+      if (e === null) {
+        error(name, MessageKind.CANNOT_RESOLVE, [name]);
+        // TODO(ahe): Return erroneous element.
+      } else if (e.kind !== ElementKind.CLASS) {
+        error(node, MessageKind.NOT_A_TYPE, [name]);
+      }
+    } else {
+      internalError(node.receiver, 'unexpected element $e');
+    }
+    return e;
+  }
+
+  Element visitIdentifier(Identifier node) {
+    SourceString name = node.source;
+    Element e = resolver.lookup(node, name);
+    if (e === null) {
+      error(node, MessageKind.CANNOT_RESOLVE, [name]);
+      // TODO(ahe): Return erroneous element.
+    } else if (e.kind === ElementKind.TYPEDEF) {
+      error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF, [name]);
+    } else if (e.kind !== ElementKind.CLASS && e.kind !== ElementKind.PREFIX) {
+      error(node, MessageKind.NOT_A_TYPE, [name]);
+    }
+    return e;
+  }
+}
+
+class Scope {
+  final Element element;
+  final Scope parent;
+
+  Scope(this.parent, this.element);
+  abstract Element add(Element element);
+  abstract Element lookup(SourceString name);
+}
+
+class TypeVariablesScope extends Scope {
+  TypeVariablesScope(parent, ClassElement element) : super(parent, element);
+  Element add(Element element) {
+    throw "Cannot add element to TypeVariableScope";
+  }
+  Element lookup(SourceString name) {
+    ClassElement cls = element;
+    Element result = cls.lookupTypeParameter(name);
+    if (result !== null) return result;
+    if (parent !== null) return parent.lookup(name);
+  }
+}
+
+class MethodScope extends Scope {
+  final Map<SourceString, Element> elements;
+
+  MethodScope(Scope parent, Element element)
+    : super(parent, element), this.elements = new Map<SourceString, Element>();
+
+  Element lookup(SourceString name) {
+    Element element = elements[name];
+    if (element !== null) return element;
+    return parent.lookup(name);
+  }
+
+  Element add(Element element) {
+    if (elements.containsKey(element.name)) return elements[element.name];
+    elements[element.name] = element;
+    return element;
+  }
+}
+
+class BlockScope extends MethodScope {
+  BlockScope(Scope parent) : super(parent, parent.element);
+}
+
+class ClassScope extends Scope {
+  ClassScope(ClassElement element, LibraryElement library)
+    : super(new TopScope(library), element);
+
+  Element lookup(SourceString name) {
+    ClassElement cls = element;
+    Element result = cls.lookupLocalMember(name);
+    if (result !== null) return result;
+    result = cls.lookupTypeParameter(name);
+    if (result !== null) return result;
+    result = parent.lookup(name);
+    if (result != null) return result;
+    return cls.lookupSuperMember(name);
+  }
+
+  Element add(Element element) {
+    throw "Cannot add an element in a class scope";
+  }
+}
+
+class TopScope extends Scope {
+  LibraryElement get library() => element;
+
+  TopScope(LibraryElement library) : super(null, library);
+  Element lookup(SourceString name) {
+    return library.find(name);
+  }
+
+  Element add(Element element) {
+    throw "Cannot add an element in the top scope";
+  }
+}
diff --git a/lib/compiler/implementation/scanner/array_based_scanner.dart b/lib/compiler/implementation/scanner/array_based_scanner.dart
new file mode 100644
index 0000000..c32ea28
--- /dev/null
+++ b/lib/compiler/implementation/scanner/array_based_scanner.dart
@@ -0,0 +1,171 @@
+// Copyright (c) 2011, 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.
+
+class ArrayBasedScanner<S> extends AbstractScanner<S> {
+  int get charOffset() => byteOffset + extraCharOffset;
+  final Token tokens;
+  Token tail;
+  int tokenStart;
+  int byteOffset;
+
+  /** Since the input is UTF8, some characters are represented by more
+   * than one byte. [extraCharOffset] tracks the difference. */
+  int extraCharOffset;
+  Link<BeginGroupToken> groupingStack = const EmptyLink<BeginGroupToken>();
+
+  ArrayBasedScanner()
+    : this.extraCharOffset = 0,
+      this.tokenStart = -1,
+      this.byteOffset = -1,
+      this.tokens = new Token(EOF_INFO, -1) {
+    this.tail = this.tokens;
+  }
+
+  int advance() {
+    int next = nextByte();
+    return next;
+  }
+
+  int select(int choice, PrecedenceInfo yes, PrecedenceInfo no) {
+    int next = advance();
+    if (next === choice) {
+      appendPrecenceToken(yes);
+      return advance();
+    } else {
+      appendPrecenceToken(no);
+      return next;
+    }
+  }
+
+  void appendPrecenceToken(PrecedenceInfo info) {
+    tail.next = new Token(info, tokenStart);
+    tail = tail.next;
+  }
+
+  void appendStringToken(PrecedenceInfo info, String value) {
+    tail.next = new StringToken(info, value, tokenStart);
+    tail = tail.next;
+  }
+
+  void appendKeywordToken(Keyword keyword) {
+    tail.next = new KeywordToken(keyword, tokenStart);
+    tail = tail.next;
+  }
+
+  void appendEofToken() {
+    tail.next = new Token(EOF_INFO, charOffset);
+    tail = tail.next;
+    // EOF points to itself so there's always infinite look-ahead.
+    tail.next = tail;
+    discardOpenLt();
+    if (!groupingStack.isEmpty()) {
+      BeginGroupToken begin = groupingStack.head;
+      throw new MalformedInputException('Unbalanced ${begin.stringValue}',
+                                        begin);
+    }
+  }
+
+  void beginToken() {
+    tokenStart = charOffset;
+  }
+
+  Token firstToken() {
+    return tokens.next;
+  }
+
+  void addToCharOffset(int offset) {
+    extraCharOffset += offset;
+  }
+
+  void appendWhiteSpace(int next) {
+    // Do nothing, we don't collect white space.
+  }
+
+  void appendBeginGroup(PrecedenceInfo info, String value) {
+    Token token = new BeginGroupToken(info, value, tokenStart);
+    tail.next = token;
+    tail = tail.next;
+    while (info.kind !== LT_TOKEN &&
+           !groupingStack.isEmpty() &&
+           groupingStack.head.kind === LT_TOKEN) {
+      groupingStack = groupingStack.tail;
+    }
+    groupingStack = groupingStack.prepend(token);
+  }
+
+  int appendEndGroup(PrecedenceInfo info, String value, int openKind) {
+    assert(openKind !== LT_TOKEN);
+    appendStringToken(info, value);
+    discardOpenLt();
+    if (groupingStack.isEmpty()) {
+      return advance();
+    }
+    BeginGroupToken begin = groupingStack.head;
+    if (begin.kind !== openKind) {
+      if (openKind !== OPEN_CURLY_BRACKET_TOKEN ||
+          begin.kind !== STRING_INTERPOLATION_TOKEN) {
+        // Not ending string interpolation.
+        throw new MalformedInputException('Unmatched ${begin.stringValue}',
+                                          begin);
+      }
+      // We're ending an interpolated expression.
+      begin.endGroup = tail;
+      groupingStack = groupingStack.tail;
+      // Using "start-of-text" to signal that we're back in string
+      // scanning mode.
+      return $STX;
+    }
+    begin.endGroup = tail;
+    groupingStack = groupingStack.tail;
+    return advance();
+  }
+
+  void appendGt(PrecedenceInfo info, String value) {
+    appendStringToken(info, value);
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack.head.endGroup = tail;
+      groupingStack = groupingStack.tail;
+    }
+  }
+
+  void appendGtGt(PrecedenceInfo info, String value) {
+    appendStringToken(info, value);
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack = groupingStack.tail;
+    }
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack.head.endGroup = tail;
+      groupingStack = groupingStack.tail;
+    }
+  }
+
+  void appendGtGtGt(PrecedenceInfo info, String value) {
+    appendStringToken(info, value);
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack = groupingStack.tail;
+    }
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack = groupingStack.tail;
+    }
+    if (groupingStack.isEmpty()) return;
+    if (groupingStack.head.kind === LT_TOKEN) {
+      groupingStack.head.endGroup = tail;
+      groupingStack = groupingStack.tail;
+    }
+  }
+
+  void discardOpenLt() {
+    while (!groupingStack.isEmpty() && groupingStack.head.kind === LT_TOKEN) {
+      groupingStack = groupingStack.tail;
+    }
+  }
+
+  // TODO(ahe): make class abstract instead of adding an abstract method.
+  abstract peek();
+}
diff --git a/lib/compiler/implementation/scanner/byte_array_scanner.dart b/lib/compiler/implementation/scanner/byte_array_scanner.dart
new file mode 100644
index 0000000..cdc7248
--- /dev/null
+++ b/lib/compiler/implementation/scanner/byte_array_scanner.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * Scanner that reads from a byte array and creates tokens that points
+ * to the same array.
+ */
+class ByteArrayScanner extends ArrayBasedScanner<ByteString> {
+  final List<int> bytes;
+
+  ByteArrayScanner(List<int> this.bytes) : super();
+
+  int nextByte() => byteAt(++byteOffset);
+
+  int peek() => byteAt(byteOffset + 1);
+
+  int byteAt(int index) => bytes[index];
+
+  AsciiString asciiString(int start, int offset) {
+    return AsciiString.of(bytes, start, byteOffset - start + offset);
+  }
+
+  Utf8String utf8String(int start, int offset) {
+    return Utf8String.of(bytes, start, byteOffset - start + offset + 1);
+  }
+
+  void appendByteStringToken(PrecedenceInfo info, ByteString value) {
+    tail.next = new ByteStringToken(info, value, tokenStart);
+    tail = tail.next;
+  }
+
+  // This method should be equivalent to the one in super. However,
+  // this is a *HOT* method and Dart VM performs better if it is easy
+  // to inline.
+  int advance() => bytes[++byteOffset];
+}
diff --git a/lib/compiler/implementation/scanner/byte_strings.dart b/lib/compiler/implementation/scanner/byte_strings.dart
new file mode 100644
index 0000000..e6daab1
--- /dev/null
+++ b/lib/compiler/implementation/scanner/byte_strings.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * An abstract string representation.
+ */
+class ByteString implements SourceString {
+  final List<int> bytes;
+  final int offset;
+  final int length;
+  int _hashCode;
+
+  ByteString(List<int> this.bytes, int this.offset, int this.length);
+
+  abstract String get charset();
+
+  String slowToString() => new String.fromCharCodes(
+      new Utf8Decoder(bytes, offset, length).decodeRest());
+
+  String toString() => "ByteString(${slowToString()})";
+
+  bool operator ==(other) {
+    throw "should be overridden in subclass";
+  }
+
+  Iterator<int> iterator() => new Utf8Decoder(bytes, offset, length);
+
+  int hashCode() {
+    if (_hashCode === null) {
+      _hashCode = computeHashCode();
+    }
+    return _hashCode;
+  }
+
+  int computeHashCode() {
+    int code = 1;
+    int end = offset + length;
+    for (int i = offset; i < end; i++) {
+      code += 19 * code + bytes[i];
+    }
+    return code;
+  }
+
+  printOn(StringBuffer sb) {
+    sb.add(slowToString());
+  }
+
+  bool isEmpty() => length == 0;
+}
+
+/**
+ * A string that consists purely of 7bit ASCII characters.
+ */
+class AsciiString extends ByteString {
+  final String charset = "ASCII";
+
+  AsciiString(List<int> bytes, int offset, int length)
+    : super(bytes, offset, length);
+
+  static AsciiString of(List<int> bytes, int offset, int length) {
+    AsciiString string = new AsciiString(bytes, offset, length);
+    return string;
+  }
+
+  Iterator<int> iterator() => new AsciiStringIterator(bytes);
+
+  SourceString copyWithoutQuotes(int initial, int terminal) {
+    return new AsciiString(bytes, offset + initial,
+                           length - initial - terminal);
+  }
+
+
+  static AsciiString fromString(String string) {
+    List<int> bytes = string.charCodes();
+    return AsciiString.of(bytes, 0, bytes.length);
+  }
+}
+
+
+class AsciiStringIterator implements Iterator<int> {
+  final List<int> bytes;
+  int offset;
+  final int end;
+  AsciiStringIterator(List<int> bytes)
+      : this.bytes = bytes, offset = 0, end = bytes.length;
+  AsciiStringIterator.range(List<int> bytes, int from, int length)
+      : this.bytes = bytes, offset = from, end = from + length;
+  bool hasNext() => offset < end;
+  int next() => bytes[offset++];
+}
+
+
+/**
+ * A string that consists of characters that can be encoded as UTF-8.
+ */
+class Utf8String extends ByteString {
+  final String charset = "UTF8";
+
+  Utf8String(List<int> bytes, int offset, int length)
+    : super(bytes, offset, length);
+
+  static Utf8String of(List<int> bytes, int offset, int length) {
+    return new Utf8String(bytes, offset, length);
+  }
+
+  static Utf8String fromString(String string) {
+    throw "not implemented yet";
+  }
+
+  Iterator<int> iterator() => new Utf8Decoder(bytes, 0, length);
+
+  SourceString copyWithoutQuotes(int initial, int terminal) {
+    assert((){
+      // Only allow dropping ASCII characters, to guarantee that
+      // the resulting Utf8String is still valid.
+      for (int i = 0; i < initial; i++) {
+        if (bytes[offset + i] >= 0x80) return false;
+      }
+      for (int i = 0; i < terminal; i++) {
+        if (bytes[offset + length - terminal + i] >= 0x80) return false;
+      }
+      return true;
+    });
+    // TODO(lrn): Check that first and last bytes use the same type of quotes.
+    return new Utf8String(bytes, offset + initial,
+                          length - initial - terminal);
+  }
+}
+
+/**
+ * A ByteString-valued token.
+ */
+class ByteStringToken extends Token {
+  final ByteString value;
+
+  ByteStringToken(PrecedenceInfo info, ByteString this.value, int charOffset)
+    : super(info, charOffset);
+
+  String toString() => value.toString();
+}
diff --git a/lib/compiler/implementation/scanner/class_element_parser.dart b/lib/compiler/implementation/scanner/class_element_parser.dart
new file mode 100644
index 0000000..ef0ebe5
--- /dev/null
+++ b/lib/compiler/implementation/scanner/class_element_parser.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2011, 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.
+
+class ClassElementParser extends PartialParser {
+  ClassElementParser(Listener listener) : super(listener);
+
+  Token parseClassBody(Token token) => fullParseClassBody(token);
+}
+
+class PartialClassElement extends ClassElement {
+  final Token beginToken;
+  final Token endToken;
+  Node cachedNode;
+
+  PartialClassElement(SourceString name,
+                      Token this.beginToken,
+                      Token this.endToken,
+                      CompilationUnitElement enclosing)
+    : super(name, enclosing);
+
+  ClassNode parseNode(DiagnosticListener diagnosticListener) {
+    if (cachedNode != null) return cachedNode;
+    MemberListener listener = new MemberListener(diagnosticListener, this);
+    Parser parser = new ClassElementParser(listener);
+    Token token = parser.parseTopLevelDeclaration(beginToken);
+    assert(token === endToken.next);
+    cachedNode = listener.popNode();
+    assert(listener.nodes.isEmpty());
+    return cachedNode;
+  }
+
+  Token position() => beginToken;
+
+  bool isInterface() => beginToken.stringValue === "interface";
+}
+
+class MemberListener extends NodeListener {
+  final ClassElement enclosingElement;
+
+  MemberListener(DiagnosticListener listener,
+                 Element enclosingElement)
+    : this.enclosingElement = enclosingElement,
+      super(listener, enclosingElement.getCompilationUnit());
+
+  bool isConstructorName(Node nameNode) {
+    if (enclosingElement === null ||
+        enclosingElement.kind != ElementKind.CLASS) {
+      return false;
+    }
+    SourceString name;
+    if (nameNode.asIdentifier() !== null) {
+      name = nameNode.asIdentifier().source;
+    } else {
+      Send send = nameNode.asSend();
+      name = send.receiver.asIdentifier().source;
+    }
+    return enclosingElement.name == name;
+  }
+
+  SourceString getMethodNameHack(Node methodName) {
+    Send send = methodName.asSend();
+    if (send === null) return methodName.asIdentifier().source;
+    Identifier receiver = send.receiver.asIdentifier();
+    Identifier selector = send.selector.asIdentifier();
+    if (selector.asOperator() !== null) {
+      return Elements.constructOperatorName(receiver.source, selector.source);
+    } else {
+      return Elements.constructConstructorName(receiver.source,
+                                               selector.source);
+    }
+  }
+
+  void endMethod(Token getOrSet, Token beginToken, Token endToken) {
+    super.endMethod(getOrSet, beginToken, endToken);
+    FunctionExpression method = popNode();
+    pushNode(null);
+    bool isConstructor = isConstructorName(method.name);
+    SourceString name = getMethodNameHack(method.name);
+    ElementKind kind = ElementKind.FUNCTION;
+    if (isConstructor) {
+      if (getOrSet !== null) {
+        recoverableError('illegal modifier', token: getOrSet);
+      }
+      kind = ElementKind.GENERATIVE_CONSTRUCTOR;
+    } else if (getOrSet !== null) {
+      kind = (getOrSet.stringValue === 'get')
+             ? ElementKind.GETTER : ElementKind.SETTER;
+    }
+    Element memberElement =
+        new PartialFunctionElement(name, beginToken, getOrSet, endToken,
+                                   kind, method.modifiers, enclosingElement);
+    enclosingElement.addMember(memberElement, listener);
+  }
+
+  void endFactoryMethod(Token factoryKeyword, Token periodBeforeName,
+                        Token endToken) {
+    super.endFactoryMethod(factoryKeyword, periodBeforeName, endToken);
+    FunctionExpression method = popNode();
+    pushNode(null);
+    SourceString name = getMethodNameHack(method.name);
+    ElementKind kind = ElementKind.FUNCTION;
+    Element memberElement =
+        new PartialFunctionElement(name, factoryKeyword, null, endToken,
+                                   kind, method.modifiers, enclosingElement);
+    enclosingElement.addMember(memberElement, listener);
+  }
+
+  void endFields(int count, Token beginToken, Token endToken) {
+    super.endFields(count, beginToken, endToken);
+    VariableDefinitions variableDefinitions = popNode();
+    Modifiers modifiers = variableDefinitions.modifiers;
+    pushNode(null);
+    void buildFieldElement(SourceString name, Element fields) {
+      Element element = new VariableElement(
+          name, fields, ElementKind.FIELD, enclosingElement);
+      enclosingElement.addMember(element, listener);
+    }
+    buildFieldElements(modifiers, variableDefinitions.definitions,
+                       enclosingElement,
+                       buildFieldElement, beginToken, endToken);
+  }
+
+  void endInitializer(Token assignmentOperator) {
+    pushNode(null); // Super expects an expression, but
+                    // ClassElementParser just skips expressions.
+    super.endInitializer(assignmentOperator);
+  }
+
+  void endInitializers(int count, Token beginToken, Token endToken) {
+    pushNode(null);
+  }
+}
diff --git a/lib/compiler/implementation/scanner/d8_scanner_bench.dart b/lib/compiler/implementation/scanner/d8_scanner_bench.dart
new file mode 100644
index 0000000..2296978
--- /dev/null
+++ b/lib/compiler/implementation/scanner/d8_scanner_bench.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2011, 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('d8_scanner_bench');
+#import('../../node/node.dart');
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('scanner_bench.dart');
+
+d8_read(String filename) native 'return read(filename)';
+
+class D8ScannerBench extends ScannerBench {
+  int getBytes(String filename, void callback(bytes)) {
+    // This actually returns a buffer, not a String.
+    var s = d8_read(filename);
+    callback(s);
+    return s.length;
+  }
+
+  Scanner makeScanner(bytes) => new StringScanner(bytes);
+
+  void checkExistence(String filename) {
+  }
+}
+
+main() {
+  new D8ScannerBench().main(argv);
+}
diff --git a/lib/compiler/implementation/scanner/file_with_non_ascii.dart b/lib/compiler/implementation/scanner/file_with_non_ascii.dart
new file mode 100644
index 0000000..d644f96
--- /dev/null
+++ b/lib/compiler/implementation/scanner/file_with_non_ascii.dart
@@ -0,0 +1,10 @@
+int fisk() {
+  ahé();
+  (blåbærgrød)();
+  (blåbærgrød)();
+  (Îñţérñåţîöñåļîžåţîờñ)();
+  சிவா + அணாமாைல;
+  िसवा + अणामालै;
+  𐐒;
+  𐄂;
+}
diff --git a/lib/compiler/implementation/scanner/keyword.dart b/lib/compiler/implementation/scanner/keyword.dart
new file mode 100644
index 0000000..e9ddcd5
--- /dev/null
+++ b/lib/compiler/implementation/scanner/keyword.dart
@@ -0,0 +1,255 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * A keyword in the Dart programming language.
+ */
+class Keyword implements SourceString {
+  static final Keyword BREAK = const Keyword("break");
+  static final Keyword CASE = const Keyword("case");
+  static final Keyword CATCH = const Keyword("catch");
+  static final Keyword CLASS = const Keyword("class");
+  static final Keyword CONST = const Keyword("const");
+  static final Keyword CONTINUE = const Keyword("continue");
+  static final Keyword DEFAULT = const Keyword("default");
+  static final Keyword DO = const Keyword("do");
+  static final Keyword ELSE = const Keyword("else");
+  static final Keyword EXTENDS = const Keyword("extends");
+  static final Keyword FALSE = const Keyword("false");
+  static final Keyword FINAL = const Keyword("final");
+  static final Keyword FINALLY = const Keyword("finally");
+  static final Keyword FOR = const Keyword("for");
+  static final Keyword IF = const Keyword("if");
+  static final Keyword IN = const Keyword("in");
+  static final Keyword IS = const Keyword("is", info: IS_INFO);
+  static final Keyword NEW = const Keyword("new");
+  static final Keyword NULL = const Keyword("null");
+  static final Keyword RETURN = const Keyword("return");
+  static final Keyword SUPER = const Keyword("super");
+  static final Keyword SWITCH = const Keyword("switch");
+  static final Keyword THIS = const Keyword("this");
+  static final Keyword THROW = const Keyword("throw");
+  static final Keyword TRUE = const Keyword("true");
+  static final Keyword TRY = const Keyword("try");
+  static final Keyword VAR = const Keyword("var");
+  static final Keyword VOID = const Keyword("void");
+  static final Keyword WHILE = const Keyword("while");
+
+  // Pseudo keywords:
+  static final Keyword ABSTRACT = const Keyword("abstract", isPseudo: true);
+  static final Keyword ASSERT = const Keyword("assert", isPseudo: true);
+  static final Keyword FACTORY = const Keyword("factory", isPseudo: true);
+  static final Keyword GET = const Keyword("get", isPseudo: true);
+  static final Keyword IMPLEMENTS = const Keyword("implements", isPseudo: true);
+  static final Keyword IMPORT = const Keyword("import", isPseudo: true);
+  static final Keyword INTERFACE = const Keyword("interface", isPseudo: true);
+  static final Keyword LIBRARY = const Keyword("library", isPseudo: true);
+  static final Keyword NATIVE = const Keyword("native", isPseudo: true);
+  static final Keyword NEGATE = const Keyword("negate", isPseudo: true);
+  static final Keyword OPERATOR = const Keyword("operator", isPseudo: true);
+  static final Keyword SET = const Keyword("set", isPseudo: true);
+  static final Keyword SOURCE = const Keyword("source", isPseudo: true);
+  static final Keyword STATIC = const Keyword("static", isPseudo: true);
+  static final Keyword TYPEDEF = const Keyword("typedef", isPseudo: true);
+
+  static final List<Keyword> values = const <Keyword> [
+      BREAK,
+      CASE,
+      CATCH,
+      CONST,
+      CONTINUE,
+      DEFAULT,
+      DO,
+      ELSE,
+      FALSE,
+      FINAL,
+      FINALLY,
+      FOR,
+      IF,
+      IN,
+      IS,
+      NEW,
+      NULL,
+      RETURN,
+      SUPER,
+      SWITCH,
+      THIS,
+      THROW,
+      TRUE,
+      TRY,
+      VAR,
+      VOID,
+      WHILE,
+      ABSTRACT,
+      ASSERT,
+      CLASS,
+      EXTENDS,
+      FACTORY,
+      GET,
+      IMPLEMENTS,
+      IMPORT,
+      INTERFACE,
+      LIBRARY,
+      NATIVE,
+      NEGATE,
+      OPERATOR,
+      SET,
+      SOURCE,
+      STATIC,
+      TYPEDEF ];
+
+  final String syntax;
+  final bool isPseudo;
+  final PrecedenceInfo info;
+
+  static Map<String, Keyword> _keywords;
+  static Map<String, Keyword> get keywords() {
+    if (_keywords === null) {
+      _keywords = computeKeywordMap();
+    }
+    return _keywords;
+  }
+
+  const Keyword(String this.syntax,
+                [bool this.isPseudo = false,
+                 PrecedenceInfo this.info = KEYWORD_INFO]);
+
+  static Map<String, Keyword> computeKeywordMap() {
+    Map<String, Keyword> result = new LinkedHashMap<String, Keyword>();
+    for (Keyword keyword in values) {
+      result[keyword.syntax] = keyword;
+    }
+    return result;
+  }
+
+  int hashCode() => syntax.hashCode();
+
+  bool operator ==(other) {
+    return other is SourceString && toString() == other.slowToString();
+  }
+
+  Iterator<int> iterator() => new StringCodeIterator(syntax);
+
+  void printOn(StringBuffer sb) {
+    sb.add(syntax);
+  }
+
+  String toString() => syntax;
+  String slowToString() => syntax;
+  String get stringValue() => syntax;
+
+  bool isEmpty() => false;
+  bool isPrivate() => false;
+}
+
+/**
+ * Abstract state in a state machine for scanning keywords.
+ */
+class KeywordState {
+  abstract bool isLeaf();
+  abstract KeywordState next(int c);
+  abstract Keyword get keyword();
+
+  static KeywordState _KEYWORD_STATE;
+  static KeywordState get KEYWORD_STATE() {
+    if (_KEYWORD_STATE === null) {
+      List<String> strings = new List<String>(Keyword.values.length);
+      for (int i = 0; i < Keyword.values.length; i++) {
+        strings[i] = Keyword.values[i].syntax;
+      }
+      strings.sort((a,b) => a.compareTo(b));
+      _KEYWORD_STATE = computeKeywordStateTable(0, strings, 0, strings.length);
+    }
+    return _KEYWORD_STATE;
+  }
+
+  static KeywordState computeKeywordStateTable(int start, List<String> strings,
+                                               int offset, int length) {
+    List<KeywordState> result = new List<KeywordState>(26);
+    assert(length != 0);
+    int chunk = 0;
+    int chunkStart = -1;
+    bool isLeaf = false;
+    for (int i = offset; i < offset + length; i++) {
+      if (strings[i].length == start) {
+        isLeaf = true;
+      }
+      if (strings[i].length > start) {
+        int c = strings[i].charCodeAt(start);
+        if (chunk != c) {
+          if (chunkStart != -1) {
+            assert(result[chunk - $a] === null);
+            result[chunk - $a] = computeKeywordStateTable(start + 1, strings,
+                                                          chunkStart,
+                                                          i - chunkStart);
+          }
+          chunkStart = i;
+          chunk = c;
+        }
+      }
+    }
+    if (chunkStart != -1) {
+      assert(result[chunk - $a] === null);
+      result[chunk - $a] =
+        computeKeywordStateTable(start + 1, strings, chunkStart,
+                                 offset + length - chunkStart);
+    } else {
+      assert(length == 1);
+      return new LeafKeywordState(strings[offset]);
+    }
+    if (isLeaf) {
+      return new ArrayKeywordState(result, strings[offset]);
+    } else {
+      return new ArrayKeywordState(result, null);
+    }
+  }
+}
+
+/**
+ * A state with multiple outgoing transitions.
+ */
+class ArrayKeywordState extends KeywordState {
+  final List<KeywordState> table;
+  final Keyword keyword;
+
+  ArrayKeywordState(List<KeywordState> this.table, String syntax)
+    : keyword = (syntax === null) ? null : Keyword.keywords[syntax];
+
+  bool isLeaf() => false;
+
+  KeywordState next(int c) => table[c - $a];
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.add("[");
+    if (keyword !== null) {
+      sb.add("*");
+      sb.add(keyword);
+      sb.add(" ");
+    }
+    List<KeywordState> foo = table;
+    for (int i = 0; i < foo.length; i++) {
+      if (foo[i] != null) {
+        sb.add("${new String.fromCharCodes([i + $a])}: ${foo[i]}; ");
+      }
+    }
+    sb.add("]");
+    return sb.toString();
+  }
+}
+
+/**
+ * A state that has no outgoing transitions.
+ */
+class LeafKeywordState extends KeywordState {
+  final Keyword keyword;
+
+  LeafKeywordState(String syntax) : keyword = Keyword.keywords[syntax];
+
+  bool isLeaf() => true;
+
+  KeywordState next(int c) => null;
+
+  String toString() => keyword.syntax;
+}
diff --git a/lib/compiler/implementation/scanner/listener.dart b/lib/compiler/implementation/scanner/listener.dart
new file mode 100644
index 0000000..768bd5f
--- /dev/null
+++ b/lib/compiler/implementation/scanner/listener.dart
@@ -0,0 +1,1476 @@
+// Copyright (c) 2012, 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.
+
+final bool VERBOSE = false;
+
+class Listener {
+  void beginArguments(Token token) {
+  }
+
+  void endArguments(int count, Token beginToken, Token endToken) {
+  }
+
+  void beginBlock(Token token) {
+  }
+
+  void endBlock(int count, Token beginToken, Token endToken) {
+  }
+
+  void beginClassBody(Token token) {
+  }
+
+  void endClassBody(int memberCount, Token beginToken, Token endToken) {
+  }
+
+  void beginClassDeclaration(Token token) {
+  }
+
+  void endClassDeclaration(int interfacesCount, Token beginToken,
+                           Token extendsKeyword, Token implementsKeyword,
+                           Token endToken) {
+  }
+
+  void beginDoWhileStatement(Token token) {
+  }
+
+  void endDoWhileStatement(Token doKeyword, Token whileKeyword,
+                           Token endToken) {
+  }
+
+  void beginExpressionStatement(Token token) {
+  }
+
+  void endExpressionStatement(Token token) {
+  }
+
+  void beginDefaultClause(Token token) {
+  }
+
+  void handleNoDefaultClause(Token token) {
+  }
+
+  void endDefaultClause(Token defaultKeyword) {
+  }
+
+  void beginFactoryMethod(Token token) {
+  }
+
+  void endFactoryMethod(Token factoryKeyword, Token periodBeforeName,
+                        Token endToken) {
+  }
+
+  void beginFormalParameter(Token token) {
+  }
+
+  void endFormalParameter(Token token, Token thisKeyword) {
+  }
+
+  void beginFormalParameters(Token token) {
+  }
+
+  void endFormalParameters(int count, Token beginToken, Token endToken) {
+  }
+
+  void endFields(int count, Token beginToken, Token endToken) {
+  }
+
+  void beginForStatement(Token token) {
+  }
+
+  void endForStatement(int updateExpressionCount,
+                       Token beginToken, Token endToken) {
+  }
+
+  void endForInStatement(Token beginToken, Token inKeyword, Token endToken) {
+  }
+
+  void beginFunction(Token token) {
+  }
+
+  void endFunction(Token getOrSet, Token endToken) {
+  }
+
+  void beginFunctionDeclaration(Token token) {
+  }
+
+  void endFunctionDeclaration(Token token) {
+  }
+
+  void beginFunctionBody(Token token) {
+  }
+
+  void endFunctionBody(int count, Token beginToken, Token endToken) {
+  }
+
+  void handleNoFunctionBody(Token token) {
+  }
+
+  void beginFunctionName(Token token) {
+  }
+
+  void endFunctionName(Token token) {
+  }
+
+  void beginFunctionTypeAlias(Token token) {
+  }
+
+  void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
+  }
+
+  void beginIfStatement(Token token) {
+  }
+
+  void endIfStatement(Token ifToken, Token elseToken) {
+  }
+
+  void beginInitializedIdentifier(Token token) {
+  }
+
+  void endInitializedIdentifier() {
+  }
+
+  void beginInitializer(Token token) {
+  }
+
+  void endInitializer(Token assignmentOperator) {
+  }
+
+  void beginInitializers(Token token) {
+  }
+
+  void endInitializers(int count, Token beginToken, Token endToken) {
+  }
+
+  void handleNoInitializers() {
+  }
+
+  void beginInterface(Token token) {
+  }
+
+  void endInterface(int supertypeCount, Token interfaceKeyword,
+                    Token extendsKeyword, Token endToken) {
+  }
+
+  void beginLabeledStatement(Token token) {
+  }
+
+  void endLabeledStatement(Token colon) {
+  }
+
+  void beginLiteralMapEntry(Token token) {
+  }
+
+  void endLiteralMapEntry(Token colon, Token endToken) {
+  }
+
+  void beginLiteralString(Token token) {
+  }
+
+  void endLiteralString(int interpolationCount) {
+  }
+
+  void handleStringJuxtaposition(int literalCount) {
+  }
+
+  void beginMember(Token token) {
+  }
+
+  void endMethod(Token getOrSet, Token beginToken, Token endToken) {
+  }
+
+  void beginOptionalFormalParameters(Token token) {
+  }
+
+  void endOptionalFormalParameters(int count,
+                                   Token beginToken, Token endToken) {
+  }
+
+  void beginReturnStatement(Token token) {
+  }
+
+  void endReturnStatement(bool hasExpression,
+                          Token beginToken, Token endToken) {
+  }
+
+  void beginScriptTag(Token token) {
+  }
+
+  void endScriptTag(bool hasPrefix, Token beginToken, Token endToken) {
+  }
+
+  void beginSend(Token token) {
+  }
+
+  void endSend(Token token) {
+  }
+
+  void beginSwitchStatement(Token token) {
+  }
+
+  void endSwitchStatement(Token switchKeyword, Token endToken) {
+  }
+
+  void beginSwitchBlock(Token token) {
+  }
+
+  void endSwitchBlock(int caseCount, Token beginToken, Token endToken) {
+  }
+
+  void beginThrowStatement(Token token) {
+  }
+
+  void endThrowStatement(Token throwToken, Token endToken) {
+  }
+
+  void endRethrowStatement(Token throwToken, Token endToken) {
+  }
+
+  void beginTopLevelMember(Token token) {
+  }
+
+  void endTopLevelFields(int count, Token beginToken, Token endToken) {
+  }
+
+  void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) {
+  }
+
+  void beginTryStatement(Token token) {
+  }
+
+  void handleCatchBlock(Token catchKeyword) {
+  }
+
+  void handleFinallyBlock(Token finallyKeyword) {
+  }
+
+  void endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) {
+  }
+
+  void endType(Token beginToken, Token endToken) {
+  }
+
+  void beginTypeArguments(Token token) {
+  }
+
+  void endTypeArguments(int count, Token beginToken, Token endToken) {
+  }
+
+  void handleNoTypeArguments(Token token) {
+  }
+
+  void beginTypeVariable(Token token) {
+  }
+
+  void endTypeVariable(Token token) {
+  }
+
+  void beginTypeVariables(Token token) {
+  }
+
+  void endTypeVariables(int count, Token beginToken, Token endToken) {
+  }
+
+  void beginUnamedFunction(Token token) {
+  }
+
+  void endUnamedFunction(Token token) {
+  }
+
+  void beginVariablesDeclaration(Token token) {
+  }
+
+  void endVariablesDeclaration(int count, Token endToken) {
+  }
+
+  void beginWhileStatement(Token token) {
+  }
+
+  void endWhileStatement(Token whileKeyword, Token endToken) {
+  }
+
+  void handleAssignmentExpression(Token token) {
+  }
+
+  void handleBinaryExpression(Token token) {
+  }
+
+  void handleConditionalExpression(Token question, Token colon) {
+  }
+
+  void handleConstExpression(Token token, bool named) {
+  }
+
+  void handleFunctionTypedFormalParameter(Token token) {
+  }
+
+  void handleIdentifier(Token token) {
+  }
+
+  void handleIndexedExpression(Token openCurlyBracket,
+                               Token closeCurlyBracket) {
+  }
+
+  void handleIsOperator(Token operathor, Token not, Token endToken) {
+    // TODO(ahe): Rename [operathor] to "operator" when VM bug is fixed.
+  }
+
+  void handleLiteralBool(Token token) {
+  }
+
+  void handleBreakStatement(bool hasTarget,
+                            Token breakKeyword, Token endToken) {
+  }
+
+  void handleContinueStatement(bool hasTarget,
+                               Token continueKeyword, Token endToken) {
+  }
+
+  void handleEmptyStatement(Token token) {
+  }
+
+  /** Called with either the token containing a double literal, or
+    * an immediately preceding "unary plus" token.
+    */
+  void handleLiteralDouble(Token token) {
+  }
+
+  /** Called with either the token containing an integer literal,
+    * or an immediately preceding "unary plus" token.
+    */
+  void handleLiteralInt(Token token) {
+  }
+
+  void handleLiteralList(int count, Token beginToken, Token constKeyword,
+                         Token endToken) {
+  }
+
+  void handleLiteralMap(int count, Token beginToken, Token constKeyword,
+                        Token endToken) {
+  }
+
+  void handleLiteralNull(Token token) {
+  }
+
+  void handleModifier(Token token) {
+  }
+
+  void handleModifiers(int count) {
+  }
+
+  void handleNamedArgument(Token colon) {
+  }
+
+  void handleNewExpression(Token token, bool named) {
+  }
+
+  void handleNoArguments(Token token) {
+  }
+
+  void handleNoExpression(Token token) {
+  }
+
+  void handleNoType(Token token) {
+  }
+
+  void handleNoTypeVariables(Token token) {
+  }
+
+  void handleOperatorName(Token operatorKeyword, Token token) {
+  }
+
+  void handleParenthesizedExpression(BeginGroupToken token) {
+  }
+
+  void handleQualified(Token period) {
+  }
+
+  void handleStringPart(Token token) {
+  }
+
+  void handleSuperExpression(Token token) {
+  }
+
+  void handleSwitchCase(Token labelToken, int expressionCount,
+                        Token defaultKeyword, int statementCount,
+                        Token firstToken, Token endToken) {
+  }
+
+  void handleThisExpression(Token token) {
+  }
+
+  void handleUnaryPostfixAssignmentExpression(Token token) {
+  }
+
+  void handleUnaryPrefixExpression(Token token) {
+  }
+
+  void handleUnaryPrefixAssignmentExpression(Token token) {
+  }
+
+  void handleValuedFormalParameter(Token equals, Token token) {
+  }
+
+  void handleVoidKeyword(Token token) {
+  }
+
+  Token expected(String string, Token token) {
+    error("expected '$string', but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  void expectedIdentifier(Token token) {
+    error("expected identifier, but got '${token.slowToString()}'", token);
+  }
+
+  Token expectedType(Token token) {
+    error("expected a type, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token expectedExpression(Token token) {
+    error("expected an expression, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token unexpected(Token token) {
+    error("unexpected token '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token expectedBlockToSkip(Token token) {
+    error("expected a block, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token expectedFunctionBody(Token token) {
+    error("expected a function body, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token expectedClassBody(Token token) {
+    error("expected a class body, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  Token expectedClassBodyToSkip(Token token) {
+    error("expected a class body, but got '${token.slowToString()}'", token);
+    return skipToEof(token);
+  }
+
+  skipToEof(Token token) {
+    while (token.info !== EOF_INFO) {
+      token = token.next;
+    }
+    return token;
+  }
+
+  void recoverableError(String message, [Token token, Node node]) {
+    if (token === null && node !== null) {
+      token = node.getBeginToken();
+    }
+    error(message, token);
+  }
+
+  void error(String message, Token token) {
+    throw new ParserError("$message @ ${token.charOffset}");
+  }
+}
+
+class ParserError {
+  final String reason;
+  ParserError(this.reason);
+  toString() => reason;
+}
+
+/**
+ * A listener for parser events.
+ */
+class ElementListener extends Listener {
+  final DiagnosticListener listener;
+  final CompilationUnitElement compilationUnitElement;
+  final StringValidator stringValidator;
+  Link<StringQuoting> interpolationScope;
+
+  Link<Node> nodes = const EmptyLink<Node>();
+
+  ElementListener(DiagnosticListener listener,
+                  CompilationUnitElement this.compilationUnitElement)
+      : this.listener = listener,
+        stringValidator = new StringValidator(listener),
+        interpolationScope = const EmptyLink<StringQuoting>();
+
+  void pushQuoting(StringQuoting quoting) {
+    interpolationScope = interpolationScope.prepend(quoting);
+  }
+
+  StringQuoting popQuoting() {
+    StringQuoting result = interpolationScope.head;
+    interpolationScope = interpolationScope.tail;
+    return result;
+  }
+
+  LiteralString popLiteralString() {
+    StringNode node = popNode();
+    // TODO(lrn): Handle interpolations in script tags.
+    if (node.isInterpolation) {
+      listener.cancel("String interpolation not supported in library tags",
+                      node: node);
+      return null;
+    }
+    return node;
+  }
+
+  void endScriptTag(bool hasPrefix, Token beginToken, Token endToken) {
+    StringNode prefix = null;
+    Identifier argumentName = null;
+    if (hasPrefix) {
+      prefix = popLiteralString();
+      argumentName = popNode();
+    }
+    StringNode firstArgument = popLiteralString();
+    Identifier tag = popNode();
+    compilationUnitElement.addTag(new ScriptTag(tag, firstArgument,
+                                                argumentName, prefix,
+                                                beginToken, endToken),
+                                  listener);
+  }
+
+  void endClassDeclaration(int interfacesCount, Token beginToken,
+                           Token extendsKeyword, Token implementsKeyword,
+                           Token endToken) {
+    SourceString nativeName = native.checkForNativeClass(this);
+    NodeList interfaces =
+        makeNodeList(interfacesCount, implementsKeyword, null, ",");
+    TypeAnnotation supertype = popNode();
+    NodeList typeParameters = popNode();
+    Identifier name = popNode();
+    ClassElement element = new PartialClassElement(
+        name.source, beginToken, endToken, compilationUnitElement);
+    element.nativeName = nativeName;
+    pushElement(element);
+  }
+
+  void endDefaultClause(Token defaultKeyword) {
+    NodeList typeParameters = popNode();
+    Node name = popNode();
+    pushNode(new TypeAnnotation(name, typeParameters));
+  }
+
+  void handleNoDefaultClause(Token token) {
+    pushNode(null);
+  }
+
+  void endInterface(int supertypeCount, Token interfaceKeyword,
+                    Token extendsKeyword, Token endToken) {
+    // TODO(ahe): Record the defaultClause.
+    Node defaultClause = popNode();
+    NodeList supertypes =
+        makeNodeList(supertypeCount, extendsKeyword, null, ",");
+    NodeList typeParameters = popNode();
+    Identifier name = popNode();
+    pushElement(new PartialClassElement(name.source, interfaceKeyword,
+                                        endToken,
+                                        compilationUnitElement));
+  }
+
+  void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
+    NodeList typeVariables = popNode(); // TOOD(karlklose): do not throw away.
+    Identifier name = popNode();
+    TypeAnnotation returnType = popNode();
+    pushElement(new TypedefElement(name.source, compilationUnitElement,
+                                   typedefKeyword));
+  }
+
+  void handleVoidKeyword(Token token) {
+    pushNode(new TypeAnnotation(new Identifier(token), null));
+  }
+
+  void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) {
+    Identifier name = popNode();
+    Modifiers modifiers = popNode();
+    ElementKind kind;
+    if (getOrSet === null) {
+      kind = ElementKind.FUNCTION;
+    } else if (getOrSet.stringValue === 'get') {
+      kind = ElementKind.GETTER;
+    } else if (getOrSet.stringValue === 'set') {
+      kind = ElementKind.SETTER;
+    }
+    pushElement(new PartialFunctionElement(name.source, beginToken, getOrSet,
+                                           endToken, kind,
+                                           modifiers, compilationUnitElement));
+  }
+
+  void endTopLevelFields(int count, Token beginToken, Token endToken) {
+    void buildFieldElement(SourceString name, Element fields) {
+      pushElement(new VariableElement(
+          name, fields, ElementKind.FIELD, compilationUnitElement));
+    }
+    NodeList variables = makeNodeList(count, null, null, ",");
+    Modifiers modifiers = popNode();
+    buildFieldElements(modifiers, variables, compilationUnitElement,
+                       buildFieldElement,
+                       beginToken, endToken);
+  }
+
+  void buildFieldElements(Modifiers modifiers,
+                          NodeList variables,
+                          ContainerElement enclosingElement,
+                          void buildFieldElement(SourceString name,
+                                                 Element fields),
+                          Token beginToken, Token endToken) {
+    Element fields = new PartialFieldListElement(beginToken,
+                                                 endToken,
+                                                 modifiers,
+                                                 enclosingElement);
+    for (Link<Node> nodes = variables.nodes; !nodes.isEmpty();
+         nodes = nodes.tail) {
+      Expression initializedIdentifier = nodes.head;
+      Identifier identifier = initializedIdentifier.asIdentifier();
+      if (identifier === null) {
+        identifier = initializedIdentifier.asSendSet().selector.asIdentifier();
+      }
+      SourceString name = identifier.source;
+      buildFieldElement(name, fields);
+    }
+  }
+
+  void handleIdentifier(Token token) {
+    pushNode(new Identifier(token));
+  }
+
+  void handleQualified(Token period) {
+    Identifier last = popNode();
+    Identifier first = popNode();
+    pushNode(new Send(first, last));
+  }
+
+  void handleNoType(Token token) {
+    pushNode(null);
+  }
+
+  void endTypeVariable(Token token) {
+    TypeAnnotation bound = popNode();
+    Identifier name = popNode();
+    pushNode(new TypeVariable(name, bound));
+  }
+
+  void endTypeVariables(int count, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, endToken, ','));
+  }
+
+  void handleNoTypeVariables(token) {
+    pushNode(null);
+  }
+
+  void endTypeArguments(int count, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, endToken, ','));
+  }
+
+  void handleNoTypeArguments(Token token) {
+    pushNode(null);
+  }
+
+  void endType(Token beginToken, Token endToken) {
+    NodeList typeArguments = popNode();
+    Expression typeName = popNode();
+    pushNode(new TypeAnnotation(typeName, typeArguments));
+  }
+
+  void handleParenthesizedExpression(BeginGroupToken token) {
+    Expression expression = popNode();
+    pushNode(new ParenthesizedExpression(expression, token));
+  }
+
+  void handleModifier(Token token) {
+    pushNode(new Identifier(token));
+  }
+
+  void handleModifiers(int count) {
+    NodeList nodes = makeNodeList(count, null, null, null);
+    pushNode(new Modifiers(nodes));
+  }
+
+  Token expected(String string, Token token) {
+    listener.cancel("expected '$string', but got '${token.slowToString()}'",
+                    token: token);
+    return skipToEof(token);
+  }
+
+  void expectedIdentifier(Token token) {
+    listener.cancel("expected identifier, but got '${token.slowToString()}'",
+                    token: token);
+    pushNode(null);
+  }
+
+  Token expectedType(Token token) {
+    listener.cancel("expected a type, but got '${token.slowToString()}'",
+                    token: token);
+    pushNode(null);
+    return skipToEof(token);
+  }
+
+  Token expectedExpression(Token token) {
+    listener.cancel("expected an expression, but got '${token.slowToString()}'",
+                    token: token);
+    pushNode(null);
+    return skipToEof(token);
+  }
+
+  Token unexpected(Token token) {
+    listener.cancel("unexpected token '${token.slowToString()}'", token: token);
+    return skipToEof(token);
+  }
+
+  Token expectedBlockToSkip(Token token) {
+    if (token.stringValue === 'native') {
+      return native.handleNativeBlockToSkip(this, token);
+    } else {
+      return unexpected(token);
+    }
+  }
+
+  Token expectedFunctionBody(Token token) {
+    String printString = token.slowToString();
+    listener.cancel("expected a function body, but got '$printString'",
+                    token: token);
+    return skipToEof(token);
+  }
+
+  Token expectedClassBody(Token token) {
+    listener.cancel("expected a class body, but got '${token.slowToString()}'",
+                    token: token);
+    return skipToEof(token);
+  }
+
+  Token expectedClassBodyToSkip(Token token) {
+    if (token.stringValue === 'native') {
+      return native.handleNativeClassBodyToSkip(this, token);
+    } else {
+      return unexpected(token);
+    }
+  }
+
+  void recoverableError(String message, [Token token, Node node]) {
+    listener.cancel(message, token: token, node: node);
+  }
+
+  void pushElement(Element element) {
+    compilationUnitElement.addMember(element, listener);
+  }
+
+  void pushNode(Node node) {
+    nodes = nodes.prepend(node);
+    if (VERBOSE) log("push $nodes");
+  }
+
+  Node popNode() {
+    assert(!nodes.isEmpty());
+    Node node = nodes.head;
+    nodes = nodes.tail;
+    if (VERBOSE) log("pop $nodes");
+    return node;
+  }
+
+  Node peekNode() {
+    assert(!nodes.isEmpty());
+    Node node = nodes.head;
+    if (VERBOSE) log("peek $node");
+    return node;
+  }
+
+  void log(message) {
+    print(message);
+  }
+
+  NodeList makeNodeList(int count, Token beginToken, Token endToken,
+                        String delimiter) {
+    Link<Node> nodes = const EmptyLink<Node>();
+    for (; count > 0; --count) {
+      // This effectively reverses the order of nodes so they end up
+      // in correct (source) order.
+      nodes = nodes.prepend(popNode());
+    }
+    SourceString sourceDelimiter =
+        (delimiter === null) ? null : new SourceString(delimiter);
+    return new NodeList(beginToken, nodes, endToken, sourceDelimiter);
+  }
+
+  void beginLiteralString(Token token) {
+    SourceString source = token.value;
+    StringQuoting quoting = StringValidator.quotingFromString(source);
+    pushQuoting(quoting);
+    // Just wrap the token for now. At the end of the interpolation,
+    // when we know how many there are, go back and validate the tokens.
+    pushNode(new LiteralString(token, null));
+  }
+
+  void handleStringPart(Token token) {
+    // Just push an unvalidated token now, and replace it when we know the
+    // end of the interpolation.
+    pushNode(new LiteralString(token, null));
+  }
+
+  void endLiteralString(int count) {
+    StringQuoting quoting = popQuoting();
+
+    Link<StringInterpolationPart> parts =
+        const EmptyLink<StringInterpolationPart>();
+    // Parts of the string interpolation are popped in reverse order,
+    // starting with the last literal string part.
+    bool isLast = true;
+    for (int i = 0; i < count; i++) {
+      LiteralString string = popNode();
+      DartString validation =
+          stringValidator.validateInterpolationPart(string.token, quoting,
+                                                    isFirst: false,
+                                                    isLast: isLast);
+      // Replace the unvalidated LiteralString with a new LiteralString
+      // object that has the validation result included.
+      string = new LiteralString(string.token, validation);
+      Expression expression = popNode();
+      parts = parts.prepend(new StringInterpolationPart(expression, string));
+      isLast = false;
+    }
+
+    LiteralString string = popNode();
+    DartString validation =
+        stringValidator.validateInterpolationPart(string.token, quoting,
+                                                  isFirst: true,
+                                                  isLast: isLast);
+    string = new LiteralString(string.token, validation);
+    if (isLast) {
+      pushNode(string);
+    } else {
+      NodeList nodes = new NodeList(null, parts, null, null);
+      pushNode(new StringInterpolation(string, nodes));
+    }
+  }
+
+  void handleStringJuxtaposition(int stringCount) {
+    assert(stringCount != 0);
+    Expression accumulator = popNode();
+    stringCount--;
+    while (stringCount > 0) {
+      Expression expression = popNode();
+      accumulator = new StringJuxtaposition(expression, accumulator);
+      stringCount--;
+    }
+    pushNode(accumulator);
+  }
+}
+
+class NodeListener extends ElementListener {
+  NodeListener(DiagnosticListener listener, CompilationUnitElement element)
+    : super(listener, element);
+
+  void endClassDeclaration(int interfacesCount, Token beginToken,
+                           Token extendsKeyword, Token implementsKeyword,
+                           Token endToken) {
+    NodeList body = popNode();
+    NodeList interfaces =
+        makeNodeList(interfacesCount, implementsKeyword, null, ",");
+    TypeAnnotation supertype = popNode();
+    NodeList typeParameters = popNode();
+    Identifier name = popNode();
+    pushNode(new ClassNode(name, typeParameters, supertype, interfaces, null,
+                           beginToken, extendsKeyword, endToken));
+  }
+
+  void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
+    NodeList formals = popNode();
+    NodeList typeParameters = null; // TODO(ahe): Don't discard these.
+    Identifier name = popNode();
+    TypeAnnotation returnType = popNode();
+    pushNode(new Typedef(returnType, name, typeParameters, formals,
+                         typedefKeyword, endToken));
+  }
+
+  void endInterface(int supertypeCount, Token interfaceKeyword,
+                    Token extendsKeyword, Token endToken) {
+    NodeList body = popNode();
+    TypeAnnotation defaultClause = popNode();
+    NodeList supertypes = makeNodeList(supertypeCount, extendsKeyword,
+                                       null, ',');
+    NodeList typeParameters = popNode();
+    Identifier name = popNode();
+    pushNode(new ClassNode(name, typeParameters, null, supertypes,
+                           defaultClause, interfaceKeyword, null, endToken));
+  }
+
+  void endClassBody(int memberCount, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(memberCount, beginToken, endToken, null));
+  }
+
+  void endTopLevelFields(int count, Token beginToken, Token endToken) {
+    NodeList variables = makeNodeList(count, null, null, ",");
+    Modifiers modifiers = popNode();
+    pushNode(new VariableDefinitions(null, modifiers, variables, endToken));
+  }
+
+  void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) {
+    Statement body = popNode();
+    NodeList formalParameters = popNode();
+    Identifier name = popNode();
+    Modifiers modifiers = popNode();
+    ElementKind kind;
+    if (getOrSet === null) {
+      kind = ElementKind.FUNCTION;
+    } else if (getOrSet.stringValue === 'get') {
+      kind = ElementKind.GETTER;
+    } else if (getOrSet.stringValue === 'set') {
+      kind = ElementKind.SETTER;
+    }
+    pushElement(new PartialFunctionElement(name.source, beginToken, getOrSet,
+                                           endToken, kind,
+                                           modifiers, compilationUnitElement));
+  }
+
+  void endFormalParameter(Token token, Token thisKeyword) {
+    Expression name = popNode();
+    if (thisKeyword !== null) {
+      Identifier thisIdentifier = new Identifier(thisKeyword);
+      if (name.asSend() === null) {
+        name = new Send(thisIdentifier, name);
+      } else {
+        name = name.asSend().copyWithReceiver(thisIdentifier);
+      }
+    }
+    TypeAnnotation type = popNode();
+    Modifiers modifiers = popNode();
+    pushNode(new VariableDefinitions(type, modifiers,
+                                     new NodeList.singleton(name), token));
+  }
+
+  void endFormalParameters(int count, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, endToken, ","));
+  }
+
+  void endArguments(int count, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, endToken, ","));
+  }
+
+  void handleNoArguments(Token token) {
+    pushNode(null);
+  }
+
+  void endReturnStatement(bool hasExpression,
+                          Token beginToken, Token endToken) {
+    Expression expression = hasExpression ? popNode() : null;
+    pushNode(new Return(beginToken, endToken, expression));
+  }
+
+  void endExpressionStatement(Token token) {
+    pushNode(new ExpressionStatement(popNode(), token));
+  }
+
+  void handleOnError(Token token, var error) {
+    listener.cancel("internal error: '${token.value}': ${error}", token: token);
+  }
+
+  Token expectedFunctionBody(Token token) {
+    if (token.stringValue === 'native') {
+      return native.handleNativeFunctionBody(this, token);
+    } else {
+      listener.cancel(
+          "expected a function body, but got '${token.slowToString()}'",
+          token: token);
+      return skipToEof(token);
+    }
+  }
+
+  Token expectedClassBody(Token token) {
+    if (token.stringValue === 'native') {
+      return native.handleNativeClassBody(this, token);
+    } else {
+      listener.cancel(
+          "expected a class body, but got '${token.slowToString()}'",
+          token: token);
+      return skipToEof(token);
+    }
+  }
+
+  void handleLiteralInt(Token token) {
+    pushNode(new LiteralInt(token, (t, e) => handleOnError(t, e)));
+  }
+
+  void handleLiteralDouble(Token token) {
+    pushNode(new LiteralDouble(token, (t, e) => handleOnError(t, e)));
+  }
+
+  void handleLiteralBool(Token token) {
+    pushNode(new LiteralBool(token, (t, e) => handleOnError(t, e)));
+  }
+
+  void handleLiteralNull(Token token) {
+    pushNode(new LiteralNull(token));
+  }
+
+  void handleBinaryExpression(Token token) {
+    Node argument = popNode();
+    Node receiver = popNode();
+    if (token.stringValue === '.') {
+      if (argument is !Send) internalError(node: argument);
+      if (argument.asSend().receiver !== null) internalError(node: argument);
+      if (argument is SendSet) internalError(node: argument);
+      pushNode(argument.asSend().copyWithReceiver(receiver));
+    } else {
+      NodeList arguments = new NodeList.singleton(argument);
+      pushNode(new Send(receiver, new Operator(token), arguments));
+    }
+  }
+
+  void handleAssignmentExpression(Token token) {
+    Node arg = popNode();
+    Node node = popNode();
+    Send send = node.asSend();
+    if (send === null) internalError(node: node);
+    if (!(send.isPropertyAccess || send.isIndex)) internalError(node: send);
+    if (send.asSendSet() !== null) internalError(node: send);
+    NodeList arguments;
+    if (send.isIndex) {
+      Link<Node> link = new Link<Node>(arg);
+      link = link.prepend(send.arguments.head);
+      arguments = new NodeList(null, link);
+    } else {
+      arguments = new NodeList.singleton(arg);
+    }
+    Operator op = new Operator(token);
+    pushNode(new SendSet(send.receiver, send.selector, op, arguments));
+  }
+
+  void handleConditionalExpression(Token question, Token colon) {
+    Node elseExpression = popNode();
+    Node thenExpression = popNode();
+    Node condition = popNode();
+    pushNode(new Conditional(
+        condition, thenExpression, elseExpression, question, colon));
+  }
+
+  void endSend(Token token) {
+    NodeList arguments = popNode();
+    Node selector = popNode();
+    // TODO(ahe): Handle receiver.
+    pushNode(new Send(null, selector, arguments));
+  }
+
+  void endFunctionBody(int count, Token beginToken, Token endToken) {
+    pushNode(new Block(makeNodeList(count, beginToken, endToken, null)));
+  }
+
+  void handleNoFunctionBody(Token token) {
+    pushNode(null);
+  }
+
+  void endFunction(Token getOrSet, Token endToken) {
+    Statement body = popNode();
+    NodeList initializers = popNode();
+    NodeList formals = popNode();
+    // The name can be an identifier or a send in case of named constructors.
+    Expression name = popNode();
+    TypeAnnotation type = popNode();
+    Modifiers modifiers = popNode();
+    pushNode(new FunctionExpression(name, formals, body, type,
+                                    modifiers, initializers, getOrSet));
+  }
+
+  void endFunctionDeclaration(Token endToken) {
+    pushNode(new FunctionDeclaration(popNode()));
+  }
+
+  void endVariablesDeclaration(int count, Token endToken) {
+    // TODO(ahe): Pick one name for this concept, either
+    // VariablesDeclaration or VariableDefinitions.
+    NodeList variables = makeNodeList(count, null, null, ",");
+    TypeAnnotation type = popNode();
+    Modifiers modifiers = popNode();
+    pushNode(new VariableDefinitions(type, modifiers, variables, endToken));
+  }
+
+  void endInitializer(Token assignmentOperator) {
+    Expression initializer = popNode();
+    NodeList arguments = new NodeList.singleton(initializer);
+    Expression name = popNode();
+    Operator op = new Operator(assignmentOperator);
+    pushNode(new SendSet(null, name, op, arguments));
+  }
+
+  void endIfStatement(Token ifToken, Token elseToken) {
+    Statement elsePart = (elseToken === null) ? null : popNode();
+    Statement thenPart = popNode();
+    ParenthesizedExpression condition = popNode();
+    pushNode(new If(condition, thenPart, elsePart, ifToken, elseToken));
+  }
+
+  void endForStatement(int updateExpressionCount,
+                       Token beginToken, Token endToken) {
+    Statement body = popNode();
+    NodeList updates = makeNodeList(updateExpressionCount, null, null, ',');
+    Statement condition = popNode();
+    Node initializer = popNode();
+    pushNode(new For(initializer, condition, updates, body, beginToken));
+  }
+
+  void handleNoExpression(Token token) {
+    pushNode(null);
+  }
+
+  void endDoWhileStatement(Token doKeyword, Token whileKeyword,
+                           Token endToken) {
+    Expression condition = popNode();
+    Statement body = popNode();
+    pushNode(new DoWhile(body, condition, doKeyword, whileKeyword, endToken));
+  }
+
+  void endWhileStatement(Token whileKeyword, Token endToken) {
+    Statement body = popNode();
+    Expression condition = popNode();
+    pushNode(new While(condition, body, whileKeyword));
+  }
+
+  void endBlock(int count, Token beginToken, Token endToken) {
+    pushNode(new Block(makeNodeList(count, beginToken, endToken, null)));
+  }
+
+  void endThrowStatement(Token throwToken, Token endToken) {
+    Expression expression = popNode();
+    pushNode(new Throw(expression, throwToken, endToken));
+  }
+
+  void endRethrowStatement(Token throwToken, Token endToken) {
+    pushNode(new Throw(null, throwToken, endToken));
+  }
+
+  void handleUnaryPrefixExpression(Token token) {
+    pushNode(new Send.prefix(popNode(), new Operator(token)));
+  }
+
+  void handleSuperExpression(Token token) {
+    pushNode(new Identifier(token));
+  }
+
+  void handleThisExpression(Token token) {
+    pushNode(new Identifier(token));
+  }
+
+  void handleUnaryAssignmentExpression(Token token, bool isPrefix) {
+    Node node = popNode();
+    Send send = node.asSend();
+    if (send === null) internalError(node: node);
+    if (!(send.isPropertyAccess || send.isIndex)) internalError(node: send);
+    if (send.asSendSet() !== null) internalError(node: send);
+    Node argument = null;
+    if (send.isIndex) argument = send.arguments.head;
+    Operator op = new Operator(token);
+
+    if (isPrefix) {
+      pushNode(new SendSet.prefix(send.receiver, send.selector, op, argument));
+    } else {
+      pushNode(new SendSet.postfix(send.receiver, send.selector, op, argument));
+    }
+  }
+
+  void handleUnaryPostfixAssignmentExpression(Token token) {
+    handleUnaryAssignmentExpression(token, false);
+  }
+
+  void handleUnaryPrefixAssignmentExpression(Token token) {
+    handleUnaryAssignmentExpression(token, true);
+  }
+
+  void endInitializers(int count, Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, null, ','));
+  }
+
+  void handleNoInitializers() {
+    pushNode(null);
+  }
+
+  void endFields(int count, Token beginToken, Token endToken) {
+    NodeList variables = makeNodeList(count, null, null, ",");
+    Modifiers modifiers = popNode();
+    pushNode(new VariableDefinitions(null, modifiers, variables, endToken));
+  }
+
+  void endMethod(Token getOrSet, Token beginToken, Token endToken) {
+    Statement body = popNode();
+    NodeList initializers = popNode();
+    NodeList formalParameters = popNode();
+    Expression name = popNode();
+    Modifiers modifiers = popNode();
+    pushNode(new FunctionExpression(name, formalParameters, body, null,
+                                    modifiers, initializers, getOrSet));
+  }
+
+  void handleLiteralMap(int count, Token beginToken, Token constKeyword,
+                        Token endToken) {
+    NodeList entries = makeNodeList(count, beginToken, endToken, ',');
+    NodeList typeArguments = popNode();
+    pushNode(new LiteralMap(typeArguments, entries));
+  }
+
+  void endLiteralMapEntry(Token colon, Token endToken) {
+    Expression value = popNode();
+    Expression key = popNode();
+    if (key.asLiteralString() === null) {
+      recoverableError('expected a constant string', node: key);
+    }
+    pushNode(new LiteralMapEntry(key, colon, value));
+  }
+
+  void handleLiteralList(int count, Token beginToken, Token constKeyword,
+                         Token endToken) {
+    NodeList elements = makeNodeList(count, beginToken, endToken, ',');
+    NodeList typeArguments = popNode();
+    // TODO(ahe): Type arguments are discarded.
+    pushNode(new LiteralList(null, elements, constKeyword));
+  }
+
+  void handleIndexedExpression(Token openSquareBracket,
+                               Token closeSquareBracket) {
+    NodeList arguments =
+        makeNodeList(1, openSquareBracket, closeSquareBracket, null);
+    Node receiver = popNode();
+    Token token =
+      new StringToken(INDEX_INFO, '[]', openSquareBracket.charOffset);
+    Node selector = new Operator(token);
+    pushNode(new Send(receiver, selector, arguments));
+  }
+
+  void handleNewExpression(Token token, bool named) {
+    NodeList arguments = popNode();
+    Node name = popNode();
+    if (named) {
+      TypeAnnotation type = popNode();
+      name = new Send(type, name);
+    }
+    pushNode(new NewExpression(token, new Send(null, name, arguments)));
+  }
+
+  void handleConstExpression(Token token, bool named) {
+    NodeList arguments = popNode();
+    if (named) {
+      Identifier name = popNode();
+    }
+    TypeAnnotation type = popNode();
+    pushNode(new NewExpression(token, new Send(null, type, arguments)));
+  }
+
+  void handleOperatorName(Token operatorKeyword, Token token) {
+    Operator op = new Operator(token);
+    pushNode(new Send(new Identifier(operatorKeyword), op, null));
+  }
+
+  void handleNamedArgument(Token colon) {
+    Expression expression = popNode();
+    Identifier name = popNode();
+    pushNode(new NamedArgument(name, colon, expression));
+  }
+
+  void endOptionalFormalParameters(int count,
+                                   Token beginToken, Token endToken) {
+    pushNode(makeNodeList(count, beginToken, endToken, ','));
+  }
+
+  void handleFunctionTypedFormalParameter(Token endToken) {
+    NodeList formals = popNode();
+    Identifier name = popNode();
+    TypeAnnotation returnType = popNode();
+    pushNode(null); // Signal "no type" to endFormalParameter.
+    pushNode(new FunctionExpression(name, formals, null, returnType,
+                                    null, null, null));
+  }
+
+  void handleValuedFormalParameter(Token equals, Token token) {
+    Expression defaultValue = popNode();
+    Expression parameterName = popNode();
+    pushNode(new SendSet(null, parameterName, new Operator(equals),
+                         new NodeList.singleton(defaultValue)));
+  }
+
+  void endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) {
+    Block finallyBlock = null;
+    if (finallyKeyword !== null) {
+      finallyBlock = popNode();
+    }
+    NodeList catchBlocks = makeNodeList(catchCount, null, null, null);
+    Block tryBlock = popNode();
+    pushNode(new TryStatement(tryBlock, catchBlocks, finallyBlock,
+                              tryKeyword, finallyKeyword));
+  }
+
+  void handleCatchBlock(Token catchKeyword) {
+    Block block = popNode();
+    NodeList formals = popNode();
+    pushNode(new CatchBlock(formals, block, catchKeyword));
+  }
+
+  void endSwitchStatement(Token switchKeyword, Token endToken) {
+    NodeList cases = popNode();
+    ParenthesizedExpression expression = popNode();
+    pushNode(new SwitchStatement(expression, cases, switchKeyword));
+  }
+
+  void endSwitchBlock(int caseCount, Token beginToken, Token endToken) {
+    Link<Node> nodes = const EmptyLink<Node>();
+    while (caseCount > 0) {
+      SwitchCase switchCase = popNode();
+      nodes = nodes.prepend(switchCase);
+      caseCount--;
+    }
+    pushNode(new NodeList(beginToken, nodes, endToken, null));
+  }
+
+  void handleSwitchCase(Token labelToken, int expressionCount,
+                        Token defaultKeyword, int statementCount,
+                        Token firstToken, Token endToken) {
+    NodeList statements = makeNodeList(statementCount, null, null, null);
+    NodeList expressions = makeNodeList(expressionCount, null, null, null);
+    Identifier label = null;
+    if (labelToken !== null) {
+      label = popNode();
+    }
+    pushNode(new SwitchCase(label, expressions, defaultKeyword, statements,
+                            firstToken));
+  }
+
+  void handleBreakStatement(bool hasTarget,
+                            Token breakKeyword, Token endToken) {
+    Identifier target = null;
+    if (hasTarget) {
+      target = popNode();
+    }
+    pushNode(new BreakStatement(target, breakKeyword, endToken));
+  }
+
+  void handleContinueStatement(bool hasTarget,
+                               Token continueKeyword, Token endToken) {
+    Identifier target = null;
+    if (hasTarget) {
+      target = popNode();
+    }
+    pushNode(new ContinueStatement(target, continueKeyword, endToken));
+  }
+
+  void handleEmptyStatement(Token token) {
+    pushNode(new EmptyStatement(token));
+  }
+
+  void endFactoryMethod(Token factoryKeyword, Token periodBeforeName,
+                        Token endToken) {
+    Statement body = popNode();
+    NodeList formals = popNode();
+    NodeList typeParameters = popNode(); // TODO(karlklose): don't throw away.
+    Node name = popNode();
+    if (periodBeforeName !== null) {
+      // A library prefix was handled in [handleQualified].
+      name = new Send(popNode(), name);
+    }
+    handleModifier(factoryKeyword);
+    handleModifiers(1);
+    Modifiers modifiers = popNode();
+    pushNode(new FunctionExpression(name, formals, body, null,
+                                    modifiers, null, null));
+  }
+
+  void endForInStatement(Token beginToken, Token inKeyword, Token endToken) {
+    Statement body = popNode();
+    Expression expression = popNode();
+    Node declaredIdentifier = popNode();
+    pushNode(new ForInStatement(declaredIdentifier, expression, body,
+                                beginToken, inKeyword));
+  }
+
+  void endUnamedFunction(Token token) {
+    Statement body = popNode();
+    NodeList formals = popNode();
+    pushNode(new FunctionExpression(null, formals, body, null,
+                                    null, null, null));
+  }
+
+  void handleIsOperator(Token operathor, Token not, Token endToken) {
+    TypeAnnotation type = popNode();
+    Expression expression = popNode();
+    Node argument;
+    if (not != null) {
+      argument = new Send.prefix(type, new Operator(not));
+    } else {
+      argument = type;
+    }
+
+    NodeList arguments = new NodeList.singleton(argument);
+    pushNode(new Send(expression, new Operator(operathor), arguments));
+  }
+
+  void endLabeledStatement(Token colon) {
+    Statement statement = popNode();
+    Identifier label = popNode();
+    pushNode(new LabeledStatement(label, colon, statement));
+  }
+
+  void log(message) {
+    listener.log(message);
+  }
+
+  void internalError([Token token, Node node]) {
+    listener.cancel('internal error', token: token, node: node);
+    throw 'internal error';
+  }
+}
+
+class PartialFunctionElement extends FunctionElement {
+  final Token beginToken;
+  final Token getOrSet;
+  final Token endToken;
+
+  PartialFunctionElement(SourceString name,
+                         Token this.beginToken,
+                         Token this.getOrSet,
+                         Token this.endToken,
+                         ElementKind kind,
+                         Modifiers modifiers,
+                         Element enclosing)
+    : super(name, kind, modifiers, enclosing);
+
+  FunctionExpression parseNode(DiagnosticListener listener) {
+    if (cachedNode != null) return cachedNode;
+    cachedNode = parse(listener,
+                       getCompilationUnit(),
+                       (p) => p.parseFunction(beginToken, getOrSet));
+    return cachedNode;
+  }
+
+  Token position() => findMyName(beginToken);
+}
+
+class PartialFieldListElement extends VariableListElement {
+  final Token beginToken;
+  final Token endToken;
+
+  PartialFieldListElement(Token this.beginToken,
+                          Token this.endToken,
+                          Modifiers modifiers,
+                          Element enclosing)
+    : super(ElementKind.VARIABLE_LIST, modifiers, enclosing);
+
+  VariableDefinitions parseNode(DiagnosticListener listener) {
+    if (cachedNode != null) return cachedNode;
+    cachedNode = parse(listener,
+                       getCompilationUnit(),
+                       (p) => p.parseVariablesDeclaration(beginToken));
+    return cachedNode;
+  }
+
+  Token position() => beginToken; // findMyName doesn't work. I'm nameless.
+}
+
+Node parse(DiagnosticListener diagnosticListener,
+           CompilationUnitElement element,
+           doParse(Parser parser)) {
+  NodeListener listener = new NodeListener(diagnosticListener, element);
+  doParse(new Parser(listener));
+  Node node = listener.popNode();
+  assert(listener.nodes.isEmpty());
+  return node;
+}
diff --git a/lib/compiler/implementation/scanner/motile.dart b/lib/compiler/implementation/scanner/motile.dart
new file mode 100644
index 0000000..98f0fda
--- /dev/null
+++ b/lib/compiler/implementation/scanner/motile.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2011, 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('motile'); // aka parser'
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('../elements/elements.dart');
+#import('vm_scanner_bench.dart', prefix: 'vm');
+#import('scanner_bench.dart');
+#source('parser_bench.dart');
+
+class BaseParserBench extends vm.VmScannerBench {
+}
diff --git a/lib/compiler/implementation/scanner/motile_d8.dart b/lib/compiler/implementation/scanner/motile_d8.dart
new file mode 100644
index 0000000..5862e93
--- /dev/null
+++ b/lib/compiler/implementation/scanner/motile_d8.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2011, 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('motile'); // aka parser'
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('../elements/elements.dart');
+#import('d8_scanner_bench.dart', prefix: 'd8');
+#import('scanner_bench.dart');
+#source('parser_bench.dart');
+
+class BaseParserBench extends d8.D8ScannerBench {
+}
diff --git a/lib/compiler/implementation/scanner/motile_node.dart b/lib/compiler/implementation/scanner/motile_node.dart
new file mode 100644
index 0000000..a42676b
--- /dev/null
+++ b/lib/compiler/implementation/scanner/motile_node.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2011, 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('motile'); // aka parser'
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('../elements/elements.dart');
+#import('node_scanner_bench.dart', prefix: 'node');
+#import('scanner_bench.dart');
+#source('parser_bench.dart');
+
+class BaseParserBench extends node.NodeScannerBench {
+}
diff --git a/lib/compiler/implementation/scanner/node_scanner_bench.dart b/lib/compiler/implementation/scanner/node_scanner_bench.dart
new file mode 100644
index 0000000..08d126c
--- /dev/null
+++ b/lib/compiler/implementation/scanner/node_scanner_bench.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2012, 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('node_scanner_bench');
+#import('../../node/node.dart');
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('scanner_bench.dart');
+#import('../../../utf/utf.dart');
+#source('byte_strings.dart');
+#source('byte_array_scanner.dart');
+
+class NodeScannerBench extends ScannerBench {
+  int getBytes(String filename, void callback(bytes)) {
+    // This actually returns a buffer, not a String.
+    var s = fs.readFileSync(filename, null);
+    callback(s);
+    return s.length;
+  }
+
+  Scanner makeScanner(bytes) => new InflexibleByteArrayScanner(bytes);
+
+  void checkExistence(String filename) {
+    if (!path.existsSync(filename)) {
+      throw "no such file: ${filename}";
+    }
+  }
+}
+
+class InflexibleByteArrayScanner extends ByteArrayScanner {
+  InflexibleByteArrayScanner(List<int> bytes) : super(bytes);
+
+  int byteAt(int index) => (bytes.length > index) ? bytes[index] : $EOF;
+
+  int advance() {
+    // This method should be equivalent to the one in super. However,
+    // this is a *HOT* method and V8 performs better if it is easy to
+    // inline.
+    int index = ++byteOffset;
+    int next = (bytes.length > index) ? bytes[index] : $EOF;
+    return next;
+  }
+}
+
+main() {
+  new NodeScannerBench().main(argv);
+}
diff --git a/lib/compiler/implementation/scanner/parser.dart b/lib/compiler/implementation/scanner/parser.dart
new file mode 100644
index 0000000..a85df01
--- /dev/null
+++ b/lib/compiler/implementation/scanner/parser.dart
@@ -0,0 +1,1592 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * An event generating parser of Dart programs. This parser expects
+ * all tokens in a linked list.
+ */
+class Parser {
+  final Listener listener;
+  bool mayParseFunctionExpressions = true;
+
+  Parser(Listener this.listener);
+
+  void parseUnit(Token token) {
+    while (token.kind !== EOF_TOKEN) {
+      token = parseTopLevelDeclaration(token);
+    }
+  }
+
+  Token parseTopLevelDeclaration(Token token) {
+    final String value = token.stringValue;
+    if (value === 'interface') {
+      return parseInterface(token);
+    } else if ((value === 'abstract') || (value === 'class')) {
+      return parseClass(token);
+    } else if (value === 'typedef') {
+      return parseNamedFunctionAlias(token);
+    } else if (value === '#') {
+      return parseScriptTags(token);
+    } else {
+      return parseTopLevelMember(token);
+    }
+  }
+
+  Token parseInterface(Token token) {
+    Token interfaceKeyword = token;
+    listener.beginInterface(token);
+    token = parseIdentifier(token.next);
+    token = parseTypeVariablesOpt(token);
+    int supertypeCount = 0;
+    Token extendsKeyword = null;
+    if (optional('extends', token)) {
+      extendsKeyword = token;
+      do {
+        token = parseType(token.next);
+        ++supertypeCount;
+      } while (optional(',', token));
+    }
+    token = parseDefaultClauseOpt(token);
+    token = parseInterfaceBody(token);
+    listener.endInterface(supertypeCount, interfaceKeyword,
+                          extendsKeyword, token);
+    return token.next;
+  }
+
+  Token parseInterfaceBody(Token token) {
+    return parseClassBody(token);
+  }
+
+  Token parseNamedFunctionAlias(Token token) {
+    Token typedefKeyword = token;
+    listener.beginFunctionTypeAlias(token);
+    token = parseReturnTypeOpt(token.next);
+    token = parseIdentifier(token);
+    token = parseTypeVariablesOpt(token);
+    token = parseFormalParameters(token);
+    listener.endFunctionTypeAlias(typedefKeyword, token);
+    return expect(';', token);
+  }
+
+  Token parseReturnTypeOpt(Token token) {
+    if (token.stringValue === 'void') {
+      listener.handleVoidKeyword(token);
+      return token.next;
+    } else {
+      return parseTypeOpt(token);
+    }
+  }
+
+  Token parseFormalParameters(Token token) {
+    Token begin = token;
+    listener.beginFormalParameters(begin);
+    expect('(', token);
+    int parameterCount = 0;
+    if (optional(')', token.next)) {
+      listener.endFormalParameters(parameterCount, begin, token.next);
+      return token.next.next;
+    }
+    do {
+      ++parameterCount;
+      token = token.next;
+      if (optional('[', token)) {
+        token = parseOptionalFormalParameters(token);
+        break;
+      }
+      token = parseFormalParameter(token);
+    } while (optional(',', token));
+    listener.endFormalParameters(parameterCount, begin, token);
+    return expect(')', token);
+  }
+
+  Token parseFormalParameter(Token token) {
+    listener.beginFormalParameter(token);
+    token = parseModifiers(token);
+    // TODO(ahe): Validate that there are formal parameters if void.
+    token = parseReturnTypeOpt(token);
+    Token thisKeyword = null;
+    if (optional('this', token)) {
+      thisKeyword = token;
+      // TODO(ahe): Validate field initializers are only used in
+      // constructors, and not for function-typed arguments.
+      token = expect('.', token.next);
+    }
+    token = parseIdentifier(token);
+    if (optional('(', token)) {
+      token = parseFormalParameters(token);
+      listener.handleFunctionTypedFormalParameter(token);
+    }
+    if (optional('=', token)) {
+      // TODO(ahe): Validate that these are only used for optional parameters.
+      Token equal = token;
+      token = parseExpression(token.next);
+      listener.handleValuedFormalParameter(equal, token);
+    }
+    listener.endFormalParameter(token, thisKeyword);
+    return token;
+  }
+
+  Token parseOptionalFormalParameters(Token token) {
+    Token begin = token;
+    listener.beginOptionalFormalParameters(begin);
+    assert(optional('[', token));
+    int parameterCount = 0;
+    do {
+      token = token.next;
+      token = parseFormalParameter(token);
+      ++parameterCount;
+    } while (optional(',', token));
+    listener.endOptionalFormalParameters(parameterCount, begin, token);
+    return expect(']', token);
+  }
+
+  Token parseTypeOpt(Token token) {
+    String value = token.stringValue;
+    if (value === 'var') return parseType(token);
+    if (value !== 'this') {
+      Token peek = peekAfterType(token);
+      if (isIdentifier(peek) || optional('this', peek)) {
+        return parseType(token);
+      }
+    }
+    listener.handleNoType(token);
+    return token;
+  }
+
+  bool isIdentifier(Token token) {
+    final kind = token.kind;
+    if (kind === IDENTIFIER_TOKEN) return true;
+    if (kind === KEYWORD_TOKEN) return token.value.isPseudo;
+    return false;
+  }
+
+  Token parseDefaultClauseOpt(Token token) {
+    if (isDefaultKeyword(token)) {
+      // TODO(ahe): Remove support for 'factory' in this position.
+      Token defaultKeyword = token;
+      listener.beginDefaultClause(defaultKeyword);
+      token = parseIdentifier(token.next);
+      token = parseQualifiedRestOpt(token);
+      token = parseTypeVariablesOpt(token);
+      listener.endDefaultClause(defaultKeyword);
+    } else {
+      listener.handleNoDefaultClause(token);
+    }
+    return token;
+  }
+
+  Token parseQualifiedRestOpt(Token token) {
+    if (optional('.', token)) {
+      Token period = token;
+      token = parseIdentifier(token.next);
+      listener.handleQualified(period);
+    }
+    return token;
+  }
+
+  bool isDefaultKeyword(Token token) {
+    String value = token.stringValue;
+    if (value === 'default') return true;
+    if (value === 'factory') {
+      listener.recoverableError("expected 'default'", token: token);
+      return true;
+    }
+    return false;
+  }
+
+  Token skipBlock(Token token) {
+    if (!optional('{', token)) {
+      return listener.expectedBlockToSkip(token);
+    }
+    BeginGroupToken beginGroupToken = token;
+    assert(beginGroupToken.endGroup === null ||
+           beginGroupToken.endGroup.kind === $CLOSE_CURLY_BRACKET);
+    return beginGroupToken.endGroup;
+  }
+
+  Token parseClass(Token token) {
+    Token begin = token;
+    listener.beginClassDeclaration(token);
+    if (optional('abstract', token)) {
+      // TODO(ahe): Notify listener about abstract modifier.
+      token = token.next;
+    }
+    token = parseIdentifier(token.next);
+    token = parseTypeVariablesOpt(token);
+    Token extendsKeyword;
+    if (optional('extends', token)) {
+      extendsKeyword = token;
+      token = parseType(token.next);
+    } else {
+      extendsKeyword = null;
+      listener.handleNoType(token);
+    }
+    Token implementsKeyword;
+    int interfacesCount = 0;
+    if (optional('implements', token)) {
+      do {
+        token = parseType(token.next);
+        ++interfacesCount;
+      } while (optional(',', token));
+    }
+    token = parseClassBody(token);
+    listener.endClassDeclaration(interfacesCount, begin, extendsKeyword,
+                                 implementsKeyword, token);
+    return token.next;
+  }
+
+
+  Token parseStringPart(Token token) {
+    if (token.kind === STRING_TOKEN) {
+      listener.handleStringPart(token);
+      return token.next;
+    } else {
+      return listener.expected('string', token);
+    }
+  }
+
+  Token parseIdentifier(Token token) {
+    if (isIdentifier(token)) {
+      listener.handleIdentifier(token);
+    } else {
+      listener.expectedIdentifier(token);
+    }
+    return token.next;
+  }
+
+  Token expect(String string, Token token) {
+    if (string !== token.stringValue) {
+      if (string === '>') {
+        if (token.stringValue === '>>') {
+          Token gt = new Token(GT_INFO, token.charOffset + 1);
+          gt.next = token.next;
+          return gt;
+        } else if (token.stringValue === '>>>') {
+          Token gtgt = new Token(GT_GT_INFO, token.charOffset + 1);
+          gtgt.next = token.next;
+          return gtgt;
+        }
+      }
+      return listener.expected(string, token);
+    }
+    return token.next;
+  }
+
+  Token parseTypeVariable(Token token) {
+    listener.beginTypeVariable(token);
+    token = parseIdentifier(token);
+    if (optional('extends', token)) {
+      token = parseType(token.next);
+    } else {
+      listener.handleNoType(token);
+    }
+    listener.endTypeVariable(token);
+    return token;
+  }
+
+  bool optional(String value, Token token) => value === token.stringValue;
+
+  bool notEofOrValue(String value, Token token) {
+    return token.kind !== EOF_TOKEN && value !== token.stringValue;
+  }
+
+  Token parseType(Token token) {
+    Token begin = token;
+    if (isIdentifier(token)) {
+      token = parseIdentifier(token);
+      token = parseQualifiedRestOpt(token);
+    } else {
+      token = listener.expectedType(token);
+    }
+    token = parseTypeArgumentsOpt(token);
+    listener.endType(begin, token);
+    return token;
+  }
+
+  Token parseTypeArgumentsOpt(Token token) {
+    return parseStuff(token,
+                      (t) => listener.beginTypeArguments(t),
+                      (t) => parseType(t),
+                      (c, bt, et) => listener.endTypeArguments(c, bt, et),
+                      (t) => listener.handleNoTypeArguments(t));
+  }
+
+  Token parseTypeVariablesOpt(Token token) {
+    return parseStuff(token,
+                      (t) => listener.beginTypeVariables(t),
+                      (t) => parseTypeVariable(t),
+                      (c, bt, et) => listener.endTypeVariables(c, bt, et),
+                      (t) => listener.handleNoTypeVariables(t));
+  }
+
+  // TODO(ahe): Clean this up.
+  Token parseStuff(Token token, Function beginStuff, Function stuffParser,
+                   Function endStuff, Function handleNoStuff) {
+    if (optional('<', token)) {
+      Token begin = token;
+      beginStuff(begin);
+      int count = 0;
+      do {
+        token = stuffParser(token.next);
+        ++count;
+      } while (optional(',', token));
+      endStuff(count, begin, token);
+      return expect('>', token);
+    }
+    handleNoStuff(token);
+    return token;
+  }
+
+  Token parseTopLevelMember(Token token) {
+    Token start = token;
+    listener.beginTopLevelMember(token);
+    token = parseModifiers(token);
+    Token getOrSet = findGetOrSet(token);
+    if (token === getOrSet) token = token.next;
+    Token peek = peekAfterType(token);
+    if (isIdentifier(peek)) {
+      // Skip type.
+      token = peek;
+    }
+    if (token === getOrSet) token = token.next;
+    token = parseIdentifier(token);
+    bool isField;
+    while (true) {
+      // Loop to allow the listener to rewrite the token stream for
+      // error handling.
+      final String value = token.stringValue;
+      if (value === '(') {
+        isField = false;
+        break;
+      } else if ((value === '=') || (value === ';') || (value === ',')) {
+        isField = true;
+        break;
+      } else {
+        token = listener.unexpected(token);
+        if (token.kind === EOF_TOKEN) {
+          // TODO(ahe): This is a hack. It would be better to tell the
+          // listener more explicitly that it must pop an identifier.
+          listener.endTopLevelFields(1, start, token);
+          return token;
+        }
+      }
+    }
+    if (isField) {
+      int fieldCount = 1;
+      token = parseVariableInitializerOpt(token);
+      while (optional(',', token)) {
+        token = parseIdentifier(token.next);
+        token = parseVariableInitializerOpt(token);
+        ++fieldCount;
+      }
+      expectSemicolon(token);
+      listener.endTopLevelFields(fieldCount, start, token);
+    } else {
+      token = parseFormalParameters(token);
+      token = parseFunctionBody(token, false);
+      listener.endTopLevelMethod(start, getOrSet, token);
+    }
+    return token.next;
+  }
+
+  Token parseVariableInitializerOpt(Token token) {
+    if (optional('=', token)) {
+      Token assignment = token;
+      listener.beginInitializer(token);
+      token = parseExpression(token.next);
+      listener.endInitializer(assignment);
+    }
+    return token;
+  }
+
+  Token parseInitializersOpt(Token token) {
+    if (optional(':', token)) {
+      return parseInitializers(token);
+    } else {
+      listener.handleNoInitializers();
+      return token;
+    }
+  }
+
+  Token parseInitializers(Token token) {
+    Token begin = token;
+    listener.beginInitializers(begin);
+    expect(':', token);
+    int count = 0;
+    bool old = mayParseFunctionExpressions;
+    mayParseFunctionExpressions = false;
+    do {
+      token = parseExpression(token.next);
+      ++count;
+    } while (optional(',', token));
+    mayParseFunctionExpressions = old;
+    listener.endInitializers(count, begin, token);
+    return token;
+  }
+
+  Token parseScriptTags(Token token) {
+    Token begin = token;
+    listener.beginScriptTag(token);
+    token = parseIdentifier(token.next);
+    token = expect('(', token);
+    token = parseLiteralStringOrRecoverExpression(token);
+    bool hasPrefix = false;
+    if (optional(',', token)) {
+      hasPrefix = true;
+      token = parseIdentifier(token.next);
+      token = expect(':', token);
+      token = parseLiteralStringOrRecoverExpression(token);
+    }
+    token = expect(')', token);
+    listener.endScriptTag(hasPrefix, begin, token);
+    return expectSemicolon(token);
+  }
+
+  Token parseLiteralStringOrRecoverExpression(Token token) {
+    if (token.kind === STRING_TOKEN) {
+      return parseLiteralString(token);
+    } else {
+      listener.recoverableError("unexpected", token: token);
+      return parseExpression(token);
+    }
+  }
+
+  Token expectSemicolon(Token token) {
+    return expect(';', token);
+  }
+
+  Token parseModifier(Token token) {
+    assert(('final' === token.stringValue) ||
+           ('var' === token.stringValue) ||
+           ('const' === token.stringValue) ||
+           ('abstract' === token.stringValue) ||
+           ('static' === token.stringValue));
+    listener.handleModifier(token);
+    return token.next;
+  }
+
+  Token parseModifiers(Token token) {
+    int count = 0;
+    while (token.kind === KEYWORD_TOKEN) {
+      final String value = token.stringValue;
+      if (('final' !== value) &&
+          ('var' !== value) &&
+          ('const' !== value) &&
+          ('abstract' !== value) &&
+          ('static' !== value))
+        break;
+      token = parseModifier(token);
+      count++;
+    }
+    listener.handleModifiers(count);
+    return token;
+  }
+
+  Token peekAfterType(Token token) {
+    // TODO(ahe): Also handle var?
+    if ('void' !== token.stringValue && !isIdentifier(token)) {
+      listener.unexpected(token);
+    }
+    // We are looking at "identifier ...".
+    Token peek = token.next;
+    if (peek.kind === PERIOD_TOKEN) {
+      if (isIdentifier(peek.next)) {
+        // Look past a library prefix.
+        peek = peek.next.next;
+      }
+    }
+    // We are looking at "qualified ...".
+    if (peek.kind === LT_TOKEN) {
+      // Possibly generic type.
+      // We are looking at "qualified '<'".
+      BeginGroupToken beginGroupToken = peek;
+      Token gtToken = beginGroupToken.endGroup;
+      if (gtToken !== null) {
+        // We are looking at "qualified '<' ... '>' ...".
+        return gtToken.next;
+      }
+    }
+    return peek;
+  }
+
+  Token parseClassBody(Token token) {
+    Token begin = token;
+    listener.beginClassBody(token);
+    if (!optional('{', token)) {
+      token = listener.expectedClassBody(token);
+    }
+    token = token.next;
+    int count = 0;
+    while (notEofOrValue('}', token)) {
+      token = parseMember(token);
+      ++count;
+    }
+    listener.endClassBody(count, begin, token);
+    return token;
+  }
+
+  bool isGetOrSet(Token token) {
+    final String value = token.stringValue;
+    return (value === 'get') || (value === 'set');
+  }
+
+  Token findGetOrSet(Token token) {
+    if (isGetOrSet(token)) {
+      if (optional('<', token.next)) {
+        // For example: get<T> ...
+        final Token peek = peekAfterType(token);
+        if (isGetOrSet(peek) && isIdentifier(peek.next)) {
+          // For example: get<T> get identifier
+          return peek;
+        }
+      } else {
+        // For example: get ...
+        if (isGetOrSet(token.next) && isIdentifier(token.next.next)) {
+          // For example: get get identifier
+          return token.next;
+        } else {
+          // For example: get identifier
+          return token;
+        }
+      }
+    } else if (token.stringValue !== 'operator') {
+      final Token peek = peekAfterType(token);
+      if (isGetOrSet(peek) && isIdentifier(peek.next)) {
+        // type? get identifier
+        return peek;
+      }
+    }
+    return null;
+  }
+
+  Token parseMember(Token token) {
+    if (optional('factory', token)) {
+      return parseFactoryMethod(token);
+    }
+    Token start = token;
+    listener.beginMember(token);
+    token = parseModifiers(token);
+    Token getOrSet = findGetOrSet(token);
+    if (token === getOrSet) token = token.next;
+    Token peek = peekAfterType(token);
+    if (isIdentifier(peek) && token.stringValue !== 'operator') {
+      // Skip type.
+      token = peek;
+    }
+    if (token === getOrSet) token = token.next;
+    if (optional('operator', token)) {
+      token = parseOperatorName(token);
+    } else {
+      token = parseIdentifier(token);
+    }
+    bool isField;
+    while (true) {
+      // Loop to allow the listener to rewrite the token stream for
+      // error handling.
+      final String value = token.stringValue;
+      if ((value === '(') || (value === '.')) {
+        isField = false;
+        break;
+      } else if ((value === '=') || (value === ';') || (value === ',')) {
+        isField = true;
+        break;
+      } else {
+        token = listener.unexpected(token);
+        if (token.kind === EOF_TOKEN) {
+          // TODO(ahe): This is a hack, see parseTopLevelMember.
+          listener.endFields(1, start, token);
+          return token;
+        }
+      }
+    }
+    if (isField) {
+      int fieldCount = 1;
+      token = parseVariableInitializerOpt(token);
+      if (getOrSet !== null) {
+        listener.recoverableError("unexpected", token: getOrSet);
+      }
+      while (optional(',', token)) {
+        // TODO(ahe): Count these.
+        token = parseIdentifier(token.next);
+        token = parseVariableInitializerOpt(token);
+        ++fieldCount;
+      }
+      expectSemicolon(token);
+      listener.endFields(fieldCount, start, token);
+    } else {
+      token = parseQualifiedRestOpt(token);
+      token = parseFormalParameters(token);
+      token = parseInitializersOpt(token);
+      token = parseFunctionBody(token, false);
+      listener.endMethod(getOrSet, start, token);
+    }
+    return token.next;
+  }
+
+  Token parseFactoryMethod(Token token) {
+    assert(optional('factory', token));
+    Token factoryKeyword = token;
+    listener.beginFactoryMethod(factoryKeyword);
+    token = token.next; // Skip 'factory'.
+    token = parseIdentifier(token);
+    token = parseQualifiedRestOpt(token);
+    token = parseTypeVariablesOpt(token);
+    Token period = null;
+    if (optional('.', token)) {
+      period = token;
+      token = parseIdentifier(token.next);
+    }
+    token = parseFormalParameters(token);
+    token = parseFunctionBody(token, false);
+    listener.endFactoryMethod(factoryKeyword, period, token);
+    return token.next;
+  }
+
+  Token parseOperatorName(Token token) {
+    assert(optional('operator', token));
+    if (isUserDefinableOperator(token.next.stringValue)) {
+      Token operator = token;
+      token = token.next;
+      listener.handleOperatorName(operator, token);
+      return token.next;
+    } else {
+      return parseIdentifier(token);
+    }
+  }
+
+  Token parseFunction(Token token, Token getOrSet) {
+    listener.beginFunction(token);
+    token = parseModifiers(token);
+    if (getOrSet === token) token = token.next;
+    token = parseReturnTypeOpt(token);
+    if (getOrSet === token) token = token.next;
+    listener.beginFunctionName(token);
+    if (optional('operator', token)) {
+      token = parseOperatorName(token);
+    } else {
+      token = parseIdentifier(token);
+    }
+    token = parseQualifiedRestOpt(token);
+    listener.endFunctionName(token);
+    token = parseFormalParameters(token);
+    token = parseInitializersOpt(token);
+    token = parseFunctionBody(token, false);
+    listener.endFunction(getOrSet, token);
+    return token.next;
+  }
+
+  Token parseUnamedFunction(Token token) {
+    listener.beginUnamedFunction(token);
+    token = parseFormalParameters(token);
+    bool isBlock = optional('{', token);
+    token = parseFunctionBody(token, true);
+    listener.endUnamedFunction(token);
+    return isBlock ? token.next : token;
+  }
+
+  Token parseFunctionDeclaration(Token token) {
+    listener.beginFunctionDeclaration(token);
+    token = parseFunction(token, null);
+    listener.endFunctionDeclaration(token);
+    return token;
+  }
+
+  Token parseFunctionExpression(Token token) {
+    listener.beginFunction(token);
+    listener.handleModifiers(0);
+    token = parseReturnTypeOpt(token);
+    listener.beginFunctionName(token);
+    token = parseIdentifier(token);
+    listener.endFunctionName(token);
+    token = parseFormalParameters(token);
+    listener.handleNoInitializers();
+    bool isBlock = optional('{', token);
+    token = parseFunctionBody(token, true);
+    listener.endFunction(null, token);
+    return isBlock ? token.next : token;
+  }
+
+  Token parseFunctionBody(Token token, bool isExpression) {
+    if (optional(';', token)) {
+      listener.endFunctionBody(0, null, token);
+      return token;
+    } else if (optional('=>', token)) {
+      Token begin = token;
+      token = parseExpression(token.next);
+      if (!isExpression) {
+        expectSemicolon(token);
+        listener.endReturnStatement(true, begin, token);
+      } else {
+        listener.endReturnStatement(true, begin, null);
+      }
+      return token;
+    }
+    Token begin = token;
+    int statementCount = 0;
+    listener.beginFunctionBody(begin);
+    if (!optional('{', token)) {
+      return listener.expectedFunctionBody(token);
+    } else {
+      token = token.next;
+    }
+    while (notEofOrValue('}', token)) {
+      token = parseStatement(token);
+      ++statementCount;
+    }
+    listener.endFunctionBody(statementCount, begin, token);
+    expect('}', token);
+    return token;
+  }
+
+  Token parseStatement(Token token) {
+    final value = token.stringValue;
+    if (token.kind === IDENTIFIER_TOKEN) {
+      return parseExpressionStatementOrDeclaration(token);
+    } else if (value === '{') {
+      return parseBlock(token);
+    } else if (value === 'return') {
+      return parseReturnStatement(token);
+    } else if (value === 'var' || value === 'final') {
+      return parseVariablesDeclaration(token);
+    } else if (value === 'if') {
+      return parseIfStatement(token);
+    } else if (value === 'for') {
+      return parseForStatement(token);
+    } else if (value === 'throw') {
+      return parseThrowStatement(token);
+    } else if (value === 'void') {
+      return parseExpressionStatementOrDeclaration(token);
+    } else if (value === 'while') {
+      return parseWhileStatement(token);
+    } else if (value === 'do') {
+      return parseDoWhileStatement(token);
+    } else if (value === 'try') {
+      return parseTryStatement(token);
+    } else if (value === 'switch') {
+      return parseSwitchStatement(token);
+    } else if (value === 'break') {
+      return parseBreakStatement(token);
+    } else if (value === 'continue') {
+      return parseContinueStatement(token);
+    } else if (value === ';') {
+      return parseEmptyStatement(token);
+    } else {
+      return parseExpressionStatement(token);
+    }
+  }
+
+  Token parseReturnStatement(Token token) {
+    Token begin = token;
+    listener.beginReturnStatement(begin);
+    assert('return' === token.stringValue);
+    token = token.next;
+    if (optional(';', token)) {
+      listener.endReturnStatement(false, begin, token);
+    } else {
+      token = parseExpression(token);
+      listener.endReturnStatement(true, begin, token);
+    }
+    return expectSemicolon(token);
+  }
+
+  Token peekIdentifierAfterType(Token token) {
+    Token peek = peekAfterType(token);
+    if (peek !== null && isIdentifier(peek)) {
+      // We are looking at "type identifier".
+      return peek;
+    } else {
+      return null;
+    }
+  }
+
+  Token parseExpressionStatementOrDeclaration(Token token) {
+    assert(isIdentifier(token) || token.stringValue === 'void');
+    Token identifier = peekIdentifierAfterType(token);
+    if (identifier !== null) {
+      assert(isIdentifier(identifier));
+      Token afterId = identifier.next;
+      int afterIdKind = afterId.kind;
+      if (afterIdKind === EQ_TOKEN ||
+          afterIdKind === SEMICOLON_TOKEN ||
+          afterIdKind === COMMA_TOKEN) {
+        // We are looking at "type identifier" followed by '=', ';', ','.
+        return parseVariablesDeclaration(token);
+      } else if (afterIdKind === OPEN_PAREN_TOKEN) {
+        // We are looking at "type identifier '('".
+        BeginGroupToken beginParen = afterId;
+        Token endParen = beginParen.endGroup;
+        Token afterParens = endParen.next;
+        if (optional('{', afterParens) || optional('=>', afterParens)) {
+          // We are looking at "type identifier '(' ... ')'" followed
+          // by '=>' or '{'.
+          return parseFunctionDeclaration(token);
+        }
+      }
+      // Fall-through to expression statement.
+    } else {
+      if (optional(':', token.next)) {
+        return parseLabeledStatement(token);
+      } else if (optional('(', token.next)) {
+        BeginGroupToken begin = token.next;
+        String afterParens = begin.endGroup.next.stringValue;
+        if (afterParens === '{' || afterParens === '=>') {
+          return parseFunctionDeclaration(token);
+        }
+      }
+    }
+    return parseExpressionStatement(token);
+  }
+
+  Token parseLabeledStatement(Token token) {
+    listener.beginLabeledStatement(token);
+    token = parseIdentifier(token);
+    Token colon = token;
+    token = expect(':', token);
+    token = parseStatement(token);
+    listener.endLabeledStatement(colon);
+    return token;
+  }
+
+  Token parseExpressionStatement(Token token) {
+    listener.beginExpressionStatement(token);
+    token = parseExpression(token);
+    listener.endExpressionStatement(token);
+    return expectSemicolon(token);
+  }
+
+  Token parseExpression(Token token) {
+    return parsePrecedenceExpression(token, 2);
+  }
+
+  Token parseConditionalExpressionRest(Token token) {
+    assert(optional('?', token));
+    Token question = token;
+    token = parseExpression(token.next);
+    Token colon = token;
+    token = expect(':', token);
+    token = parseExpression(token);
+    listener.handleConditionalExpression(question, colon);
+    return token;
+  }
+
+  Token parsePrecedenceExpression(Token token, int precedence) {
+    assert(precedence >= 2);
+    assert(precedence <= POSTFIX_PRECEDENCE);
+    token = parseUnaryExpression(token);
+    PrecedenceInfo info = token.info;
+    int tokenLevel = info.precedence;
+    for (int level = tokenLevel; level >= precedence; --level) {
+      while (tokenLevel === level) {
+        Token operator = token;
+        if (tokenLevel === ASSIGNMENT_PRECEDENCE) {
+          // Right associative, so we recurse at the same precedence
+          // level.
+          token = parsePrecedenceExpression(token.next, level);
+          listener.handleAssignmentExpression(operator);
+        } else if (tokenLevel === POSTFIX_PRECEDENCE) {
+          if (info === PERIOD_INFO) {
+            // Left associative, so we recurse at the next higher
+            // precedence level. However, POSTFIX_PRECEDENCE is the
+            // highest level, so we just call parseUnaryExpression
+            // directly.
+            token = parseUnaryExpression(token.next);
+            listener.handleBinaryExpression(operator);
+          } else if ((info === OPEN_PAREN_INFO) ||
+                     (info === OPEN_SQUARE_BRACKET_INFO)) {
+            token = parseArgumentOrIndexStar(token);
+          } else if ((info === PLUS_PLUS_INFO) ||
+                     (info === MINUS_MINUS_INFO)) {
+            listener.handleUnaryPostfixAssignmentExpression(token);
+            token = token.next;
+          } else {
+            token = listener.unexpected(token);
+          }
+        } else if (info === IS_INFO) {
+          token = parseIsOperatorRest(token);
+        } else if (info === QUESTION_INFO) {
+          token = parseConditionalExpressionRest(token);
+        } else {
+          // Left associative, so we recurse at the next higher
+          // precedence level.
+          token = parsePrecedenceExpression(token.next, level + 1);
+          listener.handleBinaryExpression(operator);
+        }
+        info = token.info;
+        tokenLevel = info.precedence;
+      }
+    }
+    return token;
+  }
+
+  Token parseUnaryExpression(Token token) {
+    String value = token.stringValue;
+    // Prefix:
+    if (value === '+') {
+      // Dart only allows "prefix plus" as an initial part of a
+      // decimal literal. We scan it as a separate token and let
+      // the parser listener combine it with the digits.
+      Token next = token.next;
+      if (next.charOffset === token.charOffset + 1) {
+        if (next.kind === INT_TOKEN) {
+          listener.handleLiteralInt(token);
+          return next.next;
+        }
+        if (next.kind === DOUBLE_TOKEN) {
+          listener.handleLiteralDouble(token);
+          return next.next;
+        }
+      }
+      listener.recoverableError("Unexpected token '+'", token: token);
+      return parsePrecedenceExpression(next, POSTFIX_PRECEDENCE);
+    } else if ((value === '!') ||
+               (value === '-') ||
+               (value === '~')) {
+      Token operator = token;
+      // Right associative, so we recurse at the same precedence
+      // level.
+      token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE);
+      listener.handleUnaryPrefixExpression(operator);
+    } else if ((value === '++') || value === '--') {
+      // TODO(ahe): Validate this is used correctly.
+      Token operator = token;
+      // Right associative, so we recurse at the same precedence
+      // level.
+      token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE);
+      listener.handleUnaryPrefixAssignmentExpression(operator);
+    } else {
+      token = parsePrimary(token);
+    }
+    return token;
+  }
+
+  Token parseArgumentOrIndexStar(Token token) {
+    while (true) {
+      if (optional('[', token)) {
+        Token openSquareBracket = token;
+        bool old = mayParseFunctionExpressions;
+        mayParseFunctionExpressions = true;
+        token = parseExpression(token.next);
+        mayParseFunctionExpressions = old;
+        listener.handleIndexedExpression(openSquareBracket, token);
+        token = expect(']', token);
+      } else if (optional('(', token)) {
+        token = parseArguments(token);
+        listener.endSend(token);
+      } else {
+        break;
+      }
+    }
+    return token;
+  }
+
+  Token parsePrimary(Token token) {
+    final kind = token.kind;
+    if (kind === IDENTIFIER_TOKEN) {
+      return parseSendOrFunctionLiteral(token);
+    } else if (kind === INT_TOKEN || kind === HEXADECIMAL_TOKEN) {
+      return parseLiteralInt(token);
+    } else if (kind === DOUBLE_TOKEN) {
+      return parseLiteralDouble(token);
+    } else if (kind === STRING_TOKEN) {
+      return parseLiteralString(token);
+    } else if (kind === KEYWORD_TOKEN) {
+      final value = token.stringValue;
+      if ((value === 'true') || (value === 'false')) {
+        return parseLiteralBool(token);
+      } else if (value === 'null') {
+        return parseLiteralNull(token);
+      } else if (value === 'this') {
+        return parseThisExpression(token);
+      } else if (value === 'super') {
+        return parseSuperExpression(token);
+      } else if (value === 'new') {
+        return parseNewExpression(token);
+      } else if (value === 'const') {
+        return parseConstExpression(token);
+      } else if (value === 'void') {
+        return parseFunctionExpression(token);
+      } else if (isIdentifier(token)) {
+        return parseSendOrFunctionLiteral(token);
+      } else {
+        return listener.expectedExpression(token);
+      }
+    } else if (kind === OPEN_PAREN_TOKEN) {
+      return parseParenthesizedExpressionOrFunctionLiteral(token);
+    } else if ((kind === LT_TOKEN) ||
+               (kind === OPEN_SQUARE_BRACKET_TOKEN) ||
+               (kind === OPEN_CURLY_BRACKET_TOKEN) ||
+               token.stringValue === '[]') {
+      return parseLiteralListOrMap(token);
+    } else {
+      return listener.expectedExpression(token);
+    }
+  }
+
+  Token parseParenthesizedExpressionOrFunctionLiteral(Token token) {
+    BeginGroupToken beginGroup = token;
+    int kind = beginGroup.endGroup.next.kind;
+    if (mayParseFunctionExpressions &&
+        (kind === FUNCTION_TOKEN || kind === OPEN_CURLY_BRACKET_TOKEN)) {
+      return parseUnamedFunction(token);
+    } else {
+      bool old = mayParseFunctionExpressions;
+      mayParseFunctionExpressions = true;
+      token = parseParenthesizedExpression(token);
+      mayParseFunctionExpressions = old;
+      return token;
+    }
+  }
+
+  Token parseParenthesizedExpression(Token token) {
+    BeginGroupToken begin = token;
+    token = expect('(', token);
+    token = parseExpression(token);
+    if (begin.endGroup !== token) {
+      listener.unexpected(token);
+      token = begin.endGroup;
+    }
+    listener.handleParenthesizedExpression(begin);
+    return expect(')', token);
+  }
+
+  Token parseThisExpression(Token token) {
+    listener.handleThisExpression(token);
+    token = token.next;
+    if (optional('(', token)) {
+      // Constructor forwarding.
+      token = parseArguments(token);
+      listener.endSend(token);
+    }
+    return token;
+  }
+
+  Token parseSuperExpression(Token token) {
+    listener.handleSuperExpression(token);
+    token = token.next;
+    if (optional('(', token)) {
+      // Super constructor.
+      token = parseArguments(token);
+      listener.endSend(token);
+    }
+    return token;
+  }
+
+  Token parseLiteralListOrMap(Token token) {
+    Token constKeyword = null;
+    if (optional('const', token)) {
+      constKeyword = token;
+      token = token.next;
+    }
+    token = parseTypeArgumentsOpt(token);
+    Token beginToken = token;
+    int count = 0;
+    if (optional('{', token)) {
+      bool old = mayParseFunctionExpressions;
+      mayParseFunctionExpressions = true;
+      do {
+        if (optional('}', token.next)) {
+          token = token.next;
+          break;
+        }
+        token = parseMapLiteralEntry(token.next);
+        ++count;
+      } while (optional(',', token));
+      mayParseFunctionExpressions = old;
+      listener.handleLiteralMap(count, beginToken, constKeyword, token);
+      return expect('}', token);
+    } else if (optional('[', token)) {
+      bool old = mayParseFunctionExpressions;
+      mayParseFunctionExpressions = true;
+      do {
+        if (optional(']', token.next)) {
+          token = token.next;
+          break;
+        }
+        token = parseExpression(token.next);
+        ++count;
+      } while (optional(',', token));
+      mayParseFunctionExpressions = old;
+      listener.handleLiteralList(count, beginToken, constKeyword, token);
+      return expect(']', token);
+    } else if (optional('[]', token)) {
+      listener.handleLiteralList(0, token, constKeyword, token);
+      return token.next;
+    } else {
+      listener.unexpected(token);
+    }
+  }
+
+  Token parseMapLiteralEntry(Token token) {
+    listener.beginLiteralMapEntry(token);
+    // Assume the listener rejects non-string keys.
+    token = parseExpression(token);
+    Token colon = token;
+    token = expect(':', token);
+    token = parseExpression(token);
+    listener.endLiteralMapEntry(colon, token);
+    return token;
+  }
+
+  Token parseSendOrFunctionLiteral(Token token) {
+    if (!mayParseFunctionExpressions) return parseSend(token);
+    Token peek = peekAfterType(token);
+    if (peek.kind === IDENTIFIER_TOKEN && isFunctionDeclaration(peek.next)) {
+      return parseFunctionExpression(token);
+    } else if (isFunctionDeclaration(token.next)) {
+      return parseFunctionExpression(token);
+    } else {
+      return parseSend(token);
+    }
+  }
+
+  bool isFunctionDeclaration(Token token) {
+    if (optional('(', token)) {
+      BeginGroupToken begin = token;
+      String afterParens = begin.endGroup.next.stringValue;
+      if (afterParens === '{' || afterParens === '=>') {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  Token parseNewExpression(Token token) {
+    Token newKeyword = token;
+    token = expect('new', token);
+    token = parseType(token);
+    bool named = false;
+    if (optional('.', token)) {
+      named = true;
+      token = parseIdentifier(token.next);
+    }
+    if (optional('(', token)) {
+      token = parseArguments(token);
+    } else {
+      listener.handleNoArguments(token);
+      token = listener.unexpected(token);
+    }
+    listener.handleNewExpression(newKeyword, named);
+    return token;
+  }
+
+  Token parseConstExpression(Token token) {
+    Token constKeyword = token;
+    token = expect('const', token);
+    final String value = token.stringValue;
+    if ((value === '<') ||
+        (value === '[') ||
+        (value === '[]') ||
+        (value === '{')) {
+      return parseLiteralListOrMap(constKeyword);
+    }
+    token = parseType(token);
+    bool named = false;
+    if (optional('.', token)) {
+      named = true;
+      token = parseIdentifier(token.next);
+    }
+    expect('(', token);
+    token = parseArguments(token);
+    listener.handleConstExpression(constKeyword, named);
+    return token;
+  }
+
+  Token parseLiteralInt(Token token) {
+    listener.handleLiteralInt(token);
+    return token.next;
+  }
+
+  Token parseLiteralDouble(Token token) {
+    listener.handleLiteralDouble(token);
+    return token.next;
+  }
+
+  Token parseLiteralString(Token token) {
+    token = parseSingleLiteralString(token);
+    int count = 1;
+    while (token.kind === STRING_TOKEN) {
+      token = parseSingleLiteralString(token);
+      count++;
+    }
+    if (count > 1) {
+      listener.handleStringJuxtaposition(count);
+    }
+    return token;
+  }
+
+  Token parseSingleLiteralString(Token token) {
+    listener.beginLiteralString(token);
+    token = token.next;
+    int interpolationCount = 0;
+    while (optional('\${', token)) {
+      token = token.next;
+      token = parseExpression(token);
+      token = expect('}', token);
+      token = parseStringPart(token);
+      ++interpolationCount;
+    }
+    listener.endLiteralString(interpolationCount);
+    return token;
+  }
+
+  Token parseLiteralBool(Token token) {
+    listener.handleLiteralBool(token);
+    return token.next;
+  }
+
+  Token parseLiteralNull(Token token) {
+    listener.handleLiteralNull(token);
+    return token.next;
+  }
+
+  Token parseSend(Token token) {
+    listener.beginSend(token);
+    token = parseIdentifier(token);
+    token = parseArgumentsOpt(token);
+    listener.endSend(token);
+    return token;
+  }
+
+  Token parseArgumentsOpt(Token token) {
+    if (!optional('(', token)) {
+      listener.handleNoArguments(token);
+      return token;
+    } else {
+      return parseArguments(token);
+    }
+  }
+
+  Token parseArguments(Token token) {
+    Token begin = token;
+    listener.beginArguments(begin);
+    assert('(' === token.stringValue);
+    int argumentCount = 0;
+    if (optional(')', token.next)) {
+      listener.endArguments(argumentCount, begin, token.next);
+      return token.next.next;
+    }
+    bool old = mayParseFunctionExpressions;
+    mayParseFunctionExpressions = true;
+    do {
+      Token colon = null;
+      if (optional(':', token.next.next)) {
+        token = parseIdentifier(token.next);
+        colon = token;
+      }
+      token = parseExpression(token.next);
+      if (colon !== null) listener.handleNamedArgument(colon);
+      ++argumentCount;
+    } while (optional(',', token));
+    mayParseFunctionExpressions = old;
+    listener.endArguments(argumentCount, begin, token);
+    return expect(')', token);
+  }
+
+  Token parseIsOperatorRest(Token token) {
+    assert(optional('is', token));
+    Token operator = token;
+    Token not = null;
+    if (optional('!', token.next)) {
+      token = token.next;
+      not = token;
+    }
+    token = parseType(token.next);
+    listener.handleIsOperator(operator, not, token);
+    if (optional('is', token)) {
+      // The is-operator cannot be chained, but it can take part of
+      // expressions like: foo is Foo || foo is Bar.
+      listener.unexpected(token);
+    }
+    return token;
+  }
+
+  Token parseVariablesDeclaration(Token token) {
+    token = parseVariablesDeclarationNoSemicolon(token);
+    return expectSemicolon(token);
+  }
+
+  Token parseVariablesDeclarationNoSemicolon(Token token) {
+    int count = 1;
+    listener.beginVariablesDeclaration(token);
+    token = parseModifiers(token);
+    token = parseTypeOpt(token);
+    token = parseOptionallyInitializedIdentifier(token);
+    while (optional(',', token)) {
+      token = parseOptionallyInitializedIdentifier(token.next);
+      ++count;
+    }
+    listener.endVariablesDeclaration(count, token);
+    return token;
+  }
+
+  Token parseOptionallyInitializedIdentifier(Token token) {
+    listener.beginInitializedIdentifier(token);
+    token = parseIdentifier(token);
+    token = parseVariableInitializerOpt(token);
+    listener.endInitializedIdentifier();
+    return token;
+  }
+
+  Token parseIfStatement(Token token) {
+    Token ifToken = token;
+    listener.beginIfStatement(ifToken);
+    token = expect('if', token);
+    token = parseParenthesizedExpression(token);
+    token = parseStatement(token);
+    Token elseToken = null;
+    if (optional('else', token)) {
+      elseToken = token;
+      token = parseStatement(token.next);
+    }
+    listener.endIfStatement(ifToken, elseToken);
+    return token;
+  }
+
+  Token parseForStatement(Token token) {
+    Token forToken = token;
+    listener.beginForStatement(forToken);
+    token = expect('for', token);
+    token = expect('(', token);
+    token = parseVariablesDeclarationOrExpressionOpt(token);
+    if (optional('in', token)) {
+      return parseForInRest(forToken, token);
+    } else {
+      return parseForRest(forToken, token);
+    }
+  }
+
+  Token parseVariablesDeclarationOrExpressionOpt(Token token) {
+    final String value = token.stringValue;
+    if (value === ';') {
+      listener.handleNoExpression(token);
+      return token;
+    } else if ((value === 'var') || (value === 'final')) {
+      return parseVariablesDeclarationNoSemicolon(token);
+    }
+    Token identifier = peekIdentifierAfterType(token);
+    if (identifier !== null) {
+      assert(isIdentifier(identifier));
+      Token afterId = identifier.next;
+      int afterIdKind = afterId.kind;
+      if (afterIdKind === EQ_TOKEN || afterIdKind === SEMICOLON_TOKEN ||
+          afterIdKind === COMMA_TOKEN || optional('in', afterId)) {
+        return parseVariablesDeclarationNoSemicolon(token);
+      }
+    }
+    return parseExpression(token);
+  }
+
+  Token parseForRest(Token forToken, Token token) {
+    token = expectSemicolon(token);
+    if (optional(';', token)) {
+      token = parseEmptyStatement(token);
+    } else {
+      token = parseExpressionStatement(token);
+    }
+    int expressionCount = 0;
+    while (true) {
+      if (optional(')', token)) break;
+      token = parseExpression(token);
+      ++expressionCount;
+      if (optional(',', token)) {
+        token = token.next;
+      } else {
+        break;
+      }
+    }
+    token = expect(')', token);
+    token = parseStatement(token);
+    listener.endForStatement(expressionCount, forToken, token);
+    return token;
+  }
+
+  Token parseForInRest(Token forToken, Token token) {
+    assert(optional('in', token));
+    Token inKeyword = token;
+    token = parseExpression(token.next);
+    token = expect(')', token);
+    token = parseStatement(token);
+    listener.endForInStatement(forToken, inKeyword, token);
+    return token;
+  }
+
+  Token parseWhileStatement(Token token) {
+    Token whileToken = token;
+    listener.beginWhileStatement(whileToken);
+    token = expect('while', token);
+    token = parseParenthesizedExpression(token);
+    token = parseStatement(token);
+    listener.endWhileStatement(whileToken, token);
+    return token;
+  }
+
+  Token parseDoWhileStatement(Token token) {
+    Token doToken = token;
+    listener.beginDoWhileStatement(doToken);
+    token = expect('do', token);
+    token = parseStatement(token);
+    Token whileToken = token;
+    token = expect('while', token);
+    token = parseParenthesizedExpression(token);
+    listener.endDoWhileStatement(doToken, whileToken, token);
+    return expectSemicolon(token);
+  }
+
+  Token parseBlock(Token token) {
+    Token begin = token;
+    listener.beginBlock(begin);
+    int statementCount = 0;
+    token = expect('{', token);
+    while (notEofOrValue('}', token)) {
+      token = parseStatement(token);
+      ++statementCount;
+    }
+    listener.endBlock(statementCount, begin, token);
+    return expect('}', token);
+  }
+
+  Token parseThrowStatement(Token token) {
+    Token throwToken = token;
+    listener.beginThrowStatement(throwToken);
+    token = expect('throw', token);
+    if (optional(';', token)) {
+      listener.endRethrowStatement(throwToken, token);
+      return token.next;
+    } else {
+      token = parseExpression(token);
+      listener.endThrowStatement(throwToken, token);
+      return expectSemicolon(token);
+    }
+  }
+
+  Token parseTryStatement(Token token) {
+    assert(optional('try', token));
+    Token tryKeyword = token;
+    listener.beginTryStatement(tryKeyword);
+    token = parseBlock(token.next);
+    int catchCount = 0;
+    while (optional('catch', token)) {
+      Token catchKeyword = token;
+      // TODO(ahe): Validate the "parameters".
+      token = parseFormalParameters(token.next);
+      token = parseBlock(token);
+      ++catchCount;
+      listener.handleCatchBlock(catchKeyword);
+    }
+    Token finallyKeyword = null;
+    if (optional('finally', token)) {
+      finallyKeyword = token;
+      token = parseBlock(token.next);
+      listener.handleFinallyBlock(finallyKeyword);
+    }
+    listener.endTryStatement(catchCount, tryKeyword, finallyKeyword);
+    return token;
+  }
+
+  Token parseSwitchStatement(Token token) {
+    assert(optional('switch', token));
+    Token switchKeyword = token;
+    listener.beginSwitchStatement(switchKeyword);
+    token = parseParenthesizedExpression(token.next);
+    token = parseSwitchBlock(token);
+    listener.endSwitchStatement(switchKeyword, token);
+    return token.next;
+  }
+
+  Token parseSwitchBlock(Token token) {
+    Token begin = token;
+    listener.beginSwitchBlock(begin);
+    token = expect('{', token);
+    int caseCount = 0;
+    while (token.kind !== EOF_TOKEN) {
+      if (optional('}', token)) {
+        break;
+      }
+      token = parseSwitchCase(token);
+      ++caseCount;
+    }
+    listener.endSwitchBlock(caseCount, begin, token);
+    expect('}', token);
+    return token;
+  }
+
+  Token parseSwitchCase(Token token) {
+    Token begin = token;
+    Token defaultKeyword = null;
+    Token label = null;
+    // First an optional label.
+    if (isIdentifier(token)) {
+      label = token;
+      token = parseIdentifier(token);
+      token = expect(':', token);
+    }
+    // Then one or more case expressions, the last of which may be
+    // 'default' instead.
+    int expressionCount = 0;
+    String value = token.stringValue;
+    do {
+      if (value === 'default') {
+        defaultKeyword = token;
+        token = expect(':', token.next);
+        break;
+      }
+      token = expect('case', token);
+      token = parseExpression(token);
+      token = expect(':', token);
+      expressionCount++;
+      value = token.stringValue;
+    } while (value === 'case' || value === 'default');
+    // Finally zero or more statements.
+    int statementCount = 0;
+    while (token.kind !== EOF_TOKEN) {
+      String value;
+      if (isIdentifier(token) && optional(':', token.next)) {
+        // Skip label.
+        value = token.next.next.stringValue;
+      } else {
+        value = token.stringValue;
+      }
+      if (value === 'case' || value === 'default' || value === '}') {
+        break;
+      } else {
+        token = parseStatement(token);
+        ++statementCount;
+      }
+    }
+    listener.handleSwitchCase(label, expressionCount, defaultKeyword,
+                              statementCount, begin, token);
+    return token;
+  }
+
+  Token parseBreakStatement(Token token) {
+    assert(optional('break', token));
+    Token breakKeyword = token;
+    token = token.next;
+    bool hasTarget = false;
+    if (isIdentifier(token)) {
+      token = parseIdentifier(token);
+      hasTarget = true;
+    }
+    listener.handleBreakStatement(hasTarget, breakKeyword, token);
+    return expectSemicolon(token);
+  }
+
+  Token parseContinueStatement(Token token) {
+    assert(optional('continue', token));
+    Token continueKeyword = token;
+    token = token.next;
+    bool hasTarget = false;
+    if (isIdentifier(token)) {
+      token = parseIdentifier(token);
+      hasTarget = true;
+    }
+    listener.handleContinueStatement(hasTarget, continueKeyword, token);
+    return expectSemicolon(token);
+  }
+
+  Token parseEmptyStatement(Token token) {
+    listener.handleEmptyStatement(token);
+    return expectSemicolon(token);
+  }
+}
diff --git a/lib/compiler/implementation/scanner/parser_bench.dart b/lib/compiler/implementation/scanner/parser_bench.dart
new file mode 100644
index 0000000..a81632f
--- /dev/null
+++ b/lib/compiler/implementation/scanner/parser_bench.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * A benchmark for the Dart parser.
+ */
+class ParserBench extends BaseParserBench {
+  int charCount = 0;
+  double score = 0.0;
+
+  Token scanFileNamed(String filename) {
+    Token token;
+    getBytes(filename, (bytes) {
+      Scanner scanner = makeScanner(bytes);
+      try {
+        token = scanner.tokenize();
+        printTokens(token);
+        charCount += scanner.charOffset;
+      } catch (MalformedInputException e) {
+        print("${filename}: ${e}");
+      }
+    });
+    return token;
+  }
+
+  void timedParseAll(List<String> arguments) {
+    charCount = 0;
+    Stopwatch timer = new Stopwatch();
+    timer.start();
+    BenchListener listener = parseAll(arguments);
+    timer.stop();
+    print("Parsing (${listener.libraryTagCount} tags, "
+          "${listener.classCount} classes, "
+          "${listener.interfaceCount} interfaces, "
+          "${listener.aliasCount} typedefs, "
+          "${listener.topLevelMemberCount} top-level members) "
+          "took ${timer.elapsedInMs()}ms");
+  }
+
+  BenchListener parseAll(List<String> arguments) {
+    charCount = 0;
+    Stopwatch timer = new Stopwatch();
+    timer.start();
+    BenchListener listener = new BenchListener();
+    for (String argument in arguments) {
+      parseFileNamed(argument, listener);
+    }
+    timer.stop();
+    score = charCount / timer.elapsedInMs();
+    return listener;
+  }
+
+  void parseFileNamed(String argument, Listener listener) {
+    bool failed = true;
+    try {
+      PartialParser parser = new PartialParser(listener);
+      parser.parseUnit(scanFileNamed(argument));
+      failed = false;
+    } finally {
+      if (failed) print('Error in ${argument}');
+    }
+  }
+
+  void main(List<String> arguments) {
+    for (int i = 0; i < 10; i++) {
+      timedParseAll(arguments);
+    }
+    final int iterations = 500;
+    VerboseProgressBar bar = new VerboseProgressBar(iterations);
+    bar.begin();
+    for (int i = 0; i < iterations; i++) {
+      bar.tick();
+      parseAll(arguments);
+      bar.recordScore(score);
+    }
+    bar.end();
+    for (int i = 0; i < 10; i++) {
+      timedParseAll(arguments);
+    }
+  }
+}
+
+main() {
+  new ParserBench().main(argv);
+}
+
+class BenchListener extends Listener {
+  int aliasCount = 0;
+  int classCount = 0;
+  int interfaceCount = 0;
+  int libraryTagCount = 0;
+  int topLevelMemberCount = 0;
+
+  void beginTopLevelMember(Token token) {
+    topLevelMemberCount++;
+  }
+
+  void beginLibraryTag(Token token) {
+    libraryTagCount++;
+  }
+
+  void beginInterface(Token token) {
+    interfaceCount++;
+  }
+
+  void beginClass(Token token) {
+    classCount++;
+  }
+
+  void beginFunctionTypeAlias(Token token) {
+    aliasCount++;
+  }
+}
diff --git a/lib/compiler/implementation/scanner/parser_task.dart b/lib/compiler/implementation/scanner/parser_task.dart
new file mode 100644
index 0000000..a88c8b9
--- /dev/null
+++ b/lib/compiler/implementation/scanner/parser_task.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2011, 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.
+
+class ParserTask extends CompilerTask {
+  ParserTask(Compiler compiler) : super(compiler);
+  String get name() => 'Parser';
+
+  Node parse(Element element) {
+    return measure(() => element.parseNode(compiler));
+  }
+}
diff --git a/lib/compiler/implementation/scanner/partial_parser.dart b/lib/compiler/implementation/scanner/partial_parser.dart
new file mode 100644
index 0000000..1e792d0
--- /dev/null
+++ b/lib/compiler/implementation/scanner/partial_parser.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2011, 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.
+
+class PartialParser extends Parser {
+  PartialParser(Listener listener) : super(listener);
+
+  Token parseClassBody(Token token) => skipClassBody(token);
+
+  Token fullParseClassBody(Token token) => super.parseClassBody(token);
+
+  Token parseExpression(Token token) => skipExpression(token);
+
+  Token skipExpression(Token token) {
+    while (true) {
+      final kind = token.kind;
+      final value = token.stringValue;
+      if ((kind === EOF_TOKEN) ||
+          (value === ';') ||
+          (value === ',') ||
+          (value === ']'))
+        return token;
+      if (value === '=') {
+        var nextValue = token.next.stringValue;
+        if (nextValue === 'const') {
+          token = token.next;
+          nextValue = token.next.stringValue;
+        }
+        if (nextValue === '{') {
+          // Handle cases like this:
+          // class Foo {
+          //   var map;
+          //   Foo() : map = {};
+          // }
+          BeginGroupToken begin = token.next;
+          token = (begin.endGroup !== null) ? begin.endGroup : token;
+          token = token.next;
+          continue;
+        }
+        if (nextValue === '<') {
+          // Handle cases like this:
+          // class Foo {
+          //   var map;
+          //   Foo() : map = <Foo>{};
+          // }
+          BeginGroupToken begin = token.next;
+          token = (begin.endGroup !== null) ? begin.endGroup : token;
+          token = token.next;
+          if (token.stringValue === '{') {
+            begin = token;
+            token = (begin.endGroup !== null) ? begin.endGroup : token;
+            token = token.next;
+          }
+          continue;
+        }
+      }
+      if (!mayParseFunctionExpressions && value === '{')
+        return token;
+      if ((value !== '<') && (token is BeginGroupToken)) {
+        BeginGroupToken begin = token;
+        token = (begin.endGroup !== null) ? begin.endGroup : token;
+      }
+      token = token.next;
+    }
+  }
+
+  Token skipClassBody(Token token) {
+    if (!optional('{', token)) {
+      return listener.expectedClassBodyToSkip(token);
+    }
+    BeginGroupToken beginGroupToken = token;
+    assert(beginGroupToken.endGroup === null ||
+           beginGroupToken.endGroup.kind === $CLOSE_CURLY_BRACKET);
+    return beginGroupToken.endGroup;
+  }
+
+  Token parseFunctionBody(Token token, bool isExpression) {
+    assert(!isExpression);
+    String value = token.stringValue;
+    if (value === ';') {
+      // No body.
+    } else if (value === '=>') {
+      token = parseExpression(token.next);
+      expectSemicolon(token);
+    } else {
+      token = skipBlock(token);
+    }
+    // There is no "skipped function body event", so we use
+    // handleNoFunctionBody instead.
+    listener.handleNoFunctionBody(token);
+    return token;
+  }
+
+  Token parseFormalParameters(Token token) => skipFormals(token);
+
+  Token skipFormals(Token token) {
+    listener.beginOptionalFormalParameters(token);
+    if (!optional('(', token)) {
+      if (optional(';', token)) {
+        listener.recoverableError("expected '('", token: token);
+        return token;
+      }
+      return listener.unexpected(token);
+    }
+    BeginGroupToken beginGroupToken = token;
+    Token endToken = beginGroupToken.endGroup;
+    listener.endFormalParameters(0, token, endToken);
+    return endToken.next;
+  }
+}
diff --git a/lib/compiler/implementation/scanner/scanner.dart b/lib/compiler/implementation/scanner/scanner.dart
new file mode 100644
index 0000000..b6bc830
--- /dev/null
+++ b/lib/compiler/implementation/scanner/scanner.dart
@@ -0,0 +1,764 @@
+// Copyright (c) 2012, 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.
+
+interface Scanner {
+  Token tokenize();
+}
+
+/**
+ * Common base class for a Dart scanner.
+ */
+class AbstractScanner<T> implements Scanner {
+  abstract int advance();
+  abstract int nextByte();
+  abstract int peek();
+  abstract int select(int choice, PrecedenceInfo yes, PrecedenceInfo no);
+  abstract void appendPrecenceToken(PrecedenceInfo info);
+  abstract void appendStringToken(PrecedenceInfo info, String value);
+  abstract void appendByteStringToken(PrecedenceInfo info, T value);
+  abstract void appendKeywordToken(Keyword keyword);
+  abstract void appendWhiteSpace(int next);
+  abstract void appendEofToken();
+  abstract T asciiString(int start, int offset);
+  abstract T utf8String(int start, int offset);
+  abstract Token firstToken();
+  abstract void beginToken();
+  abstract void addToCharOffset(int offset);
+  abstract int get charOffset();
+  abstract int get byteOffset();
+  abstract void appendBeginGroup(PrecedenceInfo info, String value);
+  abstract int appendEndGroup(PrecedenceInfo info, String value, int openKind);
+  abstract void appendGt(PrecedenceInfo info, String value);
+  abstract void appendGtGt(PrecedenceInfo info, String value);
+  abstract void appendGtGtGt(PrecedenceInfo info, String value);
+  abstract void discardOpenLt();
+
+  // TODO(ahe): Move this class to implementation.
+
+  Token tokenize() {
+    int next = advance();
+    while (next !== $EOF) {
+      next = bigSwitch(next);
+    }
+    appendEofToken();
+    return firstToken();
+  }
+
+  int bigSwitch(int next) {
+    beginToken();
+    if (next === $TAB || next === $LF || next === $CR || next === $SPACE) {
+      appendWhiteSpace(next);
+      return advance();
+    }
+
+    if ($a <= next && next <= $z) {
+      return tokenizeKeywordOrIdentifier(next, true);
+    }
+
+    if (($A <= next && next <= $Z) || next === $_ || next === $$) {
+      return tokenizeIdentifier(next, byteOffset, true);
+    }
+
+    if (next === $LT) {
+      return tokenizeLessThan(next);
+    }
+
+    if (next === $GT) {
+      return tokenizeGreaterThan(next);
+    }
+
+    if (next === $EQ) {
+      return tokenizeEquals(next);
+    }
+
+    if (next === $BANG) {
+      return tokenizeExclamation(next);
+    }
+
+    if (next === $PLUS) {
+      return tokenizePlus(next);
+    }
+
+    if (next === $MINUS) {
+      return tokenizeMinus(next);
+    }
+
+    if (next === $STAR) {
+      return tokenizeMultiply(next);
+    }
+
+    if (next === $PERCENT) {
+      return tokenizePercent(next);
+    }
+
+    if (next === $AMPERSAND) {
+      return tokenizeAmpersand(next);
+    }
+
+    if (next === $BAR) {
+      return tokenizeBar(next);
+    }
+
+    if (next === $CARET) {
+      return tokenizeCaret(next);
+    }
+
+    if (next === $OPEN_SQUARE_BRACKET) {
+      return tokenizeOpenSquareBracket(next);
+    }
+
+    if (next === $TILDE) {
+      return tokenizeTilde(next);
+    }
+
+    if (next === $BACKSLASH) {
+      appendPrecenceToken(BACKSLASH_INFO);
+      return advance();
+    }
+
+    if (next === $HASH) {
+      return tokenizeTag(next);
+    }
+
+    if (next === $OPEN_PAREN) {
+      appendBeginGroup(OPEN_PAREN_INFO, "(");
+      return advance();
+    }
+
+    if (next === $CLOSE_PAREN) {
+      return appendEndGroup(CLOSE_PAREN_INFO, ")", OPEN_PAREN_TOKEN);
+    }
+
+    if (next === $COMMA) {
+      appendPrecenceToken(COMMA_INFO);
+      return advance();
+    }
+
+    if (next === $COLON) {
+      appendPrecenceToken(COLON_INFO);
+      return advance();
+    }
+
+    if (next === $SEMICOLON) {
+      appendPrecenceToken(SEMICOLON_INFO);
+      discardOpenLt();
+      return advance();
+    }
+
+    if (next === $QUESTION) {
+      appendPrecenceToken(QUESTION_INFO);
+      return advance();
+    }
+
+    if (next === $CLOSE_SQUARE_BRACKET) {
+      return appendEndGroup(CLOSE_SQUARE_BRACKET_INFO, "]",
+                            OPEN_SQUARE_BRACKET_TOKEN);
+    }
+
+    if (next === $BACKPING) {
+      appendPrecenceToken(BACKPING_INFO);
+      return advance();
+    }
+
+    if (next === $OPEN_CURLY_BRACKET) {
+      appendBeginGroup(OPEN_CURLY_BRACKET_INFO, "{");
+      return advance();
+    }
+
+    if (next === $CLOSE_CURLY_BRACKET) {
+      return appendEndGroup(CLOSE_CURLY_BRACKET_INFO, "}",
+                            OPEN_CURLY_BRACKET_TOKEN);
+    }
+
+    if (next === $SLASH) {
+      return tokenizeSlashOrComment(next);
+    }
+
+    if (next === $AT) {
+      return tokenizeRawString(next);
+    }
+
+    if (next === $DQ || next === $SQ) {
+      return tokenizeString(next, byteOffset, false);
+    }
+
+    if (next === $PERIOD) {
+      return tokenizeDotOrNumber(next);
+    }
+
+    if (next === $0) {
+      return tokenizeHexOrNumber(next);
+    }
+
+    // TODO(ahe): Would a range check be faster?
+    if (next === $1 || next === $2 || next === $3 || next === $4 ||  next === $5
+        || next === $6 || next === $7 || next === $8 || next === $9) {
+      return tokenizeNumber(next);
+    }
+
+    if (next === $EOF) {
+      return $EOF;
+    }
+    if (next < 0x1f) {
+      throw new MalformedInputException("illegal character $next", charOffset);
+    }
+
+    // The following are non-ASCII characters.
+
+    if (next === $NBSP) {
+      appendWhiteSpace(next);
+      return advance();
+    }
+
+    return tokenizeIdentifier(next, byteOffset, true);
+  }
+
+  int tokenizeTag(int next) {
+    // # or #!.*[\n\r]
+    if (byteOffset === 0) {
+      if (peek() === $BANG) {
+        do {
+          next = advance();
+        } while (next !== $LF && next !== $CR && next !== $EOF);
+        return next;
+      }
+    }
+    appendPrecenceToken(HASH_INFO);
+    return advance();
+  }
+
+  int tokenizeTilde(int next) {
+    // ~ ~/ ~/=
+    next = advance();
+    if (next === $SLASH) {
+      return select($EQ, TILDE_SLASH_EQ_INFO, TILDE_SLASH_INFO);
+    } else {
+      appendPrecenceToken(TILDE_INFO);
+      return next;
+    }
+  }
+
+  int tokenizeOpenSquareBracket(int next) {
+    // [ [] []=
+    next = advance();
+    if (next === $CLOSE_SQUARE_BRACKET) {
+      return select($EQ, INDEX_EQ_INFO, INDEX_INFO);
+    } else {
+      appendBeginGroup(OPEN_SQUARE_BRACKET_INFO, "[");
+      return next;
+    }
+  }
+
+  int tokenizeCaret(int next) {
+    // ^ ^=
+    return select($EQ, CARET_EQ_INFO, CARET_INFO);
+  }
+
+  int tokenizeBar(int next) {
+    // | || |=
+    next = advance();
+    if (next === $BAR) {
+      appendPrecenceToken(BAR_BAR_INFO);
+      return advance();
+    } else if (next === $EQ) {
+      appendPrecenceToken(BAR_EQ_INFO);
+      return advance();
+    } else {
+      appendPrecenceToken(BAR_INFO);
+      return next;
+    }
+  }
+
+  int tokenizeAmpersand(int next) {
+    // && &= &
+    next = advance();
+    if (next === $AMPERSAND) {
+      appendPrecenceToken(AMPERSAND_AMPERSAND_INFO);
+      return advance();
+    } else if (next === $EQ) {
+      appendPrecenceToken(AMPERSAND_EQ_INFO);
+      return advance();
+    } else {
+      appendPrecenceToken(AMPERSAND_INFO);
+      return next;
+    }
+  }
+
+  int tokenizePercent(int next) {
+    // % %=
+    return select($EQ, PERCENT_EQ_INFO, PERCENT_INFO);
+  }
+
+  int tokenizeMultiply(int next) {
+    // * *=
+    return select($EQ, STAR_EQ_INFO, STAR_INFO);
+  }
+
+  int tokenizeMinus(int next) {
+    // - -- -=
+    next = advance();
+    if (next === $MINUS) {
+      appendPrecenceToken(MINUS_MINUS_INFO);
+      return advance();
+    } else if (next === $EQ) {
+      appendPrecenceToken(MINUS_EQ_INFO);
+      return advance();
+    } else {
+      appendPrecenceToken(MINUS_INFO);
+      return next;
+    }
+  }
+
+
+  int tokenizePlus(int next) {
+    // + ++ +=
+    next = advance();
+    if ($PLUS === next) {
+      appendPrecenceToken(PLUS_PLUS_INFO);
+      return advance();
+    } else if ($EQ === next) {
+      appendPrecenceToken(PLUS_EQ_INFO);
+      return advance();
+    } else {
+      appendPrecenceToken(PLUS_INFO);
+      return next;
+    }
+  }
+
+  int tokenizeExclamation(int next) {
+    // ! != !==
+    next = advance();
+    if (next === $EQ) {
+      return select($EQ, BANG_EQ_EQ_INFO, BANG_EQ_INFO);
+    }
+    appendPrecenceToken(BANG_INFO);
+    return next;
+  }
+
+  int tokenizeEquals(int next) {
+    // = == ===
+    next = advance();
+    if (next === $EQ) {
+      return select($EQ, EQ_EQ_EQ_INFO, EQ_EQ_INFO);
+    } else if (next === $GT) {
+      appendPrecenceToken(FUNCTION_INFO);
+      return advance();
+    }
+    appendPrecenceToken(EQ_INFO);
+    return next;
+  }
+
+  int tokenizeGreaterThan(int next) {
+    // > >= >> >>= >>> >>>=
+    next = advance();
+    if ($EQ === next) {
+      appendPrecenceToken(GT_EQ_INFO);
+      return advance();
+    } else if ($GT === next) {
+      next = advance();
+      if ($EQ === next) {
+        appendPrecenceToken(GT_GT_EQ_INFO);
+        return advance();
+      } else if ($GT === next) {
+        next = advance();
+        if (next === $EQ) {
+          appendPrecenceToken(GT_GT_GT_EQ_INFO);
+          return advance();
+        } else {
+          appendGtGtGt(GT_GT_GT_INFO, ">>>");
+          return next;
+        }
+      } else {
+        appendGtGt(GT_GT_INFO, ">>");
+        return next;
+      }
+    } else {
+      appendGt(GT_INFO, ">");
+      return next;
+    }
+  }
+
+  int tokenizeLessThan(int next) {
+    // < <= << <<=
+    next = advance();
+    if ($EQ === next) {
+      appendPrecenceToken(LT_EQ_INFO);
+      return advance();
+    } else if ($LT === next) {
+      return select($EQ, LT_LT_EQ_INFO, LT_LT_INFO);
+    } else {
+      appendBeginGroup(LT_INFO, "<");
+      return next;
+    }
+  }
+
+  int tokenizeNumber(int next) {
+    int start = byteOffset;
+    while (true) {
+      next = advance();
+      if ($0 <= next && next <= $9) {
+        continue;
+      } else if (next === $PERIOD) {
+        return tokenizeFractionPart(advance(), start);
+      } else if (next === $e || next === $E || next === $d || next === $D) {
+        return tokenizeFractionPart(next, start);
+      } else {
+        appendByteStringToken(INT_INFO, asciiString(start, 0));
+        return next;
+      }
+    }
+  }
+
+  int tokenizeHexOrNumber(int next) {
+    int x = peek();
+    if (x === $x || x === $X) {
+      advance();
+      return tokenizeHex(x);
+    }
+    return tokenizeNumber(next);
+  }
+
+  int tokenizeHex(int next) {
+    int start = byteOffset - 1;
+    bool hasDigits = false;
+    while (true) {
+      next = advance();
+      if (($0 <= next && next <= $9)
+          || ($A <= next && next <= $F)
+          || ($a <= next && next <= $f)) {
+        hasDigits = true;
+      } else {
+        if (!hasDigits) {
+          throw new MalformedInputException("hex digit expected", charOffset);
+        }
+        appendByteStringToken(HEXADECIMAL_INFO, asciiString(start, 0));
+        return next;
+      }
+    }
+  }
+
+  int tokenizeDotOrNumber(int next) {
+    int start = byteOffset;
+    next = advance();
+    if (($0 <= next && next <= $9)) {
+      return tokenizeFractionPart(next, start);
+    } else if ($PERIOD === next) {
+      return select($PERIOD, PERIOD_PERIOD_PERIOD_INFO, PERIOD_PERIOD_INFO);
+    } else {
+      appendPrecenceToken(PERIOD_INFO);
+      return next;
+    }
+  }
+
+  int tokenizeFractionPart(int next, int start) {
+    bool done = false;
+    bool hasDigit = false;
+    LOOP: while (!done) {
+      if ($0 <= next && next <= $9) {
+        hasDigit = true;
+      } else if ($e === next || $E === next) {
+        hasDigit = true;
+        next = tokenizeExponent(advance());
+        done = true;
+        continue LOOP;
+      } else {
+        done = true;
+        continue LOOP;
+      }
+      next = advance();
+    }
+    if (!hasDigit) {
+      appendByteStringToken(INT_INFO, asciiString(start, -1));
+      // TODO(ahe): Wrong offset for the period.
+      appendPrecenceToken(PERIOD_INFO);
+      return bigSwitch(next);
+    }
+    if (next === $d || next === $D) {
+      next = advance();
+    }
+    appendByteStringToken(DOUBLE_INFO, asciiString(start, 0));
+    return next;
+  }
+
+  int tokenizeExponent(int next) {
+    if (next === $PLUS || next === $MINUS) {
+      next = advance();
+    }
+    bool hasDigits = false;
+    while (true) {
+      if ($0 <= next && next <= $9) {
+        hasDigits = true;
+      } else {
+        if (!hasDigits) {
+          throw new MalformedInputException("digit expected", charOffset);
+        }
+        return next;
+      }
+      next = advance();
+    }
+  }
+
+  int tokenizeSlashOrComment(int next) {
+    next = advance();
+    if ($STAR === next) {
+      return tokenizeMultiLineComment(next);
+    } else if ($SLASH === next) {
+      return tokenizeSingleLineComment(next);
+    } else if ($EQ === next) {
+      appendPrecenceToken(SLASH_EQ_INFO);
+      return advance();
+    } else {
+      appendPrecenceToken(SLASH_INFO);
+      return next;
+    }
+  }
+
+  int tokenizeSingleLineComment(int next) {
+    while (true) {
+      next = advance();
+      if ($LF === next || $CR === next || $EOF === next) {
+        return next;
+      }
+    }
+  }
+
+  int tokenizeMultiLineComment(int next) {
+    int nesting = 1;
+    next = advance();
+    while (true) {
+      if ($EOF === next) {
+        // TODO(ahe): Report error.
+        return next;
+      } else if ($STAR === next) {
+        next = advance();
+        if ($SLASH === next) {
+          --nesting;
+          if (0 === nesting) {
+            return advance();
+          } else {
+            next = advance();
+          }
+        }
+      } else if ($SLASH === next) {
+        next = advance();
+        if ($STAR === next) {
+          next = advance();
+          ++nesting;
+        }
+      } else {
+        next = advance();
+      }
+    }
+  }
+
+  int tokenizeKeywordOrIdentifier(int next, bool allowDollar) {
+    KeywordState state = KeywordState.KEYWORD_STATE;
+    int start = byteOffset;
+    while (state !== null && $a <= next && next <= $z) {
+      state = state.next(next);
+      next = advance();
+    }
+    if (state === null || state.keyword === null) {
+      return tokenizeIdentifier(next, start, allowDollar);
+    }
+    if (($A <= next && next <= $Z) ||
+        ($0 <= next && next <= $9) ||
+        next === $_ ||
+        next === $$) {
+      return tokenizeIdentifier(next, start, allowDollar);
+    } else if (next < 128) {
+      appendKeywordToken(state.keyword);
+      return next;
+    } else {
+      return tokenizeIdentifier(next, start, allowDollar);
+    }
+  }
+
+  int tokenizeIdentifier(int next, int start, bool allowDollar) {
+    bool isAscii = true;
+    while (true) {
+      if (($a <= next && next <= $z) ||
+          ($A <= next && next <= $Z) ||
+          ($0 <= next && next <= $9) ||
+          next === $_ ||
+          (next === $$ && allowDollar)) {
+        next = advance();
+      } else if (next < 128) {
+        if (isAscii) {
+          appendByteStringToken(IDENTIFIER_INFO, asciiString(start, 0));
+        } else {
+          appendByteStringToken(IDENTIFIER_INFO, utf8String(start, -1));
+        }
+        return next;
+      } else {
+        int nonAsciiStart = byteOffset;
+        do {
+          next = nextByte();
+        } while (next > 127);
+        String string = utf8String(nonAsciiStart, -1).slowToString();
+        isAscii = false;
+        int byteLength = nonAsciiStart - byteOffset;
+        addToCharOffset(string.length - byteLength);
+      }
+    }
+  }
+
+  int tokenizeRawString(int next) {
+    int start = byteOffset;
+    next = advance();
+    if (next === $DQ || next === $SQ) {
+      return tokenizeString(next, start, true);
+    } else {
+      throw new MalformedInputException("expected ' or \"", charOffset);
+    }
+  }
+
+  int tokenizeString(int next, int start, bool raw) {
+    int quoteChar = next;
+    next = advance();
+    if (quoteChar === next) {
+      next = advance();
+      if (quoteChar === next) {
+        // Multiline string.
+        return tokenizeMultiLineString(quoteChar, start, raw);
+      } else {
+        // Empty string.
+        appendByteStringToken(STRING_INFO, utf8String(start, -1));
+        return next;
+      }
+    }
+    if (raw) {
+      return tokenizeSingleLineRawString(next, quoteChar, start);
+    } else {
+      return tokenizeSingleLineString(next, quoteChar, start);
+    }
+  }
+
+  static bool isHexDigit(int character) {
+    if ($0 <= character && character <= $9) return true;
+    character |= 0x20;
+    return ($a <= character && character <= $f);
+  }
+
+  int tokenizeSingleLineString(int next, int quoteChar, int start) {
+    while (next !== quoteChar) {
+      if (next === $BACKSLASH) {
+        next = advance();
+      } else if (next === $$) {
+        next = tokenizeStringInterpolation(start);
+        start = byteOffset;
+        continue;
+      }
+      if (next <= $CR && (next === $LF || next === $CR || next === $EOF)) {
+        throw new MalformedInputException("unterminated string literal",
+                                          charOffset);
+      }
+      next = advance();
+    }
+    appendByteStringToken(STRING_INFO, utf8String(start, 0));
+    return advance();
+  }
+
+  int tokenizeStringInterpolation(int start) {
+    beginToken();
+    int next = advance();
+    if (next === $OPEN_CURLY_BRACKET) {
+      return tokenizeInterpolatedExpression(next, start);
+    } else {
+      return tokenizeInterpolatedIdentifier(next, start);
+    }
+  }
+
+  int tokenizeInterpolatedExpression(int next, int start) {
+    appendByteStringToken(STRING_INFO, utf8String(start, -2));
+    appendBeginGroup(STRING_INTERPOLATION_INFO, "\${");
+    next = advance();
+    while (next !== $EOF && next !== $STX) {
+      next = bigSwitch(next);
+    }
+    if (next === $EOF) return next;
+    return advance();
+  }
+
+  int tokenizeInterpolatedIdentifier(int next, int start) {
+    appendByteStringToken(STRING_INFO, utf8String(start, -2));
+    appendBeginGroup(STRING_INTERPOLATION_INFO, "\${");
+    next = tokenizeKeywordOrIdentifier(next, false);
+    appendEndGroup(CLOSE_CURLY_BRACKET_INFO, "}", OPEN_CURLY_BRACKET_TOKEN);
+    return next;
+  }
+
+  int tokenizeSingleLineRawString(int next, int quoteChar, int start) {
+    next = advance();
+    while (next != $EOF) {
+      if (next === quoteChar) {
+        appendByteStringToken(STRING_INFO, utf8String(start, 0));
+        return advance();
+      } else if (next === $LF || next === $CR) {
+        throw new MalformedInputException("unterminated string literal",
+                                          charOffset);
+      }
+      next = advance();
+    }
+    throw new MalformedInputException("unterminated string literal",
+                                      charOffset);
+  }
+
+  int tokenizeMultiLineRawString(int quoteChar, int start) {
+    int next = advance();
+    outer: while (next !== $EOF) {
+      while (next !== quoteChar) {
+        next = advance();
+        if (next === $EOF) break outer;
+      }
+      next = advance();
+      if (next === quoteChar) {
+        next = advance();
+        if (next === quoteChar) {
+          appendByteStringToken(STRING_INFO, utf8String(start, 0));
+          return advance();
+        }
+      }
+    }
+    throw new MalformedInputException("unterminated string literal",
+                                      charOffset);
+  }
+
+  int tokenizeMultiLineString(int quoteChar, int start, bool raw) {
+    if (raw) return tokenizeMultiLineRawString(quoteChar, start);
+    int next = advance();
+    while (next !== $EOF) {
+      if (next === $$) {
+        next = tokenizeStringInterpolation(start);
+        start = byteOffset;
+        continue;
+      }
+      if (next === quoteChar) {
+        next = advance();
+        if (next === quoteChar) {
+          next = advance();
+          if (next === quoteChar) {
+            appendByteStringToken(STRING_INFO, utf8String(start, 0));
+            return advance();
+          }
+        }
+        continue;
+      }
+      if (next === $BACKSLASH) {
+        next = advance();
+        if (next === $EOF) break;
+      }
+      next = advance();
+    }
+    throw new MalformedInputException("unterminated string literal",
+                                      charOffset);
+  }
+}
+
+class MalformedInputException {
+  final String message;
+  final position;
+  MalformedInputException(this.message, this.position);
+  toString() => message;
+}
diff --git a/lib/compiler/implementation/scanner/scanner_bench.dart b/lib/compiler/implementation/scanner/scanner_bench.dart
new file mode 100644
index 0000000..2c3e345
--- /dev/null
+++ b/lib/compiler/implementation/scanner/scanner_bench.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2012, 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('scanner_bench');
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#source('source_list.dart');
+
+/**
+ * A common superclass for scanner benchmarks.
+ */
+class ScannerBench {
+  void main(List<String> arguments) {
+    for (String argument in arguments) {
+      checkExistence(argument);
+    }
+    tokenizeAll(print, 10, arguments);
+    tokenizeAll((x) {}, 1000, arguments);
+    tokenizeAll(print, 10, arguments);
+  }
+
+  void tokenizeAll(void log(String s), int iterations, List<String> arguments) {
+    VerboseProgressBar bar = new VerboseProgressBar(iterations);
+    bar.begin();
+    for (int i = 0; i < iterations; i++) {
+      bar.tick();
+      Stopwatch timer = new Stopwatch();
+      timer.start();
+      int charCount = 0;
+      for (final String argument in arguments) {
+        charCount += tokenizeOne(argument);
+      }
+      timer.stop();
+      bar.recordScore(charCount / timer.elapsedInMs());
+      log("Tokenized ${arguments.length} files " +
+          "(total size = ${charCount} chars) " +
+          "in ${timer.elapsedInMs()}ms");
+    }
+    bar.end();
+  }
+
+  int tokenizeOne(String filename) {
+    return getBytes(filename, (bytes) {
+      Scanner scanner = makeScanner(bytes);
+      try {
+        printTokens(scanner.tokenize());
+      } catch (MalformedInputException e) {
+        print("${filename}: ${e}");
+      }
+    });
+  }
+
+  void printTokens(Token token) {
+    // TODO(ahe): Turn this into a proper test.
+    return;
+    StringBuffer sb = new StringBuffer();
+    for (; token != null; token = token.next) {
+      if (token.kind < 127) {
+        sb.add(new String.fromCharCodes([token.kind]));
+      } else {
+        sb.add(token.kind);
+      }
+      sb.add(":");
+      sb.add(token);
+      sb.add(" ");
+    }
+    print(sb.toString());
+  }
+
+  abstract int getBytes(String filename, void callback(bytes));
+  abstract Scanner makeScanner(bytes);
+  abstract void checkExistence(String filename);
+}
+
+class ProgressBar {
+  static final String hashes = "##############################################";
+  static final String spaces = "                                              ";
+  static final int GEOMEAN_COUNT = 50;
+
+  final String esc;
+  final String up;
+  final String clear;
+  final int total;
+  final List<num> scores;
+  int ticks = 0;
+
+  ProgressBar(int total) : this.escape(total, new String.fromCharCodes([27]));
+
+  ProgressBar.escape(this.total, String esc)
+    : esc = esc, up = "$esc[1A", clear = "$esc[K", scores = new List<num>();
+
+  void begin() {
+    if (total > 10) {
+      print("[$spaces] 0%");
+      print("$up[${hashes.substring(0, ticks * spaces.length ~/ total)}");
+    }
+  }
+
+  void tick() {
+    if (total > 10 && ticks % 5 === 0) {
+      print("$up$clear[$spaces] ${ticks * 100 ~/ total}% ${score()}");
+      print("$up[${hashes.substring(0, ticks * spaces.length ~/ total)}");
+    }
+    ++ticks;
+  }
+
+  void end() {
+    if (total > 10) {
+      print("$up$clear[$hashes] 100% ${score()}");
+    }
+  }
+
+  void recordScore(num score) {
+    scores.addLast(score);
+  }
+
+  int score() {
+    num geoMean = 1;
+    int count = Math.min(scores.length, GEOMEAN_COUNT);
+    for (int i = scores.length - count; i < scores.length; i++) {
+      geoMean *= scores[i];
+    }
+    geoMean = Math.pow(geoMean, 1/Math.max(count, 1));
+    return geoMean.round().toInt();
+  }
+}
+
+class VerboseProgressBar {
+  final int total;
+  int ticks = 0;
+
+  VerboseProgressBar(int this.total);
+
+  void begin() {
+  }
+
+  void tick() {
+    ++ticks;
+  }
+
+  void end() {
+  }
+
+  void recordScore(num score) {
+    if (total > 10) {
+      print("$ticks, $score, ${ticks * 100 ~/ total}%");
+    }
+  }
+}
diff --git a/lib/compiler/implementation/scanner/scanner_implementation.dart b/lib/compiler/implementation/scanner/scanner_implementation.dart
new file mode 100644
index 0000000..5bc43e7
--- /dev/null
+++ b/lib/compiler/implementation/scanner/scanner_implementation.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2011, 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('scanner_implementation');
+#import('scannerlib.dart');
+#import('../util/util.dart');
+#import('../util/characters.dart');
+#source('array_based_scanner.dart');
diff --git a/lib/compiler/implementation/scanner/scanner_task.dart b/lib/compiler/implementation/scanner/scanner_task.dart
new file mode 100644
index 0000000..d0a09b6
--- /dev/null
+++ b/lib/compiler/implementation/scanner/scanner_task.dart
@@ -0,0 +1,204 @@
+// Copyright (c) 2012, 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.
+
+class ScannerTask extends CompilerTask {
+  ScannerTask(Compiler compiler) : super(compiler);
+  String get name() => 'Scanner';
+
+  void scan(CompilationUnitElement compilationUnit) {
+    measure(() {
+      if (compilationUnit.kind === ElementKind.LIBRARY) {
+        compiler.log("scanning library ${compilationUnit.script.name}");
+      }
+      scanElements(compilationUnit);
+      if (compilationUnit.kind === ElementKind.LIBRARY) {
+        processScriptTags(compilationUnit);
+      }
+    });
+  }
+
+  void processScriptTags(LibraryElement library) {
+    int tagState = TagState.NO_TAG_SEEN;
+
+    /**
+     * If [value] is less than [tagState] complain and return
+     * [tagState]. Otherwise return the new value for [tagState]
+     * (transition function for state machine).
+     */
+    int checkTag(int value, ScriptTag tag) {
+      if (tagState > value) {
+        compiler.reportError(tag, 'out of order');
+        return tagState;
+      }
+      return TagState.NEXT[value];
+    }
+
+    LinkBuilder<ScriptTag> imports = new LinkBuilder<ScriptTag>();
+    Uri cwd = new Uri(scheme: 'file', path: compiler.currentDirectory);
+    Uri base = cwd.resolve(library.script.name.toString());
+    for (ScriptTag tag in library.tags.reverse()) {
+      StringNode argument = tag.argument;
+      // TODO(lrn): Support interpolations here. We need access to the
+      // special constants that can be inserted into script tag strings.
+      Uri resolved = base.resolve(argument.dartString.slowToString());
+      if (tag.isImport()) {
+        tagState = checkTag(TagState.IMPORT, tag);
+        // It is not safe to import other libraries at this point as
+        // another library could then observe the current library
+        // before it fully declares all the members that are sourced
+        // in.
+        imports.addLast(tag);
+      } else if (tag.isLibrary()) {
+        tagState = checkTag(TagState.LIBRARY, tag);
+        if (library.libraryTag !== null) {
+          compiler.cancel("duplicated library declaration", node: tag);
+        } else {
+          library.libraryTag = tag;
+        }
+      } else if (tag.isSource()) {
+        tagState = checkTag(TagState.SOURCE, tag);
+        Script script = compiler.readScript(resolved, tag);
+        CompilationUnitElement unit =
+          new CompilationUnitElement(script, library);
+        compiler.withCurrentElement(unit, () => scan(unit));
+      } else if (tag.isResource()) {
+        tagState = checkTag(TagState.RESOURCE, tag);
+        compiler.reportWarning(tag, 'ignoring resource tag');
+      } else {
+        compiler.cancel("illegal script tag: ${tag.tag}", node: tag);
+      }
+    }
+    // TODO(ahe): During Compiler.scanBuiltinLibraries,
+    // compiler.coreLibrary is null. Clean this up when there is a
+    // better way to access "dart:core".
+    bool implicitlyImportCoreLibrary = compiler.coreLibrary !== null;
+    for (ScriptTag tag in imports.toLink()) {
+      // Now that we have processed all the source tags, it is safe to
+      // start loading other libraries.
+      StringNode argument = tag.argument;
+      Uri resolved = base.resolve(argument.dartString.slowToString());
+      if (resolved.toString() == "dart:core") {
+        implicitlyImportCoreLibrary = false;
+      }
+      importLibrary(library, loadLibrary(resolved, tag), tag);
+    }
+    if (implicitlyImportCoreLibrary) {
+      importLibrary(library, compiler.coreLibrary, null);
+    }
+  }
+
+  void scanElements(CompilationUnitElement compilationUnit) {
+    Script script = compilationUnit.script;
+    Token tokens;
+    try {
+      tokens = new StringScanner(script.text).tokenize();
+    } catch (MalformedInputException ex) {
+      Token token;
+      var message;
+      if (ex.position is num) {
+        // TODO(ahe): Always use tokens in MalformedInputException.
+        token = new Token(EOF_INFO, ex.position);
+      } else {
+        token = ex.position;
+      }
+      compiler.cancel(ex.message, token: token);
+    }
+    compiler.dietParser.dietParse(compilationUnit, tokens);
+  }
+
+  LibraryElement loadLibrary(Uri uri, ScriptTag node) {
+    bool newLibrary = false;
+    LibraryElement library =
+      compiler.universe.libraries.putIfAbsent(uri.toString(), () {
+          newLibrary = true;
+          Script script = compiler.readScript(uri, node);
+          LibraryElement element = new LibraryElement(script);
+          native.maybeEnableNative(compiler, element, uri);
+          return element;
+        });
+    if (newLibrary) {
+      compiler.withCurrentElement(library, () => scan(library));
+      compiler.onLibraryLoaded(library, uri);
+    }
+    return library;
+  }
+
+  void importLibrary(LibraryElement library, LibraryElement imported,
+                     ScriptTag tag) {
+    if (!imported.hasLibraryName()) {
+      compiler.withCurrentElement(library, () {
+        compiler.reportError(tag,
+                             'no #library tag found in ${imported.script.uri}');
+      });
+    }
+    if (tag !== null && tag.prefix !== null) {
+      SourceString prefix =
+          new SourceString(tag.prefix.dartString.slowToString());
+      Element e = library.find(prefix);
+      if (e === null) {
+        e = new PrefixElement(prefix, library, tag.getBeginToken());
+        library.define(e, compiler);
+      }
+      if (e.kind !== ElementKind.PREFIX) {
+        compiler.withCurrentElement(e, () {
+          compiler.reportWarning(new Identifier(e.position()),
+                                 'duplicated definition');
+        });
+        compiler.reportError(tag.prefix, 'duplicate defintion');
+      }
+      imported.forEachExport((Element element) {
+        Element existing = e.imported.putIfAbsent(element.name, () => element);
+        if (existing !== element) {
+          compiler.withCurrentElement(existing, () {
+            compiler.reportWarning(new Identifier(existing.position()),
+                                   'duplicated import');
+          });
+          compiler.withCurrentElement(element, () {
+            compiler.reportError(new Identifier(element.position()),
+                                 'duplicated import');
+          });
+        }
+      });
+    } else {
+      imported.forEachExport((Element element) {
+        compiler.withCurrentElement(element, () {
+          library.define(element, compiler);
+        });
+      });
+    }
+  }
+}
+
+class DietParserTask extends CompilerTask {
+  DietParserTask(Compiler compiler) : super(compiler);
+  final String name = 'Diet Parser';
+
+  dietParse(CompilationUnitElement compilationUnit, Token tokens) {
+    measure(() {
+      ElementListener listener = new ElementListener(compiler, compilationUnit);
+      PartialParser parser = new PartialParser(listener);
+      parser.parseUnit(tokens);
+    });
+  }
+}
+
+/**
+ * The fields of this class models a state machine for checking script
+ * tags come in the correct order.
+ */
+class TagState {
+  static final int NO_TAG_SEEN = 0;
+  static final int LIBRARY = 1;
+  static final int IMPORT = 2;
+  static final int SOURCE = 3;
+  static final int RESOURCE = 4;
+
+  /** Next state. */
+  static final List<int> NEXT =
+      const <int>[NO_TAG_SEEN,
+                  IMPORT, // Only one library tag is allowed.
+                  IMPORT,
+                  SOURCE,
+                  RESOURCE];
+}
diff --git a/lib/compiler/implementation/scanner/scannerlib.dart b/lib/compiler/implementation/scanner/scannerlib.dart
new file mode 100644
index 0000000..1ce859b
--- /dev/null
+++ b/lib/compiler/implementation/scanner/scannerlib.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2011, 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('scanner');
+
+#import('scanner_implementation.dart');
+#import('../../../uri/uri.dart');
+#import('../elements/elements.dart');
+#import('../leg.dart');
+#import('../native_handler.dart', prefix: 'native');
+#import('../string_validator.dart');
+#import('../tree/tree.dart');
+#import('../util/characters.dart');
+#import('../util/util.dart');
+
+#source('class_element_parser.dart');
+#source('keyword.dart');
+#source('listener.dart');
+#source('parser.dart');
+#source('parser_task.dart');
+#source('partial_parser.dart');
+#source('scanner.dart');
+#source('scanner_task.dart');
+#source('string_scanner.dart');
+#source('token.dart');
diff --git a/lib/compiler/implementation/scanner/source_list.dart b/lib/compiler/implementation/scanner/source_list.dart
new file mode 100644
index 0000000..c6f57a5
--- /dev/null
+++ b/lib/compiler/implementation/scanner/source_list.dart
@@ -0,0 +1,388 @@
+// Copyright (c) 2012, 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.
+
+final List<String> argv = const <String>[
+'../lib/dom/common/implementation.dart',
+'../lib/dom/common/public.dart',
+'../lib/dom/dom.dart',
+'../lib/dom/scripts/idlparser.dart',
+'../lib/dom/scripts/idlparser_test.dart',
+'../lib/dom/scripts/idlrenderer.dart',
+'../lib/dom/src/DOMType.dart',
+'../lib/dom/src/EventListener.dart',
+'../lib/dom/src/KeyLocation.dart',
+'../lib/dom/src/KeyName.dart',
+'../lib/dom/src/ReadyState.dart',
+'../lib/dom/src/TimeoutHandler.dart',
+'../lib/dom/src/_Collections.dart',
+'../lib/dom/src/_ListIterators.dart',
+'../lib/dom/src/_Lists.dart',
+'../lib/dom/src/dummy_FactoryProviders.dart',
+'../lib/dom/src/dummy_GlobalProperties.dart',
+'../lib/dom/src/frog_DOMType.dart',
+'../lib/dom/src/native_DOMPublic.dart',
+'../lib/dom/src/native_DOMWrapperBase.dart',
+'../lib/dom/src/native_FactoryProviders.dart',
+'../lib/dom/src/native_GlobalProperties.dart',
+'../lib/html/benchmarks/common/BenchUtil.dart',
+'../lib/html/benchmarks/common/Interval.dart',
+'../lib/html/benchmarks/common/JSON.dart',
+'../lib/html/benchmarks/common/Math2.dart',
+'../lib/html/benchmarks/dromaeo/Dromaeo.dart',
+'../lib/html/benchmarks/dromaeo/Suites.dart',
+'../lib/html/frog/html_frog.dart',
+'../lib/html/dartium/html_dartium.dart',
+'../client/testing/dartest/css.dart',
+'../client/testing/dartest/dartest.dart',
+'../client/testing/unittest/shared.dart',
+'../client/testing/unittest/unittest_dom.dart',
+'../client/testing/unittest/unittest_dartest.dart',
+'../client/testing/unittest/unittest_node.dart',
+'../client/testing/unittest/unittest_vm.dart',
+'../corelib/src/bool.dart',
+'../corelib/src/collection.dart',
+'../corelib/src/comparable.dart',
+'../corelib/src/date.dart',
+'../corelib/src/double.dart',
+'../corelib/src/duration.dart',
+'../corelib/src/exceptions.dart',
+'../corelib/src/expect.dart',
+'../corelib/src/function.dart',
+'../corelib/src/future.dart',
+'../corelib/src/hashable.dart',
+'../corelib/src/implementation/dual_pivot_quicksort.dart',
+'../corelib/src/implementation/duration_implementation.dart',
+'../corelib/src/implementation/exceptions.dart',
+'../corelib/src/implementation/future_implementation.dart',
+'../corelib/src/implementation/hash_map_set.dart',
+'../corelib/src/implementation/linked_hash_map.dart',
+'../corelib/src/implementation/maps.dart',
+'../corelib/src/implementation/queue.dart',
+'../corelib/src/implementation/splay_tree.dart',
+'../corelib/src/implementation/stopwatch_implementation.dart',
+'../corelib/src/int.dart',
+'../corelib/src/iterable.dart',
+'../corelib/src/iterator.dart',
+'../corelib/src/list.dart',
+'../corelib/src/map.dart',
+'../corelib/src/math.dart',
+'../corelib/src/num.dart',
+'../corelib/src/options.dart',
+'../corelib/src/pattern.dart',
+'../corelib/src/queue.dart',
+'../corelib/src/regexp.dart',
+'../corelib/src/set.dart',
+'../corelib/src/stopwatch.dart',
+'../corelib/src/string.dart',
+'../corelib/src/string_buffer.dart',
+'../corelib/src/strings.dart',
+'../corelib/src/time_zone.dart',
+'../lib/json/json.dart',
+'../lib/utf/utf.dart',
+'../lib/utf/utf_core.dart',
+'../lib/utf/utf8.dart',
+'../lib/utf/utf16.dart',
+'../lib/utf/utf32.dart',
+'../lib/uri/uri.dart',
+'../lib/isolate/isolate_api.dart',
+'../lib/isolate/isolate_frog.dart',
+'../lib/isolate/frog/isolateimpl.dart',
+'../lib/isolate/frog/ports.dart',
+'../lib/isolate/frog/messages.dart',
+'../samples/actors/core/actors-dom.dart',
+'../samples/actors/core/actors-interface.dart',
+'../samples/actors/core/actors-term.dart',
+'../samples/actors/core/actors-ui.dart',
+'../samples/actors/core/actors-web.dart',
+'../samples/actors/core/actors.dart',
+'../samples/actors/samples/dartcombat/dartcombat.dart',
+'../samples/actors/samples/dartcombat/dartcombatlib.dart',
+'../samples/actors/samples/dartcombat/grids.dart',
+'../samples/actors/samples/dartcombat/player.dart',
+'../samples/actors/samples/dartcombat/setup.dart',
+'../samples/actors/samples/dartcombat/state.dart',
+'../samples/actors/samples/dartcombat/views.dart',
+'../samples/actors/samples/helloworld/helloconcert.dart',
+'../samples/actors/samples/helloworld/helloworld.dart',
+'../samples/actors/samples/life/life.dart',
+'../samples/actors/samples/math/controlflow-recursive.dart',
+'../samples/actors/samples/math/sumup.dart',
+'../samples/actors/samples/pingpong/pingpong.dart',
+'../samples/actors/samples/sssp/graph.dart',
+'../samples/actors/samples/sssp/sssp-single.dart',
+'../samples/actors/samples/sssp/sssp-util.dart',
+'../samples/actors/samples/sssp/sssp.dart',
+'../samples/actors/samples/threadring/threadring.dart',
+'../samples/belay/bcap/bcap.dart',
+'../samples/belay/bcap/bcap_fling.dart',
+'../samples/belay/bcap/src/BcapFlingServer.dart',
+'../samples/belay/bcap/src/BcapTunnel.dart',
+'../samples/belay/bcap/src/BcapUtil.dart',
+'../samples/belay/bcap/src/BelayClient.dart',
+'../samples/belay/bcap/src/impl_bcap.dart',
+'../samples/belay/buzzer/BuzzerServer.dart',
+'../samples/belay/buzzer/static/Buzzer.dart',
+'../samples/belay/buzzer/static/Index.dart',
+'../samples/chat/chat_server.dart',
+'../samples/chat/chat_server_lib.dart',
+'../samples/chat/chat_stress_client.dart',
+'../samples/chat/dart_client/chat.dart',
+'../samples/chat/http.dart',
+'../samples/chat/http_impl.dart',
+'../samples/clock/Ball.dart',
+'../samples/clock/Balls.dart',
+'../samples/clock/Clock.dart',
+'../samples/clock/ClockNumber.dart',
+'../samples/clock/ClockNumbers.dart',
+'../samples/clock/Colon.dart',
+'../samples/clock/Util.dart',
+'../samples/dartcombat/dartcombat.dart',
+'../samples/dartcombat/dartcombatlib.dart',
+'../samples/dartcombat/grids.dart',
+'../samples/dartcombat/player.dart',
+'../samples/dartcombat/setup.dart',
+'../samples/dartcombat/state.dart',
+'../samples/dartcombat/views.dart',
+'../samples/hi/hi.dart',
+'../samples/isolate/HelloIsolate.dart',
+'../samples/isolate_html/IsolateSample.dart',
+'../samples/isolate_html/isolate_sample.dart',
+'../samples/logo/logo.dart',
+'../samples/markdown/ast.dart',
+'../samples/markdown/block_parser.dart',
+'../samples/markdown/html_renderer.dart',
+'../samples/markdown/inline_parser.dart',
+'../samples/markdown/lib.dart',
+'../samples/markdown/markdown.dart',
+'../samples/markdown/test/markdown_tests.dart',
+'../samples/matrix/float32.dart',
+'../samples/matrix/matrix4.dart',
+'../samples/matrix/matrix_client.dart',
+'../samples/matrix/matrix_server.dart',
+'../samples/pond/editors.dart',
+'../samples/pond/editors_stub.dart',
+'../samples/pond/html_file_system.dart',
+'../samples/pond/pond.dart',
+'../samples/pond/util.dart',
+'../samples/slider/SliderSample.dart',
+'../samples/slider/slider_sample.dart',
+'../samples/socket/SocketExample.dart',
+'../samples/spirodraw/ColorPicker.dart',
+'../samples/spirodraw/Spirodraw.dart',
+'../samples/sunflower/Sunflower.dart',
+'../samples/swarm/App.dart',
+'../samples/swarm/BiIterator.dart',
+'../samples/swarm/CSS.dart',
+'../samples/swarm/CannedData.dart',
+'../samples/swarm/ConfigHintDialog.dart',
+'../samples/swarm/DataSource.dart',
+'../samples/swarm/Decoder.dart',
+'../samples/swarm/HelpDialog.dart',
+'../samples/swarm/SwarmApp.dart',
+'../samples/swarm/SwarmState.dart',
+'../samples/swarm/SwarmViews.dart',
+'../samples/swarm/UIState.dart',
+'../samples/swarm/Views.dart',
+'../samples/swarm/swarm.dart',
+'../samples/swarm/swarmlib.dart',
+'../samples/third_party/dromaeo/Dromaeo.dart',
+'../samples/third_party/dromaeo/Suites.dart',
+'../samples/third_party/dromaeo/common/BenchUtil.dart',
+'../samples/third_party/dromaeo/common/Interval.dart',
+'../samples/third_party/dromaeo/common/JSON.dart',
+'../samples/third_party/dromaeo/common/Math2.dart',
+'../samples/third_party/dromaeo/common/common.dart',
+'../samples/time/time_server.dart',
+'../samples/total/client/CSVReader.dart',
+'../samples/total/client/Cell.dart',
+'../samples/total/client/CellContents.dart',
+'../samples/total/client/CellLocation.dart',
+'../samples/total/client/CellRange.dart',
+'../samples/total/client/Chart.dart',
+'../samples/total/client/ClientChart.dart',
+'../samples/total/client/ColorPicker.dart',
+'../samples/total/client/Command.dart',
+'../samples/total/client/ContextMenu.dart',
+'../samples/total/client/ContextMenuBuilder.dart',
+'../samples/total/client/CopyPasteManager.dart',
+'../samples/total/client/CssStyles.dart',
+'../samples/total/client/DateTimeUtils.dart',
+'../samples/total/client/DomUtils.dart',
+'../samples/total/client/Exceptions.dart',
+'../samples/total/client/Formats.dart',
+'../samples/total/client/Formula.dart',
+'../samples/total/client/Functions.dart',
+'../samples/total/client/GeneralCommand.dart',
+'../samples/total/client/HtmlTable.dart',
+'../samples/total/client/HtmlUtils.dart',
+'../samples/total/client/IdGenerator.dart',
+'../samples/total/client/IndexedValue.dart',
+'../samples/total/client/InnerMenuView.dart',
+'../samples/total/client/Logger.dart',
+'../samples/total/client/Parser.dart',
+'../samples/total/client/Picker.dart',
+'../samples/total/client/PopupHandler.dart',
+'../samples/total/client/Reader.dart',
+'../samples/total/client/RowCol.dart',
+'../samples/total/client/RowColStyle.dart',
+'../samples/total/client/SYLKReader.dart',
+'../samples/total/client/Scanner.dart',
+'../samples/total/client/SelectionManager.dart',
+'../samples/total/client/ServerChart.dart',
+'../samples/total/client/Spreadsheet.dart',
+'../samples/total/client/SpreadsheetLayout.dart',
+'../samples/total/client/SpreadsheetListener.dart',
+'../samples/total/client/SpreadsheetPresenter.dart',
+'../samples/total/client/StringUtils.dart',
+'../samples/total/client/Style.dart',
+'../samples/total/client/Total.dart',
+'../samples/total/client/TotalLib.dart',
+'../samples/total/client/UndoStack.dart',
+'../samples/total/client/UndoableAction.dart',
+'../samples/total/client/Value.dart',
+'../samples/total/client/ValuePicker.dart',
+'../samples/total/client/ZoomTracker.dart',
+'../samples/total/server/DartCompiler.dart',
+'../samples/total/server/GetSpreadsheet.dart',
+'../samples/total/server/SYLKProducer.dart',
+'../samples/total/server/TotalRunner.dart',
+'../samples/total/server/TotalServer.dart',
+'../samples/ui_lib/base/AnimationScheduler.dart',
+'../samples/ui_lib/base/Device.dart',
+'../samples/ui_lib/base/DomWrapper.dart',
+'../samples/ui_lib/base/Env.dart',
+'../samples/ui_lib/base/Size.dart',
+'../samples/ui_lib/base/base.dart',
+'../samples/ui_lib/layout/GridLayout.dart',
+'../samples/ui_lib/layout/GridLayoutParams.dart',
+'../samples/ui_lib/layout/GridLayoutParser.dart',
+'../samples/ui_lib/layout/GridTracks.dart',
+'../samples/ui_lib/layout/SizingFunctions.dart',
+'../samples/ui_lib/layout/ViewLayout.dart',
+'../samples/ui_lib/layout/layout.dart',
+'../samples/ui_lib/observable/ChangeEvent.dart',
+'../samples/ui_lib/observable/EventBatch.dart',
+'../samples/ui_lib/observable/observable.dart',
+'../samples/ui_lib/touch/BezierPhysics.dart',
+'../samples/ui_lib/touch/EventUtil.dart',
+'../samples/ui_lib/touch/FxUtil.dart',
+'../samples/ui_lib/touch/Geometry.dart',
+'../samples/ui_lib/touch/InfiniteScroller.dart',
+'../samples/ui_lib/touch/Math.dart',
+'../samples/ui_lib/touch/ScrollWatcher.dart',
+'../samples/ui_lib/touch/Scrollbar.dart',
+'../samples/ui_lib/touch/TimeUtil.dart',
+'../samples/ui_lib/touch/TouchHandler.dart',
+'../samples/ui_lib/touch/touch.dart',
+'../samples/ui_lib/util/CollectionUtils.dart',
+'../samples/ui_lib/util/DateUtils.dart',
+'../samples/ui_lib/util/StringUtils.dart',
+'../samples/ui_lib/util/Uri.dart',
+'../samples/ui_lib/util/utilslib.dart',
+'../samples/ui_lib/view/CompositeView.dart',
+'../samples/ui_lib/view/ConveyorView.dart',
+'../samples/ui_lib/view/MeasureText.dart',
+'../samples/ui_lib/view/PagedViews.dart',
+'../samples/ui_lib/view/SliderMenu.dart',
+'../samples/ui_lib/view/view.dart',
+'./analyze.dart',
+'./analyze_frame.dart',
+'./block_scope.dart',
+'./code_writer.dart',
+'./delta_blue.dart',
+'./evaluator.dart',
+'./file_system.dart',
+'./file_system_dom.dart',
+'./file_system_memory.dart',
+'./file_system_node.dart',
+'./file_system_vm.dart',
+'./fisk.dart',
+'./foo.dart',
+'./frog.dart',
+'./frog_options.dart',
+'./frog_scanner_bench.dart',
+'./frogc.dart',
+'./js_evaluator_node.dart',
+'./lang.dart',
+'./leg/compile_time_constants.dart',
+'./leg/compiler.dart',
+'./leg/diagnostic_listener.dart',
+'./leg/elements/elements.dart',
+'./leg/emitter.dart',
+'./leg/frog_leg.dart',
+'./leg/io/io.dart',
+'./leg/leg.dart',
+'./leg/lib/core.dart',
+'./leg/namer.dart',
+'./leg/resolver.dart',
+'./leg/scanner/array_based_scanner.dart',
+'./leg/scanner/byte_array_scanner.dart',
+'./leg/scanner/byte_strings.dart',
+'./leg/scanner/class_element_parser.dart',
+'./leg/scanner/file_with_non_ascii.dart',
+'./leg/scanner/listener.dart',
+'./leg/scanner/motile.dart',
+'./leg/scanner/motile_d8.dart',
+'./leg/scanner/motile_node.dart',
+'./leg/scanner/node_scanner_bench.dart',
+'./leg/scanner/parser.dart',
+'./leg/scanner/parser_bench.dart',
+'./leg/scanner/parser_task.dart',
+'./leg/scanner/partial_parser.dart',
+'./leg/scanner/scanner.dart',
+'./leg/scanner/scanner_bench.dart',
+'./leg/scanner/scanner_implementation.dart',
+'./leg/scanner/scanner_task.dart',
+'./leg/scanner/scannerlib.dart',
+'./leg/scanner/source_list.dart',
+'./leg/scanner/string_scanner.dart',
+'./leg/scanner/token.dart',
+'./leg/scanner/vm_scanner_bench.dart',
+'./leg/script.dart',
+'./leg/ssa/bailout.dart',
+'./leg/ssa/builder.dart',
+'./leg/ssa/closure.dart',
+'./leg/ssa/codegen_helpers.dart',
+'./leg/ssa/nodes.dart',
+'./leg/ssa/optimize.dart',
+'./leg/ssa/phi_eliminator.dart',
+'./leg/ssa/ssa.dart',
+'./leg/ssa/tracer.dart',
+'./leg/ssa/types.dart',
+'./leg/ssa/validate.dart',
+'./leg/ssa/value_set.dart',
+'./leg/string_validator.dart',
+'./leg/tools/mini_parser.dart',
+'./leg/tree/nodes.dart',
+'./leg/tree/tree.dart',
+'./leg/tree/unparser.dart',
+'./leg/tree/visitors.dart',
+'./leg/tree_validator.dart',
+'./leg/typechecker.dart',
+'./leg/universe.dart',
+'./leg/util/characters.dart',
+'./leg/util/link.dart',
+'./leg/util/link_implementation.dart',
+'./leg/util/util.dart',
+'./leg/util/util_implementation.dart',
+'./leg/warnings.dart',
+'./lib/arrays.dart',
+'./lib/collections.dart',
+'./lib/num.dart',
+'./lib/string_buffer.dart',
+'./member_set.dart',
+'./method_data.dart',
+'./minfrog.dart',
+'./minfrogc.dart',
+'./reader.dart',
+'./samples/ifrog.dart',
+'./server/frog_server.dart',
+'./server/toss.dart',
+'./source.dart',
+'./token.dart',
+'./tokenizer.dart',
+'./tokenizer.g.dart',
+'./tree.dart',
+'./tree.g.dart',
+'./utils.dart'];
diff --git a/lib/compiler/implementation/scanner/string_scanner.dart b/lib/compiler/implementation/scanner/string_scanner.dart
new file mode 100644
index 0000000..2f295ab
--- /dev/null
+++ b/lib/compiler/implementation/scanner/string_scanner.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * Scanner that reads from a String and creates tokens that points to
+ * substrings.
+ */
+class StringScanner extends ArrayBasedScanner<SourceString> {
+  final String string;
+
+  StringScanner(String this.string) : super();
+
+  int nextByte() => charAt(++byteOffset);
+
+  int peek() => charAt(byteOffset + 1);
+
+  int charAt(index)
+      => (string.length > index) ? string.charCodeAt(index) : $EOF;
+
+  SourceString asciiString(int start, int offset) {
+    return new SubstringWrapper(string, start, byteOffset + offset);
+  }
+
+  SourceString utf8String(int start, int offset) {
+    return new SubstringWrapper(string, start, byteOffset + offset + 1);
+  }
+
+  void appendByteStringToken(PrecedenceInfo info, SourceString value) {
+    // assert(kind != $a || keywords.get(value) == null);
+    tail.next = new StringToken.fromSource(info, value, tokenStart);
+    tail = tail.next;
+  }
+}
+
+class SubstringWrapper implements SourceString {
+  final String internalString;
+  final int begin;
+  final int end;
+
+  const SubstringWrapper(String this.internalString,
+                         int this.begin, int this.end);
+
+  int hashCode() => slowToString().hashCode();
+
+  bool operator ==(other) {
+    return other is SourceString && slowToString() == other.slowToString();
+  }
+
+  void printOn(StringBuffer sb) {
+    sb.add(internalString.substring(begin, end));
+  }
+
+  String slowToString() => internalString.substring(begin, end);
+
+  String toString() => "SubstringWrapper(${slowToString()})";
+
+  String get stringValue() => null;
+
+  Iterator<int> iterator() =>
+      new StringCodeIterator.substring(internalString, begin, end);
+
+  SourceString copyWithoutQuotes(int initial, int terminal) {
+    assert(0 <= initial);
+    assert(0 <= terminal);
+    assert(initial + terminal <= internalString.length);
+    return new SubstringWrapper(internalString,
+                                begin + initial, end - terminal);
+  }
+
+  bool isEmpty() => begin == end;
+
+  bool isPrivate() => !isEmpty() && internalString.charCodeAt(begin) === $_;
+}
diff --git a/lib/compiler/implementation/scanner/token.dart b/lib/compiler/implementation/scanner/token.dart
new file mode 100644
index 0000000..215ec18
--- /dev/null
+++ b/lib/compiler/implementation/scanner/token.dart
@@ -0,0 +1,439 @@
+// Copyright (c) 2011, 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.
+
+final int EOF_TOKEN = 0;
+
+final int KEYWORD_TOKEN = $k;
+final int IDENTIFIER_TOKEN = $a;
+final int DOUBLE_TOKEN = $d;
+final int INT_TOKEN = $i;
+final int HEXADECIMAL_TOKEN = $x;
+final int STRING_TOKEN = $SQ;
+
+final int AMPERSAND_TOKEN = $AMPERSAND;
+final int BACKPING_TOKEN = $BACKPING;
+final int BACKSLASH_TOKEN = $BACKSLASH;
+final int BANG_TOKEN = $BANG;
+final int BAR_TOKEN = $BAR;
+final int COLON_TOKEN = $COLON;
+final int COMMA_TOKEN = $COMMA;
+final int EQ_TOKEN = $EQ;
+final int GT_TOKEN = $GT;
+final int HASH_TOKEN = $HASH;
+final int OPEN_CURLY_BRACKET_TOKEN = $OPEN_CURLY_BRACKET;
+final int OPEN_SQUARE_BRACKET_TOKEN = $OPEN_SQUARE_BRACKET;
+final int OPEN_PAREN_TOKEN = $OPEN_PAREN;
+final int LT_TOKEN = $LT;
+final int MINUS_TOKEN = $MINUS;
+final int PERIOD_TOKEN = $PERIOD;
+final int PLUS_TOKEN = $PLUS;
+final int QUESTION_TOKEN = $QUESTION;
+final int CLOSE_CURLY_BRACKET_TOKEN = $CLOSE_CURLY_BRACKET;
+final int CLOSE_SQUARE_BRACKET_TOKEN = $CLOSE_SQUARE_BRACKET;
+final int CLOSE_PAREN_TOKEN = $CLOSE_PAREN;
+final int SEMICOLON_TOKEN = $SEMICOLON;
+final int SLASH_TOKEN = $SLASH;
+final int TILDE_TOKEN = $TILDE;
+final int STAR_TOKEN = $STAR;
+final int PERCENT_TOKEN = $PERCENT;
+final int CARET_TOKEN = $CARET;
+
+final int STRING_INTERPOLATION_TOKEN = 128;
+final int LT_EQ_TOKEN = STRING_INTERPOLATION_TOKEN + 1;
+final int FUNCTION_TOKEN = LT_EQ_TOKEN + 1;
+final int SLASH_EQ_TOKEN = FUNCTION_TOKEN + 1;
+final int PERIOD_PERIOD_PERIOD_TOKEN = SLASH_EQ_TOKEN + 1;
+final int PERIOD_PERIOD_TOKEN = PERIOD_PERIOD_PERIOD_TOKEN + 1;
+final int EQ_EQ_EQ_TOKEN = PERIOD_PERIOD_TOKEN + 1;
+final int EQ_EQ_TOKEN = EQ_EQ_EQ_TOKEN + 1;
+final int LT_LT_EQ_TOKEN = EQ_EQ_TOKEN + 1;
+final int LT_LT_TOKEN = LT_LT_EQ_TOKEN + 1;
+final int GT_EQ_TOKEN = LT_LT_TOKEN + 1;
+final int GT_GT_EQ_TOKEN = GT_EQ_TOKEN + 1;
+final int GT_GT_GT_EQ_TOKEN = GT_GT_EQ_TOKEN + 1;
+final int INDEX_EQ_TOKEN = GT_GT_GT_EQ_TOKEN + 1;
+final int INDEX_TOKEN = INDEX_EQ_TOKEN + 1;
+final int BANG_EQ_EQ_TOKEN = INDEX_TOKEN + 1;
+final int BANG_EQ_TOKEN = BANG_EQ_EQ_TOKEN + 1;
+final int AMPERSAND_AMPERSAND_TOKEN = BANG_EQ_TOKEN + 1;
+final int AMPERSAND_EQ_TOKEN = AMPERSAND_AMPERSAND_TOKEN + 1;
+final int BAR_BAR_TOKEN = AMPERSAND_EQ_TOKEN + 1;
+final int BAR_EQ_TOKEN = BAR_BAR_TOKEN + 1;
+final int STAR_EQ_TOKEN = BAR_EQ_TOKEN + 1;
+final int PLUS_PLUS_TOKEN = STAR_EQ_TOKEN + 1;
+final int PLUS_EQ_TOKEN = PLUS_PLUS_TOKEN + 1;
+final int MINUS_MINUS_TOKEN = PLUS_EQ_TOKEN + 1;
+final int MINUS_EQ_TOKEN = MINUS_MINUS_TOKEN + 1;
+final int TILDE_SLASH_EQ_TOKEN = MINUS_EQ_TOKEN + 1;
+final int TILDE_SLASH_TOKEN = TILDE_SLASH_EQ_TOKEN + 1;
+final int PERCENT_EQ_TOKEN = TILDE_SLASH_TOKEN + 1;
+final int GT_GT_TOKEN = PERCENT_EQ_TOKEN + 1;
+final int CARET_EQ_TOKEN = GT_GT_TOKEN + 1;
+final int IS_TOKEN = CARET_EQ_TOKEN + 1;
+
+// TODO(ahe): Get rid of this.
+final int UNKNOWN_TOKEN = 1024;
+
+/**
+ * A token that doubles as a linked list.
+ */
+class Token {
+  final PrecedenceInfo info;
+  final int charOffset;
+  Token next;
+
+  Token(PrecedenceInfo this.info, int this.charOffset);
+
+  get value() => info.value;
+  String get stringValue() => info.value.stringValue;
+  int get kind() => info.kind;
+  int get precedence() => info.precedence;
+
+  String toString() => info.value.toString();
+
+  String slowToString() => toString();
+}
+
+/**
+ * A keyword token.
+ */
+class KeywordToken extends Token {
+  final Keyword value;
+  String get stringValue() => value.syntax;
+
+  KeywordToken(Keyword value, int charOffset)
+    : this.value = value, super(value.info, charOffset);
+
+  String toString() => value.syntax;
+}
+
+/**
+ * A String-valued token.
+ */
+class StringToken extends Token {
+  final SourceString value;
+  String get stringValue() => value.stringValue;
+
+  StringToken(PrecedenceInfo info, String value, int charOffset)
+    : this.fromSource(info, new SourceString(value), charOffset);
+
+  StringToken.fromSource(PrecedenceInfo info, this.value, int charOffset)
+    : super(info, charOffset);
+
+  String toString() => "StringToken(${value.slowToString()})";
+
+  String slowToString() => value.slowToString();
+}
+
+interface SourceString extends Hashable, Iterable<int> default StringWrapper {
+  const SourceString(String string);
+
+  void printOn(StringBuffer sb);
+
+  /** Gives a [SourceString] that is not including the [initial] first and
+   * [terminal] last characters. This is only intended to be used to remove
+   * quotes from string literals (including an initial '@' for raw strings).
+   */
+  SourceString copyWithoutQuotes(int initial, int terminal);
+
+  String get stringValue();
+
+  String slowToString();
+
+  bool isEmpty();
+
+  bool isPrivate();
+}
+
+class StringWrapper implements SourceString {
+  final String stringValue;
+
+  const StringWrapper(String this.stringValue);
+
+  int hashCode() => stringValue.hashCode();
+
+  bool operator ==(other) {
+    return other is SourceString && toString() == other.slowToString();
+  }
+
+  Iterator<int> iterator() => new StringCodeIterator(stringValue);
+
+  void printOn(StringBuffer sb) {
+    sb.add(stringValue);
+  }
+
+  String toString() => stringValue;
+
+  String slowToString() => stringValue;
+
+  SourceString copyWithoutQuotes(int initial, int terminal) {
+    assert(0 <= initial);
+    assert(0 <= terminal);
+    assert(initial + terminal <= stringValue.length);
+    return new StringWrapper(
+        stringValue.substring(initial, stringValue.length - terminal));
+  }
+
+  bool isEmpty() => stringValue.isEmpty();
+
+  bool isPrivate() => !isEmpty() && stringValue.charCodeAt(0) === $_;
+}
+
+class StringCodeIterator implements Iterator<int> {
+  final String string;
+  int index;
+  final int end;
+
+  StringCodeIterator(String string) :
+    this.string = string, index = 0, end = string.length;
+
+  StringCodeIterator.substring(this.string, this.index, this.end) {
+    assert(0 <= index);
+    assert(index <= end);
+    assert(end <= string.length);
+  }
+
+  bool hasNext() => index < end;
+  int next() => string.charCodeAt(index++);
+}
+
+class BeginGroupToken extends StringToken {
+  Token endGroup;
+  BeginGroupToken(PrecedenceInfo info, String value, int charOffset)
+    : super(info, value, charOffset);
+}
+
+bool isUserDefinableOperator(String value) {
+  return
+    (value === '==') ||
+    (value === '~') ||
+    (value === 'negate') ||
+    (value === '[]') ||
+    (value === '[]=') ||
+    (value === '*') ||
+    (value === '/') ||
+    (value === '%') ||
+    (value === '~/') ||
+    (value === '+') ||
+    (value === '-') ||
+    (value === '<<') ||
+    (value === '>>>') ||
+    (value === '>>') ||
+    (value === '>=') ||
+    (value === '>') ||
+    (value === '<=') ||
+    (value === '<') ||
+    (value === '&') ||
+    (value === '^') ||
+    (value === '|');
+}
+
+class PrecedenceInfo {
+  final SourceString value;
+  final int precedence;
+  final int kind;
+
+  const PrecedenceInfo(this.value, this.precedence, this.kind);
+
+  toString() => 'PrecedenceInfo($value, $precedence, $kind)';
+}
+
+// TODO(ahe): The following are not tokens in Dart.
+final PrecedenceInfo BACKPING_INFO =
+  const PrecedenceInfo(const SourceString('`'), 0, BACKPING_TOKEN);
+final PrecedenceInfo BACKSLASH_INFO =
+  const PrecedenceInfo(const SourceString('\\'), 0, BACKSLASH_TOKEN);
+final PrecedenceInfo PERIOD_PERIOD_PERIOD_INFO =
+  const PrecedenceInfo(const SourceString('...'), 0,
+                       PERIOD_PERIOD_PERIOD_TOKEN);
+
+// TODO(ahe): This might become a token.
+final PrecedenceInfo PERIOD_PERIOD_INFO =
+  const PrecedenceInfo(const SourceString('..'), 0, PERIOD_PERIOD_TOKEN);
+
+final PrecedenceInfo BANG_INFO =
+  const PrecedenceInfo(const SourceString('!'), 0, BANG_TOKEN);
+final PrecedenceInfo COLON_INFO =
+  const PrecedenceInfo(const SourceString(':'), 0, COLON_TOKEN);
+final PrecedenceInfo INDEX_INFO =
+  const PrecedenceInfo(const SourceString('[]'), 0, INDEX_TOKEN);
+final PrecedenceInfo MINUS_MINUS_INFO =
+  const PrecedenceInfo(const SourceString('--'), POSTFIX_PRECEDENCE,
+                       MINUS_MINUS_TOKEN);
+final PrecedenceInfo PLUS_PLUS_INFO =
+  const PrecedenceInfo(const SourceString('++'), POSTFIX_PRECEDENCE,
+                       PLUS_PLUS_TOKEN);
+final PrecedenceInfo TILDE_INFO =
+  const PrecedenceInfo(const SourceString('~'), 0, TILDE_TOKEN);
+
+final PrecedenceInfo FUNCTION_INFO =
+  const PrecedenceInfo(const SourceString('=>'), 0, FUNCTION_TOKEN);
+final PrecedenceInfo HASH_INFO =
+  const PrecedenceInfo(const SourceString('#'), 0, HASH_TOKEN);
+final PrecedenceInfo INDEX_EQ_INFO =
+  const PrecedenceInfo(const SourceString('[]='), 0, INDEX_EQ_TOKEN);
+final PrecedenceInfo SEMICOLON_INFO =
+  const PrecedenceInfo(const SourceString(';'), 0, SEMICOLON_TOKEN);
+final PrecedenceInfo COMMA_INFO =
+  const PrecedenceInfo(const SourceString(','), 0, COMMA_TOKEN);
+
+// Assignment operators.
+final int ASSIGNMENT_PRECEDENCE = 2;
+final PrecedenceInfo AMPERSAND_EQ_INFO =
+  const PrecedenceInfo(const SourceString('&='),
+                       ASSIGNMENT_PRECEDENCE, AMPERSAND_EQ_TOKEN);
+final PrecedenceInfo BAR_EQ_INFO =
+  const PrecedenceInfo(const SourceString('|='),
+                       ASSIGNMENT_PRECEDENCE, BAR_EQ_TOKEN);
+final PrecedenceInfo CARET_EQ_INFO =
+  const PrecedenceInfo(const SourceString('^='),
+                       ASSIGNMENT_PRECEDENCE, CARET_EQ_TOKEN);
+final PrecedenceInfo EQ_INFO =
+  const PrecedenceInfo(const SourceString('='),
+                       ASSIGNMENT_PRECEDENCE, EQ_TOKEN);
+final PrecedenceInfo GT_GT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('>>='),
+                       ASSIGNMENT_PRECEDENCE, GT_GT_EQ_TOKEN);
+final PrecedenceInfo GT_GT_GT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('>>>='),
+                       ASSIGNMENT_PRECEDENCE, GT_GT_GT_EQ_TOKEN);
+final PrecedenceInfo LT_LT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('<<='),
+                       ASSIGNMENT_PRECEDENCE, LT_LT_EQ_TOKEN);
+final PrecedenceInfo MINUS_EQ_INFO =
+  const PrecedenceInfo(const SourceString('-='),
+                       ASSIGNMENT_PRECEDENCE, MINUS_EQ_TOKEN);
+final PrecedenceInfo PERCENT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('%='),
+                       ASSIGNMENT_PRECEDENCE, PERCENT_EQ_TOKEN);
+final PrecedenceInfo PLUS_EQ_INFO =
+  const PrecedenceInfo(const SourceString('+='),
+                       ASSIGNMENT_PRECEDENCE, PLUS_EQ_TOKEN);
+final PrecedenceInfo SLASH_EQ_INFO =
+  const PrecedenceInfo(const SourceString('/='),
+                       ASSIGNMENT_PRECEDENCE, SLASH_EQ_TOKEN);
+final PrecedenceInfo STAR_EQ_INFO =
+  const PrecedenceInfo(const SourceString('*='),
+                       ASSIGNMENT_PRECEDENCE, STAR_EQ_TOKEN);
+final PrecedenceInfo TILDE_SLASH_EQ_INFO =
+  const PrecedenceInfo(const SourceString('~/='),
+                       ASSIGNMENT_PRECEDENCE, TILDE_SLASH_EQ_TOKEN);
+
+final PrecedenceInfo QUESTION_INFO =
+  const PrecedenceInfo(const SourceString('?'), 3, QUESTION_TOKEN);
+
+final PrecedenceInfo BAR_BAR_INFO =
+  const PrecedenceInfo(const SourceString('||'), 4, BAR_BAR_TOKEN);
+
+final PrecedenceInfo AMPERSAND_AMPERSAND_INFO =
+  const PrecedenceInfo(const SourceString('&&'), 5, AMPERSAND_AMPERSAND_TOKEN);
+
+final PrecedenceInfo BAR_INFO =
+  const PrecedenceInfo(const SourceString('|'), 6, BAR_TOKEN);
+
+final PrecedenceInfo CARET_INFO =
+  const PrecedenceInfo(const SourceString('^'), 7, CARET_TOKEN);
+
+final PrecedenceInfo AMPERSAND_INFO =
+  const PrecedenceInfo(const SourceString('&'), 8, AMPERSAND_TOKEN);
+
+// Equality operators.
+final PrecedenceInfo BANG_EQ_EQ_INFO =
+  const PrecedenceInfo(const SourceString('!=='), 9, BANG_EQ_EQ_TOKEN);
+final PrecedenceInfo BANG_EQ_INFO =
+  const PrecedenceInfo(const SourceString('!='), 9, BANG_EQ_TOKEN);
+final PrecedenceInfo EQ_EQ_EQ_INFO =
+  const PrecedenceInfo(const SourceString('==='), 9, EQ_EQ_EQ_TOKEN);
+final PrecedenceInfo EQ_EQ_INFO =
+  const PrecedenceInfo(const SourceString('=='), 9, EQ_EQ_TOKEN);
+
+// Relational operators.
+final PrecedenceInfo GT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('>='), 10, GT_EQ_TOKEN);
+final PrecedenceInfo GT_INFO =
+  const PrecedenceInfo(const SourceString('>'), 10, GT_TOKEN);
+final PrecedenceInfo IS_INFO =
+  const PrecedenceInfo(const SourceString('is'), 10, IS_TOKEN);
+final PrecedenceInfo LT_EQ_INFO =
+  const PrecedenceInfo(const SourceString('<='), 10, LT_EQ_TOKEN);
+final PrecedenceInfo LT_INFO =
+  const PrecedenceInfo(const SourceString('<'), 10, LT_TOKEN);
+
+// Shift operators.
+final PrecedenceInfo GT_GT_GT_INFO =
+  const PrecedenceInfo(const SourceString('>>>'), 11, GT_GT_TOKEN);
+final PrecedenceInfo GT_GT_INFO =
+  const PrecedenceInfo(const SourceString('>>'), 11, GT_GT_TOKEN);
+final PrecedenceInfo LT_LT_INFO =
+  const PrecedenceInfo(const SourceString('<<'), 11, LT_LT_TOKEN);
+
+// Additive operators.
+final PrecedenceInfo MINUS_INFO =
+  const PrecedenceInfo(const SourceString('-'), 12, MINUS_TOKEN);
+final PrecedenceInfo PLUS_INFO =
+  const PrecedenceInfo(const SourceString('+'), 12, PLUS_TOKEN);
+
+// Multiplicative operators.
+final PrecedenceInfo PERCENT_INFO =
+  const PrecedenceInfo(const SourceString('%'), 13, PERCENT_TOKEN);
+final PrecedenceInfo SLASH_INFO =
+  const PrecedenceInfo(const SourceString('/'), 13, SLASH_TOKEN);
+final PrecedenceInfo STAR_INFO =
+  const PrecedenceInfo(const SourceString('*'), 13, STAR_TOKEN);
+final PrecedenceInfo TILDE_SLASH_INFO =
+  const PrecedenceInfo(const SourceString('~/'), 13, TILDE_SLASH_TOKEN);
+
+final int POSTFIX_PRECEDENCE = 14;
+final PrecedenceInfo PERIOD_INFO =
+  const PrecedenceInfo(const SourceString('.'), POSTFIX_PRECEDENCE,
+                       PERIOD_TOKEN);
+
+final PrecedenceInfo KEYWORD_INFO =
+  const PrecedenceInfo(const SourceString('keyword'), 0, KEYWORD_TOKEN);
+
+final PrecedenceInfo EOF_INFO =
+  const PrecedenceInfo(const SourceString('EOF'), 0, EOF_TOKEN);
+
+final PrecedenceInfo IDENTIFIER_INFO =
+  const PrecedenceInfo(const SourceString('identifier'), 0, IDENTIFIER_TOKEN);
+
+final PrecedenceInfo OPEN_PAREN_INFO =
+  const PrecedenceInfo(const SourceString('('), POSTFIX_PRECEDENCE,
+                       OPEN_PAREN_TOKEN);
+
+final PrecedenceInfo CLOSE_PAREN_INFO =
+  const PrecedenceInfo(const SourceString(')'), 0, CLOSE_PAREN_TOKEN);
+
+final PrecedenceInfo OPEN_CURLY_BRACKET_INFO =
+  const PrecedenceInfo(const SourceString('{'), 0, OPEN_CURLY_BRACKET_TOKEN);
+
+final PrecedenceInfo CLOSE_CURLY_BRACKET_INFO =
+  const PrecedenceInfo(const SourceString('}'), 0, CLOSE_CURLY_BRACKET_TOKEN);
+
+final PrecedenceInfo INT_INFO =
+  const PrecedenceInfo(const SourceString('int'), 0, INT_TOKEN);
+
+final PrecedenceInfo STRING_INFO =
+  const PrecedenceInfo(const SourceString('string'), 0, STRING_TOKEN);
+
+final PrecedenceInfo OPEN_SQUARE_BRACKET_INFO =
+  const PrecedenceInfo(const SourceString('['), POSTFIX_PRECEDENCE,
+                       OPEN_SQUARE_BRACKET_TOKEN);
+
+final PrecedenceInfo CLOSE_SQUARE_BRACKET_INFO =
+  const PrecedenceInfo(const SourceString(']'), 0, CLOSE_SQUARE_BRACKET_TOKEN);
+
+final PrecedenceInfo DOUBLE_INFO =
+  const PrecedenceInfo(const SourceString('double'), 0, DOUBLE_TOKEN);
+
+final PrecedenceInfo STRING_INTERPOLATION_INFO =
+  const PrecedenceInfo(const SourceString('\${'), 0,
+                       STRING_INTERPOLATION_TOKEN);
+
+final PrecedenceInfo HEXADECIMAL_INFO =
+  const PrecedenceInfo(const SourceString('hexadecimal'), 0, HEXADECIMAL_TOKEN);
+
+// For reporting lexical errors.
+final PrecedenceInfo ERROR_INFO =
+  const PrecedenceInfo(const SourceString('?'), 0, UNKNOWN_TOKEN);
\ No newline at end of file
diff --git a/lib/compiler/implementation/scanner/vm_scanner_bench.dart b/lib/compiler/implementation/scanner/vm_scanner_bench.dart
new file mode 100644
index 0000000..e6c776b
--- /dev/null
+++ b/lib/compiler/implementation/scanner/vm_scanner_bench.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2012, 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('vm_scanner_bench');
+#import('dart:io');
+#import('scannerlib.dart');
+#import('scanner_implementation.dart');
+#import('scanner_bench.dart');
+#import('../../../utf/utf.dart');
+#import('../util/characters.dart');
+#source('byte_strings.dart');
+#source('byte_array_scanner.dart');
+
+class VmScannerBench extends ScannerBench {
+  int getBytes(String filename, void callback(List<int> bytes)) {
+    var file = (new File(filename)).openSync();
+    int size = file.lengthSync();
+    List<int> bytes = new ByteArray(size + 1);
+    file.readListSync(bytes, 0, size);
+    bytes[size] = $EOF;
+    file.closeSync();
+    callback(bytes);
+    return bytes.length - 1;
+  }
+
+  void checkExistence(String filename) {
+    File file = new File(filename);
+    if (!file.existsSync()) {
+      print("no such file: ${filename}");
+    }
+  }
+
+  Scanner makeScanner(bytes) => new ByteArrayScanner(bytes);
+}
+
+main() {
+  new VmScannerBench().main(argv);
+}
diff --git a/lib/compiler/implementation/script.dart b/lib/compiler/implementation/script.dart
new file mode 100644
index 0000000..cd88619
--- /dev/null
+++ b/lib/compiler/implementation/script.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2011, 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.
+
+class Script {
+  final /* frog.SourceFile */ file;
+  final Uri uri;
+  Script(this.uri, this.file);
+
+  String get text() => (file === null) ? null : file.text;
+  String get name() => (file === null) ? null : file.filename;
+}
diff --git a/lib/compiler/implementation/source_file.dart b/lib/compiler/implementation/source_file.dart
new file mode 100644
index 0000000..234500d
--- /dev/null
+++ b/lib/compiler/implementation/source_file.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2012, 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('source_file');
+
+#import('colors.dart');
+
+/**
+ * Represents a file of source code.
+ */
+class SourceFile {
+
+  /** The name of the file. */
+  final String filename;
+
+  /** The text content of the file. */
+  final String text;
+
+  List<int> _lineStarts;
+
+  SourceFile(this.filename, this.text);
+
+  List<int> get lineStarts() {
+    if (_lineStarts == null) {
+      var starts = [0];
+      var index = 0;
+      while (index < text.length) {
+        index = text.indexOf('\n', index) + 1;
+        if (index <= 0) break;
+        starts.add(index);
+      }
+      starts.add(text.length + 1);
+      _lineStarts = starts;
+    }
+    return _lineStarts;
+  }
+
+  int getLine(int position) {
+    // TODO(jimhug): Implement as binary search.
+    var starts = lineStarts;
+    for (int i=0; i < starts.length; i++) {
+      if (starts[i] > position) return i-1;
+    }
+    throw 'bad position';
+  }
+
+  int getColumn(int line, int position) {
+    return position - lineStarts[line];
+  }
+
+  /**
+   * Create a pretty string representation from a character position
+   * in the file.
+   */
+  String getLocationMessage(String message, int start,
+      [int end, bool includeText=false, bool useColors = true]) {
+    var line = getLine(start);
+    var column = getColumn(line, start);
+
+    var buf = new StringBuffer(
+        '${filename}:${line + 1}:${column + 1}: $message');
+    if (includeText) {
+      buf.add('\n');
+      var textLine;
+      // +1 for 0-indexing, +1 again to avoid the last line of the file
+      if ((line + 2) < _lineStarts.length) {
+        textLine = text.substring(_lineStarts[line], _lineStarts[line+1]);
+      } else {
+        textLine = text.substring(_lineStarts[line]) + '\n';
+      }
+
+      int toColumn = Math.min(column + (end-start), textLine.length);
+      if (useColors) {
+        buf.add(textLine.substring(0, column));
+        buf.add(RED_COLOR);
+        buf.add(textLine.substring(column, toColumn));
+        buf.add(NO_COLOR);
+        buf.add(textLine.substring(toColumn));
+      } else {
+        buf.add(textLine);
+      }
+
+      int i = 0;
+      for (; i < column; i++) {
+        buf.add(' ');
+      }
+
+      if (useColors) buf.add(RED_COLOR);
+      for (; i < toColumn; i++) {
+        buf.add('^');
+      }
+      if (useColors) buf.add(NO_COLOR);
+    }
+
+    return buf.toString();
+  }
+}
diff --git a/lib/compiler/implementation/ssa/bailout.dart b/lib/compiler/implementation/ssa/bailout.dart
new file mode 100644
index 0000000..85bb752
--- /dev/null
+++ b/lib/compiler/implementation/ssa/bailout.dart
@@ -0,0 +1,366 @@
+// Copyright (c) 2012, 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.
+
+class BailoutInfo {
+  int instructionId;
+  int bailoutId;
+  BailoutInfo(this.instructionId, this.bailoutId);
+}
+
+/**
+ * Keeps track of the execution environment for instructions. An
+ * execution environment contains the SSA instructions that are live.
+ */
+class Environment {
+  final Set<HInstruction> lives;
+  final Set<HBasicBlock> loopMarkers;
+  Environment() : lives = new Set<HInstruction>(),
+                  loopMarkers = new Set<HBasicBlock>();
+  Environment.from(Environment other)
+    : lives = new Set<HInstruction>.from(other.lives),
+      loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers);
+
+  Environment.forLoopBody(Environment other)
+    : lives = new Set<HInstruction>(),
+      loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers);
+
+  void remove(HInstruction instruction) {
+    lives.remove(instruction);
+  }
+
+  void add(HInstruction instruction) {
+    if (!instruction.isCodeMotionInvariant()) {
+      lives.add(instruction);
+    } else {
+      for (int i = 0, len = instruction.inputs.length; i < len; i++) {
+        add(instruction.inputs[i]);
+      }
+    }
+  }
+
+  void addLoopMarker(HBasicBlock block) {
+    loopMarkers.add(block);
+  }
+
+  void removeLoopMarker(HBasicBlock block) {
+    loopMarkers.remove(block);
+  }
+
+  void addAll(Environment other) {
+    lives.addAll(other.lives);
+    loopMarkers.addAll(other.loopMarkers);
+  }
+
+  List<HInstruction> buildAndSetLast(HInstruction instruction) {
+    remove(instruction);
+    List<HInstruction> result = new List<HInstruction>.from(lives);
+    result.addLast(instruction);
+    add(instruction);
+    return result;
+  }
+
+  bool isEmpty() => lives.isEmpty();
+  bool contains(HInstruction instruction) => lives.contains(instruction);
+  bool containsLoopMarker(HBasicBlock block) => loopMarkers.contains(block);
+  void clear() => lives.clear();
+}
+
+/**
+ * Computes the environment for each SSA instruction: visits the graph
+ * in post-dominator order. Removes an instruction from the environment
+ * and adds its inputs to the environment at the instruction's
+ * definition.
+ *
+ * At the end of the computation, insert type guards in the graph.
+ */
+class SsaTypeGuardBuilder extends HBaseVisitor implements OptimizationPhase {
+  final Compiler compiler;
+  final WorkItem work;
+  final String name = 'SsaTypeGuardBuilder';
+  Environment environment;
+  SubGraph subGraph;
+
+  final Map<HInstruction, Environment> capturedEnvironments;
+
+  SsaTypeGuardBuilder(Compiler this.compiler, WorkItem this.work)
+    : capturedEnvironments = new Map<HInstruction, Environment>();
+
+
+  void visitGraph(HGraph graph) {
+    subGraph = new SubGraph(graph.entry, graph.exit);
+    environment = new Environment();
+    visitBasicBlock(graph.entry);
+    if (!environment.isEmpty()) {
+      compiler.internalError('Bailout environment computation',
+          node: compiler.currentElement.parseNode(compiler));
+    }
+    insertCapturedEnvironments();
+  }
+
+  void maybeCaptureEnvironment(HInstruction instruction) {
+    if (shouldCaptureEnvironment(instruction)) {
+      capturedEnvironments[instruction] = new Environment.from(environment);
+    }
+  }
+
+  void visitSubGraph(SubGraph newSubGraph) {
+    SubGraph oldSubGraph = subGraph;
+    subGraph = newSubGraph;
+    visitBasicBlock(subGraph.start);
+    subGraph = oldSubGraph;
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    if (!subGraph.contains(block)) return;
+    block.last.accept(this);
+
+    HInstruction instruction = block.last.previous;
+    while (instruction != null) {
+      HInstruction previous = instruction.previous;
+      instruction.accept(this);
+      instruction = previous;
+    }
+
+    for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
+      phi.accept(this);
+    }
+
+    if (block.isLoopHeader()) {
+      // If the block is a loop header, we need to change every uses
+      // of its loop marker to the current set of live instructions.
+      // For example with the following loop (read the example in
+      // reverse):
+      //
+      // while (true) { <-- (4) update the marker with the environment
+      //   use(x);      <-- (3) environment = {x}
+      //   bailout;     <-- (2) has the marker when computed
+      // }              <-- (1) create a loop marker
+      //
+      // The bailout instruction first captures the marker, but it
+      // will be replaced by the live environment at the loop entry,
+      // in this case {x}.
+      environment.removeLoopMarker(block);
+      capturedEnvironments.forEach((instruction, env) {
+        if (env.containsLoopMarker(block)) {
+          env.removeLoopMarker(block);
+          env.addAll(environment);
+        }
+      });
+    }
+  }
+
+  void visitPhi(HPhi phi) {
+    maybeCaptureEnvironment(phi);
+    environment.remove(phi);
+    // If the block is a loop header, we insert the incoming values of
+    // the phis, and remove the loop values.
+    // If the block is not a loop header, the phi will be handled by
+    // the control flow instruction.
+    if (phi.block.isLoopHeader()) {
+      environment.add(phi.inputs[0]);
+      for (int i = 1, len = phi.inputs.length; i < len; i++) {
+        environment.remove(phi.inputs[i]);
+      }
+    }
+  }
+
+  void visitInstruction(HInstruction instruction) {
+    maybeCaptureEnvironment(instruction);
+    environment.remove(instruction);
+    for (int i = 0, len = instruction.inputs.length; i < len; i++) {
+      environment.add(instruction.inputs[i]);
+    }
+  }
+
+  void visitIf(HIf instruction) {
+    HIfBlockInformation info = instruction.blockInformation;
+    HBasicBlock joinBlock = info.joinBlock;
+
+    if (joinBlock != null) {
+      visitBasicBlock(joinBlock);
+    }
+    Environment thenEnvironment = new Environment.from(environment);
+    Environment elseEnvironment = environment;
+
+    if (joinBlock != null) {
+      for (HPhi phi = joinBlock.phis.first; phi != null; phi = phi.next) {
+        if (joinBlock.predecessors[0] == instruction.block) {
+          // We're dealing with an 'if' without an else branch.
+          thenEnvironment.add(phi.inputs[1]);
+          elseEnvironment.add(phi.inputs[0]);
+        } else {
+          thenEnvironment.add(phi.inputs[0]);
+          elseEnvironment.add(phi.inputs[1]);
+        }
+      }
+    }
+
+    if (instruction.hasElse) {
+      environment = elseEnvironment;
+      visitSubGraph(info.elseGraph);
+      elseEnvironment = environment;
+    }
+
+    environment = thenEnvironment;
+    visitSubGraph(info.thenGraph);
+    environment.addAll(elseEnvironment);
+  }
+
+  void visitGoto(HGoto goto) {
+    HBasicBlock block = goto.block;
+    if (block.successors[0].dominator != block) return;
+    visitBasicBlock(block.successors[0]);
+  }
+
+  void visitBreak(HBreak breakInstruction) {
+    compiler.unimplemented("SsaEnvironmentBuilder.visitBreak");
+  }
+
+  void visitLoopBranch(HLoopBranch branch) {
+    HBasicBlock block = branch.block;
+
+    // Visit the code after the loop.
+    visitBasicBlock(block.successors[1]);
+
+    Environment joinEnvironment = environment;
+
+    // When visiting the loop body, we don't require the live
+    // instructions after the loop body to be in the environment. They
+    // will be either recomputed in the loop header, or inserted
+    // with the loop marker. We still need to transfer existing loop
+    // markers from the current environment, because they must be live
+    // for this loop body.
+    environment = new Environment.forLoopBody(environment);
+
+    // Put the loop phis in the environment.
+    HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
+    for (HPhi phi = header.phis.first; phi != null; phi = phi.next) {
+      for (int i = 1, len = phi.inputs.length; i < len; i++) {
+        environment.add(phi.inputs[i]);
+      }
+    }
+
+    // Add the loop marker
+    environment.addLoopMarker(header);
+
+    if (!branch.isDoWhile()) {
+      assert(block.successors[0] == block.dominatedBlocks[0]);
+      visitBasicBlock(block.successors[0]);
+    }
+
+    // We merge the environment required by the code after the loop,
+    // and the code inside the loop.
+    environment.addAll(joinEnvironment);
+  }
+
+  // Deal with all kinds of control flow instructions. In case we add
+  // a new one, we will hit an internal error.
+  void visitExit(HExit exit) {}
+
+  void visitReturn(HReturn instruction) {
+    environment.clear();
+    visitInstruction(instruction);
+  }
+
+  void visitThrow(HThrow instruction) {
+    environment.clear();
+    visitInstruction(instruction);
+  }
+
+  void visitControlFlow(HControlFlow instruction) {
+    compiler.internalError('Control flow instructions already dealt with.',
+                           instruction: instruction);
+  }
+
+  bool shouldCaptureEnvironment(HInstruction instruction) {
+    return instruction.type.isKnown() && !instruction.hasExpectedType();
+  }
+
+  void insertCapturedEnvironments() {
+    work.guards = <HTypeGuard>[];
+    int state = 1;
+    capturedEnvironments.forEach((HInstruction instruction, Environment env) {
+      List<HInstruction> inputs = env.buildAndSetLast(instruction);
+      HTypeGuard guard = new HTypeGuard(state++, inputs);
+      work.guards.add(guard);
+      instruction.block.rewrite(instruction, guard);
+      HInstruction insertionPoint = (instruction is HPhi)
+          ? instruction.block.first
+          : instruction.next;
+      insertionPoint.block.addBefore(insertionPoint, guard);
+    });
+  }
+}
+
+/**
+ * Propagates bailout information to blocks that need it. This visitor
+ * is run before codegen, to know which blocks have to deal with
+ * bailouts.
+ */
+class SsaBailoutPropagator extends HBaseVisitor {
+  final Compiler compiler;
+  final List<HBasicBlock> blocks;
+
+  SsaBailoutPropagator(Compiler this.compiler) : blocks = <HBasicBlock>[];
+
+  void visitGraph(HGraph graph) {
+    blocks.addLast(graph.entry);
+    visitBasicBlock(graph.entry);
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    if (block.isLoopHeader()) blocks.addLast(block);
+    HInstruction instruction = block.first;
+    while (instruction != null) {
+      instruction.accept(this);
+      instruction = instruction.next;
+    }
+  }
+
+  void enterBlock(HBasicBlock block) {
+    if (block == null) return;
+    blocks.addLast(block);
+    visitBasicBlock(block);
+    blocks.removeLast();
+  }
+
+  void visitIf(HIf instruction) {
+    enterBlock(instruction.thenBlock);
+    enterBlock(instruction.elseBlock);
+    enterBlock(instruction.joinBlock);
+  }
+
+  void visitGoto(HGoto goto) {
+    HBasicBlock block = goto.block;
+    if (block.successors[0].dominator != block) return;
+    visitBasicBlock(block.successors[0]);
+  }
+
+  void visitLoopBranch(HLoopBranch branch) {
+    HBasicBlock branchBlock = branch.block;
+    if (!branch.isDoWhile()) {
+      // Not a do while loop. We visit the body of the loop.
+      visitBasicBlock(branchBlock.dominatedBlocks[0]);
+    }
+    blocks.removeLast();
+    visitBasicBlock(branchBlock.successors[1]);
+  }
+
+  // Deal with all kinds of control flow instructions. In case we add
+  // a new one, we will hit an internal error.
+  void visitExit(HExit exit) {}
+  void visitReturn(HReturn instruction) {}
+  void visitThrow(HThrow instruction) {}
+
+  void visitControlFlow(HControlFlow instruction) {
+    compiler.internalError('Control flow instructions already dealt with.',
+                           instruction: instruction);
+  }
+
+  visitTypeGuard(HTypeGuard guard) {
+    blocks.forEach((HBasicBlock block) {
+      block.guards.add(guard);
+    });
+  }
+}
diff --git a/lib/compiler/implementation/ssa/builder.dart b/lib/compiler/implementation/ssa/builder.dart
new file mode 100644
index 0000000..92ba9bc
--- /dev/null
+++ b/lib/compiler/implementation/ssa/builder.dart
@@ -0,0 +1,2971 @@
+// Copyright (c) 2012, 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.
+
+class Interceptors {
+  Compiler compiler;
+  Interceptors(Compiler this.compiler);
+
+  SourceString mapOperatorToMethodName(Operator op) {
+    String name = op.source.stringValue;
+    if (name === '+') return const SourceString('add');
+    if (name === '-') return const SourceString('sub');
+    if (name === '*') return const SourceString('mul');
+    if (name === '/') return const SourceString('div');
+    if (name === '~/') return const SourceString('tdiv');
+    if (name === '%') return const SourceString('mod');
+    if (name === '<<') return const SourceString('shl');
+    if (name === '>>') return const SourceString('shr');
+    if (name === '|') return const SourceString('or');
+    if (name === '&') return const SourceString('and');
+    if (name === '^') return const SourceString('xor');
+    if (name === '<') return const SourceString('lt');
+    if (name === '<=') return const SourceString('le');
+    if (name === '>') return const SourceString('gt');
+    if (name === '>=') return const SourceString('ge');
+    if (name === '==') return const SourceString('eq');
+    if (name === '!=') return const SourceString('eq');
+    if (name === '===') return const SourceString('eqq');
+    if (name === '!==') return const SourceString('eqq');
+    if (name === '+=') return const SourceString('add');
+    if (name === '-=') return const SourceString('sub');
+    if (name === '*=') return const SourceString('mul');
+    if (name === '/=') return const SourceString('div');
+    if (name === '~/=') return const SourceString('tdiv');
+    if (name === '%=') return const SourceString('mod');
+    if (name === '<<=') return const SourceString('shl');
+    if (name === '>>=') return const SourceString('shr');
+    if (name === '|=') return const SourceString('or');
+    if (name === '&=') return const SourceString('and');
+    if (name === '^=') return const SourceString('xor');
+    if (name === '++') return const SourceString('add');
+    if (name === '--') return const SourceString('sub');
+    compiler.unimplemented('Unknown operator', node: op);
+  }
+
+  Element getStaticInterceptor(SourceString name, int parameters) {
+    String mangledName = "builtin\$${name.slowToString()}\$${parameters}";
+    Element result = compiler.findHelper(new SourceString(mangledName));
+    return result;
+  }
+
+  Element getStaticGetInterceptor(SourceString name) {
+    String mangledName = "builtin\$get\$${name.slowToString()}";
+    Element result = compiler.findHelper(new SourceString(mangledName));
+    return result;
+  }
+
+  Element getStaticSetInterceptor(SourceString name) {
+    String mangledName = "builtin\$set\$${name.slowToString()}";
+    Element result = compiler.findHelper(new SourceString(mangledName));
+    return result;
+  }
+
+  Element getOperatorInterceptor(Operator op) {
+    SourceString name = mapOperatorToMethodName(op);
+    Element result = compiler.findHelper(name);
+    return result;
+  }
+
+  Element getPrefixOperatorInterceptor(Operator op) {
+    String name = op.source.stringValue;
+    if (name === '~') {
+      return compiler.findHelper(const SourceString('not'));
+    }
+    if (name === '-') {
+      return compiler.findHelper(const SourceString('neg'));
+    }
+    compiler.unimplemented('Unknown operator', node: op);
+  }
+
+  Element getIndexInterceptor() {
+    return compiler.findHelper(const SourceString('index'));
+  }
+
+  Element getIndexAssignmentInterceptor() {
+    return compiler.findHelper(const SourceString('indexSet'));
+  }
+
+  Element getEqualsNullInterceptor() {
+    return compiler.findHelper(const SourceString('eqNull'));
+  }
+
+  Element getExceptionUnwrapper() {
+    return compiler.findHelper(const SourceString('unwrapException'));
+  }
+
+  Element getClosureConverter() {
+    return compiler.findHelper(const SourceString('convertDartClosureToJS'));
+  }
+
+  Element getTraceFromException() {
+    return compiler.findHelper(const SourceString('getTraceFromException'));
+  }
+
+  Element getEqualsInterceptor() {
+    return compiler.findHelper(const SourceString('eq'));
+  }
+
+  Element getMapMaker() {
+    return compiler.findHelper(const SourceString('makeLiteralMap'));
+  }
+}
+
+class SsaBuilderTask extends CompilerTask {
+  final Interceptors interceptors;
+  final Map<Node, ClosureData> closureDataCache;
+
+  String get name() => 'SSA builder';
+
+  SsaBuilderTask(Compiler compiler)
+    : interceptors = new Interceptors(compiler),
+      closureDataCache = new HashMap<Node, ClosureData>(),
+      super(compiler);
+
+  HGraph build(WorkItem work) {
+    return measure(() {
+      FunctionElement element = work.element;
+      HInstruction.idCounter = 0;
+      SsaBuilder builder = new SsaBuilder(compiler, work);
+      HGraph graph;
+      switch (element.kind) {
+        case ElementKind.GENERATIVE_CONSTRUCTOR:
+          graph = compileConstructor(builder, work);
+          break;
+        case ElementKind.GENERATIVE_CONSTRUCTOR_BODY:
+        case ElementKind.FUNCTION:
+        case ElementKind.GETTER:
+        case ElementKind.SETTER:
+          graph = builder.buildMethod(work.element);
+          break;
+      }
+      assert(graph.isValid());
+      if (compiler.tracer.enabled) {
+        String name;
+        if (element.enclosingElement !== null &&
+            element.enclosingElement.kind == ElementKind.CLASS) {
+          String className = element.enclosingElement.name.slowToString();
+          String memberName = element.name.slowToString();
+          name = "$className.$memberName";
+          if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+            name = "$name (body)";
+          }
+        } else {
+          name = "${element.name.slowToString()}";
+        }
+        compiler.tracer.traceCompilation(name);
+        compiler.tracer.traceGraph('builder', graph);
+      }
+      return graph;
+    });
+  }
+
+  HGraph compileConstructor(SsaBuilder builder, WorkItem work) {
+    // The body of the constructor will be generated in a separate function.
+    final ClassElement classElement = work.element.enclosingElement;
+    return builder.buildFactory(classElement, work.element);
+  }
+}
+
+/**
+ * Keeps track of locals (including parameters and phis) when building. The
+ * 'this' reference is treated as parameter and hence handled by this class,
+ * too.
+ */
+class LocalsHandler {
+  /**
+   * The values of locals that can be directly accessed (without redirections
+   * to boxes or closure-fields).
+   */
+  Map<Element, HInstruction> directLocals;
+  Map<Element, Element> redirectionMapping;
+  SsaBuilder builder;
+  ClosureData closureData;
+
+  LocalsHandler(this.builder)
+      : directLocals = new Map<Element, HInstruction>(),
+        redirectionMapping = new Map<Element, Element>();
+
+  /**
+   * Creates a new [LocalsHandler] based on [other]. We only need to
+   * copy the [directLocals], since the other fields can be shared
+   * throughout the AST visit.
+   */
+  LocalsHandler.from(LocalsHandler other)
+      : directLocals = new Map<Element, HInstruction>.from(other.directLocals),
+        redirectionMapping = other.redirectionMapping,
+        builder = other.builder,
+        closureData = other.closureData;
+
+  /**
+   * Redirects accesses from element [from] to element [to]. The [to] element
+   * must be a boxed variable or a variable that is stored in a closure-field.
+   */
+  void redirectElement(Element from, Element to) {
+    assert(redirectionMapping[from] === null);
+    redirectionMapping[from] = to;
+    assert(isStoredInClosureField(from) || isBoxed(from));
+  }
+
+  HInstruction createBox() {
+    // TODO(floitsch): Clean up this hack. Should we create a box-object by
+    // just creating an empty object literal?
+    HInstruction box = new HForeign(const LiteralDartString("{}"),
+                                    const LiteralDartString('Object'),
+                                    <HInstruction>[]);
+    builder.add(box);
+    return box;
+  }
+
+  /**
+   * If the scope (function or loop) [node] has captured variables then this
+   * method creates a box and sets up the redirections.
+   */
+  void enterScope(Node node) {
+    // See if any variable in the top-scope of the function is captured. If yes
+    // we need to create a box-object.
+    ClosureScope scopeData = closureData.capturingScopes[node];
+    if (scopeData !== null) {
+      // The scope has captured variables. Create a box.
+      // TODO(floitsch): Clean up this hack. Should we create a box-object by
+      // just creating an empty object literal?
+      HInstruction box = createBox();
+      // Add the box to the known locals.
+      directLocals[scopeData.boxElement] = box;
+      // Make sure that accesses to the boxed locals go into the box. We also
+      // need to make sure that parameters are copied into the box if necessary.
+      scopeData.capturedVariableMapping.forEach((Element from, Element to) {
+        // The [from] can only be a parameter for function-scopes and not
+        // loop scopes.
+        if (from.kind == ElementKind.PARAMETER) {
+          // Store the captured parameter in the box. Get the current value
+          // before we put the redirection in place.
+          HInstruction instruction = readLocal(from);
+          redirectElement(from, to);
+          // Now that the redirection is set up, the update to the local will
+          // write the parameter value into the box.
+          updateLocal(from, instruction);
+        } else {
+          redirectElement(from, to);
+        }
+      });
+    }
+  }
+
+  /**
+   * Replaces the current box with a new box and copies over the given list
+   * of elements from the old box into the new box.
+   */
+  void updateCaptureBox(Element boxElement, List<Element> toBeCopiedElements) {
+    // Create a new box and copy over the values from the old box into the
+    // new one.
+    HInstruction oldBox = readLocal(boxElement);
+    HInstruction newBox = createBox();
+    for (Element boxedVariable in toBeCopiedElements) {
+      // [readLocal] uses the [boxElement] to find its box. By replacing it
+      // behind its back we can still get to the old values.
+      updateLocal(boxElement, oldBox);
+      HInstruction oldValue = readLocal(boxedVariable);
+      updateLocal(boxElement, newBox);
+      updateLocal(boxedVariable, oldValue);
+    }
+    updateLocal(boxElement, newBox);
+  }
+
+  void startFunction(FunctionElement function,
+                     FunctionExpression node) {
+
+    ClosureTranslator translator =
+        new ClosureTranslator(builder.compiler, builder.elements);
+    closureData = translator.translate(node);
+
+    FunctionParameters params = function.computeParameters(builder.compiler);
+    params.forEachParameter((Element element) {
+      HParameterValue parameter = new HParameterValue(element);
+      builder.add(parameter);
+      directLocals[element] = parameter;
+    });
+    if (closureData.thisElement !== null) {
+      // Once closures have been mapped to classes their instance members might
+      // not have any thisElement if the closure was created inside a static
+      // context.
+      assert(function.isInstanceMember() || function.isGenerativeConstructor());
+      // We have to introduce 'this' before we enter the scope, since it might
+      // need to be copied into a box (if it is captured). This is similar
+      // to all other parameters that are introduced.
+      HInstruction thisInstruction = new HThis();
+      builder.add(thisInstruction);
+      directLocals[closureData.thisElement] = thisInstruction;
+    }
+
+    enterScope(node);
+
+    // If the freeVariableMapping is not empty, then this function was a
+    // nested closure that captures variables. Redirect the captured
+    // variables to fields in the closure.
+    closureData.freeVariableMapping.forEach((Element from, Element to) {
+      redirectElement(from, to);
+    });
+    if (closureData.isClosure()) {
+      // Inside closure redirect references to itself to [:this:].
+      HInstruction thisInstruction = new HThis();
+      builder.add(thisInstruction);
+      updateLocal(closureData.closureElement, thisInstruction);
+    }
+  }
+
+  bool hasValueForDirectLocal(Element element) {
+    assert(element !== null);
+    assert(isAccessedDirectly(element));
+    return directLocals[element] !== null;
+  }
+
+  /**
+   * Returns true if the local can be accessed directly. Boxed variables or
+   * captured variables that are stored in the closure-field return [false].
+   */
+  bool isAccessedDirectly(Element element) {
+    assert(element !== null);
+    return redirectionMapping[element] === null
+        && !closureData.usedVariablesInTry.contains(element);
+  }
+
+  bool isStoredInClosureField(Element element) {
+    assert(element !== null);
+    if (isAccessedDirectly(element)) return false;
+    Element redirectTarget = redirectionMapping[element];
+    if (redirectTarget == null) return false;
+    if (redirectTarget.enclosingElement.kind == ElementKind.CLASS) {
+      assert(redirectTarget is ClosureFieldElement);
+      return true;
+    }
+    return false;
+  }
+
+  bool isBoxed(Element element) {
+    if (isAccessedDirectly(element)) return false;
+    if (isStoredInClosureField(element)) return false;
+    return redirectionMapping[element] !== null;
+  }
+
+  bool isUsedInTry(Element element) {
+    return closureData.usedVariablesInTry.contains(element);
+  }
+
+  /**
+   * Returns an [HInstruction] for the given element. If the element is
+   * boxed or stored in a closure then the method generates code to retrieve
+   * the value.
+   */
+  HInstruction readLocal(Element element) {
+    if (isAccessedDirectly(element)) {
+      if (directLocals[element] == null) {
+        builder.compiler.internalError("Cannot find value $element",
+                                       element: element);
+      }
+      return directLocals[element];
+    } else if (isStoredInClosureField(element)) {
+      Element redirect = redirectionMapping[element];
+      // We must not use the [LocalsHandler.readThis()] since that could
+      // point to a captured this which would be stored in a closure-field
+      // itself.
+      HInstruction receiver = new HThis();
+      builder.add(receiver);
+      HInstruction fieldGet = new HFieldGet(redirect, receiver);
+      builder.add(fieldGet);
+      return fieldGet;
+    } else if (isBoxed(element)) {
+      Element redirect = redirectionMapping[element];
+      // In the function that declares the captured variable the box is
+      // accessed as direct local. Inside the nested closure the box is
+      // accessed through a closure-field.
+      // Calling [readLocal] makes sure we generate the correct code to get
+      // the box.
+      assert(redirect.enclosingElement.kind == ElementKind.VARIABLE);
+      HInstruction box = readLocal(redirect.enclosingElement);
+      HInstruction lookup = new HFieldGet(redirect, box);
+      builder.add(lookup);
+      return lookup;
+    } else {
+      assert(isUsedInTry(element));
+      HInstruction variable = new HFieldGet.fromActivation(element);
+      builder.add(variable);
+      return variable;
+    }
+  }
+
+  HInstruction readThis() {
+    return readLocal(closureData.thisElement);
+  }
+
+  /**
+   * Sets the [element] to [value]. If the element is boxed or stored in a
+   * closure then the method generates code to set the value.
+   */
+  void updateLocal(Element element, HInstruction value) {
+    if (isAccessedDirectly(element)) {
+      directLocals[element] = value;
+    } else if (isStoredInClosureField(element)) {
+      Element redirect = redirectionMapping[element];
+      // We must not use the [LocalsHandler.readThis()] since that could
+      // point to a captured this which would be stored in a closure-field
+      // itself.
+      HInstruction receiver = new HThis();
+      builder.add(receiver);
+      builder.add(new HFieldSet(redirect, receiver, value));
+    } else if (isBoxed(element)) {
+      Element redirect = redirectionMapping[element];
+      // The box itself could be captured, or be local. A local variable that
+      // is captured will be boxed, but the box itself will be a local.
+      // Inside the closure the box is stored in a closure-field and cannot
+      // be accessed directly.
+      assert(redirect.enclosingElement.kind == ElementKind.VARIABLE);
+      HInstruction box = readLocal(redirect.enclosingElement);
+      builder.add(new HFieldSet(redirect, box, value));
+    } else {
+      assert(isUsedInTry(element));
+      builder.add(new HFieldSet.fromActivation(element,value));
+    }
+  }
+
+  /**
+   * This function must be called before visiting any children of the loop. In
+   * particular it needs to be called before executing the initializers.
+   *
+   * The [LocalsHandler] will make the boxes and updates at the right moment.
+   * The builder just needs to call [enterLoopBody] and [enterLoopUpdates] (for
+   * [For] loops) at the correct places. For phi-handling [beginLoopHeader] and
+   * [endLoop] must also be called.
+   *
+   * The correct place for the box depends on the given loop. In most cases
+   * the box will be created when entering the loop-body: while, do-while, and
+   * for-in (assuming the call to [:next:] is inside the body) can always be
+   * constructed this way.
+   *
+   * Things are slightly more complicated for [For] loops. If no declared
+   * loop variable is boxed then the loop-body approach works here too. If a
+   * loop-variable is boxed we need to introduce a new box for the
+   * loop-variable before we enter the initializer so that the initializer
+   * writes the values into the box. In any case we need to create the box
+   * before the condition since the condition could box the variable.
+   * Since the first box is created outside the actual loop we have a second
+   * location where a box is created: just before the updates. This is
+   * necessary since updates are considered to be part of the next iteration
+   * (and can again capture variables).
+   *
+   * For example the following Dart code prints 1 3 -- 3 4.
+   *
+   *     var fs = [];
+   *     for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
+   *       i++;
+   *     }
+   *     print("--");
+   *     for (var i = 0; i < 2; i++) fs[i]();
+   *
+   * We solve this by emitting the following code (only for [For] loops):
+   *  <Create box>    <== move the first box creation outside the loop.
+   *  <initializer>;
+   *  loop-entry:
+   *    if (!<condition>) goto loop-exit;
+   *    <body>
+   *    <update box>  // create a new box and copy the captured loop-variables.
+   *    <updates>
+   *    goto loop-entry;
+   *  loop-exit:
+   */
+  void startLoop(Node node) {
+    ClosureScope scopeData = closureData.capturingScopes[node];
+    if (scopeData == null) return;
+    if (scopeData.hasBoxedLoopVariables()) {
+      // If there are boxed loop variables then we set up the box and
+      // redirections already now. This way the initializer can write its
+      // values into the box.
+      // For other loops the box will be created when entering the body.
+      enterScope(node);
+    }
+  }
+
+  void beginLoopHeader(Node node, HBasicBlock loopEntry) {
+    // Create a copy because we modify the map while iterating over
+    // it.
+    Map<Element, HInstruction> saved =
+        new Map<Element, HInstruction>.from(directLocals);
+
+    // Create phis for all elements in the definitions environment.
+    saved.forEach((Element element, HInstruction instruction) {
+      // We know 'this' cannot be modified.
+      if (element !== closureData.thisElement) {
+        HPhi phi = new HPhi.singleInput(element, instruction);
+        loopEntry.addPhi(phi);
+        directLocals[element] = phi;
+      } else {
+        directLocals[element] = instruction;
+      }
+    });
+  }
+
+  void enterLoopBody(Node node) {
+    ClosureScope scopeData = closureData.capturingScopes[node];
+    if (scopeData == null) return;
+    // If there are no declared boxed loop variables then we did not create the
+    // box before the initializer and we have to create the box now.
+    if (!scopeData.hasBoxedLoopVariables()) {
+      enterScope(node);
+    }
+  }
+
+  void enterLoopUpdates(Loop node) {
+    // If there are declared boxed loop variables then the updates might have
+    // access to the box and we must switch to a new box before executing the
+    // updates.
+    // In all other cases a new box will be created when entering the body of
+    // the next iteration.
+    ClosureScope scopeData = closureData.capturingScopes[node];
+    if (scopeData == null) return;
+    if (scopeData.hasBoxedLoopVariables()) {
+      updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
+    }
+  }
+
+  void endLoop(HBasicBlock loopEntry) {
+    loopEntry.forEachPhi((HPhi phi) {
+      Element element = phi.element;
+      HInstruction postLoopDefinition = directLocals[element];
+      phi.addInput(postLoopDefinition);
+    });
+  }
+
+  /**
+   * Merge [otherLocals] into this locals handler, creating phi-nodes when
+   * there is a conflict.
+   * If a phi node is necessary, it will use the otherLocals instruction as the
+   * first input, and this handler's instruction as the second.
+   * NOTICE: This means that the predecessor corresponding to [otherLocals]
+   * should be the first predecessor of the current block, and the one
+   * corresponding to this locals handler should be the second.
+   */
+  void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
+    // If an element is in one map but not the other we can safely
+    // ignore it. It means that a variable was declared in the
+    // block. Since variable declarations are scoped the declared
+    // variable cannot be alive outside the block. Note: this is only
+    // true for nodes where we do joins.
+    Map<Element, HInstruction> joinedLocals = new Map<Element, HInstruction>();
+    otherLocals.directLocals.forEach((element, instruction) {
+      // We know 'this' cannot be modified.
+      if (element === closureData.thisElement) {
+        assert(directLocals[element] == instruction);
+        joinedLocals[element] = instruction;
+      } else {
+        HInstruction mine = directLocals[element];
+        if (mine === null) return;
+        if (instruction === mine) {
+          joinedLocals[element] = instruction;
+        } else {
+          HInstruction phi =
+              new HPhi.manyInputs(element, <HInstruction>[instruction, mine]);
+          joinBlock.addPhi(phi);
+          joinedLocals[element] = phi;
+        }
+      }
+    });
+    directLocals = joinedLocals;
+  }
+
+  /**
+   * The current localsHandler is not used for its values, only for its
+   * declared variables. This is a way to exclude local values from the
+   * result when they are no longer in scope.
+   * Returns the new LocalsHandler to use (may not be [this]).
+   */
+  LocalsHandler mergeMultiple(List<LocalsHandler> locals,
+                              HBasicBlock joinBlock) {
+    assert(locals.length > 0);
+    if (locals.length == 1) return locals[0];
+    Map<Element, HInstruction> joinedLocals = new Map<Element,HInstruction>();
+    HInstruction thisValue = null;
+    directLocals.forEach((Element element, HInstruction instruction) {
+      if (element !== closureData.thisElement) {
+        HPhi phi = new HPhi.noInputs(element);
+        joinedLocals[element] = phi;
+        joinBlock.addPhi(phi);
+      } else {
+        // We know that "this" never changes, if it's there.
+        // Save it for later. While merging, there is no phi for "this",
+        // so we don't have to special case it in the merge loop.
+        thisValue = instruction;
+      }
+    });
+    for (LocalsHandler local in locals) {
+      local.directLocals.forEach((Element element, HInstruction instruction) {
+        HPhi phi = joinedLocals[element];
+        if (phi !== null) {
+          phi.addInput(instruction);
+        }
+      });
+    }
+    if (thisValue !== null) {
+      // If there was a "this" for the scope, add it to the new locals.
+      joinedLocals[closureData.thisElement] = thisValue;
+    }
+    directLocals = joinedLocals;
+    return this;
+  }
+}
+
+
+// Represents a single break/continue instruction.
+class JumpHandlerEntry {
+  final HGoto jumpInstruction;
+  final LocalsHandler locals;
+  bool isBreak() => jumpInstruction is HBreak;
+  bool isContinue() => jumpInstruction is HContinue;
+  JumpHandlerEntry(this.jumpInstruction, this.locals);
+}
+
+
+interface JumpHandler default JumpHandlerImpl {
+  JumpHandler(SsaBuilder builder, TargetElement target);
+  void generateBreak([LabelElement label]);
+  void generateContinue([LabelElement label]);
+  void forEachBreak(void action(HBreak instruction, LocalsHandler locals));
+  void forEachContinue(void action(HBreak instruction, LocalsHandler locals));
+  void close();
+  List<LabelElement> labels();
+}
+
+// Insert break handler used to avoid null checks when a target isn't
+// used as the target of a break, and therefore doesn't need a break
+// handler associated with it.
+class NullJumpHandler implements JumpHandler {
+  const NullJumpHandler();
+  void generateBreak([LabelElement label]) { unreachable(); }
+  void generateContinue([LabelElement label]) { unreachable(); }
+  void forEachBreak(Function ignored) { }
+  void forEachContinue(Function ignored) { }
+  void close() { }
+  List<LabelElement> labels() => const <LabelElement>[];
+}
+
+// Records breaks until a target block is available.
+// Breaks are always forward jumps.
+// Continues in loops are implemented as breaks of the body.
+// Continues in switches is currently not handled.
+class JumpHandlerImpl implements JumpHandler {
+  final SsaBuilder builder;
+  final TargetElement target;
+  final List<JumpHandlerEntry> jumps;
+
+  JumpHandlerImpl(SsaBuilder builder, this.target)
+      : this.builder = builder,
+        jumps = <JumpHandlerEntry>[] {
+    assert(builder.jumpTargets[target] === null);
+    builder.jumpTargets[target] = this;
+  }
+
+  void generateBreak([LabelElement label]) {
+    HInstruction breakInstruction;
+    if (label === null) {
+      breakInstruction = new HBreak(target);
+    } else {
+      breakInstruction = new HBreak.toLabel(label);
+    }
+    LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
+    builder.close(breakInstruction);
+    jumps.add(new JumpHandlerEntry(breakInstruction, locals));
+  }
+
+  void generateContinue([LabelElement label]) {
+    HInstruction continueInstruction;
+    if (label === null) {
+      continueInstruction = new HContinue(target);
+    } else {
+      continueInstruction = new HContinue.toLabel(label);
+    }
+    LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
+    builder.close(continueInstruction);
+    jumps.add(new JumpHandlerEntry(continueInstruction, locals));
+  }
+
+  void forEachBreak(Function action) {
+    for (JumpHandlerEntry entry in jumps) {
+      if (entry.isBreak()) action(entry.jumpInstruction, entry.locals);
+    }
+  }
+
+  void forEachContinue(Function action) {
+    for (JumpHandlerEntry entry in jumps) {
+      if (entry.isContinue()) action(entry.jumpInstruction, entry.locals);
+    }
+  }
+
+  void close() {
+    // The mapping from TargetElement to JumpHandler is no longer needed.
+    builder.jumpTargets.remove(target);
+  }
+
+  List<LabelElement> labels() {
+    List<LabelElement> result = null;
+    for (LabelElement element in target.labels) {
+      if (result === null) result = <LabelElement>[];
+      result.add(element);
+    }
+    return (result === null) ? const <LabelElement>[] : result;
+  }
+}
+
+class SsaBuilder implements Visitor {
+  final Compiler compiler;
+  TreeElements elements;
+  final Interceptors interceptors;
+  final WorkItem work;
+  bool methodInterceptionEnabled;
+  HGraph graph;
+  LocalsHandler localsHandler;
+  HInstruction rethrowableException;
+
+  Map<TargetElement, JumpHandler> jumpTargets;
+
+  // We build the Ssa graph by simulating a stack machine.
+  List<HInstruction> stack;
+
+  // The current block to add instructions to. Might be null, if we are
+  // visiting dead code.
+  HBasicBlock current;
+  // The most recently opened block. Has the same value as [current] while
+  // the block is open, but unlike [current], it isn't cleared when the current
+  // block is closed.
+  HBasicBlock lastOpenedBlock;
+
+  LibraryElement get currentLibrary() => work.element.getLibrary();
+
+  SsaBuilder(Compiler compiler, WorkItem work)
+    : this.compiler = compiler,
+      this.work = work,
+      interceptors = compiler.builder.interceptors,
+      methodInterceptionEnabled = true,
+      elements = work.resolutionTree,
+      graph = new HGraph(),
+      stack = new List<HInstruction>(),
+      jumpTargets = new Map<TargetElement, JumpHandler>() {
+    localsHandler = new LocalsHandler(this);
+  }
+
+  void disableMethodInterception() {
+    assert(methodInterceptionEnabled);
+    methodInterceptionEnabled = false;
+  }
+
+  void enableMethodInterception() {
+    assert(!methodInterceptionEnabled);
+    methodInterceptionEnabled = true;
+  }
+
+  HGraph buildMethod(FunctionElement functionElement) {
+    FunctionExpression function = functionElement.parseNode(compiler);
+    openFunction(functionElement, function);
+    function.body.accept(this);
+    return closeFunction();
+  }
+
+  /**
+   * Returns the constructor body associated with the given constructor or
+   * creates a new constructor body, if none can be found.
+   */
+  ConstructorBodyElement getConstructorBody(ClassElement classElement,
+                                            FunctionElement constructor) {
+    assert(constructor.kind === ElementKind.GENERATIVE_CONSTRUCTOR);
+    ConstructorBodyElement bodyElement;
+    for (Link<Element> backendMembers = classElement.backendMembers;
+         !backendMembers.isEmpty();
+         backendMembers = backendMembers.tail) {
+      Element backendMember = backendMembers.head;
+      if (backendMember.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+        ConstructorBodyElement body = backendMember;
+        if (body.constructor == constructor) {
+          bodyElement = backendMember;
+          break;
+        }
+      }
+    }
+    if (bodyElement === null) {
+      bodyElement = new ConstructorBodyElement(constructor);
+      TreeElements treeElements =
+          compiler.resolver.resolveMethodElement(constructor);
+      compiler.enqueue(new WorkItem.toCodegen(bodyElement, treeElements));
+      classElement.backendMembers =
+          classElement.backendMembers.prepend(bodyElement);
+    }
+    assert(bodyElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY);
+    return bodyElement;
+  }
+
+  /**
+   * Run through the initializers and inline all field initializers. Recursively
+   * inlines super initializers.
+   *
+   * The constructors of the inlined initializers is added to [constructors]
+   * with sub constructors having a lower index than super constructors.
+   */
+  void inlineInitializers(FunctionElement constructor,
+                          List<FunctionElement> constructors,
+                          Map<Element, HInstruction> fieldValues) {
+    TreeElements oldElements = elements;
+    constructors.addLast(constructor);
+    bool initializedSuper = false;
+    elements = compiler.resolver.resolveMethodElement(constructor);
+    FunctionExpression functionNode = constructor.parseNode(compiler);
+
+    if (functionNode.initializers !== null) {
+      Link<Node> initializers = functionNode.initializers.nodes;
+      for (Link<Node> link = initializers; !link.isEmpty(); link = link.tail) {
+        assert(link.head is Send);
+        if (link.head is !SendSet) {
+          // A super initializer or constructor redirection.
+          Send call = link.head;
+          assert(Initializers.isSuperConstructorCall(call) ||
+                 Initializers.isConstructorRedirect(call));
+          FunctionElement nextConstructor = elements[call];
+          // Visit arguments and map the corresponding parameter value to
+          // the resulting HInstruction value.
+          List<HInstruction> arguments = new List<HInstruction>();
+          addStaticSendArgumentsToList(call, nextConstructor, arguments);
+          int index = 0;
+          FunctionParameters parameters =
+              nextConstructor.computeParameters(compiler);
+          parameters.forEachParameter((Element parameter) {
+            HInstruction argument = arguments[index++];
+            localsHandler.updateLocal(parameter, argument);
+            // Don't forget to update the field, if the parameter is of the
+            // form [:this.x:].
+            if (parameter.kind == ElementKind.FIELD_PARAMETER) {
+              FieldParameterElement fieldParameterElement = parameter;
+              fieldValues[fieldParameterElement.fieldElement] = argument;
+            }
+          });
+          inlineInitializers(nextConstructor, constructors, fieldValues);
+          initializedSuper = true;
+        } else {
+          // A field initializer.
+          SendSet init = link.head;
+          Link<Node> arguments = init.arguments;
+          assert(!arguments.isEmpty() && arguments.tail.isEmpty());
+          visit(arguments.head);
+          fieldValues[elements[init]] = pop();
+        }
+      }
+    }
+
+    if (!initializedSuper) {
+      // No super initializer found. Try to find the default constructor if
+      // the class is not Object.
+      ClassElement enclosingClass = constructor.enclosingElement;
+      ClassElement superClass = enclosingClass.superclass;
+      if (enclosingClass != compiler.objectClass) {
+        assert(superClass !== null);
+        assert(superClass.isResolved);
+        FunctionElement nextConstructor =
+            superClass.lookupConstructor(superClass.name);
+        if (nextConstructor === null) {
+          compiler.internalError("no default constructor available");
+        }
+        inlineInitializers(nextConstructor, constructors, fieldValues);
+      }
+    }
+
+    elements = oldElements;
+  }
+
+  /**
+   * Build the factory function corresponding to the constructor
+   * [functionElement]:
+   *  - Initialize fields with the values of the field initializers of the
+   *    current constructor and super constructors or constructors redirected
+   *    to, starting from the current constructor.
+   *  - Call the the constructor bodies, starting from the constructor(s) in the
+   *    super class(es).
+   */
+  HGraph buildFactory(ClassElement classElement,
+                      FunctionElement functionElement) {
+    FunctionExpression function = functionElement.parseNode(compiler);
+    // Note that constructors (like any other static function) do not need
+    // to deal with optional arguments. It is the callers job to provide all
+    // arguments as if they were positional.
+
+    // The initializer list could contain closures.
+    openFunction(functionElement, function);
+
+    Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>();
+    FunctionParameters parameters = functionElement.computeParameters(compiler);
+    parameters.forEachParameter((Element element) {
+      if (element.kind == ElementKind.FIELD_PARAMETER) {
+        // If the [element] is a field-parameter (such as [:this.x:] then
+        // initialize the field element with its value.
+        FieldParameterElement fieldParameterElement = element;
+        HInstruction parameterValue = localsHandler.readLocal(element);
+        fieldValues[fieldParameterElement.fieldElement] = parameterValue;
+      }
+    });
+
+    final Map<FunctionElement, TreeElements> constructorElements =
+        compiler.resolver.constructorElements;
+    List<FunctionElement> constructors = new List<FunctionElement>();
+
+    // Analyze the constructor and all referenced constructors and collect
+    // initializers and constructor bodies.
+    inlineInitializers(functionElement, constructors, fieldValues);
+
+    // Call the JavaScript constructor with the fields as argument.
+    // TODO(floitsch,karlklose): move this code to ClassElement and share with
+    //                           the emitter.
+    List<HInstruction> constructorArguments = <HInstruction>[];
+    ClassElement element = classElement;
+    while (element != null) {
+      for (Element member in element.members) {
+        if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
+          HInstruction value = fieldValues[member];
+          if (value === null) {
+            // The field has no value in the initializer list. Initialize it
+            // with the declaration-site constant (if any).
+            Constant fieldValue =
+                compiler.constantHandler.compileVariable(member);
+            value = graph.addConstant(fieldValue);
+          }
+          constructorArguments.add(value);
+        }
+      }
+      element = element.superclass;
+    }
+    HForeignNew newObject = new HForeignNew(classElement, constructorArguments);
+    add(newObject);
+    // Generate calls to the constructor bodies.
+    for (int index = constructors.length - 1; index >= 0; index--) {
+      FunctionElement constructor = constructors[index];
+      // TODO(floitsch): find better way to detect that constructor body is
+      // empty.
+      if (constructor is SynthesizedConstructorElement) continue;
+      ConstructorBodyElement body = getConstructorBody(classElement,
+                                                       constructor);
+      List bodyCallInputs = <HInstruction>[];
+      bodyCallInputs.add(newObject);
+      body.functionParameters.forEachParameter((parameter) {
+        bodyCallInputs.add(localsHandler.readLocal(parameter));
+      });
+      // TODO(ahe): The constructor name is statically resolved. See
+      // SsaCodeGenerator.visitInvokeDynamicMethod. Is there a cleaner
+      // way to do this?
+      SourceString methodName = new SourceString(compiler.namer.getName(body));
+      add(new HInvokeDynamicMethod(null, methodName, bodyCallInputs));
+    }
+    close(new HReturn(newObject)).addSuccessor(graph.exit);
+    return closeFunction();
+  }
+
+  void openFunction(FunctionElement functionElement,
+                    FunctionExpression node) {
+    HBasicBlock block = graph.addNewBlock();
+    open(graph.entry);
+
+    localsHandler.startFunction(functionElement, node);
+    close(new HGoto()).addSuccessor(block);
+
+    open(block);
+  }
+
+  HGraph closeFunction() {
+    // TODO(kasperl): Make this goto an implicit return.
+    if (!isAborted()) close(new HGoto()).addSuccessor(graph.exit);
+    graph.finalize();
+    return graph;
+  }
+
+  HBasicBlock addNewBlock() {
+    HBasicBlock block = graph.addNewBlock();
+    // If adding a new block during building of an expression, it is due to
+    // conditional expressions or short-circuit logical operators.
+    return block;
+  }
+
+  void open(HBasicBlock block) {
+    block.open();
+    current = block;
+    lastOpenedBlock = block;
+  }
+
+  HBasicBlock close(HControlFlow end) {
+    HBasicBlock result = current;
+    current.close(end);
+    current = null;
+    return result;
+  }
+
+  void goto(HBasicBlock from, HBasicBlock to) {
+    from.close(new HGoto());
+    from.addSuccessor(to);
+  }
+
+  bool isAborted() {
+    return current === null;
+  }
+
+  void add(HInstruction instruction) {
+    current.add(instruction);
+  }
+
+  void push(HInstruction instruction) {
+    add(instruction);
+    stack.add(instruction);
+  }
+
+  HInstruction pop() {
+    return stack.removeLast();
+  }
+
+  HBoolify popBoolified() {
+    HBoolify boolified = new HBoolify(pop());
+    add(boolified);
+    return boolified;
+  }
+
+  void visit(Node node) {
+    if (node !== null) node.accept(this);
+  }
+
+  visitBlock(Block node) {
+    for (Link<Node> link = node.statements.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      visit(link.head);
+      if (isAborted()) {
+        // The block has been aborted by a return or a throw.
+        if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack');
+        return;
+      }
+    }
+    assert(!current.isClosed());
+    if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack');
+  }
+
+  visitClassNode(ClassNode node) {
+    unreachable();
+  }
+
+  visitExpressionStatement(ExpressionStatement node) {
+    visit(node.expression);
+    pop();
+  }
+
+  /**
+   * Creates a new loop-header block. The previous [current] block
+   * is closed with an [HGoto] and replaced by the newly created block.
+   * Also notifies the locals handler that we're entering a loop.
+   */
+  JumpHandler beginLoopHeader(Node node) {
+    assert(!isAborted());
+    HBasicBlock previousBlock = close(new HGoto());
+
+    JumpHandler jumpHandler = createJumpHandler(node);
+    HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(jumpHandler.labels());
+    previousBlock.addSuccessor(loopEntry);
+    open(loopEntry);
+
+    localsHandler.beginLoopHeader(node, loopEntry);
+    return jumpHandler;
+  }
+
+  /**
+   * Ends the loop:
+   * - creates a new block and adds it as successor to the [branchBlock].
+   * - opens the new block (setting as [current]).
+   * - notifies the locals handler that we're exiting a loop.
+   */
+  void endLoop(HBasicBlock loopEntry,
+               HBasicBlock branchBlock,
+               JumpHandler jumpHandler,
+               LocalsHandler savedLocals) {
+    HBasicBlock loopExitBlock = addNewBlock();
+    assert(branchBlock.successors.length == 1);
+    List<LocalsHandler> breakLocals = <LocalsHandler>[];
+    jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
+      breakInstruction.block.addSuccessor(loopExitBlock);
+      breakLocals.add(locals);
+    });
+    branchBlock.addSuccessor(loopExitBlock);
+    open(loopExitBlock);
+    localsHandler.endLoop(loopEntry);
+    if (!breakLocals.isEmpty()) {
+      breakLocals.add(savedLocals);
+      localsHandler = savedLocals.mergeMultiple(breakLocals, loopExitBlock);
+    } else {
+      localsHandler = savedLocals;
+    }
+  }
+
+  // For while loops, initializer and update are null.
+  // The condition function must return a boolean result.
+  // None of the functions must leave anything on the stack.
+  handleLoop(Node loop,
+             void initialize(),
+             HInstruction condition(),
+             void update(),
+             void body()) {
+    // Generate:
+    //  <initializer>
+    //  loop-entry:
+    //    if (!<condition>) goto loop-exit;
+    //    <body>
+    //    <updates>
+    //    goto loop-entry;
+    //  loop-exit:
+
+    localsHandler.startLoop(loop);
+
+    // The initializer.
+    initialize();
+    assert(!isAborted());
+
+    JumpHandler jumpHandler = beginLoopHeader(loop);
+    HBasicBlock conditionBlock = current;
+
+    HInstruction conditionInstruction = condition();
+    HBasicBlock conditionExitBlock =
+        close(new HLoopBranch(conditionInstruction));
+
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+
+    // The body.
+    HBasicBlock beginBodyBlock = addNewBlock();
+    conditionExitBlock.addSuccessor(beginBodyBlock);
+    open(beginBodyBlock);
+
+    localsHandler.enterLoopBody(loop);
+    hackAroundPossiblyAbortingBody(loop, body);
+
+    SubGraph bodyGraph = new SubGraph(beginBodyBlock, current);
+    HBasicBlock bodyBlock = close(new HGoto());
+
+    // Update.
+    // We create an update block, even when we are in a while loop. There the
+    // update block is the jump-target for continue statements. We could avoid
+    // the creation if there is no continue, but for now we always create it.
+    HBasicBlock updateBlock = addNewBlock();
+
+    List<LocalsHandler> continueLocals = <LocalsHandler>[];
+    jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
+      instruction.block.addSuccessor(updateBlock);
+      continueLocals.add(locals);
+    });
+    bodyBlock.addSuccessor(updateBlock);
+    continueLocals.add(localsHandler);
+
+    open(updateBlock);
+
+    localsHandler = localsHandler.mergeMultiple(continueLocals, updateBlock);
+
+    HLabeledBlockInformation labelInfo;
+    List<LabelElement> labels = jumpHandler.labels();
+    TargetElement target = elements[loop];
+    if (!labels.isEmpty()) {
+      beginBodyBlock.labeledBlockInformation =
+          new HLabeledBlockInformation(bodyGraph, updateBlock,
+                                       jumpHandler.labels(), isContinue: true);
+    } else if (target !== null && target.isContinueTarget) {
+      beginBodyBlock.labeledBlockInformation =
+          new HLabeledBlockInformation.implicit(bodyGraph, updateBlock,
+                                                target, isContinue: true);
+    }
+
+    localsHandler.enterLoopUpdates(loop);
+
+    update();
+
+    updateBlock = close(new HGoto());
+    // The back-edge completing the cycle.
+    updateBlock.addSuccessor(conditionBlock);
+    conditionBlock.postProcessLoopHeader();
+
+    endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals);
+  }
+
+  visitFor(For node) {
+    assert(node.body !== null);
+    void buildInitializer() {
+      if (node.initializer === null) return;
+      Node initializer = node.initializer;
+      if (initializer !== null) {
+        visit(initializer);
+        if (initializer.asExpression() !== null) {
+          pop();
+        }
+      }
+    }
+    HInstruction buildCondition() {
+      if (node.condition === null) {
+        return graph.addConstantBool(true);
+      }
+      visit(node.condition);
+      return popBoolified();
+    }
+    void buildUpdate() {
+      for (Expression expression in node.update) {
+        visit(expression);
+        assert(!isAborted());
+        // The result of the update instruction isn't used, and can just
+        // be dropped.
+        HInstruction updateInstruction = pop();
+      }
+    }
+    void buildBody() {
+      visit(node.body);
+    }
+    handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody);
+  }
+
+  visitWhile(While node) {
+    HInstruction buildCondition() {
+      visit(node.condition);
+      return popBoolified();
+    }
+    handleLoop(node,
+               () {},
+               buildCondition,
+               () {},
+               () { visit(node.body); });
+  }
+
+  visitDoWhile(DoWhile node) {
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+    localsHandler.startLoop(node);
+    JumpHandler jumpHandler = beginLoopHeader(node);
+    HBasicBlock loopEntryBlock = current;
+    HBasicBlock bodyEntryBlock = current;
+    TargetElement target = elements[node];
+    bool hasContinues = target !== null && target.isContinueTarget;
+    if (hasContinues) {
+      // Add extra block to hang labels on.
+      // It doesn't currently work if they are on the same block as the
+      // HLoopInfo. The handling of HLabeledBlockInformation will visit a
+      // SubGraph that starts at the same block again, so the HLoopInfo is
+      // either handled twice, or it's handled after the labeled block info,
+      // both of which generate the wrong code.
+      // Using a separate block is just a simple workaround.
+      bodyEntryBlock = graph.addNewBlock();
+      goto(current, bodyEntryBlock);
+      open(bodyEntryBlock);
+    }
+    localsHandler.enterLoopBody(node);
+    hackAroundPossiblyAbortingBody(node, () { visit(node.body); });
+
+    // If there are no continues we could avoid the creation of the condition
+    // block. This could also lead to a block having multiple entries and exits.
+    HBasicBlock bodyExitBlock = close(new HGoto());
+    HBasicBlock conditionBlock = addNewBlock();
+
+    List<LocalsHandler> continueLocals = <LocalsHandler>[];
+    jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
+      instruction.block.addSuccessor(conditionBlock);
+      continueLocals.add(locals);
+    });
+    bodyExitBlock.addSuccessor(conditionBlock);
+    if (!continueLocals.isEmpty()) {
+      continueLocals.add(localsHandler);
+      localsHandler = savedLocals.mergeMultiple(continueLocals, conditionBlock);
+      SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
+      List<LabelElement> labels = jumpHandler.labels();
+      if (!labels.isEmpty()) {
+        bodyEntryBlock.labeledBlockInformation =
+            new HLabeledBlockInformation(bodyGraph,
+                                         conditionBlock,
+                                         labels,
+                                         isContinue: true);
+      } else {
+        bodyEntryBlock.labeledBlockInformation =
+            new HLabeledBlockInformation.implicit(bodyGraph,
+                                                  conditionBlock,
+                                                  target,
+                                                  isContinue: true);
+      }
+    }
+    open(conditionBlock);
+
+    visit(node.condition);
+    assert(!isAborted());
+    conditionBlock = close(new HLoopBranch(popBoolified(),
+                                           HLoopBranch.DO_WHILE_LOOP));
+
+    conditionBlock.addSuccessor(loopEntryBlock);  // The back-edge.
+    loopEntryBlock.postProcessLoopHeader();
+
+    endLoop(loopEntryBlock, conditionBlock, jumpHandler, localsHandler);
+    jumpHandler.close();
+  }
+
+  visitFunctionExpression(FunctionExpression node) {
+    ClosureData nestedClosureData = compiler.builder.closureDataCache[node];
+    if (nestedClosureData === null) {
+      // TODO(floitsch): we can only assume that the reason for not having a
+      // closure data here is, because the function is inside an initializer.
+      compiler.unimplemented("Closures inside initializers", node: node);
+    }
+    assert(nestedClosureData !== null);
+    assert(nestedClosureData.closureClassElement !== null);
+    ClassElement closureClassElement =
+        nestedClosureData.closureClassElement;
+    FunctionElement callElement = nestedClosureData.callElement;
+    compiler.enqueue(new WorkItem.toCodegen(callElement, elements));
+    compiler.registerInstantiatedClass(closureClassElement);
+    assert(closureClassElement.members.isEmpty());
+
+    List<HInstruction> capturedVariables = <HInstruction>[];
+    for (Element member in closureClassElement.backendMembers) {
+      // The backendMembers also contains the call method(s). We are only
+      // interested in the fields.
+      if (member.kind == ElementKind.FIELD) {
+        Element capturedLocal = nestedClosureData.capturedFieldMapping[member];
+        assert(capturedLocal != null);
+        capturedVariables.add(localsHandler.readLocal(capturedLocal));
+      }
+    }
+
+    push(new HForeignNew(closureClassElement, capturedVariables));
+  }
+
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    visit(node.function);
+    localsHandler.updateLocal(elements[node], pop());
+  }
+
+  visitIdentifier(Identifier node) {
+    if (node.isThis()) {
+      stack.add(localsHandler.readThis());
+    } else {
+      compiler.internalError("SsaBuilder.visitIdentifier on non-this",
+                             node: node);
+    }
+  }
+
+  visitIf(If node) {
+    visit(node.condition);
+    Function visitElse;
+    if (node.elsePart != null) {
+      visitElse = () {
+        visit(node.elsePart);
+      };
+    }
+    handleIf(() => visit(node.thenPart), visitElse);
+  }
+
+  void handleIf(void visitThen(), void visitElse()) {
+    bool hasElse = visitElse != null;
+    HIf condition = new HIf(popBoolified(), hasElse);
+    HBasicBlock conditionBlock = close(condition);
+
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+
+    // The then part.
+    HBasicBlock thenBlock = addNewBlock();
+    conditionBlock.addSuccessor(thenBlock);
+    open(thenBlock);
+    visitThen();
+    SubGraph thenGraph = new SubGraph(thenBlock, lastOpenedBlock);
+    thenBlock = current;
+
+    // Reset the locals state to the state after the condition and keep the
+    // current state in [thenLocals].
+    LocalsHandler thenLocals = localsHandler;
+
+    // Now the else part.
+    localsHandler = savedLocals;
+    HBasicBlock elseBlock = null;
+    SubGraph elseGraph = null;
+    if (hasElse) {
+      elseBlock = addNewBlock();
+      conditionBlock.addSuccessor(elseBlock);
+      open(elseBlock);
+      visitElse();
+      elseGraph = new SubGraph(elseBlock, lastOpenedBlock);
+      elseBlock = current;
+    }
+
+    HBasicBlock joinBlock = null;
+    if (thenBlock !== null || elseBlock !== null || !hasElse) {
+      joinBlock = addNewBlock();
+      if (thenBlock !== null) goto(thenBlock, joinBlock);
+      if (elseBlock !== null) goto(elseBlock, joinBlock);
+      else if (!hasElse) conditionBlock.addSuccessor(joinBlock);
+      // If the join block has two predecessors we have to merge the
+      // locals. The current locals is what either the
+      // condition or the else block left us with, so we merge that
+      // with the set of locals we got after visiting the then
+      // part of the if.
+      open(joinBlock);
+      if (joinBlock.predecessors.length == 2) {
+        localsHandler.mergeWith(thenLocals, joinBlock);
+      } else if (thenBlock !== null) {
+        // The only predecessor is the then branch.
+        localsHandler = thenLocals;
+      }
+    }
+    condition.blockInformation =
+        new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock);
+  }
+
+  void visitLogicalAndOr(Send node, Operator op) {
+    handleLogicalAndOr(() { visit(node.receiver); },
+                       () { visit(node.argumentsNode); },
+                       isAnd: (const SourceString("&&") == op.source));
+  }
+
+
+  void handleLogicalAndOr(void left(), void right(), [bool isAnd = true]) {
+    // x && y is transformed into:
+    //   t0 = boolify(x);
+    //   if (t0) t1 = boolify(y);
+    //   result = phi(t0, t1);
+    //
+    // x || y is transformed into:
+    //   t0 = boolify(x);
+    //   if (not(t0)) t1 = boolify(y);
+    //   result = phi(t0, t1);
+    left();
+    HInstruction boolifiedLeft = popBoolified();
+    HInstruction condition;
+    if (isAnd) {
+      condition = boolifiedLeft;
+    } else {
+      condition = new HNot(boolifiedLeft);
+      add(condition);
+    }
+    HIf branch = new HIf(condition, false);
+    HBasicBlock leftBlock = close(branch);
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+
+    HBasicBlock rightBlock = addNewBlock();
+    leftBlock.addSuccessor(rightBlock);
+    open(rightBlock);
+
+    right();
+    HInstruction boolifiedRight = popBoolified();
+    SubGraph rightGraph = new SubGraph(rightBlock, current);
+    rightBlock = close(new HGoto());
+
+    HBasicBlock joinBlock = addNewBlock();
+    leftBlock.addSuccessor(joinBlock);
+    rightBlock.addSuccessor(joinBlock);
+    open(joinBlock);
+
+    branch.blockInformation =
+        new HIfBlockInformation(branch, rightGraph, null, joinBlock);
+
+    localsHandler.mergeWith(savedLocals, joinBlock);
+    HPhi result = new HPhi.manyInputs(null,
+        <HInstruction>[boolifiedLeft, boolifiedRight]);
+    joinBlock.addPhi(result);
+    stack.add(result);
+  }
+
+  void visitLogicalNot(Send node) {
+    assert(node.argumentsNode is Prefix);
+    visit(node.receiver);
+    HNot not = new HNot(popBoolified());
+    push(not);
+  }
+
+  void visitUnary(Send node, Operator op) {
+    assert(node.argumentsNode is Prefix);
+    visit(node.receiver);
+    assert(op.token.kind !== PLUS_TOKEN);
+    HInstruction operand = pop();
+
+    HInstruction target =
+        new HStatic(interceptors.getPrefixOperatorInterceptor(op));
+    add(target);
+    HInvokeUnary result;
+    switch (op.source.stringValue) {
+      case "-": result = new HNegate(target, operand); break;
+      case "~": result = new HBitNot(target, operand); break;
+      default: unreachable();
+    }
+    // See if we can constant-fold right away. This avoids rewrites later on.
+    if (operand is HConstant) {
+      HConstant constant = operand;
+      Constant folded = result.operation.fold(constant.constant);
+      if (folded !== null) {
+        stack.add(graph.addConstant(folded));
+        return;
+      }
+    }
+    push(result);
+  }
+
+  void visitBinary(HInstruction left, Operator op, HInstruction right) {
+    Element element = interceptors.getOperatorInterceptor(op);
+    assert(element != null);
+    HInstruction target = new HStatic(element);
+    add(target);
+    switch (op.source.stringValue) {
+      case "+":
+      case "++":
+      case "+=":
+        push(new HAdd(target, left, right));
+        break;
+      case "-":
+      case "--":
+      case "-=":
+        push(new HSubtract(target, left, right));
+        break;
+      case "*":
+      case "*=":
+        push(new HMultiply(target, left, right));
+        break;
+      case "/":
+      case "/=":
+        push(new HDivide(target, left, right));
+        break;
+      case "~/":
+      case "~/=":
+        push(new HTruncatingDivide(target, left, right));
+        break;
+      case "%":
+      case "%=":
+        push(new HModulo(target, left, right));
+        break;
+      case "<<":
+      case "<<=":
+        push(new HShiftLeft(target, left, right));
+        break;
+      case ">>":
+      case ">>=":
+        push(new HShiftRight(target, left, right));
+        break;
+      case "|":
+      case "|=":
+        push(new HBitOr(target, left, right));
+        break;
+      case "&":
+      case "&=":
+        push(new HBitAnd(target, left, right));
+        break;
+      case "^":
+      case "^=":
+        push(new HBitXor(target, left, right));
+        break;
+      case "==":
+        push(new HEquals(target, left, right));
+        break;
+      case "===":
+        push(new HIdentity(target, left, right));
+        break;
+      case "!==":
+        HIdentity eq = new HIdentity(target, left, right);
+        add(eq);
+        push(new HNot(eq));
+        break;
+      case "<":
+        push(new HLess(target, left, right));
+        break;
+      case "<=":
+        push(new HLessEqual(target, left, right));
+        break;
+      case ">":
+        push(new HGreater(target, left, right));
+        break;
+      case ">=":
+        push(new HGreaterEqual(target, left, right));
+        break;
+      case "!=":
+        HEquals eq = new HEquals(target, left, right);
+        add(eq);
+        HBoolify bl = new HBoolify(eq);
+        add(bl);
+        push(new HNot(bl));
+        break;
+      default: compiler.unimplemented("SsaBuilder.visitBinary");
+    }
+  }
+
+  void generateGetter(Send send, Element element) {
+    Selector selector = elements.getSelector(send);
+    if (Elements.isStaticOrTopLevelField(element)) {
+      push(new HStatic(element));
+      if (element.kind == ElementKind.GETTER) {
+        push(new HInvokeStatic(selector, <HInstruction>[pop()]));
+      }
+    } else if (Elements.isInstanceSend(send, elements)) {
+      HInstruction receiver;
+      if (send.receiver == null) {
+        receiver = localsHandler.readThis();
+      } else {
+        visit(send.receiver);
+        receiver = pop();
+      }
+      SourceString getterName = send.selector.asIdentifier().source;
+      Element staticInterceptor = null;
+      if (methodInterceptionEnabled) {
+        staticInterceptor = interceptors.getStaticGetInterceptor(getterName);
+      }
+      if (staticInterceptor != null) {
+        HStatic target = new HStatic(staticInterceptor);
+        add(target);
+        List<HInstruction> inputs = <HInstruction>[target, receiver];
+        push(new HInvokeInterceptor(selector, getterName, true, inputs));
+      } else {
+        push(new HInvokeDynamicGetter(selector, null, getterName, receiver));
+      }
+    } else if (Elements.isStaticOrTopLevelFunction(element)) {
+      push(new HStatic(element));
+      compiler.registerGetOfStaticFunction(element);
+    } else {
+      stack.add(localsHandler.readLocal(element));
+    }
+  }
+
+  void generateSetter(SendSet send, Element element, HInstruction value) {
+    Selector selector = elements.getSelector(send);
+    if (Elements.isStaticOrTopLevelField(element)) {
+      if (element.kind == ElementKind.SETTER) {
+        HStatic target = new HStatic(element);
+        add(target);
+        add(new HInvokeStatic(selector, <HInstruction>[target, value]));
+      } else {
+        add(new HStaticStore(element, value));
+      }
+      stack.add(value);
+    } else if (element === null || Elements.isInstanceField(element)) {
+      SourceString dartSetterName = send.selector.asIdentifier().source;
+      HInstruction receiver;
+      if (send.receiver == null) {
+        receiver = localsHandler.readThis();
+      } else {
+        visit(send.receiver);
+        receiver = pop();
+      }
+      Element staticInterceptor = null;
+      if (methodInterceptionEnabled) {
+        staticInterceptor =
+          interceptors.getStaticSetInterceptor(dartSetterName);
+      }
+      if (staticInterceptor != null) {
+        HStatic target = new HStatic(staticInterceptor);
+        add(target);
+        List<HInstruction> inputs = <HInstruction>[target, receiver, value];
+        add(new HInvokeInterceptor(selector, dartSetterName, false, inputs));
+      } else {
+        add(new HInvokeDynamicSetter(selector, null, dartSetterName,
+                                     receiver, value));
+      }
+      stack.add(value);
+    } else {
+      localsHandler.updateLocal(element, value);
+      stack.add(value);
+    }
+  }
+
+  visitOperatorSend(node) {
+    assert(node.selector is Operator);
+    Operator op = node.selector;
+    if (const SourceString("[]") == op.source) {
+      HStatic target = new HStatic(interceptors.getIndexInterceptor());
+      add(target);
+      visit(node.receiver);
+      HInstruction receiver = pop();
+      visit(node.argumentsNode);
+      HInstruction index = pop();
+      push(new HIndex(target, receiver, index));
+    } else if (const SourceString("&&") == op.source ||
+               const SourceString("||") == op.source) {
+      visitLogicalAndOr(node, op);
+    } else if (const SourceString("!") == op.source) {
+      visitLogicalNot(node);
+    } else if (node.argumentsNode is Prefix) {
+      visitUnary(node, op);
+    } else if (const SourceString("is") == op.source) {
+      visit(node.receiver);
+      HInstruction expression = pop();
+      Node argument = node.arguments.head;
+      TypeAnnotation type = argument.asTypeAnnotation();
+      bool isNot = false;
+      // TODO(ngeoffray): Duplicating pattern in resolver. We should
+      // add a new kind of node.
+      if (type == null) {
+        type = argument.asSend().receiver;
+        isNot = true;
+      }
+      HInstruction instruction = new HIs(elements[type], expression);
+      if (isNot) {
+        add(instruction);
+        instruction = new HNot(instruction);
+      }
+      push(instruction);
+    } else {
+      visit(node.receiver);
+      visit(node.argumentsNode);
+      var right = pop();
+      var left = pop();
+      visitBinary(left, op, right);
+    }
+  }
+
+  void addDynamicSendArgumentsToList(Send node, List<HInstruction> list) {
+    Selector selector = elements.getSelector(node);
+    if (selector.namedArgumentCount == 0) {
+      addGenericSendArgumentsToList(node.arguments, list);
+    } else {
+      // Visit positional arguments and add them to the list.
+      Link<Node> arguments = node.arguments;
+      int positionalArgumentCount = selector.positionalArgumentCount;
+      for (int i = 0;
+           i < positionalArgumentCount;
+           arguments = arguments.tail, i++) {
+        visit(arguments.head);
+        list.add(pop());
+      }
+
+      // Visit named arguments and add them into a temporary map.
+      Map<SourceString, HInstruction> instructions =
+          new Map<SourceString, HInstruction>();
+      List<SourceString> namedArguments = selector.namedArguments;
+      int nameIndex = 0;
+      for (; !arguments.isEmpty(); arguments = arguments.tail) {
+        visit(arguments.head);
+        instructions[namedArguments[nameIndex++]] = pop();
+      }
+
+      // Iterate through the named arguments to add them to the list
+      // of instructions, in an order that can be shared with
+      // selectors with the same named arguments.
+      List<SourceString> orderedNames = selector.getOrderedNamedArguments();
+      for (SourceString name in orderedNames) {
+        list.add(instructions[name]);
+      }
+    }
+  }
+
+  void addStaticSendArgumentsToList(Send node,
+                                    FunctionElement element,
+                                    List<HInstruction> list) {
+    HInstruction compileArgument(Node argument) {
+      visit(argument);
+      return pop();
+    }
+
+    HInstruction compileConstant(Element constantElement) {
+      Constant constant = compiler.compileVariable(constantElement);
+      return graph.addConstant(constant);
+    }
+
+    Selector selector = elements.getSelector(node);
+    FunctionParameters parameters = element.computeParameters(compiler);
+    bool succeeded = selector.addSendArgumentsToList(node, list, parameters,
+                                                     compileArgument,
+                                                     compileConstant);
+   if (!succeeded) {
+      // TODO(ngeoffray): Match the VM behavior and throw an
+      // exception at runtime.
+      compiler.cancel('Unimplemented non-matching static call', node: node);
+    }
+  }
+
+  void addGenericSendArgumentsToList(Link<Node> link, List<HInstruction> list) {
+    for (; !link.isEmpty(); link = link.tail) {
+      visit(link.head);
+      list.add(pop());
+    }
+  }
+
+  visitDynamicSend(Send node) {
+    Selector selector = elements.getSelector(node);
+    var inputs = <HInstruction>[];
+
+    SourceString dartMethodName;
+    bool isNotEquals = false;
+    if (node.isIndex && !node.arguments.tail.isEmpty()) {
+      dartMethodName = Elements.constructOperatorName(
+          const SourceString('operator'),
+          const SourceString('[]='));
+    } else if (node.selector.asOperator() != null) {
+      SourceString name = node.selector.asIdentifier().source;
+      isNotEquals = name.stringValue === '!=';
+      dartMethodName = Elements.constructOperatorName(
+          const SourceString('operator'),
+          name,
+          node.argumentsNode is Prefix);
+    } else {
+      dartMethodName = node.selector.asIdentifier().source;
+    }
+
+    Element interceptor = null;
+    if (methodInterceptionEnabled && node.receiver !== null) {
+      interceptor = interceptors.getStaticInterceptor(dartMethodName,
+                                                      node.argumentCount());
+    }
+    if (interceptor != null) {
+      HStatic target = new HStatic(interceptor);
+      add(target);
+      inputs.add(target);
+      visit(node.receiver);
+      inputs.add(pop());
+      addGenericSendArgumentsToList(node.arguments, inputs);
+      push(new HInvokeInterceptor(selector, dartMethodName, false, inputs));
+      return;
+    }
+
+    if (node.receiver === null) {
+      inputs.add(localsHandler.readThis());
+    } else {
+      visit(node.receiver);
+      inputs.add(pop());
+    }
+
+    addDynamicSendArgumentsToList(node, inputs);
+
+    // The first entry in the inputs list is the receiver.
+    push(new HInvokeDynamicMethod(selector, dartMethodName, inputs));
+
+    if (isNotEquals) {
+      HNot not = new HNot(popBoolified());
+      push(not);
+    }
+  }
+
+  visitClosureSend(Send node) {
+    Selector selector = elements.getSelector(node);
+    assert(node.receiver === null);
+    Element element = elements[node];
+    HInstruction closureTarget;
+    if (element === null) {
+      visit(node.selector);
+      closureTarget = pop();
+    } else {
+      assert(Elements.isLocal(element));
+      closureTarget = localsHandler.readLocal(element);
+    }
+    var inputs = <HInstruction>[];
+    inputs.add(closureTarget);
+    addDynamicSendArgumentsToList(node, inputs);
+    push(new HInvokeClosure(selector, inputs));
+  }
+
+  void handleForeignJs(Send node) {
+    Link<Node> link = node.arguments;
+    // If the invoke is on foreign code, don't visit the first
+    // argument, which is the type, and the second argument,
+    // which is the foreign code.
+    if (link.isEmpty() || link.isEmpty()) {
+      compiler.cancel('At least two arguments expected', node: node.arguments);
+    }
+    link = link.tail.tail;
+    List<HInstruction> inputs = <HInstruction>[];
+    addGenericSendArgumentsToList(link, inputs);
+    Node type = node.arguments.head;
+    Node literal = node.arguments.tail.head;
+    if (literal is !StringNode || literal.dynamic.isInterpolation) {
+      compiler.cancel('JS code must be a string literal', node: literal);
+    }
+    if (type is !LiteralString) {
+      compiler.cancel(
+          'The type of a JS expression must be a string literal', node: type);
+    }
+    push(new HForeign(
+        literal.dynamic.dartString, type.dynamic.dartString, inputs));
+  }
+
+  void handleForeignUnintercepted(Send node) {
+    Link<Node> link = node.arguments;
+    if (!link.tail.isEmpty()) {
+      compiler.cancel(
+          'More than one expression in UNINTERCEPTED()', node: node);
+    }
+    Expression expression = link.head;
+    disableMethodInterception();
+    visit(expression);
+    enableMethodInterception();
+  }
+
+  void handleForeignJsHasEquals(Send node) {
+    List<HInstruction> inputs = <HInstruction>[];
+    if (!node.arguments.tail.isEmpty()) {
+      compiler.cancel(
+          'More than one expression in JS_HAS_EQUALS()', node: node);
+    }
+    addGenericSendArgumentsToList(node.arguments, inputs);
+    String name = compiler.namer.instanceMethodName(
+        currentLibrary, Namer.OPERATOR_EQUALS, 1);
+    push(new HForeign(new DartString.literal('!!#.$name'),
+                      const LiteralDartString('bool'),
+                      inputs));
+  }
+
+  void handleForeignJsCurrentIsolate(Send node) {
+    if (!node.arguments.isEmpty()) {
+      compiler.cancel(
+          'Too many arguments to JS_CURRENT_ISOLATE', node: node);
+    }
+
+    if (!compiler.hasIsolateSupport()) {
+      // If the isolate library is not used, we just generate code
+      // to fetch the Leg's current isolate.
+      String name = compiler.namer.CURRENT_ISOLATE;
+      push(new HForeign(new DartString.literal(name),
+                        const LiteralDartString('var'),
+                        <HInstruction>[]));
+    } else {
+      // Call a helper method from the isolate library. The isolate
+      // library uses its own isolate structure, that encapsulates
+      // Leg's isolate.
+      Element element = compiler.isolateLibrary.find(
+          const SourceString('_currentIsolate'));
+      if (element === null) {
+        compiler.cancel(
+            'Isolate library and compiler mismatch', node: node);
+      }
+      HStatic target = new HStatic(element);
+      add(target);
+      push(new HInvokeStatic(Selector.INVOCATION_0,
+                             <HInstruction>[target]));
+    }
+  }
+
+  void handleForeignJsCallInIsolate(Send node) {
+    Link<Node> link = node.arguments;
+    if (!compiler.hasIsolateSupport()) {
+      // If the isolate library is not used, we just invoke the
+      // closure.
+      visit(link.tail.head);
+      push(new HInvokeClosure(Selector.INVOCATION_0,
+                              <HInstruction>[pop()]));
+    } else {
+      // Call a helper method from the isolate library.
+      Element element = compiler.isolateLibrary.find(
+          const SourceString('_callInIsolate'));
+      if (element === null) {
+        compiler.cancel(
+            'Isolate library and compiler mismatch', node: node);
+      }
+      HStatic target = new HStatic(element);
+      add(target);
+      List<HInstruction> inputs = <HInstruction>[target];
+      addGenericSendArgumentsToList(link, inputs);
+      push(new HInvokeStatic(Selector.INVOCATION_0, inputs));
+    }
+  }
+
+  void handleForeignDartClosureToJs(Send node) {
+    if (node.arguments.isEmpty() || !node.arguments.tail.isEmpty()) {
+      compiler.cancel('Exactly one argument required', node: node.arguments);
+    }
+    Node closure = node.arguments.head;
+    Element element = elements[closure];
+    if (!Elements.isStaticOrTopLevelFunction(element)) {
+      compiler.cancel(
+          'JS_TO_CLOSURE requires a static or top-level method',
+          node: closure);
+    }
+    FunctionElement function = element;
+    FunctionParameters parameters = element.computeParameters(compiler);
+    if (parameters.optionalParameterCount !== 0) {
+      compiler.cancel(
+          'JS_TO_CLOSURE does not handle closure with optional parameters',
+          node: closure);
+    }
+    visit(closure);
+    List<HInstruction> inputs = <HInstruction>[pop()];
+    String invocationName = compiler.namer.closureInvocationName(
+        new Selector(SelectorKind.INVOCATION,
+                     parameters.requiredParameterCount));
+    push(new HForeign(new DartString.literal('#.$invocationName'),
+                      const LiteralDartString('var'),
+                      inputs));
+  }
+
+  handleForeignSend(Send node) {
+    Element element = elements[node];
+    if (element.name == const SourceString('JS')) {
+      handleForeignJs(node);
+    } else if (element.name == const SourceString('UNINTERCEPTED')) {
+      handleForeignUnintercepted(node);
+    } else if (element.name == const SourceString('JS_HAS_EQUALS')) {
+      handleForeignJsHasEquals(node);
+    } else if (element.name == const SourceString('JS_CURRENT_ISOLATE')) {
+      handleForeignJsCurrentIsolate(node);
+    } else if (element.name == const SourceString('JS_CALL_IN_ISOLATE')) {
+      handleForeignJsCallInIsolate(node);
+    } else if (element.name == const SourceString('DART_CLOSURE_TO_JS')) {
+      handleForeignDartClosureToJs(node);
+    } else if (element.name == const SourceString('native')) {
+      native.handleSsaNative(this, node);
+    } else {
+      throw "Unknown foreign: ${node.selector}";
+    }
+  }
+
+  visitSuperSend(Send node) {
+    Selector selector = elements.getSelector(node);
+    Element element = elements[node];
+    if (element === null) {
+      ClassElement cls = work.element.getEnclosingClass();
+      element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD);
+      HStatic target = new HStatic(element);
+      add(target);
+      HInstruction self = localsHandler.readThis();
+      Identifier identifier = node.selector.asIdentifier();
+      String name = identifier.source.slowToString();
+      // TODO(ahe): Add the arguments to this list.
+      push(new HLiteralList([], true));
+      var inputs = <HInstruction>[
+          target,
+          self,
+          graph.addConstantString(new DartString.literal(name)),
+          pop()];
+      push(new HInvokeSuper(const Selector(SelectorKind.INVOCATION, 2),
+                            inputs));
+      return;
+    }
+    HInstruction target = new HStatic(element);
+    HInstruction context = localsHandler.readThis();
+    add(target);
+    var inputs = <HInstruction>[target, context];
+    if (element.kind == ElementKind.FUNCTION ||
+        element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+      addStaticSendArgumentsToList(node, element, inputs);
+      push(new HInvokeSuper(selector, inputs));
+    } else {
+      target = new HInvokeSuper(Selector.GETTER, inputs);
+      add(target);
+      inputs = <HInstruction>[target];
+      addDynamicSendArgumentsToList(node, inputs);
+      push(new HInvokeClosure(selector, inputs));
+    }
+  }
+
+  visitStaticSend(Send node) {
+    Selector selector = elements.getSelector(node);
+    Element element = elements[node];
+    if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) {
+      compiler.resolver.resolveMethodElement(element);
+      FunctionElement functionElement = element;
+      element = functionElement.defaultImplementation;
+    }
+    HInstruction target = new HStatic(element);
+    add(target);
+    var inputs = <HInstruction>[];
+    inputs.add(target);
+    if (element.kind == ElementKind.FUNCTION ||
+        element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+      addStaticSendArgumentsToList(node, element, inputs);
+      push(new HInvokeStatic(selector, inputs));
+    } else {
+      if (element.kind == ElementKind.GETTER) {
+        target = new HInvokeStatic(Selector.GETTER, inputs);
+        add(target);
+        inputs = <HInstruction>[target];
+      }
+      addDynamicSendArgumentsToList(node, inputs);
+      push(new HInvokeClosure(selector, inputs));
+    }
+  }
+
+  visitSend(Send node) {
+    if (node.isSuperCall) {
+      if (node.isPropertyAccess) {
+        compiler.unimplemented('super property read', node: node);
+      }
+      visitSuperSend(node);
+    } else if (node.selector is Operator && methodInterceptionEnabled) {
+      visitOperatorSend(node);
+    } else if (node.isPropertyAccess) {
+      generateGetter(node, elements[node]);
+    } else if (Elements.isClosureSend(node, elements)) {
+      visitClosureSend(node);
+    } else {
+      Element element = elements[node];
+      if (element === null) {
+        // Example: f() with 'f' unbound.
+        // This can only happen inside an instance method.
+        visitDynamicSend(node);
+      } else if (element.kind == ElementKind.CLASS) {
+        compiler.internalError("Cannot generate code for send", node: node);
+      } else if (element.isInstanceMember()) {
+        // Example: f() with 'f' bound to instance method.
+        visitDynamicSend(node);
+      } else if (element.kind === ElementKind.FOREIGN) {
+        handleForeignSend(node);
+      } else if (!element.isInstanceMember()) {
+        // Example: A.f() or f() with 'f' bound to a static function.
+        // Also includes new A() or new A.named() which is treated like a
+        // static call to a factory.
+        visitStaticSend(node);
+      } else {
+        compiler.internalError("Cannot generate code for send", node: node);
+      }
+    }
+  }
+
+  visitNewExpression(NewExpression node) {
+    if (node.isConst()) {
+      ConstantHandler handler = compiler.constantHandler;
+      Constant constant = handler.compileNodeWithDefinitions(node, elements);
+      stack.add(graph.addConstant(constant));
+    } else {
+      visitSend(node.send);
+    }
+  }
+
+  visitSendSet(SendSet node) {
+    Operator op = node.assignmentOperator;
+    if (node.isSuperCall) {
+      compiler.unimplemented('super property store', node: node);
+    } else if (node.isIndex) {
+      if (!methodInterceptionEnabled) {
+        assert(op.source.stringValue === '=');
+        visitDynamicSend(node);
+      } else {
+        HStatic target = new HStatic(
+            interceptors.getIndexAssignmentInterceptor());
+        add(target);
+        visit(node.receiver);
+        HInstruction receiver = pop();
+        visit(node.argumentsNode);
+        if (const SourceString("=") == op.source) {
+          HInstruction value = pop();
+          HInstruction index = pop();
+          add(new HIndexAssign(target, receiver, index, value));
+          stack.add(value);
+        } else {
+          HInstruction value;
+          HInstruction index;
+          bool isCompoundAssignment = op.source.stringValue.endsWith('=');
+          // Compound assignments are considered as being prefix.
+          bool isPrefix = !node.isPostfix;
+          Element getter = elements[node.selector];
+          if (isCompoundAssignment) {
+            value = pop();
+            index = pop();
+          } else {
+            index = pop();
+            value = graph.addConstantInt(1);
+          }
+          HStatic indexMethod = new HStatic(interceptors.getIndexInterceptor());
+          add(indexMethod);
+          HInstruction left = new HIndex(indexMethod, receiver, index);
+          add(left);
+          Element opElement = elements[op];
+          visitBinary(left, op, value);
+          value = pop();
+          HInstruction assign = new HIndexAssign(
+              target, receiver, index, value);
+          add(assign);
+          if (isPrefix) {
+            stack.add(value);
+          } else {
+            stack.add(left);
+          }
+        }
+      }
+    } else if (const SourceString("=") == op.source) {
+      Element element = elements[node];
+      Link<Node> link = node.arguments;
+      assert(!link.isEmpty() && link.tail.isEmpty());
+      visit(link.head);
+      HInstruction value = pop();
+      generateSetter(node, element, value);
+    } else if (op.source.stringValue === "is") {
+      compiler.internalError("is-operator as SendSet", node: op);
+    } else {
+      assert(const SourceString("++") == op.source ||
+             const SourceString("--") == op.source ||
+             node.assignmentOperator.source.stringValue.endsWith("="));
+      Element element = elements[node];
+      bool isCompoundAssignment = !node.arguments.isEmpty();
+      bool isPrefix = !node.isPostfix;  // Compound assignments are prefix.
+      generateGetter(node, elements[node.selector]);
+      HInstruction left = pop();
+      HInstruction right;
+      if (isCompoundAssignment) {
+        visit(node.argumentsNode);
+        right = pop();
+      } else {
+        right = graph.addConstantInt(1);
+      }
+      visitBinary(left, op, right);
+      HInstruction operation = pop();
+      assert(operation !== null);
+      generateSetter(node, element, operation);
+      if (!isPrefix) {
+        pop();
+        stack.add(left);
+      }
+    }
+  }
+
+  void visitLiteralInt(LiteralInt node) {
+    stack.add(graph.addConstantInt(node.value));
+  }
+
+  void visitLiteralDouble(LiteralDouble node) {
+    stack.add(graph.addConstantDouble(node.value));
+  }
+
+  void visitLiteralBool(LiteralBool node) {
+    stack.add(graph.addConstantBool(node.value));
+  }
+
+  void visitLiteralString(LiteralString node) {
+    stack.add(graph.addConstantString(node.dartString));
+  }
+
+  void visitStringJuxtaposition(StringJuxtaposition node) {
+    if (!node.isInterpolation) {
+      // This is a simple string with no interpolations.
+      stack.add(graph.addConstantString(node.dartString));
+      return;
+    }
+    int offset = node.getBeginToken().charOffset;
+    StringBuilderVisitor stringBuilder =
+        new StringBuilderVisitor(this, offset);
+    stringBuilder.visit(node);
+    stack.add(stringBuilder.result());
+  }
+
+  void visitLiteralNull(LiteralNull node) {
+    stack.add(graph.addConstantNull());
+  }
+
+  visitNodeList(NodeList node) {
+    for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
+      if (isAborted()) {
+        compiler.reportWarning(link.head, 'dead code');
+      } else {
+        visit(link.head);
+      }
+    }
+  }
+
+  void visitParenthesizedExpression(ParenthesizedExpression node) {
+    visit(node.expression);
+  }
+
+  visitOperator(Operator node) {
+    // Operators are intercepted in their surrounding Send nodes.
+    unreachable();
+  }
+
+  visitReturn(Return node) {
+    HInstruction value;
+    if (node.expression === null) {
+      value = graph.addConstantNull();
+    } else {
+      visit(node.expression);
+      value = pop();
+    }
+    close(new HReturn(value)).addSuccessor(graph.exit);
+  }
+
+  visitThrow(Throw node) {
+    if (node.expression === null) {
+      HInstruction exception = rethrowableException;
+      if (exception === null) {
+        exception = graph.addConstantNull();
+        compiler.reportError(node,
+                             'throw without expression outside catch block');
+      }
+      close(new HThrow(exception, isRethrow: true));
+    } else {
+      visit(node.expression);
+      close(new HThrow(pop()));
+    }
+  }
+
+  visitTypeAnnotation(TypeAnnotation node) {
+    compiler.internalError('visiting type annotation in SSA builder',
+                           node: node);
+  }
+
+  visitVariableDefinitions(VariableDefinitions node) {
+    for (Link<Node> link = node.definitions.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      Node definition = link.head;
+      if (definition is Identifier) {
+        HInstruction initialValue = graph.addConstantNull();
+        localsHandler.updateLocal(elements[definition], initialValue);
+      } else {
+        assert(definition is SendSet);
+        visitSendSet(definition);
+        pop();  // Discard value.
+      }
+    }
+  }
+
+  visitLiteralList(LiteralList node) {
+    List<HInstruction> inputs = <HInstruction>[];
+    for (Link<Node> link = node.elements.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      visit(link.head);
+      inputs.add(pop());
+    }
+    push(new HLiteralList(inputs, node.isConst()));
+  }
+
+  visitConditional(Conditional node) {
+    visit(node.condition);
+    HIf condition = new HIf(popBoolified(), true);
+    HBasicBlock conditionBlock = close(condition);
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+
+    HBasicBlock thenBlock = addNewBlock();
+    conditionBlock.addSuccessor(thenBlock);
+    open(thenBlock);
+    visit(node.thenExpression);
+    HInstruction thenInstruction = pop();
+    SubGraph thenGraph = new SubGraph(thenBlock, current);
+    thenBlock = close(new HGoto());
+    LocalsHandler thenLocals = localsHandler;
+    localsHandler = savedLocals;
+
+    HBasicBlock elseBlock = addNewBlock();
+    conditionBlock.addSuccessor(elseBlock);
+    open(elseBlock);
+    visit(node.elseExpression);
+    HInstruction elseInstruction = pop();
+    SubGraph elseGraph = new SubGraph(elseBlock, current);
+    elseBlock = close(new HGoto());
+
+    HBasicBlock joinBlock = addNewBlock();
+    thenBlock.addSuccessor(joinBlock);
+    elseBlock.addSuccessor(joinBlock);
+    condition.blockInformation =
+        new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock);
+    open(joinBlock);
+
+    localsHandler.mergeWith(thenLocals, joinBlock);
+    HPhi phi = new HPhi.manyInputs(null,
+        <HInstruction>[thenInstruction, elseInstruction]);
+    joinBlock.addPhi(phi);
+    stack.add(phi);
+  }
+
+  visitStringInterpolation(StringInterpolation node) {
+    int offset = node.getBeginToken().charOffset;
+    StringBuilderVisitor stringBuilder =
+        new StringBuilderVisitor(this, offset);
+    stringBuilder.visit(node);
+    stack.add(stringBuilder.result());
+  }
+
+  visitStringInterpolationPart(StringInterpolationPart node) {
+    // The parts are iterated in visitStringInterpolation.
+    unreachable();
+  }
+
+  visitEmptyStatement(EmptyStatement node) {
+    // Do nothing, empty statement.
+  }
+
+  visitModifiers(Modifiers node) {
+    compiler.unimplemented('SsaBuilder.visitModifiers', node: node);
+  }
+
+  visitBreakStatement(BreakStatement node) {
+    work.allowSpeculativeOptimization = false;
+    assert(!isAborted());
+    TargetElement target = elements[node];
+    assert(target !== null);
+    JumpHandler handler = jumpTargets[target];
+    assert(handler !== null);
+    if (node.target === null) {
+      handler.generateBreak();
+    } else {
+      LabelElement label = elements[node.target];
+      handler.generateBreak(label);
+    }
+  }
+
+  visitContinueStatement(ContinueStatement node) {
+    work.allowSpeculativeOptimization = false;
+    TargetElement target = elements[node];
+    assert(target !== null);
+    JumpHandler handler = jumpTargets[target];
+    assert(handler !== null);
+    if (node.target === null) {
+      handler.generateContinue();
+    } else {
+      LabelElement label = elements[node.target];
+      handler.generateContinue(label);
+    }
+  }
+
+  /**
+   * Creates a [JumpHandler] for a statement. The node must be a jump
+   * target. If there are no breaks or continues targeting the statement,
+   * a special "null handler" is returned.
+   */
+  JumpHandler createJumpHandler(Statement node) {
+    TargetElement element = elements[node];
+    if (element === null || element.statement !== node) {
+      // No breaks or continues to this node.
+      return const NullJumpHandler();
+    }
+    return new JumpHandler(this, element);
+  }
+
+  visitForInStatement(ForInStatement node) {
+    // Generate a structure equivalent to:
+    //   Iterator<E> $iter = <iterable>.iterator()
+    //   while ($iter.hasNext()) {
+    //     E <declaredIdentifier> = $iter.next();
+    //     <body>
+    //   }
+
+    // All the generated calls are to zero-argument functions.
+    Selector selector = Selector.INVOCATION_0;
+    // The iterator is shared between initializer, condition and body.
+    HInstruction iterator;
+    void buildInitializer() {
+      SourceString iteratorName = const SourceString("iterator");
+      Element interceptor = interceptors.getStaticInterceptor(iteratorName, 0);
+      assert(interceptor != null);
+      HStatic target = new HStatic(interceptor);
+      add(target);
+      visit(node.expression);
+      List<HInstruction> inputs = <HInstruction>[target, pop()];
+      iterator = new HInvokeInterceptor(selector, iteratorName, false, inputs);
+      add(iterator);
+    }
+    HInstruction buildCondition() {
+      push(new HInvokeDynamicMethod(
+           selector, const SourceString('hasNext'), <HInstruction>[iterator]));
+      return popBoolified();
+    }
+    void buildBody() {
+      push(new HInvokeDynamicMethod(
+           selector, const SourceString('next'), <HInstruction>[iterator]));
+
+      Element variable;
+      if (node.declaredIdentifier.asSend() !== null) {
+        variable = elements[node.declaredIdentifier];
+      } else {
+        assert(node.declaredIdentifier.asVariableDefinitions() !== null);
+        VariableDefinitions variableDefinitions = node.declaredIdentifier;
+        variable = elements[variableDefinitions.definitions.nodes.head];
+      }
+      localsHandler.updateLocal(variable, pop());
+
+      visit(node.body);
+    }
+    handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
+  }
+
+  visitLabeledStatement(LabeledStatement node) {
+    Statement body = node.getBody();
+    if (body is Loop || body is SwitchStatement) {
+      // Loops and switches handle their own labels.
+      visit(body);
+      return;
+    }
+    // Non-loop statements can only be break targets, not continue targets.
+    TargetElement targetElement = elements[body];
+    if (targetElement === null || targetElement.statement !== body) {
+      // Labeled statements with no element on the body have no breaks.
+      // A different target statement only happens if the body is itself
+      // a break or continue for a different target. In that case, this
+      // label is also always unused.
+      visit(body);
+      return;
+    }
+    LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler);
+    assert(targetElement.isBreakTarget);
+    JumpHandler handler = new JumpHandler(this, targetElement);
+    // Introduce a new basic block.
+    HBasicBlock entryBlock = graph.addNewBlock();
+    goto(current, entryBlock);
+    open(entryBlock);
+    hackAroundPossiblyAbortingBody(node, () { visit(body); });
+    SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock);
+
+    HBasicBlock joinBlock = graph.addNewBlock();
+    List<LocalsHandler> breakLocals = <LocalsHandler>[];
+    handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
+      breakInstruction.block.addSuccessor(joinBlock);
+      breakLocals.add(locals);
+    });
+    bool hasBreak = breakLocals.length > 0;
+    if (!isAborted()) {
+      goto(current, joinBlock);
+      breakLocals.add(localsHandler);
+    }
+    open(joinBlock);
+    localsHandler = beforeLocals.mergeMultiple(breakLocals, joinBlock);
+
+    if (hasBreak) {
+      // There was at least one reachable break, so the label is needed.
+      HLabeledBlockInformation blockInfo =
+          new HLabeledBlockInformation(bodyGraph, joinBlock, handler.labels());
+      entryBlock.labeledBlockInformation = blockInfo;
+    }
+    handler.close();
+  }
+
+  visitLiteralMap(LiteralMap node) {
+    List<HInstruction> inputs = <HInstruction>[];
+    for (Link<Node> link = node.entries.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      visit(link.head);
+      inputs.addLast(pop());
+      inputs.addLast(pop());
+    }
+    HLiteralList keyValuePairs = new HLiteralList(inputs, node.isConst());
+    HStatic mapMaker = new HStatic(interceptors.getMapMaker());
+    add(keyValuePairs);
+    add(mapMaker);
+    inputs = <HInstruction>[mapMaker, keyValuePairs];
+    push(new HInvokeStatic(Selector.INVOCATION_1, inputs));
+  }
+
+  visitLiteralMapEntry(LiteralMapEntry node) {
+    visit(node.value);
+    visit(node.key);
+  }
+
+  visitNamedArgument(NamedArgument node) {
+    visit(node.expression);
+  }
+
+  visitSwitchStatement(SwitchStatement node) {
+    work.allowSpeculativeOptimization = false;
+    LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+    HBasicBlock startBlock = graph.addNewBlock();
+    goto(current, startBlock);
+    open(startBlock);
+    visit(node.expression);
+    HInstruction expression = pop();
+    if (node.cases.isEmpty()) {
+      return;
+    }
+    Link<Node> cases = node.cases.nodes;
+
+    JumpHandler jumpHandler = createJumpHandler(node);
+
+    buildSwitchCases(cases, expression);
+
+    HBasicBlock lastBlock = lastOpenedBlock;
+
+    // Create merge block for break targets.
+    HBasicBlock joinBlock = new HBasicBlock();
+    List<LocalsHandler> caseLocals = <LocalsHandler>[];
+    jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
+      instruction.block.addSuccessor(joinBlock);
+      caseLocals.add(locals);
+    });
+    if (!isAborted()) {
+      // The current flow is only aborted if the switch has a default that
+      // aborts (all previous cases must abort, and if there is no default,
+      // it's possible to miss all the cases).
+      caseLocals.add(localsHandler);
+      goto(current, joinBlock);
+    }
+    if (caseLocals.length != 0) {
+      graph.addBlock(joinBlock);
+      open(joinBlock);
+      if (caseLocals.length == 1) {
+        localsHandler = caseLocals[0];
+      } else {
+        localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock);
+      }
+    } else {
+      // The joinblock is not used.
+      joinBlock = null;
+    }
+    startBlock.labeledBlockInformation = new HLabeledBlockInformation.implicit(
+        new SubGraph(startBlock, lastBlock),
+        joinBlock,
+        elements[node]);
+    jumpHandler.close();
+  }
+
+
+  // Recursively build an if/else structure to match the cases.
+  buildSwitchCases(Link<Node> cases, HInstruction expression) {
+    SwitchCase node = cases.head;
+
+    // Called for the statements on all but the last case block.
+    // Ensures that a user expecting a fallthrough gets an error.
+    void visitStatementsAndAbort() {
+      visit(node.statements);
+      if (!isAborted()) {
+        compiler.reportWarning(node, 'Missing break at end of switch case');
+        Element element =
+            compiler.findHelper(const SourceString("getFallThroughError"));
+        push(new HStatic(element));
+        HInstruction error = new HInvokeStatic(
+             Selector.INVOCATION_0, <HInstruction>[pop()]);
+        add(error);
+        close(new HThrow(error));
+      }
+    }
+
+    Link<Node> expressions = node.expressions.nodes;
+    if (expressions.isEmpty()) {
+      // Default case with no expressions.
+      if (!node.isDefaultCase) {
+        compiler.internalError("Case with no expression and not default",
+                               node: node);
+      }
+      visit(node.statements);
+      // This must be the final case (otherwise "default" would be invalid),
+      // so we don't need to check for fallthrough.
+      return;
+    }
+
+    // Recursively build the test conditions. Leaves the result on the
+    // expression stack.
+    void buildTests(Link<Node> expressions) {
+      // Build comparison for one case expression.
+      void left() {
+        Element equalsHelper = interceptors.getEqualsInterceptor();
+        HInstruction target = new HStatic(equalsHelper);
+        add(target);
+        visit(expressions.head);
+        push(new HEquals(target, pop(), expression));
+      }
+
+      // If this is the last expression, just return it.
+      if (expressions.tail.isEmpty()) {
+        left();
+        return;
+      }
+
+      void right() {
+        buildTests(expressions.tail);
+      }
+      handleLogicalAndOr(left, right, isAnd: false);
+    }
+
+    buildTests(expressions);
+    HInstruction result = popBoolified();
+
+    if (node.isDefaultCase) {
+      // Don't actually use the condition result.
+      // This must be final case, so don't check for abort.
+      visit(node.statements);
+    } else {
+      stack.add(result);
+      if (cases.tail.isEmpty()) {
+        handleIf(() { visit(node.statements); }, null);
+      } else {
+        handleIf(() { visitStatementsAndAbort(); },
+                 () { buildSwitchCases(cases.tail, expression); });
+      }
+    }
+  }
+
+  visitSwitchCase(SwitchCase node) {
+    unreachable();
+  }
+
+  visitTryStatement(TryStatement node) {
+    work.allowSpeculativeOptimization = false;
+    HBasicBlock enterBlock = graph.addNewBlock();
+    close(new HGoto()).addSuccessor(enterBlock);
+    open(enterBlock);
+    HTry tryInstruction = new HTry();
+    List<HBasicBlock> blocks = <HBasicBlock>[];
+    blocks.add(close(tryInstruction));
+
+    HBasicBlock tryBody = graph.addNewBlock();
+    enterBlock.addSuccessor(tryBody);
+    open(tryBody);
+    visit(node.tryBlock);
+    if (!isAborted()) blocks.add(close(new HGoto()));
+
+    if (!node.catchBlocks.isEmpty()) {
+      HBasicBlock block = graph.addNewBlock();
+      enterBlock.addSuccessor(block);
+      open(block);
+      // Note that the name of this element is irrelevant.
+      Element element = new Element(
+          const SourceString('exception'), ElementKind.PARAMETER, work.element);
+      HParameterValue exception = new HParameterValue(element);
+      add(exception);
+      HInstruction oldRethrowableException = rethrowableException;
+      rethrowableException = exception;
+      push(new HStatic(interceptors.getExceptionUnwrapper()));
+      List<HInstruction> inputs = <HInstruction>[pop(), exception];
+      HInvokeStatic unwrappedException =
+          new HInvokeStatic(Selector.INVOCATION_1, inputs);
+      add(unwrappedException);
+      tryInstruction.exception = exception;
+
+      Link<Node> link = node.catchBlocks.nodes;
+
+      void pushCondition(CatchBlock catchBlock) {
+        VariableDefinitions declaration = catchBlock.formals.nodes.head;
+        HInstruction condition = null;
+        if (declaration.type == null) {
+          condition = graph.addConstantBool(true);
+          stack.add(condition);
+        } else {
+          Element typeElement = elements[declaration.type];
+          if (typeElement == null) {
+            compiler.cancel('Catch with unresolved type', node: catchBlock);
+          }
+          condition = new HIs(typeElement, unwrappedException, nullOk: true);
+          push(condition);
+        }
+      }
+
+      void visitThen() {
+        CatchBlock catchBlock = link.head;
+        link = link.tail;
+        localsHandler.updateLocal(elements[catchBlock.exception],
+                                  unwrappedException);
+        Node trace = catchBlock.trace;
+        if (trace != null) {
+          push(new HStatic(interceptors.getTraceFromException()));
+          HInstruction traceInstruction = new HInvokeStatic(
+              Selector.INVOCATION_1, <HInstruction>[pop(), exception]);
+          add(traceInstruction);
+          localsHandler.updateLocal(elements[trace], traceInstruction);
+        }
+        visit(catchBlock);
+      }
+
+      void visitElse() {
+        if (link.isEmpty()) {
+          close(new HThrow(exception, isRethrow: true));
+        } else {
+          CatchBlock newBlock = link.head;
+          pushCondition(newBlock);
+          handleIf(visitThen, visitElse);
+        }
+      }
+
+      CatchBlock firstBlock = link.head;
+      pushCondition(firstBlock);
+      handleIf(visitThen, visitElse);
+      if (!isAborted()) blocks.add(close(new HGoto()));
+      rethrowableException = oldRethrowableException;
+    }
+
+    if (node.finallyBlock != null) {
+      HBasicBlock finallyBlock = graph.addNewBlock();
+      enterBlock.addSuccessor(finallyBlock);
+      open(finallyBlock);
+      visit(node.finallyBlock);
+      if (!isAborted()) blocks.add(close(new HGoto()));
+      tryInstruction.finallyBlock = finallyBlock;
+    }
+
+    HBasicBlock exitBlock = graph.addNewBlock();
+
+    for (HBasicBlock block in blocks) {
+      block.addSuccessor(exitBlock);
+    }
+
+    open(exitBlock);
+  }
+
+  visitScriptTag(ScriptTag node) {
+    compiler.unimplemented('SsaBuilder.visitScriptTag', node: node);
+  }
+
+  visitCatchBlock(CatchBlock node) {
+    visit(node.block);
+  }
+
+  visitTypedef(Typedef node) {
+    compiler.unimplemented('SsaBuilder.visitTypedef', node: node);
+  }
+
+  visitTypeVariable(TypeVariable node) {
+    compiler.internalError('SsaBuilder.visitTypeVariable');
+  }
+
+  generateUnimplemented(String reason, [bool isExpression = false]) {
+    DartString string = new DartString.literal(reason);
+    HInstruction message = graph.addConstantString(string);
+
+    // Normally, we would call [close] here. However, then we hit
+    // another unimplemented feature: aborting loop body. Simply
+    // calling [add] does not work as it asserts that the instruction
+    // isn't a control flow instruction. So we inline parts of [add].
+    current.addAfter(current.last, new HThrow(message));
+    if (isExpression) {
+      stack.add(graph.addConstantNull());
+    }
+  }
+
+  /** HACK HACK HACK */
+  void hackAroundPossiblyAbortingBody(Node statement, void body()) {
+    stack.add(graph.addConstantBool(true));
+    buildBody() {
+      // TODO(lrn): Make sure to take continue into account.
+      body();
+      if (isAborted()) {
+        compiler.reportWarning(statement, "aborting loop body");
+      }
+    }
+    handleIf(buildBody, null);
+  }
+}
+
+/**
+ * Visitor that handles generation of string literals (LiteralString,
+ * StringInterpolation), and otherwise delegates to the given visitor for
+ * non-literal subexpressions.
+ * TODO(lrn): Consider whether to handle compile time constant int/boolean
+ * expressions as well.
+ */
+class StringBuilderVisitor extends AbstractVisitor {
+  final SsaBuilder builder;
+
+  /**
+   * Offset used for the synthetic operator token used by concat.
+   * Can probably be removed when we stop using String.operator+.
+   */
+  final int offset;
+
+  /**
+   * Used to collect concatenated string literals into a single literal
+   * instead of introducing unnecessary concatenations.
+   */
+  DartString literalAccumulator = const LiteralDartString("");
+
+  /**
+   * The string value generated so far (not including that which is still
+   * in [literalAccumulator]).
+   */
+  HInstruction prefix = null;
+
+  StringBuilderVisitor(this.builder, this.offset);
+
+  void visit(Node node) {
+    node.accept(this);
+  }
+
+  visitNode(Node node) {
+    compiler.internalError('unexpected node', node: node);
+  }
+
+  void visitExpression(Node node) {
+    flushLiterals();
+    node.accept(builder);
+    HInstruction asString = buildToString(node, builder.pop());
+    prefix = concat(prefix, asString);
+  }
+
+  void visitLiteralNull(LiteralNull node) {
+    addLiteral(const LiteralDartString("null"));
+  }
+
+  void visitLiteralInt(LiteralInt node) {
+    addLiteral(new DartString.literal(node.value.toString()));
+  }
+
+  void visitLiteralDouble(LiteralDouble node) {
+    addLiteral(new DartString.literal(node.value.toString()));
+  }
+
+  void visitLiteralBool(LiteralBool node) {
+    addLiteral(node.value ? const LiteralDartString("true")
+                          : const LiteralDartString("false"));
+  }
+
+  void visitStringInterpolation(StringInterpolation node) {
+    node.visitChildren(this);
+  }
+
+  void visitStringInterpolationPart(StringInterpolationPart node) {
+    visit(node.expression);
+    visit(node.string);
+  }
+
+  void visitLiteralString(LiteralString node) {
+    addLiteral(node.dartString);
+  }
+
+  void visitStringJuxtaposition(StringJuxtaposition node) {
+    node.visitChildren(this);
+  }
+
+  void visitNodeList(NodeList node) {
+     node.visitChildren(this);
+  }
+
+  /**
+   * Add another literal string to the literalAccumulator.
+   */
+  void addLiteral(DartString dartString) {
+    literalAccumulator = new DartString.concat(literalAccumulator, dartString);
+  }
+
+  /**
+   * Combine the strings in [literalAccumulator] into the prefix instruction.
+   * After this, the [literalAccumulator] is empty and [prefix] is non-null.
+   */
+  void flushLiterals() {
+    if (literalAccumulator.isEmpty()) {
+      if (prefix === null) {
+        prefix = builder.graph.addConstantString(literalAccumulator);
+      }
+      return;
+    }
+    HInstruction string = builder.graph.addConstantString(literalAccumulator);
+    literalAccumulator = new DartString.empty();
+    if (prefix !== null) {
+      prefix = concat(prefix, string);
+    } else {
+      prefix = string;
+    }
+  }
+
+  HInstruction concat(HInstruction left, HInstruction right) {
+    SourceString dartMethodName = const SourceString("concat");
+    if (!builder.methodInterceptionEnabled) {
+      builder.compiler.internalError(
+        "Using string interpolations in non-intercepted code.",
+        instruction: right);
+    }
+    Element interceptor =
+        builder.interceptors.getStaticInterceptor(dartMethodName, 1);
+    if (interceptor === null) {
+      builder.compiler.internalError(
+          "concat not intercepted.", instruction: left);
+    }
+    HStatic target = new HStatic(interceptor);
+    builder.add(target);
+    builder.push(new HInvokeInterceptor(Selector.INVOCATION_1,
+                                        dartMethodName,
+                                        false,
+                                        <HInstruction>[target, left, right]));
+    return builder.pop();
+  }
+
+  HInstruction buildToString(Node node, HInstruction input) {
+    SourceString dartMethodName = const SourceString("toString");
+    if (!builder.methodInterceptionEnabled) {
+      builder.compiler.internalError(
+        "Using string interpolations in non-intercepted code.", node: node);
+    }
+    Element interceptor =
+        builder.interceptors.getStaticInterceptor(dartMethodName, 0);
+    if (interceptor === null) {
+      builder.compiler.internalError(
+        "toString not intercepted.", node: node);
+    }
+    HStatic target = new HStatic(interceptor);
+    builder.add(target);
+    builder.push(new HInvokeInterceptor(Selector.INVOCATION_0,
+                                        dartMethodName,
+                                        false,
+                                        <HInstruction>[target, input]));
+    return builder.pop();
+  }
+
+  HInstruction result() {
+    flushLiterals();
+    return prefix;
+  }
+}
diff --git a/lib/compiler/implementation/ssa/closure.dart b/lib/compiler/implementation/ssa/closure.dart
new file mode 100644
index 0000000..ab2d6a4
--- /dev/null
+++ b/lib/compiler/implementation/ssa/closure.dart
@@ -0,0 +1,417 @@
+// Copyright (c) 2012, 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.
+
+class ClosureFieldElement extends Element {
+  ClosureFieldElement(SourceString name, ClassElement enclosing)
+      : super(name, ElementKind.FIELD, enclosing);
+
+  bool isInstanceMember() => true;
+  bool isAssignable() => false;
+
+  String toString() => "ClosureFieldElement($name)";
+}
+
+class ClosureClassElement extends ClassElement {
+  ClosureClassElement(Compiler compiler, Element enclosingElement)
+      : super(compiler.closureClass.name, enclosingElement) {
+    isResolved = true;
+    compiler.closureClass.ensureResolved(compiler);
+    supertype = compiler.closureClass.computeType(compiler);
+  }
+}
+
+// The box-element for a scope, and the captured variables that need to be
+// stored in the box.
+class ClosureScope {
+  Element boxElement;
+  Map<Element, Element> capturedVariableMapping;
+  // If the scope is attached to a [For] contains the variables that are
+  // declared in the initializer of the [For] and that need to be boxed.
+  // Otherwise contains the empty List.
+  List<Element> boxedLoopVariables;
+
+  ClosureScope(this.boxElement, this.capturedVariableMapping)
+      : boxedLoopVariables = const <Element>[];
+
+  bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
+}
+
+class ClosureData {
+  // The closure's element before any translation. Will be null for methods.
+  final FunctionElement closureElement;
+  // The closureClassElement will be null for methods that are not local
+  // closures.
+  final ClassElement closureClassElement;
+  // The callElement will be null for methods that are not local closures.
+  final FunctionElement callElement;
+  // The [thisElement] makes handling 'this' easier by treating it like any
+  // other argument. It is only set for instance-members.
+  final Element thisElement;
+
+  // Maps free locals, arguments and function elements to their captured
+  // copies.
+  final Map<Element, Element> freeVariableMapping;
+  // Maps closure-fields to their captured elements. This is somehow the inverse
+  // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does
+  // not deal with boxes, here we map instance-fields (which might represent
+  // boxes) to their boxElement.
+  final Map<Element, Element> capturedFieldMapping;
+
+  // Maps scopes ([Loop] and [FunctionExpression] nodes) to their
+  // [ClosureScope] which contains their box and the
+  // captured variables that are stored in the box.
+  // This map will be empty if the method/closure of this [ClosureData] does not
+  // contain any nested closure.
+  final Map<Node, ClosureScope> capturingScopes;
+
+  final Set<Element> usedVariablesInTry;
+
+  ClosureData(this.closureElement,
+              this.closureClassElement,
+              this.callElement,
+              this.thisElement)
+      : this.freeVariableMapping = new Map<Element, Element>(),
+        this.capturedFieldMapping = new Map<Element, Element>(),
+        this.capturingScopes = new Map<Node, ClosureScope>(),
+        this.usedVariablesInTry = new Set<Element>();
+
+  bool isClosure() => closureElement !== null;
+}
+
+class ClosureTranslator extends AbstractVisitor {
+  final Compiler compiler;
+  final TreeElements elements;
+  int boxCounter = 0;
+  bool inTryCatchOrFinally = false;
+  final Map<Node, ClosureData> closureDataCache;
+
+  // Map of captured variables. Initially they will map to themselves. If
+  // a variable needs to be boxed then the scope declaring the variable
+  // will update this mapping.
+  Map<Element, Element> capturedVariableMapping;
+  // List of encountered closures.
+  List<FunctionExpression> closures;
+
+  // The variables that have been declared in the current scope.
+  List<Element> scopeVariables;
+
+  FunctionElement currentFunctionElement;
+  // The closureData of the currentFunctionElement.
+  ClosureData closureData;
+
+  bool insideClosure = false;
+
+  ClosureTranslator(Compiler compiler, this.elements)
+      : capturedVariableMapping = new Map<Element, Element>(),
+        closures = <FunctionExpression>[],
+        this.compiler = compiler,
+        this.closureDataCache = compiler.builder.closureDataCache;
+
+  ClosureData translate(Node node) {
+    // Closures have already been analyzed when visiting the surrounding
+    // method/function. This also shortcuts for bailout functions.
+    ClosureData cached = closureDataCache[node];
+    if (cached !== null) return cached;
+
+    visit(node);
+    // When variables need to be boxed their [capturedVariableMapping] is
+    // updated, but we delay updating the similar freeVariableMapping in the
+    // closure datas that capture these variables.
+    // The closures don't have their fields (in the closure class) set, either.
+    updateClosures();
+
+    return closureDataCache[node];
+  }
+
+  // This function runs through all of the existing closures and updates their
+  // free variables to the boxed value. It also adds the field-elements to the
+  // class representing the closure. At the same time it fills the
+  // [capturedFieldMapping].
+  void updateClosures() {
+    for (FunctionExpression closure in closures) {
+      // The captured variables that need to be stored in a field of the closure
+      // class.
+      Set<Element> fieldCaptures = new Set<Element>();
+      ClosureData data = closureDataCache[closure];
+      Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
+      // We get a copy of the keys and iterate over it, to avoid modifications
+      // to the map while iterating over it.
+      freeVariableMapping.getKeys().forEach((Element fromElement) {
+        assert(fromElement == freeVariableMapping[fromElement]);
+        Element updatedElement = capturedVariableMapping[fromElement];
+        assert(updatedElement !== null);
+        if (fromElement == updatedElement) {
+          assert(freeVariableMapping[fromElement] == updatedElement);
+          assert(Elements.isLocal(updatedElement));
+          // The variable has not been boxed.
+          fieldCaptures.add(updatedElement);
+        } else {
+          // A boxed element.
+          freeVariableMapping[fromElement] = updatedElement;
+          Element boxElement = updatedElement.enclosingElement;
+          assert(boxElement.kind == ElementKind.VARIABLE);
+          fieldCaptures.add(boxElement);
+        }
+      });
+      ClassElement closureElement = data.closureClassElement;
+      assert(closureElement != null || fieldCaptures.isEmpty());
+      for (Element boxElement in fieldCaptures) {
+        Element fieldElement =
+            new ClosureFieldElement(boxElement.name, closureElement);
+        closureElement.backendMembers =
+            closureElement.backendMembers.prepend(fieldElement);
+        data.capturedFieldMapping[fieldElement] = boxElement;
+        freeVariableMapping[boxElement] = fieldElement;
+      }
+    }
+  }
+
+  void useLocal(Element element) {
+    // TODO(floitsch): replace this with a general solution.
+    Element functionElement = currentFunctionElement;
+    if (functionElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+      ConstructorBodyElement body = functionElement;
+      functionElement = body.constructor;
+    }
+    // If the element is not declared in the current function and the element
+    // is not the closure itself we need to mark the element as free variable.
+    if (element.enclosingElement != functionElement &&
+        element != functionElement) {
+      assert(closureData.freeVariableMapping[element] == null ||
+             closureData.freeVariableMapping[element] == element);
+      closureData.freeVariableMapping[element] = element;
+    } else if (inTryCatchOrFinally) {
+      // Don't mark the this-element. This would complicate things in the
+      // builder.
+      if (element != closureData.thisElement) {
+        // TODO(ngeoffray): only do this if the variable is mutated.
+        closureData.usedVariablesInTry.add(element);
+      }
+    }
+  }
+
+  void declareLocal(Element element) {
+    scopeVariables.add(element);
+  }
+
+  visit(Node node) => node.accept(this);
+
+  visitNode(Node node) => node.visitChildren(this);
+
+  visitVariableDefinitions(VariableDefinitions node) {
+    for (Link<Node> link = node.definitions.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      Node definition = link.head;
+      Element element = elements[definition];
+      assert(element !== null);
+      declareLocal(element);
+    }
+    // We still need to visit the right-hand sides of the init-assignments.
+    // Simply visit all children. We will visit the locals again and make them
+    // used, but that should not be a problem.
+    node.visitChildren(this);
+  }
+
+  visitIdentifier(Identifier node) {
+    if (node.isThis()) {
+      useLocal(closureData.thisElement);
+    }
+    node.visitChildren(this);
+  }
+
+  visitSend(Send node) {
+    Element element = elements[node];
+    if (Elements.isLocal(element)) {
+      useLocal(element);
+    } else if (node.receiver === null &&
+               Elements.isInstanceSend(node, elements)) {
+      useLocal(closureData.thisElement);
+    }
+    node.visitChildren(this);
+  }
+
+  // If variables that are declared in the [node] scope are captured and need
+  // to be boxed create a box-element and update the [capturingScopes] in the
+  // current [closureData].
+  // The boxed variables are updated in the [capturedVariableMapping].
+  void attachCapturedScopeVariables(Node node) {
+    Element box = null;
+    Map<Element, Element> scopeMapping = new Map<Element, Element>();
+    for (Element element in scopeVariables) {
+      if (capturedVariableMapping.containsKey(element)) {
+        if (box == null) {
+          // TODO(floitsch): construct better box names.
+          SourceString boxName = new SourceString("box${boxCounter++}");
+          box = new Element(boxName,
+                            ElementKind.VARIABLE,
+                            currentFunctionElement);
+        }
+        // TODO(floitsch): construct better boxed names.
+        String elementName = element.name.slowToString();
+        // We are currently using the name in an HForeign which could replace
+        // "$X" with something else.
+        String escaped = elementName.replaceAll("\$", "_");
+        SourceString boxedName = new SourceString("${escaped}_${boxCounter++}");
+        Element boxed = new Element(boxedName, ElementKind.FIELD, box);
+        scopeMapping[element] = boxed;
+        capturedVariableMapping[element] = boxed;
+      }
+    }
+    if (!scopeMapping.isEmpty()) {
+      ClosureScope scope = new ClosureScope(box, scopeMapping);
+      closureData.capturingScopes[node] = scope;
+    }
+  }
+
+  visitLoop(Loop node) {
+    List<Element> oldScopeVariables = scopeVariables;
+    scopeVariables = new List<Element>();
+    node.visitChildren(this);
+    attachCapturedScopeVariables(node);
+    scopeVariables = oldScopeVariables;
+  }
+
+  visitFor(For node) {
+    visitLoop(node);
+    // See if we have declared loop variables that need to be boxed.
+    if (node.initializer === null) return;
+    VariableDefinitions definitions = node.initializer.asVariableDefinitions();
+    if (definitions == null) return;
+    ClosureScope scopeData = closureData.capturingScopes[node];
+    if (scopeData === null) return;
+    List<Element> result = <Element>[];
+    for (Link<Node> link = definitions.definitions.nodes;
+         !link.isEmpty();
+         link = link.tail) {
+      Node definition = link.head;
+      Element element = elements[definition];
+      if (capturedVariableMapping.containsKey(element)) {
+        result.add(element);
+      };
+    }
+    scopeData.boxedLoopVariables = result;
+  }
+
+  ClosureData globalizeClosure(FunctionExpression node) {
+    FunctionElement element = elements[node];
+    ClassElement globalizedElement =
+        new ClosureClassElement(compiler, element.getCompilationUnit());
+    FunctionElement callElement =
+        new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME,
+                                 element,
+                                 globalizedElement);
+    globalizedElement.backendMembers =
+        const EmptyLink<Element>().prepend(callElement);
+    // The nested function's 'this' is the same as the one for the outer
+    // function. It could be [null] if we are inside a static method.
+    Element thisElement = closureData.thisElement;
+    return new ClosureData(element, globalizedElement,
+                           callElement, thisElement);
+  }
+
+  visitFunctionExpression(FunctionExpression node) {
+    Element element = elements[node];
+    if (element.kind === ElementKind.PARAMETER) {
+      // TODO(ahe): This is a hack. This method should *not* call
+      // visitChildren.
+      return node.name.accept(this);
+    }
+    bool isClosure = (closureData !== null);
+
+    if (isClosure) closures.add(node);
+
+    bool oldInsideClosure = insideClosure;
+    FunctionElement oldFunctionElement = currentFunctionElement;
+    ClosureData oldClosureData = closureData;
+    List<Element> oldScopeVariables = scopeVariables;
+
+
+    insideClosure = isClosure;
+    currentFunctionElement = elements[node];
+    if (insideClosure) {
+      closureData = globalizeClosure(node);
+    } else {
+      Element thisElement = null;
+      // TODO(floitsch): we should not need to look for generative constructors.
+      // At the moment we store only one ClosureData for both the factory and
+      // the body.
+      if (element.isInstanceMember() ||
+          element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+        // TODO(floitsch): currently all variables are considered to be
+        // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'.
+        Element thisEnclosingElement = element;
+        if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+          ConstructorBodyElement body = element;
+          thisEnclosingElement = body.constructor;
+        }
+        thisElement = new Element(const SourceString("this"),
+                                  ElementKind.PARAMETER,
+                                  thisEnclosingElement);
+      }
+      closureData = new ClosureData(null, null, null, thisElement);
+    }
+    scopeVariables = new List<Element>();
+
+    // TODO(floitsch): a named function is visible from inside itself. Add
+    // the element to the block.
+
+    // We have to declare the implicit 'this' parameter.
+    if (!insideClosure && closureData.thisElement !== null) {
+      declareLocal(closureData.thisElement);
+    }
+    // If we are inside a named closure we have to declare ourselve. For
+    // simplicity we declare the local even if the closure does not have a name
+    // It will simply not be used.
+    if (insideClosure) {
+      declareLocal(element);
+    }
+
+    // TODO(ahe): This is problematic. The backend should not repeat
+    // the work of the resolver. It is the resolver's job to create
+    // parameters, etc. Other phases should only visit statements.
+    // TODO(floitsch): we avoid visiting the initializers on purpose so that we
+    // get an error-message later in the builder.
+    if (node.parameters !== null) node.parameters.accept(this);
+    if (node.body !== null) node.body.accept(this);
+
+    attachCapturedScopeVariables(node);
+
+    closureDataCache[node] = closureData;
+
+    ClosureData savedClosureData = closureData;
+    bool savedInsideClosure = insideClosure;
+
+    // Restore old values.
+    scopeVariables = oldScopeVariables;
+    insideClosure = oldInsideClosure;
+    closureData = oldClosureData;
+    currentFunctionElement = oldFunctionElement;
+
+    // Mark all free variables as captured and use them in the outer function.
+    List<Element> freeVariables =
+        savedClosureData.freeVariableMapping.getKeys();
+    assert(freeVariables.isEmpty() || savedInsideClosure);
+    for (Element freeElement in freeVariables) {
+      if (capturedVariableMapping[freeElement] != null &&
+          capturedVariableMapping[freeElement] != freeElement) {
+        compiler.internalError('In closure analyzer', node: node);
+      }
+      capturedVariableMapping[freeElement] = freeElement;
+      useLocal(freeElement);
+    }
+  }
+
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    node.visitChildren(this);
+    declareLocal(elements[node]);
+  }
+
+  visitTryStatement(TryStatement node) {
+    // TODO(ngeoffray): implement finer grain state.
+    inTryCatchOrFinally = true;
+    node.visitChildren(this);
+    inTryCatchOrFinally = false;
+  }
+}
diff --git a/lib/compiler/implementation/ssa/codegen.dart b/lib/compiler/implementation/ssa/codegen.dart
new file mode 100644
index 0000000..e3412386
--- /dev/null
+++ b/lib/compiler/implementation/ssa/codegen.dart
@@ -0,0 +1,1565 @@
+// Copyright (c) 2012, 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.
+
+class SsaCodeGeneratorTask extends CompilerTask {
+  SsaCodeGeneratorTask(Compiler compiler) : super(compiler);
+  String get name() => 'SSA code generator';
+
+
+  String generateMethod(WorkItem work, HGraph graph) {
+    return measure(() {
+      compiler.tracer.traceGraph("codegen", graph);
+      Map<Element, String> parameterNames = getParameterNames(work);
+      String parameters = Strings.join(parameterNames.getValues(), ', ');
+      SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator(
+          compiler, work, parameters, parameterNames);
+      codegen.visitGraph(graph);
+      return 'function($parameters) {\n${codegen.buffer}}';
+    });
+  }
+
+  String generateBailoutMethod(WorkItem work, HGraph graph) {
+    return measure(() {
+      compiler.tracer.traceGraph("codegen-bailout", graph);
+      new SsaBailoutPropagator(compiler).visitGraph(graph);
+
+      Map<Element, String> parameterNames = getParameterNames(work);
+      String parameters = Strings.join(parameterNames.getValues(), ', ');
+      SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator(
+          compiler, work, parameters, parameterNames);
+      codegen.visitGraph(graph);
+
+      StringBuffer newParameters = new StringBuffer();
+      if (!parameterNames.isEmpty()) newParameters.add('$parameters, ');
+      newParameters.add('state');
+
+      for (int i = 0; i < codegen.maxBailoutParameters; i++) {
+        newParameters.add(', env$i');
+      }
+
+      return 'function($newParameters) {\n${codegen.setup}${codegen.buffer}}';
+    });
+  }
+
+  Map<Element, String> getParameterNames(WorkItem work) {
+    Map<Element, String> parameterNames = new LinkedHashMap<Element, String>();
+    FunctionElement function = work.element;
+
+    // The dom/html libraries have inline JS code that reference
+    // parameter names directly. Long-term such code will be rejected.
+    // Now, just don't mangle the parameter name.
+    function.computeParameters(compiler).forEachParameter((Element element) {
+      parameterNames[element] = function.isNative()
+          ? element.name.slowToString()
+          : JsNames.getValid('${element.name.slowToString()}');
+    });
+    return parameterNames;
+  }
+}
+
+typedef void ElementAction(Element element);
+
+class SsaCodeGenerator implements HVisitor {
+  final Compiler compiler;
+  final WorkItem work;
+  final StringBuffer buffer;
+  final String parameters;
+
+  final Map<Element, String> parameterNames;
+  final Map<int, String> names;
+  final Map<String, int> prefixes;
+  final Set<HInstruction> generateAtUseSite;
+  final Map<HPhi, String> logicalOperations;
+  final Map<Element, ElementAction> breakAction;
+  final Map<Element, ElementAction> continueAction;
+
+  Element equalsNullElement;
+  int indent = 0;
+  int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
+  HGraph currentGraph;
+  HBasicBlock currentBlock;
+
+  // Records a block-information that is being handled specially.
+  // Used to break bad recursion.
+  HLabeledBlockInformation currentBlockInformation;
+  // The subgraph is used to delimit traversal for some constructions, e.g.,
+  // if branches.
+  SubGraph subGraph;
+
+  LibraryElement get currentLibrary() => work.element.getLibrary();
+
+  bool isGenerateAtUseSite(HInstruction instruction) {
+    return generateAtUseSite.contains(instruction);
+  }
+
+  SsaCodeGenerator(this.compiler,
+                   this.work,
+                   this.parameters,
+                   this.parameterNames)
+    : names = new Map<int, String>(),
+      prefixes = new Map<String, int>(),
+      buffer = new StringBuffer(),
+      generateAtUseSite = new Set<HInstruction>(),
+      logicalOperations = new Map<HPhi, String>(),
+      breakAction = new Map<Element, ElementAction>(),
+      continueAction = new Map<Element, ElementAction>() {
+
+    for (final name in parameterNames.getValues()) {
+      prefixes[name] = 0;
+    }
+
+    equalsNullElement =
+        compiler.builder.interceptors.getEqualsNullInterceptor();
+  }
+
+  abstract visitTypeGuard(HTypeGuard node);
+
+  abstract beginGraph(HGraph graph);
+  abstract endGraph(HGraph graph);
+
+  abstract beginLoop(HBasicBlock block);
+  abstract endLoop(HBasicBlock block);
+  abstract handleLoopCondition(HLoopBranch node);
+
+  abstract startIf(HIf node);
+  abstract endIf(HIf node);
+  abstract startThen(HIf node);
+  abstract endThen(HIf node);
+  abstract startElse(HIf node);
+  abstract endElse(HIf node);
+
+  void beginExpression(int precedence) {
+    if (precedence < expectedPrecedence) {
+      buffer.add('(');
+    }
+  }
+
+  void endExpression(int precedence) {
+    if (precedence < expectedPrecedence) {
+      buffer.add(')');
+    }
+  }
+
+  void preGenerateMethod(HGraph graph) {
+    new SsaInstructionMerger(generateAtUseSite).visitGraph(graph);
+    new SsaConditionMerger(generateAtUseSite,
+                           logicalOperations).visitGraph(graph);
+  }
+
+  visitGraph(HGraph graph) {
+    preGenerateMethod(graph);
+    currentGraph = graph;
+    indent++;  // We are already inside a function.
+    subGraph = new SubGraph(graph.entry, graph.exit);
+    beginGraph(graph);
+    visitBasicBlock(graph.entry);
+    endGraph(graph);
+  }
+
+  void visitSubGraph(SubGraph newSubGraph) {
+    SubGraph oldSubGraph = subGraph;
+    subGraph = newSubGraph;
+    visitBasicBlock(subGraph.start);
+    subGraph = oldSubGraph;
+  }
+
+  String temporary(HInstruction instruction) {
+    int id = instruction.id;
+    String name = names[id];
+    if (name !== null) return name;
+
+    if (instruction is HPhi) {
+      HPhi phi = instruction;
+      Element element = phi.element;
+      if (element != null && element.kind == ElementKind.PARAMETER) {
+        name = parameterNames[element];
+        names[id] = name;
+        return name;
+      }
+
+      String prefix;
+      if (element !== null && !element.name.isEmpty()) {
+        prefix = element.name.slowToString();
+      } else {
+        prefix = 'v';
+      }
+      if (!prefixes.containsKey(prefix)) {
+        prefixes[prefix] = 0;
+        return newName(id, prefix);
+      } else {
+        return newName(id, '${prefix}_${prefixes[prefix]++}');
+      }
+    } else {
+      String prefix = 't';
+      if (!prefixes.containsKey(prefix)) prefixes[prefix] = 0;
+      return newName(id, '${prefix}${prefixes[prefix]++}');
+    }
+  }
+
+  bool temporaryExists(HInstruction instruction) {
+    return names.containsKey(instruction.id);
+  }
+
+  String newName(int id, String name) {
+    String result = JsNames.getValid(name);
+    names[id] = result;
+    return result;
+  }
+
+  /**
+    * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET].
+    */
+  void visitArguments(List<HInstruction> inputs) {
+    assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET);
+    buffer.add('(');
+    for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) {
+      if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', ');
+      use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    }
+    buffer.add(')');
+  }
+
+  void define(HInstruction instruction) {
+    buffer.add('var ${temporary(instruction)} = ');
+    visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE);
+  }
+
+  void use(HInstruction argument, int expectedPrecedence) {
+    if (isGenerateAtUseSite(argument)) {
+      visit(argument, expectedPrecedence);
+    } else if (argument is HIntegerCheck) {
+      HIntegerCheck instruction = argument;
+      use(instruction.value, expectedPrecedence);
+    } else if (argument is HBoundsCheck) {
+      HBoundsCheck instruction = argument;
+      use(instruction.index, expectedPrecedence);
+    } else if (argument is HTypeGuard) {
+      HTypeGuard instruction = argument;
+      use(instruction.guarded, expectedPrecedence);
+    } else {
+      buffer.add(temporary(argument));
+    }
+  }
+
+  visit(HInstruction node, int expectedPrecedence) {
+    int oldPrecedence = this.expectedPrecedence;
+    this.expectedPrecedence = expectedPrecedence;
+    node.accept(this);
+    this.expectedPrecedence = oldPrecedence;
+  }
+
+  void continueAsBreak(LabelElement target) {
+    addIndentation();
+    buffer.add("break ");
+    writeContinueLabel(target);
+    buffer.add(";\n");
+  }
+
+  void implicitContinueAsBreak(TargetElement target) {
+    addIndentation();
+    buffer.add("break ");
+    writeImplicitContinueLabel(target);
+    buffer.add(";\n");
+  }
+
+  void implicitBreakWithLabel(TargetElement target) {
+    addIndentation();
+    buffer.add("break ");
+    writeImplicitLabel(target);
+    buffer.add(";\n");
+  }
+
+  void handleLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
+    addIndentation();
+    Link<Element> continueOverrides = const EmptyLink<Element>();
+    // If [labeledBlockInfo.isContinue], the block is an artificial
+    // block around the body of a loop with an update block, so that
+    // continues of the loop can be written as breaks of the body
+    // block.
+    if (labeledBlockInfo.isContinue) {
+      for (LabelElement label in labeledBlockInfo.labels) {
+        if (label.isContinueTarget) {
+          writeContinueLabel(label);
+          buffer.add(':');
+          continueAction[label] = continueAsBreak;
+          continueOverrides = continueOverrides.prepend(label);
+        }
+      }
+      // For handling unlabeled continues from the body of a loop.
+      // TODO(lrn): Consider recording whether the target is in fact
+      // a target of an unlabeled continue, and not generate this if it isn't.
+      TargetElement target = labeledBlockInfo.target;
+      writeImplicitContinueLabel(target);
+      buffer.add(':');
+      continueAction[target] = implicitContinueAsBreak;
+      continueOverrides = continueOverrides.prepend(target);
+    } else {
+      for (LabelElement label in labeledBlockInfo.labels) {
+        if (label.isBreakTarget) {
+          writeLabel(label);
+          buffer.add(':');
+        }
+      }
+      TargetElement target = labeledBlockInfo.target;
+      if (target.isSwitch) {
+        // This is an extra block around a switch that is generated
+        // as a nested if/else chain. We add an extra break target
+        // so that case code can break.
+        writeImplicitLabel(target);
+        buffer.add(':');
+        breakAction[target] = implicitBreakWithLabel;
+      }
+    }
+    buffer.add('{\n');
+    indent++;
+
+    visitSubGraph(labeledBlockInfo.body);
+
+    indent--;
+    addIndentation();
+    buffer.add('}\n');
+
+    if (labeledBlockInfo.joinBlock !== null) {
+      visitBasicBlock(labeledBlockInfo.joinBlock);
+    }
+    if (labeledBlockInfo.isContinue) {
+      while (!continueOverrides.isEmpty()) {
+        continueAction.remove(continueOverrides.head);
+        continueOverrides = continueOverrides.tail;
+      }
+    } else {
+      breakAction.remove(labeledBlockInfo.target);
+    }
+  }
+
+  void emitLogicalOperation(HPhi node, String operation) {
+    JSBinaryOperatorPrecedence operatorPrecedence =
+        JSPrecedence.binary[operation];
+    beginExpression(operatorPrecedence.precedence);
+    use(node.inputs[0], operatorPrecedence.left);
+    buffer.add(" $operation ");
+    use(node.inputs[1], operatorPrecedence.right);
+    endExpression(operatorPrecedence.precedence);
+  }
+
+  visitBasicBlock(HBasicBlock node) {
+    // Abort traversal if we are leaving the currently active sub-graph.
+    if (!subGraph.contains(node)) return;
+
+    // If this node has special behavior attached, handle it.
+    // If we reach here again while handling the attached information,
+    // e.g., because we call visitSubGraph on a subgraph starting here,
+    // don't handle it again.
+    if (node.hasLabeledBlockInformation() &&
+        node.labeledBlockInformation !== currentBlockInformation) {
+      HLabeledBlockInformation oldBlockInformation = currentBlockInformation;
+      currentBlockInformation = node.labeledBlockInformation;
+      handleLabeledBlock(currentBlockInformation);
+      currentBlockInformation = oldBlockInformation;
+      return;
+    }
+
+    currentBlock = node;
+
+    if (node.isLoopHeader()) {
+      // While loop will be closed by the conditional loop-branch.
+      // TODO(floitsch): HACK HACK HACK.
+      beginLoop(node);
+    }
+
+    HInstruction instruction = node.first;
+    while (instruction != null) {
+      if (instruction === node.last) {
+        for (HBasicBlock successor in node.successors) {
+          int index = successor.predecessors.indexOf(node);
+          successor.forEachPhi((HPhi phi) {
+            bool isLogicalOperation = logicalOperations.containsKey(phi);
+            // In case the phi is being generated by another
+            // instruction.
+            if (isLogicalOperation && isGenerateAtUseSite(phi)) return;
+            addIndentation();
+            if (!temporaryExists(phi)) buffer.add('var ');
+            buffer.add('${temporary(phi)} = ');
+            if (isLogicalOperation) {
+              emitLogicalOperation(phi, logicalOperations[phi]);
+            } else {
+              use(phi.inputs[index], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+            }
+            buffer.add(';\n');
+          });
+        }
+      }
+
+      if (instruction is HGoto || instruction is HExit || instruction is HTry) {
+        visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
+        return;
+      } else if (!isGenerateAtUseSite(instruction)) {
+        if (instruction is !HIf && instruction is !HTypeGuard) {
+          addIndentation();
+        }
+        if (instruction.usedBy.isEmpty()
+            || instruction is HTypeGuard
+            || instruction is HCheck) {
+          visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
+        } else {
+          define(instruction);
+        }
+        // Control flow instructions know how to handle ';'.
+        if (instruction is !HControlFlow && instruction is !HTypeGuard) {
+          buffer.add(';\n');
+        }
+      } else if (instruction is HIf) {
+        HIf hif = instruction;
+        // The "if" is implementing part of a logical expression.
+        // Skip directly forward to to its latest successor, since everything
+        // in-between must also be generateAtUseSite.
+        assert(hif.trueBranch.id < hif.falseBranch.id);
+        visitBasicBlock(hif.falseBranch);
+        return;
+      }
+      instruction = instruction.next;
+    }
+  }
+
+  visitInvokeBinary(HInvokeBinary node, String op) {
+    if (node.builtin) {
+      JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary[op];
+      beginExpression(operatorPrecedences.precedence);
+      use(node.left, operatorPrecedences.left);
+      buffer.add(' $op ');
+      use(node.right, operatorPrecedences.right);
+      endExpression(operatorPrecedences.precedence);
+    } else {
+      visitInvokeStatic(node);
+    }
+  }
+
+  visitInvokeUnary(HInvokeUnary node, String op) {
+    if (node.builtin) {
+      beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
+      buffer.add('$op');
+      use(node.operand, JSPrecedence.PREFIX_PRECEDENCE);
+      endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+    } else {
+      visitInvokeStatic(node);
+    }
+  }
+
+  visitEquals(HEquals node) {
+    if (node.builtin) {
+      beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+      use(node.left, JSPrecedence.EQUALITY_PRECEDENCE);
+      buffer.add(' === ');
+      use(node.right, JSPrecedence.RELATIONAL_PRECEDENCE);
+      endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    } else if (node.element === equalsNullElement) {
+      beginExpression(JSPrecedence.CALL_PRECEDENCE);
+      use(node.target, JSPrecedence.CALL_PRECEDENCE);
+      buffer.add('(');
+      use(node.left, JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      buffer.add(')');
+      endExpression(JSPrecedence.CALL_PRECEDENCE);
+    } else {
+      visitInvokeStatic(node);
+    }
+  }
+
+  visitAdd(HAdd node)               => visitInvokeBinary(node, '+');
+  visitDivide(HDivide node)         => visitInvokeBinary(node, '/');
+  visitMultiply(HMultiply node)     => visitInvokeBinary(node, '*');
+  visitSubtract(HSubtract node)     => visitInvokeBinary(node, '-');
+  // Truncating divide does not have a JS equivalent.
+  visitTruncatingDivide(HTruncatingDivide node) => visitInvokeStatic(node);
+  // Modulo cannot be mapped to the native operator (different semantics).
+  visitModulo(HModulo node)                     => visitInvokeStatic(node);
+
+  visitBitAnd(HBitAnd node)         => visitInvokeBinary(node, '&');
+  visitBitNot(HBitNot node)         => visitInvokeUnary(node, '~');
+  visitBitOr(HBitOr node)           => visitInvokeBinary(node, '|');
+  visitBitXor(HBitXor node)         => visitInvokeBinary(node, '^');
+
+  // We need to check if the left operand is negative in order to use
+  // the native operator.
+  visitShiftRight(HShiftRight node) => visitInvokeStatic(node);
+
+  // Shift left cannot be mapped to the native operator (different semantics).
+  visitShiftLeft(HShiftLeft node)   => visitInvokeStatic(node);
+
+  visitNegate(HNegate node)         => visitInvokeUnary(node, '-');
+
+  visitIdentity(HIdentity node)         => visitInvokeBinary(node, '===');
+  visitLess(HLess node)                 => visitInvokeBinary(node, '<');
+  visitLessEqual(HLessEqual node)       => visitInvokeBinary(node, '<=');
+  visitGreater(HGreater node)           => visitInvokeBinary(node, '>');
+  visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>=');
+
+  visitBoolify(HBoolify node) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    assert(node.inputs.length == 1);
+    use(node.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add(' === true');
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  visitExit(HExit node) {
+    // Don't do anything.
+  }
+
+  visitGoto(HGoto node) {
+    assert(currentBlock.successors.length == 1);
+    List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
+    // With the exception of the entry-node which dominates its successor
+    // and the exit node, no block finishing with a 'goto' can have more than
+    // one dominated block (since it has only one successor).
+    // If the successor is dominated by another block, then the other block
+    // is responsible for visiting the successor.
+    if (dominated.isEmpty()) return;
+    if (dominated.length > 2) unreachable();
+    if (dominated.length == 2 && currentBlock !== currentGraph.entry) {
+      unreachable();
+    }
+    assert(dominated[0] == currentBlock.successors[0]);
+    visitBasicBlock(dominated[0]);
+  }
+
+  // Used to write the name of labels.
+  void writeLabel(LabelElement label) {
+    buffer.add('\$${label.labelName}\$${label.target.nestingLevel}');
+  }
+
+  void writeImplicitLabel(TargetElement target) {
+    buffer.add('\$${target.nestingLevel}');
+  }
+
+  // We sometimes handle continue targets differently from break targets,
+  // so we have special continue-only labels.
+  void writeContinueLabel(LabelElement label) {
+    buffer.add('c\$${label.labelName}\$${label.target.nestingLevel}');
+  }
+
+  void writeImplicitContinueLabel(TargetElement target) {
+    buffer.add('c\$${target.nestingLevel}');
+  }
+
+  /**
+   * Checks if [map] contains an [ElementAction] for [element], and
+   * if so calls that action and returns true.
+   * Otherwise returns false.
+   */
+  bool tryCallAction(Map<Element, ElementAction> map, Element element) {
+    ElementAction action = map[element];
+    if (action === null) return false;
+    action(element);
+    return true;
+  }
+
+  visitBreak(HBreak node) {
+    assert(currentBlock.successors.length == 1);
+    if (node.label !== null) {
+      LabelElement label = node.label;
+      if (!tryCallAction(breakAction, label)) {
+        addIndentation();
+        buffer.add("break ");
+        writeLabel(label);
+        buffer.add(";\n");
+      }
+    } else {
+      TargetElement target = node.target;
+      if (!tryCallAction(breakAction, target)) {
+        addIndentation();
+        buffer.add("break;\n");
+      }
+    }
+  }
+
+  visitContinue(HContinue node) {
+    assert(currentBlock.successors.length == 1);
+    if (node.label !== null) {
+      LabelElement label = node.label;
+      if (!tryCallAction(continueAction, label)) {
+        addIndentation();
+        buffer.add("continue ");
+        writeLabel(label);
+        buffer.add(";\n");
+      }
+    } else {
+      TargetElement target = node.target;
+      if (!tryCallAction(continueAction, target)) {
+        addIndentation();
+        buffer.add("continue;\n");
+      }
+    }
+  }
+
+  visitTry(HTry node) {
+    addIndentation();
+    buffer.add('try {\n');
+    indent++;
+    List<HBasicBlock> successors = node.block.successors;
+    visitBasicBlock(successors[0]);
+    indent--;
+
+    if (node.finallyBlock != successors[1]) {
+      // Printing the catch part.
+      addIndentation();
+      String name = temporary(node.exception);
+      parameterNames[node.exception.element] = name;
+      buffer.add('} catch ($name) {\n');
+      indent++;
+      visitBasicBlock(successors[1]);
+      parameterNames.remove(node.exception.element);
+      indent--;
+    }
+
+    if (node.finallyBlock != null) {
+      addIndentation();
+      buffer.add('} finally {\n');
+      indent++;
+      visitBasicBlock(node.finallyBlock);
+      indent--;
+    }
+    addIndentation();
+    buffer.add('}\n');
+
+    visitBasicBlock(node.joinBlock);
+  }
+
+  visitIf(HIf node) {
+    List<HBasicBlock> dominated = node.block.dominatedBlocks;
+    HIfBlockInformation info = node.blockInformation;
+    startIf(node);
+    assert(!isGenerateAtUseSite(node));
+    startThen(node);
+    assert(node.thenBlock === dominated[0]);
+    visitSubGraph(info.thenGraph);
+    int preVisitedBlocks = 1;
+    endThen(node);
+    if (node.hasElse) {
+      startElse(node);
+      assert(node.elseBlock === dominated[1]);
+      visitSubGraph(info.elseGraph);
+      preVisitedBlocks = 2;
+      endElse(node);
+    }
+    endIf(node);
+    if (info.joinBlock !== null && info.joinBlock.dominator !== node.block) {
+      // The join block is dominated by a block in one of the branches.
+      // The subgraph traversal never reached it, so we visit it here
+      // instead.
+      visitBasicBlock(info.joinBlock);
+    }
+
+    // Visit all the dominated blocks that are not part of the then or else
+    // branches, and is not the join block.
+    // Depending on how the then/else branches terminate
+    // (e.g., return/throw/break) there can be any number of these.
+    int dominatedCount = dominated.length;
+    for (int i = preVisitedBlocks; i < dominatedCount; i++) {
+      HBasicBlock dominatedBlock = dominated[i];
+      assert(dominatedBlock.dominator === node.block);
+      visitBasicBlock(dominatedBlock);
+    }
+  }
+
+  visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.');
+    // Avoid adding the generative constructor name to the list of
+    // seen selectors.
+    if (node.inputs[0] is HForeignNew) {
+      HForeignNew foreignNew = node.inputs[0];
+      // Remove 'this' from the number of arguments.
+      int argumentCount = node.inputs.length - 1;
+
+      // TODO(ahe): The constructor name was statically resolved in
+      // SsaBuilder.buildFactory. Is there a cleaner way to do this?
+      node.name.printOn(buffer);
+      visitArguments(node.inputs);
+    } else {
+      buffer.add(compiler.namer.instanceMethodInvocationName(
+          currentLibrary, node.name, node.selector));
+      visitArguments(node.inputs);
+      compiler.registerDynamicInvocation(node.name, node.selector);
+    }
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+  }
+
+  visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.');
+    buffer.add(compiler.namer.setterName(currentLibrary, node.name));
+    visitArguments(node.inputs);
+    compiler.registerDynamicSetter(node.name);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+  }
+
+  visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.');
+    buffer.add(compiler.namer.getterName(currentLibrary, node.name));
+    visitArguments(node.inputs);
+    compiler.registerDynamicGetter(node.name);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+  }
+
+  visitInvokeClosure(HInvokeClosure node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.');
+    buffer.add(compiler.namer.closureInvocationName(node.selector));
+    visitArguments(node.inputs);
+    // TODO(floitsch): we should have a separate list for closure invocations.
+    compiler.registerDynamicInvocation(Namer.CLOSURE_INVOCATION_NAME,
+                                       node.selector);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+  }
+
+  visitInvokeStatic(HInvokeStatic node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    use(node.target, JSPrecedence.CALL_PRECEDENCE);
+    visitArguments(node.inputs);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+  }
+
+  visitInvokeSuper(HInvokeSuper node) {
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    Element superMethod = node.element;
+    Element superClass = superMethod.enclosingElement;
+    // Remove the element and 'this'.
+    int argumentCount = node.inputs.length - 2;
+    String className = compiler.namer.isolatePropertyAccess(superClass);
+    String methodName;
+    if (superMethod.kind == ElementKind.FUNCTION ||
+        superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+      methodName = compiler.namer.instanceMethodName(
+          currentLibrary, superMethod.name, argumentCount);
+    } else {
+      methodName = compiler.namer.getterName(currentLibrary, superMethod.name);
+      // We need to register the name to ensure that the emitter
+      // generates the necessary getter.
+      // TODO(ahe): This is not optimal for tree-shaking, but we lack
+      // API to register the precise information. In this case, the
+      // enclosingElement of superMethod needs the getter, no other
+      // class (not even its subclasses).
+      compiler.registerDynamicGetter(superMethod.name);
+    }
+    buffer.add('$className.prototype.$methodName.call');
+    visitArguments(node.inputs);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+    compiler.registerStaticUse(superMethod);
+  }
+
+  visitFieldGet(HFieldGet node) {
+    String name = JsNames.getValid(node.element.name.slowToString());
+    if (node.receiver !== null) {
+      beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
+      use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+      buffer.add('.');
+      buffer.add(name);
+      beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
+    } else {
+      buffer.add(name);
+    }
+  }
+
+  visitFieldSet(HFieldSet node) {
+    if (node.receiver !== null) {
+      beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
+      buffer.add('.');
+    } else {
+      // TODO(ngeoffray): Remove the 'var' once we don't globally box
+      // variables used in a try/catch.
+      buffer.add('var ');
+    }
+    String name = JsNames.getValid(node.element.name.slowToString());
+    buffer.add(name);
+    buffer.add(' = ');
+    use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    if (node.receiver !== null) {
+      endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    }
+  }
+
+  visitForeign(HForeign node) {
+    String code = node.code.slowToString();
+    List<HInstruction> inputs = node.inputs;
+    List<String> parts = code.split('#');
+    if (parts.length != inputs.length + 1) {
+      compiler.internalError(
+          'Wrong number of arguments for JS', instruction: node);
+    }
+    beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
+    buffer.add(parts[0]);
+    for (int i = 0; i < inputs.length; i++) {
+      use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE);
+      buffer.add(parts[i + 1]);
+    }
+    endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
+  }
+
+  visitForeignNew(HForeignNew node) {
+    String jsClassReference = compiler.namer.isolateAccess(node.element);
+    beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('new $jsClassReference(');
+    // We can't use 'visitArguments', since our arguments start at input[0].
+    List<HInstruction> inputs = node.inputs;
+    for (int i = 0; i < inputs.length; i++) {
+      if (i != 0) buffer.add(', ');
+      use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    }
+    buffer.add(')');
+    endExpression(JSPrecedence.MEMBER_PRECEDENCE);
+  }
+
+  visitConstant(HConstant node) {
+    assert(isGenerateAtUseSite(node));
+    // TODO(floitsch): the compile-time constant handler and the codegen
+    // need to work together to avoid the parenthesis. See r4928 for an
+    // implementation that still dealt with precedence.
+    ConstantHandler handler = compiler.constantHandler;
+    String name = handler.getNameForConstant(node.constant);
+    if (name === null) {
+      assert(!node.constant.isObject());
+      if (node.constant.isNum()
+          && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) {
+        buffer.add('(');
+        node.constant.writeJsCode(buffer, handler);
+        buffer.add(')');
+      } else {
+        node.constant.writeJsCode(buffer, handler);
+      }
+    } else {
+      buffer.add(compiler.namer.CURRENT_ISOLATE);
+      buffer.add(".");
+      buffer.add(name);
+    }
+  }
+
+  visitLoopBranch(HLoopBranch node) {
+    HBasicBlock branchBlock = currentBlock;
+    handleLoopCondition(node);
+    List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
+    // For a do while loop, the body has already been visited.
+    if (!node.isDoWhile()) {
+      visitBasicBlock(dominated[0]);
+    }
+    endLoop(node.block);
+    visitBasicBlock(branchBlock.successors[1]);
+    // With labeled breaks we can have more dominated blocks.
+    if (dominated.length >= 3) {
+      for (int i = 2; i < dominated.length; i++) {
+        visitBasicBlock(dominated[i]);
+      }
+    }
+  }
+
+  visitNot(HNot node) {
+    assert(node.inputs.length == 1);
+    beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add('!');
+    use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
+    endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+  }
+
+  visitParameterValue(HParameterValue node) {
+    assert(isGenerateAtUseSite(node));
+    buffer.add(parameterNames[node.element]);
+  }
+
+  visitPhi(HPhi node) {
+    String operation = logicalOperations[node];
+    if (operation !== null) {
+      emitLogicalOperation(node, operation);
+    } else {
+      buffer.add('${temporary(node)}');
+    }
+  }
+
+  visitReturn(HReturn node) {
+    assert(node.inputs.length == 1);
+    HInstruction input = node.inputs[0];
+    if (input.isConstantNull()) {
+      buffer.add('return;\n');
+    } else {
+      buffer.add('return ');
+      use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
+      buffer.add(';\n');
+    }
+  }
+
+  visitThis(HThis node) {
+    buffer.add('this');
+  }
+
+  visitThrow(HThrow node) {
+    if (node.isRethrow) {
+      buffer.add('throw ');
+      use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
+    } else {
+      generateThrowWithHelper('captureStackTrace', node.inputs[0]);
+    }
+    buffer.add(';\n');
+  }
+
+  visitBoundsCheck(HBoundsCheck node) {
+    buffer.add('if (');
+    use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
+    buffer.add(' < 0 || ');
+    use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
+    buffer.add(' >= ');
+    use(node.length, JSPrecedence.SHIFT_PRECEDENCE);
+    buffer.add(") ");
+    generateThrowWithHelper('ioore', node.index);
+  }
+
+  visitIntegerCheck(HIntegerCheck node) {
+    buffer.add('if (');
+    use(node.value, JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add(' !== (');
+    use(node.value, JSPrecedence.BITWISE_OR_PRECEDENCE);
+    buffer.add(" | 0)) ");
+    generateThrowWithHelper('iae', node.value);
+  }
+
+  void generateThrowWithHelper(String helperName, HInstruction argument) {
+    Element helper = compiler.findHelper(new SourceString(helperName));
+    compiler.registerStaticUse(helper);
+    buffer.add('throw ');
+    beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
+    beginExpression(JSPrecedence.CALL_PRECEDENCE);
+    buffer.add(compiler.namer.isolateAccess(helper));
+    visitArguments([null, argument]);
+    endExpression(JSPrecedence.CALL_PRECEDENCE);
+    endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
+  }
+
+  void addIndentation() {
+    for (int i = 0; i < indent; i++) {
+      buffer.add('  ');
+    }
+  }
+
+  void visitStatic(HStatic node) {
+    compiler.registerStaticUse(node.element);
+    buffer.add(compiler.namer.isolateAccess(node.element));
+  }
+
+  void visitStaticStore(HStaticStore node) {
+    compiler.registerStaticUse(node.element);
+    beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    buffer.add(compiler.namer.isolateAccess(node.element));
+    buffer.add(' = ');
+    use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+  }
+
+  void visitLiteralList(HLiteralList node) {
+    if (node.isConst) {
+      // TODO(floitsch): Remove this when CTC handles arrays.
+      SourceString name = new SourceString('makeLiteralListConst');
+      Element helper = compiler.findHelper(name);
+      compiler.registerStaticUse(helper);
+      beginExpression(JSPrecedence.CALL_PRECEDENCE);
+      buffer.add(compiler.namer.isolateAccess(helper));
+      buffer.add('(');
+      generateArrayLiteral(node);
+      buffer.add(')');
+      endExpression(JSPrecedence.CALL_PRECEDENCE);
+    } else {
+      generateArrayLiteral(node);
+    }
+  }
+
+  void generateArrayLiteral(HLiteralList node) {
+    buffer.add('[');
+    int len = node.inputs.length;
+    for (int i = 0; i < len; i++) {
+      if (i != 0) buffer.add(', ');
+      use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    }
+    buffer.add(']');
+  }
+
+  void visitIndex(HIndex node) {
+    if (node.builtin) {
+      beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
+      use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
+      buffer.add('[');
+      use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
+      buffer.add(']');
+      endExpression(JSPrecedence.MEMBER_PRECEDENCE);
+    } else {
+      visitInvokeStatic(node);
+    }
+  }
+
+  void visitIndexAssign(HIndexAssign node) {
+    if (node.builtin) {
+      beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
+      buffer.add('[');
+      use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
+      buffer.add('] = ');
+      use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+    } else {
+      visitInvokeStatic(node);
+    }
+  }
+
+  void visitInvokeInterceptor(HInvokeInterceptor node) {
+    if (node.builtinJsName != null) {
+      beginExpression(JSPrecedence.CALL_PRECEDENCE);
+      use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
+      buffer.add('.');
+      buffer.add(node.builtinJsName);
+      if (node.getter) return;
+      buffer.add('(');
+      for (int i = 2; i < node.inputs.length; i++) {
+        if (i != 2) buffer.add(', ');
+        use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      }
+      buffer.add(")");
+      endExpression(JSPrecedence.CALL_PRECEDENCE);
+    } else {
+      return visitInvokeStatic(node);
+    }
+  }
+
+  void checkInt(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    use(input, JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add(' $cmp (');
+    use(input, JSPrecedence.BITWISE_OR_PRECEDENCE);
+    buffer.add(' | 0)');
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkNum(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add('typeof ');
+    use(input, JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(" $cmp 'number'");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkDouble(HInstruction input, String cmp) {
+    checkNum(input, cmp);
+  }
+
+  void checkString(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add('typeof ');
+    use(input, JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(" $cmp 'string'");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkBool(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add('typeof ');
+    use(input, JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(" $cmp 'boolean'");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkObject(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add('typeof ');
+    use(input, JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(" $cmp 'object'");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkArray(HInstruction input, String cmp) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    use(input, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.constructor $cmp Array');
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkNull(HInstruction input) {
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    use(input, JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add(" === (void 0)");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+  }
+
+  void checkFunction(HInstruction input, Element element) {
+    beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+    beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add('typeof ');
+    use(input, JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(" === 'function'");
+    endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+    buffer.add(" || ");
+    beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    checkObject(input, '===');
+    buffer.add(" && ");
+    checkType(input, element);
+    endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+  }
+
+  void checkType(HInstruction input, Element element) {
+    bool requiresNativeIsCheck =
+        compiler.emitter.nativeEmitter.requiresNativeIsCheck(element);
+    if (!requiresNativeIsCheck) buffer.add('!!');
+    use(input, JSPrecedence.MEMBER_PRECEDENCE);
+    buffer.add('.');
+    buffer.add(compiler.namer.operatorIs(element));
+    if (requiresNativeIsCheck) buffer.add('()');
+  }
+
+  void handleStringSupertypeCheck(HInstruction input, Element element) {
+    // Make sure List and String don't share supertypes, otherwise we
+    // would need to check for List too.
+    assert(element !== compiler.listClass
+           && !Elements.isListSupertype(element, compiler));
+    beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+    checkString(input, '===');
+    buffer.add(' || ');
+    beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    checkObject(input, '===');
+    buffer.add(' && ');
+    checkType(input, element);
+    endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+  }
+
+  void handleListOrSupertypeCheck(HInstruction input, Element element) {
+    // Make sure List and String don't share supertypes, otherwise we
+    // would need to check for String too.
+    assert(element !== compiler.stringClass
+           && !Elements.isStringSupertype(element, compiler));
+    beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    checkObject(input, '===');
+    buffer.add(' && (');
+    beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+    checkArray(input, '===');
+    buffer.add(' || ');
+    checkType(input, element);
+    buffer.add(')');
+    endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+    endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+  }
+
+  void visitIs(HIs node) {
+    Element element = node.typeExpression;
+    if (element.kind === ElementKind.TYPE_VARIABLE) {
+      compiler.unimplemented("visitIs for type variables");
+    }
+    compiler.registerIsCheck(element);
+    LibraryElement coreLibrary = compiler.coreLibrary;
+    ClassElement objectClass = coreLibrary.find(const SourceString('Object'));
+    HInstruction input = node.expression;
+    if (node.nullOk) {
+      beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+      checkNull(input);
+      buffer.add(' || ');
+    }
+
+    if (element === objectClass || element === compiler.dynamicClass) {
+      // The constant folder also does this optimization, but we make
+      // it safe by assuming it may have not run.
+      buffer.add('true');
+    } else if (element == compiler.stringClass) {
+      checkString(input, '===');
+    } else if (element == compiler.doubleClass) {
+      checkDouble(input, '===');
+    } else if (element == compiler.numClass) {
+      checkNum(input, '===');
+    } else if (element == compiler.boolClass) {
+      checkBool(input, '===');
+    } else if (element == compiler.functionClass) {
+      checkFunction(input, element);
+    } else if (element == compiler.intClass) {
+      beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+      checkNum(input, '===');
+      buffer.add(' && ');
+      checkInt(input, '===');
+      endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    } else if (Elements.isStringSupertype(element, compiler)) {
+        handleStringSupertypeCheck(input, element);
+    } else if (element === compiler.listClass
+               || Elements.isListSupertype(element, compiler)) {
+      handleListOrSupertypeCheck(input, element);
+    } else {
+      beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+      checkObject(input, '===');
+      buffer.add(' && ');
+      checkType(input, element);
+      endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+    }
+
+    if (node.nullOk) {
+      endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+    }
+  }
+}
+
+class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
+  SsaOptimizedCodeGenerator(compiler, work, parameters, parameterNames)
+    : super(compiler, work, parameters, parameterNames);
+
+  void beginGraph(HGraph graph) {}
+  void endGraph(HGraph graph) {}
+
+  void bailout(HTypeGuard guard, String reason) {
+    HInstruction input = guard.guarded;
+    Namer namer = compiler.namer;
+    Element element = work.element;
+    buffer.add('return ');
+    if (element.isInstanceMember()) {
+      // TODO(ngeoffray): This does not work in case we come from a
+      // super call. We must make bailout names unique.
+      buffer.add('this.${namer.getBailoutName(element)}');
+    } else {
+      buffer.add(namer.isolateBailoutAccess(element));
+    }
+    int parametersCount = parameterNames.length;
+    buffer.add('($parameters');
+    if (parametersCount != 0) buffer.add(', ');
+    if (guard.guarded is !HParameterValue) {
+      buffer.add('${guard.state}');
+      bool first = true;
+      // TODO(ngeoffray): if the bailout method takes more arguments,
+      // fill the remaining arguments with undefined.
+      // TODO(ngeoffray): try to put a variable at a deterministic
+      // location, so that multiple bailout calls put the variable at
+      // the same parameter index.
+      for (int i = 0; i < guard.inputs.length; i++) {
+        buffer.add(', ');
+        use(guard.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+      }
+    } else {
+      assert(guard.guarded is HParameterValue);
+      buffer.add(' 0');
+    }
+    buffer.add(')');
+  }
+
+  void visitTypeGuard(HTypeGuard node) {
+    addIndentation();
+    HInstruction input = node.guarded;
+    assert(!isGenerateAtUseSite(input) || input.isCodeMotionInvariant());
+    if (node.isInteger()) {
+      buffer.add('if (');
+      checkInt(input, '!==');
+      buffer.add(') ');
+      bailout(node, 'Not an integer');
+    } else if (node.isNumber()) {
+      buffer.add('if (');
+      checkNum(input, '!==');
+      buffer.add(') ');
+      bailout(node, 'Not a number');
+    } else if (node.isBoolean()) {
+      buffer.add('if (');
+      checkBool(input, '!==');
+      buffer.add(') ');
+      bailout(node, 'Not a boolean');
+    } else if (node.isString()) {
+      buffer.add('if (');
+      checkString(input, '!==');
+      buffer.add(') ');
+      bailout(node, 'Not a string');
+    } else if (node.isArray()) {
+      buffer.add('if (');
+      checkObject(input, '!==');
+      buffer.add('||');
+      checkArray(input, '!==');
+      buffer.add(') ');
+      bailout(node, 'Not an array');
+    } else if (node.isStringOrArray()) {
+      buffer.add('if (');
+      checkString(input, '!==');
+      buffer.add(' && (');
+      checkObject(input, '!==');
+      buffer.add('||');
+      checkArray(input, '!==');
+      buffer.add(')) ');
+      bailout(node, 'Not a string or array');
+    } else {
+      unreachable();
+    }
+    buffer.add(';\n');
+  }
+
+  void beginLoop(HBasicBlock block) {
+    addIndentation();
+    for (LabelElement label in block.loopInformation.labels) {
+      writeLabel(label);
+      buffer.add(":");
+    }
+    buffer.add('while (true) {\n');
+    indent++;
+  }
+
+  void endLoop(HBasicBlock block) {
+    indent--;
+    addIndentation();
+    buffer.add('}\n');  // Close 'while' loop.
+  }
+
+  void handleLoopCondition(HLoopBranch node) {
+    buffer.add('if (!');
+    use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(') break;\n');
+  }
+
+  void startIf(HIf node) {
+  }
+
+  void endIf(HIf node) {
+    indent--;
+    addIndentation();
+    buffer.add('}\n');
+  }
+
+  void startThen(HIf node) {
+    addIndentation();
+    buffer.add('if (');
+    use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
+    buffer.add(') {\n');
+    indent++;
+  }
+
+  void endThen(HIf node) {
+  }
+
+  void startElse(HIf node) {
+    indent--;
+    addIndentation();
+    buffer.add('} else {\n');
+    indent++;
+  }
+
+  void endElse(HIf node) {
+  }
+}
+
+class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
+
+  final StringBuffer setup;
+  final List<String> labels;
+  int labelId = 0;
+  int maxBailoutParameters = 0;
+
+  SsaUnoptimizedCodeGenerator(compiler, work, parameters, parameterNames)
+    : super(compiler, work, parameters, parameterNames),
+      setup = new StringBuffer(),
+      labels = <String>[];
+
+  String pushLabel() {
+    String label = 'L${labelId++}';
+    labels.addLast(label);
+    return label;
+  }
+
+  String popLabel() {
+    return labels.removeLast();
+  }
+
+  String currentLabel() {
+    return labels.last();
+  }
+
+  void beginGraph(HGraph graph) {
+    if (!graph.entry.hasGuards()) return;
+    addIndentation();
+    buffer.add('switch (state) {\n');
+    indent++;
+    addIndentation();
+    buffer.add('case 0:\n');
+    indent++;
+
+    // The setup phase of a bailout function sets up the environment for
+    // each bailout target. Each bailout target will populate this
+    // setup phase. It is put at the beginning of the function.
+    setup.add('  switch (state) {\n');
+  }
+
+  void endGraph(HGraph graph) {
+    if (!graph.entry.hasGuards()) return;
+    indent--; // Close original case.
+    indent--;
+    addIndentation();
+    buffer.add('}\n');  // Close 'switch'.
+    setup.add('  }\n');
+  }
+
+  // For instructions that reference a guard or a check, we change that
+  // reference to the instruction they guard against. Therefore, we must
+  // use that instruction when restoring the environment.
+  HInstruction unwrap(HInstruction argument) {
+    if (argument is HIntegerCheck) {
+      HIntegerCheck instruction = argument;
+      return unwrap(instruction.value);
+    } else if (argument is HBoundsCheck) {
+      HBoundsCheck instruction = argument;
+      return unwrap(instruction.index);
+    } else if (argument is HTypeGuard) {
+      HTypeGuard instruction = argument;
+      return unwrap(instruction.guarded);
+    } else {
+      return argument;
+    }
+  }
+
+  void visitTypeGuard(HTypeGuard node) {
+    indent--;
+    addIndentation();
+    buffer.add('case ${node.state}:\n');
+    indent++;
+    addIndentation();
+    buffer.add('state = 0;\n');
+
+    setup.add('    case ${node.state}:\n');
+    int i = 0;
+    for (HInstruction input in node.inputs) {
+      HInstruction instruction = unwrap(input);
+      setup.add('      ${temporary(instruction)} = env$i;\n');
+      i++;
+    }
+    if (i > maxBailoutParameters) maxBailoutParameters = i;
+    setup.add('      break;\n');
+  }
+
+  void startBailoutCase(List<HTypeGuard> bailouts1,
+                        List<HTypeGuard> bailouts2) {
+    indent--;
+    handleBailoutCase(bailouts1);
+    handleBailoutCase(bailouts2);
+    indent++;
+  }
+
+  void handleBailoutCase(List<HTypeGuard> guards) {
+    for (int i = 0, len = guards.length; i < len; i++) {
+      addIndentation();
+      buffer.add('case ${guards[i].state}:\n');
+    }
+  }
+
+  void startBailoutSwitch() {
+    addIndentation();
+    buffer.add('switch (state) {\n');
+    indent++;
+    addIndentation();
+    buffer.add('case 0:\n');
+    indent++;
+  }
+
+  void endBailoutSwitch() {
+    indent--; // Close 'case'.
+    indent--;
+    addIndentation();
+    buffer.add('}\n');  // Close 'switch'.
+  }
+
+
+  void beginLoop(HBasicBlock block) {
+    // TODO(ngeoffray): Don't put labels on loops that don't bailout.
+    String newLabel = pushLabel();
+    if (block.hasGuards()) {
+      startBailoutCase(block.guards, const <HTypeGuard>[]);
+    }
+
+    addIndentation();
+    for (SourceString label in block.loopInformation.labels) {
+      writeLabel(label);
+      buffer.add(":");
+    }
+    buffer.add('$newLabel: while (true) {\n');
+    indent++;
+
+    if (block.hasGuards()) {
+      startBailoutSwitch();
+    }
+  }
+
+  void endLoop(HBasicBlock block) {
+    popLabel();
+    HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
+    if (header.hasGuards()) {
+      endBailoutSwitch();
+    }
+    indent--;
+    addIndentation();
+    buffer.add('}\n');  // Close 'while'.
+  }
+
+  void handleLoopCondition(HLoopBranch node) {
+    buffer.add('if (!');
+    use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
+    buffer.add(') break ${currentLabel()};\n');
+  }
+
+  void startIf(HIf node) {
+    bool hasGuards = node.thenBlock.hasGuards()
+        || (node.hasElse && node.elseBlock.hasGuards());
+    if (hasGuards) {
+      startBailoutCase(node.thenBlock.guards,
+          node.hasElse ? node.elseBlock.guards : const <HTypeGuard>[]);
+    }
+  }
+
+  void endIf(HIf node) {
+    indent--;
+    addIndentation();
+    buffer.add('}\n');
+  }
+
+  void startThen(HIf node) {
+    addIndentation();
+    bool hasGuards = node.thenBlock.hasGuards()
+        || (node.hasElse && node.elseBlock.hasGuards());
+    buffer.add('if (');
+    int precedence = JSPrecedence.EXPRESSION_PRECEDENCE;
+    if (hasGuards) {
+      // TODO(ngeoffray): Put the condition initialization in the
+      // [setup] buffer.
+      List<HTypeGuard> guards = node.thenBlock.guards;
+      for (int i = 0, len = guards.length; i < len; i++) {
+        buffer.add('state == ${guards[i].state} || ');
+      }
+      buffer.add('(state == 0 && ');
+      precedence = JSPrecedence.BITWISE_OR_PRECEDENCE;
+    }
+    use(node.inputs[0], precedence);
+    if (hasGuards) {
+      buffer.add(')');
+    }
+    buffer.add(') {\n');
+    indent++;
+    if (node.thenBlock.hasGuards()) {
+      startBailoutSwitch();
+    }
+  }
+
+  void endThen(HIf node) {
+    if (node.thenBlock.hasGuards()) {
+      endBailoutSwitch();
+    }
+  }
+
+  void startElse(HIf node) {
+    indent--;
+    addIndentation();
+    buffer.add('} else {\n');
+    indent++;
+    if (node.elseBlock.hasGuards()) {
+      startBailoutSwitch();
+    }
+  }
+
+  void endElse(HIf node) {
+    if (node.elseBlock.hasGuards()) {
+      endBailoutSwitch();
+    }
+  }
+}
diff --git a/lib/compiler/implementation/ssa/codegen_helpers.dart b/lib/compiler/implementation/ssa/codegen_helpers.dart
new file mode 100644
index 0000000..2945a05
--- /dev/null
+++ b/lib/compiler/implementation/ssa/codegen_helpers.dart
@@ -0,0 +1,353 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * Instead of emitting each SSA instruction with a temporary variable
+ * mark instructions that can be emitted at their use-site.
+ * For example, in:
+ *   t0 = 4;
+ *   t1 = 3;
+ *   t2 = add(t0, t1);
+ * t0 and t1 would be marked and the resulting code would then be:
+ *   t2 = add(4, 3);
+ */
+class SsaInstructionMerger extends HBaseVisitor {
+  List<HInstruction> expectedInputs;
+  Set<HInstruction> generateAtUseSite;
+
+  SsaInstructionMerger(this.generateAtUseSite);
+
+  void visitGraph(HGraph graph) {
+    visitDominatorTree(graph);
+  }
+
+  bool usedOnlyByPhis(instruction) {
+    for (HInstruction user in instruction.usedBy) {
+      if (user is !HPhi) return false;
+    }
+    return true;
+  }
+
+  void visitInstruction(HInstruction instruction) {
+    // A code motion invariant instruction is dealt before visiting it.
+    assert(!instruction.isCodeMotionInvariant());
+    for (HInstruction input in instruction.inputs) {
+      if (!generateAtUseSite.contains(input)
+          && !input.isCodeMotionInvariant()
+          && input.usedBy.length == 1) {
+        expectedInputs.add(input);
+      }
+    }
+  }
+
+  // The codegen might use the input multiple times, so it must not be
+  // set generate at use site.
+  void visitIs(HIs instruction) {}
+
+  // A check method must not have its input generate at use site,
+  // because it's using it multiple times.
+  void visitCheck(HCheck instruction) {}
+
+  // A type guard should not generate its input at use site, otherwise
+  // they would not be alive.
+  void visitTypeGuard(HTypeGuard instruction) {}
+
+  void tryGenerateAtUseSite(HInstruction instruction) {
+    // A type guard should never be generate at use site, otherwise we
+    // cannot bailout.
+    if (instruction is HTypeGuard) return;
+
+    // A check should never be generate at use site, otherwise we
+    // cannot throw.
+    if (instruction is HCheck) return;
+
+    generateAtUseSite.add(instruction);
+  }
+
+  bool isBlockSinglePredecessor(HBasicBlock block) {
+    return block.successors.length === 1
+        && block.successors[0].predecessors.length === 1;
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    // Compensate from not merging blocks: if the block is the
+    // single predecessor of its single successor, let the successor
+    // visit it.
+    if (isBlockSinglePredecessor(block)) return;
+
+    tryMergingExpressions(block);
+  }
+
+  void tryMergingExpressions(HBasicBlock block) {
+    // Visit each instruction of the basic block in last-to-first order.
+    // Keep a list of expected inputs of the current "expression" being
+    // merged. If instructions occur in the expected order, they are
+    // included in the expression.
+
+    // The expectedInputs list holds non-trivial instructions that may
+    // be generated at their use site, if they occur in the correct order.
+    if (expectedInputs === null) expectedInputs = new List<HInstruction>();
+
+    // Pop instructions from expectedInputs until instruction is found.
+    // Return true if it is found, or false if not.
+    bool findInInputs(HInstruction instruction) {
+      while (!expectedInputs.isEmpty()) {
+        HInstruction nextInput = expectedInputs.removeLast();
+        assert(!generateAtUseSite.contains(nextInput));
+        assert(nextInput.usedBy.length == 1);
+        if (nextInput == instruction) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    block.last.accept(this);
+    for (HInstruction instruction = block.last.previous;
+         instruction !== null;
+         instruction = instruction.previous) {
+      if (generateAtUseSite.contains(instruction)) {
+        continue;
+      }
+      if (instruction.isCodeMotionInvariant()) {
+        generateAtUseSite.add(instruction);
+        continue;
+      }
+      bool foundInInputs = false;
+      // See if the current instruction is the next non-trivial
+      // expected input. If not, drop the expectedInputs and
+      // start over.
+      if (findInInputs(instruction)) {
+        foundInInputs = true;
+        tryGenerateAtUseSite(instruction);
+      } else {
+        assert(expectedInputs.isEmpty());
+      }
+      if (foundInInputs || usedOnlyByPhis(instruction)) {
+        // Try merging all non-trivial inputs.
+        instruction.accept(this);
+      }
+    }
+
+    if (block.predecessors.length === 1
+        && isBlockSinglePredecessor(block.predecessors[0])) {
+      tryMergingExpressions(block.predecessors[0]);
+    } else {
+      expectedInputs = null;
+    }
+  }
+}
+
+/**
+ *  Detect control flow arising from short-circuit logical operators, and
+ *  prepare the program to be generated using these operators instead of
+ *  nested ifs and boolean variables.
+ */
+class SsaConditionMerger extends HGraphVisitor {
+  Set<HInstruction> generateAtUseSite;
+  Map<HPhi, String> logicalOperations;
+
+  SsaConditionMerger(this.generateAtUseSite, this.logicalOperations);
+
+  void visitGraph(HGraph graph) {
+    visitDominatorTree(graph);
+  }
+
+  /**
+   * Returns true if the given instruction is an expression that uses up all
+   * instructions up to the given [limit].
+   *
+   * That is, all instructions starting after the [limit] block (at the branch
+   * leading to the [instruction]) down to the given [instruction] can be
+   * generated at use-site.
+   */
+  bool isExpression(HInstruction instruction, HBasicBlock limit) {
+    if (instruction is HPhi && !logicalOperations.containsKey(instruction)) {
+      return false;
+    }
+    while (instruction.previous != null) {
+      instruction = instruction.previous;
+      if (!generateAtUseSite.contains(instruction)) {
+        return false;
+      }
+    }
+    HBasicBlock block = instruction.block;
+    if (!block.phis.isEmpty()) return false;
+    if (instruction is HPhi && logicalOperations.containsKey(instruction)) {
+      return isExpression(instruction.inputs[0], limit);
+    }
+    return block.predecessors.length == 1 && block.predecessors[0] == limit;
+  }
+
+  void replaceWithLogicalOperator(HPhi phi, String type) {
+    if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi);
+    logicalOperations[phi] = type;
+  }
+
+  bool canGenerateAtUseSite(HPhi phi) {
+    if (phi.usedBy.length != 1) return false;
+    assert(phi.next == null);
+    HInstruction use = phi.usedBy[0];
+
+    HInstruction current = phi.block.first;
+    while (current != use) {
+      if (!generateAtUseSite.contains(current)) return false;
+      if (current.next != null) {
+        current = current.next;
+      } else if (current is HPhi) {
+        current = current.block.first;
+      } else {
+        assert(current is HControlFlow);
+        if (current is !HGoto) return false;
+        HBasicBlock nextBlock = current.block.successors[0];
+        if (!nextBlock.phis.isEmpty()) {
+          current = nextBlock.phis.first;
+        } else {
+          current = nextBlock.first;
+        }
+      }
+    }
+    return true;
+  }
+
+  void detectLogicControlFlow(HPhi phi) {
+    // Check for the most common pattern for a short-circuit logic operation:
+    //   B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1)
+    //   |\
+    //   | B1 b1 = ...; goto B2
+    //   |/
+    //   B2 b2 = phi(b0,b1); if(b2) ...
+    // TODO(lrn): Also recognize ?:-flow?
+
+    if (phi.inputs.length != 2) return;
+    HInstruction first = phi.inputs[0];
+    HBasicBlock firstBlock = first.block;
+    HInstruction second = phi.inputs[1];
+    HBasicBlock secondBlock = second.block;
+    // Check second input of phi being an expression followed by a goto.
+    if (second.usedBy.length != 1) return;
+    HInstruction secondNext =
+        (second is HPhi) ? secondBlock.first : second.next;
+    if (secondNext != secondBlock.last) return;
+    if (secondBlock.last is !HGoto) return;
+    if (secondBlock.successors[0] != phi.block) return;
+    if (!isExpression(second, firstBlock)) return;
+    // Check first input of phi being followed by a (possibly negated)
+    // conditional branch based on the same value.
+    if (firstBlock != phi.block.dominator) return;
+    if (firstBlock.last is !HConditionalBranch) return;
+    HConditionalBranch firstBranch = firstBlock.last;
+    // Must be used both for value and for control to avoid the second branch.
+    if (first.usedBy.length != 2) return;
+    if (firstBlock.successors[1] != phi.block) return;
+    HInstruction firstNext = (first is HPhi) ? firstBlock.first : first.next;
+    if (firstNext == firstBranch &&
+        firstBranch.condition == first) {
+      replaceWithLogicalOperator(phi, "&&");
+    } else if (firstNext is HNot &&
+               firstNext.inputs[0] == first &&
+               generateAtUseSite.contains(firstNext) &&
+               firstNext.next == firstBlock.last &&
+               firstBranch.condition == firstNext) {
+      replaceWithLogicalOperator(phi, "||");
+    } else {
+      return;
+    }
+    // Detected as logic control flow. Mark the corresponding
+    // inputs as generated at use site. These will now be generated
+    // as part of an expression.
+    generateAtUseSite.add(first);
+    generateAtUseSite.add(firstBlock.last);
+    generateAtUseSite.add(second);
+    generateAtUseSite.add(secondBlock.last);
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    if (!block.phis.isEmpty() &&
+        block.phis.first == block.phis.last) {
+      detectLogicControlFlow(block.phis.first);
+    }
+  }
+}
+
+// Precedence information for JavaScript operators.
+class JSPrecedence {
+   // Used as precedence for something that's not even an expression.
+  static final int STATEMENT_PRECEDENCE      = 0;
+  // Precedences of JS operators.
+  static final int EXPRESSION_PRECEDENCE     = 1;
+  static final int ASSIGNMENT_PRECEDENCE     = 2;
+  static final int CONDITIONAL_PRECEDENCE    = 3;
+  static final int LOGICAL_OR_PRECEDENCE     = 4;
+  static final int LOGICAL_AND_PRECEDENCE    = 5;
+  static final int BITWISE_OR_PRECEDENCE     = 6;
+  static final int BITWISE_XOR_PRECEDENCE    = 7;
+  static final int BITWISE_AND_PRECEDENCE    = 8;
+  static final int EQUALITY_PRECEDENCE       = 9;
+  static final int RELATIONAL_PRECEDENCE     = 10;
+  static final int SHIFT_PRECEDENCE          = 11;
+  static final int ADDITIVE_PRECEDENCE       = 12;
+  static final int MULTIPLICATIVE_PRECEDENCE = 13;
+  static final int PREFIX_PRECEDENCE         = 14;
+  static final int POSTFIX_PRECEDENCE        = 15;
+  static final int CALL_PRECEDENCE           = 16;
+  // We never use "new MemberExpression" without arguments, so we can
+  // combine CallExpression and MemberExpression without ambiguity.
+  static final int MEMBER_PRECEDENCE         = CALL_PRECEDENCE;
+  static final int PRIMARY_PRECEDENCE        = 17;
+
+  // The operators that an occur in HBinaryOp.
+  static final Map<String, JSBinaryOperatorPrecedence> binary = const {
+    "||" : const JSBinaryOperatorPrecedence(LOGICAL_OR_PRECEDENCE,
+                                            LOGICAL_AND_PRECEDENCE),
+    "&&" : const JSBinaryOperatorPrecedence(LOGICAL_AND_PRECEDENCE,
+                                            BITWISE_OR_PRECEDENCE),
+    "|" : const JSBinaryOperatorPrecedence(BITWISE_OR_PRECEDENCE,
+                                           BITWISE_XOR_PRECEDENCE),
+    "^" : const JSBinaryOperatorPrecedence(BITWISE_XOR_PRECEDENCE,
+                                           BITWISE_AND_PRECEDENCE),
+    "&" : const JSBinaryOperatorPrecedence(BITWISE_AND_PRECEDENCE,
+                                           EQUALITY_PRECEDENCE),
+    "==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE,
+                                            RELATIONAL_PRECEDENCE),
+    "!=" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE,
+                                            RELATIONAL_PRECEDENCE),
+    "===" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE,
+                                             RELATIONAL_PRECEDENCE),
+    "!==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE,
+                                             RELATIONAL_PRECEDENCE),
+    "<" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE,
+                                           SHIFT_PRECEDENCE),
+    ">" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE,
+                                           SHIFT_PRECEDENCE),
+    "<=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE,
+                                            SHIFT_PRECEDENCE),
+    ">=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE,
+                                            SHIFT_PRECEDENCE),
+    "<<" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE,
+                                            ADDITIVE_PRECEDENCE),
+    ">>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE,
+                                            ADDITIVE_PRECEDENCE),
+    ">>>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE,
+                                             ADDITIVE_PRECEDENCE),
+    "+" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE,
+                                           MULTIPLICATIVE_PRECEDENCE),
+    "-" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE,
+                                           MULTIPLICATIVE_PRECEDENCE),
+    "*" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE,
+                                           PREFIX_PRECEDENCE),
+    "/" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE,
+                                           PREFIX_PRECEDENCE),
+    "%" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE,
+                                           PREFIX_PRECEDENCE),
+  };
+}
+
+class JSBinaryOperatorPrecedence {
+  final int left;
+  final int right;
+  const JSBinaryOperatorPrecedence(this.left, this.right);
+  // All binary operators (excluding assignment) are left associative.
+  int get precedence() => left;
+}
diff --git a/lib/compiler/implementation/ssa/js_names.dart b/lib/compiler/implementation/ssa/js_names.dart
new file mode 100644
index 0000000..2850897
--- /dev/null
+++ b/lib/compiler/implementation/ssa/js_names.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2011, 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.
+
+class JsNames {
+  static final javaScriptKeywords = const <String>[
+    // These are current keywords
+    "break", "delete", "function", "return", "typeof", "case", "do", "if",
+    "switch", "var", "catch", "else", "in", "this", "void", "continue",
+    "false", "instanceof", "throw", "while", "debugger", "finally", "new",
+    "true", "with", "default", "for", "null", "try",
+
+    // These are future keywords
+    "abstract", "double", "goto", "native", "static", "boolean", "enum",
+    "implements", "package", "super", "byte", "export", "import", "private",
+    "synchronized", "char", "extends", "int", "protected", "throws",
+    "class", "final", "interface", "public", "transient", "const", "float",
+    "long", "short", "volatile"
+  ];
+
+  static final reservedGlobalSymbols = const <String>[
+    // Section references are from Ecma-262
+    // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)
+
+    // 15.1.1 Value Properties of the Global Object
+    "NaN", "Infinity", "undefined",
+
+    // 15.1.2 Function Properties of the Global Object
+    "eval", "parseInt", "parseFloat", "isNan", "isFinite",
+
+    // 15.1.3 URI Handling Function Properties
+    "decodeURI", "decodeURIComponent",
+    "encodeURI",
+    "encodeURIComponent",
+
+    // 15.1.4 Constructor Properties of the Global Object
+    "Object", "Function", "Array", "String", "Boolean", "Number", "Date",
+    "RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
+    "SyntaxError", "TypeError", "URIError",
+
+    // 15.1.5 Other Properties of the Global Object
+    "Math",
+
+    // 10.1.6 Activation Object
+    "arguments",
+
+    // B.2 Additional Properties (non-normative)
+    "escape", "unescape",
+
+    // Window props (https://developer.mozilla.org/en/DOM/window)
+    "applicationCache", "closed", "Components", "content", "controllers",
+    "crypto", "defaultStatus", "dialogArguments", "directories",
+    "document", "frameElement", "frames", "fullScreen", "globalStorage",
+    "history", "innerHeight", "innerWidth", "length",
+    "location", "locationbar", "localStorage", "menubar",
+    "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel",
+    "name", "navigator", "opener", "outerHeight", "outerWidth",
+    "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11",
+    "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY",
+    "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar",
+    "top", "window",
+
+    // Window methods (https://developer.mozilla.org/en/DOM/window)
+    "alert", "addEventListener", "atob", "back", "blur", "btoa",
+    "captureEvents", "clearInterval", "clearTimeout", "close", "confirm",
+    "disableExternalCapture", "dispatchEvent", "dump",
+    "enableExternalCapture", "escape", "find", "focus", "forward",
+    "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount",
+    "getComputedStyle", "getSelection", "home", "maximize", "minimize",
+    "moveBy", "moveTo", "open", "openDialog", "postMessage", "print",
+    "prompt", "QueryInterface", "releaseEvents", "removeEventListener",
+    "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy",
+    "scrollByLines", "scrollByPages", "scrollTo", "setInterval",
+    "setResizeable", "setTimeout", "showModalDialog", "sizeToContent",
+    "stop", "uuescape", "updateCommands", "XPCNativeWrapper",
+    "XPCSafeJSOjbectWrapper",
+
+    // Mozilla Window event handlers, same cite
+    "onabort", "onbeforeunload", "onchange", "onclick", "onclose",
+    "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange",
+    "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown",
+    "onmousemove", "onmouseout", "onmouseover", "onmouseup",
+    "onmozorientation", "onpaint", "onreset", "onresize", "onscroll",
+    "onselect", "onsubmit", "onunload",
+
+    // Safari Web Content Guide
+    // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
+    // WebKit Window member data, from WebKit DOM Reference
+    // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
+    "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart",
+    "ongesturestart", "ongesturechange", "ongestureend",
+
+    // extra window methods
+    "uneval",
+
+    // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
+    // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
+    "getPrototypeOf", "let", "yield",
+
+    // "future reserved words"
+    "abstract", "int", "short", "boolean", "interface", "static", "byte",
+    "long", "char", "final", "native", "synchronized", "float", "package",
+    "throws", "goto", "private", "transient", "implements", "protected",
+    "volatile", "double", "public",
+
+    // IE methods
+    // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
+    "attachEvent", "clientInformation", "clipboardData", "createPopup",
+    "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth",
+    "onafterprint", "onbeforedeactivate", "onbeforeprint",
+    "oncontrolselect", "ondeactivate", "onhelp", "onresizeend",
+
+    // Common browser-defined identifiers not defined in ECMAScript
+    "event", "external", "Debug", "Enumerator", "Global", "Image",
+    "ActiveXObject", "VBArray", "Components",
+
+    // Functions commonly defined on Object
+    "toString", "getClass", "constructor", "prototype", "valueOf",
+
+    // Client-side JavaScript identifiers, which are needed for linkers
+    // that don't ensure GWT's window != $wnd, document != $doc, etc.
+    // Taken from the Rhino book, pg 715
+    "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient",
+    "CanvasPattern", "CanvasRenderingContext2D", "CDATASection",
+    "CharacterData", "Comment", "CSS2Properties", "CSSRule",
+    "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType",
+    "DOMException", "DOMImplementation", "DOMParser", "Element", "Event",
+    "ExternalInterface", "FlashPlayer", "Form", "Frame", "History",
+    "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image",
+    "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType",
+    "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin",
+    "ProcessingInstruction", "Range", "RangeException", "Screen", "Select",
+    "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
+    "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
+    "XPathException", "XPathResult", "XSLTProcessor",
+
+    // These keywords trigger the loading of the java-plugin. For the
+    // next-generation plugin, this results in starting a new Java process.
+    "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
+    "JavaArray", "JavaMember",
+
+    // GWT-defined identifiers
+    "\$wnd", "\$doc", "\$entry", "\$moduleName", "\$moduleBase",
+    "\$gwt_version", "\$sessionId",
+
+    // Identifiers used by JsStackEmulator; later set to obfuscatable
+    "\$stack", "\$stackDepth", "\$location",
+
+    // TODO: prove why this is necessary or remove it
+    "call"
+  ];
+
+  static final reservedPropertySymbols =
+      const <String>["__PROTO__", "prototype", "constructor"];
+
+  static Set<String> _reserved;
+
+  static Set<String> get reserved() {
+    if (_reserved === null) {
+      _reserved = new Set<String>();
+      _reserved.addAll(reservedPropertySymbols);
+      _reserved.addAll(reservedGlobalSymbols);
+      _reserved.addAll(javaScriptKeywords);
+    }
+    return _reserved;
+  }
+
+  // TODO(ngeoffray): only the namer should call this method.
+  // Eventually move it there.
+  /*
+   * Returns a name that does not clash with reserved JS keywords,
+   * and also ensures it won't clash with other identifiers.
+   */
+  static String getValid(String name) {
+    if (reserved.contains(name)) {
+      name = '$name\$';
+      assert(!reserved.contains(name));
+    } else if (name.contains(@'$')) {
+      name = name.replaceAll(@'$', @'$$');
+    }
+    return name;
+  }
+}
diff --git a/lib/compiler/implementation/ssa/nodes.dart b/lib/compiler/implementation/ssa/nodes.dart
new file mode 100644
index 0000000..e73473a
--- /dev/null
+++ b/lib/compiler/implementation/ssa/nodes.dart
@@ -0,0 +1,2083 @@
+// Copyright (c) 2011, 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.
+
+interface HVisitor<R> {
+  R visitAdd(HAdd node);
+  R visitBitAnd(HBitAnd node);
+  R visitBitNot(HBitNot node);
+  R visitBitOr(HBitOr node);
+  R visitBitXor(HBitXor node);
+  R visitBoolify(HBoolify node);
+  R visitBoundsCheck(HBoundsCheck node);
+  R visitBreak(HBreak node);
+  R visitConstant(HConstant node);
+  R visitContinue(HContinue node);
+  R visitDivide(HDivide node);
+  R visitEquals(HEquals node);
+  R visitExit(HExit node);
+  R visitFieldGet(HFieldGet node);
+  R visitFieldSet(HFieldSet node);
+  R visitForeign(HForeign node);
+  R visitForeignNew(HForeignNew node);
+  R visitGoto(HGoto node);
+  R visitGreater(HGreater node);
+  R visitGreaterEqual(HGreaterEqual node);
+  R visitIdentity(HIdentity node);
+  R visitIf(HIf node);
+  R visitIndex(HIndex node);
+  R visitIndexAssign(HIndexAssign node);
+  R visitIntegerCheck(HIntegerCheck node);
+  R visitInvokeClosure(HInvokeClosure node);
+  R visitInvokeDynamicGetter(HInvokeDynamicGetter node);
+  R visitInvokeDynamicMethod(HInvokeDynamicMethod node);
+  R visitInvokeDynamicSetter(HInvokeDynamicSetter node);
+  R visitInvokeInterceptor(HInvokeInterceptor node);
+  R visitInvokeStatic(HInvokeStatic node);
+  R visitInvokeSuper(HInvokeSuper node);
+  R visitIs(HIs node);
+  R visitLess(HLess node);
+  R visitLessEqual(HLessEqual node);
+  R visitLiteralList(HLiteralList node);
+  R visitLoopBranch(HLoopBranch node);
+  R visitModulo(HModulo node);
+  R visitMultiply(HMultiply node);
+  R visitNegate(HNegate node);
+  R visitNot(HNot node);
+  R visitParameterValue(HParameterValue node);
+  R visitPhi(HPhi node);
+  R visitReturn(HReturn node);
+  R visitShiftLeft(HShiftLeft node);
+  R visitShiftRight(HShiftRight node);
+  R visitStatic(HStatic node);
+  R visitStaticStore(HStaticStore node);
+  R visitSubtract(HSubtract node);
+  R visitThis(HThis node);
+  R visitThrow(HThrow node);
+  R visitTruncatingDivide(HTruncatingDivide node);
+  R visitTry(HTry node);
+  R visitTypeGuard(HTypeGuard node);
+}
+
+class HGraphVisitor {
+  visitDominatorTree(HGraph graph) {
+    void visitBasicBlockAndSuccessors(HBasicBlock block) {
+      visitBasicBlock(block);
+      List dominated = block.dominatedBlocks;
+      for (int i = 0; i < dominated.length; i++) {
+        visitBasicBlockAndSuccessors(dominated[i]);
+      }
+    }
+
+    visitBasicBlockAndSuccessors(graph.entry);
+  }
+
+  visitPostDominatorTree(HGraph graph) {
+    void visitBasicBlockAndSuccessors(HBasicBlock block) {
+      List dominated = block.dominatedBlocks;
+      for (int i = dominated.length - 1; i >= 0; i--) {
+        visitBasicBlockAndSuccessors(dominated[i]);
+      }
+      visitBasicBlock(block);
+    }
+
+    visitBasicBlockAndSuccessors(graph.entry);
+  }
+
+  abstract visitBasicBlock(HBasicBlock block);
+}
+
+class HInstructionVisitor extends HGraphVisitor {
+  HBasicBlock currentBlock;
+
+  abstract visitInstruction(HInstruction node);
+
+  visitBasicBlock(HBasicBlock node) {
+    void visitInstructionList(HInstructionList list) {
+      HInstruction instruction = list.first;
+      while (instruction !== null) {
+        visitInstruction(instruction);
+        instruction = instruction.next;
+        assert(instruction != list.first);
+      }
+    }
+
+    currentBlock = node;
+    visitInstructionList(node);
+  }
+}
+
+class HGraph {
+  HBasicBlock entry;
+  HBasicBlock exit;
+  final List<HBasicBlock> blocks;
+
+  // We canonicalize all constants used within a graph so we do not
+  // have to worry about them for global value numbering.
+  Map<Constant, HConstant> constants;
+
+  HGraph()
+      : blocks = new List<HBasicBlock>(),
+        constants = new Map<Constant, HConstant>() {
+    entry = addNewBlock();
+    // The exit block will be added later, so it has an id that is
+    // after all others in the system.
+    exit = new HBasicBlock();
+  }
+
+  void addBlock(HBasicBlock block) {
+    int id = blocks.length;
+    block.id = id;
+    blocks.add(block);
+    assert(blocks[id] === block);
+  }
+
+  HBasicBlock addNewBlock() {
+    HBasicBlock result = new HBasicBlock();
+    addBlock(result);
+    return result;
+  }
+
+  HBasicBlock addNewLoopHeaderBlock(List<LabelElement> labels) {
+    HBasicBlock result = addNewBlock();
+    result.loopInformation = new HLoopInformation(result, labels);
+    return result;
+  }
+
+  static HType mapConstantTypeToSsaType(Constant constant) {
+    if (constant.isNull()) return HType.UNKNOWN;
+    if (constant.isBool()) return HType.BOOLEAN;
+    if (constant.isInt()) return HType.INTEGER;
+    if (constant.isDouble()) return HType.DOUBLE;
+    if (constant.isString()) return HType.STRING;
+    if (constant.isList()) return HType.ARRAY;
+    return HType.UNKNOWN;
+  }
+
+  HConstant addConstant(Constant constant) {
+    HConstant result = constants[constant];
+    if (result === null) {
+      HType type = mapConstantTypeToSsaType(constant);
+      result = new HConstant.internal(constant, type);
+      entry.addAtExit(result);
+      constants[constant] = result;
+    }
+    return result;
+  }
+
+  HConstant addConstantInt(int i) {
+    return addConstant(new IntConstant(i));
+  }
+
+  HConstant addConstantDouble(double d) {
+    return addConstant(new DoubleConstant(d));
+  }
+
+  HConstant addConstantString(DartString str) {
+    return addConstant(new StringConstant(str));
+  }
+
+  HConstant addConstantBool(bool value) {
+    return addConstant(new BoolConstant(value));
+  }
+
+  HConstant addConstantNull() {
+    return addConstant(new NullConstant());
+  }
+
+  void finalize() {
+    addBlock(exit);
+    exit.open();
+    exit.close(new HExit());
+    assignDominators();
+  }
+
+  void assignDominators() {
+    // Run through the blocks in order of increasing ids so we are
+    // guaranteed that we have computed dominators for all blocks
+    // higher up in the dominator tree.
+    for (int i = 0, length = blocks.length; i < length; i++) {
+      HBasicBlock block = blocks[i];
+      List<HBasicBlock> predecessors = block.predecessors;
+      if (block.isLoopHeader()) {
+        assert(predecessors.length >= 2);
+        block.assignCommonDominator(predecessors[0]);
+      } else {
+        for (int j = predecessors.length - 1; j >= 0; j--) {
+          block.assignCommonDominator(predecessors[j]);
+        }
+      }
+    }
+  }
+
+  bool isValid() {
+    HValidator validator = new HValidator();
+    validator.visitGraph(this);
+    return validator.isValid;
+  }
+}
+
+class HBaseVisitor extends HGraphVisitor implements HVisitor {
+  HBasicBlock currentBlock;
+
+  visitBasicBlock(HBasicBlock node) {
+    currentBlock = node;
+
+    HInstruction instruction = node.first;
+    while (instruction !== null) {
+      instruction.accept(this);
+      instruction = instruction.next;
+    }
+  }
+
+  visitInstruction(HInstruction instruction) {}
+
+  visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node);
+  visitBinaryBitOp(HBinaryBitOp node) => visitBinaryArithmetic(node);
+  visitInvoke(HInvoke node) => visitInstruction(node);
+  visitInvokeBinary(HInvokeBinary node) => visitInvokeStatic(node);
+  visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node);
+  visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node);
+  visitInvokeUnary(HInvokeUnary node) => visitInvokeStatic(node);
+  visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node);
+  visitControlFlow(HControlFlow node) => visitInstruction(node);
+  visitRelational(HRelational node) => visitInvokeBinary(node);
+
+  visitAdd(HAdd node) => visitBinaryArithmetic(node);
+  visitBitAnd(HBitAnd node) => visitBinaryBitOp(node);
+  visitBitNot(HBitNot node) => visitInvokeUnary(node);
+  visitBitOr(HBitOr node) => visitBinaryBitOp(node);
+  visitBitXor(HBitXor node) => visitBinaryBitOp(node);
+  visitBoolify(HBoolify node) => visitInstruction(node);
+  visitBoundsCheck(HBoundsCheck node) => visitCheck(node);
+  visitBreak(HBreak node) => visitGoto(node);
+  visitContinue(HContinue node) => visitGoto(node);
+  visitCheck(HCheck node) => visitInstruction(node);
+  visitConstant(HConstant node) => visitInstruction(node);
+  visitDivide(HDivide node) => visitBinaryArithmetic(node);
+  visitEquals(HEquals node) => visitRelational(node);
+  visitExit(HExit node) => visitControlFlow(node);
+  visitFieldGet(HFieldGet node) => visitInstruction(node);
+  visitFieldSet(HFieldSet node) => visitInstruction(node);
+  visitForeign(HForeign node) => visitInstruction(node);
+  visitForeignNew(HForeignNew node) => visitForeign(node);
+  visitGoto(HGoto node) => visitControlFlow(node);
+  visitGreater(HGreater node) => visitRelational(node);
+  visitGreaterEqual(HGreaterEqual node) => visitRelational(node);
+  visitIdentity(HIdentity node) => visitRelational(node);
+  visitIf(HIf node) => visitConditionalBranch(node);
+  visitIndex(HIndex node) => visitInvokeStatic(node);
+  visitIndexAssign(HIndexAssign node) => visitInvokeStatic(node);
+  visitIntegerCheck(HIntegerCheck node) => visitCheck(node);
+  visitInvokeClosure(HInvokeClosure node)
+      => visitInvokeDynamic(node);
+  visitInvokeDynamicMethod(HInvokeDynamicMethod node)
+      => visitInvokeDynamic(node);
+  visitInvokeDynamicGetter(HInvokeDynamicGetter node)
+      => visitInvokeDynamicField(node);
+  visitInvokeDynamicSetter(HInvokeDynamicSetter node)
+      => visitInvokeDynamicField(node);
+  visitInvokeInterceptor(HInvokeInterceptor node)
+      => visitInvokeStatic(node);
+  visitInvokeStatic(HInvokeStatic node) => visitInvoke(node);
+  visitInvokeSuper(HInvokeSuper node) => visitInvoke(node);
+  visitLess(HLess node) => visitRelational(node);
+  visitLessEqual(HLessEqual node) => visitRelational(node);
+  visitLiteralList(HLiteralList node) => visitInstruction(node);
+  visitLoopBranch(HLoopBranch node) => visitConditionalBranch(node);
+  visitModulo(HModulo node) => visitBinaryArithmetic(node);
+  visitNegate(HNegate node) => visitInvokeUnary(node);
+  visitNot(HNot node) => visitInstruction(node);
+  visitPhi(HPhi node) => visitInstruction(node);
+  visitMultiply(HMultiply node) => visitBinaryArithmetic(node);
+  visitParameterValue(HParameterValue node) => visitInstruction(node);
+  visitReturn(HReturn node) => visitControlFlow(node);
+  visitShiftRight(HShiftRight node) => visitBinaryBitOp(node);
+  visitShiftLeft(HShiftLeft node) => visitBinaryBitOp(node);
+  visitSubtract(HSubtract node) => visitBinaryArithmetic(node);
+  visitStatic(HStatic node) => visitInstruction(node);
+  visitStaticStore(HStaticStore node) => visitInstruction(node);
+  visitThis(HThis node) => visitParameterValue(node);
+  visitThrow(HThrow node) => visitControlFlow(node);
+  visitTry(HTry node) => visitControlFlow(node);
+  visitTruncatingDivide(HTruncatingDivide node) => visitBinaryArithmetic(node);
+  visitTypeGuard(HTypeGuard node) => visitInstruction(node);
+  visitIs(HIs node) => visitInstruction(node);
+}
+
+class SubGraph {
+  // The first and last block of the sub-graph.
+  final HBasicBlock start;
+  final HBasicBlock end;
+
+  const SubGraph(this.start, this.end);
+
+  bool contains(HBasicBlock block) {
+    assert(start !== null);
+    assert(end !== null);
+    assert(block !== null);
+    return start.id <= block.id && block.id <= end.id;
+  }
+}
+
+class HInstructionList {
+  HInstruction first = null;
+  HInstruction last = null;
+
+  bool isEmpty() {
+    return first === null;
+  }
+
+  void addAfter(HInstruction cursor, HInstruction instruction) {
+    if (cursor === null) {
+      assert(isEmpty());
+      first = last = instruction;
+    } else if (cursor === last) {
+      last.next = instruction;
+      instruction.previous = last;
+      last = instruction;
+    } else {
+      instruction.previous = cursor;
+      instruction.next = cursor.next;
+      cursor.next.previous = instruction;
+      cursor.next = instruction;
+    }
+  }
+
+  void addBefore(HInstruction cursor, HInstruction instruction) {
+    if (cursor === null) {
+      assert(isEmpty());
+      first = last = instruction;
+    } else if (cursor === first) {
+      first.previous = instruction;
+      instruction.next = first;
+      first = instruction;
+    } else {
+      instruction.next = cursor;
+      instruction.previous = cursor.previous;
+      cursor.previous.next = instruction;
+      cursor.previous = instruction;
+    }
+  }
+
+  void detach(HInstruction instruction) {
+    assert(contains(instruction));
+    assert(instruction.isInBasicBlock());
+    if (instruction.previous === null) {
+      first = instruction.next;
+    } else {
+      instruction.previous.next = instruction.next;
+    }
+    if (instruction.next === null) {
+      last = instruction.previous;
+    } else {
+      instruction.next.previous = instruction.previous;
+    }
+    instruction.previous = null;
+    instruction.next = null;
+  }
+
+  void remove(HInstruction instruction) {
+    assert(instruction.usedBy.isEmpty());
+    detach(instruction);
+  }
+
+  /** Linear search for [instruction]. */
+  bool contains(HInstruction instruction) {
+    HInstruction cursor = first;
+    while (cursor != null) {
+      if (cursor === instruction) return true;
+      cursor = cursor.next;
+    }
+    return false;
+  }
+}
+
+class HBasicBlock extends HInstructionList implements Hashable {
+  // The [id] must be such that any successor's id is greater than
+  // this [id]. The exception are back-edges.
+  int id;
+
+  static final int STATUS_NEW = 0;
+  static final int STATUS_OPEN = 1;
+  static final int STATUS_CLOSED = 2;
+  int status = STATUS_NEW;
+
+  HInstructionList phis;
+
+  HLoopInformation loopInformation = null;
+  HLabeledBlockInformation labeledBlockInformation = null;
+  HBasicBlock parentLoopHeader = null;
+  List<HTypeGuard> guards;
+
+  final List<HBasicBlock> predecessors;
+  List<HBasicBlock> successors;
+
+  HBasicBlock dominator = null;
+  final List<HBasicBlock> dominatedBlocks;
+
+  HBasicBlock() : this.withId(null);
+  HBasicBlock.withId(this.id)
+      : phis = new HInstructionList(),
+        predecessors = <HBasicBlock>[],
+        successors = const <HBasicBlock>[],
+        dominatedBlocks = <HBasicBlock>[],
+        guards = <HTypeGuard>[];
+
+  int hashCode() => id;
+
+  bool isNew() => status == STATUS_NEW;
+  bool isOpen() => status == STATUS_OPEN;
+  bool isClosed() => status == STATUS_CLOSED;
+
+  bool isLoopHeader() => loopInformation !== null;
+  bool hasLabeledBlockInformation() => labeledBlockInformation !== null;
+
+  bool hasGuards() => !guards.isEmpty();
+
+  void open() {
+    assert(isNew());
+    status = STATUS_OPEN;
+  }
+
+  void close(HControlFlow end) {
+    assert(isOpen());
+    addAfter(last, end);
+    status = STATUS_CLOSED;
+  }
+
+  // TODO(kasperl): I really don't want to pass the compiler into this
+  // method. Maybe we need a better logging framework.
+  void printToCompiler(Compiler compiler) {
+    HInstruction instruction = first;
+    while (instruction != null) {
+      int instructionId = instruction.id;
+      String inputsAsString = instruction.inputsToString();
+      compiler.log('$instructionId: $instruction $inputsAsString');
+      instruction = instruction.next;
+    }
+  }
+
+  void addAtEntry(HInstruction instruction) {
+    assert(isClosed());
+    assert(instruction is !HPhi);
+    super.addBefore(first, instruction);
+    instruction.notifyAddedToBlock(this);
+  }
+
+  void addAtExit(HInstruction instruction) {
+    assert(isClosed());
+    assert(last is HControlFlow);
+    assert(instruction is !HPhi);
+    super.addBefore(last, instruction);
+    instruction.notifyAddedToBlock(this);
+  }
+
+  void moveAtExit(HInstruction instruction) {
+    assert(instruction is !HPhi);
+    assert(instruction.isInBasicBlock());
+    assert(isClosed());
+    assert(last is HControlFlow);
+    super.addBefore(last, instruction);
+    instruction.block = this;
+    assert(isValid());
+  }
+
+  void add(HInstruction instruction) {
+    assert(instruction is !HControlFlow);
+    assert(instruction is !HPhi);
+    super.addAfter(last, instruction);
+    instruction.notifyAddedToBlock(this);
+  }
+
+  void addPhi(HPhi phi) {
+    phis.addAfter(phis.last, phi);
+    phi.notifyAddedToBlock(this);
+  }
+
+  void removePhi(HPhi phi) {
+    phis.remove(phi);
+    phi.notifyRemovedFromBlock(this);
+  }
+
+  void addAfter(HInstruction cursor, HInstruction instruction) {
+    assert(cursor is !HPhi);
+    assert(instruction is !HPhi);
+    assert(isOpen() || isClosed());
+    super.addAfter(cursor, instruction);
+    instruction.notifyAddedToBlock(this);
+  }
+
+  void addBefore(HInstruction cursor, HInstruction instruction) {
+    assert(cursor is !HPhi);
+    assert(instruction is !HPhi);
+    assert(isOpen() || isClosed());
+    super.addBefore(cursor, instruction);
+    instruction.notifyAddedToBlock(this);
+  }
+
+  void remove(HInstruction instruction) {
+    assert(isOpen() || isClosed());
+    assert(instruction is !HPhi);
+    super.remove(instruction);
+    instruction.notifyRemovedFromBlock(this);
+  }
+
+  void addSuccessor(HBasicBlock block) {
+    // Forward branches are only allowed to new blocks.
+    assert(isClosed() && (block.isNew() || block.id < id));
+    if (successors.isEmpty()) {
+      successors = [block];
+    } else {
+      successors.add(block);
+    }
+    block.predecessors.add(this);
+  }
+
+  void postProcessLoopHeader() {
+    assert(isLoopHeader());
+    // Only the first entry into the loop is from outside the
+    // loop. All other entries must be back edges.
+    for (int i = 1, length = predecessors.length; i < length; i++) {
+      loopInformation.addBackEdge(predecessors[i]);
+    }
+  }
+
+  /**
+   * Rewrites all uses of the [from] instruction to using the [to]
+   * instruction instead.
+   */
+  void rewrite(HInstruction from, HInstruction to) {
+    for (HInstruction use in from.usedBy) {
+      rewriteInput(use, from, to);
+    }
+    to.usedBy.addAll(from.usedBy);
+    from.usedBy.clear();
+  }
+
+  static void rewriteInput(HInstruction instruction,
+                           HInstruction from,
+                           HInstruction to) {
+    List inputs = instruction.inputs;
+    for (int i = 0; i < inputs.length; i++) {
+      if (inputs[i] === from) inputs[i] = to;
+    }
+  }
+
+  bool isExitBlock() {
+    return first === last && first is HExit;
+  }
+
+  void addDominatedBlock(HBasicBlock block) {
+    assert(isClosed());
+    assert(id !== null && block.id !== null);
+    assert(dominatedBlocks.indexOf(block) < 0);
+    // Keep the list of dominated blocks sorted such that if there are two
+    // succeeding blocks in the list, the predecessor is before the successor.
+    // Assume that we add the dominated blocks in the right order.
+    int index = dominatedBlocks.length;
+    while (index > 0 && dominatedBlocks[index - 1].id > block.id) {
+      index--;
+    }
+    if (index == dominatedBlocks.length) {
+      dominatedBlocks.add(block);
+    } else {
+      dominatedBlocks.insertRange(index, 1, block);
+    }
+    assert(block.dominator === null);
+    block.dominator = this;
+  }
+
+  void removeDominatedBlock(HBasicBlock block) {
+    assert(isClosed());
+    assert(id !== null && block.id !== null);
+    int index = dominatedBlocks.indexOf(block);
+    assert(index >= 0);
+    if (index == dominatedBlocks.length - 1) {
+      dominatedBlocks.removeLast();
+    } else {
+      dominatedBlocks.removeRange(index, 1);
+    }
+    assert(block.dominator === this);
+    block.dominator = null;
+  }
+
+  void assignCommonDominator(HBasicBlock predecessor) {
+    assert(isClosed());
+    if (dominator === null) {
+      // If this basic block doesn't have a dominator yet we use the
+      // given predecessor as the dominator.
+      predecessor.addDominatedBlock(this);
+    } else if (predecessor.dominator !== null) {
+      // If the predecessor has a dominator and this basic block has a
+      // dominator, we find a common parent in the dominator tree and
+      // use that as the dominator.
+      HBasicBlock first = dominator;
+      HBasicBlock second = predecessor;
+      while (first !== second) {
+        if (first.id > second.id) {
+          first = first.dominator;
+        } else {
+          second = second.dominator;
+        }
+        assert(first !== null && second !== null);
+      }
+      if (dominator !== first) {
+        dominator.removeDominatedBlock(this);
+        first.addDominatedBlock(this);
+      }
+    }
+  }
+
+  void forEachPhi(void f(HPhi phi)) {
+    HPhi current = phis.first;
+    while (current !== null) {
+      f(current);
+      current = current.next;
+    }
+  }
+
+  bool isValid() {
+    assert(isClosed());
+    HValidator validator = new HValidator();
+    validator.visitBasicBlock(this);
+    return validator.isValid;
+  }
+}
+
+class HLabeledBlockInformation {
+  final SubGraph body;
+  final HBasicBlock joinBlock;
+  final List<LabelElement> labels;
+  final TargetElement target;
+  final bool isContinue;
+
+  HLabeledBlockInformation(this.body, this.joinBlock,
+                           List<LabelElement> labels,
+                           [this.isContinue = false]) :
+      this.labels = labels, this.target = labels[0].target;
+
+  HLabeledBlockInformation.implicit(this.body,
+                                    this.joinBlock,
+                                    this.target,
+                                    [this.isContinue = false])
+      : this.labels = const<LabelElement>[];
+}
+
+class HLoopInformation {
+  final HBasicBlock header;
+  final List<HBasicBlock> blocks;
+  final List<HBasicBlock> backEdges;
+  final List<LabelElement> labels;
+
+  HLoopInformation(this.header, this.labels)
+      : blocks = new List<HBasicBlock>(),
+        backEdges = new List<HBasicBlock>();
+
+  void addBackEdge(HBasicBlock predecessor) {
+    backEdges.add(predecessor);
+    addBlock(predecessor);
+  }
+
+  // Adds a block and transitively all its predecessors in the loop as
+  // loop blocks.
+  void addBlock(HBasicBlock block) {
+    if (block === header) return;
+    HBasicBlock parentHeader = block.parentLoopHeader;
+    if (parentHeader === header) {
+      // Nothing to do in this case.
+    } else if (parentHeader !== null) {
+      addBlock(parentHeader);
+    } else {
+      block.parentLoopHeader = header;
+      blocks.add(block);
+      for (int i = 0, length = block.predecessors.length; i < length; i++) {
+        addBlock(block.predecessors[i]);
+      }
+    }
+  }
+
+  HBasicBlock getLastBackEdge() {
+    int maxId = -1;
+    HBasicBlock result = null;
+    for (int i = 0, length = backEdges.length; i < length; i++) {
+      HBasicBlock current = backEdges[i];
+      if (current.id > maxId) {
+        maxId = current.id;
+        result = current;
+      }
+    }
+    return result;
+  }
+}
+
+class HType {
+  final int flag;
+  const HType(int this.flag);
+
+  static final int FLAG_CONFLICTING = 0;
+  static final int FLAG_UNKNOWN = 1;
+  static final int FLAG_BOOLEAN = FLAG_UNKNOWN << 1;
+  static final int FLAG_INTEGER = FLAG_BOOLEAN << 1;
+  static final int FLAG_STRING = FLAG_INTEGER << 1;
+  static final int FLAG_ARRAY = FLAG_STRING << 1;
+  static final int FLAG_DOUBLE = FLAG_ARRAY << 1;
+
+  static final HType CONFLICTING = const HType(FLAG_CONFLICTING);
+  static final HType UNKNOWN = const HType(FLAG_UNKNOWN);
+  static final HType BOOLEAN = const HType(FLAG_BOOLEAN);
+  static final HType STRING = const HType(FLAG_STRING);
+  static final HType ARRAY = const HType(FLAG_ARRAY);
+  static final HType INTEGER = const HType(FLAG_INTEGER);
+  static final HType DOUBLE = const HType(FLAG_DOUBLE);
+  static final HType STRING_OR_ARRAY = const HType(FLAG_STRING | FLAG_ARRAY);
+  static final HType NUMBER = const HType(FLAG_DOUBLE | FLAG_INTEGER);
+
+  bool isConflicting() => this === CONFLICTING;
+  bool isUnknown() => this === UNKNOWN;
+  bool isBoolean() => this === BOOLEAN;
+  bool isInteger() => this === INTEGER;
+  bool isDouble() => this === DOUBLE;
+  bool isString() => this === STRING;
+  bool isArray() => this === ARRAY;
+  bool isNumber() => (this.flag & (FLAG_INTEGER | FLAG_DOUBLE)) != 0;
+  bool isStringOrArray() => (this.flag & (FLAG_STRING | FLAG_ARRAY)) != 0;
+  bool isKnown() => this !== UNKNOWN && this !== CONFLICTING;
+
+  static HType getTypeFromFlag(int flag) {
+    if (flag === CONFLICTING.flag) return CONFLICTING;
+    if (flag === UNKNOWN.flag) return UNKNOWN;
+    if (flag === BOOLEAN.flag) return BOOLEAN;
+    if (flag === INTEGER.flag) return INTEGER;
+    if (flag === DOUBLE.flag) return DOUBLE;
+    if (flag === STRING.flag) return STRING;
+    if (flag === ARRAY.flag) return ARRAY;
+    if (flag === NUMBER.flag) return NUMBER;
+    if (flag === STRING_OR_ARRAY.flag) return STRING_OR_ARRAY;
+    assert(false);
+  }
+
+  HType combine(HType other) {
+    if (isUnknown()) return other;
+    if (other.isUnknown()) return this;
+    return getTypeFromFlag(this.flag & other.flag);
+  }
+}
+
+class HInstruction implements Hashable {
+  final int id;
+  static int idCounter;
+
+  final List<HInstruction> inputs;
+  final List<HInstruction> usedBy;
+
+  HBasicBlock block;
+  HInstruction previous = null;
+  HInstruction next = null;
+  int flags = 0;
+  HType type = HType.UNKNOWN;
+
+  // Changes flags.
+  static final int FLAG_CHANGES_SOMETHING    = 0;
+  static final int FLAG_CHANGES_COUNT        = FLAG_CHANGES_SOMETHING + 1;
+
+  // Depends flags (one for each changes flag).
+  static final int FLAG_DEPENDS_ON_SOMETHING = FLAG_CHANGES_COUNT;
+
+  // Other flags.
+  static final int FLAG_USE_GVN              = FLAG_DEPENDS_ON_SOMETHING + 1;
+
+  HInstruction(this.inputs) : id = idCounter++, usedBy = <HInstruction>[];
+
+  int hashCode() => id;
+
+  bool getFlag(int position) => (flags & (1 << position)) != 0;
+  void setFlag(int position) { flags |= (1 << position); }
+  void clearFlag(int position) { flags &= ~(1 << position); }
+
+  static int computeDependsOnFlags(int flags) => flags << FLAG_CHANGES_COUNT;
+
+  int getChangesFlags() => flags & ((1 << FLAG_CHANGES_COUNT) - 1);
+  bool hasSideEffects() => getChangesFlags() != 0;
+  void prepareGvn() { setAllSideEffects(); }
+
+  void setAllSideEffects() { flags |= ((1 << FLAG_CHANGES_COUNT) - 1); }
+  void clearAllSideEffects() { flags &= ~((1 << FLAG_CHANGES_COUNT) - 1); }
+
+  bool useGvn() => getFlag(FLAG_USE_GVN);
+  void setUseGvn() { setFlag(FLAG_USE_GVN); }
+
+  bool isArray() => type.isArray();
+  bool isBoolean() => type.isBoolean();
+  bool isInteger() => type.isInteger();
+  bool isNumber() => type.isNumber();
+  bool isString() => type.isString();
+  bool isTypeUnknown() => type.isUnknown();
+  bool isStringOrArray() => type.isStringOrArray();
+
+  // Compute the type of the instruction.
+  HType computeType() => HType.UNKNOWN;
+
+  HType computeDesiredType() {
+    HType candidateType = HType.UNKNOWN;
+    for (final user in usedBy) {
+      HType desiredType = user.computeDesiredInputType(this);
+      if (candidateType.isUnknown()) {
+        candidateType = desiredType;
+      } else if (!type.isUnknown() && candidateType != desiredType) {
+        candidateType = candidateType.combine(desiredType);
+        if (!candidateType.isKnown()) {
+          candidateType = HType.UNKNOWN;
+          break;
+        }
+      }
+    }
+    return candidateType;
+  }
+
+  HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
+
+  // Returns whether the instruction does produce the type it claims.
+  // For most instructions, this returns false. A type guard will be
+  // inserted to make sure the users get the right type in.
+  bool hasExpectedType() => false;
+
+  // Re-compute and update the type of the instruction. Returns
+  // whether or not the type was changed.
+  bool updateType() {
+    if (type.isConflicting()) return false;
+    HType newType = computeType();
+    HType desiredType = computeDesiredType();
+    HType combined = newType.combine(desiredType);
+    if (combined.isKnown()) newType = combined;
+
+    bool changed = (type != newType);
+    if (type.isUnknown()) {
+      type = newType;
+      return changed;
+    } else if (changed) {
+      type = type.combine(newType);
+      return changed;
+    }
+    return false;
+  }
+
+  bool isInBasicBlock() => block !== null;
+
+  String inputsToString() {
+    void addAsCommaSeparated(StringBuffer buffer, List<HInstruction> list) {
+      for (int i = 0; i < list.length; i++) {
+        if (i != 0) buffer.add(', ');
+        buffer.add("@${list[i].id}");
+      }
+    }
+
+    StringBuffer buffer = new StringBuffer();
+    buffer.add('(');
+    addAsCommaSeparated(buffer, inputs);
+    buffer.add(') - used at [');
+    addAsCommaSeparated(buffer, usedBy);
+    buffer.add(']');
+    return buffer.toString();
+  }
+
+  bool gvnEquals(HInstruction other) {
+    assert(useGvn() && other.useGvn());
+    // Check that the type and the flags match.
+    bool hasSameType = typeEquals(other);
+    assert(hasSameType == (typeCode() == other.typeCode()));
+    if (!hasSameType) return false;
+    if (flags != other.flags) return false;
+    // Check that the inputs match.
+    final int inputsLength = inputs.length;
+    final List<HInstruction> otherInputs = other.inputs;
+    if (inputsLength != otherInputs.length) return false;
+    for (int i = 0; i < inputsLength; i++) {
+      if (inputs[i] !== otherInputs[i]) return false;
+    }
+    // Check that the data in the instruction matches.
+    return dataEquals(other);
+  }
+
+  int gvnHashCode() {
+    int result = typeCode();
+    int length = inputs.length;
+    for (int i = 0; i < length; i++) {
+      result = (result * 19) + (inputs[i].id) + (result >> 7);
+    }
+    return result;
+  }
+
+  // These methods should be overwritten by instructions that
+  // participate in global value numbering.
+  int typeCode() => -1;
+  bool typeEquals(HInstruction other) => false;
+  bool dataEquals(HInstruction other) => false;
+
+  abstract accept(HVisitor visitor);
+
+  void notifyAddedToBlock(HBasicBlock block) {
+    assert(!isInBasicBlock());
+    assert(this.block === null);
+    // Add [this] to the inputs' uses.
+    for (int i = 0; i < inputs.length; i++) {
+      assert(inputs[i].isInBasicBlock());
+      inputs[i].usedBy.add(this);
+    }
+    this.block = block;
+    assert(isValid());
+  }
+
+  void notifyRemovedFromBlock(HBasicBlock block) {
+    assert(isInBasicBlock());
+    assert(usedBy.isEmpty());
+    assert(this.block === block);
+
+    // Remove [this] from the inputs' uses.
+    for (int i = 0; i < inputs.length; i++) {
+      List inputUsedBy = inputs[i].usedBy;
+      for (int j = 0; j < inputUsedBy.length; j++) {
+        if (inputUsedBy[j] === this) {
+          inputUsedBy[j] = inputUsedBy[inputUsedBy.length - 1];
+          inputUsedBy.removeLast();
+          break;
+        }
+      }
+    }
+    this.block = null;
+    assert(isValid());
+  }
+
+  bool isConstant() => false;
+  bool isConstantNull() => false;
+  bool isConstantNumber() => false;
+  bool isConstantString() => false;
+
+  bool isValid() {
+    HValidator validator = new HValidator();
+    validator.currentBlock = block;
+    validator.visitInstruction(this);
+    return validator.isValid;
+  }
+
+  /**
+   * The code for computing a bailout environment, and the code
+   * generation must agree on what does not need to be captured,
+   * so should always be generated at use site.
+   */
+  bool isCodeMotionInvariant() => false;
+}
+
+class HBoolify extends HInstruction {
+  HBoolify(HInstruction value) : super(<HInstruction>[value]);
+  void prepareGvn() {
+    assert(!hasSideEffects());
+    setUseGvn();
+  }
+
+  HType computeType() => HType.BOOLEAN;
+  bool hasExpectedType() => true;
+
+  accept(HVisitor visitor) => visitor.visitBoolify(this);
+  int typeCode() => 0;
+  bool typeEquals(other) => other is HBoolify;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HCheck extends HInstruction {
+  HCheck(inputs) : super(inputs);
+
+  // TODO(floitsch): make class abstract instead of adding an abstract method.
+  abstract accept(HVisitor visitor);
+}
+
+class HTypeGuard extends HInstruction {
+  int state;
+  HTypeGuard(int this.state, List<HInstruction> env) : super(env);
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+    setUseGvn();
+  }
+
+  HInstruction get guarded() => inputs.last();
+
+  HType computeType() => type;
+  bool hasExpectedType() => true;
+
+  accept(HVisitor visitor) => visitor.visitTypeGuard(this);
+  int typeCode() => 1;
+  bool typeEquals(other) => other is HTypeGuard;
+  bool dataEquals(HTypeGuard other) => type == other.type;
+}
+
+class HBoundsCheck extends HCheck {
+  HBoundsCheck(length, index) : super(<HInstruction>[length, index]) {
+    type = HType.INTEGER;
+  }
+
+  HInstruction get length() => inputs[0];
+  HInstruction get index() => inputs[1];
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+    setUseGvn();
+  }
+
+  HType computeType() => HType.INTEGER;
+  bool hasExpectedType() => true;
+
+  accept(HVisitor visitor) => visitor.visitBoundsCheck(this);
+  int typeCode() => 2;
+  bool typeEquals(other) => other is HBoundsCheck;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HIntegerCheck extends HCheck {
+  HIntegerCheck(value) : super(<HInstruction>[value]);
+
+  HInstruction get value() => inputs[0];
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+    setUseGvn();
+  }
+
+  HType computeType() => HType.INTEGER;
+  bool hasExpectedType() => true;
+
+  accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
+  int typeCode() => 3;
+  bool typeEquals(other) => other is HIntegerCheck;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HConditionalBranch extends HControlFlow {
+  HConditionalBranch(inputs) : super(inputs);
+  HInstruction get condition() => inputs[0];
+  HBasicBlock get trueBranch() => block.successors[0];
+  HBasicBlock get falseBranch() => block.successors[1];
+  abstract toString();
+}
+
+class HControlFlow extends HInstruction {
+  HControlFlow(inputs) : super(inputs);
+  abstract toString();
+}
+
+class HInvoke extends HInstruction {
+  /**
+    * The first argument must be the target: either an [HStatic] node, or
+    * the receiver of a method-call. The remaining inputs are the arguments
+    * to the invocation.
+    */
+  final Selector selector;
+  HInvoke(Selector this.selector, List<HInstruction> inputs) : super(inputs);
+  static final int ARGUMENTS_OFFSET = 1;
+
+  // TODO(floitsch): make class abstract instead of adding an abstract method.
+  abstract accept(HVisitor visitor);
+}
+
+class HInvokeDynamic extends HInvoke {
+  SourceString name;
+  HInvokeDynamic(Selector selector, this.name, List<HInstruction> inputs)
+      : super(selector, inputs);
+  toString() => 'invoke dynamic: $name';
+  HInstruction get receiver() => inputs[0];
+
+  // TODO(floitsch): make class abstract instead of adding an abstract method.
+  abstract accept(HVisitor visitor);
+}
+
+class HInvokeClosure extends HInvokeDynamic {
+  Element element;
+  HInvokeClosure(Selector selector, List<HInstruction> inputs)
+    : super(selector, const SourceString('call'), inputs);
+  accept(HVisitor visitor) => visitor.visitInvokeClosure(this);
+}
+
+class HInvokeDynamicMethod extends HInvokeDynamic {
+  HInvokeDynamicMethod(Selector selector,
+                       SourceString methodName,
+                       List<HInstruction> inputs)
+    : super(selector, methodName, inputs);
+  toString() => 'invoke dynamic method: $name';
+  accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this);
+}
+
+class HInvokeDynamicField extends HInvokeDynamic {
+  Element element;
+  HInvokeDynamicField(Selector selector,
+                      Element this.element,
+                      SourceString name,
+                      List<HInstruction>inputs)
+      : super(selector, name, inputs);
+  toString() => 'invoke dynamic field: $name';
+
+  // TODO(floitsch): make class abstract instead of adding an abstract method.
+  abstract accept(HVisitor visitor);
+}
+
+class HInvokeDynamicGetter extends HInvokeDynamicField {
+  HInvokeDynamicGetter(selector, element, name, receiver)
+    : super(selector, element, name, [receiver]);
+  toString() => 'invoke dynamic getter: $name';
+  accept(HVisitor visitor) => visitor.visitInvokeDynamicGetter(this);
+}
+
+class HInvokeDynamicSetter extends HInvokeDynamicField {
+  HInvokeDynamicSetter(selector, element, name, receiver, value)
+    : super(selector, element, name, [receiver, value]);
+  toString() => 'invoke dynamic setter: $name';
+  accept(HVisitor visitor) => visitor.visitInvokeDynamicSetter(this);
+}
+
+class HInvokeStatic extends HInvoke {
+  /** The first input must be the target. */
+  HInvokeStatic(selector, inputs) : super(selector, inputs);
+  toString() => 'invoke static: ${element.name}';
+  accept(HVisitor visitor) => visitor.visitInvokeStatic(this);
+  Element get element() => target.element;
+  HStatic get target() => inputs[0];
+
+  bool isArrayConstructor() {
+    // TODO(ngeoffray): This is not the right way to do the check,
+    // nor the right place. We need to move it to a phase.
+    return (element.isFactoryConstructor()
+        && element.enclosingElement.name.slowToString() == 'List');
+  }
+
+  HType computeType() {
+    if (isArrayConstructor()) {
+      return HType.ARRAY;
+    }
+    return HType.UNKNOWN;
+  }
+
+  bool get builtin() => isArrayConstructor();
+  bool hasExpectedType() => isArrayConstructor();
+}
+
+class HInvokeSuper extends HInvokeStatic {
+  HInvokeSuper(selector, inputs) : super(selector, inputs);
+  toString() => 'invoke super: ${element.name}';
+  accept(HVisitor visitor) => visitor.visitInvokeSuper(this);
+}
+
+class HInvokeInterceptor extends HInvokeStatic {
+  final SourceString name;
+  final bool getter;
+
+  HInvokeInterceptor(Selector selector,
+                     SourceString this.name,
+                     bool this.getter,
+                     List<HInstruction> inputs)
+      : super(selector, inputs);
+  toString() => 'invoke interceptor: ${element.name}';
+  accept(HVisitor visitor) => visitor.visitInvokeInterceptor(this);
+
+  
+  String get builtinJsName() {
+    if (getter
+        && name == const SourceString('length')
+        && inputs[1].isStringOrArray()) {
+      return 'length';
+    } else if (name == const SourceString('add') && inputs[1].isArray()) {
+      return 'push';
+    } else if (name == const SourceString('removeLast')
+               && inputs[1].isArray()) {
+      return 'pop';
+    }
+    return null;
+  }
+
+  HType computeType() {
+    if (getter
+        && name == const SourceString('length')
+        && inputs[1].isStringOrArray()) {
+      return HType.INTEGER;
+    }
+    return HType.UNKNOWN;
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    if (input == inputs[0]) return HType.UNKNOWN;
+    if (input == inputs[1] && input.isStringOrArray()) {
+      if (name == const SourceString('add')
+          || name == const SourceString('removeLast')) {
+        return HType.ARRAY;
+      }
+    }
+    return HType.UNKNOWN;
+  }
+
+  bool hasExpectedType() => builtinJsName != null;
+
+  void prepareGvn() {
+    if (builtinJsName == 'length') {
+      clearAllSideEffects();
+    } else {
+      setAllSideEffects();
+    }
+  }
+
+  int typeCode() => 4;
+  bool typeEquals(other) => other is HInvokeInterceptor;
+  bool dataEquals(HInvokeInterceptor other) {
+    return builtinJsName == other.builtinJsName && name == other.name;
+  }
+}
+
+class HFieldGet extends HInstruction {
+  final Element element;
+  HFieldGet(Element this.element, HInstruction receiver)
+      : super(<HInstruction>[receiver]);
+  HFieldGet.fromActivation(Element this.element) : super(<HInstruction>[]);
+
+  HInstruction get receiver() => inputs.length == 1 ? inputs[0] : null;
+  accept(HVisitor visitor) => visitor.visitFieldGet(this);
+}
+
+class HFieldSet extends HInstruction {
+  final Element element;
+  HFieldSet(Element this.element, HInstruction receiver, HInstruction value)
+      : super(<HInstruction>[receiver, value]);
+  HFieldSet.fromActivation(Element this.element, HInstruction value)
+      : super(<HInstruction>[value]);
+
+  HInstruction get receiver() => inputs.length == 2 ? inputs[0] : null;
+  HInstruction get value() => inputs.length == 2 ? inputs[1] : inputs[0];
+  accept(HVisitor visitor) => visitor.visitFieldSet(this);
+
+  void prepareGvn() {
+    // TODO(ngeoffray): implement more fine grain side effects.
+    setAllSideEffects();
+  }
+}
+
+class HForeign extends HInstruction {
+  final DartString code;
+  final DartString declaredType;
+  HForeign(this.code, this.declaredType, List<HInstruction> inputs)
+      : super(inputs);
+  accept(HVisitor visitor) => visitor.visitForeign(this);
+
+  HType computeType() {
+    if (declaredType.slowToString() == 'bool') return HType.BOOLEAN;
+    if (declaredType.slowToString() == 'int') return HType.INTEGER;
+    if (declaredType.slowToString() == 'num') return HType.NUMBER;
+    if (declaredType.slowToString() == 'String') return HType.STRING;
+    return HType.UNKNOWN;
+  }
+
+  bool hasExpectedType() => true;
+}
+
+class HForeignNew extends HForeign {
+  ClassElement element;
+  HForeignNew(this.element, List<HInstruction> inputs)
+      : super(const LiteralDartString("new"),
+              const LiteralDartString("Object"), inputs);
+  accept(HVisitor visitor) => visitor.visitForeignNew(this);
+}
+
+class HInvokeBinary extends HInvokeStatic {
+  HInvokeBinary(HStatic target, HInstruction left, HInstruction right)
+      : super(Selector.BINARY_OPERATOR, <HInstruction>[target, left, right]);
+
+  HInstruction get left() => inputs[1];
+  HInstruction get right() => inputs[2];
+
+  HType computeInputsType() {
+    HType leftType = left.type;
+    HType rightType = right.type;
+    if (leftType.isUnknown() || rightType.isUnknown()) {
+      return HType.UNKNOWN;
+    }
+    return leftType.combine(rightType);
+  }
+
+  abstract BinaryOperation get operation();
+}
+
+class HBinaryArithmetic extends HInvokeBinary {
+  HBinaryArithmetic(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+
+  void prepareGvn() {
+    // An arithmetic expression can take part in global value
+    // numbering and do not have any side-effects if we know that all
+    // inputs are numbers.
+    if (builtin) {
+      clearAllSideEffects();
+      setUseGvn();
+    } else {
+      setAllSideEffects();
+    }
+  }
+
+  bool get builtin() => left.isNumber() && right.isNumber();
+
+  HType computeType() {
+    HType inputsType = computeInputsType();
+    if (!inputsType.isUnknown()) return inputsType;
+    if (left.isNumber()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
+    if (type.isUnknown()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  bool hasExpectedType() => left.isNumber() && right.isNumber();
+  // TODO(1603): The class should be marked as abstract.
+  abstract BinaryOperation get operation();
+}
+
+class HAdd extends HBinaryArithmetic {
+  HAdd(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitAdd(this);
+
+  bool get builtin() {
+    return (left.isNumber() && right.isNumber())
+            || (left.isString() && right.isString())
+            || (left.isString() && right is HConstant);
+  }
+
+  HType computeType() {
+    HType computedType = computeInputsType();
+    if (computedType.isConflicting() && left.isString()) return HType.STRING;
+    if (!computedType.isUnknown()) return computedType;
+    if (left.isNumber()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  bool hasExpectedType() => builtin || type.isUnknown() || left.isString();
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (isString() || left.isString()) {
+      return (input == left) ? HType.STRING : HType.UNKNOWN;
+    }
+    if (right.isString()) return HType.STRING;
+    if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  AddOperation get operation() => const AddOperation();
+
+  int typeCode() => 5;
+  bool typeEquals(other) => other is HAdd;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HDivide extends HBinaryArithmetic {
+  HDivide(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitDivide(this);
+
+  bool get builtin() => left.isNumber() && right.isNumber();
+
+  HType computeType() {
+    HType inputsType = computeInputsType();
+    if (left.isNumber()) return HType.DOUBLE;
+    return HType.UNKNOWN;
+  }
+
+  DivideOperation get operation() => const DivideOperation();
+  int typeCode() => 6;
+  bool typeEquals(other) => other is HDivide;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HModulo extends HBinaryArithmetic {
+  HModulo(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitModulo(this);
+
+  ModuloOperation get operation() => const ModuloOperation();
+  int typeCode() => 7;
+  bool typeEquals(other) => other is HModulo;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HMultiply extends HBinaryArithmetic {
+  HMultiply(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitMultiply(this);
+
+  MultiplyOperation get operation() => const MultiplyOperation();
+  int typeCode() => 8;
+  bool typeEquals(other) => other is HMultiply;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HSubtract extends HBinaryArithmetic {
+  HSubtract(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitSubtract(this);
+
+  SubtractOperation get operation() => const SubtractOperation();
+  int typeCode() => 9;
+  bool typeEquals(other) => other is HSubtract;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HTruncatingDivide extends HBinaryArithmetic {
+  HTruncatingDivide(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitTruncatingDivide(this);
+
+  TruncatingDivideOperation get operation()
+      => const TruncatingDivideOperation();
+  int typeCode() => 10;
+  bool typeEquals(other) => other is HTruncatingDivide;
+  bool dataEquals(HInstruction other) => true;
+}
+
+
+// TODO(floitsch): Should HBinaryArithmetic really be the super class of
+// HBinaryBitOp?
+class HBinaryBitOp extends HBinaryArithmetic {
+  HBinaryBitOp(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+
+  bool get builtin() => left.isInteger() && right.isInteger();
+
+  HType computeType() {
+    HType inputsType = computeInputsType();
+    if (!inputsType.isUnknown()) return inputsType;
+    if (left.isInteger()) return HType.INTEGER;
+    return HType.UNKNOWN;
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    return HType.INTEGER;
+  }
+
+  // TODO(floitsch): make class abstract instead of adding an abstract method.
+  abstract accept(HVisitor visitor);
+}
+
+class HShiftLeft extends HBinaryBitOp {
+  HShiftLeft(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitShiftLeft(this);
+
+  ShiftLeftOperation get operation() => const ShiftLeftOperation();
+  int typeCode() => 11;
+  bool typeEquals(other) => other is HShiftLeft;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HShiftRight extends HBinaryBitOp {
+  HShiftRight(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitShiftRight(this);
+
+  ShiftRightOperation get operation() => const ShiftRightOperation();
+  int typeCode() => 12;
+  bool typeEquals(other) => other is HShiftRight;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HBitOr extends HBinaryBitOp {
+  HBitOr(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitBitOr(this);
+
+  BitOrOperation get operation() => const BitOrOperation();
+  int typeCode() => 13;
+  bool typeEquals(other) => other is HBitOr;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HBitAnd extends HBinaryBitOp {
+  HBitAnd(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitBitAnd(this);
+
+  BitAndOperation get operation() => const BitAndOperation();
+  int typeCode() => 14;
+  bool typeEquals(other) => other is HBitAnd;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HBitXor extends HBinaryBitOp {
+  HBitXor(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitBitXor(this);
+
+  BitXorOperation get operation() => const BitXorOperation();
+  int typeCode() => 15;
+  bool typeEquals(other) => other is HBitXor;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HInvokeUnary extends HInvokeStatic {
+  HInvokeUnary(HStatic target, HInstruction input)
+      : super(Selector.UNARY_OPERATOR, <HInstruction>[target, input]);
+
+  HInstruction get operand() => inputs[1];
+
+  void prepareGvn() {
+    // A unary arithmetic expression can take part in global value
+    // numbering and does not have any side-effects if its input is a
+    // number.
+    if (builtin) {
+      clearAllSideEffects();
+      setUseGvn();
+    } else {
+      setAllSideEffects();
+    }
+  }
+
+  bool get builtin() => operand.isNumber();
+
+  HType computeType() {
+    HType operandType = operand.type;
+    if (!operandType.isUnknown()) return operandType;
+    return HType.UNKNOWN;
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (type.isUnknown() || type.isNumber()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  bool hasExpectedType() => builtin || type.isUnknown();
+
+  abstract UnaryOperation get operation();
+}
+
+class HNegate extends HInvokeUnary {
+  HNegate(HStatic target, HInstruction input) : super(target, input);
+  accept(HVisitor visitor) => visitor.visitNegate(this);
+
+  NegateOperation get operation() => const NegateOperation();
+  int typeCode() => 16;
+  bool typeEquals(other) => other is HNegate;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HBitNot extends HInvokeUnary {
+  HBitNot(HStatic target, HInstruction input) : super(target, input);
+  accept(HVisitor visitor) => visitor.visitBitNot(this);
+
+  bool get builtin() => operand.isInteger();
+
+  HType computeType() {
+    HType operandType = operand.type;
+    if (!operandType.isUnknown()) return operandType;
+    return HType.UNKNOWN;
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    return HType.INTEGER;
+  }
+
+  BitNotOperation get operation() => const BitNotOperation();
+  int typeCode() => 17;
+  bool typeEquals(other) => other is HBitNot;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HExit extends HControlFlow {
+  HExit() : super(const <HInstruction>[]);
+  toString() => 'exit';
+  accept(HVisitor visitor) => visitor.visitExit(this);
+}
+
+class HGoto extends HControlFlow {
+  HGoto() : super(const <HInstruction>[]);
+  toString() => 'goto';
+  accept(HVisitor visitor) => visitor.visitGoto(this);
+}
+
+class HBreak extends HGoto {
+  final TargetElement target;
+  final LabelElement label;
+  HBreak(this.target) : label = null;
+  HBreak.toLabel(LabelElement label) : label = label, target = label.target;
+  toString() => (label !== null) ? 'break ${label.labelName}' : 'break';
+  accept(HVisitor visitor) => visitor.visitBreak(this);
+}
+
+class HContinue extends HGoto {
+  final TargetElement target;
+  final LabelElement label;
+  HContinue(this.target) : label = null;
+  HContinue.toLabel(LabelElement label) : label = label, target = label.target;
+  toString() => (label !== null) ? 'continue ${label.labelName}' : 'continue';
+  accept(HVisitor visitor) => visitor.visitContinue(this);
+}
+
+class HTry extends HControlFlow {
+  HParameterValue exception;
+  HBasicBlock finallyBlock;
+  HTry() : super(const <HInstruction>[]);
+  toString() => 'try';
+  accept(HVisitor visitor) => visitor.visitTry(this);
+  HBasicBlock get joinBlock() => this.block.successors.last();
+}
+
+class HIf extends HConditionalBranch {
+  bool hasElse;
+  HIfBlockInformation blockInformation = null;
+  HIf(HInstruction condition, this.hasElse) : super(<HInstruction>[condition]);
+  toString() => 'if';
+  accept(HVisitor visitor) => visitor.visitIf(this);
+
+  HBasicBlock get thenBlock() {
+    assert(block.dominatedBlocks[0] === block.successors[0]);
+    return block.successors[0];
+  }
+
+  HBasicBlock get elseBlock() {
+    if (hasElse) {
+      assert(block.dominatedBlocks[1] === block.successors[1]);
+      return block.successors[1];
+    } else {
+      return null;
+    }
+  }
+
+  HBasicBlock get joinBlock() => blockInformation.joinBlock;
+}
+
+class HLoopBranch extends HConditionalBranch {
+  static final int CONDITION_FIRST_LOOP = 0;
+  static final int DO_WHILE_LOOP = 1;
+
+  final int kind;
+  HLoopBranch(HInstruction condition, [this.kind = CONDITION_FIRST_LOOP])
+      : super(<HInstruction>[condition]);
+  toString() => 'loop-branch';
+  accept(HVisitor visitor) => visitor.visitLoopBranch(this);
+
+  bool isDoWhile() {
+    return kind === DO_WHILE_LOOP;
+  }
+}
+
+class HConstant extends HInstruction {
+  final Constant constant;
+  HConstant.internal(this.constant, HType type) : super(<HInstruction>[]) {
+    this.type = type;
+  }
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+  }
+
+  toString() => 'literal: $constant';
+  accept(HVisitor visitor) => visitor.visitConstant(this);
+  HType computeType() => type;
+
+  // Literals have the type they have. It can't be changed.
+  bool updateType() => false;
+
+  bool hasExpectedType() => true;
+
+  bool isConstant() => true;
+  bool isConstantBoolean() => constant.isBool();
+  bool isConstantNull() => constant.isNull();
+  bool isConstantNumber() => constant.isNum();
+  bool isConstantString() => constant.isString();
+
+  // Maybe avoid this if the literal is big?
+  bool isCodeMotionInvariant() => true;
+}
+
+class HNot extends HInstruction {
+  HNot(HInstruction value) : super(<HInstruction>[value]);
+  void prepareGvn() {
+    assert(!hasSideEffects());
+    setUseGvn();
+  }
+
+  HType computeType() => HType.BOOLEAN;
+  bool hasExpectedType() => true;
+  HType computeDesiredInputType(HInstruction input) {
+    return HType.BOOLEAN;
+  }
+
+  accept(HVisitor visitor) => visitor.visitNot(this);
+  int typeCode() => 18;
+  bool typeEquals(other) => other is HNot;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HParameterValue extends HInstruction {
+  final Element element;
+
+  HParameterValue(this.element) : super(<HInstruction>[]);
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+  }
+  toString() => 'parameter ${element.name}';
+  accept(HVisitor visitor) => visitor.visitParameterValue(this);
+  bool isCodeMotionInvariant() => true;
+}
+
+class HThis extends HParameterValue {
+  HThis() : super(null);
+  toString() => 'this';
+  accept(HVisitor visitor) => visitor.visitThis(this);
+}
+
+class HPhi extends HInstruction {
+  final Element element;
+
+  static final IS_NOT_LOGICAL_OPERATOR = 0;
+  static final IS_AND = 1;
+  static final IS_OR = 2;
+
+  int logicalOperatorType = IS_NOT_LOGICAL_OPERATOR;
+
+  // The order of the [inputs] must correspond to the order of the
+  // predecessor-edges. That is if an input comes from the first predecessor
+  // of the surrounding block, then the input must be the first in the [HPhi].
+  HPhi(this.element, List<HInstruction> inputs) : super(inputs);
+  HPhi.noInputs(Element element) : this(element, <HInstruction>[]);
+  HPhi.singleInput(Element element, HInstruction input)
+      : this(element, <HInstruction>[input]);
+  HPhi.manyInputs(Element element, List<HInstruction> inputs)
+      : this(element, inputs);
+
+  void addInput(HInstruction input) {
+    assert(isInBasicBlock());
+    inputs.add(input);
+    input.usedBy.add(this);
+  }
+
+  // Compute the (shared) type of the inputs if any. If all inputs
+  // have the same known type return it. If any two inputs have
+  // different known types, we'll return a conflict -- otherwise we'll
+  // simply return an unknown type.
+  HType computeInputsType() {
+    bool seenUnknown = false;
+    HType candidateType = inputs[0].type;
+    for (int i = 1, length = inputs.length; i < length; i++) {
+      HType inputType = inputs[i].type;
+      if (inputType.isUnknown()) return HType.UNKNOWN;
+      candidateType = candidateType.combine(inputType);
+      if (candidateType.isConflicting()) return HType.CONFLICTING;
+    }
+    return candidateType;
+  }
+
+  HType computeType() {
+    HType inputsType = computeInputsType();
+    if (!inputsType.isUnknown()) return inputsType;
+    return super.computeType();
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    if (type.isNumber()) return HType.NUMBER;
+    if (type.isStringOrArray()) return HType.STRING_OR_ARRAY;
+    return type;
+  }
+
+  bool hasExpectedType() {
+    for (int i = 0; i < inputs.length; i++) {
+      if (type.combine(inputs[i].type).isConflicting()) return false;
+    }
+    return true;
+  }
+
+  void setInitialTypeForLoopPhi() {
+    assert(block.isLoopHeader());
+    assert(type.isUnknown());
+    type = inputs[0].type;
+  }
+
+  bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
+
+  String logicalOperator() {
+    assert(isLogicalOperator());
+    if (logicalOperatorType == IS_AND) return "&&";
+    assert(logicalOperatorType == IS_OR);
+    return "||";
+  }
+
+  toString() => 'phi';
+  accept(HVisitor visitor) => visitor.visitPhi(this);
+}
+
+class HRelational extends HInvokeBinary {
+  HRelational(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right) {
+    type = HType.BOOLEAN;
+  }
+
+  void prepareGvn() {
+    // Relational expressions can take part in global value numbering
+    // and do not have any side-effects if we know all the inputs are
+    // numbers. This can be improved for at least equality.
+    if (builtin) {
+      clearAllSideEffects();
+      setUseGvn();
+    } else {
+      setAllSideEffects();
+    }
+  }
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    // For all relational operations exept HEquals, we expect to only
+    // get numbers.
+    return HType.NUMBER;
+  }
+
+  bool get builtin() => left.isNumber() && right.isNumber();
+  HType computeType() => HType.BOOLEAN;
+  // A HRelational goes through the builtin operator or the top level
+  // element. Therefore, it always has the expected type.
+  bool hasExpectedType() => true;
+  // TODO(1603): the class should be marked as abstract.
+  abstract BinaryOperation get operation();
+}
+
+class HEquals extends HRelational {
+  HEquals(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitEquals(this);
+
+  bool get builtin() {
+    return (left.isNumber() && right.isNumber()) || left is HConstant;
+  }
+
+  HType computeType() => HType.BOOLEAN;
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (left.isNumber() || right.isNumber()) return HType.NUMBER;
+    return HType.UNKNOWN;
+  }
+
+  EqualsOperation get operation() => const EqualsOperation();
+  int typeCode() => 19;
+  bool typeEquals(other) => other is HEquals;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HIdentity extends HRelational {
+  HIdentity(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitIdentity(this);
+
+  bool get builtin() => true;
+  HType computeType() => HType.BOOLEAN;
+  bool hasExpectedType() => true;
+
+  HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
+
+  IdentityOperation get operation() => const IdentityOperation();
+  int typeCode() => 20;
+  bool typeEquals(other) => other is HIdentity;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HGreater extends HRelational {
+  HGreater(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitGreater(this);
+
+  GreaterOperation get operation() => const GreaterOperation();
+  int typeCode() => 21;
+  bool typeEquals(other) => other is HGreater;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HGreaterEqual extends HRelational {
+  HGreaterEqual(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitGreaterEqual(this);
+
+  GreaterEqualOperation get operation() => const GreaterEqualOperation();
+  int typeCode() => 22;
+  bool typeEquals(other) => other is HGreaterEqual;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HLess extends HRelational {
+  HLess(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitLess(this);
+
+  LessOperation get operation() => const LessOperation();
+  int typeCode() => 23;
+  bool typeEquals(other) => other is HLess;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HLessEqual extends HRelational {
+  HLessEqual(HStatic target, HInstruction left, HInstruction right)
+      : super(target, left, right);
+  accept(HVisitor visitor) => visitor.visitLessEqual(this);
+
+  LessEqualOperation get operation() => const LessEqualOperation();
+  int typeCode() => 24;
+  bool typeEquals(other) => other is HLessEqual;
+  bool dataEquals(HInstruction other) => true;
+}
+
+class HReturn extends HControlFlow {
+  HReturn(value) : super(<HInstruction>[value]);
+  toString() => 'return';
+  accept(HVisitor visitor) => visitor.visitReturn(this);
+}
+
+class HThrow extends HControlFlow {
+  final bool isRethrow;
+  HThrow(value, [this.isRethrow = false]) : super(<HInstruction>[value]);
+  toString() => 'throw';
+  accept(HVisitor visitor) => visitor.visitThrow(this);
+}
+
+class HStatic extends HInstruction {
+  Element element;
+  HStatic(this.element) : super(<HInstruction>[]);
+
+  void prepareGvn() {
+    if (!element.isAssignable()) {
+      clearAllSideEffects();
+      setUseGvn();
+    }
+  }
+  toString() => 'static ${element.name}';
+  accept(HVisitor visitor) => visitor.visitStatic(this);
+
+  int gvnHashCode() => super.gvnHashCode() ^ element.hashCode();
+  int typeCode() => 25;
+  bool typeEquals(other) => other is HStatic;
+  bool dataEquals(HStatic other) => element == other.element;
+  bool isCodeMotionInvariant() => !element.isAssignable();
+}
+
+class HStaticStore extends HInstruction {
+  Element element;
+  HStaticStore(this.element, HInstruction value) : super(<HInstruction>[value]);
+  toString() => 'static store ${element.name}';
+  accept(HVisitor visitor) => visitor.visitStaticStore(this);
+
+  int typeCode() => 26;
+  bool typeEquals(other) => other is HStaticStore;
+  bool dataEquals(HStaticStore other) => element == other.element;
+}
+
+class HLiteralList extends HInstruction {
+  HLiteralList(inputs, this.isConst) : super(inputs);
+  toString() => 'literal list';
+  accept(HVisitor visitor) => visitor.visitLiteralList(this);
+  HType computeType() => HType.ARRAY;
+  bool hasExpectedType() => true;
+  final bool isConst; // TODO(floitsch): Remove when CTC handles arrays.
+
+  void prepareGvn() {
+    assert(!hasSideEffects());
+  }
+}
+
+class HIndex extends HInvokeStatic {
+  HIndex(HStatic target, HInstruction receiver, HInstruction index)
+      : super(Selector.INDEX, <HInstruction>[target, receiver, index]);
+  toString() => 'index operator';
+  accept(HVisitor visitor) => visitor.visitIndex(this);
+
+  void prepareGvn() {
+    if (builtin) {
+      clearAllSideEffects();
+    } else {
+      setAllSideEffects();
+    }
+  }
+
+  HInstruction get receiver() => inputs[1];
+  HInstruction get index() => inputs[2];
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (input == receiver) return HType.STRING_OR_ARRAY;
+    return HType.UNKNOWN;
+  }
+
+  bool get builtin() => receiver.isStringOrArray();
+  HType computeType() => HType.UNKNOWN;
+  bool hasExpectedType() => false;
+}
+
+class HIndexAssign extends HInvokeStatic {
+  HIndexAssign(HStatic target,
+               HInstruction receiver,
+               HInstruction index,
+               HInstruction value)
+      : super(Selector.INDEX_SET,
+              <HInstruction>[target, receiver, index, value]);
+  toString() => 'index assign operator';
+  accept(HVisitor visitor) => visitor.visitIndexAssign(this);
+
+  HInstruction get receiver() => inputs[1];
+  HInstruction get index() => inputs[2];
+  HInstruction get value() => inputs[3];
+
+  HType computeDesiredInputType(HInstruction input) {
+    // TODO(floitsch): we want the target to be a function.
+    if (input == target) return HType.UNKNOWN;
+    if (input == receiver) return HType.ARRAY;
+    return HType.UNKNOWN;
+  }
+
+  bool get builtin() => receiver.isArray();
+  HType computeType() => value.type;
+  // This instruction does not yield a new value, so it always
+  // has the expected type (void).
+  bool hasExpectedType() => true;
+}
+
+class HIs extends HInstruction {
+  // TODO(ahe): This should be a Type, not Element.
+  final Element typeExpression;
+  final bool nullOk;
+
+  HIs(this.typeExpression, HInstruction expression, [nullOk = false])
+    : this.nullOk = nullOk, super(<HInstruction>[expression]);
+
+  HInstruction get expression() => inputs[0];
+
+  HType computeType() => HType.BOOLEAN;
+  bool hasExpectedType() => true;
+
+  accept(HVisitor visitor) => visitor.visitIs(this);
+
+  toString() => "$expression is $typeExpression";
+}
+
+class HIfBlockInformation {
+  final HIf branch;
+  final SubGraph thenGraph;
+  final SubGraph elseGraph;
+  final HBasicBlock joinBlock;
+  HIfBlockInformation(this.branch,
+                      this.thenGraph,
+                      this.elseGraph,
+                      this.joinBlock);
+}
diff --git a/lib/compiler/implementation/ssa/optimize.dart b/lib/compiler/implementation/ssa/optimize.dart
new file mode 100644
index 0000000..36d6f68
--- /dev/null
+++ b/lib/compiler/implementation/ssa/optimize.dart
@@ -0,0 +1,709 @@
+// Copyright (c) 2011, 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.
+
+interface OptimizationPhase {
+  String get name();
+  void visitGraph(HGraph graph);
+}
+
+class SsaOptimizerTask extends CompilerTask {
+  SsaOptimizerTask(Compiler compiler) : super(compiler);
+  String get name() => 'SSA optimizer';
+
+  void runPhases(HGraph graph, List<OptimizationPhase> phases) {
+    for (OptimizationPhase phase in phases) {
+      phase.visitGraph(graph);
+      compiler.tracer.traceGraph(phase.name, graph);
+    }
+  }
+
+  void optimize(WorkItem work, HGraph graph) {
+    measure(() {
+      List<OptimizationPhase> phases = <OptimizationPhase>[
+          new SsaConstantFolder(compiler),
+          new SsaRedundantPhiEliminator(),
+          new SsaDeadPhiEliminator(),
+          new SsaGlobalValueNumberer(compiler),
+          new SsaCodeMotion(),
+          new SsaDeadCodeEliminator()];
+      runPhases(graph, phases);
+    });
+  }
+
+  bool trySpeculativeOptimizations(WorkItem work, HGraph graph) {
+    return measure(() {
+      // Run the phases that will generate type guards. We must also run
+      // [SsaCheckInserter] because the type propagator also propagates
+      // types non-speculatively. For example, it propagates the type
+      // array for a call to the List constructor.
+      List<OptimizationPhase> phases = <OptimizationPhase>[
+          new SsaTypePropagator(compiler),
+          new SsaTypeGuardBuilder(compiler, work),
+          new SsaCheckInserter(compiler)];
+      runPhases(graph, phases);
+      return !work.guards.isEmpty();
+    });
+  }
+
+  void prepareForSpeculativeOptimizations(WorkItem work, HGraph graph) {
+    measure(() {
+      // In order to generate correct code for the bailout version, we did not
+      // propagate types from the instruction to the type guard. We do it
+      // now to be able to optimize further.
+      work.guards.forEach((HTypeGuard guard) {
+        guard.type = guard.guarded.type;
+        guard.guarded.type = HType.UNKNOWN;
+      });
+      // We also need to insert range and integer checks for the type guards,
+      // now that they know their type. We did not need to do that
+      // before because instructions that reference a guard would
+      // have not tried to use, e.g. native array access, since the
+      // guard was not typed.
+      runPhases(graph, <OptimizationPhase>[new SsaCheckInserter(compiler)]);
+    });
+  }
+}
+
+/**
+ * If both inputs to known operations are available execute the operation at
+ * compile-time.
+ */
+class SsaConstantFolder extends HBaseVisitor implements OptimizationPhase {
+  final String name = "SsaConstantFolder";
+  final Compiler compiler;
+  HGraph graph;
+
+  SsaConstantFolder(this.compiler);
+
+  void visitGraph(HGraph graph) {
+    this.graph = graph;
+    visitDominatorTree(graph);
+  }
+
+  visitBasicBlock(HBasicBlock block) {
+    HInstruction instruction = block.first;
+    while (instruction !== null) {
+      HInstruction next = instruction.next;
+      HInstruction replacement = instruction.accept(this);
+      if (replacement !== instruction) {
+        if (!replacement.isInBasicBlock()) {
+          // The constant folding can return an instruction that is already
+          // part of the graph (like an input), so we only add the replacement
+          // if necessary.
+          block.addAfter(instruction, replacement);
+        }
+        block.rewrite(instruction, replacement);
+        block.remove(instruction);
+        // Because the constant folder runs after type propagation, we
+        // must update the type of this instruction manually. Later
+        // phases can then optimize this instruction based on its
+        // type.
+        replacement.updateType();
+      }
+      instruction = next;
+    }
+  }
+
+  HInstruction visitInstruction(HInstruction node) {
+    return node;
+  }
+
+  HInstruction visitBoolify(HBoolify node) {
+    List<HInstruction> inputs = node.inputs;
+    assert(inputs.length == 1);
+    HInstruction input = inputs[0];
+    if (input.isBoolean()) return input;
+    // All values !== true are boolified to false.
+    if (input.type.isKnown()) {
+      return graph.addConstantBool(false);
+    }
+    return node;
+  }
+
+  HInstruction visitNot(HNot node) {
+    List<HInstruction> inputs = node.inputs;
+    assert(inputs.length == 1);
+    HInstruction input = inputs[0];
+    if (input is HConstant) {
+      HConstant constant = input;
+      bool isTrue = constant.constant.isTrue();
+      return graph.addConstantBool(!isTrue);
+    }
+    return node;
+  }
+
+  HInstruction visitInvokeUnary(HInvokeUnary node) {
+    HInstruction operand = node.operand;
+    if (operand is HConstant) {
+      UnaryOperation operation = node.operation;
+      HConstant receiver = operand;
+      Constant folded = operation.fold(receiver.constant);
+      if (folded !== null) return graph.addConstant(folded);
+    }
+    return node;
+  }
+
+  HInstruction visitInvokeInterceptor(HInvokeInterceptor node) {
+    if (node.name == const SourceString('length') &&
+        node.inputs[1].isConstantString()) {
+      HConstant input = node.inputs[1];
+      StringConstant constant = input.constant;
+      DartString string = constant.value;
+      return graph.addConstantInt(string.length);
+    }
+    return node;
+  }
+
+  HInstruction visitInvokeBinary(HInvokeBinary node) {
+    HInstruction left = node.left;
+    HInstruction right = node.right;
+    if (left is HConstant && right is HConstant) {
+      BinaryOperation operation = node.operation;
+      HConstant op1 = left;
+      HConstant op2 = right;
+      Constant folded = operation.fold(op1.constant, op2.constant);
+      if (folded !== null) return graph.addConstant(folded);
+    }
+    return node;
+  }
+
+  HInstruction visitEquals(HEquals node) {
+    HInstruction left = node.left;
+    HInstruction right = node.right;
+    if (!left.isConstant() && right.isConstantNull()) {
+      // TODO(floitsch): cache interceptors.
+      HStatic target = new HStatic(
+          compiler.builder.interceptors.getEqualsNullInterceptor());
+      node.block.addBefore(node,target);
+      return new HEquals(target, node.left, node.right);
+    }
+    // All other cases are dealt with by the [visitInvokeBinary].
+    return visitInvokeBinary(node);
+  }
+
+  HInstruction visitTypeGuard(HTypeGuard node) {
+    HInstruction value = node.guarded;
+    return (value.type.combine(node.type) == value.type) ? value : node;
+  }
+
+  HInstruction visitIntegerCheck(HIntegerCheck node) {
+    HInstruction value = node.value;
+    return value.isInteger() ? value : node;
+  }
+
+  HInstruction visitIs(HIs node) {
+    Element element = node.typeExpression;
+    if (element.kind === ElementKind.TYPE_VARIABLE) {
+      compiler.unimplemented("visitIs for type variables");
+    }
+
+    HType expressionType = node.expression.type;
+    if (element === compiler.objectClass
+        || element === compiler.dynamicClass) {
+      return graph.addConstantBool(true);
+    } else if (expressionType.isInteger()) {
+      if (element === compiler.intClass || element === compiler.numClass) {
+        return graph.addConstantBool(true);
+      } else if (element === compiler.doubleClass) {
+        // We let the JS semantics decide for that check. Currently
+        // the code we emit will always return true.
+        return node;
+      } else {
+        return graph.addConstantBool(false);
+      }
+    } else if (expressionType.isDouble()) {
+      if (element === compiler.doubleClass || element === compiler.numClass) {
+        return graph.addConstantBool(true);
+      } else if (element === compiler.intClass) {
+        // We let the JS semantics decide for that check. Currently
+        // the code we emit will return true for a double that can be
+        // represented as a 31-bit integer.
+        return node;
+      } else {
+        return graph.addConstantBool(false);
+      }
+    } else if (expressionType.isNumber()) {
+      if (element === compiler.numClass) {
+        return graph.addConstantBool(true);
+      }
+      // We cannot just return false, because the expression may be of
+      // type int or double.
+    } else if (expressionType.isString()) {
+      if (element === compiler.stringClass
+               || Elements.isStringSupertype(element, compiler)) {
+        return graph.addConstantBool(true);
+      } else {
+        return graph.addConstantBool(false);
+      }
+    } else if (expressionType.isArray()) {
+      if (element === compiler.listClass
+          || Elements.isListSupertype(element, compiler)) {
+        return graph.addConstantBool(true);
+      } else {
+        return graph.addConstantBool(false);
+      }
+    }
+    return node;
+  }
+}
+
+class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
+  final String name = "SsaCheckInserter";
+  Element lengthInterceptor;
+
+  SsaCheckInserter(Compiler compiler) {
+    SourceString lengthString = const SourceString('length');
+    lengthInterceptor =
+        compiler.builder.interceptors.getStaticGetInterceptor(lengthString);
+  }
+
+  void visitGraph(HGraph graph) {
+    visitDominatorTree(graph);
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    HInstruction instruction = block.first;
+    while (instruction !== null) {
+      HInstruction next = instruction.next;
+      instruction = instruction.accept(this);
+      instruction = next;
+    }
+  }
+
+  HBoundsCheck insertBoundsCheck(HInstruction node,
+                                 HInstruction receiver,
+                                 HInstruction index) {
+    HStatic interceptor = new HStatic(lengthInterceptor);
+    node.block.addBefore(node, interceptor);
+    HInvokeInterceptor length = new HInvokeInterceptor(
+        Selector.INVOCATION_0,
+        const SourceString("length"),
+        true,
+        <HInstruction>[interceptor, receiver]);
+    length.type = HType.NUMBER;
+    node.block.addBefore(node, length);
+
+    HBoundsCheck check = new HBoundsCheck(length, index);
+    node.block.addBefore(node, check);
+    return check;
+  }
+
+  HIntegerCheck insertIntegerCheck(HInstruction node, HInstruction value) {
+    HIntegerCheck check = new HIntegerCheck(value);
+    node.block.addBefore(node, check);
+    return check;
+  }
+
+  void visitIndex(HIndex node) {
+    if (!node.builtin) return;
+    HInstruction index = insertIntegerCheck(node, node.index);
+    index = insertBoundsCheck(node, node.receiver, index);
+    HIndex newInstruction = new HIndex(node.target, node.receiver, index);
+    node.block.addBefore(node, newInstruction);
+    node.block.rewrite(node, newInstruction);
+    node.block.remove(node);
+  }
+
+  void visitIndexAssign(HIndexAssign node) {
+    if (!node.builtin) return;
+    HInstruction index = insertIntegerCheck(node, node.index);
+    index = insertBoundsCheck(node, node.receiver, index);
+    HIndexAssign newInstruction =
+        new HIndexAssign(node.target, node.receiver, index, node.value);
+    node.block.addBefore(node, newInstruction);
+    node.block.rewrite(node, newInstruction);
+    node.block.remove(node);
+  }
+}
+
+class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
+  final String name = "SsaDeadCodeEliminator";
+
+  static bool isDeadCode(HInstruction instruction) {
+    // TODO(ngeoffray): the way we handle side effects is not right
+    // (e.g. branching instructions have side effects).
+    return !instruction.hasSideEffects()
+           && instruction.usedBy.isEmpty()
+           && instruction is !HCheck
+           && instruction is !HTypeGuard;
+  }
+
+  void visitGraph(HGraph graph) {
+    visitPostDominatorTree(graph);
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    HInstruction instruction = block.last;
+    while (instruction !== null) {
+      var previous = instruction.previous;
+      if (isDeadCode(instruction)) block.remove(instruction);
+      instruction = previous;
+    }
+  }
+}
+
+class SsaDeadPhiEliminator implements OptimizationPhase {
+  final String name = "SsaDeadPhiEliminator";
+
+  void visitGraph(HGraph graph) {
+    final List<HPhi> worklist = <HPhi>[];
+    // A set to keep track of the live phis that we found.
+    final Set<HPhi> livePhis = new Set<HPhi>();
+
+    // Add to the worklist all live phis: phis referenced by non-phi
+    // instructions.
+    for (final block in graph.blocks) {
+      block.forEachPhi((HPhi phi) {
+        for (final user in phi.usedBy) {
+          if (user is !HPhi) {
+            worklist.add(phi);
+            livePhis.add(phi);
+            break;
+          }
+        }
+      });
+    }
+
+    // Process the worklist by propagating liveness to phi inputs.
+    while (!worklist.isEmpty()) {
+      HPhi phi = worklist.removeLast();
+      for (final input in phi.inputs) {
+        if (input is HPhi && !livePhis.contains(input)) {
+          worklist.add(input);
+          livePhis.add(input);
+        }
+      }
+    }
+
+    // Remove phis that are not live.
+    // Traverse in reverse order to remove phis with no uses before the
+    // phis that they might use.
+    // NOTICE: Doesn't handle circular references, but we don't currently
+    // create any.
+    List<HBasicBlock> blocks = graph.blocks;
+    for (int i = blocks.length - 1; i >= 0; i--) {
+      HBasicBlock block = blocks[i];
+      HPhi current = block.phis.first;
+      HPhi next = null;
+      while (current != null) {
+        next = current.next;
+        if (!livePhis.contains(current)
+            // TODO(ahe): Not sure the following is correct.
+            && current.usedBy.isEmpty()) {
+          block.removePhi(current);
+        }
+        current = next;
+      }
+    }
+  }
+}
+
+class SsaRedundantPhiEliminator implements OptimizationPhase {
+  final String name = "SsaRedundantPhiEliminator";
+
+  void visitGraph(HGraph graph) {
+    final List<HPhi> worklist = <HPhi>[];
+
+    // Add all phis in the worklist.
+    for (final block in graph.blocks) {
+      block.forEachPhi((HPhi phi) => worklist.add(phi));
+    }
+
+    while (!worklist.isEmpty()) {
+      HPhi phi = worklist.removeLast();
+
+      // If the phi has already been processed, continue.
+      if (!phi.isInBasicBlock()) continue;
+
+      // Find if the inputs of the phi are the same instruction.
+      // The builder ensures that phi.inputs[0] cannot be the phi
+      // itself.
+      assert(phi.inputs[0] !== phi);
+      HInstruction candidate = phi.inputs[0];
+      for (int i = 1; i < phi.inputs.length; i++) {
+        HInstruction input = phi.inputs[i];
+        // If the input is the phi, the phi is still candidate for
+        // elimination.
+        if (input !== candidate && input !== phi) {
+          candidate = null;
+          break;
+        }
+      }
+
+      // If the inputs are not the same, continue.
+      if (candidate == null) continue;
+
+      // Because we're updating the users of this phi, we may have new
+      // phis candidate for elimination. Add phis that used this phi
+      // to the worklist.
+      for (final user in phi.usedBy) {
+        if (user is HPhi) worklist.add(user);
+      }
+      phi.block.rewrite(phi, candidate);
+      phi.block.removePhi(phi);
+    }
+  }
+}
+
+class SsaGlobalValueNumberer implements OptimizationPhase {
+  final String name = "SsaGlobalValueNumberer";
+  final Compiler compiler;
+  final Set<int> visited;
+
+  List<int> blockChangesFlags;
+  List<int> loopChangesFlags;
+
+  SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>();
+
+  void visitGraph(HGraph graph) {
+    computeChangesFlags(graph);
+    moveLoopInvariantCode(graph);
+    visitBasicBlock(graph.entry, new ValueSet());
+  }
+
+  void moveLoopInvariantCode(HGraph graph) {
+    for (int i = graph.blocks.length - 1; i >= 0; i--) {
+      HBasicBlock block = graph.blocks[i];
+      if (block.isLoopHeader()) {
+        int changesFlags = loopChangesFlags[block.id];
+        HBasicBlock last = block.loopInformation.getLastBackEdge();
+        for (int j = block.id; j <= last.id; j++) {
+          moveLoopInvariantCodeFromBlock(graph.blocks[j], block, changesFlags);
+        }
+      }
+    }
+  }
+
+  void moveLoopInvariantCodeFromBlock(HBasicBlock block,
+                                      HBasicBlock loopHeader,
+                                      int changesFlags) {
+    HBasicBlock preheader = loopHeader.predecessors[0];
+    int dependsFlags = HInstruction.computeDependsOnFlags(changesFlags);
+    HInstruction instruction = block.first;
+    while (instruction != null) {
+      HInstruction next = instruction.next;
+      if (instruction.useGvn()
+          && (instruction is !HCheck)
+          && (instruction.flags & dependsFlags) == 0) {
+        bool loopInvariantInputs = true;
+        List<HInstruction> inputs = instruction.inputs;
+        for (int i = 0, length = inputs.length; i < length; i++) {
+          if (isInputDefinedAfterDominator(inputs[i], preheader)) {
+            loopInvariantInputs = false;
+            break;
+          }
+        }
+
+        // If the inputs are loop invariant, we can move the
+        // instruction from the current block to the pre-header block.
+        if (loopInvariantInputs) {
+          block.detach(instruction);
+          preheader.moveAtExit(instruction);
+        }
+      }
+      instruction = next;
+    }
+  }
+
+  bool isInputDefinedAfterDominator(HInstruction input,
+                                    HBasicBlock dominator) {
+    return input.block.id > dominator.id;
+  }
+
+  void visitBasicBlock(HBasicBlock block, ValueSet values) {
+    HInstruction instruction = block.first;
+    while (instruction !== null) {
+      HInstruction next = instruction.next;
+      int flags = instruction.getChangesFlags();
+      if (flags != 0) {
+        assert(!instruction.useGvn());
+        values.kill(flags);
+      } else if (instruction.useGvn()) {
+        HInstruction other = values.lookup(instruction);
+        if (other !== null) {
+          assert(other.gvnEquals(instruction) && instruction.gvnEquals(other));
+          block.rewrite(instruction, other);
+          block.remove(instruction);
+        } else {
+          values.add(instruction);
+        }
+      }
+      instruction = next;
+    }
+
+    List<HBasicBlock> dominatedBlocks = block.dominatedBlocks;
+    for (int i = 0, length = dominatedBlocks.length; i < length; i++) {
+      HBasicBlock dominated = dominatedBlocks[i];
+      // No need to copy the value set for the last child.
+      ValueSet successorValues = (i == length - 1) ? values : values.copy();
+      // If we have no values in our set, we do not have to kill
+      // anything. Also, if the range of block ids from the current
+      // block to the dominated block is empty, there is no blocks on
+      // any path from the current block to the dominated block so we
+      // don't have to do anything either.
+      assert(block.id < dominated.id);
+      if (!successorValues.isEmpty() && block.id + 1 < dominated.id) {
+        visited.clear();
+        int changesFlags = getChangesFlagsForDominatedBlock(block, dominated);
+        successorValues.kill(changesFlags);
+      }
+      visitBasicBlock(dominated, successorValues);
+    }
+  }
+
+  void computeChangesFlags(HGraph graph) {
+    // Create the changes flags lists. Make sure to initialize the
+    // loop changes flags list to zero so we can use bitwise or when
+    // propagating loop changes upwards.
+    final int length = graph.blocks.length;
+    blockChangesFlags = new List<int>(length);
+    loopChangesFlags = new List<int>(length);
+    for (int i = 0; i < length; i++) loopChangesFlags[i] = 0;
+
+    // Run through all the basic blocks in the graph and fill in the
+    // changes flags lists.
+    for (int i = length - 1; i >= 0; i--) {
+      final HBasicBlock block = graph.blocks[i];
+      final int id = block.id;
+
+      // Compute block changes flags for the block.
+      int changesFlags = 0;
+      HInstruction instruction = block.first;
+      while (instruction !== null) {
+        instruction.prepareGvn();
+        changesFlags |= instruction.getChangesFlags();
+        instruction = instruction.next;
+      }
+      assert(blockChangesFlags[id] === null);
+      blockChangesFlags[id] = changesFlags;
+
+      // Loop headers are part of their loop, so update the loop
+      // changes flags accordingly.
+      if (block.isLoopHeader()) {
+        loopChangesFlags[id] |= changesFlags;
+      }
+
+      // Propagate loop changes flags upwards.
+      HBasicBlock parentLoopHeader = block.parentLoopHeader;
+      if (parentLoopHeader !== null) {
+        loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader())
+            ? loopChangesFlags[id]
+            : changesFlags;
+      }
+    }
+  }
+
+  int getChangesFlagsForDominatedBlock(HBasicBlock dominator,
+                                       HBasicBlock dominated) {
+    int changesFlags = 0;
+    List<HBasicBlock> predecessors = dominated.predecessors;
+    for (int i = 0, length = predecessors.length; i < length; i++) {
+      HBasicBlock block = predecessors[i];
+      int id = block.id;
+      // If the current predecessor block is on the path from the
+      // dominator to the dominated, it must have an id that is in the
+      // range from the dominator to the dominated.
+      if (dominator.id < id && id < dominated.id && !visited.contains(id)) {
+        visited.add(id);
+        changesFlags |= blockChangesFlags[id];
+        changesFlags |= getChangesFlagsForDominatedBlock(dominator, block);
+      }
+    }
+    return changesFlags;
+  }
+}
+
+// This phase merges equivalent instructions on different paths into
+// one instruction in a dominator block. It runs through the graph
+// post dominator order and computes a ValueSet for each block of
+// instructions that can be moved to a dominator block. These
+// instructions are the ones that:
+// 1) can be used for GVN, and
+// 2) do not use definitions of their own block.
+//
+// A basic block looks at its sucessors and finds the intersection of
+// these computed ValueSet. It moves all instructions of the
+// intersection into its own list of instructions.
+class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase {
+  final String name = "SsaCodeMotion";
+
+  List<ValueSet> values;
+
+  void visitGraph(HGraph graph) {
+    values = new List<ValueSet>(graph.blocks.length);
+    for (int i = 0; i < graph.blocks.length; i++) {
+      values[graph.blocks[i].id] = new ValueSet();
+    }
+    visitPostDominatorTree(graph);
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    List<HBasicBlock> successors = block.successors;
+
+    // Phase 1: get the ValueSet of all successors, compute the
+    // intersection and move the instructions of the intersection into
+    // this block.
+    if (successors.length != 0) {
+      ValueSet instructions = values[successors[0].id];
+      for (int i = 1; i < successors.length; i++) {
+        ValueSet other = values[successors[i].id];
+        instructions = instructions.intersection(other);
+      }
+
+      if (!instructions.isEmpty()) {
+        List<HInstruction> list = instructions.toList();
+        for (HInstruction instruction in list) {
+          // Move the instruction to the current block.
+          instruction.block.detach(instruction);
+          block.moveAtExit(instruction);
+          // Go through all successors and rewrite their instruction
+          // to the shared one.
+          for (final successor in successors) {
+            HInstruction toRewrite = values[successor.id].lookup(instruction);
+            if (toRewrite != instruction) {
+              successor.rewrite(toRewrite, instruction);
+              successor.remove(toRewrite);
+            }
+          }
+        }
+      }
+    }
+
+    // Don't try to merge instructions to a dominator if we have
+    // multiple predecessors.
+    if (block.predecessors.length != 1) return;
+
+    // Phase 2: Go through all instructions of this block and find
+    // which instructions can be moved to a dominator block.
+    ValueSet set_ = values[block.id];
+    HInstruction instruction = block.first;
+    int flags = 0;
+    while (instruction !== null) {
+      int dependsFlags = HInstruction.computeDependsOnFlags(flags);
+      flags |= instruction.getChangesFlags();
+
+      HInstruction current = instruction;
+      instruction = instruction.next;
+
+      // TODO(ngeoffray): this check is needed because we currently do
+      // not have flags to express 'Gvn'able', but not movable.
+      if (current is HCheck) continue;
+      if (!current.useGvn()) continue;
+      if ((current.flags & dependsFlags) != 0) continue;
+
+      bool canBeMoved = true;
+      for (final HInstruction input in current.inputs) {
+        if (input.block == block) {
+          canBeMoved = false;
+          break;
+        }
+      }
+      if (!canBeMoved) continue;
+
+      // This is safe because we are running after GVN.
+      // TODO(ngeoffray): ensure GVN has been run.
+      set_.add(current);
+    }
+  }
+}
diff --git a/lib/compiler/implementation/ssa/ssa.dart b/lib/compiler/implementation/ssa/ssa.dart
new file mode 100644
index 0000000..dfed318
--- /dev/null
+++ b/lib/compiler/implementation/ssa/ssa.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2012, 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('ssa');
+
+#import('../leg.dart');
+#import('../native_handler.dart', prefix: 'native');
+#import('../elements/elements.dart');
+#import('../scanner/scannerlib.dart');
+#import('../tree/tree.dart');
+#import('../util/util.dart');
+#import('../util/characters.dart');
+
+#source('bailout.dart');
+#source('builder.dart');
+#source('closure.dart');
+#source('codegen.dart');
+#source('codegen_helpers.dart');
+#source('js_names.dart');
+#source('nodes.dart');
+#source('optimize.dart');
+#source('types.dart');
+#source('validate.dart');
+#source('value_set.dart');
diff --git a/lib/compiler/implementation/ssa/tracer.dart b/lib/compiler/implementation/ssa/tracer.dart
new file mode 100644
index 0000000..03c6593
--- /dev/null
+++ b/lib/compiler/implementation/ssa/tracer.dart
@@ -0,0 +1,422 @@
+// Copyright (c) 2011, 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('tracer');
+
+#import('dart:io');
+#import('ssa.dart');
+#import('../leg.dart');
+
+final bool GENERATE_SSA_TRACE = false;
+
+class HTracer extends HGraphVisitor implements Tracer {
+  int indent = 0;
+  final RandomAccessFile output;
+  final bool enabled = GENERATE_SSA_TRACE;
+
+  HTracer([String path = "dart.cfg"])
+      : output = GENERATE_SSA_TRACE ? new File(path).openSync(FileMode.WRITE)
+                                    : null;
+
+  void close() {
+    if (enabled) output.closeSync();
+  }
+
+  void traceCompilation(String methodName) {
+    tag("compilation", () {
+      printProperty("name", methodName);
+      printProperty("method", methodName);
+      printProperty("date", new Date.now().value);
+    });
+  }
+
+  void traceGraph(String name, HGraph graph) {
+    if (!enabled) return;
+    tag("cfg", () {
+      printProperty("name", name);
+      visitDominatorTree(graph);
+    });
+  }
+
+  void addPredecessors(HBasicBlock block) {
+    if (block.predecessors.isEmpty()) {
+      printEmptyProperty("predecessors");
+    } else {
+      addIndent();
+      add("predecessors");
+      for (HBasicBlock predecessor in block.predecessors) {
+        add(' "B${predecessor.id}"');
+      }
+      add("\n");
+    }
+  }
+
+  void addSuccessors(HBasicBlock block) {
+    if (block.successors.isEmpty()) {
+      printEmptyProperty("successors");
+    } else {
+      addIndent();
+      add("successors");
+      for (HBasicBlock successor in block.successors) {
+        add(' "B${successor.id}"');
+      }
+      add("\n");
+    }
+  }
+
+  void addInstructions(HInstructionStringifier stringifier,
+                       HInstructionList list) {
+    for (HInstruction instruction = list.first;
+         instruction !== null;
+         instruction = instruction.next) {
+      int bci = 0;
+      int uses = instruction.usedBy.length;
+      addIndent();
+      String temporaryId = stringifier.temporaryId(instruction);
+      String instructionString = stringifier.visit(instruction);
+      add("$bci $uses $temporaryId $instructionString <|@\n");
+    }
+  }
+
+  void visitBasicBlock(HBasicBlock block) {
+    HInstructionStringifier stringifier = new HInstructionStringifier(block);
+    assert(block.id !== null);
+    tag("block", () {
+      printProperty("name", "B${block.id}");
+      printProperty("from_bci", -1);
+      printProperty("to_bci", -1);
+      addPredecessors(block);
+      addSuccessors(block);
+      printEmptyProperty("xhandlers");
+      printEmptyProperty("flags");
+      if (block.dominator !== null) {
+        printProperty("dominator", "B${block.dominator.id}");
+      }
+      tag("states", () {
+        tag("locals", () {
+          printProperty("size", 0);
+          printProperty("method", "None");
+          block.forEachPhi((phi) {
+            String phiId = stringifier.temporaryId(phi);
+            StringBuffer inputIds = new StringBuffer();
+            for (int i = 0; i < phi.inputs.length; i++) {
+              inputIds.add(stringifier.temporaryId(phi.inputs[i]));
+              inputIds.add(" ");
+            }
+            println("${phi.id} $phiId [ $inputIds]");
+          });
+        });
+      });
+      tag("HIR", () {
+        addInstructions(stringifier, block.phis);
+        addInstructions(stringifier, block);
+      });
+    });
+  }
+
+  void tag(String tagName, Function f) {
+    println("begin_$tagName");
+    indent++;
+    f();
+    indent--;
+    println("end_$tagName");
+  }
+
+  void println(String string) {
+    addIndent();
+    add(string);
+    add("\n");
+  }
+
+  void printEmptyProperty(String propertyName) {
+    println(propertyName);
+  }
+
+  void printProperty(String propertyName, var value) {
+    if (value is num) {
+      println("$propertyName $value");
+    } else {
+      println('$propertyName "$value"');
+    }
+  }
+
+  void add(String string) {
+    output.writeStringSync(string);
+  }
+
+  void addIndent() {
+    for (int i = 0; i < indent; i++) {
+      add("  ");
+    }
+  }
+}
+
+class HInstructionStringifier implements HVisitor<String> {
+  HBasicBlock currentBlock;
+
+  HInstructionStringifier(this.currentBlock);
+
+  visit(HInstruction node) => node.accept(this);
+
+  visitBasicBlock(HBasicBlock node) {
+    unreachable();
+  }
+
+  String temporaryId(HInstruction instruction) {
+    String prefix;
+    switch (instruction.type) {
+      case HType.ARRAY: prefix = 'a'; break;
+      case HType.BOOLEAN: prefix = 'b'; break;
+      case HType.INTEGER: prefix = 'i'; break;
+      case HType.DOUBLE: prefix = 'd'; break;
+      case HType.NUMBER: prefix = 'n'; break;
+      case HType.STRING: prefix = 's'; break;
+      case HType.UNKNOWN: prefix = 'v'; break;
+      case HType.CONFLICTING: prefix = 'c'; break;
+      case HType.STRING_OR_ARRAY: prefix = 'sa'; break;
+      default: unreachable();
+    }
+    return "$prefix${instruction.id}";
+  }
+
+  String visitBoolify(HBoolify node) {
+    return "Boolify: ${temporaryId(node.inputs[0])}";
+  }
+
+  String visitAdd(HAdd node) => visitInvokeStatic(node);
+
+  String visitBitAnd(HBitAnd node) => visitInvokeStatic(node);
+
+  String visitBitNot(HBitNot node) => visitInvokeStatic(node);
+
+  String visitBitOr(HBitOr node) => visitInvokeStatic(node);
+
+  String visitBitXor(HBitXor node) => visitInvokeStatic(node);
+
+  String visitBoundsCheck(HBoundsCheck node) {
+    String lengthId = temporaryId(node.length);
+    String indexId = temporaryId(node.index);
+    return "Bounds check: length = $lengthId, index = $indexId";
+  }
+
+  String visitBreak(HBreak node) {
+    HBasicBlock target = currentBlock.successors[0];
+    if (node.label !== null) {
+      return "Break ${node.label.labelName}: (B${target.id})";
+    }
+    return "Break: (B${target.id})";
+  }
+
+  String visitConstant(HConstant constant) => "Constant ${constant.constant}";
+
+  String visitContinue(HContinue node) {
+    HBasicBlock target = currentBlock.successors[0];
+    if (node.label !== null) {
+      return "Continue ${node.label.labelName}: (B${target.id})";
+    }
+    return "Continue: (B${target.id})";
+  }
+
+  String visitDivide(HDivide node) => visitInvokeStatic(node);
+
+  String visitEquals(HEquals node) => visitInvokeStatic(node);
+
+  String visitExit(HExit node) => "exit";
+
+  String visitFieldGet(HFieldGet node) {
+    return 'get ${node.element.name.slowToString()}';
+  }
+
+  String visitFieldSet(HFieldSet node) {
+    String valueId = temporaryId(node.value);
+    return 'set ${node.element.name.slowToString()} to $valueId';
+  }
+
+  String visitGoto(HGoto node) {
+    HBasicBlock target = currentBlock.successors[0];
+    return "Goto: (B${target.id})";
+  }
+
+  String visitGreater(HGreater node) => visitInvokeStatic(node);
+  String visitGreaterEqual(HGreaterEqual node) => visitInvokeStatic(node);
+
+  String visitIdentity(HIdentity node) => visitInvokeStatic(node);
+
+  String visitIf(HIf node) {
+    HBasicBlock thenBlock = currentBlock.successors[0];
+    HBasicBlock elseBlock = currentBlock.successors[1];
+    String conditionId = temporaryId(node.inputs[0]);
+    return "If ($conditionId): (B${thenBlock.id}) else (B${elseBlock.id})";
+  }
+
+  String visitGenericInvoke(String invokeType, String functionName,
+                            List<HInstruction> arguments) {
+    StringBuffer argumentsString = new StringBuffer();
+    for (int i = 0; i < arguments.length; i++) {
+      if (i != 0) argumentsString.add(", ");
+      argumentsString.add(temporaryId(arguments[i]));
+    }
+    return "$invokeType: $functionName($argumentsString)";
+  }
+
+  String visitIndex(HIndex node) => visitInvokeStatic(node);
+  String visitIndexAssign(HIndexAssign node) => visitInvokeStatic(node);
+
+  String visitIntegerCheck(HIntegerCheck node) {
+    String value = temporaryId(node.value);
+    return "Integer check: $value";
+  }
+
+  String visitInvokeClosure(HInvokeClosure node)
+      => visitInvokeDynamic(node, "closure");
+
+  String visitInvokeDynamic(HInvokeDynamic invoke, String kind) {
+    String receiver = temporaryId(invoke.receiver);
+    String target = "($kind) $receiver.${invoke.name.slowToString()}";
+    int offset = HInvoke.ARGUMENTS_OFFSET;
+    List arguments =
+        invoke.inputs.getRange(offset, invoke.inputs.length - offset);
+    return visitGenericInvoke("Invoke", target, arguments);
+  }
+
+  String visitInvokeDynamicMethod(HInvokeDynamicMethod node)
+      => visitInvokeDynamic(node, "method");
+  String visitInvokeDynamicGetter(HInvokeDynamicGetter node)
+      => visitInvokeDynamic(node, "get");
+  String visitInvokeDynamicSetter(HInvokeDynamicSetter node)
+      => visitInvokeDynamic(node, "set");
+
+  String visitInvokeInterceptor(HInvokeInterceptor invoke)
+      => visitInvokeStatic(invoke);
+
+  String visitInvokeStatic(HInvokeStatic invoke) {
+    String target = temporaryId(invoke.target);
+    int offset = HInvoke.ARGUMENTS_OFFSET;
+    List arguments =
+        invoke.inputs.getRange(offset, invoke.inputs.length - offset);
+    return visitGenericInvoke("Invoke", target, arguments);
+  }
+
+  String visitInvokeSuper(HInvokeSuper invoke) {
+    String target = temporaryId(invoke.target);
+    int offset = HInvoke.ARGUMENTS_OFFSET + 1;
+    List arguments =
+        invoke.inputs.getRange(offset, invoke.inputs.length - offset);
+    return visitGenericInvoke("Invoke super", target, arguments);
+  }
+
+  String visitForeign(HForeign foreign) {
+    return visitGenericInvoke("Foreign", "${foreign.code}", foreign.inputs);
+  }
+
+  String visitForeignNew(HForeignNew node) {
+    return visitGenericInvoke("New",
+                              "${node.element.name.slowToString()}",
+                              node.inputs);
+  }
+
+  String visitLess(HLess node) => visitInvokeStatic(node);
+  String visitLessEqual(HLessEqual node) => visitInvokeStatic(node);
+
+  String visitLiteralList(HLiteralList node) {
+    StringBuffer elementsString = new StringBuffer();
+    for (int i = 0; i < node.inputs.length; i++) {
+      if (i != 0) elementsString.add(", ");
+      elementsString.add(temporaryId(node.inputs[i]));
+    }
+    return "Literal list: [$elementsString]";
+  }
+
+  String visitLoopBranch(HLoopBranch branch) {
+    HBasicBlock bodyBlock = currentBlock.successors[0];
+    HBasicBlock exitBlock = currentBlock.successors[1];
+    String conditionId = temporaryId(branch.inputs[0]);
+    return "While ($conditionId): (B${bodyBlock.id}) then (B${exitBlock.id})";
+  }
+
+  String visitModulo(HModulo node) => visitInvokeStatic(node);
+
+  String visitMultiply(HMultiply node) => visitInvokeStatic(node);
+
+  String visitNegate(HNegate node) => visitInvokeStatic(node);
+
+  String visitNot(HNot node) => "Not: ${temporaryId(node.inputs[0])}";
+
+  String visitParameterValue(HParameterValue node) {
+    return "p${node.element.name.slowToString()}";
+  }
+
+  String visitPhi(HPhi phi) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.add("Phi(");
+    for (int i = 0; i < phi.inputs.length; i++) {
+      if (i > 0) buffer.add(", ");
+      buffer.add(temporaryId(phi.inputs[i]));
+    }
+    buffer.add(")");
+    return buffer.toString();
+  }
+
+  String visitReturn(HReturn node) => "Return ${temporaryId(node.inputs[0])}";
+
+  String visitShiftLeft(HShiftLeft node) => visitInvokeStatic(node);
+
+  String visitShiftRight(HShiftRight node) => visitInvokeStatic(node);
+
+  String visitStatic(HStatic node)
+      => "Static ${node.element.name.slowToString()}";
+  String visitStaticStore(HStaticStore node) {
+    String lhs = node.element.name.slowToString();
+    return "Static $lhs = ${temporaryId(node.inputs[0])}";
+  }
+
+  String visitSubtract(HSubtract node) => visitInvokeStatic(node);
+
+  String visitThis(HThis node) => "this";
+
+  String visitThrow(HThrow node) => "Throw ${temporaryId(node.inputs[0])}";
+
+  String visitTruncatingDivide(HTruncatingDivide node) {
+    return visitInvokeStatic(node);
+  }
+
+  String visitTry(HTry node) {
+    List<HBasicBlock> successors = currentBlock.successors;
+    String tryBlock = 'B${successors[0].id}';
+    StringBuffer catchBlocks = new StringBuffer();
+    for (int i = 1; i < successors.length - 1; i++) {
+      catchBlocks.add('B${successors[i].id}, ');
+    }
+
+    String finallyBlock;
+    if (node.finallyBlock != null) {
+      finallyBlock = 'B${node.finallyBlock.id}';
+    } else {
+      catchBlocks.add('B${successors[successors.length - 1].id}');
+      finallyBlock = 'none';
+    }
+    return "Try: $tryBlock, Catch: $catchBlocks, Finally: $finallyBlock";
+  }
+
+  String visitTypeGuard(HTypeGuard node) {
+    String type;
+    switch (node.type) {
+      case HType.ARRAY: type = "array"; break;
+      case HType.BOOLEAN: type = "bool"; break;
+      case HType.INTEGER: type = "integer"; break;
+      case HType.DOUBLE: type = "double"; break;
+      case HType.NUMBER: type = "number"; break;
+      case HType.STRING: type = "string"; break;
+      case HType.STRING_OR_ARRAY: type = "string_or_array"; break;
+      case HType.UNKNOWN: type = 'unknown'; break;
+      default: unreachable();
+    }
+    return "TypeGuard: ${temporaryId(node.inputs[0])} is $type";
+  }
+
+  String visitIs(HIs node) {
+    String type = node.typeExpression.toString();
+    return "TypeTest: ${temporaryId(node.expression)} is $type";
+  }
+}
diff --git a/lib/compiler/implementation/ssa/types.dart b/lib/compiler/implementation/ssa/types.dart
new file mode 100644
index 0000000..c58aa57
--- /dev/null
+++ b/lib/compiler/implementation/ssa/types.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2011, 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.
+
+class SsaTypePropagator extends HGraphVisitor implements OptimizationPhase {
+
+  final Map<int, HInstruction> workmap;
+  final List<int> worklist;
+  final Compiler compiler;
+  final String name = 'type propagator';
+
+  SsaTypePropagator(Compiler this.compiler)
+      : workmap = new Map<int, HInstruction>(),
+        worklist = new List<int>();
+
+  void visitGraph(HGraph graph) {
+    visitDominatorTree(graph);
+    processWorklist();
+  }
+
+  visitBasicBlock(HBasicBlock block) {
+    if (block.isLoopHeader()) {
+      block.forEachPhi((HPhi phi) {
+        phi.setInitialTypeForLoopPhi();
+        addToWorkList(phi);
+      });
+    } else {
+      block.forEachPhi((HPhi phi) {
+        if (phi.updateType()) addUsersAndInputsToWorklist(phi);
+      });
+    }
+
+    HInstruction instruction = block.first;
+    while (instruction !== null) {
+      if (instruction.updateType()) addUsersAndInputsToWorklist(instruction);
+      instruction = instruction.next;
+    }
+  }
+
+  void processWorklist() {
+    while (!worklist.isEmpty()) {
+      int id = worklist.removeLast();
+      HInstruction instruction = workmap[id];
+      assert(instruction !== null);
+      workmap.remove(id);
+      if (instruction.updateType()) addUsersAndInputsToWorklist(instruction);
+    }
+  }
+
+  void addUsersAndInputsToWorklist(HInstruction instruction) {
+    for (int i = 0, length = instruction.usedBy.length; i < length; i++) {
+      addToWorkList(instruction.usedBy[i]);
+    }
+    for (int i = 0, length = instruction.inputs.length; i < length; i++) {
+      addToWorkList(instruction.inputs[i]);
+    }
+  }
+
+  void addToWorkList(HInstruction instruction) {
+    final int id = instruction.id;
+    if (!workmap.containsKey(id)) {
+      worklist.add(id);
+      workmap[id] = instruction;
+    }
+  }
+}
diff --git a/lib/compiler/implementation/ssa/validate.dart b/lib/compiler/implementation/ssa/validate.dart
new file mode 100644
index 0000000..5851cad
--- /dev/null
+++ b/lib/compiler/implementation/ssa/validate.dart
@@ -0,0 +1,153 @@
+// Copyright (c) 2011, 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.
+
+class HValidator extends HInstructionVisitor {
+  bool isValid = true;
+  HGraph graph;
+
+  void visitGraph(HGraph graph) {
+    this.graph = graph;
+    visitDominatorTree(graph);
+  }
+
+  void markInvalid(String reason) {
+    print(reason);
+    isValid = false;
+  }
+
+  // Note that during construction of the Ssa graph the basic blocks are
+  // not required to be valid yet.
+  void visitBasicBlock(HBasicBlock block) {
+    currentBlock = block;
+    if (!isValid) return;  // Don't need to continue if we are already invalid.
+
+    // Test that the last instruction is a branching instruction and that the
+    // basic block contains the branch-target.
+    if (block.first === null || block.last === null) {
+      markInvalid("empty block");
+    }
+    if (block.last is !HControlFlow) {
+      markInvalid("block ends with non-tail node.");
+    }
+    if (block.last is HIf && block.successors.length != 2) {
+      markInvalid("If node without two successors");
+    }
+    if (block.last is HConditionalBranch && block.successors.length != 2) {
+      markInvalid("Conditional node without two successors");
+    }
+    if (block.last is HGoto && block.successors.length != 1) {
+      markInvalid("Goto node without one successor");
+    }
+    if (block.last is HReturn &&
+        (block.successors.length != 1 || !block.successors[0].isExitBlock())) {
+      markInvalid("Return node with > 1 succesor or not going to exit-block");
+    }
+    if (block.last is HExit && !block.successors.isEmpty()) {
+      markInvalid("Exit block with successor");
+    }
+    if (block.last is HThrow && !block.successors.isEmpty()) {
+      markInvalid("Throw block with successor");
+    }
+
+    if (block.successors.isEmpty() &&
+        block.last is !HThrow &&
+        !block.isExitBlock()) {
+      markInvalid("Non-exit or throw block without successor");
+    }
+
+    // Make sure that successors ids are always higher than the current one.
+    // TODO(floitsch): this is, of course, not true for back-branches.
+    if (block.id === null) markInvalid("block without id");
+    for (HBasicBlock successor in block.successors) {
+      if (!isValid) break;
+      if (successor.id === null) markInvalid("successor without id");
+      if (successor.id <= block.id && !successor.isLoopHeader()) {
+        markInvalid("successor with lower id, but not a loop-header");
+      }
+    }
+
+    // Make sure that the entries in the dominated-list are sorted.
+    int lastId = 0;
+    for (HBasicBlock dominated in block.dominatedBlocks) {
+      if (!isValid) break;
+      if (dominated.dominator !== block) {
+        markInvalid("dominated block not pointing back");
+      }
+      if (dominated.id === null || dominated.id <= lastId) {
+        markInvalid("dominated.id === null or dominated has <= id");
+      }
+      lastId = dominated.id;
+    }
+
+    if (!isValid) return;
+    block.forEachPhi(visitInstruction);
+    super.visitBasicBlock(block);
+  }
+
+  /** Returns how often [instruction] is contained in [instructions]. */
+  static int countInstruction(List<HInstruction> instructions,
+                              HInstruction instruction) {
+    int result = 0;
+    for (int i = 0; i < instructions.length; i++) {
+      if (instructions[i] === instruction) result++;
+    }
+    return result;
+  }
+
+  /**
+   * Returns true if the predicate returns true for every instruction in the
+   * list. The argument to [f] is an instruction with the count of how often
+   * it appeared in the list [instructions].
+   */
+  static bool everyInstruction(List<HInstruction> instructions, Function f) {
+    var copy = new List<HInstruction>.from(instructions);
+    // TODO(floitsch): there is currently no way to sort HInstructions before
+    // we have assigned an ID. The loop is therefore O(n^2) for now.
+    for (int i = 0; i < copy.length; i++) {
+      var current = copy[i];
+      if (current === null) continue;
+      int count = 1;
+      for (int j = i + 1; j < copy.length; j++) {
+        if (copy[j] === current) {
+          copy[j] = null;
+          count++;
+        }
+      }
+      if (!f(current, count)) return false;
+    }
+    return true;
+  }
+
+  void visitInstruction(HInstruction instruction) {
+    // Verifies that we are in the use list of our inputs.
+    bool hasCorrectInputs(instruction) {
+      bool inBasicBlock = instruction.isInBasicBlock();
+      return everyInstruction(instruction.inputs, (input, count) {
+        if (inBasicBlock) {
+          return countInstruction(input.usedBy, instruction) == count;
+        } else {
+          return countInstruction(input.usedBy, instruction) == 0;
+        }
+      });
+    }
+
+    // Verifies that all our uses have us in their inputs.
+    bool hasCorrectUses(instruction) {
+      if (!instruction.isInBasicBlock()) return true;
+      return everyInstruction(instruction.usedBy, (use, count) {
+        return countInstruction(use.inputs, instruction) == count;
+      });
+    }
+
+    if (instruction.block !== currentBlock) {
+      markInvalid("Instruction in wrong block");
+    }
+    if (!hasCorrectInputs(instruction)) {
+      markInvalid("Incorrect inputs");
+    }
+    if (!hasCorrectUses(instruction)) {
+      markInvalid("Incorrect uses");
+    }
+  }
+}
diff --git a/lib/compiler/implementation/ssa/value_set.dart b/lib/compiler/implementation/ssa/value_set.dart
new file mode 100644
index 0000000..9447dfd
--- /dev/null
+++ b/lib/compiler/implementation/ssa/value_set.dart
@@ -0,0 +1,153 @@
+// Copyright (c) 2011, 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.
+
+class ValueSet {
+  int size = 0;
+  List<HInstruction> table;
+  ValueSetNode collisions;
+  ValueSet() : table = new List<HInstruction>(8);
+
+  bool isEmpty() => size == 0;
+  int get length() => size;
+
+  void add(HInstruction instruction) {
+    assert(lookup(instruction) === null);
+    int hashCode = instruction.gvnHashCode();
+    int capacity = table.length;
+    // Resize when half of the hash table is in use.
+    if (size >= capacity >> 1) {
+      capacity = capacity << 1;
+      resize(capacity);
+    }
+    // Try to insert in the hash table first.
+    int index = hashCode % capacity;
+    if (table[index] === null) {
+      table[index] = instruction;
+    } else {
+      collisions = new ValueSetNode(instruction, hashCode, collisions);
+    }
+    size++;
+  }
+
+  HInstruction lookup(HInstruction instruction) {
+    int hashCode = instruction.gvnHashCode();
+    int index = hashCode % table.length;
+    // Look in the hash table.
+    HInstruction probe = table[index];
+    if (probe !== null && probe.gvnEquals(instruction)) return probe;
+    // Look in the collisions list.
+    for (ValueSetNode node = collisions; node !== null; node = node.next) {
+      if (node.hashCode == hashCode) {
+        HInstruction cached = node.value;
+        if (cached.gvnEquals(instruction)) return cached;
+      }
+    }
+    return null;
+  }
+
+  void kill(int flags) {
+    int depends = HInstruction.computeDependsOnFlags(flags);
+    // Kill in the hash table.
+    for (int index = 0, length = table.length; index < length; index++) {
+      HInstruction instruction = table[index];
+      if (instruction !== null && (instruction.flags & depends) != 0) {
+        table[index] = null;
+        size--;
+      }
+    }
+    // Kill in the collisions list.
+    ValueSetNode previous = null;
+    ValueSetNode current = collisions;
+    while (current !== null) {
+      ValueSetNode next = current.next;
+      HInstruction cached = current.value;
+      if ((cached.flags & depends) != 0) {
+        if (previous === null) {
+          collisions = next;
+        } else {
+          previous.next = next;
+        }
+        size--;
+      } else {
+        previous = current;
+      }
+      current = next;
+    }
+  }
+
+  ValueSet copy() {
+    return copyTo(new ValueSet(), table, collisions);
+  }
+
+  List<HInstruction> toList() {
+    return copyTo(<HInstruction>[], table, collisions);
+  }
+
+  // Copy the instructions in value set defined by [table] and
+  // [collisions] into [other] and returns [other]. The copy is done
+  // by iterating through the hash table and the collisions list and
+  // calling [:other.add:].
+  static copyTo(var other, List<HInstruction> table, ValueSetNode collisions) {
+    // Copy elements from the hash table.
+    for (int index = 0, length = table.length; index < length; index++) {
+      HInstruction instruction = table[index];
+      if (instruction !== null) other.add(instruction);
+    }
+    // Copy elements from the collision list.
+    ValueSetNode current = collisions;
+    while (current !== null) {
+      // TODO(kasperl): Maybe find a way of reusing the hash code
+      // rather than recomputing it every time.
+      other.add(current.value);
+      current = current.next;
+    }
+    return other;
+  }
+
+  ValueSet intersection(ValueSet other) {
+    if (size > other.size) return other.intersection(this);
+    ValueSet result = new ValueSet();
+    // Look in the hash table.
+    for (int index = 0, length = table.length; index < length; index++) {
+      HInstruction instruction = table[index];
+      if (instruction !== null && other.lookup(instruction) !== null) {
+        result.add(instruction);
+      }
+    }
+    // Look in the collision list.
+    ValueSetNode current = collisions;
+    while (current !== null) {
+      HInstruction value = current.value;
+      if (other.lookup(value) !== null) {
+        result.add(value);
+      }
+      current = current.next;
+    }
+    return result;
+  }
+
+  void resize(int capacity) {
+    var oldSize = size;
+    var oldTable = table;
+    var oldCollisions = collisions;
+    // Reset the table with a bigger capacity.
+    assert(capacity > table.length);
+    size = 0;
+    table = new List<HInstruction>(capacity);
+    collisions = null;
+    // Add the old instructions to the new table.
+    copyTo(this, oldTable, oldCollisions);
+    // Make sure we preserved all elements and that no resizing
+    // happened as part of this resizing.
+    assert(size == oldSize);
+    assert(table.length == capacity);
+  }
+}
+
+class ValueSetNode {
+  final HInstruction value;
+  final int hashCode;
+  ValueSetNode next;
+  ValueSetNode(this.value, this.hashCode, this.next);
+}
diff --git a/lib/compiler/implementation/string_validator.dart b/lib/compiler/implementation/string_validator.dart
new file mode 100644
index 0000000..1017931
--- /dev/null
+++ b/lib/compiler/implementation/string_validator.dart
@@ -0,0 +1,187 @@
+// Copyright (c) 2012, 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.
+
+// Check the validity of string literals.
+
+#library("stringvalidator");
+
+#import("leg.dart");
+#import("scanner/scannerlib.dart");
+#import("tree/tree.dart");
+#import("elements/elements.dart");
+#import("util/characters.dart");
+
+class StringValidator  {
+  final DiagnosticListener listener;
+
+  StringValidator(this.listener);
+
+  DartString validateQuotedString(Token token) {
+    SourceString source = token.value;
+    StringQuoting quoting = quotingFromString(source);
+    int leftQuote = quoting.leftQuoteLength;
+    int rightQuote = quoting.rightQuoteLength;
+    SourceString content = source.copyWithoutQuotes(leftQuote, rightQuote);
+    return validateString(token,
+                          token.charOffset + leftQuote,
+                          content,
+                          quoting);
+  }
+
+  DartString validateInterpolationPart(Token token, StringQuoting quoting,
+                                       [bool isFirst = false,
+                                        bool isLast = false]) {
+    SourceString source = token.value;
+    int leftQuote = 0;
+    int rightQuote = 0;
+    if (isFirst) leftQuote = quoting.leftQuoteLength;
+    if (isLast) rightQuote = quoting.rightQuoteLength;
+    SourceString content = source.copyWithoutQuotes(leftQuote, rightQuote);
+    return validateString(token,
+                          token.charOffset + leftQuote,
+                          content,
+                          quoting);
+  }
+
+  static StringQuoting quotingFromString(SourceString sourceString) {
+    Iterator<int> source = sourceString.iterator();
+    bool raw = false;
+    int quoteLength = 1;
+    int quoteChar = source.next();
+    if (quoteChar == $AT) {
+      raw = true;
+      quoteChar = source.next();
+    }
+    assert(quoteChar === $SQ || quoteChar === $DQ);
+    // String has at least one quote. Check it if has three.
+    // If it only have two, the string must be an empty string literal,
+    // and end after the second quote.
+    bool multiline = false;
+    if (source.hasNext() && source.next() === quoteChar && source.hasNext()) {
+      int code = source.next();
+      assert(code === quoteChar);  // If not, there is a bug in the parser.
+      quoteLength = 3;
+      // Check if a multiline string starts with a newline (CR, LF or CR+LF).
+      if (source.hasNext()) {
+        code = source.next();
+        if (code === $CR) {
+          quoteLength += 1;
+          if (source.hasNext() && source.next() === $LF) {
+            quoteLength += 1;
+          }
+        } else if (code === $LF) {
+          quoteLength += 1;
+        }
+      }
+    }
+    return StringQuoting.getQuoting(quoteChar, raw, quoteLength);
+  }
+
+  void stringParseError(String message, Token token, int offset) {
+    listener.cancel("$message @ $offset", token : token);
+  }
+
+  /**
+   * Validates the escape sequences and special characters of a string literal.
+   * Returns a DartString if valid, and null if not.
+   */
+  DartString validateString(Token token,
+                            int startOffset,
+                            SourceString string,
+                            StringQuoting quoting) {
+    // We only need to check for invalid x and u escapes, for line
+    // terminators in non-multiline strings, and for invalid Unicode
+    // scalar values (either directly or as u-escape values).
+    int length = 0;
+    int index = startOffset;
+    bool containsEscape = false;
+    for(Iterator<int> iter = string.iterator(); iter.hasNext(); length++) {
+      index++;
+      int code = iter.next();
+      if (code === $BACKSLASH) {
+        if (quoting.raw) continue;
+        containsEscape = true;
+        if (!iter.hasNext()) {
+          stringParseError("Incomplete escape sequence",token, index);
+          return null;
+        }
+        index++;
+        code = iter.next();
+        if (code === $x) {
+          for (int i = 0; i < 2; i++) {
+            if (!iter.hasNext()) {
+              stringParseError("Incomplete escape sequence", token, index);
+              return null;
+            }
+            index++;
+            code = iter.next();
+            if (!isHexDigit(code)) {
+              stringParseError("Invalid character in escape sequence",
+                               token, index);
+              return null;
+            }
+          }
+          // A two-byte hex escape can't generate an invalid value.
+          continue;
+        } else if (code === $u) {
+          int escapeStart = index - 1;
+          index++;
+          code = iter.next();
+          int value = 0;
+          if (code == $OPEN_CURLY_BRACKET) {
+            // expect 1-6 hex digits.
+            int count = 0;
+            index++;
+            code = iter.next();
+            do {
+              if (!isHexDigit(code)) {
+                stringParseError("Invalid character in escape sequence",
+                                 token, index);
+                return null;
+              }
+              count++;
+              value = value * 16 + hexDigitValue(code);
+              index++;
+              code = iter.next();
+            } while (code != $CLOSE_CURLY_BRACKET);
+            if (count > 6) {
+              stringParseError("Invalid character in escape sequence",
+                               token, index - (count - 6));
+              return null;
+            }
+          } else {
+            // Expect four hex digits, including the one just read.
+            for (int i = 0; i < 4; i++) {
+              if (i > 0) {
+                index++;
+                code = iter.next();
+              }
+              if (!isHexDigit(code)) {
+                stringParseError("Invalid character in escape sequence",
+                                 token, index);
+                return null;
+              }
+              value = value * 16 + hexDigitValue(code);
+            }
+          }
+          code = value;
+        }
+      }
+      // This handles both unescaped characters and the value of unicode
+      // escapes.
+      if (!isUnicodeScalarValue(code)) {
+        stringParseError(
+            "Invalid Unicode scalar value U+${code.toRadixString(16)}",
+            token, index);
+        return null;
+      }
+    }
+    // String literal successfully validated.
+    if (quoting.raw || !containsEscape) {
+      // A string without escapes could just as well have been raw.
+      return new DartString.rawString(string, length);
+    }
+    return new DartString.escapedString(string, length);
+  }
+}
diff --git a/lib/compiler/implementation/tools/find_file_to_parse.sh b/lib/compiler/implementation/tools/find_file_to_parse.sh
new file mode 100755
index 0000000..9dd1405
--- /dev/null
+++ b/lib/compiler/implementation/tools/find_file_to_parse.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+find .. \( \
+    -name README.dart \
+    -o -name Examples_A03_t01.dart \
+    -o -name Examples_A03_t02.dart \
+    -o -name Examples_A07_t01.dart \
+    -o -name 13_3_1_Typedef_A01_t02.dart \
+    -o -name 13_3_1_Typedef_A01_t03.dart \
+    -o -name 13_3_1_Typedef_A01_t04.dart \
+    -o -name 13_3_1_Typedef_A01_t06.dart \
+    -o -name 13_3_1_Typedef_A05_t01.dart \
+    -o -name 13_3_1_Typedef_A05_t02.dart \
+    -o -name 13_3_1_Typedef_A05_t03.dart \
+    -o -name 13_3_1_Typedef_A06_t01.dart \
+    -o -name 13_3_1_Typedef_A06_t03.dart \
+    -o -name 13_3_1_Typedef_A06_t04.dart \
+    -o -name 13_7_Type_Void_A01_t06.dart \
+    -o -name 13_7_Type_Void_A01_t07.dart \
+    -o -name 02_1_Class_A02_t02.dart \
+    -o -name 'Map_operator\[\]_A01_t03.dart' \
+    -o -name 'Map_operator\[\]=_A01_t03.dart' \
+    -o -name int_operator_mul_A01_t01.dart \
+    -o -name Isolate_A01_t01.dart \
+    -o -name Isolate_A02_t01.dart \
+    -o -name IsNotClass4NegativeTest.dart \
+    -o -name NamedParameters9NegativeTest.dart \
+    -o -name ClassKeywordTest.dart \
+    -o -name Prefix19NegativeTest.dart \
+    -o -name Operator2NegativeTest.dart \
+    -o -name 02_1_Class_Construction_A16_t02.dart \
+    -o -name 02_1_Class_Construction_A19_t01.dart \
+    -o -name 02_2_Interface_A02_t02.dart \
+    -o -name 13_4_Interface_Types_A04_t01.dart \
+    -o -name 13_4_Interface_Types_A04_t02.dart \
+    -o -name MapLiteral2Test.dart \
+    -o -name Switch1NegativeTest.dart \
+    -o \( -type d -name xcodebuild \) \
+    -o \( -type d -name out \) \
+    -o \( -type d -name await \) \
+    \) -prune -o \
+    -name \*.dart -type f -print \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/ClassTest.dart \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/FunctionTest.dart \
+    | grep -v /compiler/javatests/com/google/dart/compiler/parser/StringsErrorsNegativeTest.dart \
+    | grep -v /compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsUnknownInterfaceNegativeTest.dart \
+    | grep -v /tests/language/src/InterfaceFunctionTypeAlias1NegativeTest.dart \
+    | grep -v /tests/language/src/InterfaceFunctionTypeAlias2NegativeTest.dart \
+    | grep -v /tests/language/src/InterfaceInjection1NegativeTest.dart \
+    | grep -v /tests/language/src/InterfaceFunctionTypeAlias3NegativeTest.dart \
+    | grep -v /tests/language/src/InterfaceInjection2NegativeTest.dart \
+    | grep -v /tests/language/src/NewExpression2NegativeTest.dart \
+    | grep -v /tests/language/src/NewExpression3NegativeTest.dart \
+    | grep -v /tests/language/src/TestNegativeTest.dart \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/BadErrorMessages.dart \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/CoreRuntimeTypesTest.dart \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/NamingTest.dart \
+    | grep -v /editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/internal/model/testsource/SpreadArgumentTest.dart \
+    | grep -v /tests/language/src/IsNotClass1NegativeTest.dart \
+    | grep -v /tests/language/src/Label8NegativeTest.dart \
+    | grep -v /frog/tests/await/ \
+    | grep -v /tests/language/src/ListLiteralNegativeTest.dart \
+    | grep -v /tests/language/src/MapLiteralNegativeTest.dart \
+    | grep -v /tests/language/src/TryCatch2NegativeTest.dart \
+    | grep -v /tests/language/src/NewExpression1NegativeTest.dart \
+    | grep -v /tests/language/src/TryCatch4NegativeTest.dart \
+    | grep -v /tests/language/src/ParameterInitializer3NegativeTest.dart \
+    | grep -v /compiler/javatests/com/google/dart/compiler/parser/FactoryInitializersNegativeTest.dart \
+    | grep -v /frog/tests/leg_only/src/TypedLocalsTest.dart \
+    | grep -v '/editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/formatter/testsource/test006$A_in.dart' \
+    | grep -v '/editor/tools/plugins/com.google.dart.tools.core_test/src/com/google/dart/tools/core/formatter/testsource/test006$A_out.dart' \
+    | grep -v '/utils/dartdoc/dartdoc.dart' \
+    | xargs grep -L -E 'native|@compile-error|@needsreview'
diff --git a/lib/compiler/implementation/tools/mini_parser.dart b/lib/compiler/implementation/tools/mini_parser.dart
new file mode 100644
index 0000000..6d518fb
--- /dev/null
+++ b/lib/compiler/implementation/tools/mini_parser.dart
@@ -0,0 +1,333 @@
+// Copyright (c) 2012, 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('parser');
+
+#import('dart:io');
+
+#import('../../../utf/utf.dart');
+
+#import('../elements/elements.dart');
+#import('../scanner/scanner_implementation.dart');
+#import('../scanner/scannerlib.dart');
+#import('../tree/tree.dart');
+#import('../util/characters.dart');
+
+#source('../diagnostic_listener.dart');
+#source('../../source.dart');
+#source('../scanner/byte_array_scanner.dart');
+#source('../scanner/byte_strings.dart');
+
+int charCount = 0;
+Stopwatch stopwatch;
+
+void main() {
+  filesWithCrashes = [];
+  stopwatch = new Stopwatch();
+  MyOptions options = new MyOptions();
+
+  void printStats() {
+    int kb = (charCount / 1024).round().toInt();
+    String stats =
+        '$classCount classes (${kb}Kb) in ${stopwatch.elapsedInMs()}ms';
+    if (errorCount != 0) {
+      stats += ' with $errorCount errors';
+    }
+    if (options.diet) {
+      print('Diet parsed $stats.');
+    } else {
+      print('Parsed $stats.');
+    }
+    if (filesWithCrashes.length !== 0) {
+      print('The following ${filesWithCrashes.length} files caused a crash:');
+      for (String file in filesWithCrashes) {
+        print(file);
+      }
+    }
+  }
+
+  for (String argument in new Options().arguments) {
+    if (argument == "--diet") {
+      options.diet = true;
+      continue;
+    }
+    if (argument == "--throw") {
+      options.throwOnError = true;
+      continue;
+    }
+    if (argument == "--scan-only") {
+      options.scanOnly = true;
+      continue;
+    }
+    if (argument == "--read-only") {
+      options.readOnly = true;
+      continue;
+    }
+    if (argument == "--ast") {
+      options.buildAst = true;
+      continue;
+    }
+    if (argument == "-") {
+      parseFilesFrom(stdin, options, printStats);
+      return;
+    }
+    stopwatch.start();
+    parseFile(argument, options);
+    stopwatch.stop();
+  }
+
+  printStats();
+}
+
+void parseFile(String filename, MyOptions options) {
+  List<int> bytes = read(filename);
+  charCount += bytes.length;
+  if (options.readOnly) return;
+  MySourceFile file = new MySourceFile(filename, bytes);
+  final Listener listener = options.buildAst
+      ? new MyNodeListener(file, options)
+      : new MyListener(file);
+  final Parser parser = options.diet
+      ? new PartialParser(listener)
+      : new Parser(listener);
+  try {
+    Token token = scan(file);
+    if (!options.scanOnly) parser.parseUnit(token);
+  } catch (ParserError ex) {
+    if (options.throwOnError) {
+      throw;
+    } else {
+      print(ex);
+    }
+  } catch (MalformedInputException ex) {
+    // Already diagnosed.
+  } catch (var ex) {
+    print('Error in file: $filename');
+    throw;
+  }
+  if (options.buildAst) {
+    MyNodeListener l = listener;
+    if (!l.nodes.isEmpty()) {
+      String message = 'Stack not empty after parsing';
+      print(formatError(message, l.nodes.head.getBeginToken(),
+                        l.nodes.head.getEndToken(), file));
+      throw message;
+    }
+  }
+}
+
+Token scan(MySourceFile source) {
+  Scanner scanner = new ByteArrayScanner(source.rawText);
+  try {
+    return scanner.tokenize();
+  } catch (MalformedInputException ex) {
+    if (ex.position is Token) {
+      print(formatError(ex.message, ex.position, ex.position, source));
+    } else {
+      Token fakeToken = new Token(QUESTION_INFO, ex.position);
+      print(formatError(ex.message, fakeToken, fakeToken, source));
+    }
+    throw;
+  }
+}
+
+var filesWithCrashes;
+
+void parseFilesFrom(InputStream input, MyOptions options, Function whenDone) {
+  void readLine(String line) {
+    stopwatch.start();
+    try {
+      parseFile(line, options);
+    } catch (var ex, var trace) {
+      filesWithCrashes.add(line);
+      print(ex);
+      print(trace);
+    }
+    stopwatch.stop();
+  }
+  forEachLine(input, readLine, whenDone);
+}
+
+void forEachLine(InputStream input,
+                 void lineHandler(String line),
+                 void closeHandler()) {
+  StringInputStream stringStream = new StringInputStream(input);
+  stringStream.onLine = () {
+    String line;
+    while ((line = stringStream.readLine()) !== null) {
+      lineHandler(line);
+    }
+  };
+  stringStream.onClosed = closeHandler;
+}
+
+List<int> read(String filename) {
+  RandomAccessFile file = new File(filename).openSync();
+  bool threw = true;
+  try {
+    int size = file.lengthSync();
+    List<int> bytes = new ByteArray(size + 1);
+    file.readListSync(bytes, 0, size);
+    bytes[size] = $EOF;
+    threw = false;
+    return bytes;
+  } finally {
+    try {
+      file.closeSync();
+    } catch (var ex) {
+      if (!threw) throw;
+    }
+  }
+}
+
+int classCount = 0;
+int errorCount = 0;
+
+class MyListener extends Listener {
+  final SourceFile file;
+
+  MyListener(this.file);
+
+  void beginClassDeclaration(Token token) {
+    classCount++;
+  }
+
+  void beginInterface(Token token) {
+    classCount++;
+  }
+
+  void error(String message, Token token) {
+    throw new ParserError(formatError(message, token, token, file));
+  }
+}
+
+String formatError(String message, Token beginToken, Token endToken,
+                   SourceFile file) {
+  ++errorCount;
+  if (beginToken === null) return '${file.filename}: $message';
+  String tokenString = endToken.toString();
+  int begin = beginToken.charOffset;
+  int end = endToken.charOffset + tokenString.length;
+  return file.getLocationMessage(message, begin, end, true);
+}
+
+class MyNodeListener extends NodeListener {
+  MyNodeListener(SourceFile file, MyOptions options)
+    : super(new MyCanceller(file, options), null);
+
+  void beginClassDeclaration(Token token) {
+    classCount++;
+  }
+
+  void beginInterface(Token token) {
+    classCount++;
+  }
+
+  void endClassDeclaration(int interfacesCount, Token beginToken,
+                           Token extendsKeyword, Token implementsKeyword,
+                           Token endToken) {
+    super.endClassDeclaration(interfacesCount, beginToken,
+                              extendsKeyword, implementsKeyword,
+                              endToken);
+    ClassNode node = popNode(); // Discard ClassNode and assert the type.
+  }
+
+  void endInterface(int supertypeCount, Token interfaceKeyword,
+                    Token extendsKeyword, Token endToken) {
+    super.endInterface(supertypeCount, interfaceKeyword, extendsKeyword,
+                       endToken);
+    ClassNode node = popNode(); // Discard ClassNode and assert the type.
+  }
+
+  void endTopLevelFields(int count, Token beginToken, Token endToken) {
+    super.endTopLevelFields(count, beginToken, endToken);
+    VariableDefinitions node = popNode(); // Discard node and assert the type.
+  }
+
+  void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
+    super.endFunctionTypeAlias(typedefKeyword, endToken);
+    Typedef node = popNode(); // Discard Typedef and assert type type.
+  }
+
+  void endLibraryTag(bool hasPrefix, Token beginToken, Token endToken) {
+    super.endLibraryTag(hasPrefix, beginToken, endToken);
+    ScriptTag node = popNode(); // Discard ScriptTag and assert type type.
+  }
+
+  void log(message) {
+    print(message);
+  }
+}
+
+class MyCanceller implements DiagnosticListener {
+  final SourceFile file;
+  final MyOptions options;
+
+  MyCanceller(this.file, this.options);
+
+  void log(String message) {}
+
+  void cancel([String reason, node, token, instruction, element]) {
+    Token beginToken;
+    Token endToken;
+    if (token !== null) {
+      beginToken = token;
+      endToken = token;
+    } else if (node !== null) {
+      beginToken = node.getBeginToken();
+      endToken = node.getEndToken();
+    }
+    String message = formatError(reason, beginToken, endToken, file);
+    if (options.throwOnError) throw new ParserError(message);
+    print(message);
+  }
+}
+
+class MyOptions {
+  bool diet = false;
+  bool throwOnError = false;
+  bool scanOnly = false;
+  bool readOnly = false;
+  bool buildAst = false;
+}
+
+class MySourceFile extends SourceFile {
+  final rawText;
+  var stringText;
+
+  MySourceFile(filename, this.rawText) : super(filename, null);
+
+  String get text() {
+    if (rawText is String) {
+      return rawText;
+    } else {
+      if (stringText === null) {
+        stringText = new String.fromCharCodes(rawText);
+        if (stringText.endsWith('\u0000')) {
+          // Strip trailing NUL used by ByteArrayScanner to signal EOF.
+          stringText = stringText.substring(0, stringText.length - 1);
+        }
+      }
+      return stringText;
+    }
+  }
+
+  set text(String newText) {
+    throw "not supported";
+  }
+}
+
+// Hacks to allow sourcing in ../source.dart:
+var world = const Mock();
+var options = const Mock();
+String _GREEN_COLOR = '\u001b[32m';
+String _RED_COLOR = '\u001b[31m';
+String _MAGENTA_COLOR = '\u001b[35m';
+String _NO_COLOR = '\u001b[0m';
+
+class Mock {
+  const Mock();
+  bool get useColors() => true;
+  internalError(message) { throw message.toString(); }
+}
diff --git a/lib/compiler/implementation/tree/dartstring.dart b/lib/compiler/implementation/tree/dartstring.dart
new file mode 100644
index 0000000..e46a03a
--- /dev/null
+++ b/lib/compiler/implementation/tree/dartstring.dart
@@ -0,0 +1,209 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * The [DartString] type represents a Dart string value as a sequence of Unicode
+ * Scalar Values.
+ * After parsing, any valid [LiteralString] will contain a [DartString]
+ * representing its content after removing quotes and resolving escapes in
+ * its source.
+ */
+class DartString implements Iterable<int> {
+  factory DartString.empty() => const LiteralDartString("");
+  // This is a convenience constructor. If you need a const literal DartString,
+  // use [const LiteralDartString(string)] directly.
+  factory DartString.literal(String string) => new LiteralDartString(string);
+  factory DartString.rawString(SourceString source, int length) =>
+      new RawSourceDartString(source, length);
+  factory DartString.escapedString(SourceString source, int length) =>
+      new EscapedSourceDartString(source, length);
+  factory DartString.concat(DartString first, DartString second) {
+    if (first.isEmpty()) return second;
+    if (second.isEmpty()) return first;
+    return new ConsDartString(first, second);
+  }
+  const DartString();
+  abstract int get length();
+  bool isEmpty() => length == 0;
+  abstract Iterator<int> iterator();
+  abstract String slowToString();
+
+  bool operator ==(var other) {
+    if (other is !DartString) return false;
+    DartString otherString = other;
+    if (length != otherString.length) return false;
+    Iterator it1 = iterator();
+    Iterator it2 = otherString.iterator();
+    while (it1.hasNext()) {
+      if (it1.next() != it2.next()) return false;
+    }
+    return true;
+  }
+  String toString() => "DartString#${length}:${slowToString()}";
+  abstract SourceString get source();
+}
+
+
+/**
+ * A [DartString] where the content is represented by an actual [String].
+ */
+class LiteralDartString extends DartString {
+  final String string;
+  const LiteralDartString(this.string);
+  int get length() => string.length;
+  Iterator<int> iterator() => new StringCodeIterator(string);
+  String slowToString() => string;
+  SourceString get source() => new StringWrapper(string);
+}
+
+/**
+ * A [DartString] where the content comes from a slice of the program source.
+ */
+class SourceBasedDartString extends DartString {
+  String toStringCache = null;
+  final SourceString source;
+  final int length;
+  SourceBasedDartString(this.source, this.length);
+  abstract Iterator<int> iterator();
+}
+
+/**
+ * Special case of a [SourceBasedDartString] where we know the source doesn't
+ * contain any escapes.
+ */
+class RawSourceDartString extends SourceBasedDartString {
+  RawSourceDartString(source, length) : super(source, length);
+  Iterator<int> iterator() => source.iterator();
+  String slowToString() {
+    if (toStringCache !== null) return toStringCache;
+    toStringCache  = source.slowToString();
+    return toStringCache;
+  }
+}
+
+/**
+ * General case of a [SourceBasedDartString] where the source might contain
+ * escapes.
+ */
+class EscapedSourceDartString extends SourceBasedDartString {
+  EscapedSourceDartString(source, length) : super(source, length);
+  Iterator<int> iterator() {
+    if (toStringCache !== null) return new StringCodeIterator(toStringCache);
+    return new StringEscapeIterator(source);
+  }
+  String slowToString() {
+    if (toStringCache !== null) return toStringCache;
+    StringBuffer buffer = new StringBuffer();
+    StringEscapeIterator it = new StringEscapeIterator(source);
+    while (it.hasNext()) {
+      buffer.addCharCode(it.next());
+    }
+    toStringCache = buffer.toString();
+    return toStringCache;
+  }
+}
+
+/**
+ * The concatenation of two [DartString]s.
+ */
+class ConsDartString extends DartString {
+  final DartString left;
+  final DartString right;
+  final int length;
+  String toStringCache;
+  ConsDartString(DartString left, DartString right)
+      : this.left = left,
+        this.right = right,
+        length = left.length + right.length;
+
+  Iterator<int> iterator() => new ConsDartStringIterator(this);
+
+  String slowToString() {
+    if (toStringCache !== null) return toStringCache;
+    toStringCache = left.slowToString().concat(right.slowToString());
+    return toStringCache;
+  }
+  SourceString get source() => new StringWrapper(slowToString());
+}
+
+class ConsDartStringIterator implements Iterator<int> {
+  Iterator<int> current;
+  DartString right;
+  bool hasNextLookAhead;
+  ConsDartStringIterator(ConsDartString cons)
+      : current = cons.left.iterator(),
+        right = cons.right {
+    hasNextLookAhead = current.hasNext();
+    if (!hasNextLookAhead) {
+      nextPart();
+    }
+  }
+  bool hasNext() {
+    return hasNextLookAhead;
+  }
+  int next() {
+    assert(hasNextLookAhead);
+    int result = current.next();
+    hasNextLookAhead = current.hasNext();
+    if (!hasNextLookAhead) {
+      nextPart();
+    }
+    return result;
+  }
+  void nextPart() {
+    if (right !== null) {
+      current = right.iterator();
+      right = null;
+      hasNextLookAhead = current.hasNext();
+    }
+  }
+}
+
+/**
+ *Iterator that returns the actual string contents of a string with escapes.
+ */
+class StringEscapeIterator implements Iterator<int>{
+  final Iterator<int> source;
+  StringEscapeIterator(SourceString source) : this.source = source.iterator();
+  bool hasNext() => source.hasNext();
+  int next() {
+    int code = source.next();
+    if (code !== $BACKSLASH) {
+      return code;
+    }
+    code = source.next();
+    if (code === $n) return $LF;
+    if (code === $r) return $CR;
+    if (code === $t) return $TAB;
+    if (code === $b) return $BS;
+    if (code === $f) return $FF;
+    if (code === $v) return $VTAB;
+    if (code === $x) {
+      int value = hexDigitValue(source.next());
+      value = value * 16 + hexDigitValue(source.next());
+      return value;
+    }
+    if (code === $u) {
+      int value = 0;
+      code = source.next();
+      if (code === $OPEN_CURLY_BRACKET) {
+        for (code = source.next();
+             code != $CLOSE_CURLY_BRACKET;
+             code = source.next()) {
+           value = value * 16 + hexDigitValue(code);
+        }
+        return value;
+      }
+      // Four digit hex value.
+      value = hexDigitValue(code);
+      for (int i = 0; i < 3; i++) {
+        code = source.next();
+        value = value * 16 + hexDigitValue(code);
+      }
+      return value;
+    }
+    return code;
+  }
+}
+
diff --git a/lib/compiler/implementation/tree/nodes.dart b/lib/compiler/implementation/tree/nodes.dart
new file mode 100644
index 0000000..6b489f4
--- /dev/null
+++ b/lib/compiler/implementation/tree/nodes.dart
@@ -0,0 +1,1644 @@
+// Copyright (c) 2012, 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.
+
+interface Visitor<R> {
+  R visitBlock(Block node);
+  R visitBreakStatement(BreakStatement node);
+  R visitCatchBlock(CatchBlock node);
+  R visitClassNode(ClassNode node);
+  R visitConditional(Conditional node);
+  R visitContinueStatement(ContinueStatement node);
+  R visitDoWhile(DoWhile node);
+  R visitEmptyStatement(EmptyStatement node);
+  R visitExpressionStatement(ExpressionStatement node);
+  R visitFor(For node);
+  R visitForInStatement(ForInStatement node);
+  R visitFunctionDeclaration(FunctionDeclaration node);
+  R visitFunctionExpression(FunctionExpression node);
+  R visitIdentifier(Identifier node);
+  R visitIf(If node);
+  R visitLabeledStatement(LabeledStatement node);
+  R visitLiteralBool(LiteralBool node);
+  R visitLiteralDouble(LiteralDouble node);
+  R visitLiteralInt(LiteralInt node);
+  R visitLiteralList(LiteralList node);
+  R visitLiteralMap(LiteralMap node);
+  R visitLiteralMapEntry(LiteralMapEntry node);
+  R visitLiteralNull(LiteralNull node);
+  R visitLiteralString(LiteralString node);
+  R visitStringJuxtaposition(StringJuxtaposition node);
+  R visitModifiers(Modifiers node);
+  R visitNamedArgument(NamedArgument node);
+  R visitNewExpression(NewExpression node);
+  R visitNodeList(NodeList node);
+  R visitOperator(Operator node);
+  R visitParenthesizedExpression(ParenthesizedExpression node);
+  R visitReturn(Return node);
+  R visitScriptTag(ScriptTag node);
+  R visitSend(Send node);
+  R visitSendSet(SendSet node);
+  R visitStringInterpolation(StringInterpolation node);
+  R visitStringInterpolationPart(StringInterpolationPart node);
+  R visitSwitchCase(SwitchCase node);
+  R visitSwitchStatement(SwitchStatement node);
+  R visitThrow(Throw node);
+  R visitTryStatement(TryStatement node);
+  R visitTypeAnnotation(TypeAnnotation node);
+  R visitTypedef(Typedef node);
+  R visitTypeVariable(TypeVariable node);
+  R visitVariableDefinitions(VariableDefinitions node);
+  R visitWhile(While node);
+}
+
+Token firstBeginToken(Node first, Node second) {
+  if (first !== null) return first.getBeginToken();
+  if (second !== null) return second.getBeginToken();
+  return null;
+}
+
+class NodeAssertionFailure implements Exception {
+  final Node node;
+  final String message;
+  NodeAssertionFailure(this.node, this.message);
+}
+
+/**
+ * A node in a syntax tree.
+ *
+ * The abstract part of "abstract syntax tree" is invalidated when
+ * supporting tools such as code formatting. These tools need concrete
+ * syntax such as parentheses and no constant folding.
+ *
+ * We support these tools by storing additional references back to the
+ * token stream. These references are stored in fields ending with
+ * "Token".
+ */
+class Node implements Hashable {
+  final int _hashCode;
+  static int _HASH_COUNTER = 0;
+
+  Node() : _hashCode = ++_HASH_COUNTER;
+
+  hashCode() => _hashCode;
+
+  abstract accept(Visitor visitor);
+
+  abstract visitChildren(Visitor visitor);
+
+  toString() => unparse(false);
+
+  toDebugString() => unparse(true);
+
+  String getObjectDescription() => super.toString();
+
+  String unparse(bool printDebugInfo) {
+    Unparser unparser = new Unparser(printDebugInfo);
+    try {
+      return unparser.unparse(this);
+    } catch (var e, var trace) {
+      print(trace);
+      return '<<unparse error: ${getObjectDescription()}: ${unparser.sb}>>';
+    }
+  }
+
+  abstract Token getBeginToken();
+
+  abstract Token getEndToken();
+
+  Block asBlock() => null;
+  BreakStatement asBreakStatement() => null;
+  CatchBlock asCatchBlock() => null;
+  ClassNode asClassNode() => null;
+  Conditional asConditional() => null;
+  ContinueStatement asContinueStatement() => null;
+  DoWhile asDoWhile() => null;
+  EmptyStatement asEmptyStatement() => null;
+  Expression asExpression() => null;
+  ExpressionStatement asExpressionStatement() => null;
+  For asFor() => null;
+  ForInStatement asForInStatement() => null;
+  FunctionDeclaration asFunctionDeclaration() => null;
+  FunctionExpression asFunctionExpression() => null;
+  Identifier asIdentifier() => null;
+  If asIf() => null;
+  LabeledStatement asLabeledStatement() => null;
+  LiteralBool asLiteralBool() => null;
+  LiteralDouble asLiteralDouble() => null;
+  LiteralInt asLiteralInt() => null;
+  LiteralList asLiteralList() => null;
+  LiteralMap asLiteralMap() => null;
+  LiteralMapEntry asLiteralMapEntry() => null;
+  LiteralNull asLiteralNull() => null;
+  LiteralString asLiteralString() => null;
+  Modifiers asModifiers() => null;
+  NamedArgument asNamedArgument() => null;
+  NodeList asNodeList() => null;
+  Operator asOperator() => null;
+  ParenthesizedExpression asParenthesizedExpression() => null;
+  Return asReturn() => null;
+  ScriptTag asScriptTag() => null;
+  Send asSend() => null;
+  SendSet asSendSet() => null;
+  Statement asStatement() => null;
+  StringInterpolation asStringInterpolation() => null;
+  StringInterpolationPart asStringInterpolationPart() => null;
+  StringJuxtaposition asStringJuxtaposition() => null;
+  SwitchCase asSwitchCase() => null;
+  SwitchStatement asSwitchStatement() => null;
+  Throw asThrow() => null;
+  TryStatement asTryStatement() => null;
+  TypeAnnotation asTypeAnnotation() => null;
+  Typedef asTypedef() => null;
+  TypeVariable asTypeVariable() => null;
+  VariableDefinitions asVariableDefinitions() => null;
+  While asWhile() => null;
+
+  bool isValidBreakTarget() => false;
+  bool isValidContinueTarget() => false;
+}
+
+class ClassNode extends Node {
+  final Identifier name;
+  final TypeAnnotation superclass;
+  final NodeList interfaces;
+  final NodeList typeParameters;
+
+  // TODO(ahe, karlklose): the default keyword is not recorded.
+  final TypeAnnotation defaultClause;
+
+  final Token beginToken;
+  final Token extendsKeyword;
+  final Token endToken;
+
+  ClassNode(this.name, this.typeParameters, this.superclass, this.interfaces,
+            this.defaultClause, this.beginToken, this.extendsKeyword,
+            this.endToken);
+
+  ClassNode asClassNode() => this;
+
+  accept(Visitor visitor) => visitor.visitClassNode(this);
+
+  visitChildren(Visitor visitor) {
+    if (name !== null) name.accept(visitor);
+    if (superclass !== null) superclass.accept(visitor);
+    if (interfaces !== null) interfaces.accept(visitor);
+  }
+
+  bool get isInterface() => beginToken.stringValue === 'interface';
+
+  bool get isClass() => !isInterface;
+
+  Token getBeginToken() => beginToken;
+
+  Token getEndToken() => endToken;
+}
+
+class Expression extends Node {
+  Expression();
+
+  Expression asExpression() => this;
+
+  // TODO(ahe): make class abstract instead of adding an abstract method.
+  abstract accept(Visitor visitor);
+}
+
+class Statement extends Node {
+  Statement();
+
+  Statement asStatement() => this;
+
+  // TODO(ahe): make class abstract instead of adding an abstract method.
+  abstract accept(Visitor visitor);
+
+  bool isValidBreakTarget() => true;
+}
+
+/**
+ * A message send aka method invocation. In Dart, most operations can
+ * (and should) be considered as message sends. Getters and setters
+ * are just methods with a special syntax. Consequently, we model
+ * property access, assignment, operators, and method calls with this
+ * one node.
+ */
+class Send extends Expression {
+  final Node receiver;
+  final Node selector;
+  final NodeList argumentsNode;
+  Link<Node> get arguments() => argumentsNode.nodes;
+
+  Send([this.receiver, this.selector, this.argumentsNode]);
+  Send.postfix(this.receiver, this.selector, [Node argument = null])
+      : argumentsNode = (argument === null)
+        ? new Postfix()
+        : new Postfix.singleton(argument);
+  Send.prefix(this.receiver, this.selector, [Node argument = null])
+      : argumentsNode = (argument === null)
+        ? new Prefix()
+        : new Prefix.singleton(argument);
+
+  Send asSend() => this;
+
+  accept(Visitor visitor) => visitor.visitSend(this);
+
+  visitChildren(Visitor visitor) {
+    if (receiver !== null) receiver.accept(visitor);
+    if (selector !== null) selector.accept(visitor);
+    if (argumentsNode !== null) argumentsNode.accept(visitor);
+  }
+
+  int argumentCount() => argumentsNode.length();
+
+  bool get isSuperCall() {
+    return receiver !== null &&
+           receiver.asIdentifier() !== null &&
+           receiver.asIdentifier().isSuper();
+  }
+  bool get isOperator() => selector is Operator;
+  bool get isPropertyAccess() => argumentsNode === null;
+  bool get isFunctionObjectInvocation() => selector === null;
+  bool get isPrefix() => argumentsNode is Prefix;
+  bool get isPostfix() => argumentsNode is Postfix;
+  bool get isIndex() =>
+      isOperator && selector.asOperator().source.stringValue === '[]';
+
+  Token getBeginToken() {
+    if (isPrefix && !isIndex) return selector.getBeginToken();
+    return firstBeginToken(receiver, selector);
+  }
+
+  Token getEndToken() {
+    if (isPrefix) {
+      if (receiver !== null) return receiver.getEndToken();
+      if (selector !== null) return selector.getEndToken();
+      return null;
+    }
+    if (!isPostfix && argumentsNode !== null) {
+      return argumentsNode.getEndToken();
+    }
+    if (selector !== null) return selector.getEndToken();
+    return receiver.getBeginToken();
+  }
+
+  Send copyWithReceiver(Node newReceiver) {
+    assert(receiver === null);
+    return new Send(newReceiver, selector, argumentsNode);
+  }
+}
+
+class Postfix extends NodeList {
+  Postfix() : super(nodes: const EmptyLink<Node>());
+  Postfix.singleton(Node argument) : super.singleton(argument);
+}
+
+class Prefix extends NodeList {
+  Prefix() : super(nodes: const EmptyLink<Node>());
+  Prefix.singleton(Node argument) : super.singleton(argument);
+}
+
+class SendSet extends Send {
+  final Operator assignmentOperator;
+  SendSet(receiver, selector, this.assignmentOperator, argumentsNode)
+    : super(receiver, selector, argumentsNode);
+  SendSet.postfix(receiver,
+                  selector,
+                  this.assignmentOperator,
+                  [Node argument = null])
+      : super.postfix(receiver, selector, argument);
+  SendSet.prefix(receiver,
+                 selector,
+                 this.assignmentOperator,
+                 [Node argument = null])
+      : super.prefix(receiver, selector, argument);
+
+  SendSet asSendSet() => this;
+
+  accept(Visitor visitor) => visitor.visitSendSet(this);
+
+  visitChildren(Visitor visitor) {
+    super.visitChildren(visitor);
+    if (assignmentOperator !== null) assignmentOperator.accept(visitor);
+  }
+
+  Send copyWithReceiver(Node newReceiver) {
+    assert(receiver === null);
+    return new SendSet(newReceiver, selector, assignmentOperator,
+                       argumentsNode);
+  }
+}
+
+class NewExpression extends Expression {
+  /** The token NEW or CONST */
+  final Token newToken;
+
+  // Note: we expect that send.receiver is null.
+  final Send send;
+
+  NewExpression([this.newToken, this.send]);
+
+  accept(Visitor visitor) => visitor.visitNewExpression(this);
+
+  visitChildren(Visitor visitor) {
+    if (send !== null) send.accept(visitor);
+  }
+
+  bool isConst() => newToken.stringValue === 'const';
+
+  Token getBeginToken() => newToken;
+
+  Token getEndToken() => send.getEndToken();
+}
+
+class NodeList extends Node implements Iterable<Node> {
+  final Link<Node> nodes;
+  final Token beginToken;
+  final Token endToken;
+  final SourceString delimiter;
+  bool isEmpty() => nodes.isEmpty();
+
+  NodeList([this.beginToken, this.nodes, this.endToken, this.delimiter]);
+
+  Iterator<Node> iterator() => nodes.iterator();
+
+  NodeList.singleton(Node node) : this(null, new Link<Node>(node));
+  NodeList.empty() : this(null, const EmptyLink<Node>());
+
+  NodeList asNodeList() => this;
+
+  int length() {
+    int result = 0;
+    for (Link<Node> cursor = nodes; !cursor.isEmpty(); cursor = cursor.tail) {
+      result++;
+    }
+    return result;
+  }
+
+  accept(Visitor visitor) => visitor.visitNodeList(this);
+
+  visitChildren(Visitor visitor) {
+    if (nodes === null) return;
+    for (Link<Node> link = nodes; !link.isEmpty(); link = link.tail) {
+      if (link.head !== null) link.head.accept(visitor);
+    }
+  }
+
+  Token getBeginToken() {
+    if (beginToken !== null) return beginToken;
+     if (nodes !== null) {
+       for (Link<Node> link = nodes; !link.isEmpty(); link = link.tail) {
+         if (link.head.getBeginToken() !== null) {
+           return link.head.getBeginToken();
+         }
+         if (link.head.getEndToken() !== null) {
+           return link.head.getEndToken();
+         }
+       }
+     }
+    return endToken;
+  }
+
+  Token getEndToken() {
+    if (endToken !== null) return endToken;
+    if (nodes !== null) {
+      Link<Node> link = nodes;
+      if (link.isEmpty()) return beginToken;
+      while (!link.tail.isEmpty()) link = link.tail;
+      if (link.head.getEndToken() !== null) return link.head.getEndToken();
+      if (link.head.getBeginToken() !== null) return link.head.getBeginToken();
+    }
+    return beginToken;
+  }
+}
+
+class Block extends Statement {
+  final NodeList statements;
+
+  Block(this.statements);
+
+  Block asBlock() => this;
+
+  accept(Visitor visitor) => visitor.visitBlock(this);
+
+  visitChildren(Visitor visitor) {
+    if (statements !== null) statements.accept(visitor);
+  }
+
+  Token getBeginToken() => statements.getBeginToken();
+
+  Token getEndToken() => statements.getEndToken();
+}
+
+class If extends Statement {
+  final ParenthesizedExpression condition;
+  final Statement thenPart;
+  final Statement elsePart;
+
+  final Token ifToken;
+  final Token elseToken;
+
+  If(this.condition, this.thenPart, this.elsePart,
+     this.ifToken, this.elseToken);
+
+  If asIf() => this;
+
+  bool get hasElsePart() => elsePart !== null;
+
+  void validate() {
+    // TODO(ahe): Check that condition has size one.
+  }
+
+  accept(Visitor visitor) => visitor.visitIf(this);
+
+  visitChildren(Visitor visitor) {
+    if (condition !== null) condition.accept(visitor);
+    if (thenPart !== null) thenPart.accept(visitor);
+    if (elsePart !== null) elsePart.accept(visitor);
+  }
+
+  Token getBeginToken() => ifToken;
+
+  Token getEndToken() {
+    if (elsePart === null) return thenPart.getEndToken();
+    return elsePart.getEndToken();
+  }
+}
+
+class Conditional extends Expression {
+  final Expression condition;
+  final Expression thenExpression;
+  final Expression elseExpression;
+
+  final Token questionToken;
+  final Token colonToken;
+
+  Conditional(this.condition, this.thenExpression,
+              this.elseExpression, this.questionToken, this.colonToken);
+
+  Conditional asConditional() => this;
+
+  accept(Visitor visitor) => visitor.visitConditional(this);
+
+  visitChildren(Visitor visitor) {
+    condition.accept(visitor);
+    thenExpression.accept(visitor);
+    elseExpression.accept(visitor);
+  }
+
+  Token getBeginToken() => condition.getBeginToken();
+
+  Token getEndToken() => elseExpression.getEndToken();
+}
+
+class For extends Loop {
+  /** Either a variable declaration or an expression. */
+  final Node initializer;
+  /** Either an expression statement or an empty statement. */
+  final Statement conditionStatement;
+  final NodeList update;
+
+  final Token forToken;
+
+  For(this.initializer, this.conditionStatement, this.update, body,
+      this.forToken) : super(body);
+
+  For asFor() => this;
+
+  Expression get condition() {
+    if (conditionStatement is ExpressionStatement) {
+      return conditionStatement.asExpressionStatement().expression;
+    } else {
+      return null;
+    }
+  }
+
+  accept(Visitor visitor) => visitor.visitFor(this);
+
+  visitChildren(Visitor visitor) {
+    if (initializer !== null) initializer.accept(visitor);
+    if (conditionStatement !== null) conditionStatement.accept(visitor);
+    if (update !== null) update.accept(visitor);
+    if (body !== null) body.accept(visitor);
+  }
+
+  Token getBeginToken() => forToken;
+
+  Token getEndToken() {
+    return body.getEndToken();
+  }
+}
+
+class FunctionDeclaration extends Statement {
+  final FunctionExpression function;
+
+  FunctionDeclaration(this.function);
+
+  FunctionDeclaration asFunctionDeclaration() => this;
+
+  accept(Visitor visitor) => visitor.visitFunctionDeclaration(this);
+
+  visitChildren(Visitor visitor) => function.accept(visitor);
+
+  Token getBeginToken() => function.getBeginToken();
+  Token getEndToken() => function.getEndToken();
+}
+
+class FunctionExpression extends Expression {
+  final Node name;
+
+  /**
+   * List of VariableDefinitions or NodeList.
+   *
+   * A NodeList can only occur at the end and holds named parameters.
+   */
+  final NodeList parameters;
+
+  final Statement body;
+  final TypeAnnotation returnType;
+  final Modifiers modifiers;
+  final NodeList initializers;
+
+  final Token getOrSet;
+
+  FunctionExpression(this.name, this.parameters, this.body, this.returnType,
+                     this.modifiers, this.initializers, this.getOrSet);
+
+  FunctionExpression asFunctionExpression() => this;
+
+  accept(Visitor visitor) => visitor.visitFunctionExpression(this);
+
+  visitChildren(Visitor visitor) {
+    if (modifiers !== null) modifiers.accept(visitor);
+    if (returnType !== null) returnType.accept(visitor);
+    if (name !== null) name.accept(visitor);
+    if (parameters !== null) parameters.accept(visitor);
+    if (initializers !== null) initializers.accept(visitor);
+    if (body !== null) body.accept(visitor);
+  }
+
+  bool hasBody() {
+    // TODO(karlklose,ahe): refactor AST nodes (issue 1713).
+    if (body.asReturn() !== null) return true;
+    NodeList statements = body.asBlock().statements;
+    return (!statements.nodes.isEmpty() ||
+            statements.getBeginToken().kind !== $SEMICOLON);
+  }
+
+  Token getBeginToken() {
+    Token token = firstBeginToken(modifiers, returnType);
+    if (token !== null) return token;
+    if (getOrSet !== null) return getOrSet;
+    return firstBeginToken(name, parameters);
+  }
+
+  Token getEndToken() {
+    Token token = (body === null) ? null : body.getEndToken();
+    token = (token === null) ? parameters.getEndToken() : token;
+    return (token === null) ? name.getEndToken() : token;
+  }
+}
+
+typedef void DecodeErrorHandler(Token token, var error);
+
+class Literal<T> extends Expression {
+  final Token token;
+  final DecodeErrorHandler handler;
+
+  Literal(Token this.token, DecodeErrorHandler this.handler);
+
+  abstract T get value();
+
+  visitChildren(Visitor visitor) {}
+
+  Token getBeginToken() => token;
+
+  Token getEndToken() => token;
+}
+
+class LiteralInt extends Literal<int> {
+  LiteralInt(Token token, DecodeErrorHandler handler) : super(token, handler);
+
+  LiteralInt asLiteralInt() => this;
+
+  int get value() {
+    try {
+      Token token = this.token;
+      if (token.kind === PLUS_TOKEN) token = token.next;
+      return Math.parseInt(token.value.slowToString());
+    } catch (BadNumberFormatException ex) {
+      (this.handler)(token, ex);
+    }
+  }
+
+  accept(Visitor visitor) => visitor.visitLiteralInt(this);
+}
+
+class LiteralDouble extends Literal<double> {
+  LiteralDouble(Token token, DecodeErrorHandler handler)
+    : super(token, handler);
+
+  LiteralDouble asLiteralDouble() => this;
+
+  double get value() {
+    try {
+      Token token = this.token;
+      if (token.kind === PLUS_TOKEN) token = token.next;
+      return Math.parseDouble(token.value.slowToString());
+    } catch (BadNumberFormatException ex) {
+      (this.handler)(token, ex);
+    }
+  }
+
+  accept(Visitor visitor) => visitor.visitLiteralDouble(this);
+}
+
+class LiteralBool extends Literal<bool> {
+  LiteralBool(Token token, DecodeErrorHandler handler) : super(token, handler);
+
+  LiteralBool asLiteralBool() => this;
+
+  bool get value() {
+    switch (token.value) {
+      case Keyword.TRUE: return true;
+      case Keyword.FALSE: return false;
+      default:
+        (this.handler)(token, "not a bool ${token.value}");
+    }
+  }
+
+  accept(Visitor visitor) => visitor.visitLiteralBool(this);
+}
+
+
+class StringQuoting {
+  static final StringQuoting SINGLELINE_DQ =
+      const StringQuoting($DQ, raw: false, leftQuoteLength: 1);
+  static final StringQuoting RAW_SINGLELINE_DQ =
+      const StringQuoting($DQ, raw: true, leftQuoteLength: 1);
+  static final StringQuoting MULTILINE_DQ =
+      const StringQuoting($DQ, raw: false, leftQuoteLength: 3);
+  static final StringQuoting RAW_MULTILINE_DQ =
+      const StringQuoting($DQ, raw: true, leftQuoteLength: 3);
+  static final StringQuoting MULTILINE_NL_DQ =
+      const StringQuoting($DQ, raw: false, leftQuoteLength: 4);
+  static final StringQuoting RAW_MULTILINE_NL_DQ =
+      const StringQuoting($DQ, raw: true, leftQuoteLength: 4);
+  static final StringQuoting MULTILINE_NL2_DQ =
+      const StringQuoting($DQ, raw: false, leftQuoteLength: 5);
+  static final StringQuoting RAW_MULTILINE_NL2_DQ =
+      const StringQuoting($DQ, raw: true, leftQuoteLength: 5);
+  static final StringQuoting SINGLELINE_SQ =
+      const StringQuoting($SQ, raw: false, leftQuoteLength: 1);
+  static final StringQuoting RAW_SINGLELINE_SQ =
+      const StringQuoting($SQ, raw: true, leftQuoteLength: 1);
+  static final StringQuoting MULTILINE_SQ =
+      const StringQuoting($SQ, raw: false, leftQuoteLength: 3);
+  static final StringQuoting RAW_MULTILINE_SQ =
+      const StringQuoting($SQ, raw: true, leftQuoteLength: 3);
+  static final StringQuoting MULTILINE_NL_SQ =
+      const StringQuoting($SQ, raw: false, leftQuoteLength: 4);
+  static final StringQuoting RAW_MULTILINE_NL_SQ =
+      const StringQuoting($SQ, raw: true, leftQuoteLength: 4);
+  static final StringQuoting MULTILINE_NL2_SQ =
+      const StringQuoting($SQ, raw: false, leftQuoteLength: 5);
+  static final StringQuoting RAW_MULTILINE_NL2_SQ =
+      const StringQuoting($SQ, raw: true, leftQuoteLength: 5);
+
+
+  static final List<StringQuoting> mapping = const <StringQuoting>[
+    SINGLELINE_DQ,
+    RAW_SINGLELINE_DQ,
+    MULTILINE_DQ,
+    RAW_MULTILINE_DQ,
+    MULTILINE_NL_DQ,
+    RAW_MULTILINE_NL_DQ,
+    MULTILINE_NL2_DQ,
+    RAW_MULTILINE_NL2_DQ,
+    SINGLELINE_SQ,
+    RAW_SINGLELINE_SQ,
+    MULTILINE_SQ,
+    RAW_MULTILINE_SQ,
+    MULTILINE_NL_SQ,
+    RAW_MULTILINE_NL_SQ,
+    MULTILINE_NL2_SQ,
+    RAW_MULTILINE_NL2_SQ
+  ];
+  final bool raw;
+  final int leftQuoteCharCount;
+  final int quote;
+  const StringQuoting(this.quote, [bool raw, int leftQuoteLength])
+      : this.raw = raw, this.leftQuoteCharCount = leftQuoteLength;
+  String get quoteChar() => quote === $DQ ? '"' : "'";
+
+  int get leftQuoteLength() => (raw ? 1 : 0) + leftQuoteCharCount;
+  int get rightQuoteLength() => (leftQuoteCharCount > 2) ? 3 : 1;
+  static StringQuoting getQuoting(int quote, bool raw, int quoteLength) {
+    int index = quoteLength - 1;
+    if (quoteLength > 2) index -= 1;
+    return mapping[(raw ? 1 : 0) + index * 2 + (quote === $SQ ? 8 : 0)];
+  }
+}
+
+/**
+  * Superclass for classes representing string literals.
+  */
+class StringNode extends Expression {
+  abstract DartString get dartString();
+  abstract bool get isInterpolation();
+}
+
+class LiteralString extends StringNode {
+  final Token token;
+  /** Non-null on validated string literals. */
+  final DartString dartString;
+
+  LiteralString(this.token, this.dartString);
+
+  LiteralString asLiteralString() => this;
+
+  void visitChildren(Visitor visitor) {}
+
+  bool get isInterpolation() => false;
+  bool isValidated() => dartString !== null;
+
+  Token getBeginToken() => token;
+  Token getEndToken() => token;
+
+  accept(Visitor visitor) => visitor.visitLiteralString(this);
+}
+
+class LiteralNull extends Literal<SourceString> {
+  LiteralNull(Token token) : super(token, null);
+
+  LiteralNull asLiteralNull() => this;
+
+  SourceString get value() => null;
+
+  accept(Visitor visitor) => visitor.visitLiteralNull(this);
+}
+
+class LiteralList extends Expression {
+  final TypeAnnotation type;
+  final NodeList elements;
+
+  final Token constKeyword;
+
+  LiteralList(this.type, this.elements, this.constKeyword);
+
+  bool isConst() => constKeyword !== null;
+
+  LiteralList asLiteralList() => this;
+  accept(Visitor visitor) => visitor.visitLiteralList(this);
+
+  visitChildren(Visitor visitor) {
+    if (type !== null) type.accept(visitor);
+    elements.accept(visitor);
+  }
+
+  Token getBeginToken() {
+    if (constKeyword !== null) return constKeyword;
+    return firstBeginToken(type, elements);
+  }
+
+  Token getEndToken() => elements.getEndToken();
+}
+
+class Identifier extends Expression {
+  final Token token;
+
+  SourceString get source() => token.value;
+
+  Identifier(Token this.token);
+
+  bool isThis() => source.stringValue === 'this';
+
+  bool isSuper() => source.stringValue === 'super';
+
+  Identifier asIdentifier() => this;
+
+  accept(Visitor visitor) => visitor.visitIdentifier(this);
+
+  visitChildren(Visitor visitor) {}
+
+  Token getBeginToken() => token;
+
+  Token getEndToken() => token;
+}
+
+class Operator extends Identifier {
+  Operator(Token token) : super(token);
+
+  Operator asOperator() => this;
+
+  accept(Visitor visitor) => visitor.visitOperator(this);
+}
+
+class Return extends Statement {
+  final Expression expression;
+  final Token beginToken;
+  final Token endToken;
+
+  Return(this.beginToken, this.endToken, this.expression);
+
+  Return asReturn() => this;
+
+  bool get hasExpression() => expression !== null;
+
+  accept(Visitor visitor) => visitor.visitReturn(this);
+
+  visitChildren(Visitor visitor) {
+    if (expression !== null) expression.accept(visitor);
+  }
+
+  Token getBeginToken() => beginToken;
+
+  Token getEndToken() {
+    if (endToken === null) return expression.getEndToken();
+    return endToken;
+  }
+}
+
+class ExpressionStatement extends Statement {
+  final Expression expression;
+  final Token endToken;
+
+  ExpressionStatement(this.expression, this.endToken);
+
+  ExpressionStatement asExpressionStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitExpressionStatement(this);
+
+  visitChildren(Visitor visitor) {
+    if (expression !== null) expression.accept(visitor);
+  }
+
+  Token getBeginToken() => expression.getBeginToken();
+
+  Token getEndToken() => endToken;
+}
+
+class Throw extends Statement {
+  final Expression expression;
+
+  final Token throwToken;
+  final Token endToken;
+
+  Throw(this.expression, this.throwToken, this.endToken);
+
+  Throw asThrow() => this;
+
+  accept(Visitor visitor) => visitor.visitThrow(this);
+
+  visitChildren(Visitor visitor) {
+    if (expression !== null) expression.accept(visitor);
+  }
+
+  Token getBeginToken() => throwToken;
+
+  Token getEndToken() => endToken;
+}
+
+class TypeAnnotation extends Node {
+  final Expression typeName;
+  final NodeList typeArguments;
+
+  TypeAnnotation(Expression this.typeName, NodeList this.typeArguments);
+
+  TypeAnnotation asTypeAnnotation() => this;
+
+  accept(Visitor visitor) => visitor.visitTypeAnnotation(this);
+
+  visitChildren(Visitor visitor) {
+    typeName.accept(visitor);
+    if (typeArguments !== null) typeArguments.accept(visitor);
+  }
+
+  Token getBeginToken() => typeName.getBeginToken();
+
+  Token getEndToken() => typeName.getEndToken();
+}
+
+class TypeVariable extends Node {
+  final Identifier name;
+  final TypeAnnotation bound;
+  TypeVariable(Identifier this.name, TypeAnnotation this.bound);
+
+  accept(Visitor visitor) => visitor.visitTypeVariable(this);
+
+  visitChildren(Visitor visitor) {
+    name.accept(visitor);
+    if (bound !== null) {
+      bound.accept(visitor);
+    }
+  }
+
+  TypeVariable asTypeVariable() => this;
+
+  Token getBeginToken() => name.getBeginToken();
+
+  Token getEndToken() {
+    return (bound !== null) ? bound.getEndToken() : name.getEndToken();
+  }
+}
+
+class VariableDefinitions extends Statement {
+  final Token endToken;
+  final TypeAnnotation type;
+  final Modifiers modifiers;
+  final NodeList definitions;
+  VariableDefinitions(this.type, this.modifiers, this.definitions,
+                      this.endToken);
+
+  VariableDefinitions asVariableDefinitions() => this;
+
+  accept(Visitor visitor) => visitor.visitVariableDefinitions(this);
+
+  visitChildren(Visitor visitor) {
+    if (type !== null) type.accept(visitor);
+    if (definitions !== null) definitions.accept(visitor);
+  }
+
+  Token getBeginToken() {
+    return firstBeginToken(type, definitions);
+  }
+
+  Token getEndToken() => endToken;
+}
+
+class Loop extends Statement {
+  abstract Expression get condition();
+  final Statement body;
+
+  Loop(this.body);
+
+  bool isValidContinueTarget() => true;
+}
+
+class DoWhile extends Loop {
+  final Token doKeyword;
+  final Token whileKeyword;
+  final Token endToken;
+
+  final Expression condition;
+
+  DoWhile(Statement body, Expression this.condition,
+          Token this.doKeyword, Token this.whileKeyword, Token this.endToken)
+    : super(body);
+
+  DoWhile asDoWhile() => this;
+
+  accept(Visitor visitor) => visitor.visitDoWhile(this);
+
+  visitChildren(Visitor visitor) {
+    if (condition !== null) condition.accept(visitor);
+    if (body !== null) body.accept(visitor);
+  }
+
+  Token getBeginToken() => doKeyword;
+
+  Token getEndToken() => endToken;
+}
+
+class While extends Loop {
+  final Token whileKeyword;
+  final Expression condition;
+
+  While(Expression this.condition, Statement body,
+        Token this.whileKeyword) : super(body);
+
+  While asWhile() => this;
+
+  accept(Visitor visitor) => visitor.visitWhile(this);
+
+  visitChildren(Visitor visitor) {
+    if (condition !== null) condition.accept(visitor);
+    if (body !== null) body.accept(visitor);
+  }
+
+  Token getBeginToken() => whileKeyword;
+
+  Token getEndToken() => body.getEndToken();
+}
+
+class ParenthesizedExpression extends Expression {
+  final Expression expression;
+  final BeginGroupToken beginToken;
+
+  ParenthesizedExpression(Expression this.expression,
+                          BeginGroupToken this.beginToken);
+
+  ParenthesizedExpression asParenthesizedExpression() => this;
+
+  accept(Visitor visitor) => visitor.visitParenthesizedExpression(this);
+
+  visitChildren(Visitor visitor) {
+    if (expression !== null) expression.accept(visitor);
+  }
+
+  Token getBeginToken() => beginToken;
+
+  Token getEndToken() => beginToken.endGroup;
+}
+
+/** Representation of modifiers such as static, abstract, final, etc. */
+class Modifiers extends Node {
+  /* TODO(ahe): The following should be validated relating to modifiers:
+   * 1. The nodes must come in a certain order.
+   * 2. The keywords "var" and "final" may not be used at the same time.
+   * 3. The type of an element must be null if isVar() is true.
+   */
+
+  final NodeList nodes;
+  /** Bit pattern to easy check what modifiers are present. */
+  final int flags;
+
+  static final int FLAG_STATIC = 1;
+  static final int FLAG_ABSTRACT = FLAG_STATIC << 1;
+  static final int FLAG_FINAL = FLAG_ABSTRACT << 1;
+  static final int FLAG_VAR = FLAG_FINAL << 1;
+  static final int FLAG_CONST = FLAG_VAR << 1;
+  static final int FLAG_FACTORY = FLAG_CONST << 1;
+
+  Modifiers(NodeList nodes)
+    : this.nodes = nodes, flags = computeFlags(nodes.nodes);
+
+  Modifiers.empty() : this(new NodeList.empty());
+
+  static int computeFlags(Link<Node> nodes) {
+    int flags = 0;
+    for (; !nodes.isEmpty(); nodes = nodes.tail) {
+      String value = nodes.head.asIdentifier().source.stringValue;
+      if (value === 'static') flags += FLAG_STATIC;
+      else if (value === 'abstract') flags += FLAG_ABSTRACT;
+      else if (value === 'final') flags += FLAG_FINAL;
+      else if (value === 'var') flags += FLAG_VAR;
+      else if (value === 'const') flags += FLAG_CONST;
+      else if (value === 'factory') flags += FLAG_FACTORY;
+      else throw 'internal error: ${nodes.head}';
+    }
+    return flags;
+  }
+
+  Modifiers asModifiers() => this;
+  Token getBeginToken() => nodes.getBeginToken();
+  Token getEndToken() => nodes.getEndToken();
+  accept(Visitor visitor) => visitor.visitModifiers(this);
+  visitChildren(Visitor visitor) => nodes.accept(visitor);
+
+  bool isStatic() => (flags & FLAG_STATIC) != 0;
+  bool isAbstract() => (flags & FLAG_ABSTRACT) != 0;
+  bool isFinal() => (flags & FLAG_FINAL) != 0;
+  bool isVar() => (flags & FLAG_VAR) != 0;
+  bool isConst() => (flags & FLAG_CONST) != 0;
+  bool isFactory() => (flags & FLAG_FACTORY) != 0;
+}
+
+class StringInterpolation extends StringNode {
+  final LiteralString string;
+  final NodeList parts;
+
+  StringInterpolation(this.string, this.parts);
+
+  StringInterpolation asStringInterpolation() => this;
+
+  DartString get dartString() => null;
+  bool get isInterpolation() => true;
+
+  accept(Visitor visitor) => visitor.visitStringInterpolation(this);
+
+  visitChildren(Visitor visitor) {
+    string.accept(visitor);
+    parts.accept(visitor);
+  }
+
+  Token getBeginToken() => string.getBeginToken();
+  Token getEndToken() => parts.getEndToken();
+}
+
+class StringInterpolationPart extends Node {
+  final Expression expression;
+  final LiteralString string;
+
+  StringInterpolationPart(this.expression, this.string);
+
+  StringInterpolationPart asStringInterpolationPart() => this;
+
+  accept(Visitor visitor) => visitor.visitStringInterpolationPart(this);
+
+  visitChildren(Visitor visitor) {
+    expression.accept(visitor);
+    string.accept(visitor);
+  }
+
+  Token getBeginToken() => expression.getBeginToken();
+
+  Token getEndToken() => string.getEndToken();
+}
+
+/**
+ * A class representing juxtaposed string literals.
+ * The string literals can be both plain literals and string interpolations.
+ */
+class StringJuxtaposition extends StringNode {
+  final Expression first;
+  final Expression second;
+
+  /**
+   * Caches the check for whether this juxtaposition contains a string
+   * interpolation
+   */
+  bool isInterpolationCache = null;
+
+  /**
+   * Caches a Dart string representation of the entire juxtaposition's
+   * content. Only juxtapositions that don't (transitively) contains
+   * interpolations have a static representation.
+   */
+  DartString dartStringCache = null;
+
+  StringJuxtaposition(this.first, this.second);
+
+  StringJuxtaposition asStringJuxtaposition() => this;
+
+  bool get isInterpolation() {
+    if (isInterpolationCache === null) {
+      isInterpolationCache = (first.accept(const IsInterpolationVisitor()) ||
+                          second.accept(const IsInterpolationVisitor()));
+    }
+    return isInterpolationCache;
+  }
+
+  /**
+   * Retrieve a single DartString that represents this entire juxtaposition
+   * of string literals.
+   * Should only be called if [isInterpolation] returns false.
+   */
+  DartString get dartString() {
+    if (isInterpolation) {
+      throw new NodeAssertionFailure(this,
+                                     "Getting dartString on interpolation;");
+    }
+    if (dartStringCache === null) {
+      DartString firstString = first.accept(const GetDartStringVisitor());
+      DartString secondString = second.accept(const GetDartStringVisitor());
+      if (firstString === null || secondString === null) {
+        return null;
+      }
+      dartStringCache = new DartString.concat(firstString, secondString);
+    }
+    return dartStringCache;
+  }
+
+  accept(Visitor visitor) => visitor.visitStringJuxtaposition(this);
+
+  void visitChildren(Visitor visitor) {
+    first.accept(visitor);
+    second.accept(visitor);
+  }
+
+  Token getBeginToken() => first.getBeginToken();
+
+  Token getEndToken() => second.getEndToken();
+}
+
+class EmptyStatement extends Statement {
+  final Token semicolonToken;
+
+  EmptyStatement(this.semicolonToken);
+
+  EmptyStatement asEmptyStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitEmptyStatement(this);
+
+  visitChildren(Visitor visitor) {}
+
+  Token getBeginToken() => semicolonToken;
+
+  Token getEndToken() => semicolonToken;
+}
+
+class LiteralMap extends Expression {
+  final NodeList typeArguments;
+  final NodeList entries;
+
+  LiteralMap(this.typeArguments, this.entries);
+
+  bool isConst() => false; // TODO(ahe): Store constness.
+
+  LiteralMap asLiteralMap() => this;
+
+  accept(Visitor visitor) => visitor.visitLiteralMap(this);
+
+  visitChildren(Visitor visitor) {
+    if (typeArguments != null) typeArguments.accept(visitor);
+    entries.accept(visitor);
+  }
+
+  Token getBeginToken() => firstBeginToken(typeArguments, entries);
+
+  Token getEndToken() => entries.getEndToken();
+}
+
+class LiteralMapEntry extends Node {
+  final Expression key;
+  final Expression value;
+
+  final Token colonToken;
+
+  LiteralMapEntry(this.key, this.colonToken, this.value);
+
+  LiteralMapEntry asLiteralMapEntry() => this;
+
+  accept(Visitor visitor) => visitor.visitLiteralMapEntry(this);
+
+  visitChildren(Visitor visitor) {
+    key.accept(visitor);
+    value.accept(visitor);
+  }
+
+  Token getBeginToken() => key.getBeginToken();
+
+  Token getEndToken() => value.getEndToken();
+}
+
+class NamedArgument extends Expression {
+  final Identifier name;
+  final Expression expression;
+
+  final Token colonToken;
+
+  NamedArgument(this.name, this.colonToken, this.expression);
+
+  NamedArgument asNamedArgument() => this;
+
+  accept(Visitor visitor) => visitor.visitNamedArgument(this);
+
+  visitChildren(Visitor visitor) {
+    name.accept(visitor);
+    expression.accept(visitor);
+  }
+
+  Token getBeginToken() => name.getBeginToken();
+
+  Token getEndToken() => expression.getEndToken();
+}
+
+class SwitchStatement extends Statement {
+  final ParenthesizedExpression parenthesizedExpression;
+  final NodeList cases;
+
+  final Token switchKeyword;
+
+  SwitchStatement(this.parenthesizedExpression, this.cases,
+                  this.switchKeyword);
+
+  SwitchStatement asSwitchStatement() => this;
+
+  Expression get expression() => parenthesizedExpression.expression;
+
+  accept(Visitor visitor) => visitor.visitSwitchStatement(this);
+
+  visitChildren(Visitor visitor) {
+    parenthesizedExpression.accept(visitor);
+    cases.accept(visitor);
+  }
+
+  Token getBeginToken() => switchKeyword;
+
+  Token getEndToken() => cases.getEndToken();
+}
+
+class SwitchCase extends Node {
+  // Represents the grammar:
+  //   label? ('case' expression ':')* ('default' ':')? statement*
+  // Each expression is collected in [expressions].
+  // The 'case' keywords can be obtained using [caseKeywords()].
+  // Any actual switch case must have at least one 'case' or 'default'
+  // clause.
+  final Identifier label;
+  final NodeList expressions;
+  final Token defaultKeyword;
+  final NodeList statements;
+
+  final Token startToken;
+
+  SwitchCase(this.label, this.expressions, this.defaultKeyword,
+             this.statements, this.startToken);
+
+  SwitchCase asSwitchCase() => this;
+
+  bool get isDefaultCase() => defaultKeyword !== null;
+
+  accept(Visitor visitor) => visitor.visitSwitchCase(this);
+
+  visitChildren(Visitor visitor) {
+    if (label !== null) label.accept(visitor);
+    expressions.accept(visitor);
+    statements.accept(visitor);
+  }
+
+  Token getBeginToken() {
+    return startToken;
+  }
+
+  Token getEndToken() {
+    if (statements.nodes.isEmpty()) {
+      // All cases must have at least one expression or be the default.
+      if (defaultKeyword !== null) {
+        // The colon after 'default'.
+        return defaultKeyword.next;
+      }
+      // The colon after the expression.
+      return expressions.getEndToken().next;
+    } else {
+      return statements.getEndToken();
+    }
+  }
+
+  Link<Token> caseKeywords() {
+    Token token = startToken;
+    if (label !== null) {
+      // Skip past the label: <Identifier> ':'.
+      token = token.next.next;
+    }
+    Link<Token> recursiveGetCases(Token token, Link<Expression> expressions) {
+      if (token.stringValue === 'case') {
+        Token colon = expressions.head.getEndToken().next;
+        return new Link<Token>(token,
+                               recursiveGetCases(colon.next, expressions.tail));
+      }
+      return const EmptyLink<Token>();
+    }
+    return recursiveGetCases(token, expressions);
+  }
+}
+
+class GotoStatement extends Statement {
+  final Identifier target;
+  final Token keywordToken;
+  final Token semicolonToken;
+
+  GotoStatement(this.target, this.keywordToken, this.semicolonToken);
+
+  visitChildren(Visitor visitor) {
+    if (target !== null) target.accept(visitor);
+  }
+
+  Token getBeginToken() => keywordToken;
+
+  Token getEndToken() => semicolonToken;
+
+  // TODO(ahe): make class abstract instead of adding an abstract method.
+  abstract accept(Visitor visitor);
+}
+
+class BreakStatement extends GotoStatement {
+  BreakStatement(Identifier target, Token keywordToken, Token semicolonToken)
+    : super(target, keywordToken, semicolonToken);
+
+  BreakStatement asBreakStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitBreakStatement(this);
+}
+
+class ContinueStatement extends GotoStatement {
+  ContinueStatement(Identifier target, Token keywordToken, Token semicolonToken)
+    : super(target, keywordToken, semicolonToken);
+
+  ContinueStatement asContinueStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitContinueStatement(this);
+}
+
+class ForInStatement extends Loop {
+  final Node declaredIdentifier;
+  final Expression expression;
+
+  final Token forToken;
+  final Token inToken;
+
+  ForInStatement(this.declaredIdentifier, this.expression,
+                 Statement body, this.forToken, this.inToken) : super(body);
+
+  Expression get condition() => null;
+
+  ForInStatement asForInStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitForInStatement(this);
+
+  visitChildren(Visitor visitor) {
+    declaredIdentifier.accept(visitor);
+    expression.accept(visitor);
+    body.accept(visitor);
+  }
+
+  Token getBeginToken() => forToken;
+
+  Token getEndToken() => body.getEndToken();
+}
+
+class LabeledStatement extends Statement {
+  final Identifier label;
+  final Token colonToken;
+  final Statement statement;
+
+  LabeledStatement(this.label, this.colonToken, this.statement);
+
+  LabeledStatement asLabeledStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitLabeledStatement(this);
+
+  visitChildren(Visitor visitor) {
+    label.accept(visitor);
+    statement.accept(visitor);
+  }
+
+  Token getBeginToken() => label.getBeginToken();
+
+  Token getEndToken() => statement.getEndToken();
+
+  bool isValidContinueTarget() => statement.isValidContinueTarget();
+
+  Node getBody() {
+    if (statement is! LabeledStatement) return statement;
+    LabeledStatement labeled = statement;
+    return labeled.getBody();
+  }
+}
+
+class ScriptTag extends Node {
+  final Identifier tag;
+  final StringNode argument;
+  final Identifier prefixIdentifier;
+  final StringNode prefix;
+
+  final Token beginToken;
+  final Token endToken;
+
+  ScriptTag(this.tag, this.argument, this.prefixIdentifier, this.prefix,
+            this.beginToken, this.endToken);
+
+  bool isImport() => tag.source == const SourceString("import");
+  bool isSource() => tag.source == const SourceString("source");
+  bool isLibrary() => tag.source == const SourceString("library");
+  bool isResource() => tag.source == const SourceString("resource");
+
+  ScriptTag asScriptTag() => this;
+
+  accept(Visitor visitor) => visitor.visitScriptTag(this);
+
+  visitChildren(Visitor visitor) {
+    tag.accept(visitor);
+    argument.accept(visitor);
+    if (prefixIdentifier !== null) prefixIdentifier.accept(visitor);
+    if (prefix !== null) prefix.accept(visitor);
+  }
+
+  Token getBeginToken() => beginToken;
+
+  Token getEndToken() => endToken;
+}
+
+class Typedef extends Node {
+  final TypeAnnotation returnType;
+  final Identifier name;
+  final NodeList typeParameters;
+  final NodeList formals;
+
+  final Token typedefKeyword;
+  final Token endToken;
+
+  Typedef(this.returnType, this.name, this.typeParameters, this.formals,
+          this.typedefKeyword, this.endToken);
+
+  Typedef asTypedef() => this;
+
+  accept(Visitor visitor) => visitor.visitTypedef(this);
+
+  visitChildren(Visitor visitor) {
+    if (returnType !== null) returnType.accept(visitor);
+    name.accept(visitor);
+    if (typeParameters !== null) typeParameters.accept(visitor);
+    formals.accept(visitor);
+  }
+
+  Token getBeginToken() => typedefKeyword;
+
+  Token getEndToken() => endToken;
+}
+
+class TryStatement extends Statement {
+  final Block tryBlock;
+  final NodeList catchBlocks;
+  final Block finallyBlock;
+
+  final Token tryKeyword;
+  final Token finallyKeyword;
+
+  TryStatement(this.tryBlock, this.catchBlocks, this.finallyBlock,
+               this.tryKeyword, this.finallyKeyword);
+
+  TryStatement asTryStatement() => this;
+
+  accept(Visitor visitor) => visitor.visitTryStatement(this);
+
+  visitChildren(Visitor visitor) {
+    tryBlock.accept(visitor);
+    catchBlocks.accept(visitor);
+    if (finallyBlock !== null) finallyBlock.accept(visitor);
+  }
+
+  Token getBeginToken() => tryKeyword;
+
+  Token getEndToken() {
+    if (finallyBlock !== null) return finallyBlock.getEndToken();
+    if (!catchBlocks.isEmpty()) return catchBlocks.getEndToken();
+    return tryBlock.getEndToken();
+  }
+}
+
+class CatchBlock extends Node {
+  final NodeList formals;
+  final Block block;
+
+  final catchKeyword;
+
+  CatchBlock(this.formals, this.block, this.catchKeyword);
+
+  CatchBlock asCatchBlock() => this;
+
+  accept(Visitor visitor) => visitor.visitCatchBlock(this);
+
+  Node get exception() {
+    if (formals.nodes.isEmpty()) return null;
+    VariableDefinitions declarations = formals.nodes.head;
+    return declarations.definitions.nodes.head;
+  }
+
+  Node get trace() {
+    if (formals.nodes.isEmpty()) return null;
+    Link<Node> declarations = formals.nodes.tail;
+    if (declarations.isEmpty()) return null;
+    VariableDefinitions head = declarations.head;
+    return head.definitions.nodes.head;
+  }
+
+  visitChildren(Visitor visitor) {
+    formals.accept(visitor);
+    block.accept(visitor);
+  }
+
+  Token getBeginToken() => catchKeyword;
+
+  Token getEndToken() => block.getEndToken();
+}
+
+class Initializers {
+  static bool isSuperConstructorCall(Send node) {
+    return (node.receiver === null &&
+            node.selector.asIdentifier() !== null &&
+            node.selector.asIdentifier().isSuper()) ||
+           (node.receiver !== null &&
+            node.receiver.asIdentifier() !== null &&
+            node.receiver.asIdentifier().isSuper() &&
+            node.selector.asIdentifier() !== null);
+  }
+
+  static bool isConstructorRedirect(Send node) {
+    return (node.receiver === null &&
+            node.selector.asIdentifier() !== null &&
+            node.selector.asIdentifier().isThis()) ||
+           (node.receiver !== null &&
+            node.receiver.asIdentifier() !== null &&
+            node.receiver.asIdentifier().isThis() &&
+            node.selector.asIdentifier() !== null);
+  }
+}
+
+class GetDartStringVisitor extends AbstractVisitor<DartString> {
+  const GetDartStringVisitor();
+  DartString visitNode(Node node) => null;
+  DartString visitStringJuxtaposition(StringJuxtaposition node)
+      => node.dartString;
+  DartString visitLiteralString(LiteralString node) => node.dartString;
+}
+
+class IsInterpolationVisitor extends AbstractVisitor<bool> {
+  const IsInterpolationVisitor();
+  bool visitNode(Node node) => false;
+  bool visitStringInterpolation(StringInterpolation node) => true;
+  bool visitStringJuxtaposition(StringJuxtaposition node)
+      => node.isInterpolation;
+}
+
+/**
+ * If the given node is a send set, it visits its initializer (first
+ * argument).
+ *
+ * TODO(ahe): This method is controversial, the team needs to discuss
+ * if top-level methods are acceptable and what naming conventions to
+ * use.
+ */
+initializerDo(Node node, f(Node node)) {
+  SendSet send = node.asSendSet();
+  if (send !== null) return f(send.arguments.head);
+}
diff --git a/lib/compiler/implementation/tree/tree.dart b/lib/compiler/implementation/tree/tree.dart
new file mode 100644
index 0000000..8c0cf1e
--- /dev/null
+++ b/lib/compiler/implementation/tree/tree.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2011, 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('tree');
+
+#import('../scanner/scannerlib.dart');
+#import('../util/util.dart');
+#import('../util/characters.dart');
+
+#source('nodes.dart');
+#source('dartstring.dart');
+#source('unparser.dart');
+#source('visitors.dart');
diff --git a/lib/compiler/implementation/tree/unparser.dart b/lib/compiler/implementation/tree/unparser.dart
new file mode 100644
index 0000000..f831c1a
--- /dev/null
+++ b/lib/compiler/implementation/tree/unparser.dart
@@ -0,0 +1,399 @@
+// Copyright (c) 2012, 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.
+
+class Unparser implements Visitor {
+  StringBuffer sb;
+  final bool printDebugInfo;
+
+  Unparser([this.printDebugInfo = false]);
+
+  String unparse(Node node) {
+    sb = new StringBuffer();
+    visit(node);
+    return sb.toString();
+  }
+
+  void add(SourceString string) {
+    string.printOn(sb);
+  }
+
+  visit(Node node) {
+    if (node !== null) {
+      if (printDebugInfo) sb.add('[${node.getObjectDescription()}: ');
+      node.accept(this);
+      if (printDebugInfo) sb.add(']');
+    } else if (printDebugInfo) {
+      sb.add('[null]');
+    }
+  }
+
+  visitBlock(Block node) {
+    visit(node.statements);
+  }
+
+  visitClassNode(ClassNode node) {
+    node.beginToken.value.printOn(sb);
+    sb.add(' ');
+    visit(node.name);
+    sb.add(' ');
+    if (node.extendsKeyword !== null) {
+      node.extendsKeyword.value.printOn(sb);
+      sb.add(' ');
+      visit(node.superclass);
+      sb.add(' ');
+    }
+    visit(node.interfaces);
+    if (node.defaultClause !== null) {
+      visit(node.defaultClause);
+      sb.add(' ');
+    }
+    sb.add('{\n');
+    sb.add('}\n');
+  }
+
+  visitConditional(Conditional node) {
+    visit(node.condition);
+    add(node.questionToken.value);
+    visit(node.thenExpression);
+    add(node.colonToken.value);
+    visit(node.elseExpression);
+  }
+
+  visitExpressionStatement(ExpressionStatement node) {
+    visit(node.expression);
+    add(node.endToken.value);
+  }
+
+  visitFor(For node) {
+    add(node.forToken.value);
+    sb.add('(');
+    visit(node.initializer);
+    visit(node.conditionStatement);
+    visit(node.update);
+    sb.add(')');
+    visit(node.body);
+  }
+
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    visit(node.function);
+  }
+
+  visitFunctionExpression(FunctionExpression node) {
+    if (node.returnType !== null) {
+      visit(node.returnType);
+      sb.add(' ');
+    }
+    visit(node.name);
+    visit(node.parameters);
+    visit(node.body);
+  }
+
+  visitIdentifier(Identifier node) {
+    add(node.token.value);
+  }
+
+  visitIf(If node) {
+    add(node.ifToken.value);
+    visit(node.condition);
+    visit(node.thenPart);
+    if (node.hasElsePart) {
+      add(node.elseToken.value);
+      visit(node.elsePart);
+    }
+  }
+
+  visitLiteralBool(LiteralBool node) {
+    add(node.token.value);
+  }
+
+  visitLiteralDouble(LiteralDouble node) {
+    add(node.token.value);
+  }
+
+  visitLiteralInt(LiteralInt node) {
+    add(node.token.value);
+  }
+
+  visitLiteralString(LiteralString node) {
+    add(node.token.value);
+  }
+
+  visitStringJuxtaposition(LiteralStringJuxtaposition node) {
+    visit(node.first);
+    sb.add(" ");
+    visit(node.second);
+  }
+
+  visitLiteralNull(LiteralNull node) {
+    add(node.token.value);
+  }
+
+  visitNewExpression(NewExpression node) {
+    add(node.newToken.value);
+    sb.add(' ');
+    visit(node.send);
+  }
+
+  visitLiteralList(LiteralList node) {
+    if (node.type !== null) {
+      sb.add('<');
+      visit(node.type);
+      sb.add('>');
+    }
+    sb.add(' ');
+    visit(node.elements);
+  }
+
+  visitModifiers(Modifiers node) => node.visitChildren(this);
+
+  visitNodeList(NodeList node) {
+    if (node.beginToken !== null) add(node.beginToken.value);
+    if (node.nodes !== null) {
+      String delimiter = (node.delimiter === null) ? " " : "${node.delimiter} ";
+      node.nodes.printOn(sb, delimiter);
+    }
+    if (node.endToken !== null) add(node.endToken.value);
+  }
+
+  visitOperator(Operator node) {
+    visitIdentifier(node);
+  }
+
+  visitReturn(Return node) {
+    add(node.beginToken.value);
+    if (node.hasExpression) {
+      sb.add(' ');
+      visit(node.expression);
+    }
+    if (node.endToken !== null) add(node.endToken.value);
+  }
+
+
+  unparseSendPart(Send node) {
+    if (node.isPrefix) {
+      visit(node.selector);
+    }
+    if (node.receiver !== null) {
+      visit(node.receiver);
+      if (node.selector is !Operator) sb.add('.');
+    }
+    if (!node.isPrefix) {
+      visit(node.selector);
+    }
+  }
+
+  visitSend(Send node) {
+    unparseSendPart(node);
+    visit(node.argumentsNode);
+  }
+
+  visitSendSet(SendSet node) {
+    unparseSendPart(node);
+    add(node.assignmentOperator.token.value);
+    visit(node.argumentsNode);
+  }
+
+  visitThrow(Throw node) {
+    add(node.throwToken.value);
+    if (node.expression !== null) {
+      sb.add(' ');
+      visit(node.expression);
+    }
+    node.endToken.value.printOn(sb);
+  }
+
+  visitTypeAnnotation(TypeAnnotation node) {
+    node.visitChildren(this);
+  }
+
+  visitTypeVariable(TypeVariable node) {
+    visit(node.name);
+    if (node.bound !== null) {
+      sb.add(' extends ');
+      visit(node.bound);
+    }
+  }
+
+  visitVariableDefinitions(VariableDefinitions node) {
+    if (node.type !== null) {
+      visit(node.type);
+    } else {
+      sb.add('var');
+    }
+    sb.add(' ');
+    // TODO(karlklose): print modifiers.
+    visit(node.definitions);
+    if (node.endToken.value == const SourceString(';')) {
+      add(node.endToken.value);
+    }
+  }
+
+  visitDoWhile(DoWhile node) {
+    add(node.doKeyword.value);
+    sb.add(' ');
+    visit(node.body);
+    sb.add(' ');
+    add(node.whileKeyword.value);
+    sb.add(' ');
+    visit(node.condition);
+    sb.add(node.endToken.value);
+  }
+
+  visitWhile(While node) {
+    add(node.whileKeyword.value);
+    sb.add(' ');
+    visit(node.condition);
+    sb.add(' ');
+    visit(node.body);
+  }
+
+  visitParenthesizedExpression(ParenthesizedExpression node) {
+    add(node.getBeginToken().value);
+    visit(node.expression);
+    add(node.getEndToken().value);
+  }
+
+  visitStringInterpolation(StringInterpolation node) {
+    visit(node.string);
+    visit(node.parts);
+  }
+
+  visitStringInterpolationPart(StringInterpolationPart node) {
+    sb.add('\${'); // TODO(ahe): Preserve the real tokens.
+    visit(node.expression);
+    sb.add('}');
+    visit(node.string);
+  }
+
+  visitEmptyStatement(EmptyStatement node) {
+    add(node.semicolonToken.value);
+  }
+
+  visitGotoStatement(GotoStatement node) {
+    add(node.keywordToken.value);
+    if (node.target !== null) {
+      sb.add(' ');
+      visit(node.target);
+    }
+    add(node.semicolonToken.value);
+  }
+
+  visitBreakStatement(BreakStatement node) {
+    visitGotoStatement(node);
+  }
+
+  visitContinueStatement(ContinueStatement node) {
+    visitGotoStatement(node);
+  }
+
+  visitForInStatement(ForInStatement node) {
+    add(node.forToken.value);
+    sb.add(' (');
+    visit(node.declaredIdentifier);
+    add(node.inToken.value);
+    visit(node.expression);
+    sb.add(') ');
+    visit(node.body);
+  }
+
+  visitLabeledStatement(LabeledStatement node) {
+    visit(node.label);
+    add(node.colonToken.value);
+    sb.add(' ');
+    visit(node.statement);
+  }
+
+  visitLiteralMap(LiteralMap node) {
+    if (node.typeArguments !== null) visit(node.typeArguments);
+    visit(node.entries);
+  }
+
+  visitLiteralMapEntry(LiteralMapEntry node) {
+    visit(node.key);
+    add(node.colonToken.value);
+    sb.add(' ');
+    visit(node.value);
+  }
+
+  visitNamedArgument(NamedArgument node) {
+    visit(node.name);
+    add(node.colonToken.value);
+    sb.add(' ');
+    visit(node.expression);
+  }
+
+  visitSwitchStatement(SwitchStatement node) {
+    add(node.switchKeyword.value);
+    sb.add(' ');
+    visit(node.parenthesizedExpression);
+    sb.add(' ');
+    visit(node.cases);
+  }
+
+  visitSwitchCase(SwitchCase node) {
+    if (node.label !== null) {
+      visit(node.label);
+      sb.add(': ');
+    }
+    for (Expression expression in node.expressions) {
+      sb.add('case ');
+      visit(expression);
+      sb.add(': ');
+    }
+    if (node.isDefaultCase) {
+      sb.add('default:');
+    }
+    visit(node.statements);
+  }
+
+  visitScriptTag(ScriptTag node) {
+    add(node.beginToken.value);
+    visit(node.tag);
+    sb.add('(');
+    visit(node.argument);
+    if (node.prefixIdentifier !== null) {
+      visit(node.prefixIdentifier);
+      sb.add(': ');
+      visit(node.prefix);
+    }
+    sb.add(')');
+    add(node.endToken.value);
+  }
+
+  visitTryStatement(TryStatement node) {
+    add(node.tryKeyword.value);
+    sb.add(' ');
+    visit(node.tryBlock);
+    visit(node.catchBlocks);
+    if (node.finallyKeyword !== null) {
+      sb.add(' ');
+      add(node.finallyKeyword.value);
+      sb.add(' ');
+      visit(node.finallyBlock);
+    }
+  }
+
+  visitCatchBlock(CatchBlock node) {
+    add(node.catchKeyword.value);
+    sb.add(' ');
+    visit(node.formals);
+    sb.add(' ');
+    visit(node.block);
+  }
+
+  visitTypedef(Typedef node) {
+    add(node.typedefKeyword.value);
+    sb.add(' ');
+    if (node.returnType !== null) {
+      visit(node.returnType);
+      sb.add(' ');
+    }
+    visit(node.name);
+    if (node.typeParameters !== null) {
+      visit(node.typeParameters);
+    }
+    visit(node.formals);
+    add(node.endToken.value);
+  }
+}
diff --git a/lib/compiler/implementation/tree/visitors.dart b/lib/compiler/implementation/tree/visitors.dart
new file mode 100644
index 0000000..880c1a2
--- /dev/null
+++ b/lib/compiler/implementation/tree/visitors.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2011, 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.
+
+class AbstractVisitor<R> implements Visitor<R> {
+  const AbstractVisitor();
+
+  abstract R visitNode(Node node);
+
+  R visitBlock(Block node) => visitStatement(node);
+  R visitBreakStatement(BreakStatement node) => visitGotoStatement(node);
+  R visitCatchBlock(CatchBlock node) => visitNode(node);
+  R visitClassNode(ClassNode node) => visitNode(node);
+  R visitConditional(Conditional node) => visitExpression(node);
+  R visitContinueStatement(ContinueStatement node) => visitGotoStatement(node);
+  R visitDoWhile(DoWhile node) => visitLoop(node);
+  R visitEmptyStatement(EmptyStatement node) => visitStatement(node);
+  R visitExpression(Expression node) => visitNode(node);
+  R visitExpressionStatement(ExpressionStatement node) => visitStatement(node);
+  R visitFor(For node) => visitLoop(node);
+  R visitForInStatement(ForInStatement node) => visitLoop(node);
+  R visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node);
+  R visitFunctionExpression(FunctionExpression node) => visitExpression(node);
+  R visitGotoStatement(GotoStatement node) => visitStatement(node);
+  R visitIdentifier(Identifier node) => visitExpression(node);
+  R visitIf(If node) => visitStatement(node);
+  R visitLabeledStatement(LabeledStatement node) => visitStatement(node);
+  R visitLiteral(Literal node) => visitExpression(node);
+  R visitLiteralBool(LiteralBool node) => visitLiteral(node);
+  R visitLiteralDouble(LiteralDouble node) => visitLiteral(node);
+  R visitLiteralInt(LiteralInt node) => visitLiteral(node);
+  R visitLiteralList(LiteralList node) => visitExpression(node);
+  R visitLiteralMap(LiteralMap node) => visitExpression(node);
+  R visitLiteralMapEntry(LiteralMapEntry node) => visitNode(node);
+  R visitLiteralNull(LiteralNull node) => visitLiteral(node);
+  R visitLiteralString(LiteralString node) => visitStringNode(node);
+  R visitStringJuxtaposition(StringJuxtaposition node) => visitStringNode(node);
+  R visitLoop(Loop node) => visitStatement(node);
+  R visitModifiers(Modifiers node) => visitNode(node);
+  R visitNamedArgument(NamedArgument node) => visitExpression(node);
+  R visitNewExpression(NewExpression node) => visitExpression(node);
+  R visitNodeList(NodeList node) => visitNode(node);
+  R visitOperator(Operator node) => visitIdentifier(node);
+  R visitParenthesizedExpression(ParenthesizedExpression node) {
+    return visitExpression(node);
+  }
+  R visitPostfix(Postfix node) => visitNodeList(node);
+  R visitPrefix(Prefix node) => visitNodeList(node);
+  R visitReturn(Return node) => visitStatement(node);
+  R visitScriptTag(ScriptTag node) => visitNode(node);
+  R visitSend(Send node) => visitExpression(node);
+  R visitSendSet(SendSet node) => visitSend(node);
+  R visitStatement(Statement node) => visitNode(node);
+  R visitStringNode(StringNode node) => visitExpression(node);
+  R visitStringInterpolation(StringInterpolation node) => visitStringNode(node);
+  R visitStringInterpolationPart(StringInterpolationPart node) {
+    return visitNode(node);
+  }
+  R visitSwitchCase(SwitchCase node) => visitNode(node);
+  R visitSwitchStatement(SwitchStatement node) => visitStatement(node);
+  R visitThrow(Throw node) => visitStatement(node);
+  R visitTryStatement(TryStatement node) => visitStatement(node);
+  R visitTypeAnnotation(TypeAnnotation node) => visitNode(node);
+  R visitTypedef(Typedef node) => visitNode(node);
+  R visitTypeVariable(TypeVariable node) => visitNode(node);
+  R visitVariableDefinitions(VariableDefinitions node) => visitStatement(node);
+  R visitWhile(While node) => visitLoop(node);
+}
+
+/**
+ * This visitor takes another visitor and applies it to every
+ * node in the tree. There is currently no way to control the
+ * traversal.
+ */
+class TraversingVisitor extends AbstractVisitor {
+  final Visitor visitor;
+
+  TraversingVisitor(Visitor this.visitor);
+
+  visitNode(Node node) {
+    node.accept(visitor);
+    node.visitChildren(this);
+  }
+}
diff --git a/lib/compiler/implementation/tree_validator.dart b/lib/compiler/implementation/tree_validator.dart
new file mode 100644
index 0000000..9683721
--- /dev/null
+++ b/lib/compiler/implementation/tree_validator.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2011, 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.
+
+class TreeValidatorTask extends CompilerTask {
+  TreeValidatorTask(Compiler compiler) : super(compiler);
+
+  void validate(Node tree) {
+    assert(check(tree));
+  }
+
+  bool check(Node tree) {
+    List<InvalidNodeError> errors = [];
+    void report(node, message) {
+      final error = new InvalidNodeError(node, message);
+      errors.add(error);
+      compiler.reportWarning(node, message);
+    };
+    final validator = new ValidatorVisitor(report);
+    tree.accept(new TraversingVisitor(validator));
+
+    return errors.isEmpty();
+  }
+}
+
+class ValidatorVisitor extends AbstractVisitor {
+  final Function reportInvalidNode;
+
+  ValidatorVisitor(Function this.reportInvalidNode);
+
+  expect(Node node, bool test, [message]) {
+    if (!test) reportInvalidNode(node, message);
+  }
+
+  visitNode(Node node) {}
+
+  visitSendSet(SendSet node) {
+    final selector = node.selector;
+    final name = node.assignmentOperator.source.stringValue;
+    final arguments = node.arguments;
+
+    expect(node, arguments !== null);
+    expect(node, selector is Identifier, 'selector is not assignable');
+    if (name === '++' || name === '--') {
+      expect(node, node.assignmentOperator is Operator);
+      if (node.isIndex) {
+        expect(node.arguments.tail.head, node.arguments.tail.isEmpty());
+      } else {
+        expect(node.arguments.head, node.arguments.isEmpty());
+      }
+    } else {
+      expect(node, !node.arguments.isEmpty());
+    }
+  }
+}
+
+class InvalidNodeError {
+  final Node node;
+  final String message;
+  InvalidNodeError(this.node, [this.message]);
+
+  toString() {
+    String nodeString = new Unparser(true).unparse(node);
+    String result = 'invalid node: $nodeString';
+    if (message !== null) result = '$result ($message)';
+    return result;
+  }
+}
diff --git a/lib/compiler/implementation/typechecker.dart b/lib/compiler/implementation/typechecker.dart
new file mode 100644
index 0000000..ef555b2
--- /dev/null
+++ b/lib/compiler/implementation/typechecker.dart
@@ -0,0 +1,716 @@
+// Copyright (c) 2012, 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.
+
+class TypeCheckerTask extends CompilerTask {
+  TypeCheckerTask(Compiler compiler) : super(compiler);
+  String get name() => "Type checker";
+
+  static final bool LOG_FAILURES = false;
+
+  void check(Node tree, TreeElements elements) {
+    measure(() {
+      Visitor visitor =
+          new TypeCheckerVisitor(compiler, elements, compiler.types);
+      try {
+        tree.accept(visitor);
+      } catch (CancelTypeCheckException e) {
+        if (LOG_FAILURES) {
+          // Do not warn about unimplemented features; log message instead.
+          compiler.log("'${e.node}': ${e.reason}");
+        }
+      }
+    });
+  }
+}
+
+interface Type {
+  SourceString get name();
+  Element get element();
+}
+
+class TypeVariableType implements Type {
+  final SourceString name;
+  Element element;
+  TypeVariableType(this.name, [this.element]);
+}
+
+/**
+ * A statement type tracks whether a statement returns or may return.
+ */
+class StatementType implements Type {
+  final String stringName;
+  Element get element() => null;
+
+  SourceString get name() => new SourceString(stringName);
+
+  const StatementType(this.stringName);
+
+  static final RETURNING = const StatementType('<returning>');
+  static final NOT_RETURNING = const StatementType('<not returning>');
+  static final MAYBE_RETURNING = const StatementType('<maybe returning>');
+
+  /** Combine the information about two control-flow edges that are joined. */
+  StatementType join(StatementType other) {
+    return (this === other) ? this : MAYBE_RETURNING;
+  }
+
+  String toString() => stringName;
+}
+
+class SimpleType implements Type {
+  final SourceString name;
+  final Element element;
+
+  const SimpleType(SourceString this.name, Element this.element);
+
+  String toString() => name.slowToString();
+}
+
+class FunctionType implements Type {
+  final Element element;
+  final Type returnType;
+  final Link<Type> parameterTypes;
+
+  const FunctionType(Type this.returnType, Link<Type> this.parameterTypes,
+                     Element this.element);
+
+  toString() {
+    StringBuffer sb = new StringBuffer();
+    bool first = true;
+    sb.add('(');
+    parameterTypes.printOn(sb, ', ');
+    sb.add(') -> ${returnType}');
+    return sb.toString();
+  }
+
+  SourceString get name() => const SourceString('Function');
+}
+
+class Types {
+  static final VOID = const SourceString('void');
+  static final INT = const SourceString('int');
+  static final DOUBLE = const SourceString('double');
+  static final DYNAMIC = const SourceString('Dynamic');
+  static final STRING = const SourceString('String');
+  static final BOOL = const SourceString('bool');
+  static final OBJECT = const SourceString('Object');
+  static final LIST = const SourceString('List');
+
+  final SimpleType voidType;
+  final SimpleType dynamicType;
+
+  Types() : this.with(new LibraryElement(new Script(null, null)));
+  Types.with(LibraryElement library)
+    : voidType = new SimpleType(VOID, new ClassElement(VOID, library)),
+      dynamicType = new SimpleType(DYNAMIC, new ClassElement(DYNAMIC, library));
+
+  Type lookup(SourceString s) {
+    if (VOID == s) {
+      return voidType;
+    } else if (DYNAMIC == s || s.stringValue === 'var') {
+      return dynamicType;
+    }
+    return null;
+  }
+
+  /** Returns true if t is a subtype of s */
+  bool isSubtype(Type t, Type s) {
+    if (t === s || t === dynamicType || s === dynamicType ||
+        s.name == OBJECT) return true;
+    if (t is SimpleType) {
+      if (s is !SimpleType) return false;
+      ClassElement tc = t.element;
+      for (Link<Type> supertypes = tc.allSupertypes;
+           supertypes != null && !supertypes.isEmpty();
+           supertypes = supertypes.tail) {
+        Type supertype = supertypes.head;
+        if (supertype.element === s.element) return true;
+      }
+      return false;
+    } else if (t is FunctionType) {
+      if (s is !FunctionType) return false;
+      FunctionType tf = t;
+      FunctionType sf = s;
+      Link<Type> tps = tf.parameterTypes;
+      Link<Type> sps = sf.parameterTypes;
+      while (!tps.isEmpty() && !sps.isEmpty()) {
+        if (!isAssignable(tps.head, sps.head)) return false;
+        tps = tps.tail;
+        sps = sps.tail;
+      }
+      if (!tps.isEmpty() || !sps.isEmpty()) return false;
+      if (!isAssignable(sf.returnType, tf.returnType)) return false;
+      return true;
+    } else {
+      throw 'internal error: unknown type kind';
+    }
+  }
+
+  bool isAssignable(Type r, Type s) {
+    return isSubtype(r, s) || isSubtype(s, r);
+  }
+}
+
+class CancelTypeCheckException {
+  final Node node;
+  final String reason;
+
+  CancelTypeCheckException(this.node, this.reason);
+}
+
+Type lookupType(SourceString name, Compiler compiler, types) {
+  Type t = types.lookup(name);
+  if (t !== null) return t;
+  Element element = compiler.coreLibrary.find(name);
+  if (element !== null && element.kind === ElementKind.CLASS) {
+    return element.computeType(compiler);
+  }
+  return null;
+}
+
+class TypeCheckerVisitor implements Visitor<Type> {
+  final Compiler compiler;
+  final TreeElements elements;
+  Node lastSeenNode;
+  final Types types;
+
+  Type expectedReturnType;
+  ClassElement currentClass;
+
+  Type intType;
+  Type doubleType;
+  Type boolType;
+  Type stringType;
+  Type objectType;
+  Type listType;
+
+  TypeCheckerVisitor(Compiler this.compiler, TreeElements this.elements,
+                     Types this.types) {
+    intType = lookupType(Types.INT, compiler, types);
+    doubleType = lookupType(Types.DOUBLE, compiler, types);
+    boolType = lookupType(Types.BOOL, compiler, types);
+    stringType = lookupType(Types.STRING, compiler, types);
+    objectType = lookupType(Types.OBJECT, compiler, types);
+    listType = lookupType(Types.LIST, compiler, types);
+  }
+
+  Type fail(node, [reason]) {
+    String message = 'cannot type-check';
+    if (reason !== null) {
+      message = '$message: $reason';
+    }
+    throw new CancelTypeCheckException(node, message);
+  }
+
+  reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) {
+    compiler.reportWarning(node, new TypeWarning(kind, arguments));
+  }
+
+  Type analyzeNonVoid(Node node) {
+    Type type = analyze(node);
+    if (type == types.voidType) {
+      reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
+    }
+    return type;
+  }
+
+  Type analyzeWithDefault(Node node, Type defaultValue) {
+    return node !== null ? analyze(node) : defaultValue;
+  }
+
+  Type analyze(Node node) {
+    if (node == null) {
+      final String error = 'internal error: unexpected node: null';
+      if (lastSeenNode != null) {
+        fail(null, error);
+      } else {
+        compiler.cancel(error);
+      }
+    } else {
+      lastSeenNode = node;
+    }
+    Type result = node.accept(this);
+    // TODO(karlklose): record type?
+    if (result === null) {
+      fail(node, 'internal error: type is null');
+    }
+    return result;
+  }
+
+  /**
+   * Check if a value of type t can be assigned to a variable,
+   * parameter or return value of type s.
+   */
+  checkAssignable(Node node, Type s, Type t) {
+    if (!types.isAssignable(s, t)) {
+      reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]);
+    }
+  }
+
+  checkCondition(Expression condition) {
+    checkAssignable(condition, boolType, analyze(condition));
+  }
+
+  Type visitBlock(Block node) {
+    return analyze(node.statements);
+  }
+
+  Type visitClassNode(ClassNode node) {
+    fail(node);
+  }
+
+  Type visitDoWhile(DoWhile node) {
+    StatementType bodyType = analyze(node.body);
+    checkCondition(node.condition);
+    return bodyType.join(StatementType.NOT_RETURNING);
+  }
+
+  Type visitExpressionStatement(ExpressionStatement node) {
+    analyze(node.expression);
+    return StatementType.NOT_RETURNING;
+  }
+
+  /** Dart Programming Language Specification: 11.5.1 For Loop */
+  Type visitFor(For node) {
+    analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING);
+    checkCondition(node.condition);
+    analyzeWithDefault(node.update, StatementType.NOT_RETURNING);
+    StatementType bodyType = analyze(node.body);
+    return bodyType.join(StatementType.NOT_RETURNING);
+  }
+
+  Type visitFunctionDeclaration(FunctionDeclaration node) {
+    analyze(node.function);
+    return StatementType.NOT_RETURNING;
+  }
+
+  Type visitFunctionExpression(FunctionExpression node) {
+    Type type;
+    Type returnType;
+    Type previousType;
+    final FunctionElement element = elements[node];
+    if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR ||
+        element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
+      type = types.dynamicType;
+      returnType = types.voidType;
+    } else {
+      FunctionType functionType = computeType(element);
+      returnType = functionType.returnType;
+      type = functionType;
+    }
+    Type previous = expectedReturnType;
+    expectedReturnType = returnType;
+    if (element.isMember()) currentClass = element.enclosingElement;
+    StatementType bodyType = analyze(node.body);
+    if (returnType != types.voidType && returnType != types.dynamicType
+        && bodyType != StatementType.RETURNING) {
+      MessageKind kind;
+      if (bodyType == StatementType.MAYBE_RETURNING) {
+        kind = MessageKind.MAYBE_MISSING_RETURN;
+      } else {
+        kind = MessageKind.MISSING_RETURN;
+      }
+      reportTypeWarning(node.name, kind);
+    }
+    expectedReturnType = previous;
+    return type;
+  }
+
+  Type visitIdentifier(Identifier node) {
+    if (node.isThis()) {
+      return currentClass.computeType(compiler);
+    } else {
+      fail(node, 'internal error: unexpected identifier');
+    }
+  }
+
+  Type visitIf(If node) {
+    checkCondition(node.condition);
+    StatementType thenType = analyze(node.thenPart);
+    StatementType elseType = node.hasElsePart ? analyze(node.elsePart)
+                                              : StatementType.NOT_RETURNING;
+    return thenType.join(elseType);
+  }
+
+  Type visitLoop(Loop node) {
+    fail(node, 'internal error');
+  }
+
+  Type lookupMethodType(Node node, ClassElement classElement,
+                        SourceString name) {
+    Element member = classElement.lookupLocalMember(name);
+    if (member === null) {
+      classElement.ensureResolved(compiler);
+      for (Link<Type> supertypes = classElement.allSupertypes;
+           !supertypes.isEmpty();
+           supertypes = supertypes.tail) {
+        ClassElement lookupTarget = supertypes.head.element;
+        member = lookupTarget.lookupLocalMember(name);
+        if (member !== null) return computeType(member);
+      }
+    }
+    if (member !== null && member.kind == ElementKind.FUNCTION) {
+      return computeType(member);
+    }
+    reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
+                      [classElement.name, name]);
+    return types.dynamicType;
+  }
+
+  Link<Type> analyzeArguments(Link<Node> arguments) {
+    LinkBuilder<Type> builder = new LinkBuilder<Type>();
+    while(!arguments.isEmpty()) {
+      builder.addLast(analyze(arguments.head));
+      arguments = arguments.tail;
+    }
+    return builder.toLink();
+  }
+
+  Type visitSend(Send node) {
+    if (Elements.isClosureSend(node, elements)) {
+      // TODO(karlklose): Finish implementation.
+      return types.dynamicType;
+    }
+
+    Identifier selector = node.selector.asIdentifier();
+    String name = selector.source.stringValue;
+
+    if (node.isOperator && name === 'is') {
+      analyze(node.receiver);
+      return boolType;
+    } else if (node.isOperator) {
+      final Node firstArgument = node.receiver;
+      final Type firstArgumentType = analyze(node.receiver);
+      final arguments = node.arguments;
+      final Node secondArgument = arguments.isEmpty() ? null : arguments.head;
+      final Type secondArgumentType = analyzeWithDefault(secondArgument, null);
+
+      if (name === '+' || name === '=' || name === '-'
+          || name === '*' || name === '/' || name === '%'
+          || name === '~/' || name === '|' || name ==='&'
+          || name === '^' || name === '~'|| name === '<<'
+          || name === '>>' || name === '[]') {
+        return types.dynamicType;
+      } else if (name === '<' || name === '>' || name === '<='
+                 || name === '>=' || name === '==' || name === '!='
+                 || name === '===' || name === '!==') {
+        return boolType;
+      } else if (name === '||' || name === '&&' || name === '!') {
+        checkAssignable(firstArgument, boolType, firstArgumentType);
+        if (!arguments.isEmpty()) {
+          // TODO(karlklose): check number of arguments in validator.
+          checkAssignable(secondArgument, boolType, secondArgumentType);
+        }
+        return boolType;
+      }
+      fail(selector, 'unexpected operator ${name}');
+
+    } else if (node.isPropertyAccess) {
+      if (node.receiver !== null) fail(node, 'cannot handle fields');
+      Element element = elements[node];
+      if (element === null) fail(node.selector, 'unresolved property');
+      return computeType(element);
+
+    } else if (node.isFunctionObjectInvocation) {
+      fail(node.receiver, 'function object invocation unimplemented');
+
+    } else {
+      Link<Type> argumentTypes = analyzeArguments(node.arguments);
+      FunctionType funType;
+      if (node.receiver !== null) {
+        Type receiverType = analyze(node.receiver);
+        if (receiverType === types.dynamicType) return types.dynamicType;
+        if (receiverType === null) {
+          fail(node.receiver, 'receivertype is null');
+        }
+        if (receiverType.element.kind !== ElementKind.CLASS) {
+          fail(node.receiver, 'receivertype is not a class');
+        }
+        ClassElement classElement = receiverType.element;
+        // TODO(karlklose): substitute type arguments.
+        Type memberType =
+          lookupMethodType(selector, classElement, selector.source);
+        if (memberType === types.dynamicType) return types.dynamicType;
+        if (memberType is !FunctionType) {
+          fail(node, 'can only handle function types');
+        }
+        funType = memberType;
+      } else {
+        Element element = elements[node];
+        if (element === null) {
+          fail(node, 'unresolved ${node.selector}');
+        } else if (element.kind === ElementKind.FUNCTION) {
+          funType = computeType(element);
+        } else if (element.kind === ElementKind.FOREIGN) {
+          return types.dynamicType;
+        } else {
+          fail(node, 'unexpected element kind ${element.kind}');
+        }
+      }
+      Link<Type> parameterTypes = funType.parameterTypes;
+      Link<Node> argumentNodes = node.arguments;
+      while (!argumentTypes.isEmpty() && !parameterTypes.isEmpty()) {
+        checkAssignable(argumentNodes.head, parameterTypes.head,
+                        argumentTypes.head);
+        argumentTypes = argumentTypes.tail;
+        parameterTypes = parameterTypes.tail;
+        argumentNodes = argumentNodes.tail;
+      }
+      if (!argumentTypes.isEmpty()) {
+        reportTypeWarning(argumentNodes.head, MessageKind.ADDITIONAL_ARGUMENT);
+      } else if (!parameterTypes.isEmpty()) {
+        reportTypeWarning(node, MessageKind.MISSING_ARGUMENT,
+                          [parameterTypes.head]);
+      }
+      return funType.returnType;
+    }
+  }
+
+  visitSendSet(SendSet node) {
+    Identifier selector = node.selector;
+    final name = node.assignmentOperator.source.stringValue;
+    if (name === '++' || name === '--') {
+      final Element element = elements[node.selector];
+      final Type receiverType = computeType(element);
+      // TODO(karlklose): this should be the return type instead of int.
+      return node.isPrefix ? intType : receiverType;
+    } else {
+      Type targetType = computeType(elements[node]);
+      Node value = node.arguments.head;
+      checkAssignable(value, targetType, analyze(value));
+      return targetType;
+    }
+  }
+
+  Type visitLiteralInt(LiteralInt node) {
+    return intType;
+  }
+
+  Type visitLiteralDouble(LiteralDouble node) {
+    return doubleType;
+  }
+
+  Type visitLiteralBool(LiteralBool node) {
+    return boolType;
+  }
+
+  Type visitLiteralString(LiteralString node) {
+    return stringType;
+  }
+
+  Type visitStringJuxtaposition(StringJuxtaposition node) {
+    analyze(node.first);
+    analyze(node.second);
+    return stringType;
+  }
+
+  Type visitLiteralNull(LiteralNull node) {
+    return types.dynamicType;
+  }
+
+  Type visitNewExpression(NewExpression node) {
+    return analyze(node.send.selector);
+  }
+
+  Type visitLiteralList(LiteralList node) {
+    return listType;
+  }
+
+  Type visitNodeList(NodeList node) {
+    Type type = StatementType.NOT_RETURNING;
+    bool reportedDeadCode = false;
+    for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
+      Type nextType = analyze(link.head);
+      if (type == StatementType.RETURNING) {
+        if (!reportedDeadCode) {
+          reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE);
+          reportedDeadCode = true;
+        }
+      } else if (type == StatementType.MAYBE_RETURNING){
+        if (nextType == StatementType.RETURNING) {
+          type = nextType;
+        }
+      } else {
+        type = nextType;
+      }
+    }
+    return type;
+  }
+
+  Type visitOperator(Operator node) {
+    fail(node, 'internal error');
+  }
+
+  /** Dart Programming Language Specification: 11.10 Return */
+  Type visitReturn(Return node) {
+    final expression = node.expression;
+    final isVoidFunction = (expectedReturnType === types.voidType);
+
+    // Executing a return statement return e; [...] It is a static type warning
+    // if the type of e may not be assigned to the declared return type of the
+    // immediately enclosing function.
+    if (expression !== null) {
+      final expressionType = analyze(expression);
+      if (isVoidFunction
+          && !types.isAssignable(expressionType, types.voidType)) {
+        reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID,
+                          [expressionType]);
+      } else {
+        checkAssignable(expression, expectedReturnType, expressionType);
+      }
+
+    // Let f be the function immediately enclosing a return statement of the
+    // form 'return;' It is a static warning if both of the following conditions
+    // hold:
+    // - f is not a generative constructor.
+    // - The return type of f may not be assigned to void.
+    } else if (!types.isAssignable(expectedReturnType, types.voidType)) {
+      reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]);
+    }
+    return StatementType.RETURNING;
+  }
+
+  Type visitThrow(Throw node) {
+    if (node.expression !== null) analyze(node.expression);
+    return StatementType.RETURNING;
+  }
+
+  Type computeType(Element element) {
+    if (element === null) return types.dynamicType;
+    Type result = element.computeType(compiler);
+    return (result !== null) ? result : types.dynamicType;
+  }
+
+  Type visitTypeAnnotation(TypeAnnotation node) {
+    if (node.typeName === null) return types.dynamicType;
+    Identifier identifier = node.typeName.asIdentifier();
+    if (identifier === null) {
+      fail(node.typeName, 'library prefix not implemented');
+    }
+    // TODO(ahe): Why wasn't this resolved by the resolver?
+    Type type = lookupType(identifier.source, compiler, types);
+    if (type === null) {
+      // The type name cannot be resolved, but the resolver
+      // already gave a warning, so we continue checking.
+      return types.dynamicType;
+    }
+    return type;
+  }
+
+  visitTypeVariable(TypeVariable node) {
+    return types.dynamicType;
+  }
+
+  Type visitVariableDefinitions(VariableDefinitions node) {
+    Type type = analyzeWithDefault(node.type, types.dynamicType);
+    if (type == types.voidType) {
+      reportTypeWarning(node.type, MessageKind.VOID_VARIABLE);
+      type = types.dynamicType;
+    }
+    for (Link<Node> link = node.definitions.nodes; !link.isEmpty();
+         link = link.tail) {
+      Node initialization = link.head;
+      compiler.ensure(initialization is Identifier
+                      || initialization is Send);
+      if (initialization is Send) {
+        Type initializer = analyzeNonVoid(link.head);
+        checkAssignable(node, type, initializer);
+      }
+    }
+    return StatementType.NOT_RETURNING;
+  }
+
+  Type visitWhile(While node) {
+    checkCondition(node.condition);
+    StatementType bodyType = analyze(node.body);
+    return bodyType.join(StatementType.NOT_RETURNING);
+  }
+
+  Type visitParenthesizedExpression(ParenthesizedExpression node) {
+    return analyze(node.expression);
+  }
+
+  Type visitConditional(Conditional node) {
+    checkCondition(node.condition);
+    Type thenType = analyzeNonVoid(node.thenExpression);
+    Type elseType = analyzeNonVoid(node.elseExpression);
+    if (types.isSubtype(thenType, elseType)) {
+      return thenType;
+    } else if (types.isSubtype(elseType, thenType)) {
+      return elseType;
+    } else {
+      return objectType;
+    }
+  }
+
+  Type visitModifiers(Modifiers node) {}
+
+  visitStringInterpolation(StringInterpolation node) {
+    node.visitChildren(this);
+    return stringType;
+  }
+
+  visitStringInterpolationPart(StringInterpolationPart node) {
+    node.visitChildren(this);
+    return stringType;
+  }
+
+  visitEmptyStatement(EmptyStatement node) {
+    return StatementType.NOT_RETURNING;
+  }
+
+  visitBreakStatement(BreakStatement node) {
+    return StatementType.NOT_RETURNING;
+  }
+
+  visitContinueStatement(ContinueStatement node) {
+    return StatementType.NOT_RETURNING;
+  }
+
+  visitForInStatement(ForInStatement node) {
+    analyze(node.expression);
+    StatementType bodyType = analyze(node.body);
+    return bodyType.join(StatementType.NOT_RETURNING);
+  }
+
+  visitLabeledStatement(LabeledStatement node) {
+    return node.statement.accept(this);
+  }
+
+  visitLiteralMap(LiteralMap node) {
+    fail(node);
+  }
+
+  visitLiteralMapEntry(LiteralMapEntry node) {
+    fail(node);
+  }
+
+  visitNamedArgument(NamedArgument node) {
+    fail(node, 'named argument not implemented');
+  }
+
+  visitSwitchStatement(SwitchStatement node) {
+    fail(node);
+  }
+
+  visitSwitchCase(SwitchCase node) {
+    fail(node);
+  }
+
+  visitTryStatement(TryStatement node) {
+    fail(node, 'unimplemented');
+  }
+
+  visitScriptTag(ScriptTag node) {
+    fail(node);
+  }
+
+  visitCatchBlock(CatchBlock node) {
+    fail(node);
+  }
+
+  visitTypedef(Typedef node) {
+    fail(node);
+  }
+}
diff --git a/lib/compiler/implementation/universe.dart b/lib/compiler/implementation/universe.dart
new file mode 100644
index 0000000..c79d0ea
--- /dev/null
+++ b/lib/compiler/implementation/universe.dart
@@ -0,0 +1,234 @@
+// Copyright (c) 2012, 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.
+
+class Universe {
+  Map<Element, String> generatedCode;
+  Map<Element, String> generatedBailoutCode;
+  final Set<ClassElement> instantiatedClasses;
+  final Set<SourceString> instantiatedClassInstanceFields;
+  final Set<FunctionElement> staticFunctionsNeedingGetter;
+  final Map<SourceString, Set<Selector>> invokedNames;
+  final Set<SourceString> invokedGetters;
+  final Set<SourceString> invokedSetters;
+  final Map<String, LibraryElement> libraries;
+  // TODO(ngeoffray): This should be a Set<Type>.
+  final Set<Element> isChecks;
+
+  Universe() : generatedCode = new Map<Element, String>(),
+               generatedBailoutCode = new Map<Element, String>(),
+               libraries = new Map<String, LibraryElement>(),
+               instantiatedClasses = new Set<ClassElement>(),
+               instantiatedClassInstanceFields = new Set<SourceString>(),
+               staticFunctionsNeedingGetter = new Set<FunctionElement>(),
+               invokedNames = new Map<SourceString, Set<Selector>>(),
+               invokedGetters = new Set<SourceString>(),
+               invokedSetters = new Set<SourceString>(),
+               isChecks = new Set<Element>();
+
+  void addGeneratedCode(WorkItem work, String code) {
+    generatedCode[work.element] = code;
+  }
+
+  void addBailoutCode(WorkItem work, String code) {
+    generatedBailoutCode[work.element] = code;
+  }
+}
+
+class SelectorKind {
+  final String name;
+  const SelectorKind(this.name);
+
+  static final SelectorKind GETTER = const SelectorKind('getter');
+  static final SelectorKind SETTER = const SelectorKind('setter');
+  static final SelectorKind INVOCATION = const SelectorKind('invocation');
+  static final SelectorKind OPERATOR = const SelectorKind('operator');
+  static final SelectorKind INDEX = const SelectorKind('index');
+
+  toString() => name;
+}
+
+class Selector implements Hashable {
+  // The numbers of arguments of the selector. Includes named
+  // arguments.
+  final int argumentCount;
+  final SelectorKind kind;
+  const Selector(this.kind, this.argumentCount);
+
+  int hashCode() => argumentCount + 1000 * namedArguments.length;
+  List<SourceString> get namedArguments() => const <SourceString>[];
+  int get namedArgumentCount() => 0;
+  int get positionalArgumentCount() => argumentCount;
+
+  static final Selector GETTER = const Selector(SelectorKind.GETTER, 0);
+  static final Selector SETTER = const Selector(SelectorKind.SETTER, 1);
+  static final Selector UNARY_OPERATOR =
+      const Selector(SelectorKind.OPERATOR, 0);
+  static final Selector BINARY_OPERATOR =
+      const Selector(SelectorKind.OPERATOR, 1);
+  static final Selector INDEX = const Selector(SelectorKind.INDEX, 1);
+  static final Selector INDEX_SET = const Selector(SelectorKind.INDEX, 2);
+  static final Selector INDEX_AND_INDEX_SET =
+      const Selector(SelectorKind.INDEX, 2);
+  static final Selector GETTER_AND_SETTER =
+      const Selector(SelectorKind.SETTER, 1);
+  static final Selector INVOCATION_0 =
+      const Selector(SelectorKind.INVOCATION, 0);
+  static final Selector INVOCATION_1 =
+      const Selector(SelectorKind.INVOCATION, 1);
+  static final Selector INVOCATION_2 =
+      const Selector(SelectorKind.INVOCATION, 2);
+
+  bool applies(FunctionParameters parameters) {
+    if (argumentCount > parameters.parameterCount) return false;
+    int requiredParameterCount = parameters.requiredParameterCount;
+    int optionalParameterCount = parameters.optionalParameterCount;
+
+    bool hasOptionalParameters = !parameters.optionalParameters.isEmpty();
+    if (namedArguments.isEmpty()) {
+      if (!hasOptionalParameters) {
+        return requiredParameterCount == argumentCount;
+      } else {
+        return argumentCount >= requiredParameterCount &&
+            argumentCount <= requiredParameterCount + optionalParameterCount;
+      }
+    } else {
+      if (!hasOptionalParameters) return false;
+      Link<Element> remainingNamedParameters = parameters.optionalParameters;
+      for (int i = requiredParameterCount; i < positionalArgumentCount; i++) {
+        remainingNamedParameters = remainingNamedParameters.tail;
+      }
+      Set<SourceString> nameSet = new Set<SourceString>();
+      for (;
+           !remainingNamedParameters.isEmpty();
+           remainingNamedParameters = remainingNamedParameters.tail) {
+        nameSet.add(remainingNamedParameters.head.name);
+      }
+
+      for (SourceString name in namedArguments) {
+        if (!nameSet.contains(name)) {
+          return false;
+        }
+        nameSet.remove(name);
+      }
+      return true;
+    }
+  }
+
+  /**
+   * Returns [:true:] if the selector and the [element] match; [:false:]
+   * otherwise.
+   */
+  bool addSendArgumentsToList(Send send,
+                              List list,
+                              FunctionParameters parameters,
+                              compileArgument(Node argument),
+                              compileConstant(Element element)) {
+    void addMatchingSendArgumentsToList(Link<Node> link) {
+      for (; !link.isEmpty(); link = link.tail) {
+        list.add(compileArgument(link.head));
+      }
+    }
+
+    if (!this.applies(parameters)) return false;
+    if (this.positionalArgumentCount == parameters.parameterCount) {
+      addMatchingSendArgumentsToList(send.arguments);
+      return true;
+    }
+
+    // If there are named arguments, provide them in the order
+    // expected by the called function, which is the source order.
+
+    // Visit positional arguments and add them to the list.
+    Link<Node> arguments = send.arguments;
+    int positionalArgumentCount = this.positionalArgumentCount;
+    for (int i = 0;
+         i < positionalArgumentCount;
+         arguments = arguments.tail, i++) {
+      list.add(compileArgument(arguments.head));
+    }
+
+    // Visit named arguments and add them into a temporary list.
+    List namedArguments = [];
+    for (; !arguments.isEmpty(); arguments = arguments.tail) {
+      NamedArgument namedArgument = arguments.head;
+      namedArguments.add(compileArgument(namedArgument.expression));
+    }
+
+    Link<Element> remainingNamedParameters = parameters.optionalParameters;
+    // Skip the optional parameters that have been given in the
+    // positional arguments.
+    for (int i = parameters.requiredParameterCount;
+         i < positionalArgumentCount;
+         i++) {
+      remainingNamedParameters = remainingNamedParameters.tail;
+    }
+
+    // Loop over the remaining named parameters, and try to find
+    // their values: either in the temporary list or using the
+    // default value.
+    for (;
+         !remainingNamedParameters.isEmpty();
+         remainingNamedParameters = remainingNamedParameters.tail) {
+      Element parameter = remainingNamedParameters.head;
+      int foundIndex = -1;
+      for (int i = 0; i < this.namedArguments.length; i++) {
+        SourceString name = this.namedArguments[i];
+        if (name == parameter.name) {
+          foundIndex = i;
+          break;
+        }
+      }
+      if (foundIndex != -1) {
+        list.add(namedArguments[foundIndex]);
+      } else {
+        list.add(compileConstant(parameter)); 
+      }
+    }
+    return true;
+  }
+
+  static bool sameNames(List<SourceString> first, List<SourceString> second) {
+    for (int i = 0; i < first.length; i++) {
+      if (first[i] != second[i]) return false;
+    }
+    return true;
+  }
+
+  bool operator ==(other) {
+    if (other is !Selector) return false;
+    return argumentCount == other.argumentCount
+           && namedArguments.length == other.namedArguments.length
+           && sameNames(namedArguments, other.namedArguments);
+  }
+
+  List<SourceString> getOrderedNamedArguments() => namedArguments;
+
+  toString() => '$kind $argumentCount';
+}
+
+class Invocation extends Selector {
+  final List<SourceString> namedArguments;
+  List<SourceString> orderedNamedArguments;
+  int get namedArgumentCount() => namedArguments.length;
+  int get positionalArgumentCount() => argumentCount - namedArgumentCount;
+
+  Invocation(int argumentCount,
+             [List<SourceString> names = const <SourceString>[]])
+      : super(SelectorKind.INVOCATION, argumentCount),
+        namedArguments = names,
+        orderedNamedArguments = const <SourceString>[];
+
+  List<SourceString> getOrderedNamedArguments() {
+    if (namedArguments.isEmpty()) return namedArguments;
+    // We use the empty const List as a sentinel.
+    if (!orderedNamedArguments.isEmpty()) return orderedNamedArguments;
+
+    List<SourceString> list = new List<SourceString>.from(namedArguments);
+    list.sort((SourceString first, SourceString second) {
+      return first.slowToString().compareTo(second.slowToString());
+    });
+    orderedNamedArguments = list;
+    return orderedNamedArguments;
+  }
+}
diff --git a/lib/compiler/implementation/util/characters.dart b/lib/compiler/implementation/util/characters.dart
new file mode 100644
index 0000000..694afd7
--- /dev/null
+++ b/lib/compiler/implementation/util/characters.dart
@@ -0,0 +1,135 @@
+// Copyright (c) 2011, 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("characters");
+
+final int $EOF = 0;
+final int $STX = 2;
+final int $BS  = 8;
+final int $TAB = 9;
+final int $LF = 10;
+final int $VTAB = 11;
+final int $FF = 12;
+final int $CR = 13;
+final int $SPACE = 32;
+final int $BANG = 33;
+final int $DQ = 34;
+final int $HASH = 35;
+final int $$ = 36;
+final int $PERCENT = 37;
+final int $AMPERSAND = 38;
+final int $SQ = 39;
+final int $OPEN_PAREN = 40;
+final int $CLOSE_PAREN = 41;
+final int $STAR = 42;
+final int $PLUS = 43;
+final int $COMMA = 44;
+final int $MINUS = 45;
+final int $PERIOD = 46;
+final int $SLASH = 47;
+final int $0 = 48;
+final int $1 = 49;
+final int $2 = 50;
+final int $3 = 51;
+final int $4 = 52;
+final int $5 = 53;
+final int $6 = 54;
+final int $7 = 55;
+final int $8 = 56;
+final int $9 = 57;
+final int $COLON = 58;
+final int $SEMICOLON = 59;
+final int $LT = 60;
+final int $EQ = 61;
+final int $GT = 62;
+final int $QUESTION = 63;
+final int $AT = 64;
+final int $A = 65;
+final int $B = 66;
+final int $C = 67;
+final int $D = 68;
+final int $E = 69;
+final int $F = 70;
+final int $G = 71;
+final int $H = 72;
+final int $I = 73;
+final int $J = 74;
+final int $K = 75;
+final int $L = 76;
+final int $M = 77;
+final int $N = 78;
+final int $O = 79;
+final int $P = 80;
+final int $Q = 81;
+final int $R = 82;
+final int $S = 83;
+final int $T = 84;
+final int $U = 85;
+final int $V = 86;
+final int $W = 87;
+final int $X = 88;
+final int $Y = 89;
+final int $Z = 90;
+final int $OPEN_SQUARE_BRACKET = 91;
+final int $BACKSLASH = 92;
+final int $CLOSE_SQUARE_BRACKET = 93;
+final int $CARET = 94;
+final int $_ = 95;
+final int $BACKPING = 96;
+final int $a = 97;
+final int $b = 98;
+final int $c = 99;
+final int $d = 100;
+final int $e = 101;
+final int $f = 102;
+final int $g = 103;
+final int $h = 104;
+final int $i = 105;
+final int $j = 106;
+final int $k = 107;
+final int $l = 108;
+final int $m = 109;
+final int $n = 110;
+final int $o = 111;
+final int $p = 112;
+final int $q = 113;
+final int $r = 114;
+final int $s = 115;
+final int $t = 116;
+final int $u = 117;
+final int $v = 118;
+final int $w = 119;
+final int $x = 120;
+final int $y = 121;
+final int $z = 122;
+final int $OPEN_CURLY_BRACKET = 123;
+final int $BAR = 124;
+final int $CLOSE_CURLY_BRACKET = 125;
+final int $TILDE = 126;
+final int $DEL = 127;
+final int $NBSP = 160;
+final int $LS = 0x2028;
+final int $PS = 0x2029;
+
+final int $FIRST_SURROGATE = 0xd800;
+final int $LAST_SURROGATE = 0xdfff;
+final int $LAST_CODE_POINT = 0x10ffff;
+
+bool isHexDigit(int characterCode) {
+  if (characterCode <= $9) return $0 <= characterCode;
+  characterCode |= $a ^ $A;
+  return ($a <= characterCode && characterCode <= $f);
+}
+
+int hexDigitValue(int hexDigit) {
+  assert(isHexDigit(hexDigit));
+  // hexDigit is one of '0'..'9', 'A'..'F' and 'a'..'f'.
+  if (hexDigit <= $9) return hexDigit - $0;
+  return (hexDigit | ($a ^ $A)) - ($a - 10);
+}
+
+bool isUnicodeScalarValue(int value) {
+  return value < $FIRST_SURROGATE ||
+      (value > $LAST_SURROGATE && value <= $LAST_CODE_POINT);
+}
diff --git a/lib/compiler/implementation/util/link.dart b/lib/compiler/implementation/util/link.dart
new file mode 100644
index 0000000..cf97bd9
--- /dev/null
+++ b/lib/compiler/implementation/util/link.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2011, 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.
+
+interface Link<T> extends Iterable<T> default LinkFactory<T> {
+  final T head;
+  final Link<T> tail;
+
+  Link(T head, [Link<T> tail]);
+  Link.fromList(List<T> list);
+
+  Link<T> prepend(T element);
+  List<T> toList();
+  bool isEmpty();
+  Link<T> reverse();
+  Link<T> reversePrependAll(Link<T> from);
+
+  void printOn(StringBuffer buffer, [separatedBy]);
+
+  void forEach(void f(T element));
+}
+
+interface EmptyLink<T> extends Link<T> default LinkTail<T> {
+  const EmptyLink();
+}
+
+interface LinkBuilder<T> default LinkBuilderImplementation<T> {
+  LinkBuilder();
+
+  Link<T> toLink();
+  void addLast(T t);
+
+  final int length;
+}
diff --git a/lib/compiler/implementation/util/link_implementation.dart b/lib/compiler/implementation/util/link_implementation.dart
new file mode 100644
index 0000000..75a4b9e
--- /dev/null
+++ b/lib/compiler/implementation/util/link_implementation.dart
@@ -0,0 +1,165 @@
+// Copyright (c) 2011, 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.
+
+class LinkIterator<T> implements Iterator<T> {
+  Link<T> current;
+  LinkIterator(Link<T> this.current);
+  bool hasNext() => !current.isEmpty();
+  T next() {
+    T result = current.head;
+    current = current.tail;
+    return result;
+  }
+}
+
+class LinkFactory<T> {
+  factory Link(T head, [Link<T> tail]) {
+    if (tail === null) {
+      tail = new LinkTail<T>();
+    }
+    return new LinkEntry<T>(head, tail);
+  }
+
+  factory Link.fromList(List<T> list) {
+    switch (list.length) {
+      case 0:
+        return new LinkTail<T>();
+      case 1:
+        return new Link<T>(list[0]);
+      case 2:
+        return new Link<T>(list[0], new Link<T>(list[1]));
+      case 3:
+        return new Link<T>(list[0], new Link<T>(list[1], new Link<T>(list[2])));
+    }
+    Link link = new Link<T>(list.last());
+    for (int i = list.length - 1; i > 0; i--) {
+      link = link.prepend(list[i - 1]);
+    }
+    return link;
+  }
+}
+
+class LinkTail<T> implements EmptyLink<T> {
+  T get head() => null;
+  Link<T> get tail() => null;
+
+  const LinkTail();
+
+  Link<T> prepend(T element) {
+    // TODO(ahe): Use new Link<T>, but this cost 8% performance on VM.
+    return new LinkEntry<T>(element, this);
+  }
+
+  Iterator<T> iterator() => new LinkIterator<T>(this);
+
+  void printOn(StringBuffer buffer, [separatedBy]) {
+  }
+
+  String toString() => "[]";
+
+  Link<T> reverse() => this;
+
+  Link<T> reversePrependAll(Link<T> from) {
+    if (from.isEmpty()) return this;
+    return from.head.reversePrependAll(from.tail);
+  }
+
+  List toList() => const [];
+
+  bool isEmpty() => true;
+
+  void forEach(void f(T element)) {}
+}
+
+class LinkEntry<T> implements Link<T> {
+  final T head;
+  Link<T> tail;
+
+  LinkEntry(T this.head, Link<T> this.tail);
+
+  Link<T> prepend(T element) {
+    // TODO(ahe): Use new Link<T>, but this cost 8% performance on VM.
+    return new LinkEntry<T>(element, this);
+  }
+
+  Iterator<T> iterator() => new LinkIterator<T>(this);
+
+  void printOn(StringBuffer buffer, [separatedBy]) {
+    buffer.add(head);
+    if (separatedBy === null) separatedBy = '';
+    for (Link link = tail; !link.isEmpty(); link = link.tail) {
+      buffer.add(separatedBy);
+      buffer.add(link.head);
+    }
+  }
+
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.add('[ ');
+    printOn(buffer, ', ');
+    buffer.add(' ]');
+    return buffer.toString();
+  }
+
+  Link<T> reverse() {
+    Link<T> result = const LinkTail();
+    for (Link<T> link = this; !link.isEmpty(); link = link.tail) {
+      result = result.prepend(link.head);
+    }
+    return result;
+  }
+
+  Link<T> reversePrependAll(Link<T> from) {
+    Link<T> result;
+    for (result = this; !from.isEmpty(); from = from.tail) {
+      result = result.prepend(from.head);
+    }
+    return result;
+  }
+
+
+  bool isEmpty() => false;
+
+  List<T> toList() {
+    List<T> list = new List<T>();
+    for (Link<T> link = this; !link.isEmpty(); link = link.tail) {
+      list.addLast(link.head);
+    }
+    return list;
+  }
+
+  void forEach(void f(T element)) {
+    for (Link<T> link = this; !link.isEmpty(); link = link.tail) {
+      f(link.head);
+    }
+  }
+}
+
+class LinkBuilderImplementation<T> implements LinkBuilder<T> {
+  LinkEntry<T> head = null;
+  LinkEntry<T> lastLink = null;
+  int length = 0;
+
+  LinkBuilderImplementation();
+
+  Link<T> toLink() {
+    if (head === null) return const LinkTail();
+    lastLink.tail = const LinkTail();
+    Link<T> link = head;
+    lastLink = null;
+    head = null;
+    return link;
+  }
+
+  void addLast(T t) {
+    length++;
+    LinkEntry<T> entry = new LinkEntry<T>(t, null);
+    if (head === null) {
+      head = entry;
+    } else {
+      lastLink.tail = entry;
+    }
+    lastLink = entry;
+  }
+}
diff --git a/lib/compiler/implementation/util/util.dart b/lib/compiler/implementation/util/util.dart
new file mode 100644
index 0000000..afb058d
--- /dev/null
+++ b/lib/compiler/implementation/util/util.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2011, 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('util');
+#import('util_implementation.dart');
+#source('link.dart');
diff --git a/lib/compiler/implementation/util/util_implementation.dart b/lib/compiler/implementation/util/util_implementation.dart
new file mode 100644
index 0000000..ed4228a
--- /dev/null
+++ b/lib/compiler/implementation/util/util_implementation.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2011, 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('util_implementation');
+#import('util.dart');
+#source('link_implementation.dart');
diff --git a/lib/compiler/implementation/warnings.dart b/lib/compiler/implementation/warnings.dart
new file mode 100644
index 0000000..099f146
--- /dev/null
+++ b/lib/compiler/implementation/warnings.dart
@@ -0,0 +1,230 @@
+// Copyright (c) 2011, 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.
+
+class MessageKind {
+  final String template;
+  const MessageKind(this.template);
+
+  static final GENERIC = const MessageKind('#{1}');
+
+  static final NOT_ASSIGNABLE = const MessageKind(
+      '#{2} is not assignable to #{1}');
+  static final VOID_EXPRESSION = const MessageKind(
+      'expression does not yield a value');
+  static final VOID_VARIABLE = const MessageKind(
+      'variable cannot be of type void');
+  static final RETURN_VALUE_IN_VOID = const MessageKind(
+      'cannot return value from void function');
+  static final RETURN_NOTHING = const MessageKind(
+      'value of type #{1} expected');
+  static final MISSING_ARGUMENT = const MessageKind(
+      'missing argument of type #{1}');
+  static final ADDITIONAL_ARGUMENT = const MessageKind(
+      'additional argument');
+  static final METHOD_NOT_FOUND = const MessageKind(
+      'no method named #{2} in class #{1}');
+  static final MEMBER_NOT_STATIC = const MessageKind(
+      '#{1}.#{2} is not static');
+  static final NO_INSTANCE_AVAILABLE = const MessageKind(
+      '#{1} is only available in instance methods');
+
+  static final UNREACHABLE_CODE = const MessageKind(
+      'unreachable code');
+  static final MISSING_RETURN = const MessageKind(
+      'missing return');
+  static final MAYBE_MISSING_RETURN = const MessageKind(
+      'not all paths lead to a return or throw statement');
+
+  static final CANNOT_RESOLVE = const MessageKind(
+      'cannot resolve #{1}');
+  static final CANNOT_RESOLVE_CONSTRUCTOR = const MessageKind(
+      'cannot resolve constructor #{1}');
+  static final CANNOT_RESOLVE_TYPE = const MessageKind(
+      'cannot resolve type #{1}');
+  static final DUPLICATE_DEFINITION = const MessageKind(
+      'duplicate definition of #{1}');
+  static final NOT_A_TYPE = const MessageKind(
+      '#{1} is not a type');
+  static final NOT_A_PREFIX = const MessageKind(
+      '#{1} is not a prefix');
+  static final NO_SUPER_IN_OBJECT = const MessageKind(
+      "'Object' does not have a superclass");
+  static final CANNOT_FIND_CONSTRUCTOR = const MessageKind(
+      'cannot find constructor #{1}');
+  static final CANNOT_FIND_CONSTRUCTOR2 = const MessageKind(
+      'cannot find constructor #{1} or #{2}');
+  static final CYCLIC_CLASS_HIERARCHY = const MessageKind(
+      '#{1} creates a cycle in the class hierarchy');
+  static final INVALID_RECEIVER_IN_INITIALIZER = const MessageKind(
+      'field initializer expected');
+  static final NO_SUPER_IN_STATIC = const MessageKind(
+      "'super' is only available in instance methods");
+  static final DUPLICATE_INITIALIZER = const MessageKind(
+      'field #{1} is initialized more than once');
+  static final ALREADY_INITIALIZED = const MessageKind(
+      '#{1} was already initialized here');
+  static final INIT_STATIC_FIELD = const MessageKind(
+      'cannot initialize static field #{1}');
+  static final NOT_A_FIELD = const MessageKind(
+      '#{1} is not a field');
+  static final CONSTRUCTOR_CALL_EXPECTED = const MessageKind(
+      "only call to 'this' or 'super' constructor allowed");
+  static final INVALID_FOR_IN = const MessageKind(
+      'invalid for-in variable declaration.');
+  static final INVALID_INITIALIZER = const MessageKind(
+      'invalid initializer');
+  static final FUNCTION_WITH_INITIALIZER = const MessageKind(
+      'only constructors can have initializers');
+  static final REDIRECTING_CONSTRUCTOR_CYCLE = const MessageKind(
+      'cyclic constructor redirection');
+  static final REDIRECTING_CONSTRUCTOR_HAS_BODY = const MessageKind(
+      'redirecting constructor cannot have a body');
+  static final REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER = const MessageKind(
+      'redirecting constructor cannot have other initializers');
+  static final SUPER_INITIALIZER_IN_OBJECT = const MessageKind(
+      "'Object' cannot have a super initializer");
+  static final DUPLICATE_SUPER_INITIALIZER = const MessageKind(
+      'cannot have more than one super initializer');
+  static final NO_MATCHING_CONSTRUCTOR = const MessageKind(
+      'no matching constructor found');
+  static final NO_CONSTRUCTOR = const MessageKind(
+      '#{1} is a #{2}, not a constructor');
+  static final FIELD_PARAMETER_NOT_ALLOWED = const MessageKind(
+      'a field parameter is only allowed in generative constructors');
+  static final INVALID_PARAMETER = const MessageKind(
+      "cannot resolve parameter");
+  static final NOT_INSTANCE_FIELD = const MessageKind(
+      '#{1} is not an instance field');
+  static final NO_CATCH_NOR_FINALLY = const MessageKind(
+      "expected 'catch' or 'finally'");
+  static final EMPTY_CATCH_DECLARATION = const MessageKind(
+      'expected a variable in catch declaration');
+  static final EXTRA_CATCH_DECLARATION = const MessageKind(
+      'extra variable in catch declaration');
+  static final UNBOUND_LABEL = const MessageKind(
+      'cannot resolve label #{1}');
+  static final NO_BREAK_TARGET = const MessageKind(
+      'break statement not inside switch or loop');
+  static final NO_CONTINUE_TARGET = const MessageKind(
+      'continue statement not inside loop');
+  static final EXISTING_LABEL = const MessageKind(
+      'original declaration of duplicate label #{1}');
+  static final DUPLICATE_LABEL = const MessageKind(
+      'duplicate declaration of label #{1}');
+  static final UNUSED_LABEL = const MessageKind(
+      'unused label #{1}');
+  static final INVALID_CONTINUE = const MessageKind(
+      'target of continue is not a loop or switch case');
+  static final TYPE_VARIABLE_AS_CONSTRUCTOR = const MessageKind(
+      'cannot use type variable as constructor');
+  static final INVALID_BREAK = const MessageKind(
+      'target of break is not a statement');
+  static final INVALID_USE_OF_SUPER = const MessageKind(
+      'super not allowed here');
+  static final INVALID_CASE_DEFAULT = const MessageKind(
+      'default only allowed on last case of a switch');
+  static final INVALID_ARGUMENT_AFTER_NAMED = const MessageKind(
+      'non-named argument after named argument');
+
+  static final NOT_A_COMPILE_TIME_CONSTANT = const MessageKind(
+      'not a compile-time constant');
+  static final CYCLIC_COMPILE_TIME_CONSTANTS = const MessageKind(
+      'cycle in the compile-time constant computation');
+
+  static final KEY_NOT_A_STRING_LITERAL = const MessageKind(
+      'map-literal key not a string literal');
+
+  static final NO_SUCH_LIBRARY_MEMBER = const MessageKind(
+      '#{1} has no member named #{2}');
+
+  static final CANNOT_INSTANTIATE_INTERFACE = const MessageKind(
+      "cannot instantiate interface '#{1}'");
+
+  static final CANNOT_INSTANTIATE_TYPEDEF = const MessageKind(
+      "cannot instantiate typedef '#{1}'");
+
+  static final NO_DEFAULT_CLASS = const MessageKind(
+      "no default class on enclosing interface '#{1}'");
+
+  static final CYCLIC_TYPE_VARIABLE = const MessageKind(
+      "cyclic reference to type variable #{1}");
+  static final TYPE_NAME_EXPECTED = const MessageKind(
+      "class or interface name exptected");
+
+  static final CANNOT_EXTEND = const MessageKind(
+      "#{1} cannot be extended");
+
+  static final CANNOT_IMPLEMENT = const MessageKind(
+      "#{1} cannot be implemented");
+
+  static final ILLEGAL_SUPER_SEND = const MessageKind(
+      "#{1} cannot be called on super");
+
+  toString() => template;
+}
+
+class Message {
+  final kind;
+  final List arguments;
+  String message;
+
+  Message(this.kind, this.arguments);
+
+  String toString() {
+    if (message === null) {
+      message = kind.template;
+      int position = 1;
+      for (var argument in arguments) {
+        String string = slowToString(argument);
+        message = message.replaceAll('#{${position++}}', string);
+      }
+    }
+    return message;
+  }
+
+  bool operator==(other) {
+    if (other is !Message) return false;
+    return (kind == other.kind) && (toString() == other.toString());
+  }
+
+  String slowToString(object) {
+    if (object is SourceString) {
+      return object.slowToString();
+    } else {
+      return object.toString();
+    }
+  }
+}
+
+class TypeWarning {
+  final Message message;
+  TypeWarning.message(this.message);
+  TypeWarning(MessageKind kind, List<Type> arguments)
+    : message = new Message(kind, arguments);
+  String toString() => message.toString();
+}
+
+class ResolutionError {
+  final Message message;
+  ResolutionError.message(this.message);
+  ResolutionError(MessageKind kind, List<Type> arguments)
+    : message = new Message(kind, arguments);
+  String toString() => message.toString();
+}
+
+class ResolutionWarning {
+  final Message message;
+  ResolutionWarning.message(this.message);
+  ResolutionWarning(MessageKind kind, List<Type> arguments)
+    : message = new Message(kind, arguments);
+  String toString() => message.toString();
+}
+
+class CompileTimeConstantError {
+  final Message message;
+  CompileTimeConstantError.message(this.message);
+  CompileTimeConstantError(MessageKind kind, List<Type> arguments)
+    : message = new Message(kind, arguments);
+  String toString() => message.toString();
+}
diff --git a/tests/utils/src/DummyCompilerTest.dart b/tests/utils/src/DummyCompilerTest.dart
index 9ffe116..838ffa7 100644
--- a/tests/utils/src/DummyCompilerTest.dart
+++ b/tests/utils/src/DummyCompilerTest.dart
@@ -4,7 +4,7 @@
 
 // Smoke test of the dart2js compiler API.
 
-#import('../../../frog/leg/api.dart');
+#import('../../../lib/compiler/compiler.dart');
 #import('../../../lib/uri/uri.dart');
 
 Future<String> provider(Uri uri) {
diff --git a/utils/compiler/build_helper.dart b/utils/compiler/build_helper.dart
index 9d5331b..1b49b3b 100644
--- a/utils/compiler/build_helper.dart
+++ b/utils/compiler/build_helper.dart
@@ -38,13 +38,13 @@
 
 #import('dart:io');
 
-#import('${uri.resolve('../../frog/leg/dart2js.dart').path}');
+#import('${uri.resolve('../../lib/compiler/implementation/dart2js.dart').path}');
 
 class Helper {
   void run() {
     try {
       List<String> argv =
-        ['--library-root=${uri.resolve('../../frog/leg/lib').path}'];
+        ['--library-root=${uri.resolve('../../lib/compiler/implementation/lib').path}'];
       argv.addAll(new Options().arguments);
       compile(argv);
     } catch (var exception, var trace) {