Version 2.15.0-160.0.dev

Merge commit 'cc9c3ff325c0c448747641df04c1b12cbfe1fb92' into 'dev'
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index 07c4956..5972a14 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -330,8 +330,10 @@
       try {
         var session = await plugin.start(byteStorePath, sdkPath);
         session?.onDone.then((_) {
-          _pluginMap.remove(path);
-          _notifyPluginsChanged();
+          if (_pluginMap[path] == plugin) {
+            _pluginMap.remove(path);
+            _notifyPluginsChanged();
+          }
         });
       } catch (exception, stackTrace) {
         // Record the exception (for debugging purposes) and record the fact
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index 64000e8..49751f1 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -6,7 +6,7 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_kind.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart'
-    show ClassElement, ExtensionElement;
+    show ClassElement, ExtensionElement, PrefixElement;
 import 'package:analyzer/dart/element/type.dart';
 
 /// An object that can be used to determine whether an element is appropriate
@@ -131,9 +131,15 @@
         return ['', node.name];
       } else if (parent is MethodDeclaration && node == parent.name) {
         return [node.name];
-      } else if ((parent is MethodInvocation && node == parent.methodName) ||
-          (parent is PrefixedIdentifier && node == parent.identifier) ||
-          (parent is PropertyAccess && node == parent.propertyName)) {
+      } else if ((parent is MethodInvocation &&
+              node == parent.methodName &&
+              !_isPrefix(parent.target)) ||
+          (parent is PrefixedIdentifier &&
+              node == parent.identifier &&
+              !_isPrefix(parent.prefix)) ||
+          (parent is PropertyAccess &&
+              node == parent.propertyName &&
+              !_isPrefix(parent.target))) {
         return _componentsFromParent(node);
       }
       return _componentsFromIdentifier(node);
@@ -262,6 +268,11 @@
     return importedUris;
   }
 
+  /// Return `true` if the [node] is a prefix
+  static bool _isPrefix(AstNode? node) {
+    return node is SimpleIdentifier && node.staticElement is PrefixElement;
+  }
+
   /// Return the kinds of elements that could reasonably be referenced at the
   /// location of the [node]. If [child] is not `null` then the [node] is a
   /// parent of the [child].
@@ -283,7 +294,9 @@
           ElementKind.enumKind,
           ElementKind.mixinKind
         ];
-      } else if (node.realTarget != null) {
+      }
+      var realTarget = node.realTarget;
+      if (realTarget != null && !_isPrefix(realTarget)) {
         return const [ElementKind.constructorKind, ElementKind.methodKind];
       }
       return const [
@@ -304,7 +317,8 @@
         ElementKind.typedefKind
       ];
     } else if (node is PrefixedIdentifier) {
-      if (node.prefix == child) {
+      var prefix = node.prefix;
+      if (prefix == child) {
         return const [
           ElementKind.classKind,
           ElementKind.enumKind,
@@ -312,6 +326,26 @@
           ElementKind.mixinKind,
           ElementKind.typedefKind
         ];
+      } else if (prefix.staticElement is PrefixElement) {
+        var parent = node.parent;
+        if ((parent is NamedType && parent.parent is! ConstructorName) ||
+            (parent is PropertyAccess && parent.target == node)) {
+          return const [
+            ElementKind.classKind,
+            ElementKind.enumKind,
+            ElementKind.extensionKind,
+            ElementKind.mixinKind,
+            ElementKind.typedefKind
+          ];
+        }
+        return const [
+          // If the old class has been removed then this might have been a
+          // constructor invocation.
+          ElementKind.constructorKind,
+          ElementKind.getterKind,
+          ElementKind.setterKind,
+          ElementKind.variableKind
+        ];
       }
       return const [
         ElementKind.fieldKind,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/rename.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/rename.dart
index 104d268..c825366 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/rename.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/rename.dart
@@ -52,6 +52,10 @@
         // The constructor was renamed from an unnamed constructor to a named
         // constructor.
         builder.addSimpleInsertion(parent.end, '.$newName');
+      } else if (parent is PrefixedIdentifier) {
+        // The constructor was renamed from an unnamed constructor to a named
+        // constructor.
+        builder.addSimpleInsertion(parent.end, '.$newName');
       } else {
         // The constructor was renamed from a named constructor to another named
         // constructor.
@@ -81,6 +85,8 @@
       return _Data(node);
     } else if (node is ConstructorName) {
       return _Data(node.name);
+    } else if (node is PrefixedIdentifier) {
+      return _Data(node.identifier);
     }
     return null;
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 3385e85..ab54076 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -725,6 +725,9 @@
     CompileTimeErrorCode.UNDEFINED_OPERATOR: [
       ImportLibrary.forExtensionMember,
     ],
+    CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME: [
+      DataDriven.newInstance,
+    ],
     CompileTimeErrorCode.UNDEFINED_SETTER: [
       DataDriven.newInstance,
       // TODO(brianwilkerson) Support ImportLibrary for non-extension members.
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
index 6d0dc25..cf1aeb9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
@@ -21,6 +21,7 @@
     defineReflectiveTests(RenameMethodTest);
     defineReflectiveTests(RenameMixinTest);
     defineReflectiveTests(RenameTopLevelFunctionTest);
+    defineReflectiveTests(RenameTopLevelVariableTest);
     defineReflectiveTests(RenameTypedefTest);
   });
 }
