blob: ea975ca0aad2fe49ea8864e450005eb7d882007f [file] [log] [blame]
// Copyright (c) 2012, 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 "platform/assert.h"
#include "vm/bootstrap_natives.h"
#include "vm/class_finalizer.h"
#include "vm/dart.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/longjump.h"
#include "vm/message_handler.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/port.h"
#include "vm/resolver.h"
#include "vm/snapshot.h"
#include "vm/symbols.h"
#include "vm/thread.h"
namespace dart {
class IsolateStartData {
public:
IsolateStartData(char* library_url,
char* class_name,
intptr_t port_id)
: library_url_(library_url),
class_name_(class_name),
port_id_(port_id) {}
char* library_url_;
char* class_name_;
intptr_t port_id_;
};
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 StoreError(Isolate* isolate, const Object& obj) {
ASSERT(obj.IsError());
isolate->object_store()->set_sticky_error(Error::Cast(obj));
}
// TODO(turnidge): Move to DartLibraryCalls.
static RawObject* ReceivePortCreate(intptr_t port_id) {
Isolate* isolate = Isolate::Current();
Function& func =
Function::Handle(isolate,
isolate->object_store()->receive_port_create_function());
const int kNumArguments = 1;
if (func.IsNull()) {
Library& isolate_lib = Library::Handle(Library::IsolateLibrary());
ASSERT(!isolate_lib.IsNull());
const String& class_name =
String::Handle(isolate_lib.PrivateName(Symbols::_ReceivePortImpl()));
const String& function_name =
String::Handle(isolate_lib.PrivateName(Symbols::_get_or_create()));
func = Resolver::ResolveStatic(isolate_lib,
class_name,
function_name,
kNumArguments,
Object::empty_array(),
Resolver::kIsQualified);
ASSERT(!func.IsNull());
isolate->object_store()->set_receive_port_create_function(func);
}
const Array& args = Array::Handle(isolate, Array::New(kNumArguments));
args.SetAt(0, Integer::Handle(isolate, Integer::New(port_id)));
const Object& result =
Object::Handle(isolate, DartEntry::InvokeFunction(func, args));
if (!result.IsError()) {
PortMap::SetLive(port_id);
}
return result.raw();
}
static void ShutdownIsolate(uword parameter) {
Isolate* isolate = reinterpret_cast<Isolate*>(parameter);
{
// Print the error if there is one. This may execute dart code to
// print the exception object, so we need to use a StartIsolateScope.
StartIsolateScope start_scope(isolate);
StackZone zone(isolate);
HandleScope handle_scope(isolate);
Error& error = Error::Handle();
error = isolate->object_store()->sticky_error();
if (!error.IsNull()) {
OS::PrintErr("in ShutdownIsolate: %s\n", error.ToErrorCString());
}
}
{
// Shut the isolate down.
SwitchIsolateScope switch_scope(isolate);
Dart::ShutdownIsolate();
}
}
static char* GetRootScriptUri(Isolate* isolate) {
const Library& library =
Library::Handle(isolate->object_store()->root_library());
ASSERT(!library.IsNull());
const String& script_name = String::Handle(library.url());
return isolate->current_zone()->MakeCopyOfString(script_name.ToCString());
}
DEFINE_NATIVE_ENTRY(ReceivePortImpl_factory, 1) {
ASSERT(AbstractTypeArguments::CheckedHandle(
arguments->NativeArgAt(0)).IsNull());
intptr_t port_id =
PortMap::CreatePort(arguments->isolate()->message_handler());
const Object& port = Object::Handle(ReceivePortCreate(port_id));
if (port.IsError()) {
Exceptions::PropagateError(Error::Cast(port));
}
return port.raw();
}
DEFINE_NATIVE_ENTRY(ReceivePortImpl_closeInternal, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, id, arguments->NativeArgAt(0));
PortMap::ClosePort(id.Value());
return Object::null();
}
DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 3) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, send_id, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, reply_id, arguments->NativeArgAt(1));
// TODO(iposva): Allow for arbitrary messages to be sent.
GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(2));
uint8_t* data = NULL;
MessageWriter writer(&data, &allocator);
writer.WriteMessage(obj);
// TODO(turnidge): Throw an exception when the return value is false?
PortMap::PostMessage(new Message(send_id.Value(), reply_id.Value(),
data, writer.BytesWritten(),
Message::kNormalPriority));
return Object::null();
}
static void ThrowIllegalArgException(const String& message) {
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
Exceptions::ThrowByType(Exceptions::kArgument, args);
}
static void ThrowIsolateSpawnException(const String& message) {
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
Exceptions::ThrowByType(Exceptions::kIsolateSpawn, args);
}
static bool CanonicalizeUri(Isolate* isolate,
const Library& library,
const String& uri,
char** canonical_uri,
char** error) {
Zone* zone = isolate->current_zone();
Dart_LibraryTagHandler handler = isolate->library_tag_handler();
if (handler == NULL) {
*error = zone->PrintToString(
"Unable to canonicalize uri '%s': no library tag handler found.",
uri.ToCString());
return false;
}
Dart_Handle result = handler(kCanonicalizeUrl,
Api::NewHandle(isolate, library.raw()),
Api::NewHandle(isolate, uri.raw()));
const Object& obj = Object::Handle(Api::UnwrapHandle(result));
if (obj.IsError()) {
Error& error_obj = Error::Handle();
error_obj ^= obj.raw();
*error = zone->PrintToString("Unable to canonicalize uri '%s': %s",
uri.ToCString(), error_obj.ToErrorCString());
return false;
} else if (obj.IsString()) {
*canonical_uri = zone->MakeCopyOfString(String::Cast(obj).ToCString());
return true;
} else {
*error = zone->PrintToString("Unable to canonicalize uri '%s': "
"library tag handler returned wrong type",
uri.ToCString());
return false;
}
}
class SpawnState {
public:
SpawnState(const Function& func, const Function& callback_func)
: isolate_(NULL),
script_url_(NULL),
library_url_(NULL),
function_name_(NULL),
exception_callback_name_(NULL) {
script_url_ = strdup(GetRootScriptUri(Isolate::Current()));
const Class& cls = Class::Handle(func.Owner());
ASSERT(cls.IsTopLevel());
const Library& lib = Library::Handle(cls.library());
const String& lib_url = String::Handle(lib.url());
library_url_ = strdup(lib_url.ToCString());
const String& func_name = String::Handle(func.name());
function_name_ = strdup(func_name.ToCString());
if (!callback_func.IsNull()) {
const String& callback_name = String::Handle(callback_func.name());
exception_callback_name_ = strdup(callback_name.ToCString());
} else {
exception_callback_name_ = strdup("_unhandledExceptionCallback");
}
}
explicit SpawnState(const char* script_url)
: isolate_(NULL),
library_url_(NULL),
function_name_(NULL),
exception_callback_name_(NULL) {
script_url_ = strdup(script_url);
library_url_ = NULL;
function_name_ = strdup("main");
exception_callback_name_ = strdup("_unhandledExceptionCallback");
}
~SpawnState() {
free(script_url_);
free(library_url_);
free(function_name_);
free(exception_callback_name_);
}
Isolate* isolate() const { return isolate_; }
void set_isolate(Isolate* value) { isolate_ = value; }
char* script_url() const { return script_url_; }
char* library_url() const { return library_url_; }
char* function_name() const { return function_name_; }
char* exception_callback_name() const { return exception_callback_name_; }
RawObject* ResolveFunction() {
// Resolve the library.
Library& lib = Library::Handle();
if (library_url()) {
const String& lib_url = String::Handle(String::New(library_url()));
lib = Library::LookupLibrary(lib_url);
if (lib.IsNull() || lib.IsError()) {
const String& msg = String::Handle(String::NewFormatted(
"Unable to find library '%s'.", library_url()));
return LanguageError::New(msg);
}
} else {
lib = isolate()->object_store()->root_library();
}
ASSERT(!lib.IsNull());
// Resolve the function.
const String& func_name =
String::Handle(String::New(function_name()));
const Function& func = Function::Handle(lib.LookupLocalFunction(func_name));
if (func.IsNull()) {
const String& msg = String::Handle(String::NewFormatted(
"Unable to resolve function '%s' in library '%s'.",
function_name(), (library_url() ? library_url() : script_url())));
return LanguageError::New(msg);
}
return func.raw();
}
void Cleanup() {
SwitchIsolateScope switch_scope(isolate());
Dart::ShutdownIsolate();
}
private:
Isolate* isolate_;
char* script_url_;
char* library_url_;
char* function_name_;
char* exception_callback_name_;
};
static bool CreateIsolate(SpawnState* state, char** error) {
Isolate* parent_isolate = Isolate::Current();
Dart_IsolateCreateCallback callback = Isolate::CreateCallback();
if (callback == NULL) {
*error = strdup("Null callback specified for isolate creation\n");
Isolate::SetCurrent(parent_isolate);
return false;
}
void* init_data = parent_isolate->init_callback_data();
bool retval = (callback)(state->script_url(),
state->function_name(),
init_data,
error);
if (!retval) {
Isolate::SetCurrent(parent_isolate);
return false;
}
Isolate* child_isolate = Isolate::Current();
ASSERT(child_isolate);
state->set_isolate(child_isolate);
// Attempt to resolve the entry function now, so that we fail fast
// in the case that the function cannot be resolved.
//
// TODO(turnidge): Revisit this once we have an isolate death api.
bool resolve_error = false;
{
StackZone zone(child_isolate);
HandleScope handle_scope(child_isolate);
const Object& result = Object::Handle(state->ResolveFunction());
if (result.IsError()) {
Error& errobj = Error::Handle();
errobj ^= result.raw();
*error = strdup(errobj.ToErrorCString());
resolve_error = true;
} else {
const String& callback_name =
String::Handle(child_isolate,
String::New(state->exception_callback_name()));
child_isolate->object_store()->
set_unhandled_exception_handler(callback_name);
}
}
if (resolve_error) {
Dart::ShutdownIsolate();
Isolate::SetCurrent(parent_isolate);
return false;
}
Isolate::SetCurrent(parent_isolate);
return true;
}
static bool RunIsolate(uword parameter) {
Isolate* isolate = reinterpret_cast<Isolate*>(parameter);
SpawnState* state = reinterpret_cast<SpawnState*>(isolate->spawn_data());
isolate->set_spawn_data(0);
{
StartIsolateScope start_scope(isolate);
StackZone zone(isolate);
HandleScope handle_scope(isolate);
if (!ClassFinalizer::FinalizePendingClasses()) {
// Error is in sticky error already.
return false;
}
Object& result = Object::Handle();
result = state->ResolveFunction();
delete state;
state = NULL;
if (result.IsError()) {
StoreError(isolate, result);
return false;
}
ASSERT(result.IsFunction());
Function& func = Function::Handle(isolate);
func ^= result.raw();
result = DartEntry::InvokeFunction(func, Object::empty_array());
if (result.IsError()) {
StoreError(isolate, result);
return false;
}
}
return true;
}
static RawObject* Spawn(NativeArguments* arguments, SpawnState* state) {
// Create a new isolate.
char* error = NULL;
if (!CreateIsolate(state, &error)) {
delete state;
const String& msg = String::Handle(String::New(error));
free(error);
ThrowIsolateSpawnException(msg);
}
// Try to create a SendPort for the new isolate.
const Object& port = Object::Handle(
DartLibraryCalls::NewSendPort(state->isolate()->main_port()));
if (port.IsError()) {
state->Cleanup();
delete state;
Exceptions::PropagateError(Error::Cast(port));
}
// Start the new isolate.
state->isolate()->set_spawn_data(reinterpret_cast<uword>(state));
state->isolate()->message_handler()->Run(
Dart::thread_pool(), RunIsolate, ShutdownIsolate,
reinterpret_cast<uword>(state->isolate()));
return port.raw();
}
DEFINE_NATIVE_ENTRY(isolate_spawnFunction, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(0));
bool throw_exception = false;
Function& func = Function::Handle();
if (closure.IsClosure()) {
func = Closure::function(closure);
const Class& cls = Class::Handle(func.Owner());
if (!func.IsClosureFunction() || !func.is_static() || !cls.IsTopLevel()) {
throw_exception = true;
}
} else {
throw_exception = true;
}
if (throw_exception) {
const String& msg = String::Handle(String::New(
"spawnFunction expects to be passed a closure to a top-level static "
"function"));
ThrowIllegalArgException(msg);
}
GET_NATIVE_ARGUMENT(Instance, callback, arguments->NativeArgAt(1));
Function& callback_func = Function::Handle();
if (callback.IsClosure()) {
callback_func = Closure::function(callback);
const Class& cls = Class::Handle(callback_func.Owner());
if (!callback_func.IsClosureFunction() || !callback_func.is_static() ||
!cls.IsTopLevel()) {
throw_exception = true;
}
} else if (!callback.IsNull()) {
throw_exception = true;
}
if (throw_exception) {
const String& msg = String::Handle(String::New(
"spawnFunction expects to be passed either a unhandled exception "
"callback to a top-level static function, or null"));
ThrowIllegalArgException(msg);
}
#if defined(DEBUG)
Context& ctx = Context::Handle();
ctx = Closure::context(closure);
ASSERT(ctx.num_variables() == 0);
if (!callback.IsNull()) {
ctx = Closure::context(callback);
ASSERT(ctx.num_variables() == 0);
}
#endif
return Spawn(arguments, new SpawnState(func, callback_func));
}
DEFINE_NATIVE_ENTRY(isolate_spawnUri, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(0));
// Canonicalize the uri with respect to the current isolate.
char* error = NULL;
char* canonical_uri = NULL;
const Library& root_lib =
Library::Handle(arguments->isolate()->object_store()->root_library());
if (!CanonicalizeUri(arguments->isolate(), root_lib, uri,
&canonical_uri, &error)) {
const String& msg = String::Handle(String::New(error));
ThrowIsolateSpawnException(msg);
}
return Spawn(arguments, new SpawnState(canonical_uri));
}
DEFINE_NATIVE_ENTRY(isolate_getPortInternal, 0) {
const Object& port = Object::Handle(ReceivePortCreate(isolate->main_port()));
if (port.IsError()) {
Exceptions::PropagateError(Error::Cast(port));
}
return port.raw();
}
} // namespace dart