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 | |
14 | namespace tonic { |
15 | |
16 | DartMessageHandler::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 | |
24 | DartMessageHandler::~DartMessageHandler() { |
25 | task_dispatcher_ = nullptr; |
26 | } |
27 | |
28 | void 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 | |
35 | void 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 | |
47 | void 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 | |
63 | void 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 | |
143 | void 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 |