Version 0.5.20.3

svn merge -r 24213:24214 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

This fixes http://dartbug.com/11375

R=kmillikin@google.com

Review URL: https://codereview.chromium.org//17494002

git-svn-id: http://dart.googlecode.com/svn/trunk@24216 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
index 1898ad1..bc6b7cf 100644
--- a/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_impl.dart
+++ b/pkg/analyzer_experimental/lib/src/services/runtime/coverage/coverage_impl.dart
@@ -28,20 +28,20 @@
 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);
-        });
-      });
+  var server = new CoverageServer(targetFolder, targetPath, outPath);
+  server.start().then((port) {
+    var options = new Options();
+    var targetArgs = ['http://127.0.0.1:$port/$targetName'];
+    var dartExecutable = options.executable;
+    return Process.start(dartExecutable, targetArgs);
+  }).then((process) {
+    stdin.pipe(process.stdin);
+    process.stdout.pipe(stdout);
+    process.stderr.pipe(stderr);
+    return process.exitCode;
+  }).then(exit).catchError((e) {
+    log.severe('Error starting $targetPath. $e');
+  });
 }
 
 
@@ -69,13 +69,12 @@
     });
   }
 
-  handlePostRequest(HttpRequest request);
+  void handlePostRequest(HttpRequest request);
 
-  handleGetRequest(HttpRequest request) {
+  void handleGetRequest(HttpRequest request) {
     var response = request.response;
     // Prepare path.
-    var path = basePath + '/' + request.uri.path;
-    path = pathos.normalize(path);
+    var path = getFilePath(request.uri);
     log.info('[$path] Requested.');
     // May be serve using just path.
     {
@@ -94,30 +93,38 @@
       if (found) {
         // May be this files should be sent as is.
         if (!shouldRewriteFile(path)) {
-          sendFile(request, file);
-          return;
+          return sendFile(request, file);
         }
         // Rewrite content of the file.
-        file.readAsString().then((content) {
+        return 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();
+          return response.close();
         });
       } else {
         log.severe('[$path] File not found.');
         response.statusCode = HttpStatus.NOT_FOUND;
-        response.close();
+        return response.close();
       }
+    }).catchError((e) {
+      log.severe('[$path] $e.');
+      response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
+      return response.close();
     });
   }
 
-  void sendFile(HttpRequest request, File file) {
+  String getFilePath(Uri uri) {
+    var path = uri.path;
+    path = pathos.joinAll(uri.pathSegments);
+    path = pathos.join(basePath, path);
+    return pathos.normalize(path);
+  }
+
+  Future sendFile(HttpRequest request, File file) {
     file.fullPath().then((fullPath) {
-      file.openRead()
-        .pipe(request.response)
-        .catchError((e) {});
+      return file.openRead().pipe(request.response);
     });
   }
 
@@ -134,6 +141,18 @@
 }
 
 
+/// Here `CCC` means 'code coverage configuration'.
+const TEST_UNIT_CCC = '''
+class __CCC extends __cc_ut.Configuration {
+  void onDone(bool success) {
+    __cc.postStatistics();
+    super.onDone(success);
+  }
+}''';
+
+const TEST_UNIT_CCC_SET = '__cc_ut.unittestConfiguration = new __CCC();';
+
+
 /// Server that rewrites Dart code so that it reports execution of statements
 /// and other nodes.
 class CoverageServer extends RewriteServer {
@@ -160,11 +179,14 @@
       }
     }).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();
+      var buffer = new StringBuffer();
+      appInfo.write(buffer, executedIds);
+      new File(outPath).writeAsString(buffer.toString()).then((_) {
+        return request.response.close();
+      }).catchError((e) {
+        log.severe('Error in receiving statistics $e.');
+        return request.response.close();
+      });
     });
   }
 
@@ -175,8 +197,7 @@
           '..', '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 content.replaceAll('0; // replaced during rewrite', '$port;');
     }
     return null;
   }
@@ -188,10 +209,7 @@
       return true;
     }
     // TODO(scheglov) use configuration
-    if (path.contains('/packages/analyzer_experimental/')) {
-      return true;
-    }
-    return false;
+    return path.contains('/packages/analyzer_experimental/');
   }
 
   String rewriteFileContent(String path, String code) {
@@ -212,16 +230,8 @@
         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();');
+            injector.inject(node.offset, TEST_UNIT_CCC);
+            injector.inject(body.offset + 1, TEST_UNIT_CCC_SET);
           }
         }
       }