@@ -130,6 +131,29 @@
 ''', errorFilter: ignoreUnusedImport);
   }
 
+  Future<void> test_constructor_unnamed_removed_prefixed() async {
+    setPackageContent('''
+class New {
+  New();
+}
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.Old();
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.New();
+}
+''');
+  }
+
   Future<void> test_inExtends_deprecated() async {
     setPackageContent('''
 @deprecated
@@ -501,6 +525,29 @@
 }
 ''');
   }
+
+  Future<void> test_unnamed_named_removed_prefixed() async {
+    setPackageContent('''
+class C {
+  C.a();
+}
+''');
+    setPackageData(_rename(['', 'C'], 'a'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.C();
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.C.a();
+}
+''');
+  }
 }
 
 @reflectiveTest
@@ -591,6 +638,25 @@
 var s = New.empty;
 ''', errorFilter: ignoreUnusedImport);
   }
+
+  Future<void> test_staticField_removed_prefixed() async {
+    setPackageContent('''
+extension New on String {
+  static String empty = '';
+}
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+var s = p.Old.empty;
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+var s = p.New.empty;
+''');
+  }
 }
 
 @reflectiveTest
@@ -772,6 +838,29 @@
 }
 ''');
   }
+
+  Future<void> test_static_reference_removed_prefixed() async {
+    setPackageContent('''
+class C {
+  static int b;
+}
+''');
+    setPackageData(_rename(['a', 'C'], 'b'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.C.a;
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.C.b;
+}
+''');
+  }
 }
 
 @reflectiveTest
@@ -967,6 +1056,27 @@
 }
 ''', errorFilter: ignoreUnusedImport);
   }
+
+  Future<void> test_topLevel_reference_removed_prefixed() async {
+    setPackageContent('''
+int get b => 1;
+''');
+    setPackageData(_rename(['a'], 'b'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.a;
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.b;
+}
+''');
+  }
 }
 
 @reflectiveTest
@@ -1122,6 +1232,29 @@
 }
 ''');
   }
+
+  Future<void> test_static_reference_removed_prefixed() async {
+    setPackageContent('''
+class C {
+  static int b() {}
+}
+''');
+    setPackageData(_rename(['a', 'C'], 'b'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.C.a();
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.C.b();
+}
+''');
+  }
 }
 
 @reflectiveTest
@@ -1164,6 +1297,23 @@
 class C with New {}
 ''', errorFilter: ignoreUnusedImport);
   }
+
+  Future<void> test_inWith_removed_prefixed() async {
+    setPackageContent('''
+mixin New {}
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+class C with p.Old {}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+class C with p.New {}
+''');
+  }
 }
 
 @reflectiveTest
