Chromium Code Reviews| 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(); |