Version 0.5.20.2

svn merge -r 24148:24149 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@24160 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_impl.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_impl.dart
new file mode 100644
index 0000000..1898ad1
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_impl.dart
@@ -0,0 +1,332 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A library for code coverage support for Dart.
+library runtime.coverage.impl;
+
+import 'dart:async';
+import 'dart:collection' show SplayTreeMap;
+import 'dart:io';
+import 'dart:json' as json;
+
+import 'package:pathos/path.dart' as pathos;
+
+import 'package:analyzer_experimental/src/generated/source.dart' show Source, SourceRange;
+import 'package:analyzer_experimental/src/generated/scanner.dart' show StringScanner;
+import 'package:analyzer_experimental/src/generated/parser.dart' show Parser;
+import 'package:analyzer_experimental/src/generated/ast.dart';
+import 'package:analyzer_experimental/src/generated/engine.dart' show RecordingErrorListener;
+
+import '../log.dart' as log;
+import 'models.dart';
+import 'utils.dart';
+
+/// Run the [targetPath] with code coverage rewriting.
+/// Redirects stdandard process streams.
+/// On process exit dumps coverage statistics into the [outPath].
+void runServerApplication(String targetPath, String outPath) {
+  var targetFolder = pathos.dirname(targetPath);
+  var targetName = pathos.basename(targetPath);
+  new CoverageServer(targetFolder, targetPath, outPath)
+      .start()
+      .then((port) {
+        var options = new Options();
+        var targetArgs = ['http://127.0.0.1:$port/$targetName'];
+        var dartExecutable = options.executable;
+        Process.start(dartExecutable, targetArgs).then((Process process) {
+          process.exitCode.then(exit);
+          // Redirect process streams.
+          stdin.pipe(process.stdin);
+          process.stdout.pipe(stdout);
+          process.stderr.pipe(stderr);
+        });
+      });
+}
+
+
+/// Abstract server to listen requests and serve files, may be rewriting them.
+abstract class RewriteServer {
+  final String basePath;
+  int port;
+
+  RewriteServer(this.basePath);
+
+  /// Runs the HTTP server on the ephemeral port and returns [Future] with it.
+  Future<int> start() {
+    return HttpServer.bind('127.0.0.1', 0).then((server) {
+      port = server.port;
+      log.info('RewriteServer is listening at: $port.');
+      server.listen((request) {
+        if (request.method == 'GET') {
+          handleGetRequest(request);
+        }
+        if (request.method == 'POST') {
+          handlePostRequest(request);
+        }
+      });
+      return port;
+    });
+  }
+
+  handlePostRequest(HttpRequest request);
+
+  handleGetRequest(HttpRequest request) {
+    var response = request.response;
+    // Prepare path.
+    var path = basePath + '/' + request.uri.path;
+    path = pathos.normalize(path);
+    log.info('[$path] Requested.');
+    // May be serve using just path.
+    {
+      var content = rewritePathContent(path);
+      if (content != null) {
+        log.info('[$path] Request served by path.');
+        response.write(content);
+        response.close();
+        return;
+      }
+    }
+    // Serve from file.
+    log.info('[$path] Serving file.');
+    var file = new File(path);
+    file.exists().then((found) {
+      if (found) {
+        // May be this files should be sent as is.
+        if (!shouldRewriteFile(path)) {
+          sendFile(request, file);
+          return;
+        }
+        // Rewrite content of the file.
+        file.readAsString().then((content) {
+          log.finest('[$path] Done reading ${content.length} characters.');
+          content = rewriteFileContent(path, content);
+          log.fine('[$path] Rewritten.');
+          response.write(content);
+          response.close();
+        });
+      } else {
+        log.severe('[$path] File not found.');
+        response.statusCode = HttpStatus.NOT_FOUND;
+        response.close();
+      }
+    });
+  }
+
+  void sendFile(HttpRequest request, File file) {
+    file.fullPath().then((fullPath) {
+      file.openRead()
+        .pipe(request.response)
+        .catchError((e) {});
+    });
+  }
+
+  bool shouldRewriteFile(String path);
+
+  /// Subclasses implement this method to rewrite the provided [code] of the
+  /// file with [path]. Returns some content or `null` if file content
+  /// should be requested.
+  String rewritePathContent(String path);
+
+  /// Subclasses implement this method to rewrite the provided [code] of the
+  /// file with [path].
+  String rewriteFileContent(String path, String code);
+}
+
+
+/// Server that rewrites Dart code so that it reports execution of statements
+/// and other nodes.
+class CoverageServer extends RewriteServer {
+  final appInfo = new AppInfo();
+  final String targetPath;
+  final String outPath;
+
+  CoverageServer(String basePath, this.targetPath, this.outPath)
+      : super(basePath);
+
+  void handlePostRequest(HttpRequest request) {
+    var id = 0;
+    var executedIds = new Set<int>();
+    request.listen((data) {
+      log.fine('Received statistics, ${data.length} bytes.');
+      while (true) {
+        var listIndex = id ~/ 8;
+        if (listIndex >= data.length) break;
+        var bitIndex = id % 8;
+        if ((data[listIndex] & (1 << bitIndex)) != 0) {
+          executedIds.add(id);
+        }
+        id++;
+      }
+    }).onDone(() {
+      log.fine('Received all statistics.');
+      var sb = new StringBuffer();
+      appInfo.write(sb, executedIds);
+      new File(outPath).writeAsString(sb.toString());
+      log.fine('Results are written to $outPath.');
+      request.response.close();
+    });
+  }
+
+  String rewritePathContent(String path) {
+    if (path.endsWith('__coverage_lib.dart')) {
+      String implPath = pathos.joinAll([
+          pathos.dirname(new Options().script),
+          '..', 'lib', 'src', 'services', 'runtime', 'coverage',
+          'coverage_lib.dart']);
+      var content = new File(implPath).readAsStringSync();
+      content = content.replaceAll('0; // replaced during rewrite', '$port;');
+      return content;
+    }
+    return null;
+  }
+
+  bool shouldRewriteFile(String path) {
+    if (pathos.extension(path).toLowerCase() != '.dart') return false;
+    // Rewrite target itself, only to send statistics.
+    if (path == targetPath) {
+      return true;
+    }
+    // TODO(scheglov) use configuration
+    if (path.contains('/packages/analyzer_experimental/')) {
+      return true;
+    }
+    return false;
+  }
+
+  String rewriteFileContent(String path, String code) {
+    var unit = _parseCode(code);
+    log.finest('[$path] Parsed.');
+    var injector = new CodeInjector(code);
+    // Inject imports.
+    var directives = unit.directives;
+    if (directives.isNotEmpty && directives[0] is LibraryDirective) {
+      injector.inject(directives[0].end,
+          'import "package:unittest/unittest.dart" as __cc_ut;'
+          'import "http://127.0.0.1:$port/__coverage_lib.dart" as __cc;');
+    }
+    // Inject statistics sender.
+    var isTargetScript = path == targetPath;
+    if (isTargetScript) {
+      for (var node in unit.declarations) {
+        if (node is FunctionDeclaration) {
+          var body = node.functionExpression.body;
+          if (node.name.name == 'main' && body is BlockFunctionBody) {
+            injector.inject(node.offset,
+                'class __CCC extends __cc_ut.Configuration {'
+                '  void onDone(bool success) {'
+                '    __cc.postStatistics();'
+                '    super.onDone(success);'
+                '  }'
+                '}');
+            injector.inject(
+                body.offset + 1,
+                '__cc_ut.unittestConfiguration = new __CCC();');
+          }
+        }
+      }
+    }
+    // Inject touch() invocations.
+    if (!isTargetScript) {
+      appInfo.enterUnit(path, code);
+      unit.accept(new InsertTouchInvocationsVisitor(appInfo, injector));
+    }
+    // Done.
+    return injector.getResult();
+  }
+
+  CompilationUnit _parseCode(String code) {
+    var source = null;
+    var errorListener = new RecordingErrorListener();
+    var parser = new Parser(source, errorListener);
+    var scanner = new StringScanner(source, code, errorListener);
+    var token = scanner.tokenize();
+    return parser.parseCompilationUnit(token);
+  }
+}
+
+
+/// The visitor that inserts `touch` method invocations.
+class InsertTouchInvocationsVisitor extends GeneralizingASTVisitor {
+  final AppInfo appInfo;
+  final CodeInjector injector;
+
+  InsertTouchInvocationsVisitor(this.appInfo, this.injector);
+
+  visitClassDeclaration(ClassDeclaration node) {
+    appInfo.enter('class', node.name.name);
+    super.visitClassDeclaration(node);
+    appInfo.leave();
+  }
+
+  visitConstructorDeclaration(ConstructorDeclaration node) {
+    var className = (node.parent as ClassDeclaration).name.name;
+    var constructorName;
+    if (node.name == null) {
+      constructorName = className;
+    } else {
+      constructorName = className + '.' + node.name.name;
+    }
+    appInfo.enter('constructor', constructorName);
+    super.visitConstructorDeclaration(node);
+    appInfo.leave();
+  }
+
+  visitMethodDeclaration(MethodDeclaration node) {
+    if (node.isAbstract) {
+      super.visitMethodDeclaration(node);
+    } else {
+      var kind;
+      if (node.isGetter) {
+        kind = 'getter';
+      } else if (node.isSetter) {
+        kind = 'setter';
+      } else {
+        kind = 'method';
+      }
+      appInfo.enter(kind, node.name.name);
+      super.visitMethodDeclaration(node);
+      appInfo.leave();
+    }
+  }
+
+  visitStatement(Statement node) {
+    insertTouch(node);
+    super.visitStatement(node);
+  }
+
+  void insertTouch(Statement node) {
+    if (node is Block) return;
+    if (node.parent is LabeledStatement) return;
+    if (node.parent is! Block) return;
+    // Inject 'touch' invocation.
+    var offset = node.offset;
+    var id = appInfo.addNode(node);
+    injector.inject(offset, '__cc.touch($id);');
+  }
+}
+
+
+/// Helper for injecting fragments into some existing code.
+class CodeInjector {
+  final String _code;
+  final offsetFragmentMap = new SplayTreeMap<int, String>();
+
+  CodeInjector(this._code);
+
+  void inject(int offset, String fragment) {
+    offsetFragmentMap[offset] = fragment;
+  }
+
+  String getResult() {
+    var sb = new StringBuffer();
+    var lastOffset = 0;
+    offsetFragmentMap.forEach((offset, fragment) {
+      sb.write(_code.substring(lastOffset, offset));
+      sb.write(fragment);
+      lastOffset = offset;
+    });
+    sb.write(_code.substring(lastOffset, _code.length));
+    return sb.toString();
+  }
+}
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_lib.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_lib.dart
new file mode 100644
index 0000000..8f1bac8
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_lib.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// This library is injected into the applications under coverage.
+library coverage_lib;
+
+import 'dart:io';
+import 'dart:typed_data';
+
+const PORT = 0; // replaced during rewrite
+final _executedIds = new Uint8List(1024 * 64);
+
+touch(int id) {
+  int listIndex = id ~/ 8;
+  int bitIndex = id % 8;
+  _executedIds[listIndex] |= 1 << bitIndex;
+}
+
+postStatistics() {
+  var httpClient = new HttpClient();
+  return httpClient.post('127.0.0.1', PORT, '/statistics')
+      .then((request) {
+        request.add(_executedIds);
+        return request.close();
+      });
+}
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/coverage/models.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/models.dart
new file mode 100644
index 0000000..463a0cd
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/models.dart
@@ -0,0 +1,168 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A library with code coverage models.
+library runtime.coverage.model;
+
+import 'dart:collection' show SplayTreeMap;
+
+import 'package:analyzer_experimental/src/generated/source.dart' show Source, SourceRange;
+import 'package:analyzer_experimental/src/generated/ast.dart' show ASTNode;
+
+import 'utils.dart';
+
+
+/// Contains information about the application.
+class AppInfo {
+  final nodeStack = new List<NodeInfo>();
+  final units = new List<UnitInfo>();
+  final pathToFile = new Map<String, UnitInfo>();
+  NodeInfo currentNode;
+  int nextId = 0;
+
+  void enterUnit(String path, String content) {
+    var unit = new UnitInfo(this, path, content);
+    units.add(unit);
+    currentNode = unit;
+  }
+
+  void enter(String kind, String name) {
+    nodeStack.add(currentNode);
+    currentNode = new NodeInfo(this, currentNode, kind, name);
+  }
+
+  void leave() {
+    currentNode = nodeStack.removeLast();
+  }
+
+  int addNode(ASTNode node) {
+    return currentNode.addNode(node);
+  }
+
+  void write(StringSink sink, Set<int> executedIds) {
+    sink.writeln('{');
+    units.fold(null, (prev, unit) {
+      if (prev != null) sink.writeln(',');
+      return unit..write(sink, executedIds, '  ');
+    });
+    sink.writeln();
+    sink.writeln('}');
+  }
+}
+
+/// Information about some node - unit, class, method, function.
+class NodeInfo {
+  final AppInfo appInfo;
+  final NodeInfo parent;
+  final String kind;
+  final String name;
+  final idToRange = new SplayTreeMap<int, SourceRange>();
+  final children = <NodeInfo>[];
+
+  NodeInfo(this.appInfo, this.parent, this.kind, this.name) {
+    if (parent != null) {
+      parent.children.add(this);
+    }
+  }
+
+  int addNode(ASTNode node) {
+    var id = appInfo.nextId++;
+    var range = new SourceRange(node.offset, node.length);
+    idToRange[id] = range;
+    return id;
+  }
+
+  void write(StringSink sink, Set<int> executedIds, String prefix) {
+    sink.writeln('$prefix"$name": {');
+    // Kind.
+    sink.writeln('$prefix  "kind": "$kind",');
+    // Print children.
+    if (children.isNotEmpty) {
+      sink.writeln('$prefix  "children": {');
+      children.fold(null, (prev, child) {
+        if (prev != null) sink.writeln(',');
+        return child..write(sink, executedIds, '$prefix    ');
+      });
+      sink.writeln();
+      sink.writeln('$prefix  }');
+    }
+    // Print source and line ranges.
+    if (children.isEmpty) {
+      sink.write('${prefix}  "ranges": [');
+      var rangePrinter = new RangePrinter(unit, sink, executedIds);
+      idToRange.forEach(rangePrinter.handle);
+      rangePrinter.printRange();
+      sink.writeln(']');
+    }
+    // Close this node.
+    sink.write('$prefix}');
+  }
+
+  UnitInfo get unit => parent.unit;
+}
+
+/// Helper for printing merged source/line intervals.
+class RangePrinter {
+  final UnitInfo unit;
+  final StringSink sink;
+  final Set<int> executedIds;
+
+  bool first = true;
+  int startId = -1;
+  int startOffset = -1;
+  int endId = -1;
+  int endOffset = -1;
+
+  RangePrinter(this.unit, this.sink, this.executedIds);
+
+  handle(int id, SourceRange range) {
+    if (executedIds.contains(id)) {
+      printRange();
+    } else {
+      if (endId == id - 1) {
+        endId = id;
+        endOffset = range.end;
+      } else {
+        startId = id;
+        endId = id;
+        startOffset = range.offset;
+        endOffset = range.end;
+      }
+    }
+  }
+
+  void printRange() {
+    if (endId == -1) return;
+    printSeparator();
+    var startLine = unit.getLine(startOffset);
+    var endLine = unit.getLine(endOffset);
+    sink.write('$startOffset,$endOffset,$startLine,$endLine');
+    startId = startOffset = startLine = -1;
+    endId = endOffset = endLine = -1;
+  }
+
+  void printSeparator() {
+    if (first) {
+      first = false;
+    } else {
+      sink.write(', ');
+    }
+  }
+}
+
+/// Contains information about the single unit of the application.
+class UnitInfo extends NodeInfo {
+  List<int> lineOffsets;
+
+  UnitInfo(AppInfo appInfo, String path, String content)
+      : super(appInfo, null, 'unit', path) {
+    lineOffsets = getLineOffsets(content);
+  }
+
+  UnitInfo get unit => this;
+
+  int getLine(int offset) {
+    return binarySearch(lineOffsets, (x) => x >= offset);
+  }
+}
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/coverage/utils.dart b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/utils.dart
new file mode 100644
index 0000000..3af2276
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/utils.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2013, 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 utils;
+
+
+List<int> getLineOffsets(String text) {
+  var offsets =  <int> [0];
+  var wasSR = false;
+  text.codeUnits.asMap().forEach((int i, int codeUnit) {
+    if (codeUnit == 13) {
+      wasSR = true;
+      return;
+    }
+    if (codeUnit == 10) {
+      if (wasSR) {
+        offsets.add(i - 1);
+      } else {
+        offsets.add(i);
+      }
+    }
+    wasSR = false;
+  });
+  return offsets;
+}
+
+/// Find the first entry in a sorted [list] that matches a monotonic predicate.
+/// Given a result `n`, that all items before `n` will not match, `n` matches,
+/// and all items after `n` match too. The result is -1 when there are no
+/// items, 0 when all items match, and list.length when none does.
+// TODO(scheglov) remove this function after dartbug.com/5624 is fixed.
+int binarySearch(List list, bool matches(item)) {
+  if (list.length == 0) return -1;
+  if (matches(list.first)) return 0;
+  if (!matches(list.last)) return list.length;
+  var min = 0;
+  var max = list.length - 1;
+  while (min < max) {
+    var half = min + ((max - min) ~/ 2);
+    if (matches(list[half])) {
+      max = half;
+    } else {
+      min = half + 1;
+    }
+  }
+  return max;
+}
diff --git a/pkg/analyzer_experimental/lib/src/services/runtime/log.dart b/pkg/analyzer_experimental/lib/src/services/runtime/log.dart
new file mode 100644
index 0000000..48d66d7
--- /dev/null
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/log.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, 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.
+
+/// Simple wrapper for [Logger] library.
+library runtime.log;
+
+import "package:logging/logging.dart";
+
+/** Log message at level [Level.FINEST]. */
+void finest(String message) => _logger.log(Level.FINEST, message);
+
+/** Log message at level [Level.FINER]. */
+void finer(String message) => _logger.log(Level.FINER, message);
+
+/** Log message at level [Level.FINE]. */
+void fine(String message) => _logger.log(Level.FINE, message);
+
+/** Log message at level [Level.CONFIG]. */
+void config(String message) => _logger.log(Level.CONFIG, message);
+
+/** Log message at level [Level.INFO]. */
+void info(String message) => _logger.log(Level.INFO, message);
+
+/** Log message at level [Level.WARNING]. */
+void warning(String message) => _logger.log(Level.WARNING, message);
+
+/** Log message at level [Level.SEVERE]. */
+void severe(String message) => _logger.log(Level.SEVERE, message);
+
+/** Log message at level [Level.SHOUT]. */
+void shout(String message) => _logger.log(Level.SHOUT, message);
+
+/// Specifies that all log records should be logged.
+void everything() {
+  _logger.level = Level.ALL;
+}
+
+/// Sends all log record to the console.
+void toConsole() {
+  _logger.onRecord.listen((LogRecord record) {
+    String levelString = record.level.toString();
+    while (levelString.length < 6) levelString += ' ';
+    print('${record.time}: ${levelString} ${record.message}');
+  });
+}
+
+/// The root [Logger].
+final Logger _logger = Logger.root;
diff --git a/pkg/args/lib/src/options.dart b/pkg/args/lib/src/options.dart
new file mode 100644
index 0000000..00c0ad8
--- /dev/null
+++ b/pkg/args/lib/src/options.dart
@@ -0,0 +1,47 @@
+library options;
+
+/**
+ * A command-line option. Includes both flags and options which take a value.
+ */
+class Option {
+  final String name;
+  final String abbreviation;
+  final List<String> allowed;
+  final defaultValue;
+  final Function callback;
+  final String help;
+  final Map<String, String> allowedHelp;
+  final bool isFlag;
+  final bool negatable;
+  final bool allowMultiple;
+
+  Option(this.name, this.abbreviation, this.help, this.allowed,
+      this.allowedHelp, this.defaultValue, this.callback, {this.isFlag,
+      this.negatable, this.allowMultiple: false}) {
+
+    if (name.isEmpty) {
+      throw new ArgumentError('Name cannot be empty.');
+    } else if (name.startsWith('-')) {
+      throw new ArgumentError('Name $name cannot start with "-".');
+    }
+
+    // Ensure name does not contain any invalid characters.
+    if (_invalidChars.hasMatch(name)) {
+      throw new ArgumentError('Name "$name" contains invalid characters.');
+    }
+
+    if (abbreviation != null) {
+      if (abbreviation.length != 1) {
+        throw new ArgumentError('Abbreviation must be null or have length 1.');
+      } else if(abbreviation == '-') {
+        throw new ArgumentError('Abbreviation cannot be "-".');
+      }
+
+      if (_invalidChars.hasMatch(abbreviation)) {
+        throw new ArgumentError('Abbreviation is an invalid character.');
+      }
+    }
+  }
+
+  static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]''');
+}
diff --git a/tests/corelib/uri_http_test.dart b/tests/corelib/uri_http_test.dart
new file mode 100644
index 0000000..354e052
--- /dev/null
+++ b/tests/corelib/uri_http_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+testHttpUri() {
+  void check(Uri uri, String expected) {
+    Expect.equals(expected, uri.toString());
+  }
+
+  check(new Uri.http("", ""), "http:");
+  check(new Uri.http("@:", ""), "http:");
+  check(new Uri.http("@:8080", ""), "http:");
+  check(new Uri.http("@host:", ""), "http://host");
+  check(new Uri.http("@host:", ""), "http://host");
+  check(new Uri.http("xxx:yyy@host:8080", ""), "http://xxx:yyy@host:8080");
+  check(new Uri.http("host", "a"), "http://host/a");
+  check(new Uri.http("host", "/a"), "http://host/a");
+  check(new Uri.http("host", "a/"), "http://host/a/");
+  check(new Uri.http("host", "/a/"), "http://host/a/");
+  check(new Uri.http("host", "a/b"), "http://host/a/b");
+  check(new Uri.http("host", "/a/b"), "http://host/a/b");
+  check(new Uri.http("host", "a/b/"), "http://host/a/b/");
+  check(new Uri.http("host", "/a/b/"), "http://host/a/b/");
+  check(new Uri.http("host", "a b"), "http://host/a%20b");
+  check(new Uri.http("host", "/a b"), "http://host/a%20b");
+  check(new Uri.http("host", "/a b/"), "http://host/a%20b/");
+  check(new Uri.http("host", "/a%2F"), "http://host/a%252F");
+  check(new Uri.http("host", "/a%2F/"), "http://host/a%252F/");
+  check(new Uri.http("host", "/a/b", { "c": "d" }), "http://host/a/b?c=d");
+  check(new Uri.http("host",
+                     "/a/b", { "c=": "&d" }), "http://host/a/b?c%3D=%26d");
+}
+
+testHttpsUri() {
+  void check(Uri uri, String expected) {
+    Expect.equals(expected, uri.toString());
+  }
+
+  check(new Uri.https("", ""), "https:");
+  check(new Uri.https("@:", ""), "https:");
+  check(new Uri.https("@:8080", ""), "https:");
+  check(new Uri.https("@host:", ""), "https://host");
+  check(new Uri.https("@host:", ""), "https://host");
+  check(new Uri.https("xxx:yyy@host:8080", ""), "https://xxx:yyy@host:8080");
+  check(new Uri.https("host", "a"), "https://host/a");
+  check(new Uri.https("host", "/a"), "https://host/a");
+  check(new Uri.https("host", "a/"), "https://host/a/");
+  check(new Uri.https("host", "/a/"), "https://host/a/");
+  check(new Uri.https("host", "a/b"), "https://host/a/b");
+  check(new Uri.https("host", "/a/b"), "https://host/a/b");
+  check(new Uri.https("host", "a/b/"), "https://host/a/b/");
+  check(new Uri.https("host", "/a/b/"), "https://host/a/b/");
+  check(new Uri.https("host", "a b"), "https://host/a%20b");
+  check(new Uri.https("host", "/a b"), "https://host/a%20b");
+  check(new Uri.https("host", "/a b/"), "https://host/a%20b/");
+  check(new Uri.https("host", "/a%2F"), "https://host/a%252F");
+  check(new Uri.https("host", "/a%2F/"), "https://host/a%252F/");
+  check(new Uri.https("host", "/a/b", { "c": "d" }), "https://host/a/b?c=d");
+  check(new Uri.https("host",
+                      "/a/b", { "c=": "&d" }), "https://host/a/b?c%3D=%26d");
+}
+
+main() {
+  testHttpUri();
+  testHttpsUri();
+}
diff --git a/tests/lib/mirrors/intercepted_class_test.dart b/tests/lib/mirrors/intercepted_class_test.dart
new file mode 100644
index 0000000..a17b7e9
--- /dev/null
+++ b/tests/lib/mirrors/intercepted_class_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, 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.
+
+// Ensure that classes handled specially by dart2js can be reflected on.
+
+library test.intercepted_class_test;
+
+import 'dart:mirrors';
+
+import 'reflect_model_test.dart' show stringify, expect;
+
+checkClassMirror(ClassMirror cls, String name) {
+  expect('s($name)', cls.simpleName);
+  var variables = new Map();
+  cls.variables.forEach((Symbol key, VariableMirror value) {
+    if (!value.isStatic && !value.isPrivate) {
+      variables[key] = value;
+    }
+  });
+  expect('{}', variables);
+}
+
+main() {
+  checkClassMirror(reflectClass(String), 'String');
+  checkClassMirror(reflectClass(int), 'int');
+  checkClassMirror(reflectClass(double), 'double');
+  checkClassMirror(reflectClass(num), 'num');
+  checkClassMirror(reflectClass(bool), 'bool');
+  checkClassMirror(reflectClass(List), 'List');
+}
diff --git a/tests/lib/mirrors/intercepted_object_test.dart b/tests/lib/mirrors/intercepted_object_test.dart
new file mode 100644
index 0000000..26016e1
--- /dev/null
+++ b/tests/lib/mirrors/intercepted_object_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2013, 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.
+
+// Ensure that objects handled specially by dart2js can be reflected on.
+
+library test.intercepted_class_test;
+
+import 'dart:mirrors';
+
+import 'reflect_model_test.dart' show stringify, expect;
+
+import 'intercepted_class_test.dart' show checkClassMirror;
+
+checkObject(object, String name) {
+  checkClassMirror(reflect(object).type, name);
+}
+
+main() {
+  checkObject('', 'String');
+  checkObject(1, 'int');
+  checkObject(1.5, 'double');
+  checkObject(true, 'bool');
+  checkObject(false, 'bool');
+  checkObject([], 'List');
+}
diff --git a/tests/lib/mirrors/is_odd_test.dart b/tests/lib/mirrors/is_odd_test.dart
new file mode 100644
index 0000000..c9fdde1
--- /dev/null
+++ b/tests/lib/mirrors/is_odd_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2013, 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 otherwise unused intercepted methods are reified correctly.  This
+/// was a bug in dart2js.
+library test.is_odd_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+main() {
+  Expect.isTrue(reflect(1).getField(new Symbol('isOdd')).reflectee);
+  Expect.isFalse(reflect(2).getField(new Symbol('isOdd')).reflectee);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 1f38ac9..c06980b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 5
 BUILD 20
-PATCH 1
+PATCH 2