1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "tonic/dart_message_handler.h"
6
7#include "third_party/dart/runtime/include/dart_api.h"
8#include "third_party/dart/runtime/include/dart_native_api.h"
9#include "third_party/dart/runtime/include/dart_tools_api.h"
10#include "tonic/common/macros.h"
11#include "tonic/dart_state.h"
12#include "tonic/logging/dart_error.h"
13
14namespace tonic {
15
16DartMessageHandler::DartMessageHandler()
17 : handled_first_message_(false),
18 isolate_exited_(false),
19 isolate_had_uncaught_exception_error_(false),
20 isolate_had_fatal_error_(false),
21 isolate_last_error_(kNoError),
22 task_dispatcher_(nullptr) {}
23
24DartMessageHandler::~DartMessageHandler() {
25 task_dispatcher_ = nullptr;
26}
27
28void DartMessageHandler::Initialize(TaskDispatcher dispatcher) {
29 // Only can be called once.
30 TONIC_CHECK(!task_dispatcher_ && dispatcher);
31 task_dispatcher_ = dispatcher;
32 Dart_SetMessageNotifyCallback(MessageNotifyCallback);
33}
34
35void DartMessageHandler::OnMessage(DartState* dart_state) {
36 auto task_dispatcher_ = dart_state->message_handler().task_dispatcher_;
37
38 // Schedule a task to run on the message loop thread.
39 auto weak_dart_state = dart_state->GetWeakPtr();
40 task_dispatcher_([weak_dart_state]() {
41 if (auto dart_state = weak_dart_state.lock()) {
42 dart_state->message_handler().OnHandleMessage(dart_state.get());
43 }
44 });
45}
46
47void DartMessageHandler::UnhandledError(Dart_Handle error) {
48 TONIC_DCHECK(Dart_CurrentIsolate());
49 TONIC_DCHECK(Dart_IsError(error));
50
51 isolate_last_error_ = GetErrorHandleType(error);
52 // Remember that we had an uncaught exception error.
53 isolate_had_uncaught_exception_error_ = true;
54 if (Dart_IsFatalError(error)) {
55 isolate_had_fatal_error_ = true;
56 // Stop handling messages.
57 Dart_SetMessageNotifyCallback(nullptr);
58 // Shut down the isolate.
59 Dart_ShutdownIsolate();
60 }
61}
62
63void DartMessageHandler::OnHandleMessage(DartState* dart_state) {
64 if (isolate_had_fatal_error_) {
65 // Don't handle any more messages.
66 return;
67 }
68
69 DartIsolateScope scope(dart_state->isolate());
70 DartApiScope dart_api_scope;
71 Dart_Handle result = Dart_Null();
72 bool error = false;
73
74 // On the first message, check if we should pause on isolate start.
75 if (!handled_first_message()) {
76 set_handled_first_message(true);
77 if (Dart_ShouldPauseOnStart()) {
78 // Mark that we are paused on isolate start.
79 Dart_SetPausedOnStart(true);
80 }
81 }
82
83 if (Dart_IsPausedOnStart()) {
84 // We are paused on isolate start. Only handle service messages until we are
85 // requested to resume.
86 if (Dart_HasServiceMessages()) {
87 bool resume = Dart_HandleServiceMessages();
88 if (!resume) {
89 return;
90 }
91 Dart_SetPausedOnStart(false);
92 // We've resumed, handle normal messages that are in the queue.
93 result = Dart_HandleMessage();
94 error = LogIfError(result);
95 dart_state->MessageEpilogue(result);
96 if (!Dart_CurrentIsolate()) {
97 isolate_exited_ = true;
98 return;
99 }
100 }
101 } else if (Dart_IsPausedOnExit()) {
102 // We are paused on isolate exit. Only handle service messages until we are
103 // requested to resume.
104 if (Dart_HasServiceMessages()) {
105 bool resume = Dart_HandleServiceMessages();
106 if (!resume) {
107 return;
108 }
109 Dart_SetPausedOnExit(false);
110 }
111 } else {
112 // We are processing messages normally.
113 result = Dart_HandleMessage();
114 // If the Dart program has set a return code, then it is intending to shut
115 // down by way of a fatal error, and so there is no need to emit a log
116 // message.
117 if (dart_state->has_set_return_code() && Dart_IsError(result) &&
118 Dart_IsFatalError(result)) {
119 error = true;
120 } else {
121 error = LogIfError(result);
122 }
123 dart_state->MessageEpilogue(result);
124 if (!Dart_CurrentIsolate()) {
125 isolate_exited_ = true;
126 return;
127 }
128 }
129
130 if (error) {
131 UnhandledError(result);
132 } else if (!Dart_HasLivePorts()) {
133 // The isolate has no live ports and would like to exit.
134 if (!Dart_IsPausedOnExit() && Dart_ShouldPauseOnExit()) {
135 // Mark that we are paused on exit.
136 Dart_SetPausedOnExit(true);
137 } else {
138 isolate_exited_ = true;
139 }
140 }
141}
142
143void DartMessageHandler::MessageNotifyCallback(Dart_Isolate dest_isolate) {
144 auto dart_state = DartState::From(dest_isolate);
145 TONIC_CHECK(dart_state);
146 dart_state->message_handler().OnMessage(dart_state);
147}
148
149} // namespace tonic
150