@@ -1214,6 +1364,110 @@
 }
 ''', errorFilter: ignoreUnusedImport);
   }
+
+  Future<void> test_removed_prefixed() async {
+    setPackageContent('''
+int b() {}
+''');
+    setPackageData(_rename(['a'], 'b'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f() {
+  p.a();
+}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f() {
+  p.b();
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RenameTopLevelVariableTest extends _AbstractRenameTest {
+  @override
+  String get _kind => 'variable';
+
+  Future<void> test_toStaticField_noPrefix_deprecated() async {
+    setPackageContent('''
+@deprecated
+int Old = 0;
+class C {
+  int New = 1;
+}
+''');
+    setPackageData(_rename(['Old'], 'C.New'));
+    await resolveTestCode('''
+import '$importUri';
+
+int f() => Old;
+''');
+    await assertHasFix('''
+import '$importUri';
+
+int f() => C.New;
+''');
+  }
+
+  Future<void> test_toTopLevel_withoutPrefix_deprecated() async {
+    setPackageContent('''
+@deprecated
+int Old = 0;
+int New = 1;
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri';
+
+int f() => Old;
+''');
+    await assertHasFix('''
+import '$importUri';
+
+int f() => New;
+''');
+  }
+
+  Future<void> test_toTopLevel_withoutPrefix_removed() async {
+    setPackageContent('''
+C New = C();
+class C {}
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri';
+
+C f() => Old;
+''');
+    await assertHasFix('''
+import '$importUri';
+
+C f() => New;
+''');
+  }
+
+  Future<void> test_toTopLevel_withPrefix_deprecated() async {
+    setPackageContent('''
+@deprecated
+int Old = 0;
+int New = 1;
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+int f() => p.Old;
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+int f() => p.New;
+''');
+  }
 }
 
 @reflectiveTest
@@ -1256,6 +1510,23 @@
 void f(New o) {}
 ''', errorFilter: ignoreUnusedImport);
   }
+
+  Future<void> test_removed_prefixed() async {
+    setPackageContent('''
+typedef New = int Function(int);
+''');
+    setPackageData(_rename(['Old'], 'New'));
+    await resolveTestCode('''
+import '$importUri' as p;
+
+void f(p.Old o) {}
+''');
+    await assertHasFix('''
+import '$importUri' as p;
+
+void f(p.New o) {}
+''');
+  }
 }
 
 abstract class _AbstractRenameTest extends DataDrivenFixProcessorTest {
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 0af5e76..f8c0bb5 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -302,8 +302,6 @@
       isolate->group()->api_state()->AllocatePersistentHandle();
   handle->set_ptr(msg_array);
   isolate->bequeath(std::unique_ptr<Bequest>(new Bequest(handle, port.Id())));
-  // TODO(aam): Ensure there are no dart api calls after this point as we want
-  // to ensure that validated message won't get tampered with.
   Isolate::KillIfExists(isolate, Isolate::LibMsgId::kKillMsg);
   // Drain interrupts before running so any IMMEDIATE operations on the current
   // isolate happen synchronously.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 7504a27..cfc1759 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -485,6 +485,16 @@
   return reinterpret_cast<Dart_Handle>(acquired_error_handle);
 }
 
