Index: runtime/vm/isolate.cc |
=================================================================== |
--- runtime/vm/isolate.cc (revision 44200) |
+++ runtime/vm/isolate.cc (working copy) |
@@ -129,6 +129,11 @@ |
kResumeMsg = 2, |
kPingMsg = 3, |
kKillMsg = 4, |
+ kAddExitMsg = 5, |
+ kDelExitMsg = 6, |
+ kAddErrorMsg = 7, |
+ kDelErrorMsg = 8, |
+ kErrorFatalMsg = 9, |
kImmediateAction = 0, |
kBeforeNextEventAction = 1, |
@@ -164,8 +169,8 @@ |
if (message.Length() < 2) return true; |
const Object& type = Object::Handle(I, message.At(1)); |
if (!type.IsSmi()) return true; |
- const Smi& msg_type = Smi::Cast(type); |
- switch (msg_type.Value()) { |
+ const intptr_t msg_type = Smi::Cast(type).Value(); |
+ switch (msg_type) { |
case kPauseMsg: { |
// [ OOB, kPauseMsg, pause capability, resume capability ] |
if (message.Length() != 4) return true; |
@@ -252,6 +257,45 @@ |
} |
break; |
} |
+ case kAddExitMsg: |
+ case kDelExitMsg: |
+ case kAddErrorMsg: |
+ case kDelErrorMsg: { |
+ // [ OOB, msg, listener port ] |
+ if (message.Length() != 3) return true; |
+ Object& obj = Object::Handle(I, message.At(2)); |
siva
2015/03/06 23:56:04
const Object& obj = ...;
Ivan Posva
2015/03/07 04:20:39
Done.
|
+ if (!obj.IsSendPort()) return true; |
siva
2015/03/06 23:56:04
Did we change the C++ style here, we always used t
Ivan Posva
2015/03/07 04:20:39
There are 2628 places in the project that use the
|
+ const SendPort& listener = SendPort::Cast(obj); |
+ switch (msg_type) { |
+ case kAddExitMsg: |
+ I->AddExitListener(listener); |
+ break; |
+ case kDelExitMsg: |
+ I->RemoveExitListener(listener); |
+ break; |
+ case kAddErrorMsg: |
+ I->AddErrorListener(listener); |
+ break; |
+ case kDelErrorMsg: |
+ I->RemoveErrorListener(listener); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ break; |
+ } |
+ case kErrorFatalMsg: { |
+ // [ OOB, kErrorFatalMsg, terminate capability, val ] |
+ if (message.Length() != 4) return true; |
+ // Check that the terminate capability has been passed correctly. |
+ Object& obj = Object::Handle(I, message.At(2)); |
+ if (!I->VerifyTerminateCapability(obj)) return true; |
+ // Get the value to be set. |
+ obj = message.At(3); |
+ if (!obj.IsBool()) return true; |
+ I->SetErrorsFatal(Bool::Cast(obj).value()); |
+ break; |
+ } |
#if defined(DEBUG) |
// Malformed OOB messages are silently ignored in release builds. |
default: |
@@ -417,8 +461,35 @@ |
Dart_ExitScope(); |
} |
- I->object_store()->set_sticky_error(result); |
- return false; |
+ // Generate the error and stacktrace strings for the error message. |
+ String& exc_str = String::Handle(I); |
+ String& stacktrace_str = String::Handle(I); |
+ if (result.IsUnhandledException()) { |
+ const UnhandledException& uhe = UnhandledException::Cast(result); |
+ const Instance& exception = Instance::Handle(I, uhe.exception()); |
+ Object& tmp = Object::Handle(I); |
+ tmp = DartLibraryCalls::ToString(exception); |
+ if (!tmp.IsString()) { |
+ tmp = String::New(exception.ToCString()); |
+ } |
+ exc_str ^= tmp.raw(); |
+ |
+ const Instance& stacktrace = Instance::Handle(I, uhe.stacktrace()); |
+ tmp = DartLibraryCalls::ToString(stacktrace); |
+ if (!tmp.IsString()) { |
+ tmp = String::New(stacktrace.ToCString()); |
+ } |
+ stacktrace_str ^= tmp.raw();; |
+ } else { |
+ exc_str = String::New(result.ToErrorCString()); |
+ } |
+ I->NotifyErrorListeners(exc_str, stacktrace_str); |
+ |
+ if (I->ErrorsFatal()) { |
+ I->object_store()->set_sticky_error(result); |
+ return false; |
+ } |
+ return true; |
} |
@@ -449,6 +520,7 @@ |
origin_id_(0), |
pause_capability_(0), |
terminate_capability_(0), |
+ errors_fatal_(true), |
heap_(NULL), |
object_store_(NULL), |
top_exit_frame_info_(0), |
@@ -514,6 +586,7 @@ |
main_port_(0), |
pause_capability_(0), |
terminate_capability_(0), |
+ errors_fatal_(true), |
heap_(NULL), |
object_store_(NULL), |
top_exit_frame_info_(0), |
@@ -917,6 +990,144 @@ |
} |
+// TODO(iposva): Remove duplicated code and start using some hash based |
+// structure instead of these linear lookups. |
+void Isolate::AddExitListener(const SendPort& listener) { |
+ // Ensure a limit for the number of listeners remembered. |
+ static const intptr_t kMaxListeners = kSmiMax / (6*kWordSize); |
siva
2015/03/06 23:56:04
spaces 6 * kWOrdSize
Ivan Posva
2015/03/07 04:20:39
Done.
|
+ |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, object_store()->exit_listeners()); |
+ SendPort& current = SendPort::Handle(this); |
+ intptr_t insertion_index = -1; |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ current ^= listeners.At(i); |
+ if (current.IsNull()) { |
+ if (insertion_index < 0) { |
+ insertion_index = i; |
+ } |
+ } else if (current.Id() == listener.Id()) { |
+ return; |
+ } |
+ } |
+ if (insertion_index < 0) { |
+ if (listeners.Length() >= kMaxListeners) { |
+ // Cannot grow the array of listeners beyond its max. Additional |
+ // listeners are ignored. In practice will never happen as we will |
+ // run out of memory beforehand. |
+ return; |
+ } |
+ listeners.Add(listener); |
+ } else { |
+ listeners.SetAt(insertion_index, listener); |
+ } |
+} |
+ |
+ |
+void Isolate::RemoveExitListener(const SendPort& listener) { |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, object_store()->exit_listeners()); |
+ SendPort& current = SendPort::Handle(this); |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ current ^= listeners.At(i); |
+ if (!current.IsNull() && (current.Id() == listener.Id())) { |
+ // Remove the matching listener from the list. |
+ current = SendPort::null(); |
+ listeners.SetAt(i, current); |
+ return; |
+ } |
+ } |
+} |
+ |
+ |
+void Isolate::NotifyExitListeners() { |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, this->object_store()->exit_listeners()); |
+ SendPort& listener = SendPort::Handle(this); |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ listener ^= listeners.At(i); |
+ if (!listener.IsNull()) { |
+ Dart_Port port_id = listener.Id(); |
+ uint8_t* data = NULL; |
+ intptr_t len = 0; |
+ SerializeObject(Object::null_instance(), &data, &len, false); |
+ Message* msg = new Message(port_id, data, len, Message::kNormalPriority); |
+ PortMap::PostMessage(msg); |
+ } |
+ } |
+} |
+ |
+ |
+void Isolate::AddErrorListener(const SendPort& listener) { |
+ // Ensure a limit for the number of listeners remembered. |
+ static const intptr_t kMaxListeners = kSmiMax / (6*kWordSize); |
+ |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, object_store()->error_listeners()); |
+ SendPort& current = SendPort::Handle(this); |
+ intptr_t insertion_index = -1; |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ current ^= listeners.At(i); |
+ if (current.IsNull()) { |
+ if (insertion_index < 0) { |
+ insertion_index = i; |
+ } |
+ } else if (current.Id() == listener.Id()) { |
+ return; |
+ } |
+ } |
+ if (insertion_index < 0) { |
+ if (listeners.Length() >= kMaxListeners) { |
+ // Cannot grow the array of listeners beyond its max. Additional |
+ // listeners are ignored. In practice will never happen as we will |
+ // run out of memory beforehand. |
+ return; |
+ } |
+ listeners.Add(listener); |
+ } else { |
+ listeners.SetAt(insertion_index, listener); |
+ } |
+} |
+ |
+ |
+void Isolate::RemoveErrorListener(const SendPort& listener) { |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, object_store()->error_listeners()); |
+ SendPort& current = SendPort::Handle(this); |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ current ^= listeners.At(i); |
+ if (!current.IsNull() && (current.Id() == listener.Id())) { |
+ // Remove the matching listener from the list. |
+ current = SendPort::null(); |
+ listeners.SetAt(i, current); |
+ return; |
+ } |
+ } |
+} |
+ |
+ |
+void Isolate::NotifyErrorListeners(const String& msg, |
+ const String& stacktrace) { |
+ const Array& arr = Array::Handle(this, Array::New(2)); |
+ arr.SetAt(0, msg); |
+ arr.SetAt(1, stacktrace); |
+ const GrowableObjectArray& listeners = GrowableObjectArray::Handle( |
+ this, this->object_store()->error_listeners()); |
+ SendPort& listener = SendPort::Handle(this); |
+ for (intptr_t i = 0; i < listeners.Length(); i++) { |
+ listener ^= listeners.At(i); |
+ if (!listener.IsNull()) { |
+ Dart_Port port_id = listener.Id(); |
+ uint8_t* data = NULL; |
+ intptr_t len = 0; |
+ SerializeObject(arr, &data, &len, false); |
+ Message* msg = new Message(port_id, data, len, Message::kNormalPriority); |
+ PortMap::PostMessage(msg); |
+ } |
+ } |
+} |
+ |
+ |
static void StoreError(Isolate* isolate, const Object& obj) { |
ASSERT(obj.IsError()); |
isolate->object_store()->set_sticky_error(Error::Cast(obj)); |
@@ -1149,6 +1360,9 @@ |
StackZone stack_zone(this); |
HandleScope handle_scope(this); |
+ // Notify exit listeners that this isolate is shutting down. |
+ NotifyExitListeners(); |
+ |
// Clean up debugger resources. |
debugger()->Shutdown(); |