a576fec3c0
be setup automatically or not when a native function is invoked. This would allow the embedder to treat some native functions as leaf functions whose invocation can be very light. R=hausner@google.com, zra@google.com Review URL: https://codereview.chromium.org//117723002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@31286 260f80e4-7a28-3924-810f-c04153c831b5
555 lines
17 KiB
C++
555 lines
17 KiB
C++
// 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 "bin/vmservice_impl.h"
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/dartutils.h"
|
|
#include "bin/isolate_data.h"
|
|
#include "bin/resources.h"
|
|
#include "bin/thread.h"
|
|
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/message.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/native_arguments.h"
|
|
#include "vm/object.h"
|
|
#include "vm/port.h"
|
|
#include "vm/snapshot.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
// snapshot_buffer points to a snapshot if we link in a snapshot otherwise
|
|
// it is initialized to NULL.
|
|
extern const uint8_t* snapshot_buffer;
|
|
#define RETURN_ERROR_HANDLE(handle) \
|
|
if (Dart_IsError(handle)) { \
|
|
return handle; \
|
|
}
|
|
|
|
#define SHUTDOWN_ON_ERROR(handle) \
|
|
if (Dart_IsError(handle)) { \
|
|
error_msg_ = strdup(Dart_GetError(handle)); \
|
|
Dart_ExitScope(); \
|
|
Dart_ShutdownIsolate(); \
|
|
return false; \
|
|
}
|
|
|
|
#define kLibraryResourceNamePrefix "/vmservice"
|
|
static const char* kVMServiceIOLibraryScriptResourceName =
|
|
kLibraryResourceNamePrefix "/vmservice_io.dart";
|
|
static const char* kVMServiceLibraryName =
|
|
kLibraryResourceNamePrefix "/vmservice.dart";
|
|
|
|
#define kClientResourceNamePrefix "/vmservice/client/deployed/web"
|
|
|
|
Dart_Isolate VmService::isolate_ = NULL;
|
|
Dart_Port VmService::port_ = ILLEGAL_PORT;
|
|
dart::Monitor* VmService::monitor_ = NULL;
|
|
const char* VmService::error_msg_ = NULL;
|
|
|
|
// These must be kept in sync with vmservice/constants.dart
|
|
#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
|
|
#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
|
|
|
|
|
|
static Dart_NativeFunction VmServiceNativeResolver(Dart_Handle name,
|
|
int num_arguments,
|
|
bool* auto_setup_scope);
|
|
|
|
|
|
bool VmService::Start(intptr_t server_port) {
|
|
monitor_ = new dart::Monitor();
|
|
ASSERT(monitor_ != NULL);
|
|
error_msg_ = NULL;
|
|
|
|
|
|
{
|
|
// Take lock before spawning new thread.
|
|
MonitorLocker ml(monitor_);
|
|
// Spawn new thread.
|
|
dart::Thread::Start(ThreadMain, server_port);
|
|
// Wait until service is running on spawned thread.
|
|
ml.Wait();
|
|
}
|
|
return port_ != ILLEGAL_PORT;
|
|
}
|
|
|
|
|
|
bool VmService::_Start(intptr_t server_port) {
|
|
ASSERT(isolate_ == NULL);
|
|
char* error = NULL;
|
|
const char* script_uri = "vmservice:";
|
|
IsolateData* isolate_data = new IsolateData(script_uri);
|
|
isolate_ = Dart_CreateIsolate(script_uri, "main", snapshot_buffer,
|
|
isolate_data,
|
|
&error);
|
|
if (isolate_ == NULL) {
|
|
error_msg_ = error;
|
|
return false;
|
|
}
|
|
|
|
Dart_EnterScope();
|
|
|
|
if (snapshot_buffer != NULL) {
|
|
// Setup the native resolver as the snapshot does not carry it.
|
|
Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
|
|
Builtin::SetNativeResolver(Builtin::kIOLibrary);
|
|
}
|
|
|
|
// Set up the library tag handler for this isolate.
|
|
Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler);
|
|
SHUTDOWN_ON_ERROR(result);
|
|
|
|
// Load the specified application script into the newly created isolate.
|
|
|
|
// Prepare builtin and its dependent libraries for use to resolve URIs.
|
|
// The builtin library is part of the core snapshot and would already be
|
|
// available here in the case of script snapshot loading.
|
|
Dart_Handle builtin_lib =
|
|
Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
|
|
SHUTDOWN_ON_ERROR(builtin_lib);
|
|
|
|
// Prepare for script loading by setting up the 'print' and 'timer'
|
|
// closures and setting up 'package root' for URI resolution.
|
|
result = DartUtils::PrepareForScriptLoading("", builtin_lib);
|
|
SHUTDOWN_ON_ERROR(result);
|
|
|
|
{
|
|
// Load source into service isolate.
|
|
Dart_Handle library = LoadScript(kVMServiceIOLibraryScriptResourceName);
|
|
SHUTDOWN_ON_ERROR(library);
|
|
}
|
|
|
|
// Make the isolate runnable so that it is ready to handle messages.
|
|
Dart_ExitScope();
|
|
Dart_ExitIsolate();
|
|
|
|
bool retval = Dart_IsolateMakeRunnable(isolate_);
|
|
if (!retval) {
|
|
Dart_EnterIsolate(isolate_);
|
|
Dart_ShutdownIsolate();
|
|
error_msg_ = "Invalid isolate state - Unable to make it runnable.";
|
|
return false;
|
|
}
|
|
|
|
Dart_EnterIsolate(isolate_);
|
|
Dart_EnterScope();
|
|
|
|
|
|
Dart_Handle library = Dart_RootLibrary();
|
|
// Set requested TCP port.
|
|
DartUtils::SetIntegerField(library, "_port", server_port);
|
|
result = Dart_Invoke(library, DartUtils::NewString("main"), 0, NULL);
|
|
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 receivePort = Dart_GetField(library,
|
|
DartUtils::NewString("_receivePort"));
|
|
SHUTDOWN_ON_ERROR(receivePort);
|
|
|
|
{
|
|
// Extract the Dart_Port from the receive port.
|
|
HANDLESCOPE(Isolate::Current());
|
|
const Object& unwrapped_rp = Object::Handle(Api::UnwrapHandle(receivePort));
|
|
const Instance& rp = Instance::Cast(unwrapped_rp);
|
|
// Extract ReceivePort port id.
|
|
const Object& rp_id_obj = Object::Handle(DartLibraryCalls::PortGetId(rp));
|
|
if (rp_id_obj.IsError()) {
|
|
const Error& error = Error::Cast(rp_id_obj);
|
|
error_msg_ = strdup(error.ToErrorCString());
|
|
Dart_ExitScope();
|
|
Dart_ShutdownIsolate();
|
|
return false;
|
|
}
|
|
ASSERT(rp_id_obj.IsSmi() || rp_id_obj.IsMint());
|
|
Integer& id = Integer::Handle();
|
|
id ^= rp_id_obj.raw();
|
|
port_ = static_cast<Dart_Port>(id.AsInt64Value());
|
|
}
|
|
|
|
Dart_Handle library_name = Dart_NewStringFromCString(kVMServiceLibraryName);
|
|
library = Dart_LookupLibrary(library_name);
|
|
SHUTDOWN_ON_ERROR(library);
|
|
result = LoadResources(library);
|
|
SHUTDOWN_ON_ERROR(result);
|
|
|
|
Dart_ExitScope();
|
|
Dart_ExitIsolate();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void VmService::_Stop() {
|
|
port_ = ILLEGAL_PORT;
|
|
}
|
|
|
|
|
|
const char* VmService::GetErrorMessage() {
|
|
return error_msg_ == NULL ? "No error." : error_msg_;
|
|
}
|
|
|
|
|
|
Dart_Port VmService::port() {
|
|
return port_;
|
|
}
|
|
|
|
|
|
bool VmService::IsRunning() {
|
|
return port_ != ILLEGAL_PORT;
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::GetSource(const char* name) {
|
|
const char* vmservice_source = NULL;
|
|
int r = Resources::ResourceLookup(name, &vmservice_source);
|
|
ASSERT(r != Resources::kNoSuchInstance);
|
|
return Dart_NewStringFromCString(vmservice_source);
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::LoadScript(const char* name) {
|
|
Dart_Handle url = Dart_NewStringFromCString(name);
|
|
Dart_Handle source = GetSource(name);
|
|
return Dart_LoadScript(url, source, 0, 0);
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::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 VmService::LoadSources(Dart_Handle library, const char* names[]) {
|
|
Dart_Handle result = Dart_Null();
|
|
for (int i = 0; names[i] != NULL; i++) {
|
|
result = LoadSource(library, names[i]);
|
|
if (Dart_IsError(result)) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::LoadResource(Dart_Handle library,
|
|
const char* resource_name,
|
|
const char* prefix) {
|
|
intptr_t prefix_len = strlen(prefix);
|
|
// Prepare for invoke call.
|
|
Dart_Handle name = Dart_NewStringFromCString(resource_name+prefix_len);
|
|
RETURN_ERROR_HANDLE(name);
|
|
const char* data_buffer = NULL;
|
|
int data_buffer_length = Resources::ResourceLookup(resource_name,
|
|
&data_buffer);
|
|
if (data_buffer_length == Resources::kNoSuchInstance) {
|
|
printf("Could not find %s %s\n", resource_name, resource_name+prefix_len);
|
|
}
|
|
ASSERT(data_buffer_length != Resources::kNoSuchInstance);
|
|
Dart_Handle data_list = Dart_NewTypedData(Dart_TypedData_kUint8,
|
|
data_buffer_length);
|
|
RETURN_ERROR_HANDLE(data_list);
|
|
Dart_TypedData_Type type = Dart_TypedData_kInvalid;
|
|
void* data_list_buffer = NULL;
|
|
intptr_t data_list_buffer_length = 0;
|
|
Dart_Handle result = Dart_TypedDataAcquireData(data_list, &type,
|
|
&data_list_buffer,
|
|
&data_list_buffer_length);
|
|
RETURN_ERROR_HANDLE(result);
|
|
ASSERT(data_buffer_length == data_list_buffer_length);
|
|
ASSERT(data_list_buffer != NULL);
|
|
ASSERT(type = Dart_TypedData_kUint8);
|
|
memmove(data_list_buffer, &data_buffer[0], data_buffer_length);
|
|
result = Dart_TypedDataReleaseData(data_list);
|
|
RETURN_ERROR_HANDLE(result);
|
|
|
|
// Make invoke call.
|
|
const intptr_t kNumArgs = 2;
|
|
Dart_Handle args[kNumArgs] = { name, data_list };
|
|
result = Dart_Invoke(library, Dart_NewStringFromCString("_addResource"),
|
|
kNumArgs, args);
|
|
return result;
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::LoadResources(Dart_Handle library) {
|
|
Dart_Handle result = Dart_Null();
|
|
intptr_t prefixLen = strlen(kClientResourceNamePrefix);
|
|
for (intptr_t i = 0; i < Resources::get_resource_count(); i++) {
|
|
const char* path = Resources::get_resource_path(i);
|
|
if (!strncmp(path, kClientResourceNamePrefix, prefixLen)) {
|
|
result = LoadResource(library, path, kClientResourceNamePrefix);
|
|
if (Dart_IsError(result)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
static bool IsVMServiceURL(const char* url) {
|
|
static const intptr_t kLibraryResourceNamePrefixLen =
|
|
strlen(kLibraryResourceNamePrefix);
|
|
return 0 == strncmp(kLibraryResourceNamePrefix, url,
|
|
kLibraryResourceNamePrefixLen);
|
|
}
|
|
|
|
|
|
static bool IsVMServiceLibrary(const char* url) {
|
|
return 0 == strcmp(kVMServiceLibraryName, url);
|
|
}
|
|
|
|
|
|
Dart_Handle VmService::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* url_string = NULL;
|
|
Dart_Handle result = Dart_StringToCString(url, &url_string);
|
|
if (Dart_IsError(result)) {
|
|
return result;
|
|
}
|
|
Dart_Handle library_url = Dart_LibraryUrl(library);
|
|
const char* library_url_string = NULL;
|
|
result = Dart_StringToCString(library_url, &library_url_string);
|
|
if (Dart_IsError(result)) {
|
|
return result;
|
|
}
|
|
bool is_vm_service_url = IsVMServiceURL(url_string);
|
|
if (!is_vm_service_url) {
|
|
// Pass to DartUtils.
|
|
return DartUtils::LibraryTagHandler(tag, library, url);
|
|
}
|
|
switch (tag) {
|
|
case Dart_kCanonicalizeUrl:
|
|
// The URL is already canonicalized.
|
|
return url;
|
|
break;
|
|
case Dart_kImportTag: {
|
|
Dart_Handle source = GetSource(url_string);
|
|
if (Dart_IsError(source)) {
|
|
return source;
|
|
}
|
|
Dart_Handle lib = Dart_LoadLibrary(url, source);
|
|
if (Dart_IsError(lib)) {
|
|
return lib;
|
|
}
|
|
if (IsVMServiceLibrary(url_string)) {
|
|
// Install native resolver for this library.
|
|
result = Dart_SetNativeResolver(lib, VmServiceNativeResolver);
|
|
if (Dart_IsError(result)) {
|
|
return result;
|
|
}
|
|
}
|
|
return lib;
|
|
}
|
|
break;
|
|
case Dart_kSourceTag: {
|
|
Dart_Handle source = GetSource(url_string);
|
|
if (Dart_IsError(source)) {
|
|
return source;
|
|
}
|
|
return Dart_LoadSource(library, url, source);
|
|
}
|
|
break;
|
|
default:
|
|
UNIMPLEMENTED();
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
return result;
|
|
}
|
|
|
|
|
|
void VmService::ThreadMain(uword parameters) {
|
|
ASSERT(Dart_CurrentIsolate() == NULL);
|
|
ASSERT(isolate_ == NULL);
|
|
|
|
intptr_t server_port = static_cast<intptr_t>(parameters);
|
|
ASSERT(server_port >= 0);
|
|
|
|
// Lock scope.
|
|
{
|
|
MonitorLocker ml(monitor_);
|
|
bool r = _Start(server_port);
|
|
if (!r) {
|
|
port_ = ILLEGAL_PORT;
|
|
monitor_->Notify();
|
|
return;
|
|
}
|
|
|
|
Dart_EnterIsolate(isolate_);
|
|
Dart_EnterScope();
|
|
|
|
Dart_Handle receievePort = Dart_GetReceivePort(port_);
|
|
ASSERT(!Dart_IsError(receievePort));
|
|
monitor_->Notify();
|
|
}
|
|
|
|
// Keep handling messages until the last active receive port is closed.
|
|
Dart_Handle result = Dart_RunLoop();
|
|
if (Dart_IsError(result)) {
|
|
printf("VmService has exited with an error:\n%s\n", Dart_GetError(result));
|
|
}
|
|
|
|
_Stop();
|
|
|
|
Dart_ExitScope();
|
|
Dart_ExitIsolate();
|
|
}
|
|
|
|
|
|
static Dart_Handle MakeServiceControlMessage(Dart_Port port_id, intptr_t code,
|
|
Dart_Handle name) {
|
|
Dart_Handle result;
|
|
Dart_Handle list = Dart_NewList(4);
|
|
ASSERT(!Dart_IsError(list));
|
|
Dart_Handle code_handle = Dart_NewInteger(code);
|
|
ASSERT(!Dart_IsError(code_handle));
|
|
result = Dart_ListSetAt(list, 0, code_handle);
|
|
ASSERT(!Dart_IsError(result));
|
|
Dart_Handle port_id_handle = Dart_NewInteger(port_id);
|
|
ASSERT(!Dart_IsError(port_id_handle));
|
|
result = Dart_ListSetAt(list, 1, port_id_handle);
|
|
ASSERT(!Dart_IsError(result));
|
|
Dart_Handle sendPort = Dart_NewSendPort(port_id);
|
|
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 VmService::SendIsolateStartupMessage() {
|
|
if (!IsRunning()) {
|
|
return false;
|
|
}
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
HANDLESCOPE(isolate);
|
|
Dart_Handle name = Api::NewHandle(isolate, String::New(isolate->name()));
|
|
ASSERT(!Dart_IsError(name));
|
|
Dart_Handle list =
|
|
MakeServiceControlMessage(Dart_GetMainPortId(),
|
|
VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID,
|
|
name);
|
|
ASSERT(!Dart_IsError(list));
|
|
return Dart_Post(port_, list);
|
|
}
|
|
|
|
|
|
bool VmService::SendIsolateShutdownMessage() {
|
|
if (!IsRunning()) {
|
|
return false;
|
|
}
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
HANDLESCOPE(isolate);
|
|
Dart_Handle list =
|
|
MakeServiceControlMessage(Dart_GetMainPortId(),
|
|
VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID,
|
|
Dart_Null());
|
|
ASSERT(!Dart_IsError(list));
|
|
return Dart_Post(port_, list);
|
|
}
|
|
|
|
|
|
void VmService::VmServiceShutdownCallback(void* callback_data) {
|
|
ASSERT(Dart_CurrentIsolate() != NULL);
|
|
Dart_EnterScope();
|
|
VmService::SendIsolateShutdownMessage();
|
|
Dart_ExitScope();
|
|
}
|
|
|
|
|
|
static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) {
|
|
void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size);
|
|
return reinterpret_cast<uint8_t*>(new_ptr);
|
|
}
|
|
|
|
|
|
static void SendServiceMessage(Dart_NativeArguments args) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
Isolate* isolate = arguments->isolate();
|
|
StackZone zone(isolate);
|
|
HANDLESCOPE(isolate);
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, sp, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(1));
|
|
|
|
// Extract SendPort port id.
|
|
const Object& sp_id_obj = Object::Handle(DartLibraryCalls::PortGetId(sp));
|
|
if (sp_id_obj.IsError()) {
|
|
Exceptions::PropagateError(Error::Cast(sp_id_obj));
|
|
}
|
|
Integer& id = Integer::Handle();
|
|
id ^= sp_id_obj.raw();
|
|
Dart_Port sp_id = static_cast<Dart_Port>(id.AsInt64Value());
|
|
ASSERT(sp_id != ILLEGAL_PORT);
|
|
|
|
// Serialize message.
|
|
uint8_t* data = NULL;
|
|
MessageWriter writer(&data, &allocator);
|
|
writer.WriteMessage(message);
|
|
|
|
// TODO(turnidge): Throw an exception when the return value is false?
|
|
PortMap::PostMessage(new Message(sp_id, data, writer.BytesWritten(),
|
|
Message::kOOBPriority));
|
|
}
|
|
|
|
|
|
struct VmServiceNativeEntry {
|
|
const char* name;
|
|
int num_arguments;
|
|
Dart_NativeFunction function;
|
|
};
|
|
|
|
|
|
static VmServiceNativeEntry _VmServiceNativeEntries[] = {
|
|
{"VMService_SendServiceMessage", 2, SendServiceMessage}
|
|
};
|
|
|
|
|
|
static Dart_NativeFunction VmServiceNativeResolver(Dart_Handle name,
|
|
int num_arguments,
|
|
bool* auto_setup_scope) {
|
|
const Object& obj = Object::Handle(Api::UnwrapHandle(name));
|
|
if (!obj.IsString()) {
|
|
return NULL;
|
|
}
|
|
const char* function_name = obj.ToCString();
|
|
ASSERT(function_name != NULL);
|
|
ASSERT(auto_setup_scope != NULL);
|
|
*auto_setup_scope = true;
|
|
intptr_t n =
|
|
sizeof(_VmServiceNativeEntries) / sizeof(_VmServiceNativeEntries[0]);
|
|
for (intptr_t i = 0; i < n; i++) {
|
|
VmServiceNativeEntry entry = _VmServiceNativeEntries[i];
|
|
if (!strcmp(function_name, entry.name) &&
|
|
(num_arguments == entry.num_arguments)) {
|
|
return entry.function;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|