1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include <functional>
6
7#include "include/dart_native_api.h"
8
9#include "platform/assert.h"
10#include "platform/utils.h"
11#include "vm/dart_api_impl.h"
12#include "vm/dart_api_message.h"
13#include "vm/dart_api_state.h"
14#include "vm/message.h"
15#include "vm/native_message_handler.h"
16#include "vm/port.h"
17#include "vm/service_isolate.h"
18
19namespace dart {
20
21// --- Message sending/receiving from native code ---
22
23class IsolateLeaveScope {
24 public:
25 explicit IsolateLeaveScope(Isolate* current_isolate)
26 : saved_isolate_(current_isolate) {
27 if (current_isolate != NULL) {
28 ASSERT(current_isolate == Isolate::Current());
29 Dart_ExitIsolate();
30 }
31 }
32 ~IsolateLeaveScope() {
33 if (saved_isolate_ != NULL) {
34 Dart_Isolate I = reinterpret_cast<Dart_Isolate>(saved_isolate_);
35 Dart_EnterIsolate(I);
36 }
37 }
38
39 private:
40 Isolate* saved_isolate_;
41
42 DISALLOW_COPY_AND_ASSIGN(IsolateLeaveScope);
43};
44
45static bool PostCObjectHelper(Dart_Port port_id, Dart_CObject* message) {
46 ApiMessageWriter writer;
47 std::unique_ptr<Message> msg =
48 writer.WriteCMessage(message, port_id, Message::kNormalPriority);
49
50 if (msg == nullptr) {
51 return false;
52 }
53
54 // Post the message at the given port.
55 return PortMap::PostMessage(std::move(msg));
56}
57
58DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) {
59 return PostCObjectHelper(port_id, message);
60}
61
62DART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message) {
63 if (Smi::IsValid(message)) {
64 return PortMap::PostMessage(
65 Message::New(port_id, Smi::New(message), Message::kNormalPriority));
66 }
67 Dart_CObject cobj;
68 cobj.type = Dart_CObject_kInt64;
69 cobj.value.as_int64 = message;
70 return PostCObjectHelper(port_id, &cobj);
71}
72
73DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
74 Dart_NativeMessageHandler handler,
75 bool handle_concurrently) {
76 if (name == NULL) {
77 name = "<UnnamedNativePort>";
78 }
79 if (handler == NULL) {
80 OS::PrintErr("%s expects argument 'handler' to be non-null.\n",
81 CURRENT_FUNC);
82 return ILLEGAL_PORT;
83 }
84 // Start the native port without a current isolate.
85 IsolateLeaveScope saver(Isolate::Current());
86
87 NativeMessageHandler* nmh = new NativeMessageHandler(name, handler);
88 Dart_Port port_id = PortMap::CreatePort(nmh);
89 PortMap::SetPortState(port_id, PortMap::kLivePort);
90 nmh->Run(Dart::thread_pool(), NULL, NULL, 0);
91 return port_id;
92}
93
94DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id) {
95 // Close the native port without a current isolate.
96 IsolateLeaveScope saver(Isolate::Current());
97
98 // TODO(turnidge): Check that the port is native before trying to close.
99 return PortMap::ClosePort(native_port_id);
100}
101
102static Monitor* vm_service_calls_monitor = new Monitor();
103
104DART_EXPORT bool Dart_InvokeVMServiceMethod(uint8_t* request_json,
105 intptr_t request_json_length,
106 uint8_t** response_json,
107 intptr_t* response_json_length,
108 char** error) {
109#if !defined(PRODUCT)
110 Isolate* isolate = Isolate::Current();
111 ASSERT(isolate == nullptr || !isolate->is_service_isolate());
112 IsolateLeaveScope saver(isolate);
113
114 // We only allow one isolate reload at a time. If this turns out to be on the
115 // critical path, we can change it to have a global datastructure which is
116 // mapping the reply ports to receive buffers.
117 MonitorLocker _(vm_service_calls_monitor);
118
119 static Monitor* vm_service_call_monitor = new Monitor();
120 static uint8_t* result_bytes = nullptr;
121 static intptr_t result_length = 0;
122
123 ASSERT(result_bytes == nullptr);
124 ASSERT(result_length == 0);
125
126 struct Utils {
127 static void HandleResponse(Dart_Port dest_port_id, Dart_CObject* message) {
128 MonitorLocker monitor(vm_service_call_monitor);
129
130 RELEASE_ASSERT(message->type == Dart_CObject_kTypedData);
131 RELEASE_ASSERT(message->value.as_typed_data.type ==
132 Dart_TypedData_kUint8);
133 result_length = message->value.as_typed_data.length;
134 result_bytes = reinterpret_cast<uint8_t*>(malloc(result_length));
135 memmove(result_bytes, message->value.as_typed_data.values, result_length);
136
137 monitor.Notify();
138 }
139 };
140
141 auto port =
142 ::Dart_NewNativePort("service-rpc", &Utils::HandleResponse, false);
143 if (port == ILLEGAL_PORT) {
144 if (error != nullptr) {
145 *error = ::dart::Utils::StrDup("Was unable to create native port.");
146 }
147 return false;
148 }
149
150 // Before sending the message we'll lock the monitor, which the receiver
151 // will later on notify once the answer has been received.
152 MonitorLocker monitor(vm_service_call_monitor);
153
154 if (ServiceIsolate::SendServiceRpc(request_json, request_json_length, port,
155 error)) {
156 // We posted successfully and expect the vm-service to send the reply, so
157 // we will wait for it now.
158 auto wait_result = monitor.Wait();
159 ASSERT(wait_result == Monitor::kNotified);
160
161 // The caller takes ownership of the data.
162 *response_json = result_bytes;
163 *response_json_length = result_length;
164
165 // Reset global data, which can be used by the next call (after the mutex
166 // has been released).
167 result_bytes = nullptr;
168 result_length = 0;
169
170 // After the data has been received, we will not get any more messages on
171 // this port and can safely close it now.
172 Dart_CloseNativePort(port);
173
174 return true;
175 } else {
176 // We couldn't post the message and will not receive any reply. Therefore we
177 // clean up the port and return an error.
178 Dart_CloseNativePort(port);
179 return false;
180 }
181#else // !defined(PRODUCT)
182 if (error != nullptr) {
183 *error = Utils::StrDup("VM Service is not supported in PRODUCT mode.");
184 }
185 return false;
186#endif // !defined(PRODUCT)
187}
188
189// --- Verification tools ---
190
191DART_EXPORT Dart_Handle Dart_CompileAll() {
192#if defined(DART_PRECOMPILED_RUNTIME)
193 return Api::NewError("%s: Cannot compile on an AOT runtime.", CURRENT_FUNC);
194#else
195 DARTSCOPE(Thread::Current());
196 API_TIMELINE_DURATION(T);
197 Dart_Handle result = Api::CheckAndFinalizePendingClasses(T);
198 if (Api::IsError(result)) {
199 return result;
200 }
201 CHECK_CALLBACK_STATE(T);
202 const Error& error = Error::Handle(T->zone(), Library::CompileAll());
203 if (!error.IsNull()) {
204 return Api::NewHandle(T, error.raw());
205 }
206 return Api::Success();
207#endif // defined(DART_PRECOMPILED_RUNTIME)
208}
209
210DART_EXPORT Dart_Handle Dart_ReadAllBytecode() {
211#if defined(DART_PRECOMPILED_RUNTIME)
212 return Api::NewError("%s: Cannot read bytecode on an AOT runtime.",
213 CURRENT_FUNC);
214#else
215 DARTSCOPE(Thread::Current());
216 API_TIMELINE_DURATION(T);
217 Dart_Handle result = Api::CheckAndFinalizePendingClasses(T);
218 if (Api::IsError(result)) {
219 return result;
220 }
221 CHECK_CALLBACK_STATE(T);
222 const Error& error = Error::Handle(T->zone(), Library::ReadAllBytecode());
223 if (!error.IsNull()) {
224 return Api::NewHandle(T, error.raw());
225 }
226 return Api::Success();
227#endif // defined(DART_PRECOMPILED_RUNTIME)
228}
229
230DART_EXPORT Dart_Handle Dart_FinalizeAllClasses() {
231#if defined(DART_PRECOMPILED_RUNTIME)
232 return Api::NewError("%s: All classes are already finalized in AOT runtime.",
233 CURRENT_FUNC);
234#else
235 DARTSCOPE(Thread::Current());
236 API_TIMELINE_DURATION(T);
237 Dart_Handle result = Api::CheckAndFinalizePendingClasses(T);
238 if (Api::IsError(result)) {
239 return result;
240 }
241 CHECK_CALLBACK_STATE(T);
242 const Error& error = Error::Handle(T->zone(), Library::FinalizeAllClasses());
243 if (!error.IsNull()) {
244 return Api::NewHandle(T, error.raw());
245 }
246 return Api::Success();
247#endif // defined(DART_PRECOMPILED_RUNTIME)
248}
249
250struct RunInSafepointAndRWCodeArgs {
251 Isolate* isolate;
252 std::function<void()>* callback;
253};
254
255DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg) {
256 if (!FLAG_enable_testing_pragmas) return nullptr;
257
258 if (strcmp(command, "gc-on-nth-allocation") == 0) {
259 TransitionNativeToVM _(Thread::Current());
260 intptr_t argument = reinterpret_cast<intptr_t>(arg);
261 ASSERT(argument > 0);
262 Isolate::Current()->heap()->CollectOnNthAllocation(argument);
263 return nullptr;
264
265 } else if (strcmp(command, "gc-now") == 0) {
266 ASSERT(arg == nullptr); // Don't pass an argument to this command.
267 TransitionNativeToVM _(Thread::Current());
268 Isolate::Current()->heap()->CollectAllGarbage();
269 return nullptr;
270
271 } else if (strcmp(command, "is-mutator-in-native") == 0) {
272 Isolate* const isolate = reinterpret_cast<Isolate*>(arg);
273 if (isolate->mutator_thread()->execution_state_cross_thread_for_testing() ==
274 Thread::kThreadInNative) {
275 return arg;
276 } else {
277 return nullptr;
278 }
279
280 } else if (strcmp(command, "run-in-safepoint-and-rw-code") == 0) {
281 const RunInSafepointAndRWCodeArgs* const args =
282 reinterpret_cast<RunInSafepointAndRWCodeArgs*>(arg);
283 Thread::EnterIsolateAsHelper(args->isolate, Thread::TaskKind::kUnknownTask);
284 Thread* const thread = Thread::Current();
285 {
286 SafepointOperationScope scope(thread);
287 args->isolate->heap()->WriteProtectCode(/*read_only=*/false);
288 (*args->callback)();
289 args->isolate->heap()->WriteProtectCode(/*read_only=*/true);
290 }
291 Thread::ExitIsolateAsHelper();
292 return nullptr;
293
294 } else {
295 UNREACHABLE();
296 }
297}
298
299} // namespace dart
300