diff --git a/pkg/args/test/parse_all_test.dart b/pkg/args/test/parse_all_test.dart
new file mode 100644
index 0000000..a921046
--- /dev/null
+++ b/pkg/args/test/parse_all_test.dart
@@ -0,0 +1,62 @@
+// 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 parse_all_test;
+
+import 'package:unittest/unittest.dart';
+import 'package:args/args.dart';
+
+main() {
+  group('ArgParser.parse() starting with a non-option', () {
+    test('followed by flag', () {
+      var parser = new ArgParser()..addFlag('flag');
+      var args = ['A', '--flag'];
+
+      var results = parser.parse(args);
+      expect(results['flag'], isFalse);
+      expect(results.rest, orderedEquals(args));
+    });
+
+    test('followed by option', () {
+      var parser = new ArgParser()..addOption('opt');
+      var args = ['A', '--opt'];
+
+      var results = parser.parse(args);
+      expect(results['opt'], isNull);
+      expect(results.rest, orderedEquals(args));
+    });
+
+    test('followed by option and value', () {
+      var parser = new ArgParser()..addOption('opt');
+      var args = ['A', '--opt', 'V'];
+
+      var results = parser.parse(args);
+      expect(results['opt'], isNull);
+      expect(results.rest, orderedEquals(args));
+    });
+
+    test('followed by unknown flag', () {
+      var parser = new ArgParser();
+      var args = ['A', '--xflag'];
+      var results = parser.parse(args);
+      expect(results.rest, orderedEquals(args));
+    });
+
+    test('followed by unknown option and value', () {
+      var parser = new ArgParser();
+      var args = ['A', '--xopt', 'V'];
+      var results = parser.parse(args);
+      expect(results.rest, orderedEquals(args));
+    });
+
+    test('followed by command', () {
+      var parser = new ArgParser()..addCommand('com');
+      var args = ['A', 'com'];
+
+      var results = parser.parse(args);
+      expect(results.command, isNull);
+      expect(results.rest, orderedEquals(args));
+    });
+  });
+}
diff --git a/pkg/mdv_observe/test/path_observer_test.dart b/pkg/mdv_observe/test/path_observer_test.dart
new file mode 100644
index 0000000..4222f73
--- /dev/null
+++ b/pkg/mdv_observe/test/path_observer_test.dart
@@ -0,0 +1,249 @@
+// 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.
+
+import 'package:mdv_observe/mdv_observe.dart';
+import 'package:unittest/unittest.dart';
+
+// This file contains code ported from:
+// https://github.com/rafaelw/ChangeSummary/blob/master/tests/test.js
+
+main() {
+  group('PathObserver', observePathTests);
+}
+
+observePath(obj, path) => new PathObserver(obj, path);
+
+sym(x) => new Symbol(x);
+
+toSymbolMap(Map map) {
+  var result = new ObservableMap.linked();
+  map.forEach((key, value) {
+    if (value is Map) value = toSymbolMap(value);
+    result[new Symbol(key)] = value;
+  });
+  return result;
+}
+
+observePathTests() {
+
+  test('Degenerate Values', () {
+    expect(observePath(null, '').value, null);
+    expect(observePath(123, '').value, 123);
+    expect(observePath(123, 'foo.bar.baz').value, null);
+
+    // shouldn't throw:
+    observePath(123, '').values.listen((_) {}).cancel();
+    observePath(null, '').value = null;
+    observePath(123, '').value = 42;
+    observePath(123, 'foo.bar.baz').value = 42;
+
+    var foo = {};
+    expect(observePath(foo, '').value, foo);
+
+    foo = new Object();
+    expect(observePath(foo, '').value, foo);
+
+    expect(observePath(foo, 'a/3!').value, null);
+  });
+
+  test('get value at path ObservableBox', () {
+    var obj = new ObservableBox(new ObservableBox(new ObservableBox(1)));
+
+    expect(observePath(obj, '').value, obj);
+    expect(observePath(obj, 'value').value, obj.value);
+    expect(observePath(obj, 'value.value').value, obj.value.value);
+    expect(observePath(obj, 'value.value.value').value, 1);
+
+    obj.value.value.value = 2;
+    expect(observePath(obj, 'value.value.value').value, 2);
+
+    obj.value.value = new ObservableBox(3);
+    expect(observePath(obj, 'value.value.value').value, 3);
+
+    obj.value = new ObservableBox(4);
+    expect(observePath(obj, 'value.value.value').value, null);
+    expect(observePath(obj, 'value.value').value, 4);
+  });
+
+
+  test('get value at path ObservableMap', () {
+    var obj = toSymbolMap({'a': {'b': {'c': 1}}});
+
+    expect(observePath(obj, '').value, obj);
+    expect(observePath(obj, 'a').value, obj[sym('a')]);
+    expect(observePath(obj, 'a.b').value, obj[sym('a')][sym('b')]);
+    expect(observePath(obj, 'a.b.c').value, 1);
+
+    obj[sym('a')][sym('b')][sym('c')] = 2;
+    expect(observePath(obj, 'a.b.c').value, 2);
+
+    obj[sym('a')][sym('b')] = toSymbolMap({'c': 3});
+    expect(observePath(obj, 'a.b.c').value, 3);
+
+    obj[sym('a')] = toSymbolMap({'b': 4});
+    expect(observePath(obj, 'a.b.c').value, null);
+    expect(observePath(obj, 'a.b').value, 4);
+  });
+
+  test('set value at path', () {
+    var obj = toSymbolMap({});
+    observePath(obj, 'foo').value = 3;
+    expect(obj[sym('foo')], 3);
+
+    var bar = toSymbolMap({ 'baz': 3 });
+    observePath(obj, 'bar').value = bar;
+    expect(obj[sym('bar')], bar);
+
+    observePath(obj, 'bar.baz.bat').value = 'not here';
+    expect(observePath(obj, 'bar.baz.bat').value, null);
+  });
+
+  test('set value back to same', () {
+    var obj = toSymbolMap({});
+    var path = observePath(obj, 'foo');
+    var values = [];
+    path.values.listen((v) { values.add(v); });
+
+    path.value = 3;
+    expect(obj[sym('foo')], 3);
+    expect(path.value, 3);
+
+    observePath(obj, 'foo').value = 2;
+    deliverChangeRecords();
+    expect(path.value, 2);
+    expect(observePath(obj, 'foo').value, 2);
+
+    observePath(obj, 'foo').value = 3;
+    deliverChangeRecords();
+    expect(path.value, 3);
+
+    deliverChangeRecords();
+    expect(values, [2, 3]);
+  });
+
+  test('Observe and Unobserve - Paths', () {
+    var arr = toSymbolMap({});
+
+    arr[sym('foo')] = 'bar';
+    var fooValues = [];
+    var fooPath = observePath(arr, 'foo');
+    var fooSub = fooPath.values.listen((v) {
+      fooValues.add(v);
+    });
+    arr[sym('foo')] = 'baz';
+    arr[sym('bat')] = 'bag';
+    var batValues = [];
+    var batPath = observePath(arr, 'bat');
+    var batSub = batPath.values.listen((v) {
+      batValues.add(v);
+    });
+
+    deliverChangeRecords();
+    expect(fooValues, ['baz']);
+    expect(batValues, []);
+
+    arr[sym('foo')] = 'bar';
+    fooSub.cancel();
+    arr[sym('bat')] = 'boo';
+    batSub.cancel();
+    arr[sym('bat')] = 'boot';
+
+    deliverChangeRecords();
+    expect(fooValues, ['baz']);
+    expect(batValues, []);
+  });
+
+  test('Path Value With Indices', () {
+    var model = toObservable([]);
+    observePath(model, '0').values.listen(expectAsync1((v) {
+      expect(v, 123);
+    }));
+    model.add(123);
+  });
+
+  test('Path Observation', () {
+    var model = new TestModel(const Symbol('a'),
+        new TestModel(const Symbol('b'),
+            new TestModel(const Symbol('c'), 'hello, world')));
+
+    var path = observePath(model, 'a.b.c');
+    var lastValue = null;
+    var sub = path.values.listen((v) { lastValue = v; });
+
+    model.value.value.value = 'hello, mom';
+
+    expect(lastValue, null);
+    deliverChangeRecords();
+    expect(lastValue, 'hello, mom');
+
+    model.value.value = new TestModel(const Symbol('c'), 'hello, dad');
+    deliverChangeRecords();
+    expect(lastValue, 'hello, dad');
+
+    model.value = new TestModel(const Symbol('b'),
+        new TestModel(const Symbol('c'), 'hello, you'));
+    deliverChangeRecords();
+    expect(lastValue, 'hello, you');
+
+    model.value.value = 1;
+    deliverChangeRecords();
+    expect(lastValue, null);
+
+    // Stop observing
+    sub.cancel();
+
+    model.value.value = new TestModel(const Symbol('c'),
+        'hello, back again -- but not observing');
+    deliverChangeRecords();
+    expect(lastValue, null);
+
+    // Resume observing
+    sub = path.values.listen((v) { lastValue = v; });
+
+    model.value.value.value = 'hello. Back for reals';
+    deliverChangeRecords();
+    expect(lastValue, 'hello. Back for reals');
+  });
+
+  test('observe map', () {
+    var model = toSymbolMap({'a': 1});
+    var path = observePath(model, 'a');
+
+    var values = [path.value];
+    var sub = path.values.listen((v) { values.add(v); });
+    expect(values, [1]);
+
+    model[sym('a')] = 2;
+    deliverChangeRecords();
+    expect(values, [1, 2]);
+
+    sub.cancel();
+    model[sym('a')] = 3;
+    deliverChangeRecords();
+    expect(values, [1, 2]);
+  });
+}
+
+class TestModel extends ObservableBase {
+  final Symbol fieldName;
+  var _value;
+
+  TestModel(this.fieldName, [initialValue]) : _value = initialValue;
+
+  get value => _value;
+
+  void set value(newValue) {
+    _value = notifyPropertyChange(fieldName, _value, newValue);
+  }
+
+  getValueWorkaround(key) {
+    if (key == fieldName) return value;
+    return null;
+  }
+  void setValueWorkaround(key, newValue) {
+    if (key == fieldName) value = newValue;
+  }
+
+  toString() => '#<$runtimeType $fieldName: $_value>';
+}
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index c8ae244..ed23d28 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -1705,33 +1705,27 @@
   node->increment()->Visit(&for_increment);
 
   // Join the loop body and increment and then tie the loop.
