Revision: 25126
Author:   [email protected]
Date:     Wed Nov  5 07:23:28 2014 UTC
Log:      Allow uncaught exception messaging in Object.observe callbacks.

This also naturally handles pausing on uncaught exceptions in Object.observe callbacks.

[email protected], [email protected], [email protected]
BUG=chromium:335660
LOG=Y

Review URL: https://codereview.chromium.org/692313003
https://code.google.com/p/v8/source/detail?r=25126

Modified:
 /branches/bleeding_edge/src/object-observe.js
 /branches/bleeding_edge/src/runtime/runtime-observe.cc
 /branches/bleeding_edge/src/runtime/runtime.h
 /branches/bleeding_edge/test/cctest/test-api.cc
 /branches/bleeding_edge/test/cctest/test-debug.cc
 /branches/bleeding_edge/test/cctest/test-object-observe.cc

=======================================
--- /branches/bleeding_edge/src/object-observe.js Wed Oct 29 17:27:09 2014 UTC +++ /branches/bleeding_edge/src/object-observe.js Wed Nov 5 07:23:28 2014 UTC
@@ -567,12 +567,11 @@
   if (!IS_NULL(pendingObservers))
     delete pendingObservers[priority];

+  // TODO: combine the following runtime calls for perf optimization.
   var delivered = [];
   %MoveArrayContents(callbackInfo, delivered);
+  %DeliverObservationChangeRecords(callback, delivered);

-  try {
-    %_CallFunction(UNDEFINED, delivered, callback);
-  } catch (ex) {}  // TODO(rossberg): perhaps log uncaught exceptions.
   return true;
 }

=======================================
--- /branches/bleeding_edge/src/runtime/runtime-observe.cc Mon Oct 20 12:07:45 2014 UTC +++ /branches/bleeding_edge/src/runtime/runtime-observe.cc Wed Nov 5 07:23:28 2014 UTC
@@ -50,6 +50,28 @@
   isolate->RunMicrotasks();
   return isolate->heap()->undefined_value();
 }
+
+
+RUNTIME_FUNCTION(Runtime_DeliverObservationChangeRecords) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 2);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, argument, 1);
+  v8::TryCatch catcher;
+  // We should send a message on uncaught exception thrown during
+  // Object.observe delivery while not interrupting further delivery, thus
+  // we make a call inside a verbose TryCatch.
+  catcher.SetVerbose(true);
+  Handle<Object> argv[] = {argument};
+ USE(Execution::Call(isolate, callback, isolate->factory()->undefined_value(),
+                      arraysize(argv), argv));
+  if (isolate->has_pending_exception()) {
+    isolate->ReportPendingMessages();
+    isolate->clear_pending_exception();
+    isolate->set_external_caught_exception(false);
+  }
+  return isolate->heap()->undefined_value();
+}


 RUNTIME_FUNCTION(Runtime_GetObservationState) {
=======================================
--- /branches/bleeding_edge/src/runtime/runtime.h Tue Nov 4 10:02:25 2014 UTC +++ /branches/bleeding_edge/src/runtime/runtime.h Wed Nov 5 07:23:28 2014 UTC
@@ -350,6 +350,7 @@
   F(GetObjectContextObjectObserve, 1, 1)               \
   F(GetObjectContextObjectGetNotifier, 1, 1)           \
   F(GetObjectContextNotifierPerformChange, 1, 1)       \
+  F(DeliverObservationChangeRecords, 2, 1)             \
                                                        \
   /* Harmony typed arrays */                           \
   F(ArrayBufferInitialize, 2, 1)                       \
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Tue Nov 4 10:06:44 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-api.cc Wed Nov 5 07:23:28 2014 UTC
@@ -8809,6 +8809,33 @@
   CHECK_EQ(1, report_count);
   v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
 }
+
+
+TEST(ApiUncaughtExceptionInObjectObserve) {
+  v8::internal::FLAG_stack_size = 150;
+  report_count = 0;
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope scope(isolate);
+  v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
+  CompileRun(
+      "var obj = {};"
+      "var observe_count = 0;"
+      "function observer1() { ++observe_count; };"
+      "function observer2() { ++observe_count; };"
+      "function observer_throws() { throw new Error(); };"
+ "function stack_overflow() { return (function f(x) { f(x+1); })(0); };"
+      "Object.observe(obj, observer_throws.bind());"
+      "Object.observe(obj, observer1);"
+      "Object.observe(obj, stack_overflow);"
+      "Object.observe(obj, observer2);"
+      "Object.observe(obj, observer_throws.bind());"
+      "obj.foo = 'bar';");
+  CHECK_EQ(3, report_count);
+  ExpectInt32("observe_count", 2);
+  v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
+}
+

 static const char* script_resource_name = "ExceptionInNativeScript.js";