+Dart_Handle Api::UnwindInProgressError() {
+  Thread* T = Thread::Current();
+  CHECK_API_SCOPE(T);
+  TransitionToVM transition(T);
+  HANDLESCOPE(T);
+  const String& message = String::Handle(
+      Z, String::New("No api calls are allowed while unwind is in progress"));
+  return Api::NewHandle(T, UnwindError::New(message));
+}
+
 bool Api::IsValid(Dart_Handle handle) {
   Isolate* isolate = Isolate::Current();
   Thread* thread = Thread::Current();
diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h
index bd5d3e9..683be98 100644
--- a/runtime/vm/dart_api_impl.h
+++ b/runtime/vm/dart_api_impl.h
@@ -194,6 +194,9 @@
   // Gets the handle which holds the pre-created acquired error object.
   static Dart_Handle AcquiredError(IsolateGroup* isolate_group);
 
+  // Gets the handle for unwind-is-in-progress error.
+  static Dart_Handle UnwindInProgressError();
+
   // Returns true if the handle holds a Smi.
   static bool IsSmi(Dart_Handle handle) {
     // Important: we do not require current thread to be in VM state because
@@ -335,6 +338,9 @@
   if (thread->no_callback_scope_depth() != 0) {                                \
     return reinterpret_cast<Dart_Handle>(                                      \
         Api::AcquiredError(thread->isolate_group()));                          \
+  }                                                                            \
+  if (thread->is_unwind_in_progress()) {                                       \
+    return reinterpret_cast<Dart_Handle>(Api::UnwindInProgressError());        \
   }
 
 #define ASSERT_CALLBACK_STATE(thread)                                          \
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index dada22e..863880e 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -707,6 +707,83 @@
   EXPECT_STREQ(kRegularString, exception_cstr);
 }
 
+void JustPropagateErrorNative(Dart_NativeArguments args) {
+  Dart_Handle closure = Dart_GetNativeArgument(args, 0);
+  EXPECT(Dart_IsClosure(closure));
+  Dart_Handle result = Dart_InvokeClosure(closure, 0, NULL);
+  EXPECT(Dart_IsError(result));
+  Dart_PropagateError(result);
+  UNREACHABLE();
+}
+
+static Dart_NativeFunction JustPropagateError_lookup(Dart_Handle name,
+                                                     int argument_count,
+                                                     bool* auto_setup_scope) {
+  ASSERT(auto_setup_scope != NULL);
+  *auto_setup_scope = true;
+  return JustPropagateErrorNative;
+}
+
+TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenKilled) {
+  const char* kScriptChars = R"(
+import 'dart:isolate';
+
+exitRightNow() {
+  Isolate.current.kill(priority: Isolate.immediate);
+}
+
+@pragma("vm:external-name", "Test_nativeFunc")
+external void nativeFunc(closure);
+
+void Func1() {
+  nativeFunc(() => exitRightNow());
+}
+)";
+  Dart_Handle lib =
+      TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup);
+  Dart_Handle result;
+
+  result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
+  EXPECT(Dart_IsError(result));
+  EXPECT_SUBSTRING("isolate terminated by Isolate.kill", Dart_GetError(result));
+
+  result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
+  EXPECT(Dart_IsError(result));
+  EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress",
+                   Dart_GetError(result));
+}
+
+TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenSendAndExit) {
+  const char* kScriptChars = R"(
+import 'dart:isolate';
+import 'dart:_internal' show sendAndExit;
+
+sendAndExitNow() {
+  final receivePort = ReceivePort();
+  sendAndExit(receivePort.sendPort, true);
+}
+
+@pragma("vm:external-name", "Test_nativeFunc")
+external void nativeFunc(closure);
+
+void Func1() {
+  nativeFunc(() => sendAndExitNow());
+}
+)";
+  Dart_Handle lib =
+      TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup);
+  Dart_Handle result;
+
+  result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
+  EXPECT(Dart_IsError(result));
+  EXPECT_SUBSTRING("isolate terminated by Isolate.kill", Dart_GetError(result));
+
+  result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
+  EXPECT(Dart_IsError(result));
+  EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress",
+                   Dart_GetError(result));
+}
+
 // Should we propagate the error via Dart_SetReturnValue?
 static bool use_set_return = false;
 
diff --git a/runtime/vm/heap/become.h b/runtime/vm/heap/become.h
index 4b945bf..73d43ef 100644
--- a/runtime/vm/heap/become.h
+++ b/runtime/vm/heap/become.h
@@ -27,8 +27,9 @@
   ObjectPtr target() const { return target_; }
   void set_target(ObjectPtr target) { target_ = target; }
 