-  JoinEntryInstr* join = node->label()->join_for_continue();
-  if ((join != NULL) || for_body.is_open()) {
-    JoinEntryInstr* loop_start =
+  JoinEntryInstr* continue_join = node->label()->join_for_continue();
+  if ((continue_join != NULL) || for_body.is_open()) {
+    JoinEntryInstr* loop_entry =
         new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
-    if (join != NULL) {
-      if (for_body.is_open()) for_body.Goto(join);
-      AppendFragment(join, for_increment);
-      for_increment.Goto(loop_start);
+    if (continue_join != NULL) {
+      if (for_body.is_open()) for_body.Goto(continue_join);
+      Instruction* current = AppendFragment(continue_join, for_increment);
+      current->Goto(loop_entry);
     } else {
       for_body.Append(for_increment);
-      for_body.Goto(loop_start);
+      for_body.Goto(loop_entry);
     }
-    Goto(loop_start);
-    exit_ = loop_start;
+    Goto(loop_entry);
+    exit_ = loop_entry;
     AddInstruction(new CheckStackOverflowInstr(node->token_pos(), true));
   }
 
   if (node->condition() == NULL) {
     // Endless loop, no test.
-    JoinEntryInstr* body_entry =
-        new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
-    AppendFragment(body_entry, for_body);
-    Goto(body_entry);
-    if (node->label()->join_for_break() != NULL) {
-      // Control flow of ForLoop continues into join_for_break.
-      exit_ = node->label()->join_for_break();
-    }
+    Append(for_body);
+    exit_ = node->label()->join_for_break();  // May be NULL.
   } else {
     TestGraphVisitor for_test(owner(),
                               temp_index(),
diff --git a/sdk/lib/_internal/compiler/implementation/types/element_type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/element_type_mask.dart
new file mode 100644
index 0000000..0ebb9f0
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/types/element_type_mask.dart
@@ -0,0 +1,54 @@
+// 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.
+
+part of types;
+
+/**
+ * A [TypeMask] specific to an element: the return type for a
+ * function, or the type for a field.
+ */
+class ElementTypeMask extends ForwardingTypeMask {
+  final Element element;
+  // Callback function to fetch the actual inferred type of the
+  // element. It is used when a user wants to know about the type this
+  // [ForwardingTypeMask] forwards to.
+  final Function fetchForwardTo;
+  final bool isNullable;
+
+  ElementTypeMask(
+      this.fetchForwardTo, this.element, {this.isNullable: true});
+
+  bool get isElement => true;
+
+  TypeMask get forwardTo {
+    TypeMask forward = fetchForwardTo(element);
+    return isNullable ? forward.nullable() : forward.nonNullable();
+  }
+
+  bool operator==(other) {
+    if (other is! ElementTypeMask) return false;
+    return element == other.element && isNullable == other.isNullable;
+  }
+
+  bool equalsDisregardNull(other) {
+    if (other is! ElementTypeMask) return false;
+    return element == other.element;
+  }
+
+  TypeMask nullable() {
+    return isNullable
+        ? this
+        : new ElementTypeMask(fetchForwardTo, element, isNullable: true);
+  }
+
+  TypeMask nonNullable() {
+    return isNullable
+        ? new ElementTypeMask(fetchForwardTo, element, isNullable: false)
+        : this;
+  }
+
+  String toString() {
+    return 'Type for element $element';
+  }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart
new file mode 100644
index 0000000..2741a99
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/types/forwarding_type_mask.dart
@@ -0,0 +1,104 @@
+// 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.
+
+part of types;
+
+/**
+ * A type mask that wraps an other one, and delecate all its
+ * implementation methods to it.
+ */
+abstract class ForwardingTypeMask implements TypeMask {
+
+  TypeMask get forwardTo;
+
+  ForwardingTypeMask();
+
+  bool get isEmpty => forwardTo.isEmpty;
+  bool get isNullable => forwardTo.isNullable;
+  bool get isExact => forwardTo.isExact;
+
+  bool get isUnion => false;
+  bool get isContainer => false;
+  bool get isForwarding => true;
+  bool get isElement => false;
+
+  bool containsOnlyInt(Compiler compiler) {
+    return forwardTo.containsOnlyInt(compiler);
+  }
+
+  bool containsOnlyDouble(Compiler compiler) {
+    return forwardTo.containsOnlyDouble(compiler);
+  }
+
+  bool containsOnlyNum(Compiler compiler) {
+    return forwardTo.containsOnlyNum(compiler);
+  }
+
+  bool containsOnlyNull(Compiler compiler) {
+    return forwardTo.containsOnlyNull(compiler);
+  }
+
+  bool containsOnlyBool(Compiler compiler) {
+    return forwardTo.containsOnlyBool(compiler);
+  }
+
+  bool containsOnlyString(Compiler compiler) {
+    return forwardTo.containsOnlyString(compiler);
+  }
+
+  bool containsOnly(ClassElement element) {
+    return forwardTo.containsOnly(element);
+  }
+
+  bool satisfies(ClassElement cls, Compiler compiler) {
+    return forwardTo.satisfies(cls, compiler);
+  }
+
+  bool contains(DartType type, Compiler compiler) {
+    return forwardTo.contains(type, compiler);
+  }
+
+  bool containsAll(Compiler compiler) {
+    return forwardTo.containsAll(compiler);
+  }
+
+  ClassElement singleClass(Compiler compiler) {
+    return forwardTo.singleClass(compiler);
+  }
+
+  Iterable<ClassElement> containedClasses(Compiler compiler) {
+    return forwardTo.containedClasses(compiler);
+  }
+
+  TypeMask union(other, Compiler compiler) {
+    if (this == other) {
+      return this;
+    } else if (equalsDisregardNull(other)) {
+      return other.isNullable ? other : this;
+    } else if (other.isEmpty) {
+      return other.isNullable ? this.nullable() : this;
+    }
+    return forwardTo.union(other, compiler);
+  }
+
+  TypeMask intersection(TypeMask other, Compiler compiler) {
+    return forwardTo.intersection(other, compiler);
+  }
+
+  bool willHit(Selector selector, Compiler compiler) {
+    return forwardTo.willHit(selector, compiler);
+  }
+
+  bool canHit(Element element, Selector selector, Compiler compiler) {
+    return forwardTo.canHit(element, selector, compiler);
+  }
+
+  Element locateSingleElement(Selector selector, Compiler compiler) {
+    return forwardTo.locateSingleElement(selector, compiler);
+  }
+
+  TypeMask simplify(Compiler compiler) => forwardTo.simplify(compiler);
+
+  bool equalsDisregardNull(other);
+}
diff --git a/sdk/lib/_internal/lib/js_names.dart b/sdk/lib/_internal/lib/js_names.dart
new file mode 100644
index 0000000..5e0fc89
--- /dev/null
+++ b/sdk/lib/_internal/lib/js_names.dart
@@ -0,0 +1,64 @@
+// 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 dart._js_names;
+
+import 'dart:_foreign_helper' show JS;
+
+/// No-op method that is called to inform the compiler that unmangled named
+/// must be preserved.
+preserveNames() {}
+
+/// A map from mangled names to "reflective" names, that is, unmangled names
+/// with some additional information, such as, number of required arguments.
+/// This map is for mangled names used as instance members.
+final Map<String, String> mangledNames =
+    computeMangledNames(JS('', 'init.mangledNames'));
+
+/// A map from "reflective" names to mangled names (the reverse of
+/// [mangledNames]).
+final Map<String, String> reflectiveNames =
+    computeReflectiveNames(mangledNames);
+
+/// A map from mangled names to "reflective" names (see [mangledNames]).  This
+/// map is for globals, that is, static and top-level members.
+final Map<String, String> mangledGlobalNames =
+    computeMangledNames(JS('', 'init.mangledGlobalNames'));
+
+/// A map from "reflective" names to mangled names (the reverse of
+/// [mangledGlobalNames]).
+final Map<String, String> reflectiveGlobalNames =
+    computeReflectiveNames(mangledGlobalNames);
+
+/// [jsMangledNames] is a JavaScript object literal.  The keys are the mangled
+/// names, and the values are the "reflective" names.
+Map<String, String> computeMangledNames(jsMangledNames) {
+  preserveNames();
+  var keys = extractKeys(jsMangledNames);
+  var result = <String, String>{};
+  for (String key in keys) {
+    result[key] = JS('String', '#[#]', jsMangledNames, key);
+  }
+  return result;
+}
+
+Map<String, String> computeReflectiveNames(Map<String, String> map) {
+  preserveNames();
+  var result = <String, String>{};
+  map.forEach((String mangledName, String reflectiveName) {
+    result[reflectiveName] = mangledName;
+  });
+  return result;
+}
+
+List extractKeys(victim) {
+  return JS('List', '''
+(function(victim, hasOwnProperty) {
+  var result = [];
+  for (var key in victim) {
+    if (hasOwnProperty.call(victim, key)) result.push(key);
+  }
+  return result;
+})(#, Object.prototype.hasOwnProperty)''', victim);
+}
diff --git a/sdk/lib/mdv_observe_impl/path_observer.dart b/sdk/lib/mdv_observe_impl/path_observer.dart
new file mode 100644
index 0000000..5d3fae4
--- /dev/null
+++ b/sdk/lib/mdv_observe_impl/path_observer.dart
@@ -0,0 +1,287 @@
+// 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.
+
+part of dart.mdv_observe_impl;
+
+// This code is inspired by ChangeSummary:
+// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+// ...which underlies MDV. Since we don't need the functionality of
+// ChangeSummary, we just implement what we need for data bindings.
+// This allows our implementation to be much simpler.
+
+// TODO(jmesserly): should we make these types stronger, and require
+// Observable objects? Currently, it is fine to say something like:
+//     var path = new PathObserver(123, '');
+//     print(path.value); // "123"
+//
+// Furthermore this degenerate case is allowed:
+//     var path = new PathObserver(123, 'foo.bar.baz.qux');
+//     print(path.value); // "null"
+//
+// Here we see that any invalid (i.e. not Observable) value will break the
+// path chain without producing an error or exception.
+//
+// Now the real question: should we do this? For the former case, the behavior
+// is correct but we could chose to handle it in the dart:html bindings layer.
+// For the latter case, it might be better to throw an error so users can find
+// the problem.
+
+
+/**
+ * A data-bound path starting from a view-model or model object, for example
+ * `foo.bar.baz`.
+ *
+ * When the [values] stream is being listened to, this will observe changes to
+ * the object and any intermediate object along the path, and send [values]
+ * accordingly. When all listeners are unregistered it will stop observing
+ * the objects.
+ *
+ * This class is used to implement [Node.bind] and similar functionality.
+ */
+// TODO(jmesserly): find a better home for this type.
+class PathObserver {
+  /** The object being observed. */
+  final object;
+
+  /** The path string. */
+  final String path;
+
+  /** True if the path is valid, otherwise false. */
+  final bool _isValid;
+
+  // TODO(jmesserly): same issue here as ObservableMixin: is there an easier
+  // way to get a broadcast stream?
+  StreamController _values;
+  Stream _valueStream;
+
+  _PropertyObserver _observer, _lastObserver;
+
+  Object _lastValue;
+  bool _scheduled = false;
+
+  /**
+   * Observes [path] on [object] for changes. This returns an object that can be
+   * used to get the changes and get/set the value at this path.
+   * See [PathObserver.values] and [PathObserver.value].
+   */
+  PathObserver(this.object, String path)
+    : path = path, _isValid = _isPathValid(path) {
+
+    // TODO(jmesserly): if the path is empty, or the object is! Observable, we
+    // can optimize the PathObserver to be more lightweight.
+
+    _values = new StreamController.broadcast(sync: true,
+                                             onListen: _observe,
+                                             onCancel: _unobserve);
+
+    if (_isValid) {
+      var segments = [];
+      for (var segment in path.trim().split('.')) {
+        if (segment == '') continue;
+        var index = int.parse(segment, onError: (_) {});
+        segments.add(index != null ? index : new Symbol(segment));
+      }
+
+      // Create the property observer linked list.
+      // Note that the structure of a path can't change after it is initially
+      // constructed, even though the objects along the path can change.
+      for (int i = segments.length - 1; i >= 0; i--) {
+        _observer = new _PropertyObserver(this, segments[i], _observer);
+        if (_lastObserver == null) _lastObserver = _observer;
+      }
+    }
+  }
+
+  // TODO(jmesserly): we could try adding the first value to the stream, but
+  // that delivers the first record async.
+  /**
+   * Listens to the stream, and invokes the [callback] immediately with the
+   * current [value]. This is useful for bindings, which want to be up-to-date
+   * immediately.
+   */
+  StreamSubscription bindSync(void callback(value)) {
+    var result = values.listen(callback);
+    callback(value);
+    return result;
+  }
+
+  // TODO(jmesserly): should this be a change record with the old value?
+  // TODO(jmesserly): should this be a broadcast stream? We only need
+  // single-subscription in the bindings system, so single sub saves overhead.
+  /**
+   * Gets the stream of values that were observed at this path.
+   * This returns a single-subscription stream.
+   */
+  Stream get values => _values.stream;
+
+  /** Force synchronous delivery of [values]. */
+  void _deliverValues() {
+    _scheduled = false;
+
+    var newValue = value;
+    if (!identical(_lastValue, newValue)) {
+      _values.add(newValue);
+      _lastValue = newValue;
+    }
+  }
+
+  void _observe() {
+    if (_observer != null) {
+      _lastValue = value;
+      _observer.observe();
+    }
+  }
+
+  void _unobserve() {
+    if (_observer != null) _observer.unobserve();
+  }
+
+  void _notifyChange() {
+    if (_scheduled) return;
+    _scheduled = true;
+
+    // TODO(jmesserly): should we have a guarenteed order with respect to other
+    // paths? If so, we could implement this fairly easily by sorting instances
+    // of this class by birth order before delivery.
+    queueChangeRecords(_deliverValues);
+  }
+
+  /** Gets the last reported value at this path. */
+  get value {
+    if (!_isValid) return null;
+    if (_observer == null) return object;
+    _observer.ensureValue(object);
+    return _lastObserver.value;
+  }
+
+  /** Sets the value at this path. */
+  void set value(Object value) {
+    // TODO(jmesserly): throw if property cannot be set?
+    // MDV seems tolerant of these error.
+    if (_observer == null || !_isValid) return;
+    _observer.ensureValue(object);
+    var last = _lastObserver;
+    if (_setObjectProperty(last._object, last._property, value)) {
+      // Technically, this would get updated asynchronously via a change record.
+      // However, it is nice if calling the getter will yield the same value
+      // that was just set. So we use this opportunity to update our cache.
+      last.value = value;
+    }
+  }
+}
+
+// TODO(jmesserly): these should go away in favor of mirrors!
+_getObjectProperty(object, property) {
+  if (object is List && property is int) {
+    if (property >= 0 && property < object.length) {
+      return object[property];
+    } else {
+      return null;
+    }
+  }
+
+  // TODO(jmesserly): what about length?
+  if (object is Map) return object[property];
+
+  if (object is Observable) return object.getValueWorkaround(property);
+
+  return null;
+}
+
+bool _setObjectProperty(object, property, value) {
+  if (object is List && property is int) {
+    object[property] = value;
+  } else if (object is Map) {
+    object[property] = value;
+  } else if (object is Observable) {
+    (object as Observable).setValueWorkaround(property, value);
+  } else {
+    return false;
+  }
+  return true;
+}
+
+
+class _PropertyObserver {
+  final PathObserver _path;
+  final _property;
+  final _PropertyObserver _next;
+
+  // TODO(jmesserly): would be nice not to store both of these.
+  Object _object;
+  Object _value;
+  StreamSubscription _sub;
+
+  _PropertyObserver(this._path, this._property, this._next);
+
+  get value => _value;
+
+  void set value(Object newValue) {
+    _value = newValue;
+    if (_next != null) {
+      if (_sub != null) _next.unobserve();
+      _next.ensureValue(_value);
+      if (_sub != null) _next.observe();
+    }
+  }
+
+  void ensureValue(object) {
+    // If we're observing, values should be up to date already.
+    if (_sub != null) return;
+
+    _object = object;
+    value = _getObjectProperty(object, _property);
+  }
+
+  void observe() {
+    if (_object is Observable) {
+      assert(_sub == null);
+      _sub = (_object as Observable).changes.listen(_onChange);
+    }
+    if (_next != null) _next.observe();
+  }
+
+  void unobserve() {
+    if (_sub == null) return;
+
+    _sub.cancel();
+    _sub = null;
+    if (_next != null) _next.unobserve();
+  }
+
+  void _onChange(List<ChangeRecord> changes) {
+    for (var change in changes) {
+      // TODO(jmesserly): what to do about "new Symbol" here?
+      // Ideally this would only preserve names if the user has opted in to
+      // them being preserved.
+      // TODO(jmesserly): should we drop observable maps with String keys?
+      // If so then we only need one check here.
+      if (change.changes(_property)) {
+        value = _getObjectProperty(_object, _property);
+        _path._notifyChange();
+        return;
+      }
+    }
+  }
+}
+
+// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+
+const _pathIndentPart = r'[$a-z0-9_]+[$a-z0-9_\d]*';
+final _pathRegExp = new RegExp('^'
+    '(?:#?' + _pathIndentPart + ')?'
+    '(?:'
+      '(?:\\.' + _pathIndentPart + ')'
+    ')*'
+    r'$', caseSensitive: false);
+
+final _spacesRegExp = new RegExp(r'\s');
+
+bool _isPathValid(String s) {
+  s = s.replaceAll(_spacesRegExp, '');
+
+  if (s == '') return true;
+  if (s[0] == '.') return false;
+  return _pathRegExp.hasMatch(s);
+}
diff --git a/tests/compiler/dart2js/list_tracer_node_type_test.dart b/tests/compiler/dart2js/list_tracer_node_type_test.dart
new file mode 100644
index 0000000..e8022e6
--- /dev/null
+++ b/tests/compiler/dart2js/list_tracer_node_type_test.dart
@@ -0,0 +1,48 @@
+// 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.
+
+import "package:expect/expect.dart";
+import 'compiler_helper.dart';
+
+const String TEST1 = r"""
+main() {
+  var a = [42, null];
+  return a[0] + 42;
+}
+""";
+
+const String TEST2 = r"""
+main() {
+  var a = new List();
+  a.add(42);
+  a.add(null);
+  return a[0] + 42;
+}
+""";
+
+const String TEST3 = r"""
+main() {
+  var a = new List(42);
+  a[0] = 42;
+  return a[0] + 42;
+}
+""";
+
+
+main() {
+  String generated = compileAll(TEST1);
+  // Check that we only do a null check on the receiver for
+  // [: a[0] + 42 :]. We can do a null check because we inferred that
+  // the list is of type int or null.
+  Expect.isFalse(generated.contains('if (typeof t1'));
+  Expect.isTrue(generated.contains('if (t1 == null)'));
+
+  generated = compileAll(TEST2);
+  Expect.isFalse(generated.contains('if (typeof t1'));
+  Expect.isTrue(generated.contains('if (t1 == null)'));
+
+  generated = compileAll(TEST3);
+  Expect.isFalse(generated.contains('if (typeof t1'));
+  Expect.isTrue(generated.contains('if (t1 == null)'));
+}
diff --git a/tests/language/for_test.dart b/tests/language/for_test.dart
index 8576acb..9677399 100644
--- a/tests/language/for_test.dart
+++ b/tests/language/for_test.dart
@@ -31,6 +31,20 @@
     }
     return i;
   }
+
+  static var status;
+  static void f5() {
+    status = 0;
+    for (var stop = false;;) {
+      if (stop) {
+        break;
+      } else {
+        stop = true;
+        continue;
+      }
+    }
+    status = 1;
+  }
 }
 
 class ForTest {
@@ -50,6 +64,9 @@
     Expect.equals(1, Helper.f4(1));
     Expect.equals(6, Helper.f4(6));
     Expect.equals(6, Helper.f4(10));
+
+    Helper.f5();
+    Expect.equals(1, Helper.status);
   }
 }
 
diff --git a/tests/language/list_in_closure_test.dart b/tests/language/list_in_closure_test.dart
new file mode 100644
index 0000000..3c7bcc3
--- /dev/null
+++ b/tests/language/list_in_closure_test.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// Regression test for dart2js that used to see aborting closure
+// bodies as aborting their enclosing element.
+
+import "package:expect/expect.dart";
+
+main() {
+  var c = () { throw 42; };
+  () {
+    // dart2js would not seen this initialization and therefore think
+    // that the argument passed to a is a list of nulls.
+    var a = [42];
+    foo(a);
+  }();
+}
+
+foo(arg) {
+  Expect.isTrue(arg[0] == 42);
+}
diff --git a/tests/lib/mirrors/get_symbol_name_no_such_method_test.dart b/tests/lib/mirrors/get_symbol_name_no_such_method_test.dart
new file mode 100644
index 0000000..7210d98
--- /dev/null
+++ b/tests/lib/mirrors/get_symbol_name_no_such_method_test.dart
@@ -0,0 +1,37 @@
+// 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 MirrorSystem.getName works correctly on symbols returned from
+/// Invocation.memberName.  This is especially relevant when minifying.
+
+import 'dart:mirrors' show MirrorSystem;
+
+class Foo {
+  String noSuchMethod(Invocation invocation) {
+    return MirrorSystem.getName(invocation.memberName);
+  }
+}
+
+expect(expected, actual) {
+  if (expected != actual) {
+    throw 'Expected: "$expected", but got "$actual"';
+  }
+}
+
+main() {
+  var foo = new Foo();
+  expect('foo', foo.foo);
+  expect('foo', foo.foo());
+  expect('foo', foo.foo(null));
+  // TODO(ahe): Why does the following not work in dart2js/minified.
+  // expect('foo', foo.foo(null, null));
+  expect('foo', foo.foo(a: null, b: null));
+
+  expect('baz', foo.baz);
+  expect('baz', foo.baz());
+  expect('baz', foo.baz(null));
+  // TODO(ahe): Why does the following not work in dart2js/minified.
+  // expect('baz', foo.baz(null, null));
+  expect('baz', foo.baz(a: null, b: null));
+}
diff --git a/tests/standalone/debugger/breakpoint_resolved_test.dart b/tests/standalone/debugger/breakpoint_resolved_test.dart
new file mode 100644
index 0000000..9fef0df
--- /dev/null
+++ b/tests/standalone/debugger/breakpoint_resolved_test.dart
@@ -0,0 +1,35 @@
+// 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.
+
+import "debug_lib.dart";
+
+main() {
+  if (RunScript(testScript)) return;
+  
+  bar();
+  
+  print("Hello from debuggee");
+}
+
+bar() {
+  
+  print("bar");
+}
+
+var testScript = [
+  MatchFrame(0, "main"),
+  SetBreakpoint(10),
+  SetBreakpoint(12),
+  SetBreakpoint(16),
+  Resume(),
+  MatchFrame(0, "main"),
+  Resume(),
+  MatchFrame(0, "bar"),
+  Resume(),
+  MatchFrame(0, "main"),
+  ExpectEvent("breakpointResolved", {"breakpointId": 1}),
+  ExpectEvent("breakpointResolved", {"breakpointId": 2}),
+  ExpectEvent("breakpointResolved", {"breakpointId": 3}),
+  Resume(),
+];
diff --git a/tests/standalone/debugger/tostring_throws_test.dart b/tests/standalone/debugger/tostring_throws_test.dart
new file mode 100644
index 0000000..f25050d
--- /dev/null
+++ b/tests/standalone/debugger/tostring_throws_test.dart
@@ -0,0 +1,28 @@
+// 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.
+
+import "debug_lib.dart";
+
+main() {
+  if (RunScript(testScript)) return;
+  
+  Foo foo = new Foo();
+  
+  print("Hello from debuggee");
+}
+
+class Foo {
+  String toString() {
+    throw 'I always throw';
+  }
+}
+
+var testScript = [
+  MatchFrame(0, "main"),
+  SetBreakpoint(12),
+  Resume(),
+  MatchFrame(0, "main"),
+  MatchLocals({"foo": "#ERROR"}),
+  Resume(),
+];
diff --git a/tools/VERSION b/tools/VERSION
index c06980b..ae9465f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 5
 BUILD 20
-PATCH 2
+PATCH 3