Version 1.8.0-dev.4.3

svn merge -c 41685 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 41709 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 41796 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 41822 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@41833 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index 734bba3..9b923bb 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -80,6 +80,8 @@
                 forceIncrementalSupport ||
                 hasOption(options, '--incremental-support'),
             suppressWarnings: hasOption(options, '--suppress-warnings'),
+            enableExperimentalMirrors:
+                hasOption(options, '--enable-experimental-mirrors'),
             enableAsyncAwait: hasOption(options, '--enable-async'),
             enableEnums: hasOption(options, '--enable-enum')) {
     tasks.addAll([
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index c60a44c..74e408f 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -428,7 +428,7 @@
   /// This method is called when all new libraries loaded through
   /// [LibraryLoader.loadLibrary] has been loaded and their imports/exports
   /// have been computed.
-  Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) {
+  Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
     return new Future.value();
   }
 
@@ -657,6 +657,7 @@
   final bool disableTypeInferenceFlag;
   final bool dumpInfo;
   final bool useContentSecurityPolicy;
+  final bool enableExperimentalMirrors;
 
   /**
    * The maximum size of a concrete type before it widens to dynamic during
@@ -969,6 +970,7 @@
             this.useContentSecurityPolicy: false,
             this.suppressWarnings: false,
             bool hasIncrementalSupport: false,
+            this.enableExperimentalMirrors: false,
             this.enableAsyncAwait: false,
             this.enableEnums: false,
             api.CompilerOutputProvider outputProvider,
@@ -1223,9 +1225,67 @@
   ///
   /// The method returns a [Future] allowing for the loading of additional
   /// libraries.
-  Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) {
+  Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
     return new Future.sync(() {
-      if (!loadedLibraries.containsKey(DART_CORE)) return new Future.value();
+      if (!loadedLibraries.containsLibrary(DART_CORE)) {
+        return null;
+      }
+      if (!enableExperimentalMirrors &&
+          loadedLibraries.containsLibrary(DART_MIRRORS)) {
+        // TODO(johnniwinther): Move computation of dependencies to the library
+        // loader.
+        Uri rootUri = loadedLibraries.rootUri;
+        Set<String> importChains = new Set<String>();
+        // The maximum number of full imports chains to process.
+        final int chainLimit = 10000;
+        // The maximum number of imports chains to show.
+        final int compactChainLimit = verbose ? 20 : 10;
+        int chainCount = 0;
+        bool limitExceeded = false;
+        loadedLibraries.forEachImportChain(DART_MIRRORS,
+            callback: (Link<Uri> importChainReversed) {
+          Link<CodeLocation> compactImportChain = const Link<CodeLocation>();
+          CodeLocation currentCodeLocation =
+              new UriLocation(importChainReversed.head);
+          compactImportChain = compactImportChain.prepend(currentCodeLocation);
+          for (Link<Uri> link = importChainReversed.tail;
+               !link.isEmpty;
+               link = link.tail) {
+            Uri uri = link.head;
+            if (!currentCodeLocation.inSameLocation(uri)) {
+              currentCodeLocation =
+                  verbose ? new UriLocation(uri) : new CodeLocation(uri);
+              compactImportChain =
+                  compactImportChain.prepend(currentCodeLocation);
+            }
+          }
+          String importChain =
+              compactImportChain.map((CodeLocation codeLocation) {
+                return codeLocation.relativize(rootUri);
+              }).join(' => ');
+
+          if (!importChains.contains(importChain)) {
+            if (importChains.length > compactChainLimit) {
+              importChains.add('...');
+              return false;
+            } else {
+              importChains.add(importChain);
+            }
+          }
+
+          chainCount++;
+          if (chainCount > chainLimit) {
+            // Assume there are more import chains.
+            importChains.add('...');
+            return false;
+          }
+          return true;
+        });
+        reportWarning(NO_LOCATION_SPANNABLE,
+            MessageKind.IMPORT_EXPERIMENTAL_MIRRORS,
+            {'importChain': importChains.join(
+                 MessageKind.IMPORT_EXPERIMENTAL_MIRRORS_PADDING)});
+      }
 
       functionClass.ensureResolved(this);
       functionApplyMethod = functionClass.lookupLocalMember('apply');
@@ -1235,8 +1295,8 @@
               coreLibrary.find('proxy')).value;
 
       // TODO(johnniwinther): Move this to the JavaScript backend.
-      LibraryElement jsHelperLibrary =
-          loadedLibraries[js_backend.JavaScriptBackend.DART_JS_HELPER];
+      LibraryElement jsHelperLibrary = loadedLibraries.getLibrary(
+              js_backend.JavaScriptBackend.DART_JS_HELPER);
       if (jsHelperLibrary != null) {
         patchConstant = resolver.constantCompiler.compileConstant(
             jsHelperLibrary.find('patch')).value;
@@ -1963,40 +2023,29 @@
   /// If [assumeInUserCode] is `true`, [element] is assumed to be in user code
   /// if no entrypoints have been set.
   bool inUserCode(Element element, {bool assumeInUserCode: false}) {
-    List<Uri> entrypoints = <Uri>[];
+    if (element == null) return false;
+    Iterable<CodeLocation> userCodeLocations =
+        computeUserCodeLocations(assumeInUserCode: assumeInUserCode);
+    Uri libraryUri = element.library.canonicalUri;
+    return userCodeLocations.any(
+        (CodeLocation codeLocation) => codeLocation.inSameLocation(libraryUri));
+  }
+
+  Iterable<CodeLocation> computeUserCodeLocations(
+      {bool assumeInUserCode: false}) {
+    List<CodeLocation> userCodeLocations = <CodeLocation>[];
     if (mainApp != null) {
-      entrypoints.add(mainApp.canonicalUri);
+      userCodeLocations.add(new CodeLocation(mainApp.canonicalUri));
     }
     if (librariesToAnalyzeWhenRun != null) {
-      entrypoints.addAll(librariesToAnalyzeWhenRun);
+      userCodeLocations.addAll(librariesToAnalyzeWhenRun.map(
+          (Uri uri) => new CodeLocation(uri)));
     }
-    if (entrypoints.isEmpty && assumeInUserCode) {
+    if (userCodeLocations.isEmpty && assumeInUserCode) {
       // Assume in user code since [mainApp] has not been set yet.
-      return true;
+      userCodeLocations.add(const AnyLocation());
     }
-    if (element == null) return false;
-    Uri libraryUri = element.library.canonicalUri;
-    if (libraryUri.scheme == 'package') {
-      for (Uri uri in entrypoints) {
-        if (uri.scheme != 'package') continue;
-        int slashPos = libraryUri.path.indexOf('/');
-        if (slashPos != -1) {
-          String packageName = libraryUri.path.substring(0, slashPos + 1);
-          if (uri.path.startsWith(packageName)) {
-            return true;
-          }
-        } else {
-          if (libraryUri.path == uri.path) {
-            return true;
-          }
-        }
-      }
-    } else {
-      for (Uri uri in entrypoints) {
-        if (libraryUri.scheme == uri.scheme) return true;
-      }
-    }
-    return false;
+    return userCodeLocations;
   }
 
   /// Return a canonical URI for the source of [element].
@@ -2188,3 +2237,86 @@
   GenericTask(this.name, Compiler compiler)
       : super(compiler);
 }
+
+/// [CodeLocation] divides uris into different classes.
+///
+/// These are used to group uris from user code, platform libraries and
+/// packages.
+abstract class CodeLocation {
+  /// Returns `true` if [uri] is in this code location.
+  bool inSameLocation(Uri uri);
+
+  /// Returns the uri of this location relative to [baseUri].
+  String relativize(Uri baseUri);
+
+  factory CodeLocation(Uri uri) {
+    if (uri.scheme == 'package') {
+      int slashPos = uri.path.indexOf('/');
+      if (slashPos != -1) {
+        String packageName = uri.path.substring(0, slashPos);
+        return new PackageLocation(packageName);
+      } else {
+        return new UriLocation(uri);
+      }
+    } else {
+      return new SchemeLocation(uri);
+    }
+  }
+}
+
+/// A code location defined by the scheme of the uri.
+///
+/// Used for non-package uris, such as 'dart', 'file', and 'http'.
+class SchemeLocation implements CodeLocation {
+  final Uri uri;
+
+  SchemeLocation(this.uri);
+
+  bool inSameLocation(Uri uri) {
+    return this.uri.scheme == uri.scheme;
+  }
+
+  String relativize(Uri baseUri) {
+    return uri_extras.relativize(baseUri, uri, false);
+  }
+}
+
+/// A code location defined by the package name.
+///
+/// Used for package uris, separated by their `package names`, that is, the
+/// 'foo' of 'package:foo/bar.dart'.
+class PackageLocation implements CodeLocation {
+  final String packageName;
+
+  PackageLocation(this.packageName);
+
+  bool inSameLocation(Uri uri) {
+    return uri.scheme == 'package' && uri.path.startsWith('$packageName/');
+  }
+
+  String relativize(Uri baseUri) => 'package:$packageName';
+}
+
+/// A code location defined by the whole uri.
+///
+/// Used for package uris with no package name. For instance 'package:foo.dart'.
+class UriLocation implements CodeLocation {
+  final Uri uri;
+
+  UriLocation(this.uri);
+
+  bool inSameLocation(Uri uri) => this.uri == uri;
+
+  String relativize(Uri baseUri) {
+    return uri_extras.relativize(baseUri, uri, false);
+  }
+}
+
+/// A code location that contains any uri.
+class AnyLocation implements CodeLocation {
+  const AnyLocation();
+
+  bool inSameLocation(Uri uri) => true;
+
+  String relativize(Uri baseUri) => '$baseUri';
+}
diff --git a/pkg/compiler/lib/src/constants/expressions.dart b/pkg/compiler/lib/src/constants/expressions.dart
index ab55c45..ac763bb 100644
--- a/pkg/compiler/lib/src/constants/expressions.dart
+++ b/pkg/compiler/lib/src/constants/expressions.dart
@@ -30,7 +30,7 @@
   // and back-end.
   int get precedence => 16;
 
-  accept(ConstantExpressionVisitor visitor);
+  accept(ConstantExpressionVisitor visitor, [context]);
 
   String getText() {
     ConstExpPrinter printer = new ConstExpPrinter();
@@ -53,7 +53,9 @@
     assert(value != null);
   }
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitPrimitive(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitPrimitive(this, context);
+  }
 }
 
 /// Literal list constant.
@@ -64,7 +66,9 @@
 
   ListConstantExpression(this.value, this.type, this.values);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitList(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitList(this, context);
+  }
 }
 
 /// Literal map constant.
@@ -76,7 +80,9 @@
 
   MapConstantExpression(this.value, this.type, this.keys, this.values);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitMap(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitMap(this, context);
+  }
 }
 
 /// Invocation of a const constructor.
@@ -95,7 +101,9 @@
     assert(type.element == target.enclosingClass);
   }
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitConstructor(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitConstructed(this, context);
+  }
 }
 
 /// String literal with juxtaposition and/or interpolations.
@@ -106,7 +114,9 @@
 
   ConcatenateConstantExpression(this.value, this.arguments);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitConcatenate(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitConcatenate(this, context);
+  }
 }
 
 /// Symbol literal.
@@ -116,7 +126,9 @@
 
   SymbolConstantExpression(this.value, this.name);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitSymbol(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitSymbol(this, context);
+  }
 }
 
 /// Type literal.
@@ -129,7 +141,9 @@
     assert(type is GenericType || type is DynamicType);
   }
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitType(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitType(this, context);
+  }
 }
 
 /// Reference to a constant local, top-level, or static variable.
@@ -139,7 +153,9 @@
 
   VariableConstantExpression(this.value, this.element);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitVariable(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitVariable(this, context);
+  }
 }
 
 /// Reference to a top-level or static function.
@@ -149,7 +165,9 @@
 
   FunctionConstantExpression(this.value, this.element);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitFunction(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitFunction(this, context);
+  }
 }
 
 /// A constant binary expression like `a * b` or `identical(a, b)`.
@@ -163,7 +181,9 @@
     assert(PRECEDENCE_MAP[operator] != null);
   }
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitBinary(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitBinary(this, context);
+  }
 
   int get precedence => PRECEDENCE_MAP[operator];
 
@@ -201,7 +221,9 @@
     assert(PRECEDENCE_MAP[operator] != null);
   }
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitUnary(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitUnary(this, context);
+  }
 
   int get precedence => PRECEDENCE_MAP[operator];
 
@@ -224,26 +246,32 @@
                                 this.trueExp,
                                 this.falseExp);
 
-  accept(ConstantExpressionVisitor visitor) => visitor.visitConditional(this);
+  accept(ConstantExpressionVisitor visitor, [context]) {
+    return visitor.visitConditional(this, context);
+  }
 
   int get precedence => 3;
 }
 
-abstract class ConstantExpressionVisitor<T> {
-  T visit(ConstantExpression constant) => constant.accept(this);
+abstract class ConstantExpressionVisitor<C, R> {
+  const ConstantExpressionVisitor();
 
-  T visitPrimitive(PrimitiveConstantExpression exp);
-  T visitList(ListConstantExpression exp);
-  T visitMap(MapConstantExpression exp);
-  T visitConstructor(ConstructedConstantExpresssion exp);
-  T visitConcatenate(ConcatenateConstantExpression exp);
-  T visitSymbol(SymbolConstantExpression exp);
-  T visitType(TypeConstantExpression exp);
-  T visitVariable(VariableConstantExpression exp);
-  T visitFunction(FunctionConstantExpression exp);
-  T visitBinary(BinaryConstantExpression exp);
-  T visitUnary(UnaryConstantExpression exp);
-  T visitConditional(ConditionalConstantExpression exp);
+  R visit(ConstantExpression constant, [C context]) {
+    return constant.accept(this, context);
+  }
+
+  R visitPrimitive(PrimitiveConstantExpression exp, [C context]);
+  R visitList(ListConstantExpression exp, [C context]);
+  R visitMap(MapConstantExpression exp, [C context]);
+  R visitConstructed(ConstructedConstantExpresssion exp, [C context]);
+  R visitConcatenate(ConcatenateConstantExpression exp, [C context]);
+  R visitSymbol(SymbolConstantExpression exp, [C context]);
+  R visitType(TypeConstantExpression exp, [C context]);
+  R visitVariable(VariableConstantExpression exp, [C context]);
+  R visitFunction(FunctionConstantExpression exp, [C context]);
+  R visitBinary(BinaryConstantExpression exp, [C context]);
+  R visitUnary(UnaryConstantExpression exp, [C context]);
+  R visitConditional(ConditionalConstantExpression exp, [C context]);
 }
 
 /// Represents the declaration of a constant [element] with value [expression].
@@ -285,11 +313,11 @@
     sb.write('>');
   }
 
-  visitPrimitive(PrimitiveConstantExpression exp) {
+  visitPrimitive(PrimitiveConstantExpression exp, [_]) {
     sb.write(exp.value.unparse());
   }
 
-  visitList(ListConstantExpression exp) {
+  visitList(ListConstantExpression exp, [_]) {
     sb.write('const ');
     writeTypeArguments(exp.type);
     sb.write('[');
@@ -304,7 +332,7 @@
     sb.write(']');
   }
 
-  visitMap(MapConstantExpression exp) {
+  visitMap(MapConstantExpression exp, [_]) {
     sb.write('const ');
     writeTypeArguments(exp.type);
     sb.write('{');
@@ -319,7 +347,7 @@
     sb.write('}');
   }
 
-  visitConstructor(ConstructedConstantExpresssion exp) {
+  visitConstructed(ConstructedConstantExpresssion exp, [_]) {
     sb.write('const ');
     sb.write(exp.target.enclosingClass.name);
     if (exp.target.name != '') {
@@ -350,20 +378,20 @@
     sb.write(')');
   }
 
-  visitConcatenate(ConcatenateConstantExpression exp) {
+  visitConcatenate(ConcatenateConstantExpression exp, [_]) {
     sb.write(exp.value.unparse());
   }
 
-  visitSymbol(SymbolConstantExpression exp) {
+  visitSymbol(SymbolConstantExpression exp, [_]) {
     sb.write('#');
     sb.write(exp.name);
   }
 
-  visitType(TypeConstantExpression exp) {
+  visitType(TypeConstantExpression exp, [_]) {
     sb.write(exp.type.name);
   }
 
-  visitVariable(VariableConstantExpression exp) {
+  visitVariable(VariableConstantExpression exp, [_]) {
     if (exp.element.isStatic) {
       sb.write(exp.element.enclosingClass.name);
       sb.write('.');
@@ -371,7 +399,7 @@
     sb.write(exp.element.name);
   }
 
-  visitFunction(FunctionConstantExpression exp) {
+  visitFunction(FunctionConstantExpression exp, [_]) {
     if (exp.element.isStatic) {
       sb.write(exp.element.enclosingClass.name);
       sb.write('.');
@@ -379,7 +407,7 @@
     sb.write(exp.element.name);
   }
 
-  visitBinary(BinaryConstantExpression exp) {
+  visitBinary(BinaryConstantExpression exp, [_]) {
     if (exp.operator == 'identical') {
       sb.write('identical(');
       visit(exp.left);
@@ -395,12 +423,12 @@
     }
   }
 
-  visitUnary(UnaryConstantExpression exp) {
+  visitUnary(UnaryConstantExpression exp, [_]) {
     sb.write(exp.operator);
     write(exp, exp.expression);
   }
 
-  visitConditional(ConditionalConstantExpression exp) {
+  visitConditional(ConditionalConstantExpression exp, [_]) {
     write(exp, exp.condition, leftAssociative: false);
     sb.write(' ? ');
     write(exp, exp.trueExp);
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index af0fad3..324e33a 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -332,6 +332,7 @@
                       (_) => hasDisallowUnsafeEval = true),
     new OptionHandler('--show-package-warnings', passThrough),
     new OptionHandler('--csp', passThrough),
+    new OptionHandler('--enable-experimental-mirrors', passThrough),
     new OptionHandler('--enable-async', setEnableAsync),
     new OptionHandler('--enable-enum', passThrough),
     new OptionHandler('-D.+=.*', addInEnvironment),
diff --git a/pkg/compiler/lib/src/dart2jslib.dart b/pkg/compiler/lib/src/dart2jslib.dart
index a59cefc..01cd054 100644
--- a/pkg/compiler/lib/src/dart2jslib.dart
+++ b/pkg/compiler/lib/src/dart2jslib.dart
@@ -34,7 +34,8 @@
 import 'js_backend/js_backend.dart' as js_backend;
 import 'library_loader.dart'
     show LibraryLoader,
-         LibraryLoaderTask;
+         LibraryLoaderTask,
+         LoadedLibraries;
 import 'mirrors_used.dart' show MirrorUsageAnalyzerTask;
 import 'native/native.dart' as native;
 import 'ordered_typeset.dart';
@@ -49,6 +50,7 @@
 import 'types/types.dart' as ti;
 import 'universe/universe.dart';
 import 'util/characters.dart' show $_;
+import 'util/uri_extras.dart' as uri_extras show relativize;
 import 'util/util.dart';
 
 export 'helpers/helpers.dart';
diff --git a/pkg/compiler/lib/src/dart_backend/backend.dart b/pkg/compiler/lib/src/dart_backend/backend.dart
index 3479b49..174c58b 100644
--- a/pkg/compiler/lib/src/dart_backend/backend.dart
+++ b/pkg/compiler/lib/src/dart_backend/backend.dart
@@ -261,10 +261,10 @@
 
   log(String message) => compiler.log('[DartBackend] $message');
 
-  Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) {
+  Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
     // All platform classes must be resolved to ensure that their member names
     // are preserved.
-    loadedLibraries.values.forEach((LibraryElement library) {
+    loadedLibraries.forEachLibrary((LibraryElement library) {
       if (library.isPlatformLibrary) {
         library.forEachLocalMember((Element element) {
           if (element.isClass) {
@@ -275,10 +275,10 @@
       }
     });
     if (useMirrorHelperLibrary &&
-        loadedLibraries.containsKey(Compiler.DART_MIRRORS)) {
+        loadedLibraries.containsLibrary(Compiler.DART_MIRRORS)) {
       return compiler.libraryLoader.loadLibrary(
           compiler.translateResolvedUri(
-              loadedLibraries[Compiler.DART_MIRRORS],
+              loadedLibraries.getLibrary(Compiler.DART_MIRRORS),
               MirrorRenamerImpl.DART_MIRROR_HELPER, null)).
           then((LibraryElement library) {
         mirrorRenamer = new MirrorRenamerImpl(compiler, this, library);
diff --git a/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart b/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
index 7ceefa6..7ae2d22 100644
--- a/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
+++ b/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
@@ -708,7 +708,7 @@
   }
 }
 
-class ConstantEmitter extends ConstantExpressionVisitor<Expression> {
+class ConstantEmitter extends ConstantExpressionVisitor<Null, Expression> {
   ASTEmitter parent;
   ConstantEmitter(this.parent);
 
@@ -730,13 +730,13 @@
   }
 
   @override
-  Expression visitPrimitive(PrimitiveConstantExpression exp) {
+  Expression visitPrimitive(PrimitiveConstantExpression exp, [_]) {
     return handlePrimitiveConstant(exp.value);
   }
 
   /// Given a negative num constant, returns the corresponding positive
   /// literal wrapped by a unary minus operator.
-  Expression negatedLiteral(NumConstantValue constant) {
+  Expression negatedLiteral(NumConstantValue constant, [_]) {
     assert(constant.primitiveValue.isNegative);
     NumConstantValue positiveConstant;
     if (constant.isInt) {
@@ -750,7 +750,7 @@
   }
 
   @override
-  Expression visitList(ListConstantExpression exp) {
+  Expression visitList(ListConstantExpression exp, [_]) {
     return new LiteralList(
         exp.values.map(visit).toList(growable: false),
         isConst: true,
@@ -758,7 +758,7 @@
   }
 
   @override
-  Expression visitMap(MapConstantExpression exp) {
+  Expression visitMap(MapConstantExpression exp, [_]) {
     List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate(
         exp.values.length,
         (i) => new LiteralMapEntry(visit(exp.keys[i]),
@@ -770,7 +770,7 @@
   }
 
   @override
-  Expression visitConstructor(ConstructedConstantExpresssion exp) {
+  Expression visitConstructed(ConstructedConstantExpresssion exp, [_]) {
     int positionalArgumentCount = exp.selector.positionalArgumentCount;
     List<Argument> args = new List<Argument>.generate(
         positionalArgumentCount,
@@ -791,24 +791,24 @@
   }
 
   @override
-  Expression visitConcatenate(ConcatenateConstantExpression exp) {
+  Expression visitConcatenate(ConcatenateConstantExpression exp, [_]) {
     return new StringConcat(exp.arguments.map(visit).toList(growable: false));
   }
 
   @override
-  Expression visitSymbol(SymbolConstantExpression exp) {
+  Expression visitSymbol(SymbolConstantExpression exp, [_]) {
     return new LiteralSymbol(exp.name);
   }
 
   @override
-  Expression visitType(TypeConstantExpression exp) {
+  Expression visitType(TypeConstantExpression exp, [_]) {
     DartType type = exp.type;
     return new LiteralType(type.name)
                ..type = type;
   }
 
   @override
-  Expression visitVariable(VariableConstantExpression exp) {
+  Expression visitVariable(VariableConstantExpression exp, [_]) {
     Element element = exp.element;
     if (element.kind != ElementKind.VARIABLE) {
       return new Identifier(element.name)..element = element;
@@ -819,18 +819,18 @@
   }
 
   @override
-  Expression visitFunction(FunctionConstantExpression exp) {
+  Expression visitFunction(FunctionConstantExpression exp, [_]) {
     return new Identifier(exp.element.name)
                ..element = exp.element;
   }
 
   @override
-  Expression visitBinary(BinaryConstantExpression exp) {
+  Expression visitBinary(BinaryConstantExpression exp, [_]) {
     return handlePrimitiveConstant(exp.value);
   }
 
   @override
-  Expression visitConditional(ConditionalConstantExpression exp) {
+  Expression visitConditional(ConditionalConstantExpression exp, [_]) {
     if (exp.condition.value.isTrue) {
       return exp.trueExp.accept(this);
     } else {
@@ -839,7 +839,7 @@
   }
 
   @override
-  Expression visitUnary(UnaryConstantExpression exp) {
+  Expression visitUnary(UnaryConstantExpression exp, [_]) {
     return handlePrimitiveConstant(exp.value);
   }
 }
diff --git a/pkg/compiler/lib/src/dart_backend/dart_backend.dart b/pkg/compiler/lib/src/dart_backend/dart_backend.dart
index 36746b7..e44d1d5 100644
--- a/pkg/compiler/lib/src/dart_backend/dart_backend.dart
+++ b/pkg/compiler/lib/src/dart_backend/dart_backend.dart
@@ -8,6 +8,7 @@
 import 'dart:math' show max;
 import '../elements/elements.dart';
 import '../dart2jslib.dart';
+import '../library_loader.dart' show LoadedLibraries;
 import '../dart_types.dart';
 import '../tree/tree.dart';
 import '../cps_ir/cps_ir_nodes.dart' as cps_ir;
diff --git a/pkg/compiler/lib/src/elements/elements.dart b/pkg/compiler/lib/src/elements/elements.dart
index eabc991..f8b7844 100644
--- a/pkg/compiler/lib/src/elements/elements.dart
+++ b/pkg/compiler/lib/src/elements/elements.dart
@@ -1414,6 +1414,12 @@
   void addConstructor(FunctionElement constructor);
 }
 
+/// Enum declaration.
+abstract class EnumClassElement extends ClassElement {
+  /// The static fields implied by the enum values.
+  Iterable<FieldElement> get enumValues;
+}
+
 /// The label entity defined by a labeled statement.
 abstract class LabelDefinition extends Entity {
   Label get label;
diff --git a/pkg/compiler/lib/src/elements/modelx.dart b/pkg/compiler/lib/src/elements/modelx.dart
index fbffa06..e862b14 100644
--- a/pkg/compiler/lib/src/elements/modelx.dart
+++ b/pkg/compiler/lib/src/elements/modelx.dart
@@ -2414,8 +2414,9 @@
   }
 }
 
-class EnumClassElementX extends ClassElementX {
+class EnumClassElementX extends ClassElementX implements EnumClassElement {
   final Enum node;
+  Iterable<FieldElement> _enumValues;
 
   EnumClassElementX(String name, Element enclosing, int id, this.node)
       : super(name, enclosing, id, STATE_NOT_STARTED);
@@ -2433,9 +2434,21 @@
   Node parseNode(Compiler compiler) => node;
 
   @override
-  accept(ElementVisitor visitor) => visitor.visitClassElement(this);
+  accept(ElementVisitor visitor) => visitor.visitEnumClassElement(this);
 
   List<DartType> computeTypeParameters(Compiler compiler) => const <DartType>[];
+
+  Iterable<FieldElement> get enumValues {
+    assert(invariant(this, _enumValues != null,
+        message: "enumValues has not been computed for $this."));
+    return _enumValues;
+  }
+
+  void set enumValues(Iterable<FieldElement> values) {
+    assert(invariant(this, _enumValues == null,
+        message: "enumValues has already been computed for $this."));
+    _enumValues = values;
+  }
 }
 
 class EnumConstructorElementX extends ConstructorElementX {
diff --git a/pkg/compiler/lib/src/elements/visitor.dart b/pkg/compiler/lib/src/elements/visitor.dart
index 8d0323f..a1b2745 100644
--- a/pkg/compiler/lib/src/elements/visitor.dart
+++ b/pkg/compiler/lib/src/elements/visitor.dart
@@ -36,6 +36,7 @@
   R visitMixinApplicationElement(MixinApplicationElement e) {
     return visitClassElement(e);
   }
+  R visitEnumClassElement(EnumClassElement e) => visitClassElement(e);
   R visitTypeVariableElement(TypeVariableElement e) => visitElement(e);
   R visitBoxFieldElement(BoxFieldElement e) => visitElement(e);
   R visitClosureClassElement(ClosureClassElement e) => visitClassElement(e);
diff --git a/pkg/compiler/lib/src/helpers/helpers.dart b/pkg/compiler/lib/src/helpers/helpers.dart
index 097a222..bcfa12c 100644
--- a/pkg/compiler/lib/src/helpers/helpers.dart
+++ b/pkg/compiler/lib/src/helpers/helpers.dart
@@ -67,7 +67,7 @@
 }
 
 /// Implementation of [debugWrapPrint].
-DebugWrapPrint _debugWrapPrint(s, f()) {
+_debugWrapPrint(s, f()) {
   debugPrint('start:$s');
   var result = _indentation.indentBlock(f);
   debugPrint('end:$s');
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 0f01f9c..893445ba 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -1763,14 +1763,14 @@
     });
   }
 
-  Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) {
-    if (!loadedLibraries.containsKey(Compiler.DART_CORE)) {
+  Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
+    if (!loadedLibraries.containsLibrary(Compiler.DART_CORE)) {
       return new Future.value();
     }
 
-    assert(loadedLibraries.containsKey(Compiler.DART_CORE));
-    assert(loadedLibraries.containsKey(DART_INTERCEPTORS));
-    assert(loadedLibraries.containsKey(DART_JS_HELPER));
+    assert(loadedLibraries.containsLibrary(Compiler.DART_CORE));
+    assert(loadedLibraries.containsLibrary(DART_INTERCEPTORS));
+    assert(loadedLibraries.containsLibrary(DART_JS_HELPER));
 
     if (jsInvocationMirrorClass != null) {
       jsInvocationMirrorClass.ensureResolved(compiler);
diff --git a/pkg/compiler/lib/src/js_backend/js_backend.dart b/pkg/compiler/lib/src/js_backend/js_backend.dart
index 0f9f0ce..df03b80 100644
--- a/pkg/compiler/lib/src/js_backend/js_backend.dart
+++ b/pkg/compiler/lib/src/js_backend/js_backend.dart
@@ -19,7 +19,7 @@
 import '../js/js.dart' show js;
 import '../js_emitter/js_emitter.dart'
     show Emitter, CodeEmitterTask, ClassBuilder, MetadataEmitter;
-import '../library_loader.dart' show LibraryLoader;
+import '../library_loader.dart' show LibraryLoader, LoadedLibraries;
 import '../native/native.dart' as native;
 import '../ssa/ssa.dart';
 import '../tree/tree.dart';
diff --git a/pkg/compiler/lib/src/library_loader.dart b/pkg/compiler/lib/src/library_loader.dart
index ff7d59a..b2db0bb 100644
--- a/pkg/compiler/lib/src/library_loader.dart
+++ b/pkg/compiler/lib/src/library_loader.dart
@@ -249,6 +249,7 @@
  */
 class _LibraryLoaderTask extends CompilerTask implements LibraryLoaderTask {
   _LibraryLoaderTask(Compiler compiler) : super(compiler);
+
   String get name => 'LibraryLoader';
 
   final Map<Uri, LibraryElement> libraryCanonicalUriMap =
@@ -342,11 +343,8 @@
         return compiler.withCurrentElement(library, () {
           return measure(() {
             currentHandler.computeExports();
-            Map<Uri, LibraryElement> loadedLibraries = <Uri, LibraryElement>{};
-            currentHandler.loadedLibraries.forEach(
-                (LibraryElement loadedLibrary) {
-              loadedLibraries[loadedLibrary.canonicalUri] = loadedLibrary;
-            });
+            LoadedLibraries loadedLibraries =
+                new _LoadedLibraries(library, currentHandler.nodeMap, this);
             currentHandler = null;
             return compiler.onLibrariesLoaded(loadedLibraries)
                 .then((_) => library);
@@ -1032,3 +1030,119 @@
     return task.processLibraryTags(this, library);
   }
 }
+
+/// Information on the bulk of newly loaded libraries through a call to
+/// [LibraryLoader.loadLibrary].
+abstract class LoadedLibraries {
+  /// The uri passed to [LibraryLoader.loadLibrary].
+  Uri get rootUri;
+
+  /// Returns `true` if a library with canonical [uri] was loaded in this bulk.
+  bool containsLibrary(Uri uri);
+
+  /// Returns the library with canonical [uri] that was loaded in this bulk.
+  LibraryElement getLibrary(Uri uri);
+
+  /// Applies all libraries in this bulk to [f].
+  void forEachLibrary(f(LibraryElement library));
+
+  /// Applies all imports chains of [uri] in this bulk to [callback].
+  ///
+  /// The argument [importChainReversed] to [callback] contains the chain of
+  /// imports uris that lead to importing [uri] starting in [uri] and ending in
+  /// [rootUri].
+  ///
+  /// [callback] is called once for each chain of imports leading to [uri] until
+  /// [callback] returns `false`.
+  void forEachImportChain(Uri uri,
+                          {bool callback(Link<Uri> importChainReversed)});
+}
+
+class _LoadedLibraries implements LoadedLibraries {
+  final _LibraryLoaderTask task;
+  final LibraryElement rootLibrary;
+  final Map<Uri, LibraryElement> loadedLibraries = <Uri, LibraryElement>{};
+  final Map<LibraryElement, LibraryDependencyNode> nodeMap;
+
+  _LoadedLibraries(this.rootLibrary, this.nodeMap, this.task) {
+    nodeMap.keys.forEach((LibraryElement loadedLibrary) {
+      loadedLibraries[loadedLibrary.canonicalUri] = loadedLibrary;
+    });
+  }
+
+  Uri get rootUri => rootLibrary.canonicalUri;
+
+  bool containsLibrary(Uri uri) => loadedLibraries.containsKey(uri);
+
+  LibraryElement getLibrary(Uri uri) => loadedLibraries[uri];
+
+  void forEachLibrary(f(LibraryElement library)) => nodeMap.keys.forEach(f);
+
+  void forEachImportChain(Uri targetUri,
+                          {bool callback(Link<Uri> importChainReversed)}) {
+    bool aborted = false;
+
+    /// Map from libraries to the set of (unreversed) paths to [uri].
+    Map<LibraryElement, Iterable<Link<Uri>>> suffixChainMap =
+        <LibraryElement, Iterable<Link<Uri>>>{};
+
+    /// Computes the set of (unreversed) paths to [targetUri].
+    ///
+    /// Finds all paths (suffixes) from the current library to [uri] and stores
+    /// it in [suffixChainMap].
+    ///
+    /// For every found suffix it prepends the given [prefix] and the canonical
+    /// uri of [library] and invokes the [callback] with the concatenated chain.
+    void computeSuffixes(LibraryElement library,
+                         Link<Uri> prefix) {
+      if (aborted) return;
+
+      Uri canonicalUri = library.canonicalUri;
+      prefix = prefix.prepend(canonicalUri);
+      if (suffixChainMap.containsKey(library)) return;
+      suffixChainMap[library] = const <Link<Uri>>[];
+      List<Link<Uri>> suffixes = [];
+      if (targetUri != canonicalUri) {
+        LibraryDependencyNode node = nodeMap[library];
+        for (ImportLink import in node.imports.reverse()) {
+          bool suffixesArePrecomputed =
+              suffixChainMap.containsKey(import.importedLibrary);
+
+          if (!suffixesArePrecomputed) {
+            computeSuffixes(import.importedLibrary, prefix);
+            if (aborted) return;
+          }
+
+          for (Link<Uri> suffix in suffixChainMap[import.importedLibrary]) {
+            suffixes.add(suffix.prepend(canonicalUri));
+
+            if (suffixesArePrecomputed) {
+              // Only report chains through [import] if the suffixes had already
+              // been computed, otherwise [computeSuffixes] have reported the
+              // paths through [prefix].
+              Link<Uri> chain = prefix;
+              while (!suffix.isEmpty) {
+                chain = chain.prepend(suffix.head);
+                suffix = suffix.tail;
+              }
+              if (!callback(chain)) {
+                aborted = true;
+                return;
+              }
+            }
+          }
+        }
+      } else { // Here `targetUri == canonicalUri`.
+        if (!callback(prefix)) {
+          aborted = true;
+          return;
+        }
+        suffixes.add(const Link<Uri>().prepend(canonicalUri));
+      }
+      suffixChainMap[library] = suffixes;
+      return;
+    }
+
+    computeSuffixes(rootLibrary, const Link<Uri>());
+  }
+}
diff --git a/pkg/compiler/lib/src/mirrors/dart2js_mirrors.dart b/pkg/compiler/lib/src/mirrors/dart2js_mirrors.dart
index f809260..5aa71de 100644
--- a/pkg/compiler/lib/src/mirrors/dart2js_mirrors.dart
+++ b/pkg/compiler/lib/src/mirrors/dart2js_mirrors.dart
@@ -448,19 +448,6 @@
   Uri get uri => _element.script.resourceUri;
 }
 
-class ResolvedNode {
-  final node;
-  final _elements;
-  final _mirrorSystem;
-  ResolvedNode(this.node, this._elements, this._mirrorSystem);
-
-  Mirror resolvedMirror(node) {
-    var element = _elements[node];
-    if (element == null) return null;
-    return _convertElementToDeclarationMirror(_mirrorSystem, element);
-  }
-}
-
 /**
  * Transitional class that allows access to features that have not yet
  * made it to the mirror API.
@@ -474,28 +461,25 @@
         (cu) => new Dart2JsCompilationUnitMirror(cu, library));
   }
 
-  static Iterable metadataSyntaxOf(Dart2JsElementMirror declaration) {
-    Compiler compiler = declaration.mirrorSystem.compiler;
-    return declaration._element.metadata.map((metadata) {
-      var node = metadata.parseNode(compiler);
-      var treeElements = metadata.annotatedElement.treeElements;
-      return new ResolvedNode(
-          node, treeElements, declaration.mirrorSystem);
-    });
+  static Iterable<ConstantExpression> metadataSyntaxOf(
+      Dart2JsElementMirror declaration) {
+    return declaration._element.metadata.map((metadata) => metadata.constant);
   }
 
-  static ResolvedNode initializerSyntaxOf(Dart2JsFieldMirror variable) {
-    var node = variable._variable.initializer;
-    if (node == null) return null;
-    return new ResolvedNode(
-        node, variable._variable.treeElements, variable.mirrorSystem);
+  static ConstantExpression initializerSyntaxOf(Dart2JsFieldMirror variable) {
+    Compiler compiler = variable.mirrorSystem.compiler;
+    return compiler.constants.getConstantForVariable(variable._variable);
   }
 
-  static ResolvedNode defaultValueSyntaxOf(Dart2JsParameterMirror parameter) {
+  static ConstantExpression defaultValueSyntaxOf(
+        Dart2JsParameterMirror parameter) {
     if (!parameter.hasDefaultValue) return null;
     ParameterElement parameterElement = parameter._element;
-    var node = parameterElement.initializer;
-    var treeElements = parameterElement.treeElements;
-    return new ResolvedNode(node, treeElements, parameter.mirrorSystem);
+    Compiler compiler = parameter.mirrorSystem.compiler;
+    return compiler.constants.getConstantForVariable(parameterElement);
+  }
+
+  static Mirror getMirrorFromElement(Dart2JsMirror mirror, Element element) {
+    return _convertElementToDeclarationMirror(mirror.mirrorSystem, element);
   }
 }
diff --git a/pkg/compiler/lib/src/resolution/enum_creator.dart b/pkg/compiler/lib/src/resolution/enum_creator.dart
index fcc1796..fc9ee70 100644
--- a/pkg/compiler/lib/src/resolution/enum_creator.dart
+++ b/pkg/compiler/lib/src/resolution/enum_creator.dart
@@ -20,13 +20,27 @@
 
   int get charOffset => position.charOffset;
 
-  final Modifiers finalModifiers =
-      new Modifiers.withFlags(null, Modifiers.FLAG_FINAL);
-  final Modifiers constModifiers =
-      new Modifiers.withFlags(null, Modifiers.FLAG_CONST);
-  final Modifiers staticConstModifiers =
-      new Modifiers.withFlags(null,
-          Modifiers.FLAG_STATIC | Modifiers.FLAG_CONST);
+  Modifiers modifiers({bool isConst: false,
+                       bool isFinal: false,
+                       bool isStatic: false}) {
+    List identifiers = [];
+    int flags = 0;
+    if (isConst) {
+      identifiers.add(identifier('const'));
+      flags |= Modifiers.FLAG_CONST;
+    }
+    if (isFinal) {
+      identifiers.add(identifier('final'));
+      flags |= Modifiers.FLAG_FINAL;
+    }
+    if (isStatic) {
+      identifiers.add(identifier('static'));
+      flags |= Modifiers.FLAG_STATIC;
+    }
+    return new Modifiers.withFlags(
+        new NodeList(null, linkedList(identifiers), null, ''),
+        flags);
+  }
 
   Token keywordToken(String text) {
     return new KeywordToken(Keyword.keywords[text], position.charOffset);
@@ -135,6 +149,29 @@
         new Send(null, identifier(typeName), arguments));
   }
 
+  Send reference(Identifier identifier) {
+    return new Send(null, identifier);
+  }
+
+  Send indexGet(Expression receiver, Expression index) {
+    return new Send(receiver,
+                    new Operator(symbolToken(INDEX_INFO)),
+                    new NodeList.singleton(index));
+  }
+
+  LiteralMapEntry mapLiteralEntry(Expression key, Expression value) {
+    return new LiteralMapEntry(key, symbolToken(COLON_INFO), value);
+  }
+
+  LiteralMap mapLiteral(List<LiteralMapEntry> entries, {bool isConst: false}) {
+    return new LiteralMap(
+        null, // Type arguments.
+        new NodeList(symbolToken(OPEN_CURLY_BRACKET_INFO),
+                     linkedList(entries),
+                     symbolToken(CLOSE_CURLY_BRACKET_INFO),
+                     ','),
+        isConst ? keywordToken('const') : null);
+  }
 }
 
 class EnumCreator {
@@ -153,7 +190,8 @@
 
     EnumFieldElementX addInstanceMember(String name, InterfaceType type) {
       Identifier identifier = builder.identifier(name);
-      VariableList variableList = new VariableList(builder.finalModifiers);
+      VariableList variableList =
+          new VariableList(builder.modifiers(isFinal: true));
       variableList.type = type;
       EnumFieldElementX variable = new EnumFieldElementX(
           identifier, enumClass, variableList, identifier);
@@ -162,20 +200,18 @@
     }
 
     EnumFieldElementX indexVariable = addInstanceMember('index', intType);
-    EnumFieldElementX nameVariable = addInstanceMember('_name', stringType);
 
     VariableDefinitions indexDefinition = builder.initializingFormal('index');
-    VariableDefinitions nameDefinition = builder.initializingFormal('_name');
 
     FunctionExpression constructorNode = builder.functionExpression(
-        builder.constModifiers,
+        builder.modifiers(isConst: true),
         enumClass.name,
-        builder.argumentList([indexDefinition, nameDefinition]),
+        builder.argumentList([indexDefinition]),
         builder.emptyStatement());
 
     EnumConstructorElementX constructor = new EnumConstructorElementX(
         enumClass,
-        builder.constModifiers,
+        builder.modifiers(isConst: true),
         constructorNode);
 
     EnumFormalElementX indexFormal = new EnumFormalElementX(
@@ -184,48 +220,50 @@
         builder.identifier('index'),
         indexVariable);
 
-    EnumFormalElementX nameFormal = new EnumFormalElementX(
-        constructor,
-        nameDefinition,
-        builder.identifier('_name'),
-        nameVariable);
-
     FunctionSignatureX constructorSignature = new FunctionSignatureX(
-        requiredParameters: builder.linkedList([indexFormal, nameFormal]),
-        requiredParameterCount: 2,
+        requiredParameters: builder.linkedList([indexFormal]),
+        requiredParameterCount: 1,
         type: new FunctionType(constructor, const VoidType(),
-            <DartType>[intType, stringType]));
+            <DartType>[intType]));
     constructor.functionSignatureCache = constructorSignature;
     enumClass.addMember(constructor, compiler);
 
-    VariableList variableList = new VariableList(builder.staticConstModifiers);
+    List<FieldElement> enumValues = <FieldElement>[];
+    VariableList variableList =
+        new VariableList(builder.modifiers(isStatic: true, isConst: true));
     variableList.type = enumType;
     int index = 0;
     List<Node> valueReferences = <Node>[];
+    List<LiteralMapEntry> mapEntries = <LiteralMapEntry>[];
     for (Link<Node> link = node.names.nodes;
          !link.isEmpty;
          link = link.tail) {
       Identifier name = link.head;
       AstBuilder valueBuilder = new AstBuilder(name.token);
-      valueReferences.add(new Send(null, name));
+
+      // Add reference for the `values` field.
+      valueReferences.add(valueBuilder.reference(name));
+
+      // Add map entry for `toString` implementation.
+      mapEntries.add(valueBuilder.mapLiteralEntry(
+            valueBuilder.literalInt(index),
+            valueBuilder.literalString('${enumClass.name}.${name.source}')));
 
       Expression initializer = valueBuilder.newExpression(
           enumClass.name,
-          valueBuilder.argumentList([
-            valueBuilder.literalInt(index),
-            valueBuilder.literalString('${name.source}')
-          ]),
+          valueBuilder.argumentList([valueBuilder.literalInt(index)]),
           isConst: true);
       SendSet definition = valueBuilder.createDefinition(name, initializer);
 
       EnumFieldElementX field = new EnumFieldElementX(
           name, enumClass, variableList, definition, initializer);
+      enumValues.add(field);
       enumClass.addMember(field, compiler);
       index++;
     }
 
     VariableList valuesVariableList =
-        new VariableList(builder.staticConstModifiers);
+        new VariableList(builder.modifiers(isStatic: true, isConst: true));
     InterfaceType listType = compiler.listClass.computeType(compiler);
     valuesVariableList.type = listType.createInstantiation([enumType]);
 
@@ -249,12 +287,10 @@
         'toString',
         builder.argumentList([]),
         builder.returnStatement(
-            new StringInterpolation(
-                builder.literalString('${enumClass.name}.', suffix: ''),
-                new NodeList.singleton(new StringInterpolationPart(
-                    new Send(null, builder.identifier('_name')),
-                    builder.literalString('', prefix: '')))
-            ))
+              builder.indexGet(
+                  builder.mapLiteral(mapEntries, isConst: true),
+                  builder.reference(builder.identifier('index')))
+            )
         );
 
     EnumMethodElementX toString = new EnumMethodElementX('toString',
@@ -263,5 +299,7 @@
         type: new FunctionType(toString, stringType));
     toString.functionSignatureCache = toStringSignature;
     enumClass.addMember(toString, compiler);
+
+    enumClass.enumValues = enumValues;
   }
 }
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
index 353bfdf..c697b2c 100644
--- a/pkg/compiler/lib/src/resolution/members.dart
+++ b/pkg/compiler/lib/src/resolution/members.dart
@@ -2909,11 +2909,11 @@
 
         // Don't try to make constants of calls to type literals.
         if (!node.isCall) {
-          analyzeConstant(node);
+          analyzeConstantDeferred(node);
         } else {
           // The node itself is not a constant but we register the selector (the
           // identifier that refers to the class/typedef) as a constant.
-          analyzeConstant(node.selector);
+          analyzeConstantDeferred(node.selector);
         }
       }
       if (isPotentiallyMutableTarget(target)) {
@@ -3177,7 +3177,7 @@
           MessageKind.UNSUPPORTED_LITERAL_SYMBOL,
           {'value': node.slowNameString});
     }
-    analyzeConstant(node);
+    analyzeConstantDeferred(node);
   }
 
   visitStringJuxtaposition(StringJuxtaposition node) {
@@ -3428,7 +3428,7 @@
       compiler.mirrorUsageAnalyzerTask.validate(node, registry.mapping);
     }
     if (node.isConst) {
-      analyzeConstant(node);
+      analyzeConstantDeferred(node);
     }
 
     return null;
@@ -3452,34 +3452,38 @@
   }
 
   void analyzeConstant(Node node) {
-    addDeferredAction(enclosingElement, () {
-      ConstantExpression constant =
-          compiler.resolver.constantCompiler.compileNode(
-              node, registry.mapping);
+    ConstantExpression constant =
+        compiler.resolver.constantCompiler.compileNode(
+            node, registry.mapping);
 
-      ConstantValue value = constant.value;
-      if (value.isMap) {
-        checkConstMapKeysDontOverrideEquals(node, value);
-      }
+    ConstantValue value = constant.value;
+    if (value.isMap) {
+      checkConstMapKeysDontOverrideEquals(node, value);
+    }
 
-      // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names
-      // a class that will be instantiated outside the program by attaching a
-      // native class dispatch record referencing the interceptor.
-      if (argumentsToJsInterceptorConstant != null &&
-          argumentsToJsInterceptorConstant.contains(node)) {
-        if (value.isType) {
-          TypeConstantValue typeConstant = value;
-          if (typeConstant.representedType is InterfaceType) {
-            registry.registerInstantiatedType(typeConstant.representedType);
-          } else {
-            compiler.reportError(node,
-                MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
-          }
+    // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names
+    // a class that will be instantiated outside the program by attaching a
+    // native class dispatch record referencing the interceptor.
+    if (argumentsToJsInterceptorConstant != null &&
+        argumentsToJsInterceptorConstant.contains(node)) {
+      if (value.isType) {
+        TypeConstantValue typeConstant = value;
+        if (typeConstant.representedType is InterfaceType) {
+          registry.registerInstantiatedType(typeConstant.representedType);
         } else {
           compiler.reportError(node,
               MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
         }
+      } else {
+        compiler.reportError(node,
+            MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
       }
+    }
+  }
+
+  void analyzeConstantDeferred(Node node) {
+    addDeferredAction(enclosingElement, () {
+      analyzeConstant(node);
     });
   }
 
@@ -3569,7 +3573,7 @@
     registry.registerRequiredType(listType, enclosingElement);
     visit(node.elements);
     if (node.isConst) {
-      analyzeConstant(node);
+      analyzeConstantDeferred(node);
     }
 
     sendIsMemberAccess = false;
@@ -3790,7 +3794,7 @@
     registry.registerRequiredType(mapType, enclosingElement);
     node.visitChildren(this);
     if (node.isConst) {
-      analyzeConstant(node);
+      analyzeConstantDeferred(node);
     }
 
     sendIsMemberAccess = false;
@@ -3900,7 +3904,7 @@
       for (Node labelOrCase in switchCase.labelsAndCases) {
         CaseMatch caseMatch = labelOrCase.asCaseMatch();
         if (caseMatch != null) {
-          analyzeConstant(caseMatch.expression);
+          analyzeConstantDeferred(caseMatch.expression);
           continue;
         }
         Label label = labelOrCase;
@@ -4361,6 +4365,12 @@
     element.interfaces = const Link<DartType>();
     calculateAllSupertypes(element);
 
+    if (node.names.nodes.isEmpty) {
+      compiler.reportError(node,
+                           MessageKind.EMPTY_ENUM_DECLARATION,
+                           {'enumName': element.name});
+    }
+
     EnumCreator creator = new EnumCreator(compiler, element);
     creator.createMembers();
     return enumType;
diff --git a/pkg/compiler/lib/src/typechecker.dart b/pkg/compiler/lib/src/typechecker.dart
index b1fb8cd..4073f29 100644
--- a/pkg/compiler/lib/src/typechecker.dart
+++ b/pkg/compiler/lib/src/typechecker.dart
@@ -1736,11 +1736,16 @@
   visitSwitchStatement(SwitchStatement node) {
     // TODO(johnniwinther): Handle reachability based on reachability of
     // switch cases.
+    // TODO(johnniwinther): Provide hint of duplicate case constants.
 
     DartType expressionType = analyze(node.expression);
 
     // Check that all the case expressions are assignable to the expression.
+    bool hasDefaultCase = false;
     for (SwitchCase switchCase in node.cases) {
+      if (switchCase.isDefaultCase) {
+        hasDefaultCase = true;
+      }
       for (Node labelOrCase in switchCase.labelsAndCases) {
         CaseMatch caseMatch = labelOrCase.asCaseMatch();
         if (caseMatch == null) continue;
@@ -1752,6 +1757,44 @@
       analyze(switchCase);
     }
 
+    if (!hasDefaultCase && expressionType.isEnumType) {
+      compiler.enqueuer.resolution.addDeferredAction(
+          elements.analyzedElement, () {
+        Map<ConstantValue, FieldElement> enumValues =
+            <ConstantValue, FieldElement>{};
+        List<FieldElement> unreferencedFields = <FieldElement>[];
+        EnumClassElement enumClass = expressionType.element;
+        enumClass.enumValues.forEach((FieldElement field) {
+          ConstantExpression constantExpression =
+              compiler.constants.getConstantForVariable(field);
+          if (constantExpression == null) {
+            // The field might not have been resolved.
+            unreferencedFields.add(field);
+          } else {
+            enumValues[constantExpression.value] = field;
+          }
+        });
+
+        for (SwitchCase switchCase in node.cases) {
+          for (Node labelOrCase in switchCase.labelsAndCases) {
+            CaseMatch caseMatch = labelOrCase.asCaseMatch();
+            if (caseMatch != null) {
+              ConstantExpression caseConstant =
+                  compiler.resolver.constantCompiler.compileNode(
+                      caseMatch.expression, elements);
+              enumValues.remove(caseConstant.value);
+            }
+          }
+        }
+        unreferencedFields.addAll(enumValues.values);
+        if (!unreferencedFields.isEmpty) {
+          compiler.reportWarning(node, MessageKind.MISSING_ENUM_CASES,
+              {'enumType': expressionType,
+               'enumValues': unreferencedFields.map((e) => e.name).join(', ')});
+        }
+      });
+    }
+
     return const StatementType();
   }
 
diff --git a/pkg/compiler/lib/src/util/uri_extras.dart b/pkg/compiler/lib/src/util/uri_extras.dart
index 65a4ed2..aa8cdcf 100644
--- a/pkg/compiler/lib/src/util/uri_extras.dart
+++ b/pkg/compiler/lib/src/util/uri_extras.dart
@@ -7,18 +7,20 @@
 import 'dart:math';
 
 String relativize(Uri base, Uri uri, bool isWindows) {
-  if (!base.path.startsWith('/')) {
-    // Also throw an exception if [base] or base.path is null.
-    throw new ArgumentError('Expected absolute path: ${base.path}');
-  }
-  if (!uri.path.startsWith('/')) {
-    // Also throw an exception if [uri] or uri.path is null.
-    throw new ArgumentError('Expected absolute path: ${uri.path}');
-  }
   bool equalsNCS(String a, String b) {
     return a.toLowerCase() == b.toLowerCase();
   }
 
+  if (!equalsNCS(base.scheme, uri.scheme) ||
+      equalsNCS(base.scheme, 'dart') ||
+      equalsNCS(base.scheme, 'package')) {
+    return uri.toString();
+  }
+
+  if (!equalsNCS(base.scheme, 'file')) {
+    isWindows = false;
+  }
+
   String normalize(String path) {
     if (isWindows) {
       return path.toLowerCase();
@@ -27,9 +29,7 @@
     }
   }
 
-  if (equalsNCS(base.scheme, 'file') &&
-      equalsNCS(base.scheme, uri.scheme) &&
-      base.userInfo == uri.userInfo &&
+  if (base.userInfo == uri.userInfo &&
       equalsNCS(base.host, uri.host) &&
       base.port == uri.port &&
       uri.query == "" && uri.fragment == "") {
@@ -37,6 +37,11 @@
       return uri.path.substring(base.path.lastIndexOf('/') + 1);
     }
 
+    if (!base.path.startsWith('/') ||
+        !uri.path.startsWith('/')) {
+      return uri.toString();
+    }
+
     List<String> uriParts = uri.path.split('/');
     List<String> baseParts = base.path.split('/');
     int common = 0;
diff --git a/pkg/compiler/lib/src/warnings.dart b/pkg/compiler/lib/src/warnings.dart
index 1d72b64..5b63242 100644
--- a/pkg/compiler/lib/src/warnings.dart
+++ b/pkg/compiler/lib/src/warnings.dart
@@ -804,9 +804,9 @@
       howToFix: "Try making '#{enumType}' a normal class or removing the "
         "'extends' clause.",
       examples: const ["""
-enum Enum {}
-class A extends Enum {}
-main() => new A();"""]);
+enum Enum { A }
+class B extends Enum {}
+main() => new B();"""]);
 
   static const MessageKind CANNOT_IMPLEMENT_ENUM = const MessageKind(
       "Class '#{className}' can't implement the type '#{enumType}' "
@@ -815,9 +815,9 @@
       howToFix: "Try making '#{enumType}' a normal class or removing the "
         "type from the 'implements' clause.",
       examples: const ["""
-enum Enum {}
-class A implements Enum {}
-main() => new A();"""]);
+enum Enum { A }
+class B implements Enum {}
+main() => new B();"""]);
 
   static const MessageKind CANNOT_MIXIN_ENUM = const MessageKind(
       "Class '#{className}' can't mixin the type '#{enumType}' because it "
@@ -826,9 +826,9 @@
       howToFix: "Try making '#{enumType}' a normal class or removing the "
         "type from the 'with' clause.",
       examples: const ["""
-enum Enum {}
-class A extends Object with Enum {}
-main() => new A();"""]);
+enum Enum { A }
+class B extends Object with Enum {}
+main() => new B();"""]);
 
   static const MessageKind CANNOT_INSTANTIATE_ENUM = const MessageKind(
       "Enum type '#{enumName}' cannot be instantiated.",
@@ -836,10 +836,37 @@
       howToFix: "Try making '#{enumType}' a normal class or use an enum "
                 "constant.",
       examples: const ["""
+enum Enum { A }
+main() => new Enum(0);""", """
+enum Enum { A }
+main() => const Enum(0);"""]);
+
+  static const MessageKind EMPTY_ENUM_DECLARATION = const MessageKind(
+      "Enum '#{enumName}' must contain at least one value.",
+      options: const ['--enable-enum'],
+      howToFix: "Try adding an enum constant or making #{enumName} a "
+                "normal class.",
+      examples: const ["""
 enum Enum {}
-main() => new Enum(0, '');""", """
-enum Enum {}
-main() => const Enum(0, '');"""]);
+main() { Enum e; }"""]);
+
+  static const MessageKind MISSING_ENUM_CASES = const MessageKind(
+      "Missing enum constants in switch statement: #{enumValues}.",
+      options: const ['--enable-enum'],
+      howToFix: "Try adding the missing constants or a default case.",
+      examples: const ["""
+enum Enum { A, B }
+main() {
+  switch (Enum.A) {
+  case Enum.B: break;
+  }
+}""", """
+enum Enum { A, B, C }
+main() {
+  switch (Enum.A) {
+  case Enum.B: break;
+  }
+}"""]);
 
   static const MessageKind DUPLICATE_EXTENDS_IMPLEMENTS = const MessageKind(
       "'#{type}' can not be both extended and implemented.");
@@ -2226,6 +2253,30 @@
   // Patch errors end.
   //////////////////////////////////////////////////////////////////////////////
 
+  static const String IMPORT_EXPERIMENTAL_MIRRORS_PADDING = '\n*   ';
+
+  static const MessageKind IMPORT_EXPERIMENTAL_MIRRORS =
+      const MessageKind(r'''
+
+****************************************************************
+* WARNING: dart:mirrors support in dart2js is experimental,
+*          and not recommended.
+*          This implementation of mirrors is incomplete,
+*          and often greatly increases the size of the generated
+*          JavaScript code.
+*
+* Your app imports dart:mirrors via:''''''
+$IMPORT_EXPERIMENTAL_MIRRORS_PADDING#{importChain}
+*
+* Starting with Dart 1.9, you must use the
+* --enable-experimental-mirrors command-line flag to opt-in.
+* You can begin using this flag now if mirrors support is critical.
+*
+* To learn what to do next, please visit:
+*    http://dartlang.org/dart2js-reflection
+****************************************************************
+''');
+
   static const MessageKind CALL_NOT_SUPPORTED_ON_NATIVE_CLASS =
       const MessageKind(
           "Non-supported 'call' member on a native class, or a "
diff --git a/pkg/docgen/lib/src/models/annotation.dart b/pkg/docgen/lib/src/models/annotation.dart
index 5a8dd11..766939c 100644
--- a/pkg/docgen/lib/src/models/annotation.dart
+++ b/pkg/docgen/lib/src/models/annotation.dart
@@ -6,37 +6,22 @@
 
 import '../exports/source_mirrors.dart';
 
-import '../exports/dart2js_mirrors.dart' show ResolvedNode;
-
 import '../library_helpers.dart';
 
 import 'library.dart';
 import 'mirror_based.dart';
 
 import 'dart:mirrors';
-import 'package:compiler/src/tree/tree.dart';
 
 /// Holds the name of the annotation, and its parameters.
 class Annotation extends MirrorBased<ClassMirror> {
   /// The class of this annotation.
   DeclarationMirror mirror;
-  Send node;
   final Library owningLibrary;
   List<String> parameters;
 
-  Annotation(ResolvedNode resolvedNode, this.owningLibrary) {
-    parameters = [];
-    getMirrorForResolvedNode(resolvedNode, (m, n) { mirror = m; node = n;},
-        (String param) => parameters.add(param));
-  }
-
-  String getMirrorForResolvedNode(ResolvedNode node, callbackFunc,
-      paramCallbackFunc) {
-    ResolvedNodeMirrorFinder finder = new ResolvedNodeMirrorFinder(node,
-        callbackFunc, paramCallbackFunc);
-    finder.unparse(node.node);
-    return finder.result;
-  }
+  Annotation(this.owningLibrary, this.mirror,
+             [List<String> this.parameters = const <String>[]]);
 
   Map toMap() => {
     'name': owningLibrary.packagePrefix +
@@ -45,70 +30,3 @@
   };
 }
 
-class ResolvedNodeMirrorFinder extends Unparser {
-  final ResolvedNode resolvedNode;
-  final Function annotationMirrorCallback;
-  final Function parameterValueCallback;
-  int recursionLevel;
-
-  ResolvedNodeMirrorFinder(this.resolvedNode, this.annotationMirrorCallback,
-      this.parameterValueCallback) : recursionLevel = 0;
-
-  visitSend(Send node) {
-    if (recursionLevel == 0) {
-      var m = resolvedNode.resolvedMirror(node.selector);
-      annotationMirrorCallback(m, node);
-    } else {
-      Operator op = node.selector.asOperator();
-      String opString = op != null ? op.source : null;
-      bool spacesNeeded =
-          identical(opString, 'is') || identical(opString, 'as');
-      if (node.isPrefix) visit(node.selector);
-          unparseSendReceiver(node, spacesNeeded: spacesNeeded);
-          if (!node.isPrefix && !node.isIndex) visit(node.selector);
-          if (spacesNeeded) sb.write(' ');
-          // Also add a space for sequences like x + +1 and y - -y.
-          // TODO(ahe): remove case for '+' when we drop the support for it.
-          if (node.argumentsNode != null && (identical(opString, '-')
-              || identical(opString, '+'))) {
-            var beginToken = node.argumentsNode.getBeginToken();
-            if (beginToken != null &&
-                identical(beginToken.stringValue, opString)) {
-              sb.write(' ');
-            }
-          }
-    }
-    recursionLevel++;
-    visit(node.argumentsNode);
-    recursionLevel--;
-  }
-
-  unparseNodeListFrom(NodeList node, var from, {bool spaces: true}) {
-    if (from.isEmpty) return;
-
-    visit(from.head);
-
-    for (var link = from.tail; !link.isEmpty; link = link.tail) {
-      if (recursionLevel >= 2) {
-        parameterValueCallback(sb.toString());
-        sb.clear();
-      }
-      visit(link.head);
-    }
-    if (recursionLevel >= 2) {
-      parameterValueCallback(sb.toString());
-      sb.clear();
-    }
-  }
-
-  visitNodeList(NodeList node) {
-    addToken(node.beginToken);
-    if (recursionLevel == 1) sb.clear();
-    if (node.nodes != null) {
-      recursionLevel++;
-      unparseNodeListFrom(node, node.nodes);
-      recursionLevel--;
-    }
-    if (node.endToken != null) write(node.endToken.value);
-  }
-}
diff --git a/pkg/docgen/lib/src/models/model_helpers.dart b/pkg/docgen/lib/src/models/model_helpers.dart
index e131472..526ab4a 100644
--- a/pkg/docgen/lib/src/models/model_helpers.dart
+++ b/pkg/docgen/lib/src/models/model_helpers.dart
@@ -6,6 +6,8 @@
 
 import 'dart:collection';
 
+import 'package:compiler/src/constants/expressions.dart';
+
 import '../exports/dart2js_mirrors.dart' as dart2js_mirrors;
 import '../exports/mirrors_util.dart' as dart2js_util;
 import '../exports/source_mirrors.dart';
@@ -53,22 +55,119 @@
   return '${mirror.defaultValue}';
 }
 
-// TODO(johnniwinther): Use the `ConstExp` classes instead of `ResolvedNode`.
 /// Returns a list of meta annotations assocated with a mirror.
 List<Annotation> createAnnotations(DeclarationMirror mirror,
     Library owningLibrary) {
   var annotations = [];
-  for (dart2js_mirrors.ResolvedNode node in
-      dart2js_mirrors.BackDoor.metadataSyntaxOf(mirror)) {
-    var docgenAnnotation = new Annotation(node, owningLibrary);
-    if (!_SKIPPED_ANNOTATIONS.contains(dart2js_util.qualifiedNameOf(
-        docgenAnnotation.mirror))) {
+  var info = new AnnotationInfo(mirror, owningLibrary);
+  for (var expr in dart2js_mirrors.BackDoor.metadataSyntaxOf(mirror)) {
+    var docgenAnnotation = expr.accept(const AnnotationCreator(), info);
+    if (docgenAnnotation != null &&
+        !_SKIPPED_ANNOTATIONS.contains(
+            dart2js_util.qualifiedNameOf(docgenAnnotation.mirror))) {
       annotations.add(docgenAnnotation);
     }
   }
   return annotations;
 }
 
+class AnnotationInfo {
+  final Mirror mirror;
+  final Library owningLibrary;
+
+  AnnotationInfo(this.mirror, this.owningLibrary);
+}
+
+class AnnotationCreator
+    extends ConstantExpressionVisitor<AnnotationInfo, Annotation> {
+
+  const AnnotationCreator();
+
+  Annotation createAnnotation(var element,
+                              AnnotationInfo context,
+                              [List<String> parameters = const <String>[]]) {
+    var mirror =
+        dart2js_mirrors.BackDoor.getMirrorFromElement(context.mirror, element);
+    if (mirror != null) {
+      return new Annotation(context.owningLibrary, mirror, parameters);
+    }
+    return null;
+  }
+
+  @override
+  Annotation visitBinary(BinaryConstantExpression exp,
+                         [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitConcatenate(ConcatenateConstantExpression exp,
+                              [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitConditional(ConditionalConstantExpression exp,
+                              [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitConstructed(ConstructedConstantExpresssion exp,
+                              [AnnotationInfo context]) {
+    return createAnnotation(exp.target, context,
+        exp.arguments.map((a) => a.getText()).toList());
+  }
+
+  @override
+  Annotation visitFunction(FunctionConstantExpression exp,
+                           [AnnotationInfo context]) {
+    return createAnnotation(exp.element, context);
+  }
+
+  @override
+  Annotation visitList(ListConstantExpression exp,
+                       [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitMap(MapConstantExpression exp,
+                      [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitPrimitive(PrimitiveConstantExpression exp,
+                            [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitSymbol(SymbolConstantExpression exp,
+                         [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitType(TypeConstantExpression exp,
+                       [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitUnary(UnaryConstantExpression exp,
+                        [AnnotationInfo context]) {
+    return null;
+  }
+
+  @override
+  Annotation visitVariable(VariableConstantExpression exp,
+                           [AnnotationInfo context]) {
+    return createAnnotation(exp.element, context);
+  }
+}
+
 /// A declaration is private if itself is private, or the owner is private.
 bool isHidden(DeclarationSourceMirror mirror) {
   if (mirror is LibraryMirror) {
diff --git a/pkg/docgen/test/metadata_test.dart b/pkg/docgen/test/metadata_test.dart
new file mode 100644
index 0000000..87ea6f5
--- /dev/null
+++ b/pkg/docgen/test/metadata_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2014, 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 async_await_test;
+
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+import 'package:unittest/unittest.dart';
+
+import '../lib/src/exports/mirrors_util.dart' as dart2js_util;
+import '../lib/src/models/annotation.dart';
+import '../lib/docgen.dart';
+
+const Map<String, String> SOURCES = const <String, String>{
+  'main.dart': '''
+import 'lib.dart' as lib;
+
+@lib.Annotation("foo", 42)
+main() {
+}
+''',
+  'lib.dart': '''
+class Annotation {
+  final String arg1;
+  final int arg2;
+  const Annotation(this.arg1, this.arg2);
+}
+'''};
+
+main() {
+  group('Generate docs for', () {
+    test('files with annotations', () {
+      var temporaryDir = Directory.systemTemp.createTempSync('metadata_');
+      var uris = <Uri>[];
+      SOURCES.forEach((name, code) {
+        var fileName = path.join(temporaryDir.path, name);
+        var file = new File(fileName);
+        file.writeAsStringSync(code);
+        uris.add(new Uri.file(fileName));
+      });
+
+      return getMirrorSystem(uris, false).then((mirrorSystem) {
+        var library = new Library(mirrorSystem.libraries[uris[0]]);
+        expect(library is Library, isTrue);
+
+        var main = library.functions['main'];
+        expect(main is Method, isTrue);
+
+        var annotations = main.annotations;
+        expect(annotations.length, equals(1));
+
+        var annotation = annotations[0];
+        expect(annotation is Annotation, isTrue);
+
+        var map = annotation.toMap();
+        expect(map['name'], equals('lib-dart.Annotation.Annotation-'));
+        expect(map['parameters'].length, equals(2));
+        expect(map['parameters'][0], equals('"foo"'));
+        expect(map['parameters'][1], equals('42'));
+      }).whenComplete(() => temporaryDir.deleteSync(recursive: true));
+    });
+  });
+}
diff --git a/tests/compiler/dart2js/exit_code_test.dart b/tests/compiler/dart2js/exit_code_test.dart
index 4b9899d..9966f59 100644
--- a/tests/compiler/dart2js/exit_code_test.dart
+++ b/tests/compiler/dart2js/exit_code_test.dart
@@ -54,7 +54,7 @@
     return super.onLibraryScanned(element, loader);

   }

 

-  Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) {

+  Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {

     test('Compiler.onLibrariesLoaded');

     return super.onLibrariesLoaded(loadedLibraries);

   }

diff --git a/tests/compiler/dart2js/import_mirrors_test.dart b/tests/compiler/dart2js/import_mirrors_test.dart
new file mode 100644
index 0000000..07c00d0
--- /dev/null
+++ b/tests/compiler/dart2js/import_mirrors_test.dart
@@ -0,0 +1,316 @@
+// Copyright (c) 2014, 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.

+

+// Test that the compiler emits a warning on import of 'dart:mirrors' unless

+// the flag --enable-experimental-mirrors is used.

+

+library dart2js.test.import;

+

+import 'package:expect/expect.dart';

+import 'package:async_helper/async_helper.dart';

+import 'package:compiler/src/dart2jslib.dart' show MessageKind;

+import 'memory_compiler.dart';

+

+const DIRECT_IMPORT = const {

+  '/main.dart': '''

+import 'dart:mirrors';

+

+main() {}

+''',

+

+  'paths':

+      "main.dart => dart:mirrors",

+};

+

+const INDIRECT_IMPORT1 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "first.dart => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => dart:mirrors",

+};

+

+const INDIRECT_IMPORT2 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'second.dart';

+''',

+  '/second.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "second.dart => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => second.dart => dart:mirrors",

+};

+

+const INDIRECT_PACKAGE_IMPORT1 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'package:second.dart';

+''',

+  '/pkg/second.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "first.dart => package:second.dart => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => package:second.dart => dart:mirrors",

+};

+

+const INDIRECT_PACKAGE_IMPORT2 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'package:package-name/second.dart';

+''',

+  '/pkg/package-name/second.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "first.dart => package:package-name => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => package:package-name/second.dart "

+      "=> dart:mirrors",

+};

+

+const INDIRECT_PACKAGE_IMPORT3 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'package:package1/second.dart';

+''',

+  '/pkg/package1/second.dart': '''

+import 'package:package2/third.dart';

+''',

+  '/pkg/package2/third.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "first.dart => package:package1 => package:package2 => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => package:package1/second.dart "

+      "=> package:package2/third.dart => dart:mirrors",

+};

+

+const INDIRECT_PACKAGE_IMPORT4 = const {

+  '/main.dart': '''

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'package:package1/second.dart';

+''',

+  '/pkg/package1/second.dart': '''

+import 'sub/third.dart';

+''',

+  '/pkg/package1/sub/third.dart': '''

+import 'package:package2/fourth.dart';

+''',

+  '/pkg/package2/fourth.dart': '''

+import 'lib/src/fifth.dart';

+''',

+  '/pkg/package2/lib/src/fifth.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "first.dart => package:package1 => package:package2 => dart:mirrors",

+  'verbosePaths':

+      "main.dart => first.dart => package:package1/second.dart "

+      "=> package:package1/sub/third.dart => package:package2/fourth.dart "

+      "=> package:package2/lib/src/fifth.dart => dart:mirrors",

+};

+

+const DUAL_DIRECT_IMPORT = const {

+  '/main.dart': '''

+import 'dart:mirrors';

+import 'dart:mirrors';

+

+main() {}

+''',

+

+  'paths':

+      "main.dart => dart:mirrors",

+};

+

+const DUAL_INDIRECT_IMPORT1 = const {

+  '/main.dart': '''

+import 'dart:mirrors';

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths': const

+      ["main.dart => dart:mirrors",

+       "first.dart => dart:mirrors"],

+  'verbosePaths': const

+      ["main.dart => dart:mirrors",

+       "main.dart => first.dart => dart:mirrors"],

+};

+

+const DUAL_INDIRECT_IMPORT2 = const {

+  '/main.dart': '''

+import 'first.dart';

+import 'second.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'dart:mirrors';

+''',

+  '/second.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths': const

+      ["first.dart => dart:mirrors",

+       "second.dart => dart:mirrors"],

+  'verbosePaths': const

+      ["main.dart => first.dart => dart:mirrors",

+       "main.dart => second.dart => dart:mirrors"],

+};

+

+const DUAL_INDIRECT_IMPORT3 = const {

+  '/main.dart': '''

+import 'first.dart';

+import 'second.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'third.dart';

+''',

+  '/second.dart': '''

+import 'third.dart';

+''',

+  '/third.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths':

+      "third.dart => dart:mirrors",

+  'verbosePaths': const

+      ["main.dart => first.dart => third.dart => dart:mirrors",

+       "main.dart => second.dart => third.dart => dart:mirrors"],

+};

+

+const DUAL_INDIRECT_PACKAGE_IMPORT1 = const {

+  '/main.dart': '''

+import 'package:package1/second.dart';

+import 'first.dart';

+

+main() {}

+''',

+  '/first.dart': '''

+import 'package:package2/third.dart';

+''',

+  '/pkg/package1/second.dart': '''

+import 'dart:mirrors';

+''',

+  '/pkg/package2/third.dart': '''

+import 'dart:mirrors';

+''',

+

+  'paths': const

+      ["main.dart => package:package1 => dart:mirrors",

+       "first.dart => package:package2 => dart:mirrors"],

+  'verbosePaths': const

+      ["main.dart => package:package1/second.dart => dart:mirrors",

+       "main.dart => first.dart => package:package2/third.dart => dart:mirrors"]

+};

+

+

+test(Map sourceFiles,

+    {expectedPaths,

+     bool verbose: false,

+     bool enableExperimentalMirrors: false}) {

+  if (expectedPaths is! List) {

+    expectedPaths = [expectedPaths];

+  }

+  var collector = new DiagnosticCollector();

+  var options = [];

+  if (verbose) {

+    options.add('--verbose');

+  }

+  if (enableExperimentalMirrors) {

+    options.add('--enable-experimental-mirrors');

+  }

+  var compiler = compilerFor(sourceFiles, diagnosticHandler: collector,

+                             packageRoot: Uri.parse('memory:/pkg/'),

+                             options: options);

+  asyncTest(() => compiler.run(Uri.parse('memory:/main.dart')).then((_) {

+    Expect.equals(0, collector.errors.length, 'Errors: ${collector.errors}');

+    if (enableExperimentalMirrors) {

+      Expect.equals(0, collector.warnings.length,

+                    'Warnings: ${collector.errors}');

+    } else {

+      Expect.equals(1, collector.warnings.length,

+                    'Warnings: ${collector.errors}');

+      Expect.equals(

+          MessageKind.IMPORT_EXPERIMENTAL_MIRRORS.message(

+              {'importChain': expectedPaths.join(

+                  MessageKind.IMPORT_EXPERIMENTAL_MIRRORS_PADDING)}).toString(),

+          collector.warnings.first.message);

+    }

+  }));

+}

+

+checkPaths(Map sourceData) {

+  Map sourceFiles = sourceData;

+  var expectedPaths = sourceData['paths'];

+  var expectedVerbosePaths = sourceData['verbosePaths'];

+  if (expectedVerbosePaths == null) {

+    expectedVerbosePaths = expectedPaths;

+  }

+  test(sourceFiles, expectedPaths: expectedPaths);

+  test(sourceFiles, expectedPaths: expectedVerbosePaths, verbose: true);

+  test(sourceFiles, enableExperimentalMirrors: true);

+}

+

+void main() {

+  checkPaths(DIRECT_IMPORT);

+  checkPaths(INDIRECT_IMPORT1);

+  checkPaths(INDIRECT_IMPORT2);

+  checkPaths(INDIRECT_PACKAGE_IMPORT1);

+  checkPaths(INDIRECT_PACKAGE_IMPORT2);

+  checkPaths(INDIRECT_PACKAGE_IMPORT3);

+  checkPaths(INDIRECT_PACKAGE_IMPORT4);

+  checkPaths(DUAL_DIRECT_IMPORT);

+  checkPaths(DUAL_INDIRECT_IMPORT1);

+  checkPaths(DUAL_INDIRECT_IMPORT2);

+  checkPaths(DUAL_INDIRECT_IMPORT3);

+  checkPaths(DUAL_INDIRECT_PACKAGE_IMPORT1);

+}

diff --git a/tests/compiler/dart2js/memory_compiler.dart b/tests/compiler/dart2js/memory_compiler.dart
index de75d6c..88bc8e2 100644
--- a/tests/compiler/dart2js/memory_compiler.dart
+++ b/tests/compiler/dart2js/memory_compiler.dart
@@ -10,13 +10,16 @@
        show NullSink;
 
 import 'package:compiler/compiler.dart'
-       show Diagnostic, DiagnosticHandler, CompilerOutputProvider;
+    show Diagnostic, DiagnosticHandler, CompilerOutputProvider;
 
 import 'dart:async';
 
 import 'package:compiler/src/mirrors/source_mirrors.dart';
 import 'package:compiler/src/mirrors/analyze.dart';
 
+import 'package:compiler/src/library_loader.dart'
+    show LoadedLibraries;
+
 class DiagnosticMessage {
   final Uri uri;
   final int begin;
@@ -183,7 +186,7 @@
     });
     // TODO(johnniwinther): Assert that no libraries are loaded lazily from
     // this call.
-    compiler.onLibrariesLoaded(copiedLibraries);
+    compiler.onLibrariesLoaded(new MemoryLoadedLibraries(copiedLibraries));
 
     compiler.symbolConstructor = cachedCompiler.symbolConstructor;
     compiler.mirrorSystemClass = cachedCompiler.mirrorSystemClass;
@@ -229,6 +232,27 @@
   return compiler;
 }
 
+class MemoryLoadedLibraries implements LoadedLibraries {
+  final Map copiedLibraries;
+
+  MemoryLoadedLibraries(this.copiedLibraries);
+
+  @override
+  bool containsLibrary(Uri uri) => copiedLibraries.containsKey(uri);
+
+  @override
+  void forEachImportChain(f) {}
+
+  @override
+  void forEachLibrary(f) {}
+
+  @override
+  getLibrary(Uri uri) => copiedLibraries[uri];
+
+  @override
+  Uri get rootUri => null;
+}
+
 Future<MirrorSystem> mirrorSystemFor(Map<String,String> memorySourceFiles,
                                      {DiagnosticHandler diagnosticHandler,
                                       List<String> options: const [],
diff --git a/tests/compiler/dart2js/message_kind_helper.dart b/tests/compiler/dart2js/message_kind_helper.dart
index 67f1d60..2258935 100644
--- a/tests/compiler/dart2js/message_kind_helper.dart
+++ b/tests/compiler/dart2js/message_kind_helper.dart
@@ -83,7 +83,8 @@
     Compiler compiler = compilerFor(
         example,
         diagnosticHandler: collect,
-        options: ['--analyze-only']..addAll(kind.options),
+        options: ['--analyze-only',
+                  '--enable-experimental-mirrors']..addAll(kind.options),
         cachedCompiler: cachedCompiler);
 
     return compiler.run(Uri.parse('memory:main.dart')).then((_) {
diff --git a/tests/compiler/dart2js/message_kind_test.dart b/tests/compiler/dart2js/message_kind_test.dart
index 75ad6c8..368d51b 100644
--- a/tests/compiler/dart2js/message_kind_test.dart
+++ b/tests/compiler/dart2js/message_kind_test.dart
@@ -21,10 +21,14 @@
     VariableMirror variable = declaration;
     if (variable.isStatic) {
       var value = cls.getField(name).reflectee;
+      String variableName = MirrorSystem.getName(name);
+      if (variableName == 'IMPORT_EXPERIMENTAL_MIRRORS_PADDING') {
+        return;
+      }
       if (value is MessageKind) {
-        kinds[MirrorSystem.getName(name)] = value;
+        kinds[variableName] = value;
       } else {
-        Expect.fail("Weird static field: '${MirrorSystem.getName(name)}'.");
+        Expect.fail("Weird static field: '${variableName}'.");
       }
     }
   });
diff --git a/tests/compiler/dart2js/mirrors_used_test.dart b/tests/compiler/dart2js/mirrors_used_test.dart
index 611dd98..2d26968 100644
--- a/tests/compiler/dart2js/mirrors_used_test.dart
+++ b/tests/compiler/dart2js/mirrors_used_test.dart
@@ -45,7 +45,8 @@
 
 void main() {
   Compiler compiler = compilerFor(
-      MEMORY_SOURCE_FILES, diagnosticHandler: expectOnlyVerboseInfo);
+      MEMORY_SOURCE_FILES, diagnosticHandler: expectOnlyVerboseInfo,
+      options: ['--enable-experimental-mirrors']);
   asyncTest(() => compiler.runCompiler(Uri.parse('memory:main.dart')).then((_) {
     print('');
     List generatedCode =
diff --git a/tests/compiler/dart2js/resolver_test.dart b/tests/compiler/dart2js/resolver_test.dart
index bc88820..653a12d 100644
--- a/tests/compiler/dart2js/resolver_test.dart
+++ b/tests/compiler/dart2js/resolver_test.dart
@@ -817,20 +817,7 @@
       compiler.resolver.resolve(mainElement);
       Expect.equals(0, compiler.warnings.length,
                     'Unexpected warnings: ${compiler.warnings}');
-      Expect.equals(0, compiler.errors.length,
-                    'Unexpected errors: ${compiler.errors}');
-    }, enableEnums: true),
-
-    MockCompiler.create((MockCompiler compiler) {
-      compiler.parseScript("""enum Enum {}
-                              main() { Enum e = Enum.A; }""");
-      FunctionElement mainElement = compiler.mainApp.find(MAIN);
-      compiler.resolver.resolve(mainElement);
-      Expect.equals(1, compiler.warnings.length,
-                    'Unexpected warnings: ${compiler.warnings}');
-      Expect.equals(MessageKind.MEMBER_NOT_FOUND,
-                    compiler.warnings[0].message.kind);
-      Expect.equals(0, compiler.errors.length,
+      Expect.equals(1, compiler.errors.length,
                     'Unexpected errors: ${compiler.errors}');
     }, enableEnums: true),
 
@@ -870,7 +857,7 @@
     }, enableEnums: true),
 
     MockCompiler.create((MockCompiler compiler) {
-      compiler.parseScript("""enum Enum {}
+      compiler.parseScript("""enum Enum { A }
                               main() { new Enum(0, ''); }""");
       FunctionElement mainElement = compiler.mainApp.find(MAIN);
       compiler.resolver.resolve(mainElement);
@@ -883,7 +870,7 @@
     }, enableEnums: true),
 
     MockCompiler.create((MockCompiler compiler) {
-      compiler.parseScript("""enum Enum {}
+      compiler.parseScript("""enum Enum { A }
                               main() { const Enum(0, ''); }""");
       FunctionElement mainElement = compiler.mainApp.find(MAIN);
       compiler.resolver.resolve(mainElement);
diff --git a/tests/compiler/dart2js/type_checker_test.dart b/tests/compiler/dart2js/type_checker_test.dart
index c7ab34b..f00a0d7 100644
--- a/tests/compiler/dart2js/type_checker_test.dart
+++ b/tests/compiler/dart2js/type_checker_test.dart
@@ -29,6 +29,7 @@
                 testWhile,
                 testTry,
                 testSwitch,
+                testEnumSwitch,
                 testOperators,
                 testConstructorInvocationArgumentCount,
                 testConstructorInvocationArgumentTypes,
@@ -169,6 +170,89 @@
         warnings: [NOT_ASSIGNABLE, NOT_ASSIGNABLE]);
 }
 
+testEnumSwitch(MockCompiler compiler) {
+  String DECLARATIONS = """
+enum Enum { A, B, C }
+""";
+
+  check(String code, {warnings}) {
+    MockCompiler compiler = new MockCompiler.internal(enableEnums: true);
+    return compiler.init(DECLARATIONS).then((_) {
+      analyze(compiler, code, warnings: warnings, flushDeferred: true);
+    });
+  }
+
+  check("""
+switch (Enum.A) {
+default: break;
+}""");
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+default: break;
+}""");
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+case Enum.B: break;
+default: break;
+}""");
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+case Enum.B: break;
+case Enum.C: break;
+default: break;
+}""");
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+case Enum.B: break;
+case Enum.C: break;
+}""");
+
+  check("""
+switch (Enum.A) {
+case Enum.B: break;
+case Enum.C: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+case Enum.C: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+case Enum.B: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+case Enum.A: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+case Enum.B: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+case Enum.C: break;
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+
+  check("""
+switch (Enum.A) {
+}""", warnings: MessageKind.MISSING_ENUM_CASES);
+}
+
 testOperators(MockCompiler compiler) {
   check(String code, {warnings}) {
     analyze(compiler, code, warnings: warnings);
@@ -2061,7 +2145,8 @@
  */
 analyze(MockCompiler compiler,
         String text,
-        {errors, warnings, List hints, List infos}) {
+        {errors, warnings, List hints, List infos,
+         bool flushDeferred: false}) {
   if (warnings == null) warnings = [];
   if (warnings is !List) warnings = [warnings];
   if (errors == null) errors = [];
@@ -2078,10 +2163,14 @@
     new CompilationUnitElementX(new Script(null, null, null), compiler.mainApp);
   Element function = new MockElement(compilationUnit);
   TreeElements elements = compiler.resolveNodeStatement(node, function);
+  compiler.enqueuer.resolution.emptyDeferredTaskQueue();
   TypeCheckerVisitor checker = new TypeCheckerVisitor(
       compiler, elements, compiler.types);
   compiler.clearMessages();
   checker.analyze(node);
+  if (flushDeferred) {
+    compiler.enqueuer.resolution.emptyDeferredTaskQueue();
+  }
   compareWarningKinds(text, warnings, compiler.warnings);
   compareWarningKinds(text, errors, compiler.errors);
   if (hints != null) compareWarningKinds(text, hints, compiler.hints);
diff --git a/tests/language/enum_test.dart b/tests/language/enum_test.dart
new file mode 100644
index 0000000..84daba1
--- /dev/null
+++ b/tests/language/enum_test.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2014, 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.
+
+// SharedOptions=--enable-enum
+
+import 'package:expect/expect.dart';
+
+enum Enum1 { _ }
+enum Enum2 { A }
+enum Enum3 { B, C }
+enum Enum4 { D, E, }
+enum Enum5 { F, G, H }
+
+main() {
+  Expect.equals('Enum1._', Enum1._.toString());
+  Expect.equals(0, Enum1._.index);
+  Expect.listEquals([Enum1._], Enum1.values);
+  Enum1.values.forEach(test1);
+
+  Expect.equals('Enum2.A', Enum2.A.toString());
+  Expect.equals(0, Enum2.A.index);
+  Expect.listEquals([Enum2.A], Enum2.values);
+  Enum2.values.forEach(test2);
+
+  Expect.equals('Enum3.B', Enum3.B.toString());
+  Expect.equals('Enum3.C', Enum3.C.toString());
+  Expect.equals(0, Enum3.B.index);
+  Expect.equals(1, Enum3.C.index);
+  Expect.listEquals([Enum3.B, Enum3.C], Enum3.values);
+  Enum3.values.forEach(test3);
+
+  Expect.equals('Enum4.D', Enum4.D.toString());
+  Expect.equals('Enum4.E', Enum4.E.toString());
+  Expect.equals(0, Enum4.D.index);
+  Expect.equals(1, Enum4.E.index);
+  Expect.listEquals([Enum4.D, Enum4.E], Enum4.values);
+  Enum4.values.forEach(test4);
+
+  Expect.equals('Enum5.F', Enum5.F.toString());
+  Expect.equals('Enum5.G', Enum5.G.toString());
+  Expect.equals('Enum5.H', Enum5.H.toString());
+  Expect.equals(0, Enum5.F.index);
+  Expect.equals(1, Enum5.G.index);
+  Expect.equals(2, Enum5.H.index);
+  Expect.listEquals([Enum5.F, Enum5.G, Enum5.H], Enum5.values);
+  Enum5.values.forEach(test5);
+}
+
+test1(Enum1 e) {
+  int index;
+  switch (e) {
+    case Enum1._:
+      index = 0;
+      break;
+  }
+  Expect.equals(e.index, index);
+}
+
+test2(Enum2 e) {
+  int index;
+  switch (e) {
+    case Enum2.A:
+      index = 0;
+      break;
+  }
+  Expect.equals(e.index, index);
+}
+
+test3(Enum3 e) {
+  int index;
+  switch (e) {
+    case Enum3.C:
+      index = 1;
+      break;
+    case Enum3.B:
+      index = 0;
+      break;
+  }
+  Expect.equals(e.index, index);
+}
+
+test4(Enum4 e) {
+  int index;
+  switch (e) {
+    case Enum4.D:
+      index = 0;
+      break;
+    case Enum4.E:
+      index = 1;
+      break;
+  }
+  Expect.equals(e.index, index);
+}
+
+test5(Enum5 e) {
+  int index;
+  switch (e) {
+    case Enum5.H:
+      index = 2;
+      break;
+    case Enum5.F:
+      index = 0;
+      break;
+    case Enum5.G:
+      index = 1;
+      break;
+  }
+  Expect.equals(e.index, index);
+}
\ No newline at end of file
diff --git a/tests/language/language.status b/tests/language/language.status
index 1e35edc..5d7549a 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -7,6 +7,7 @@
 
 [ $compiler == none ]
 built_in_identifier_prefix_test: Fail # Issue 6970
+enum_test: Fail # Issue 21416
 
 # These bugs refer currently ongoing language discussions.
 constructor_initializer_test/none: Fail # Issue 12633
@@ -49,6 +50,7 @@
 deferred_closurize_load_library_test: Fail  # Issue 17523
 deferred_inlined_test: Fail  # Issue 17523
 deferred_optimized_test: Fail  # Issue 17523
+enum_test: Crash # Issue 21416/21417
 
 override_inheritance_mixed_test/08: Fail # Issue 18124
 override_inheritance_mixed_test/09: Fail # Issue 18124
diff --git a/tools/VERSION b/tools/VERSION
index 9146f20..bfc0b1c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 8
 PATCH 0
 PRERELEASE 4
-PRERELEASE_PATCH 2
+PRERELEASE_PATCH 3