Version 1.10.0-dev.1.8

svn merge -c 45141 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 45275 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@45282 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index d3c936e..ff8de84 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -9,6 +9,10 @@
 [ $system == windows ]
 code_test: Skip
 
+# Issue 23201
+[ $system == windows ]
+debugging_test: Skip
+
 # Tests that depend on token positions (which differ in release mode).
 [ $mode == release ]
 debugging_test: Skip
diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc
index 890f540..8cf0295 100644
--- a/runtime/vm/constant_propagator.cc
+++ b/runtime/vm/constant_propagator.cc
@@ -313,7 +313,16 @@
 
 
 void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) {
-  SetValue(instr, instr->value()->definition()->constant_value());
+  // Ensure that we never remove redefinition of a constant unless we are also
+  // are guaranteed to fold away code paths that correspond to non-matching
+  // class ids. Otherwise LICM might potentially hoist incorrect code.
+  const Object& value = instr->value()->definition()->constant_value();
+  if (IsConstant(value) &&
+      CheckClassInstr::IsImmutableClassId(value.GetClassId())) {
+    SetValue(instr, value);
+  } else {
+    SetValue(instr, non_constant_);
+  }
 }
 
 
@@ -783,10 +792,14 @@
     SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
     return;
   }
+
   const Object& object = instr->object()->definition()->constant_value();
   if (IsConstant(object)) {
-    SetValue(instr, Smi::ZoneHandle(Z, Smi::New(object.GetClassId())));
-    return;
+    cid = object.GetClassId();
+    if (CheckClassInstr::IsImmutableClassId(cid)) {
+      SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
+      return;
+    }
   }
   SetValue(instr, non_constant_);
 }
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index c439b41..7233ef2 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -8829,6 +8829,119 @@
 }
 
 
+TEST_CASE(ExternalStringPolymorphicDeoptimize) {
+  const char* kScriptChars =
+      "const strA = 'AAAA';\n"
+      "class A {\n"
+      "  static change_str(String s) native 'A_change_str';\n"
+      "}\n"
+      "compare(a, b, [i = 0]) {\n"
+      "  return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+      "}\n"
+      "compareA(b, [i = 0]) {\n"
+      "  return compare(strA, b, i);\n"
+      "}\n"
+      "main() {\n"
+      "  var externalA = 'AA' + 'AA';\n"
+      "  A.change_str(externalA);\n"
+      "  compare('AA' + 'AA', strA);\n"
+      "  compare(externalA, strA);\n"
+      "  for (var i = 0; i < 10000; i++) compareA(strA);\n"
+      "  A.change_str(strA);\n"
+      "  return compareA('AA' + 'AA');\n"
+      "}\n";
+  Dart_Handle lib =
+      TestCase::LoadTestScript(kScriptChars,
+                               &ExternalStringDeoptimize_native_lookup);
+  Dart_Handle result = Dart_Invoke(lib,
+                                   NewString("main"),
+                                   0,
+                                   NULL);
+  EXPECT_VALID(result);
+  bool value = false;
+  result = Dart_BooleanValue(result, &value);
+  EXPECT_VALID(result);
+  EXPECT(value);
+}
+
+
+TEST_CASE(ExternalStringGuardFieldDeoptimize) {
+  const char* kScriptChars =
+      "const strA = 'AAAA';\n"
+      "class A {\n"
+      "  static change_str(String s) native 'A_change_str';\n"
+      "}\n"
+      "class G { var f = 'A'; }\n"
+      "final guard = new G();\n"
+      "var shouldExternalize = false;\n"
+      "ext() { if (shouldExternalize) A.change_str(strA); }\n"
+      "compare(a, b, [i = 0]) {\n"
+      "  guard.f = a;\n"
+      "  ext();"
+      "  return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+      "}\n"
+      "compareA(b, [i = 0]) {\n"
+      "  return compare(strA, b, i);\n"
+      "}\n"
+      "main() {\n"
+      "  var externalA = 'AA' + 'AA';\n"
+      "  A.change_str(externalA);\n"
+      "  compare('AA' + 'AA', strA);\n"
+      "  for (var i = 0; i < 10000; i++) compareA(strA);\n"
+      "  shouldExternalize = true;\n"
+      "  return compareA('AA' + 'AA');\n"
+      "}\n";
+  Dart_Handle lib =
+      TestCase::LoadTestScript(kScriptChars,
+                               &ExternalStringDeoptimize_native_lookup);
+  Dart_Handle result = Dart_Invoke(lib,
+                                   NewString("main"),
+                                   0,
+                                   NULL);
+  EXPECT_VALID(result);
+  bool value = false;
+  result = Dart_BooleanValue(result, &value);
+  EXPECT_VALID(result);
+  EXPECT(value);
+}
+
+
+TEST_CASE(ExternalStringStaticFieldDeoptimize) {
+  const char* kScriptChars =
+      "const strA = 'AAAA';\n"
+      "class A {\n"
+      "  static change_str(String s) native 'A_change_str';\n"
+      "}\n"
+      "class G { static final f = strA; }\n"
+      "compare(a, b, [i = 0]) {\n"
+      "  return a.codeUnitAt(i) == b.codeUnitAt(i);\n"
+      "}\n"
+      "compareA(b, [i = 0]) {\n"
+      "  return compare(G.f, b, i);\n"
+      "}\n"
+      "main() {\n"
+      "  var externalA = 'AA' + 'AA';\n"
+      "  A.change_str(externalA);\n"
+      "  compare('AA' + 'AA', strA);\n"
+      "  for (var i = 0; i < 10000; i++) compareA(strA);\n"
+      "  A.change_str(G.f);"
+      "  return compareA('AA' + 'AA');\n"
+      "}\n";
+  Dart_Handle lib =
+      TestCase::LoadTestScript(kScriptChars,
+                               &ExternalStringDeoptimize_native_lookup);
+  Dart_Handle result = Dart_Invoke(lib,
+                                   NewString("main"),
+                                   0,
+                                   NULL);
+  EXPECT_VALID(result);
+  bool value = false;
+  result = Dart_BooleanValue(result, &value);
+  EXPECT_VALID(result);
+  EXPECT(value);
+}
+
+
 TEST_CASE(ExternalStringTrimDoubleParse) {
   const char* kScriptChars =
       "String str = 'A';\n"
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 2b7001f..0d646b8 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -149,7 +149,7 @@
     }
   }
 
