Chromium Code Reviews| Index: Source/bindings/dart/DartService.cpp |
| diff --git a/Source/bindings/dart/DartService.cpp b/Source/bindings/dart/DartService.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..95090f338de37cd909a31cf7f52c7b29a7bd2919 |
| --- /dev/null |
| +++ b/Source/bindings/dart/DartService.cpp |
| @@ -0,0 +1,532 @@ |
| +// 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 "config.h" |
| + |
| +#include "bindings/dart/DartService.h" |
| + |
| +#include "DartApplicationLoader.h" |
| +#include "DartController.h" |
| +#include "DartDocument.h" |
| +#include "DartServiceInternal.h" |
| +#include "DartUtilities.h" |
| +#include "DartWindow.h" |
| + |
| + |
| +// These must be kept in sync with vmservice/constants.dart |
| +// TODO(johnmccutchan): Put these constants in one place. |
| +#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 |
| +#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 |
| + |
| +// The following Resources class is used in combination with a generated |
| +// source file that expects underscores in names. The NOLINT tags are |
| +// used to suppress the errors. |
| +// TODO(johnmccutchan): Move VM service source into runtime and out of bin. |
| +namespace dart { |
| +namespace bin { |
| +class Resources { |
| +public: |
| + static const int kNoSuchInstance = -1; |
| + |
| + static int ResourceLookup(const char* path, const char** resource) |
| + { |
| + for (int i = 0; i < get_resource_count(); i++) { |
| + resource_map_entry* entry = get_resource(i); |
| + if (!strcmp(path, entry->path_)) { |
| + *resource = entry->resource_; |
| + ASSERT(entry->length_ > 0); |
| + return entry->length_; |
| + } |
| + } |
| + return kNoSuchInstance; |
| + } |
| + |
| + static intptr_t get_resource_count() // NOLINT. |
| + { |
| + return builtin_resources_count_; |
| + } |
| + |
| + static const char* get_resource_path(intptr_t i) // NOLINT. |
| + { |
| + return get_resource(i)->path_; |
| + } |
| + |
| +private: |
| + struct resource_map_entry { // NOLINT. |
| + const char* path_; // NOLINT. |
| + const char* resource_; // NOLINT. |
| + intptr_t length_; // NOLINT. |
| + }; |
| + |
| + // These fields are generated by resources_gen.cc. |
| + static resource_map_entry builtin_resources_[]; // NOLINT. |
| + static const intptr_t builtin_resources_count_; // NOLINT. |
| + |
| + static resource_map_entry* get_resource(int i) // NOLINT. |
| + { |
| + ASSERT(i >= 0 && i < builtin_resources_count_); |
| + return &builtin_resources_[i]; |
| + } |
| + |
| + DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); |
| +}; |
| + |
| +} |
| +} |
| +namespace WebCore { |
| + |
| +#define SHUTDOWN_ON_ERROR(handle) \ |
| + if (Dart_IsError(handle)) { \ |
| + m_errorMsg = strdup(Dart_GetError(handle)); \ |
| + goto error; \ |
| + } |
| + |
| +static const char* kScriptUri = "vmservice:"; |
| +#define kLibraryResourceNamePrefix "/vmservice" |
| +static const char* kVMServiceDartiumLibraryScriptResourceName = |
| + kLibraryResourceNamePrefix "/vmservice_dartium.dart"; |
| +static const char* kVMServiceLibraryName = |
| + kLibraryResourceNamePrefix "/vmservice.dart"; |
| + |
| + |
| +Dart_Isolate DartService::m_isolate = 0; |
| +Dart_Port DartService::m_port = ILLEGAL_PORT; |
| +Dart_Port DartService::m_requestPort = ILLEGAL_PORT; |
| +Dart_Port DartService::m_nativePort = ILLEGAL_PORT; |
| +const char* DartService::m_errorMsg = 0; |
| + |
| + |
| +bool DartService::Start(Document* document) |
| +{ |
| + if (m_isolate) { |
| + // Already running. |
| + return true; |
| + } |
| + { |
| + char* error = 0; |
| + |
| + m_isolate = DartController::createIsolate(kScriptUri, "main", document, true, &error); |
| + if (!m_isolate) { |
| + m_errorMsg = error; |
| + return false; |
|
Jacob
2013/12/05 19:31:31
Use Scope auto close opbjects here and elsewhere t
Cutch
2013/12/05 22:48:15
Everywhere but the startup and shutdown of the VM
|
| + } |
| + |
| + Dart_EnterScope(); |
| + // Set up the library tag handler for this isolate. |
| + Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler); |
| + SHUTDOWN_ON_ERROR(result); |
| + |
| + { |
| + // Load source into service isolate. |
| + Dart_Handle library = |
| + LoadScript(kVMServiceDartiumLibraryScriptResourceName); |
| + SHUTDOWN_ON_ERROR(library); |
| + } |
| + // Make the isolate runnable so that it is ready to handle messages. |
| + Dart_ExitScope(); |
| + Dart_ExitIsolate(); |
| + |
| + bool retval = Dart_IsolateMakeRunnable(m_isolate); |
| + if (!retval) { |
| + Dart_EnterIsolate(m_isolate); |
| + Dart_ShutdownIsolate(); |
| + m_errorMsg = "Invalid isolate state - Unable to make it runnable."; |
| + return false; |
| + } |
| + |
| + Dart_EnterIsolate(m_isolate); |
| + Dart_EnterScope(); |
| + |
| + // Invoke main. |
| + Dart_Handle library = Dart_RootLibrary(); |
| + Dart_Handle entryFunctioName = Dart_NewStringFromCString("main"); |
| + SHUTDOWN_ON_ERROR(entryFunctioName); |
| + result = Dart_Invoke(library, entryFunctioName, 0, 0); |
| + SHUTDOWN_ON_ERROR(result); |
| + |
| + // Retrieve the ReceivePort that the service is waiting on. The _receivePort |
| + // variable is setup in the call to main. |
| + Dart_Handle portFieldName = Dart_NewStringFromCString("_receivePort"); |
| + SHUTDOWN_ON_ERROR(portFieldName); |
| + Dart_Handle receivePort = Dart_GetField(library, portFieldName); |
| + SHUTDOWN_ON_ERROR(receivePort); |
| + |
| + m_port = DartServiceInternal::GetPortIdFromPort(receivePort); |
| + if (m_port == ILLEGAL_PORT) { |
| + Dart_ExitScope(); |
| + Dart_ShutdownIsolate(); |
| + m_errorMsg = "Invalid isolate state - Unable to get receivePort"; |
| + return false; |
| + } |
| + |
| + { |
| + // Retrieve the ReceivePort that the service is waiting on. The _receivePort |
| + // variable is setup in the call to main. |
| + Dart_Handle portFieldName = Dart_NewStringFromCString("_requestPort"); |
| + SHUTDOWN_ON_ERROR(portFieldName); |
| + Dart_Handle receivePort = Dart_GetField(library, portFieldName); |
| + SHUTDOWN_ON_ERROR(receivePort); |
| + m_requestPort = DartServiceInternal::GetPortIdFromPort(receivePort); |
| + ASSERT(m_requestPort != ILLEGAL_PORT); |
| + } |
| + |
| + Dart_ExitScope(); |
| + Dart_ExitIsolate(); |
| + SetupNativePort(); |
| + return true; |
| + } |
| +error: |
| + Dart_ExitScope(); |
| + Dart_ShutdownIsolate(); |
| + m_isolate = 0; |
| + m_port = ILLEGAL_PORT; |
| + m_requestPort = ILLEGAL_PORT; |
| + return false; |
| +} |
| + |
| + |
| +bool DartService::Stop() |
| +{ |
|
Jacob
2013/12/05 19:31:31
remove return values that provide no value.
|
| + if (!m_isolate) { |
| + // Already shutdown. |
| + return true; |
| + } |
| + ShutdownNativePort(); |
| + m_port = ILLEGAL_PORT; |
| + m_requestPort = ILLEGAL_PORT; |
| + Dart_Isolate isolate = m_isolate; |
| + m_isolate = 0; |
| + Dart_EnterIsolate(isolate); |
| + DartController::shutdownIsolate(isolate); |
| + return true; |
| +} |
| + |
| + |
| +void DartService::SetupNativePort() |
| +{ |
| + if (m_nativePort != ILLEGAL_PORT) { |
| + // Already running. |
| + return; |
| + } |
| + const char* nativePortName = "VMServiceNativePort"; |
| + m_nativePort = Dart_NewNativePort(nativePortName, NativePortMessageHandler, false); |
| + ASSERT(m_nativePort != ILLEGAL_PORT); |
| +} |
| + |
| + |
| +void DartService::ShutdownNativePort() |
| +{ |
| + if (m_nativePort == ILLEGAL_PORT) { |
| + return; |
| + } |
| + Dart_CloseNativePort(m_nativePort); |
| + m_nativePort = ILLEGAL_PORT; |
| +} |
| + |
| + |
| +const char* DartService::GetErrorMessage() |
| +{ |
| + return m_errorMsg ? m_errorMsg : "No error."; |
| +} |
| + |
| + |
| +Dart_Port DartService::port() |
| +{ |
| + return m_port; |
| +} |
| + |
| + |
| +bool DartService::IsRunning() |
| +{ |
| + return m_port != ILLEGAL_PORT; |
| +} |
| + |
| + |
| +static Dart_Handle MakeServiceControlMessage(Dart_Port portId, intptr_t code, Dart_Handle name) |
| +{ |
| + Dart_Handle result; |
| + Dart_Handle list = Dart_NewList(4); |
| + ASSERT(!Dart_IsError(list)); |
| + Dart_Handle codeHandle = Dart_NewInteger(code); |
| + ASSERT(!Dart_IsError(codeHandle)); |
| + result = Dart_ListSetAt(list, 0, codeHandle); |
| + ASSERT(!Dart_IsError(result)); |
| + Dart_Handle portIdHandle = Dart_NewInteger(portId); |
| + ASSERT(!Dart_IsError(portIdHandle)); |
| + result = Dart_ListSetAt(list, 1, portIdHandle); |
| + ASSERT(!Dart_IsError(result)); |
| + Dart_Handle sendPort = Dart_NewSendPort(portId); |
| + ASSERT(!Dart_IsError(sendPort)); |
| + result = Dart_ListSetAt(list, 2, sendPort); |
| + ASSERT(!Dart_IsError(result)); |
| + result = Dart_ListSetAt(list, 3, name); |
| + ASSERT(!Dart_IsError(result)); |
| + return list; |
| +} |
| + |
| + |
| +bool DartService::SendIsolateStartupMessage() |
| +{ |
| + if (!IsRunning()) { |
| + return false; |
| + } |
| + Dart_Handle name = Dart_DebugName(); |
| + ASSERT(!Dart_IsError(name)); |
| + Dart_Handle list = MakeServiceControlMessage(Dart_GetMainPortId(), VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, name); |
| + ASSERT(!Dart_IsError(list)); |
| + return Dart_Post(m_port, list); |
| +} |
| + |
| + |
| +bool DartService::SendIsolateShutdownMessage() |
| +{ |
| + if (!IsRunning()) |
| + return false; |
| + Dart_Handle list = MakeServiceControlMessage(Dart_GetMainPortId(), VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, Dart_Null()); |
| + ASSERT(!Dart_IsError(list)); |
| + return Dart_Post(m_port, list); |
| +} |
| + |
| + |
| +DartServiceRequest::DartServiceRequest(const char* request) |
| +{ |
| + ASSERT(request); |
| + m_request = strdup(request); |
| + ASSERT(m_request); |
| +} |
| + |
| +DartServiceRequest::~DartServiceRequest() |
| +{ |
| + free(m_request); |
| +} |
| + |
| + |
| + |
| +// The format of the message is: |
| +// [send port, request string, address of DartServiceRequest]. |
| +static Dart_Handle MakeServiceRequestMessage(Dart_Port port, DartServiceRequest* request) |
| +{ |
| + intptr_t requestAddress = reinterpret_cast<intptr_t>(request); |
| + int64_t requestAddress64 = static_cast<int64_t>(requestAddress); |
| + Dart_Handle result; |
| + Dart_Handle list = Dart_NewList(3); |
| + ASSERT(!Dart_IsError(list)); |
| + Dart_Handle portHandle = Dart_NewSendPort(port); |
| + ASSERT(!Dart_IsError(portHandle)); |
| + Dart_Handle requestHandle = Dart_NewStringFromCString(request->GetRequestString()); |
| + ASSERT(!Dart_IsError(requestHandle)); |
| + Dart_Handle addressHandle = Dart_NewInteger(requestAddress64); |
| + ASSERT(!Dart_IsError(addressHandle)); |
| + result = Dart_ListSetAt(list, 0, portHandle); |
| + ASSERT(!Dart_IsError(result)); |
| + result = Dart_ListSetAt(list, 1, requestHandle); |
| + ASSERT(!Dart_IsError(result)); |
| + result = Dart_ListSetAt(list, 2, addressHandle); |
| + ASSERT(!Dart_IsError(result)); |
| + return list; |
| +} |
| + |
| + |
| +void DartService::MakeServiceRequest(DartServiceRequest* request) |
| +{ |
| + // TODO(johnmccutchan): Once the VM service is no longer a DOM isolate, |
| + // we must be careful about entering the isolate. |
| + DartIsolateScope isolateScope(m_isolate); |
| + { |
|
Jacob
2013/12/05 19:31:31
no need for the extra {
}
here.
Cutch
2013/12/05 22:48:15
Done.
|
| + DartApiScope apiScope; |
| + Dart_Handle message = MakeServiceRequestMessage(m_nativePort, request); |
| + Dart_Post(m_requestPort, message); |
| + } |
| +} |
| + |
| + |
| +void DartService::NativePortMessageHandler(Dart_Port destPortId, Dart_CObject* message) |
| +{ |
| + // The format of the reply message is: |
| + // [response string, address of DartServiceRequest]. |
| + ASSERT(destPortId == m_nativePort); |
| + ASSERT(message->type == Dart_CObject_kArray); |
| + ASSERT(message->value.as_array.length == 2); |
| + ASSERT(message->value.as_array.values[0]->type == Dart_CObject_kString); |
| + intptr_t requestAddress = 0; |
| + if (message->value.as_array.values[1]->type == Dart_CObject_kInt32) { |
| + requestAddress = static_cast<intptr_t>(message->value.as_array.values[1]->value.as_int32); |
| + } else { |
| + ASSERT(message->value.as_array.values[1]->type == Dart_CObject_kInt64); |
| + requestAddress = static_cast<intptr_t>(message->value.as_array.values[1]->value.as_int64); |
| + } |
| + ASSERT(request_address); |
| + DartServiceRequest* request = reinterpret_cast<DartServiceRequest*>(requestAddress); |
| + request->ResponseReady(message->value.as_array.values[0]->value.as_string); |
| +} |
| + |
| + |
| +Dart_Handle DartService::GetSource(const char* name) |
| +{ |
| + const char* vmserviceSource = 0; |
| + int r = dart::bin::Resources::ResourceLookup(name, &vmserviceSource); |
| + ASSERT(r != dart::bin::Resources::kNoSuchInstance); |
| + return Dart_NewStringFromCString(vmserviceSource); |
| +} |
| + |
| + |
| +Dart_Handle DartService::LoadScript(const char* name) |
| +{ |
| + Dart_Handle url = Dart_NewStringFromCString(name); |
| + Dart_Handle source = GetSource(name); |
| + return Dart_LoadScript(url, source, 0, 0); |
| +} |
| + |
| + |
| +Dart_Handle DartService::LoadSource(Dart_Handle library, const char* name) |
| +{ |
| + Dart_Handle url = Dart_NewStringFromCString(name); |
| + Dart_Handle source = GetSource(name); |
| + return Dart_LoadSource(library, url, source); |
| +} |
| + |
| + |
| +Dart_Handle DartService::LoadSources(Dart_Handle library, const char* names[]) |
| +{ |
| + Dart_Handle result = Dart_Null(); |
| + for (int i = 0; names[i]; i++) { |
| + result = LoadSource(library, names[i]); |
| + if (Dart_IsError(result)) |
| + break; |
| + } |
| + return result; |
| +} |
| + |
| + |
| +static bool IsVMServiceURL(const char* url) |
| +{ |
| + static const intptr_t kLibraryResourceNamePrefixLen = strlen(kLibraryResourceNamePrefix); |
| + return !strncmp(kLibraryResourceNamePrefix, url, kLibraryResourceNamePrefixLen); |
| +} |
| + |
| + |
| +static bool IsVMServiceLibrary(const char* url) |
| +{ |
| + return !strcmp(kVMServiceLibraryName, url); |
| +} |
| + |
| +static bool IsDartLibrary(const char* url) |
| +{ |
| + static const char* kDartPrefix = "dart:"; |
| + static const intptr_t kDartPrefixLen = strlen(kDartPrefix); |
| + return !strncmp(kDartPrefix, url, kDartPrefixLen); |
| +} |
| + |
| +static Dart_Handle Canonicalize(const char* url) |
| +{ |
| + if (IsVMServiceURL(url)) { |
| + // Already canonicalized. |
| + return Dart_NewStringFromCString(url); |
| + } |
| + // FIXME(johnmccutchan): Remove hard coded 1024 character limit. |
| + char buffer[1024]; |
| + snprintf(&buffer[0], sizeof(buffer), "%s/%s", kLibraryResourceNamePrefix, url); |
| + return Dart_NewStringFromCString(buffer); |
| +} |
| + |
| + |
| +Dart_Handle DartService::LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle library, Dart_Handle url) |
| +{ |
| + if (!Dart_IsLibrary(library)) |
| + return Dart_NewApiError("not a library"); |
| + if (!Dart_IsString(url)) |
| + return Dart_NewApiError("url is not a string"); |
| + const char* urlString = 0; |
| + Dart_Handle result = Dart_StringToCString(url, &urlString); |
| + if (Dart_IsError(result)) |
| + return result; |
| + Dart_Handle libraryUrl = Dart_LibraryUrl(library); |
| + const char* libraryUrlString = 0; |
| + result = Dart_StringToCString(libraryUrl, &libraryUrlString); |
| + if (Dart_IsError(result)) |
| + return result; |
| + if (IsDartLibrary(urlString)) |
| + return DartApplicationLoader::libraryTagHandlerCallback(tag, library, url); |
| + switch (tag) { |
| + case Dart_kCanonicalizeUrl: |
| + return Canonicalize(urlString); |
| + break; |
| + case Dart_kImportTag: { |
| + Dart_Handle source = GetSource(urlString); |
| + if (Dart_IsError(source)) |
| + return source; |
| + Dart_Handle lib = Dart_LoadLibrary(url, source); |
| + if (Dart_IsError(lib)) |
| + return lib; |
| + if (IsVMServiceLibrary(urlString)) { |
| + // Install native resolver for this library. |
| + result = Dart_SetNativeResolver(lib, DartService::NativeResolver); |
| + if (Dart_IsError(result)) |
| + return result; |
| + } |
| + return lib; |
| + } |
| + break; |
| + case Dart_kSourceTag: { |
| + Dart_Handle source = GetSource(urlString); |
| + if (Dart_IsError(source)) |
| + return source; |
| + return Dart_LoadSource(library, url, source); |
| + } |
| + break; |
| + default: |
| + DART_UNIMPLEMENTED(); |
| + break; |
| + } |
| + ASSERT_NOT_REACHED(); |
| + return result; |
| +} |
| + |
| + |
| +void DartService::VmServiceShutdownCallback(void* callbackData) |
| +{ |
| + ASSERT(Dart_CurrentIsolate()); |
| + Dart_EnterScope(); |
| + SendIsolateShutdownMessage(); |
| + Dart_ExitScope(); |
| +} |
| + |
| +static void SendServiceMessage(Dart_NativeArguments args) |
| +{ |
| + Dart_Handle sp = Dart_GetNativeArgument(args, 0); |
| + Dart_Handle rp = Dart_GetNativeArgument(args, 1); |
| + Dart_Handle message = Dart_GetNativeArgument(args, 2); |
| + DartServiceInternal::PostOOB(sp, rp, message); |
| +} |
| + |
| + |
| +struct VmServiceNativeEntry { |
| + const char* name; |
| + int numArguments; |
| + Dart_NativeFunction function; |
| +}; |
| + |
| + |
| +static VmServiceNativeEntry VmServiceNativeEntries[] = { |
| + {"SendServiceMessage", 3, SendServiceMessage} |
| +}; |
| + |
| + |
| +Dart_NativeFunction DartService::NativeResolver(Dart_Handle name, int numArguments) |
| +{ |
| + const char* functionName = 0; |
| + Dart_Handle result = Dart_StringToCString(name, &functionName); |
| + ASSERT(!Dart_IsError(result)); |
| + ASSERT(functionName); |
| + intptr_t n = sizeof(VmServiceNativeEntries) / sizeof(VmServiceNativeEntries[0]); |
| + for (intptr_t i = 0; i < n; i++) { |
| + VmServiceNativeEntry entry = VmServiceNativeEntries[i]; |
| + if (!strcmp(functionName, entry.name) && (numArguments == entry.numArguments)) { |
| + return entry.function; |
| + } |
| + } |
| + return 0; |
| +} |
| + |
| +} |