-  intptr_t HeapSize() {
-    intptr_t size = UntaggedObject::SizeTag::decode(tags_);
+  intptr_t HeapSize() { return HeapSize(tags_); }
+  intptr_t HeapSize(uword tags) {
+    intptr_t size = UntaggedObject::SizeTag::decode(tags);
     if (size != 0) return size;
     return *SizeAddress();
   }
diff --git a/runtime/vm/heap/freelist.h b/runtime/vm/heap/freelist.h
index 7a1e2d0..8040f5f 100644
--- a/runtime/vm/heap/freelist.h
+++ b/runtime/vm/heap/freelist.h
@@ -28,8 +28,9 @@
 
   void set_next(FreeListElement* next) { next_ = next; }
 
-  intptr_t HeapSize() {
-    intptr_t size = UntaggedObject::SizeTag::decode(tags_);
+  intptr_t HeapSize() { return HeapSize(tags_); }
+  intptr_t HeapSize(uword tags) {
+    intptr_t size = UntaggedObject::SizeTag::decode(tags);
     if (size != 0) return size;
     return *SizeAddress();
   }
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index 810a26e..4b73f85 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -121,7 +121,7 @@
     ASSERT(owner_ == nullptr);
     uword result = top_;
     uword new_top = result + size;
-    if (LIKELY(new_top < end_)) {
+    if (LIKELY(new_top <= end_)) {
       top_ = new_top;
       return result;
     }
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 940b893..9d49ce6 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1126,6 +1126,7 @@
       if (!obj.IsSmi()) return Error::null();
       const intptr_t priority = Smi::Cast(obj).Value();
       if (priority == Isolate::kImmediateAction) {
+        Thread::Current()->StartUnwindError();
         obj = message.At(2);
         if (I->VerifyTerminateCapability(obj)) {
           // We will kill the current isolate by returning an UnwindError.
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 9e4013f..2deada7 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -222,13 +222,13 @@
     case kFreeListElement: {
       uword addr = UntaggedObject::ToAddr(this);
       FreeListElement* element = reinterpret_cast<FreeListElement*>(addr);
-      instance_size = element->HeapSize();
+      instance_size = element->HeapSize(tags);
       break;
     }
     case kForwardingCorpse: {
       uword addr = UntaggedObject::ToAddr(this);
       ForwardingCorpse* element = reinterpret_cast<ForwardingCorpse*>(addr);
-      instance_size = element->HeapSize();
+      instance_size = element->HeapSize(tags);
       break;
     }
     case kWeakSerializationReferenceCid: {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 994273d..54e4108 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -3635,6 +3635,9 @@
   if (thread->no_callback_scope_depth() != 0) {
     FATAL("Cannot invoke native callback when API callbacks are prohibited.");
   }
+  if (thread->is_unwind_in_progress()) {
+    FATAL("Cannot invoke native callback while unwind error propagates.");
+  }
   if (!thread->IsMutatorThread()) {
     FATAL("Native callbacks must be invoked on the mutator thread.");
   }
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index baa448d..6824dd8 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -521,6 +521,10 @@
     no_callback_scope_depth_ -= 1;
   }
 
+  bool is_unwind_in_progress() const { return is_unwind_in_progress_; }
+
+  void StartUnwindError() { is_unwind_in_progress_ = true; }
+
 #if defined(DEBUG)
   void EnterCompiler() {
     ASSERT(!IsInsideCompiler());
@@ -1182,6 +1186,8 @@
   Thread* next_;  // Used to chain the thread structures in an isolate.
   bool is_mutator_thread_ = false;
 
+  bool is_unwind_in_progress_ = false;
+
 #if defined(DEBUG)
   bool inside_compiler_ = false;
 #endif
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 9a858de..4eedcf8 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -642,7 +642,7 @@
       _toNullTerminatedUtf8Array(utf8.encoder.convert(s));
 
   static Uint8List _toNullTerminatedUtf8Array(Uint8List l) {
-    if (l.isNotEmpty && l.last != 0) {
+    if (l.isEmpty || (l.isNotEmpty && l.last != 0)) {
       final tmp = new Uint8List(l.length + 1);
       tmp.setRange(0, l.length, l);
       return tmp;
diff --git a/tests/standalone/io/file_error_test.dart b/tests/standalone/io/file_error_test.dart
index ee80df4..2050e23 100644
--- a/tests/standalone/io/file_error_test.dart
+++ b/tests/standalone/io/file_error_test.dart
@@ -14,6 +14,20 @@
   return Directory.systemTemp.createTempSync('dart_file_error');
 }
 
+bool checkCannotOpenFileException(e) {
+  Expect.isTrue(e is FileSystemException);
+  Expect.isTrue(e.osError != null);
+  Expect.isTrue(e.toString().indexOf("Cannot open file") != -1);
+  if (Platform.operatingSystem == "linux") {
+    Expect.equals(2, e.osError.errorCode);
+  } else if (Platform.operatingSystem == "macos") {
+    Expect.equals(2, e.osError.errorCode);
+  } else if (Platform.operatingSystem == "windows") {
+    Expect.equals(3, e.osError.errorCode);
+  }
+  return true;
+}
+
 bool checkNonExistentFileSystemException(e, str) {
   Expect.isTrue(e is FileSystemException);
   Expect.isTrue(e.osError != null);
@@ -36,6 +50,20 @@
       e, "Cannot retrieve length of file");
 }
 
+void testOpenBlankFilename() {
+  asyncStart();
+  var file = new File("");
+
+  // Non-existing file should throw exception.
+  Expect.throws(() => file.openSync(), (e) => checkCannotOpenFileException(e));
+
+  var openFuture = file.open(mode: FileMode.read);
+  openFuture.then((raf) => Expect.fail("Unreachable code")).catchError((error) {
+    checkCannotOpenFileException(error);
+    asyncEnd();
+  });
+}
+
 void testOpenNonExistent() {
   asyncStart();
   Directory temp = tempDir();
@@ -409,6 +437,7 @@
 }
 
 main() {
+  testOpenBlankFilename();
   testOpenNonExistent();
   testDeleteNonExistent();
   testLengthNonExistent();
diff --git a/tests/standalone_2/io/file_error_test.dart b/tests/standalone_2/io/file_error_test.dart
index ee55a05..d52f2ee 100644
--- a/tests/standalone_2/io/file_error_test.dart
+++ b/tests/standalone_2/io/file_error_test.dart
@@ -16,6 +16,20 @@
   return Directory.systemTemp.createTempSync('dart_file_error');
 }
 
+bool checkCannotOpenFileException(e) {
+  Expect.isTrue(e is FileSystemException);
+  Expect.isTrue(e.osError != null);
+  Expect.isTrue(e.toString().indexOf("Cannot open file") != -1);
+  if (Platform.operatingSystem == "linux") {
+    Expect.equals(2, e.osError.errorCode);
+  } else if (Platform.operatingSystem == "macos") {
+    Expect.equals(2, e.osError.errorCode);
+  } else if (Platform.operatingSystem == "windows") {
+    Expect.equals(3, e.osError.errorCode);
+  }
+  return true;
+}
+
 bool checkNonExistentFileSystemException(e, str) {
   Expect.isTrue(e is FileSystemException);
   Expect.isTrue(e.osError != null);
@@ -38,6 +52,20 @@
       e, "Cannot retrieve length of file");
 }
 
+void testOpenBlankFilename() {
+  asyncStart();
+  var file = new File("");
+
+  // Non-existing file should throw exception.
+  Expect.throws(() => file.openSync(), (e) => checkCannotOpenFileException(e));
+
+  var openFuture = file.open(mode: FileMode.read);
+  openFuture.then((raf) => Expect.fail("Unreachable code")).catchError((error) {
+    checkCannotOpenFileException(error);
+    asyncEnd();
+  });
+}
+
 void testOpenNonExistent() {
   asyncStart();
   Directory temp = tempDir();
@@ -411,6 +439,7 @@
 }
 
 main() {
+  testOpenBlankFilename();
   testOpenNonExistent();
   testDeleteNonExistent();
   testLengthNonExistent();
diff --git a/tools/VERSION b/tools/VERSION
index e31f9b6..ba9cb17 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 159
+PRERELEASE 160
 PRERELEASE_PATCH 0
\ No newline at end of file