| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include <functional> |
| |
| #include "include/dart_native_api.h" |
| |
| #include "platform/assert.h" |
| #include "platform/utils.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/dart_api_message.h" |
| #include "vm/dart_api_state.h" |
| #include "vm/message.h" |
| #include "vm/message_snapshot.h" |
| #include "vm/native_message_handler.h" |
| #include "vm/port.h" |
| #include "vm/service_isolate.h" |
| |
| namespace dart { |
| |
| // --- Message sending/receiving from native code --- |
| |
| class IsolateLeaveScope { |
| public: |
| explicit IsolateLeaveScope(Isolate* current_isolate) |
| : saved_isolate_(current_isolate) { |
| if (current_isolate != NULL) { |
| ASSERT(current_isolate == Isolate::Current()); |
| Dart_ExitIsolate(); |
| } |
| } |
| ~IsolateLeaveScope() { |
| if (saved_isolate_ != NULL) { |
| Dart_Isolate I = reinterpret_cast<Dart_Isolate>(saved_isolate_); |
| Dart_EnterIsolate(I); |
| } |
| } |
| |
| private: |
| Isolate* saved_isolate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IsolateLeaveScope); |
| }; |
| |
| static bool PostCObjectHelper(Dart_Port port_id, Dart_CObject* message) { |
| AllocOnlyStackZone zone; |
| std::unique_ptr<Message> msg = WriteApiMessage( |
| zone.GetZone(), message, port_id, Message::kNormalPriority); |
| |
| if (msg == nullptr) { |
| return false; |
| } |
| |
| // Post the message at the given port. |
| return PortMap::PostMessage(std::move(msg)); |
| } |
| |
| DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) { |
| return PostCObjectHelper(port_id, message); |
| } |
| |
| DART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message) { |
| if (Smi::IsValid(message)) { |
| return PortMap::PostMessage( |
| Message::New(port_id, Smi::New(message), Message::kNormalPriority)); |
| } |
| Dart_CObject cobj; |
| cobj.type = Dart_CObject_kInt64; |
| cobj.value.as_int64 = message; |
| return PostCObjectHelper(port_id, &cobj); |
| } |
| |
| DART_EXPORT Dart_Port Dart_NewNativePort(const char* name, |
| Dart_NativeMessageHandler handler, |
| bool handle_concurrently) { |
| if (name == NULL) { |
| name = "<UnnamedNativePort>"; |
| } |
| if (handler == NULL) { |
| OS::PrintErr("%s expects argument 'handler' to be non-null.\n", |
| CURRENT_FUNC); |
| return ILLEGAL_PORT; |
| } |
| if (!Dart::SetActiveApiCall()) { |
| return ILLEGAL_PORT; |
| } |
| // Start the native port without a current isolate. |
| IsolateLeaveScope saver(Isolate::Current()); |
| |
| NativeMessageHandler* nmh = new NativeMessageHandler(name, handler); |
| Dart_Port port_id = PortMap::CreatePort(nmh); |
| if (port_id != ILLEGAL_PORT) { |
| PortMap::SetPortState(port_id, PortMap::kLivePort); |
| if (!nmh->Run(Dart::thread_pool(), NULL, NULL, 0)) { |
| PortMap::ClosePort(port_id); |
| port_id = ILLEGAL_PORT; |
| } |
| } |
| Dart::ResetActiveApiCall(); |
| return port_id; |
| } |
| |
| DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id) { |
| // Close the native port without a current isolate. |
| IsolateLeaveScope saver(Isolate::Current()); |
| |
| // TODO(turnidge): Check that the port is native before trying to close. |
| return PortMap::ClosePort(native_port_id); |
| } |
| |
| static Monitor* vm_service_calls_monitor = new Monitor(); |
| |
| DART_EXPORT bool Dart_InvokeVMServiceMethod(uint8_t* request_json, |
| intptr_t request_json_length, |
| uint8_t** response_json, |
| intptr_t* response_json_length, |
| char** error) { |
| #if !defined(PRODUCT) |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate == nullptr || !isolate->is_service_isolate()); |
| IsolateLeaveScope saver(isolate); |
| |
| if (!Dart::IsInitialized()) { |
| *error = ::dart::Utils::StrDup("VM Service is not active."); |
| return false; |
| } |
| |
| // We only allow one isolate reload at a time. If this turns out to be on the |
| // critical path, we can change it to have a global datastructure which is |
| // mapping the reply ports to receive buffers. |
| MonitorLocker _(vm_service_calls_monitor); |
| |
| static Monitor* vm_service_call_monitor = new Monitor(); |
| static uint8_t* result_bytes = nullptr; |
| static intptr_t result_length = 0; |
| |
| ASSERT(result_bytes == nullptr); |
| ASSERT(result_length == 0); |
| |
| struct Utils { |
| static void HandleResponse(Dart_Port dest_port_id, Dart_CObject* message) { |
| MonitorLocker monitor(vm_service_call_monitor); |
| |
| RELEASE_ASSERT(message->type == Dart_CObject_kTypedData); |
| RELEASE_ASSERT(message->value.as_typed_data.type == |
| Dart_TypedData_kUint8); |
| result_length = message->value.as_typed_data.length; |
| result_bytes = reinterpret_cast<uint8_t*>(malloc(result_length)); |
| memmove(result_bytes, message->value.as_typed_data.values, result_length); |
| |
| monitor.Notify(); |
| } |
| }; |
| |
| auto port = |
| ::Dart_NewNativePort("service-rpc", &Utils::HandleResponse, false); |
| if (port == ILLEGAL_PORT) { |
| if (error != nullptr) { |
| *error = ::dart::Utils::StrDup("Was unable to create native port."); |
| } |
| return false; |
| } |
| |
| // Before sending the message we'll lock the monitor, which the receiver |
| // will later on notify once the answer has been received. |
| MonitorLocker monitor(vm_service_call_monitor); |
| |
| if (ServiceIsolate::SendServiceRpc(request_json, request_json_length, port, |
| error)) { |
| // We posted successfully and expect the vm-service to send the reply, so |
| // we will wait for it now. Since the service isolate could have shutdown |
| // after we sent the message we make sure to wake up periodically and |
| // check to see if the service isolate has shutdown. |
| do { |
| auto wait_result = monitor.Wait(1000); /* milliseconds */ |
| if (wait_result == Monitor::kNotified) { |
| break; |
| } |
| if (!ServiceIsolate::IsRunning()) { |
| // Service Isolate has shutdown while we were waiting for a reply, |
| // We will not get a reply anymore, cleanup and return an error. |
| Dart_CloseNativePort(port); |
| return false; |
| } |
| } while (true); |
| |
| // The caller takes ownership of the data. |
| *response_json = result_bytes; |
| *response_json_length = result_length; |
| |
| // Reset global data, which can be used by the next call (after the mutex |
| // has been released). |
| result_bytes = nullptr; |
| result_length = 0; |
| |
| // After the data has been received, we will not get any more messages on |
| // this port and can safely close it now. |
| Dart_CloseNativePort(port); |
| |
| return true; |
| } else { |
| // We couldn't post the message and will not receive any reply. Therefore we |
| // clean up the port and return an error. |
| Dart_CloseNativePort(port); |
| return false; |
| } |
| #else // !defined(PRODUCT) |
| if (error != nullptr) { |
| *error = Utils::StrDup("VM Service is not supported in PRODUCT mode."); |
| } |
| return false; |
| #endif // !defined(PRODUCT) |
| } |
| |
| // --- Verification tools --- |
| |
| DART_EXPORT Dart_Handle Dart_CompileAll() { |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| return Api::NewError("%s: Cannot compile on an AOT runtime.", CURRENT_FUNC); |
| #else |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| Dart_Handle result = Api::CheckAndFinalizePendingClasses(T); |
| if (Api::IsError(result)) { |
| return result; |
| } |
| CHECK_CALLBACK_STATE(T); |
| const Error& error = Error::Handle(T->zone(), Library::CompileAll()); |
| if (!error.IsNull()) { |
| return Api::NewHandle(T, error.ptr()); |
| } |
| return Api::Success(); |
| #endif // defined(DART_PRECOMPILED_RUNTIME) |
| } |
| |
| DART_EXPORT Dart_Handle Dart_FinalizeAllClasses() { |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| return Api::NewError("%s: All classes are already finalized in AOT runtime.", |
| CURRENT_FUNC); |
| #else |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| Dart_Handle result = Api::CheckAndFinalizePendingClasses(T); |
| if (Api::IsError(result)) { |
| return result; |
| } |
| CHECK_CALLBACK_STATE(T); |
| const Error& error = Error::Handle(T->zone(), Library::FinalizeAllClasses()); |
| if (!error.IsNull()) { |
| return Api::NewHandle(T, error.ptr()); |
| } |
| return Api::Success(); |
| #endif // defined(DART_PRECOMPILED_RUNTIME) |
| } |
| |
| struct RunInSafepointAndRWCodeArgs { |
| Isolate* isolate; |
| std::function<void()>* callback; |
| }; |
| |
| DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg) { |
| if (strcmp(command, "gc-on-nth-allocation") == 0) { |
| Thread* const thread = Thread::Current(); |
| Isolate* isolate = (thread == NULL) ? NULL : thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| TransitionNativeToVM _(thread); |
| intptr_t argument = reinterpret_cast<intptr_t>(arg); |
| ASSERT(argument > 0); |
| IsolateGroup::Current()->heap()->CollectOnNthAllocation(argument); |
| return nullptr; |
| |
| } else if (strcmp(command, "gc-now") == 0) { |
| ASSERT(arg == nullptr); // Don't pass an argument to this command. |
| Thread* const thread = Thread::Current(); |
| Isolate* isolate = (thread == NULL) ? NULL : thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| TransitionNativeToVM _(thread); |
| IsolateGroup::Current()->heap()->CollectAllGarbage(GCReason::kDebugging); |
| return nullptr; |
| |
| } else if (strcmp(command, "is-thread-in-generated") == 0) { |
| if (Thread::Current()->execution_state() == Thread::kThreadInGenerated) { |
| return reinterpret_cast<void*>(1); |
| } |
| return nullptr; |
| |
| } else if (strcmp(command, "is-mutator-in-native") == 0) { |
| Isolate* const isolate = reinterpret_cast<Isolate*>(arg); |
| CHECK_ISOLATE(isolate); |
| if (isolate->mutator_thread()->execution_state_cross_thread_for_testing() == |
| Thread::kThreadInNative) { |
| return arg; |
| } else { |
| return nullptr; |
| } |
| |
| } else if (strcmp(command, "run-in-safepoint-and-rw-code") == 0) { |
| const RunInSafepointAndRWCodeArgs* const args = |
| reinterpret_cast<RunInSafepointAndRWCodeArgs*>(arg); |
| Isolate* const isolate = args->isolate; |
| CHECK_ISOLATE(isolate); |
| Thread::EnterIsolateAsHelper(isolate, Thread::TaskKind::kUnknownTask); |
| Thread* const thread = Thread::Current(); |
| { |
| GcSafepointOperationScope scope(thread); |
| args->isolate->group()->heap()->WriteProtectCode(/*read_only=*/false); |
| (*args->callback)(); |
| args->isolate->group()->heap()->WriteProtectCode(/*read_only=*/true); |
| } |
| Thread::ExitIsolateAsHelper(); |
| return nullptr; |
| |
| } else { |
| UNREACHABLE(); |
| } |
| } |
| |
| } // namespace dart |