| // Copyright 2013 The Flutter 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 "tonic/dart_message_handler.h" | |
| #include "third_party/dart/runtime/include/dart_api.h" | |
| #include "third_party/dart/runtime/include/dart_native_api.h" | |
| #include "third_party/dart/runtime/include/dart_tools_api.h" | |
| #include "tonic/common/macros.h" | |
| #include "tonic/dart_state.h" | |
| #include "tonic/logging/dart_error.h" | |
| namespace tonic { | |
| DartMessageHandler::DartMessageHandler() | |
| : handled_first_message_(false), | |
| isolate_exited_(false), | |
| isolate_had_uncaught_exception_error_(false), | |
| isolate_had_fatal_error_(false), | |
| isolate_last_error_(kNoError), | |
| task_dispatcher_(nullptr) {} | |
| DartMessageHandler::~DartMessageHandler() { | |
| task_dispatcher_ = nullptr; | |
| } | |
| void DartMessageHandler::Initialize(TaskDispatcher dispatcher) { | |
| // Only can be called once. | |
| TONIC_CHECK(!task_dispatcher_ && dispatcher); | |
| task_dispatcher_ = dispatcher; | |
| Dart_SetMessageNotifyCallback(MessageNotifyCallback); | |
| } | |
| void DartMessageHandler::OnMessage(DartState* dart_state) { | |
| auto task_dispatcher_ = dart_state->message_handler().task_dispatcher_; | |
| // Schedule a task to run on the message loop thread. | |
| auto weak_dart_state = dart_state->GetWeakPtr(); | |
| task_dispatcher_([weak_dart_state]() { | |
| if (auto dart_state = weak_dart_state.lock()) { | |
| dart_state->message_handler().OnHandleMessage(dart_state.get()); | |
| } | |
| }); | |
| } | |
| void DartMessageHandler::UnhandledError(Dart_Handle error) { | |
| TONIC_DCHECK(Dart_CurrentIsolate()); | |
| TONIC_DCHECK(Dart_IsError(error)); | |
| isolate_last_error_ = GetErrorHandleType(error); | |
| // Remember that we had an uncaught exception error. | |
| isolate_had_uncaught_exception_error_ = true; | |
| if (Dart_IsFatalError(error)) { | |
| isolate_had_fatal_error_ = true; | |
| // Stop handling messages. | |
| Dart_SetMessageNotifyCallback(nullptr); | |
| // Shut down the isolate. | |
| Dart_ShutdownIsolate(); | |
| } | |
| } | |
| void DartMessageHandler::OnHandleMessage(DartState* dart_state) { | |
| if (isolate_had_fatal_error_) { | |
| // Don't handle any more messages. | |
| return; | |
| } | |
| DartIsolateScope scope(dart_state->isolate()); | |
| DartApiScope dart_api_scope; | |
| Dart_Handle result = Dart_Null(); | |
| bool error = false; | |
| // On the first message, check if we should pause on isolate start. | |
| if (!handled_first_message()) { | |
| set_handled_first_message(true); | |
| if (Dart_ShouldPauseOnStart()) { | |
| // Mark that we are paused on isolate start. | |
| Dart_SetPausedOnStart(true); | |
| } | |
| } | |
| if (Dart_IsPausedOnStart()) { | |
| // We are paused on isolate start. Only handle service messages until we are | |
| // requested to resume. | |
| if (Dart_HasServiceMessages()) { | |
| bool resume = Dart_HandleServiceMessages(); | |
| if (!resume) { | |
| return; | |
| } | |
| Dart_SetPausedOnStart(false); | |
| // We've resumed, handle normal messages that are in the queue. | |
| result = Dart_HandleMessage(); | |
| error = CheckAndHandleError(result); | |
| dart_state->MessageEpilogue(result); | |
| if (!Dart_CurrentIsolate()) { | |
| isolate_exited_ = true; | |
| return; | |
| } | |
| } | |
| } else if (Dart_IsPausedOnExit()) { | |
| // We are paused on isolate exit. Only handle service messages until we are | |
| // requested to resume. | |
| if (Dart_HasServiceMessages()) { | |
| bool resume = Dart_HandleServiceMessages(); | |
| if (!resume) { | |
| return; | |
| } | |
| Dart_SetPausedOnExit(false); | |
| } | |
| } else { | |
| // We are processing messages normally. | |
| result = Dart_HandleMessage(); | |
| // If the Dart program has set a return code, then it is intending to shut | |
| // down by way of a fatal error, and so there is no need to emit a log | |
| // message. | |
| if (dart_state->has_set_return_code() && Dart_IsError(result) && | |
| Dart_IsFatalError(result)) { | |
| error = true; | |
| } else { | |
| error = CheckAndHandleError(result); | |
| } | |
| dart_state->MessageEpilogue(result); | |
| if (!Dart_CurrentIsolate()) { | |
| isolate_exited_ = true; | |
| return; | |
| } | |
| } | |
| if (error) { | |
| UnhandledError(result); | |
| } else if (!Dart_HasLivePorts()) { | |
| // The isolate has no live ports and would like to exit. | |
| if (!Dart_IsPausedOnExit() && Dart_ShouldPauseOnExit()) { | |
| // Mark that we are paused on exit. | |
| Dart_SetPausedOnExit(true); | |
| } else { | |
| isolate_exited_ = true; | |
| } | |
| } | |
| } | |
| void DartMessageHandler::MessageNotifyCallback(Dart_Isolate dest_isolate) { | |
| auto dart_state = DartState::From(dest_isolate); | |
| TONIC_CHECK(dart_state); | |
| dart_state->message_handler().OnMessage(dart_state); | |
| } | |
| } // namespace tonic |