static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
=======================================
--- /branches/bleeding_edge/test/cctest/test-debug.cc Wed Oct 8 08:56:57 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-debug.cc Wed Nov 5 07:23:28 2014 UTC
@@ -7594,3 +7594,29 @@
   CHECK(CompileRun("r")->Equals(v8_str("rejectedrejection")));
   CHECK_EQ(1, exception_event_counter);
 }
+
+
+TEST(DebugBreakOnExceptionInObserveCallback) {
+  DebugLocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope scope(isolate);
+  v8::Debug::SetDebugEventListener(&DebugEventCountException);
+  // Break on uncaught exception
+  ChangeBreakOnException(false, true);
+  exception_event_counter = 0;
+
+  v8::Handle<v8::FunctionTemplate> fun =
+      v8::FunctionTemplate::New(isolate, ThrowCallback);
+  env->Global()->Set(v8_str("fun"), fun->GetFunction());
+
+  CompileRun(
+      "var obj = {};"
+      "var callbackRan = false;"
+      "Object.observe(obj, function() {"
+      "   callbackRan = true;"
+      "   throw Error('foo');"
+      "});"
+      "obj.prop = 1");
+  CHECK(CompileRun("callbackRan")->BooleanValue());
+  CHECK_EQ(1, exception_event_counter);
+}
=======================================
--- /branches/bleeding_edge/test/cctest/test-object-observe.cc Tue Aug 26 09:19:24 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-object-observe.cc Wed Nov 5 07:23:28 2014 UTC
@@ -128,6 +128,59 @@
   CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
   CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
 }
+
+
+TEST(DeliveryCallbackThrows) {
+  HandleScope scope(CcTest::isolate());
+  LocalContext context(CcTest::isolate());
+  CompileRun(
+      "var obj = {};"
+      "var ordering = [];"
+      "function observer1() { ordering.push(1); };"
+      "function observer2() { ordering.push(2); };"
+      "function observer_throws() {"
+      "  ordering.push(0);"
+      "  throw new Error();"
+      "  ordering.push(-1);"
+      "};"
+      "Object.observe(obj, observer_throws.bind());"
+      "Object.observe(obj, observer1);"
+      "Object.observe(obj, observer_throws.bind());"
+      "Object.observe(obj, observer2);"
+      "Object.observe(obj, observer_throws.bind());"
+      "obj.foo = 'bar';");
+  CHECK_EQ(5, CompileRun("ordering.length")->Int32Value());
+  CHECK_EQ(0, CompileRun("ordering[0]")->Int32Value());
+  CHECK_EQ(1, CompileRun("ordering[1]")->Int32Value());
+  CHECK_EQ(0, CompileRun("ordering[2]")->Int32Value());
+  CHECK_EQ(2, CompileRun("ordering[3]")->Int32Value());
+  CHECK_EQ(0, CompileRun("ordering[4]")->Int32Value());
+}
+
+
+TEST(DeliveryChangesMutationInCallback) {
+  HandleScope scope(CcTest::isolate());
+  LocalContext context(CcTest::isolate());
+  CompileRun(
+      "var obj = {};"
+      "var ordering = [];"
+      "function observer1(records) {"
+      "  ordering.push(100 + records.length);"
+      "  records.push(11);"
+      "  records.push(22);"
+      "};"
+      "function observer2(records) {"
+      "  ordering.push(200 + records.length);"
+      "  records.push(33);"
+      "  records.push(44);"
+      "};"
+      "Object.observe(obj, observer1);"
+      "Object.observe(obj, observer2);"
+      "obj.foo = 'bar';");
+  CHECK_EQ(2, CompileRun("ordering.length")->Int32Value());
+  CHECK_EQ(101, CompileRun("ordering[0]")->Int32Value());
+  CHECK_EQ(201, CompileRun("ordering[1]")->Int32Value());
+}


 TEST(DeliveryOrderingReentrant) {

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to