Add DartStickyError utility class

Change-Id: I079c58cc3341abb04de4eaf08a4f99bc59ef692d
diff --git a/BUILD.gn b/BUILD.gn
index 67cf7ef..b51765c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -20,6 +20,8 @@
     "dart_persistent_value.h",
     "dart_state.cc",
     "dart_state.h",
+    "dart_sticky_error.cc",
+    "dart_sticky_error.h",
     "dart_wrappable.cc",
     "dart_wrappable.h",
     "dart_wrapper_info.h",
diff --git a/dart_message_handler.cc b/dart_message_handler.cc
index 6528fd3..53fbdf4 100644
--- a/dart_message_handler.cc
+++ b/dart_message_handler.cc
@@ -10,6 +10,7 @@
 #include "lib/ftl/logging.h"
 #include "lib/tonic/logging/dart_error.h"
 #include "lib/tonic/dart_state.h"
+#include "lib/tonic/dart_sticky_error.h"
 
 namespace tonic {
 
@@ -82,6 +83,11 @@
       }
       Dart_SetPausedOnExit(false);
     }
+  } else if (DartStickyError::IsSet()) {
+    // Only process service messages if we have a sticky error set.
+    if (Dart_HasServiceMessages()) {
+      Dart_HandleServiceMessages();
+    }
   } else {
     // We are processing messages normally.
     result = Dart_HandleMessages();
@@ -89,10 +95,9 @@
   }
 
   if (error) {
-    if (!Dart_HasStickyError() && Dart_IsUnhandledExceptionError(result)) {
+    if (DartStickyError::MaybeSet(result)) {
       // Remember that we had an uncaught exception error.
       isolate_had_uncaught_exception_error_ = true;
-      Dart_SetStickyError(result);
     }
   } else if (!Dart_HasLivePorts()) {
     // The isolate has no live ports and would like to exit.
diff --git a/dart_microtask_queue.cc b/dart_microtask_queue.cc
index 8caaf9a..182a94a 100644
--- a/dart_microtask_queue.cc
+++ b/dart_microtask_queue.cc
@@ -9,6 +9,7 @@
 #include "lib/tonic/logging/dart_invoke.h"
 #include "lib/tonic/dart_persistent_value.h"
 #include "lib/tonic/dart_state.h"
+#include "lib/tonic/dart_sticky_error.h"
 
 namespace tonic {
 namespace {
@@ -36,7 +37,14 @@
       if (!dart_state.get())
         continue;
       DartState::Scope dart_scope(dart_state.get());
-      DartInvokeVoid(callback.value());
+      if (DartStickyError::IsSet()) {
+        break;
+      }
+      Dart_Handle result = DartInvokeVoid(callback.value());
+      // Let the isolate remember any uncaught exceptions.
+      if (DartStickyError::MaybeSet(result)) {
+        break;
+      }
     }
   }
 }
diff --git a/dart_sticky_error.cc b/dart_sticky_error.cc
new file mode 100644
index 0000000..c232948
--- /dev/null
+++ b/dart_sticky_error.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "lib/tonic/dart_sticky_error.h"
+
+namespace tonic {
+
+bool DartStickyError::MaybeSet(Dart_Handle result) {
+  if (!Dart_IsError(result)) {
+    // Not an error.
+    return false;
+  }
+  if (Dart_HasStickyError()) {
+    // We only remember the first error.
+    return false;
+  }
+  if (!Dart_IsUnhandledExceptionError(result)) {
+    result = Dart_NewUnhandledExceptionError(result);
+  }
+  Dart_SetStickyError(result);
+  return true;
+}
+
+bool DartStickyError::IsSet() {
+  return Dart_HasStickyError();
+}
+
+}  // namespace tonic
diff --git a/dart_sticky_error.h b/dart_sticky_error.h
new file mode 100644
index 0000000..3064849
--- /dev/null
+++ b/dart_sticky_error.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIB_TONIC_DART_STICKY_ERROR_H_
+#define LIB_TONIC_DART_STICKY_ERROR_H_
+
+#include "dart/runtime/include/dart_api.h"
+
+namespace tonic {
+
+class DartStickyError {
+ public:
+  // Returns true if the sticky error was set.
+  static bool MaybeSet(Dart_Handle result);
+
+  // Returns true if the isolate has a sticky error set.
+  static bool IsSet();
+};
+
+}  // namespace tonic
+
+#endif  // LIB_TONIC_DART_MICROTASK_QUEUE_H_
diff --git a/logging/dart_invoke.cc b/logging/dart_invoke.cc
index 8f86e88..87406f3 100644
--- a/logging/dart_invoke.cc
+++ b/logging/dart_invoke.cc
@@ -24,9 +24,10 @@
   LogIfError(handle);
 }
 
-void DartInvokeVoid(Dart_Handle closure) {
+Dart_Handle DartInvokeVoid(Dart_Handle closure) {
   Dart_Handle handle = Dart_InvokeClosure(closure, 0, nullptr);
   LogIfError(handle);
+  return handle;
 }
 
 }  // namespace tonic
diff --git a/logging/dart_invoke.h b/logging/dart_invoke.h
index 0d84746..28c001c 100644
--- a/logging/dart_invoke.h
+++ b/logging/dart_invoke.h
@@ -16,7 +16,7 @@
                      std::initializer_list<Dart_Handle> args);
 
 void DartInvoke(Dart_Handle closure, std::initializer_list<Dart_Handle> args);
-void DartInvokeVoid(Dart_Handle closure);
+Dart_Handle DartInvokeVoid(Dart_Handle closure);
 
 }  // namespace tonic