blob: b1c501ea1f22533bf45815942803f6822f08e637 [file] [log] [blame]
// 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