-  HandleBranchOnNull(block);
+  HandleBranchOnStrictCompare(block);
 
   for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
     PropagateRecursive(block->dominated_blocks()[i]);
@@ -159,7 +159,8 @@
 }
 
 
-void FlowGraphTypePropagator::HandleBranchOnNull(BlockEntryInstr* block) {
+void FlowGraphTypePropagator::HandleBranchOnStrictCompare(
+    BlockEntryInstr* block) {
   BranchInstr* branch = block->last_instruction()->AsBranch();
   if (branch == NULL) {
     return;
@@ -318,7 +319,9 @@
 void FlowGraphTypePropagator::VisitGuardFieldClass(
     GuardFieldClassInstr* guard) {
   const intptr_t cid = guard->field().guarded_cid();
-  if ((cid == kIllegalCid) || (cid == kDynamicCid)) {
+  if ((cid == kIllegalCid) ||
+      (cid == kDynamicCid) ||
+      !CheckClassInstr::IsImmutableClassId(cid)) {
     return;
   }
 
@@ -794,13 +797,17 @@
     return CompileType::Null();
   }
 
+  intptr_t cid = value().GetClassId();
+  if (!CheckClassInstr::IsImmutableClassId(cid)) {
+    cid = kDynamicCid;
+  }
+
   if (value().IsInstance()) {
-    return CompileType::Create(
-        value().GetClassId(),
+    return CompileType::Create(cid,
         AbstractType::ZoneHandle(Instance::Cast(value()).GetType()));
   } else {
     // Type info for non-instance objects.
-    return CompileType::FromCid(value().GetClassId());
+    return CompileType::FromCid(cid);
   }
 }
 
@@ -974,6 +981,9 @@
       cid = obj.GetClassId();
     }
   }
+  if (!CheckClassInstr::IsImmutableClassId(cid)) {
+    cid = kDynamicCid;
+  }
   return CompileType(is_nullable, cid, abstract_type);
 }
 
diff --git a/runtime/vm/flow_graph_type_propagator.h b/runtime/vm/flow_graph_type_propagator.h
index 2e09ef3..4b1baf4 100644
--- a/runtime/vm/flow_graph_type_propagator.h
+++ b/runtime/vm/flow_graph_type_propagator.h
@@ -20,7 +20,7 @@
   void Propagate();
 
   void PropagateRecursive(BlockEntryInstr* block);
-  void HandleBranchOnNull(BlockEntryInstr* block);
+  void HandleBranchOnStrictCompare(BlockEntryInstr* block);
 
   void RollbackTo(intptr_t rollback_point);
 
diff --git a/tests/language/vm/licm_constant_redefinition_vm_test.dart b/tests/language/vm/licm_constant_redefinition_vm_test.dart
new file mode 100644
index 0000000..24f481e
--- /dev/null
+++ b/tests/language/vm/licm_constant_redefinition_vm_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--optimization_counter_threshold=100 --no-use-osr
+
+import "package:expect/expect.dart";
+
+class X {
+  final nested = [];
+
+  get length => nested.length;
+}
+
+loop(val) {
+  var sum = 0;
+  for (var i = 0; i < 10; i++) {
+    sum += val.length;
+  }
+  return sum;
+}
+
+// LoadField(LoadField(",", nested), length) should not be hoisted.
+// Otherwise it would crash.
+testRedef() => loop(",");
+
+main() {
+  // Provide polymorphic type feedback.
+  loop("");
+  loop(new X());
+
+  // Optimize loop with a constant argument.
+  for (var i = 0; i < 100; i++) {
+    testRedef();
+  }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 518e69f..031f2b2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 10
 PATCH 0
 PRERELEASE 1
-PRERELEASE_PATCH 7
+PRERELEASE_PATCH 8