blob: 922ad2b09ff32ac5157d9393e3afbf7bba4edae5 [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 <memory>
#include <utility>
#include "include/dart_native_api.h"
#include "platform/assert.h"
#include "platform/unicode.h"
#include "vm/bootstrap_natives.h"
#include "vm/class_finalizer.h"
#include "vm/dart.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_api_message.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/hash_table.h"
#include "vm/lockers.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/service.h"
#include "vm/snapshot.h"
#include "vm/symbols.h"
namespace dart {
DEFINE_NATIVE_ENTRY(CapabilityImpl_factory, 0, 1) {
ASSERT(
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
uint64_t id = isolate->random()->NextUInt64();
return Capability::New(id);
}
DEFINE_NATIVE_ENTRY(CapabilityImpl_equals, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Capability, recv, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Capability, other, arguments->NativeArgAt(1));
return (recv.Id() == other.Id()) ? Bool::True().ptr() : Bool::False().ptr();
}
DEFINE_NATIVE_ENTRY(CapabilityImpl_get_hashcode, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Capability, cap, arguments->NativeArgAt(0));
int64_t id = cap.Id();
int32_t hi = static_cast<int32_t>(id >> 32);
int32_t lo = static_cast<int32_t>(id);
int32_t hash = (hi ^ lo) & kSmiMax;
return Smi::New(hash);
}
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 2) {
ASSERT(
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
GET_NON_NULL_NATIVE_ARGUMENT(String, debug_name, arguments->NativeArgAt(1));
Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
return ReceivePort::New(port_id, debug_name, false /* not control port */);
}
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_id, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
return Integer::New(port.Id());
}
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_sendport, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
return port.send_port();
}
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_closeInternal, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
Dart_Port id = port.Id();
PortMap::ClosePort(id);
return Integer::New(id);
}
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_setActive, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Bool, active, arguments->NativeArgAt(1));
Dart_Port id = port.Id();
PortMap::SetPortState(
id, active.value() ? PortMap::kLivePort : PortMap::kInactivePort);
return Object::null();
}
DEFINE_NATIVE_ENTRY(SendPortImpl_get_id, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
return Integer::New(port.Id());
}
DEFINE_NATIVE_ENTRY(SendPortImpl_get_hashcode, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
int64_t id = port.Id();
int32_t hi = static_cast<int32_t>(id >> 32);
int32_t lo = static_cast<int32_t>(id);
int32_t hash = (hi ^ lo) & kSmiMax;
return Smi::New(hash);
}
DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
// TODO(iposva): Allow for arbitrary messages to be sent.
GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
const Dart_Port destination_port_id = port.Id();
const bool can_send_any_object = isolate->origin_id() == port.origin_id();
if (ApiObjectConverter::CanConvert(obj.ptr())) {
PortMap::PostMessage(
Message::New(destination_port_id, obj.ptr(), Message::kNormalPriority));
} else {
MessageWriter writer(can_send_any_object);
// TODO(turnidge): Throw an exception when the return value is false?
PortMap::PostMessage(writer.WriteMessage(obj, destination_port_id,
Message::kNormalPriority));
}
return Object::null();
}
class UntaggedObjectPtrSetTraits {
public:
static bool ReportStats() { return false; }
static const char* Name() { return "RawObjectPtrSetTraits"; }
static bool IsMatch(const ObjectPtr a, const ObjectPtr b) { return a == b; }
static uword Hash(const ObjectPtr obj) { return static_cast<uword>(obj); }
};
static ObjectPtr ValidateMessageObject(Zone* zone,
Isolate* isolate,
const Object& obj) {
TIMELINE_DURATION(Thread::Current(), Isolate, "ValidateMessageObject");
class SendMessageValidator : public ObjectPointerVisitor {
public:
SendMessageValidator(IsolateGroup* isolate_group,
WeakTable* visited,
MallocGrowableArray<ObjectPtr>* const working_set)
: ObjectPointerVisitor(isolate_group),
visited_(visited),
working_set_(working_set) {}
private:
void VisitPointers(ObjectPtr* from, ObjectPtr* to) {
for (ObjectPtr* ptr = from; ptr <= to; ptr++) {
VisitObject(*ptr);
}
}
void VisitCompressedPointers(uword heap_base,
CompressedObjectPtr* from,
CompressedObjectPtr* to) {
for (CompressedObjectPtr* ptr = from; ptr <= to; ptr++) {
VisitObject(ptr->Decompress(heap_base));
}
}
void VisitObject(ObjectPtr obj) {
if (!obj->IsHeapObject() || obj->untag()->IsCanonical()) {
return;
}
if (visited_->GetValueExclusive(obj) == 1) {
return;
}
visited_->SetValueExclusive(obj, 1);
working_set_->Add(obj);
}
WeakTable* visited_;
MallocGrowableArray<ObjectPtr>* const working_set_;
};
if (!obj.ptr()->IsHeapObject() || obj.ptr()->untag()->IsCanonical()) {
return obj.ptr();
}
ClassTable* class_table = isolate->group()->class_table();
Class& klass = Class::Handle(zone);
Closure& closure = Closure::Handle(zone);
bool error_found = false;
Function& erroneous_closure_function = Function::Handle(zone);
Class& erroneous_nativewrapper_class = Class::Handle(zone);
const char* error_message = nullptr;
{
NoSafepointScope no_safepoint;
// working_set contains only elements that have not been visited yet that
// need to be processed.
// So before adding elements to working_set ensure to check visited flag,
// set visited flag at the same time as the element is added.
MallocGrowableArray<ObjectPtr> working_set;
std::unique_ptr<WeakTable> visited(new WeakTable());
SendMessageValidator visitor(isolate->group(), visited.get(), &working_set);
visited->SetValueExclusive(obj.ptr(), 1);
working_set.Add(obj.ptr());
while (!working_set.is_empty() && !error_found) {
ObjectPtr raw = working_set.RemoveLast();
const intptr_t cid = raw->GetClassId();
switch (cid) {
// List below matches the one in raw_object_snapshot.cc
#define MESSAGE_SNAPSHOT_ILLEGAL(type) \
case k##type##Cid: \
error_message = \
"Illegal argument in isolate message : (object is a " #type ")"; \
error_found = true; \
break;
MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary);
MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference);
MESSAGE_SNAPSHOT_ILLEGAL(Pointer);
MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort);
MESSAGE_SNAPSHOT_ILLEGAL(RegExp);
MESSAGE_SNAPSHOT_ILLEGAL(StackTrace);
MESSAGE_SNAPSHOT_ILLEGAL(UserTag);
case kClosureCid: {
closure = Closure::RawCast(raw);
FunctionPtr func = closure.function();
// We only allow closure of top level methods or static functions in a
// class to be sent in isolate messages.
if (!Function::IsImplicitStaticClosureFunction(func)) {
// All other closures are errors.
erroneous_closure_function = func;
error_found = true;
break;
}
break;
}
default:
if (cid >= kNumPredefinedCids) {
klass = class_table->At(cid);
if (klass.num_native_fields() != 0) {
erroneous_nativewrapper_class = klass.ptr();
error_found = true;
break;
}
}
}
raw->untag()->VisitPointers(&visitor);
}
}
if (error_found) {
const char* exception_message;
if (error_message != nullptr) {
exception_message = error_message;
} else if (!erroneous_closure_function.IsNull()) {
exception_message = OS::SCreate(zone,
"Illegal argument in isolate message"
" : (object is a closure - %s)",
erroneous_closure_function.ToCString());
} else {
ASSERT(!erroneous_nativewrapper_class.IsNull());
exception_message =
OS::SCreate(zone,
"Illegal argument in isolate message"
" : (object extends NativeWrapper - %s)",
erroneous_nativewrapper_class.ToCString());
}
return Exceptions::CreateUnhandledException(
zone, Exceptions::kArgumentValue, exception_message);
}
isolate->set_forward_table_new(nullptr);
return obj.ptr();
}
DEFINE_NATIVE_ENTRY(SendPortImpl_sendAndExitInternal_, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
if (!PortMap::IsReceiverInThisIsolateGroup(port.Id(), isolate->group())) {
const auto& error =
String::Handle(String::New("sendAndExit is only supported across "
"isolates spawned via spawnFunction."));
Exceptions::ThrowArgumentError(error);
UNREACHABLE();
}
GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
Object& validated_result = Object::Handle(zone);
const Object& msg_obj = Object::Handle(zone, obj.ptr());
validated_result = ValidateMessageObject(zone, isolate, msg_obj);
// msg_array = [<message>, <object-in-message-to-rehash>]
const Array& msg_array = Array::Handle(zone, Array::New(2));
msg_array.SetAt(0, msg_obj);
if (validated_result.IsUnhandledException()) {
Exceptions::PropagateError(Error::Cast(validated_result));
UNREACHABLE();
}
PersistentHandle* handle =
isolate->group()->api_state()->AllocatePersistentHandle();
handle->set_ptr(msg_array);
isolate->bequeath(std::unique_ptr<Bequest>(new Bequest(handle, port.Id())));
// TODO(aam): Ensure there are no dart api calls after this point as we want
// to ensure that validated message won't get tampered with.
Isolate::KillIfExists(isolate, Isolate::LibMsgId::kKillMsg);
// Drain interrupts before running so any IMMEDIATE operations on the current
// isolate happen synchronously.
const Error& error = Error::Handle(thread->HandleInterrupts());
RELEASE_ASSERT(error.IsUnwindError());
Exceptions::PropagateError(error);
// We will never execute dart code again in this isolate.
return Object::null();
}
class IsolateSpawnState {
public:
IsolateSpawnState(Dart_Port parent_port,
Dart_Port origin_id,
const char* script_url,
const Function& func,
SerializedObjectBuffer* message_buffer,
const char* package_config,
bool paused,
bool errorsAreFatal,
Dart_Port onExit,
Dart_Port onError,
const char* debug_name,
IsolateGroup* group);
IsolateSpawnState(Dart_Port parent_port,
const char* script_url,
const char* package_config,
SerializedObjectBuffer* args_buffer,
SerializedObjectBuffer* message_buffer,
bool paused,
bool errorsAreFatal,
Dart_Port onExit,
Dart_Port onError,
const char* debug_name,
IsolateGroup* group);
~IsolateSpawnState();
Isolate* isolate() const { return isolate_; }
void set_isolate(Isolate* value) { isolate_ = value; }
Dart_Port parent_port() const { return parent_port_; }
Dart_Port origin_id() const { return origin_id_; }
Dart_Port on_exit_port() const { return on_exit_port_; }
Dart_Port on_error_port() const { return on_error_port_; }
const char* script_url() const { return script_url_; }
const char* package_config() const { return package_config_; }
const char* library_url() const { return library_url_; }
const char* class_name() const { return class_name_; }
const char* function_name() const { return function_name_; }
const char* debug_name() const { return debug_name_; }
bool is_spawn_uri() const { return library_url_ == nullptr; }
bool paused() const { return paused_; }
bool errors_are_fatal() const { return errors_are_fatal_; }
Dart_IsolateFlags* isolate_flags() { return &isolate_flags_; }
ObjectPtr ResolveFunction();
InstancePtr BuildArgs(Thread* thread);
InstancePtr BuildMessage(Thread* thread);
IsolateGroup* isolate_group() const { return isolate_group_; }
private:
Isolate* isolate_ = nullptr;
Dart_Port parent_port_;
Dart_Port origin_id_ = ILLEGAL_PORT;
Dart_Port on_exit_port_;
Dart_Port on_error_port_;
const char* script_url_;
const char* package_config_;
const char* library_url_ = nullptr;
const char* class_name_ = nullptr;
const char* function_name_ = nullptr;
const char* debug_name_;
IsolateGroup* isolate_group_;
std::unique_ptr<Message> serialized_args_;
std::unique_ptr<Message> serialized_message_;
Dart_IsolateFlags isolate_flags_;
bool paused_;
bool errors_are_fatal_;
};
static const char* NewConstChar(const char* chars) {
size_t len = strlen(chars);
char* mem = new char[len + 1];
memmove(mem, chars, len + 1);
return mem;
}
IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
Dart_Port origin_id,
const char* script_url,
const Function& func,
SerializedObjectBuffer* message_buffer,
const char* package_config,
bool paused,
bool errors_are_fatal,
Dart_Port on_exit_port,
Dart_Port on_error_port,
const char* debug_name,
IsolateGroup* isolate_group)
: parent_port_(parent_port),
origin_id_(origin_id),
on_exit_port_(on_exit_port),
on_error_port_(on_error_port),
script_url_(script_url),
package_config_(package_config),
debug_name_(debug_name),
isolate_group_(isolate_group),
serialized_args_(nullptr),
serialized_message_(message_buffer->StealMessage()),
paused_(paused),
errors_are_fatal_(errors_are_fatal) {
auto thread = Thread::Current();
auto isolate = thread->isolate();
auto zone = thread->zone();
const auto& cls = Class::Handle(zone, func.Owner());
const auto& lib = Library::Handle(zone, cls.library());
const auto& lib_url = String::Handle(zone, lib.url());
library_url_ = NewConstChar(lib_url.ToCString());
String& func_name = String::Handle(zone);
func_name = func.name();
function_name_ = NewConstChar(String::ScrubName(func_name));
if (!cls.IsTopLevel()) {
const auto& class_name = String::Handle(zone, cls.Name());
class_name_ = NewConstChar(class_name.ToCString());
}
// Inherit flags from spawning isolate.
isolate->FlagsCopyTo(isolate_flags());
}
IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
const char* script_url,
const char* package_config,
SerializedObjectBuffer* args_buffer,
SerializedObjectBuffer* message_buffer,
bool paused,
bool errors_are_fatal,
Dart_Port on_exit_port,
Dart_Port on_error_port,
const char* debug_name,
IsolateGroup* group)
: parent_port_(parent_port),
on_exit_port_(on_exit_port),
on_error_port_(on_error_port),
script_url_(script_url),
package_config_(package_config),
debug_name_(debug_name),
isolate_group_(group),
serialized_args_(args_buffer->StealMessage()),
serialized_message_(message_buffer->StealMessage()),
isolate_flags_(),
paused_(paused),
errors_are_fatal_(errors_are_fatal) {
function_name_ = NewConstChar("main");
// By default inherit flags from spawning isolate. These can be overridden
// from the calling code.
Isolate::Current()->FlagsCopyTo(isolate_flags());
}
IsolateSpawnState::~IsolateSpawnState() {
delete[] script_url_;
delete[] package_config_;
delete[] library_url_;
delete[] class_name_;
delete[] function_name_;
delete[] debug_name_;
}
ObjectPtr IsolateSpawnState::ResolveFunction() {
Thread* thread = Thread::Current();
auto IG = thread->isolate_group();
Zone* zone = thread->zone();
const String& func_name = String::Handle(zone, String::New(function_name()));
if (library_url() == nullptr) {
// Handle spawnUri lookup rules.
// Check whether the root library defines a main function.
const Library& lib =
Library::Handle(zone, IG->object_store()->root_library());
Function& func = Function::Handle(zone, lib.LookupLocalFunction(func_name));
if (func.IsNull()) {
// Check whether main is reexported from the root library.
const Object& obj = Object::Handle(zone, lib.LookupReExport(func_name));
if (obj.IsFunction()) {
func ^= obj.ptr();
}
}
if (func.IsNull()) {
const String& msg = String::Handle(
zone, String::NewFormatted(
"Unable to resolve function '%s' in script '%s'.",
function_name(), script_url()));
return LanguageError::New(msg);
}
return func.ptr();
}
// Lookup the to be spawned function for the Isolate.spawn implementation.
// Resolve the library.
const String& lib_url = String::Handle(zone, String::New(library_url()));
const Library& lib =
Library::Handle(zone, Library::LookupLibrary(thread, lib_url));
if (lib.IsNull() || lib.IsError()) {
const String& msg = String::Handle(
zone,
String::NewFormatted("Unable to find library '%s'.", library_url()));
return LanguageError::New(msg);
}
// Resolve the function.
if (class_name() == nullptr) {
const Function& func =
Function::Handle(zone, lib.LookupLocalFunction(func_name));
if (func.IsNull()) {
const String& msg = String::Handle(
zone, String::NewFormatted(
"Unable to resolve function '%s' in library '%s'.",
function_name(), library_url()));
return LanguageError::New(msg);
}
return func.ptr();
}
const String& cls_name = String::Handle(zone, String::New(class_name()));
const Class& cls = Class::Handle(zone, lib.LookupLocalClass(cls_name));
if (cls.IsNull()) {
const String& msg = String::Handle(
zone, String::NewFormatted(
"Unable to resolve class '%s' in library '%s'.", class_name(),
(library_url() != nullptr ? library_url() : script_url())));
return LanguageError::New(msg);
}
Function& func = Function::Handle(zone);
const auto& error = cls.EnsureIsFinalized(thread);
if (error == Error::null()) {
func = cls.LookupStaticFunctionAllowPrivate(func_name);
}
if (func.IsNull()) {
const String& msg = String::Handle(
zone, String::NewFormatted(
"Unable to resolve static method '%s.%s' in library '%s'.",
class_name(), function_name(),
(library_url() != nullptr ? library_url() : script_url())));
return LanguageError::New(msg);
}
return func.ptr();
}
static InstancePtr DeserializeMessage(Thread* thread, Message* message) {
if (message == NULL) {
return Instance::null();
}
Zone* zone = thread->zone();
if (message->IsRaw()) {
return Instance::RawCast(message->raw_obj());
} else {
MessageSnapshotReader reader(message, thread);
const Object& obj = Object::Handle(zone, reader.ReadObject());
ASSERT(!obj.IsError());
return Instance::RawCast(obj.ptr());
}
}
InstancePtr IsolateSpawnState::BuildArgs(Thread* thread) {
return DeserializeMessage(thread, serialized_args_.get());
}
InstancePtr IsolateSpawnState::BuildMessage(Thread* thread) {
return DeserializeMessage(thread, serialized_message_.get());
}
static void ThrowIsolateSpawnException(const String& message) {
const Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
Exceptions::ThrowByType(Exceptions::kIsolateSpawn, args);
}
class SpawnIsolateTask : public ThreadPool::Task {
public:
SpawnIsolateTask(Isolate* parent_isolate,
std::unique_ptr<IsolateSpawnState> state)
: parent_isolate_(parent_isolate), state_(std::move(state)) {
parent_isolate->IncrementSpawnCount();
}
~SpawnIsolateTask() override {
if (parent_isolate_ != nullptr) {
parent_isolate_->DecrementSpawnCount();
}
}
void Run() override {
const char* name = (state_->debug_name() == nullptr)
? state_->function_name()
: state_->debug_name();
ASSERT(name != nullptr);
auto group = state_->isolate_group();
if (!IsolateGroup::AreIsolateGroupsEnabled() || group == nullptr) {
RunHeavyweight(name);
} else {
RunLightweight(name);
}
}
void RunHeavyweight(const char* name) {
// The create isolate group callback is mandatory. If not provided we
// cannot spawn isolates.
auto create_group_callback = Isolate::CreateGroupCallback();
if (create_group_callback == nullptr) {
FailedSpawn("Isolate spawn is not supported by this Dart embedder\n");
return;
}
char* error = nullptr;
// Make a copy of the state's isolate flags and hand it to the callback.
Dart_IsolateFlags api_flags = *(state_->isolate_flags());
Dart_Isolate isolate = (create_group_callback)(
state_->script_url(), name, nullptr, state_->package_config(),
&api_flags, parent_isolate_->init_callback_data(), &error);
parent_isolate_->DecrementSpawnCount();
parent_isolate_ = nullptr;
if (isolate == nullptr) {
FailedSpawn(error);
free(error);
return;
}
Dart_EnterIsolate(isolate);
Run(reinterpret_cast<Isolate*>(isolate));
}
void RunLightweight(const char* name) {
// The create isolate initialize callback is mandatory if
// --enable-isolate-groups was passed.
auto initialize_callback = Isolate::InitializeCallback();
if (initialize_callback == nullptr) {
FailedSpawn(
"Lightweight isolate spawn is not supported by this Dart embedder\n");
return;
}
char* error = nullptr;
auto group = state_->isolate_group();
Isolate* isolate = CreateWithinExistingIsolateGroup(group, name, &error);
parent_isolate_->DecrementSpawnCount();
parent_isolate_ = nullptr;
if (isolate == nullptr) {
FailedSpawn(error);
free(error);
return;
}
void* child_isolate_data = nullptr;
const bool success = initialize_callback(&child_isolate_data, &error);
if (!success) {
Dart_ShutdownIsolate();
FailedSpawn(error);
free(error);
return;
}
isolate->set_init_callback_data(child_isolate_data);
Run(isolate);
}
private:
void Run(Isolate* child) {
if (!EnsureIsRunnable(child)) {
Dart_ShutdownIsolate();
return;
}
state_->set_isolate(child);
child->set_origin_id(state_->origin_id());
bool success = true;
{
auto thread = Thread::Current();
TransitionNativeToVM transition(thread);
StackZone zone(thread);
HandleScope hs(thread);
success = EnqueueEntrypointInvocationAndNotifySpawner(thread);
}
if (!success) {
Dart_ShutdownIsolate();
return;
}
// All preconditions are met for this to always succeed.
char* error = nullptr;
if (!Dart_RunLoopAsync(state_->errors_are_fatal(), state_->on_error_port(),
state_->on_exit_port(), &error)) {
FATAL("Dart_RunLoopAsync() failed: %s. Please file a Dart VM bug report.",
error);
}
}
bool EnsureIsRunnable(Isolate* child) {
// We called out to the embedder to create/initialize a new isolate. The
// embedder callback sucessfully did so. It is now our responsibility to
// run the isolate.
// If the isolate was not marked as runnable, we'll do so here and run it.
if (!child->is_runnable()) {
const char* error = child->MakeRunnable();
if (error != nullptr) {
FailedSpawn(error);
return false;
}
}
ASSERT(child->is_runnable());
return true;
}
bool EnqueueEntrypointInvocationAndNotifySpawner(Thread* thread) {
auto isolate = thread->isolate();
auto zone = thread->zone();
// Step 1) Resolve the entrypoint function.
auto& result = Object::Handle(zone, state_->ResolveFunction());
const bool is_spawn_uri = state_->is_spawn_uri();
if (result.IsError()) {
ASSERT(is_spawn_uri);
ReportError("Failed to resolve entrypoint function.");
return false;
}
ASSERT(result.IsFunction());
auto& func = Function::Handle(zone, Function::Cast(result).ptr());
func = func.ImplicitClosureFunction();
const auto& entrypoint_closure =
Object::Handle(zone, func.ImplicitStaticClosure());
// Step 2) Enqueue delayed invocation of entrypoint callback.
const Array& args = Array::Handle(zone, Array::New(4));
args.SetAt(0, entrypoint_closure);
args.SetAt(1, Instance::Handle(zone, state_->BuildArgs(thread)));
args.SetAt(2, Instance::Handle(zone, state_->BuildMessage(thread)));
args.SetAt(3, is_spawn_uri ? Bool::True() : Bool::False());
const auto& lib = Library::Handle(zone, Library::IsolateLibrary());
const auto& entry_name = String::Handle(zone, String::New("_startIsolate"));
const auto& entry_point =
Function::Handle(zone, lib.LookupLocalFunction(entry_name));
ASSERT(entry_point.IsFunction() && !entry_point.IsNull());
result = DartEntry::InvokeFunction(entry_point, args);
if (result.IsError()) {
ReportError("Failed to enqueue delayed entrypoint invocation.");
return false;
}
// Step 3) Pause the isolate if required & Notify parent isolate about
// isolate creation.
const auto& capabilities = Array::Handle(zone, Array::New(2));
auto& capability = Capability::Handle(zone);
capability = Capability::New(isolate->pause_capability());
capabilities.SetAt(0, capability);
capability = Capability::New(isolate->terminate_capability());
capabilities.SetAt(1, capability);
const auto& send_port =
SendPort::Handle(zone, SendPort::New(isolate->main_port()));
const auto& message = Array::Handle(zone, Array::New(2));
message.SetAt(0, send_port);
message.SetAt(1, capabilities);
if (state_->paused()) {
capability ^= capabilities.At(0);
const bool added = isolate->AddResumeCapability(capability);
ASSERT(added);
isolate->message_handler()->increment_paused();
}
{
MessageWriter writer(/*can_send_any_object=*/false);
// If parent isolate died, we ignore the fact that we cannot notify it.
PortMap::PostMessage(writer.WriteMessage(message, state_->parent_port(),
Message::kNormalPriority));
}
return true;
}
void FailedSpawn(const char* error) {
ReportError(error != nullptr
? error
: "Unknown error occured during Isolate spawning.");
state_ = nullptr;
}
void ReportError(const char* error) {
Dart_CObject error_cobj;
error_cobj.type = Dart_CObject_kString;
error_cobj.value.as_string = const_cast<char*>(error);
if (!Dart_PostCObject(state_->parent_port(), &error_cobj)) {
// Perhaps the parent isolate died or closed the port before we
// could report the error. Ignore.
}
}
Isolate* parent_isolate_;
std::unique_ptr<IsolateSpawnState> state_;
DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask);
};
static const char* String2UTF8(const String& str) {
intptr_t len = Utf8::Length(str);
char* result = new char[len + 1];
str.ToUTF8(reinterpret_cast<uint8_t*>(result), len);
result[len] = 0;
return result;
}
DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(5));
GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(6));
GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(7));
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(8));
GET_NATIVE_ARGUMENT(Bool, newIsolateGroup, arguments->NativeArgAt(9));
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(10));
if (closure.IsClosure()) {
Function& func = Function::Handle();
func = Closure::Cast(closure).function();
if (func.IsImplicitClosureFunction() && func.is_static()) {
#if defined(DEBUG)
Context& ctx = Context::Handle();
ctx = Closure::Cast(closure).context();
ASSERT(ctx.IsNull());
#endif
// Get the parent function so that we get the right function name.
func = func.parent_function();
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
// We first try to serialize the message. In case the message is not
// serializable this will throw an exception.
SerializedObjectBuffer message_buffer;
{
MessageWriter writer(/* can_send_any_object = */ true);
message_buffer.set_message(writer.WriteMessage(
message, ILLEGAL_PORT, Message::kNormalPriority));
}
const char* utf8_package_config =
packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
const char* utf8_debug_name =
debugName.IsNull() ? NULL : String2UTF8(debugName);
const bool in_new_isolate_group = newIsolateGroup.value();
std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
&message_buffer, utf8_package_config, paused.value(), fatal_errors,
on_exit_port, on_error_port, utf8_debug_name,
in_new_isolate_group ? nullptr : isolate->group()));
// Since this is a call to Isolate.spawn, copy the parent isolate's code.
state->isolate_flags()->copy_parent_code = true;
isolate->group()->thread_pool()->Run<SpawnIsolateTask>(isolate,
std::move(state));
return Object::null();
}
}
const String& msg = String::Handle(String::New(
"Isolate.spawn expects to be passed a static or top-level function"));
Exceptions::ThrowArgumentError(msg);
return Object::null();
}
static const char* CanonicalizeUri(Thread* thread,
const Library& library,
const String& uri,
char** error) {
const char* result = NULL;
Zone* zone = thread->zone();
auto isolate_group = thread->isolate_group();
if (isolate_group->HasTagHandler()) {
const Object& obj = Object::Handle(
isolate_group->CallTagHandler(Dart_kCanonicalizeUrl, library, uri));
if (obj.IsString()) {
result = String2UTF8(String::Cast(obj));
} else if (obj.IsError()) {
Error& error_obj = Error::Handle();
error_obj ^= obj.ptr();
*error = zone->PrintToString("Unable to canonicalize uri '%s': %s",
uri.ToCString(), error_obj.ToErrorCString());
} else {
*error = zone->PrintToString(
"Unable to canonicalize uri '%s': "
"library tag handler returned wrong type",
uri.ToCString());
}
} else {
*error = zone->PrintToString(
"Unable to canonicalize uri '%s': no library tag handler found.",
uri.ToCString());
}
return result;
}
DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 12) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, args, arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(5));
GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(6));
GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(7));
GET_NATIVE_ARGUMENT(Bool, checked, arguments->NativeArgAt(8));
GET_NATIVE_ARGUMENT(Array, environment, arguments->NativeArgAt(9));
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(10));
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(11));
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
// We first try to serialize the arguments and the message. In case the
// arguments or the message are not serializable this will throw an exception.
SerializedObjectBuffer arguments_buffer;
SerializedObjectBuffer message_buffer;
{
MessageWriter writer(/* can_send_any_object = */ false);
arguments_buffer.set_message(
writer.WriteMessage(args, ILLEGAL_PORT, Message::kNormalPriority));
}
{
MessageWriter writer(/* can_send_any_object = */ false);
message_buffer.set_message(
writer.WriteMessage(message, ILLEGAL_PORT, Message::kNormalPriority));
}
// Canonicalize the uri with respect to the current isolate.
const Library& root_lib =
Library::Handle(isolate->group()->object_store()->root_library());
char* error = NULL;
const char* canonical_uri = CanonicalizeUri(thread, root_lib, uri, &error);
if (canonical_uri == NULL) {
const String& msg = String::Handle(String::New(error));
ThrowIsolateSpawnException(msg);
}
const char* utf8_package_config =
packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
const char* utf8_debug_name =
debugName.IsNull() ? NULL : String2UTF8(debugName);
std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
port.Id(), canonical_uri, utf8_package_config, &arguments_buffer,
&message_buffer, paused.value(), fatal_errors, on_exit_port,
on_error_port, utf8_debug_name, /*group=*/nullptr));
// If we were passed a value then override the default flags state for
// checked mode.
if (!checked.IsNull()) {
Dart_IsolateFlags* flags = state->isolate_flags();
flags->enable_asserts = checked.value();
}
// Since this is a call to Isolate.spawnUri, don't copy the parent's code.
state->isolate_flags()->copy_parent_code = false;
isolate->group()->thread_pool()->Run<SpawnIsolateTask>(isolate,
std::move(state));
return Object::null();
}
DEFINE_NATIVE_ENTRY(Isolate_getDebugName, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
auto name = Isolate::LookupIsolateNameByPort(port.Id());
if (name == nullptr) {
return String::null();
}
return String::New(name.get());
}
DEFINE_NATIVE_ENTRY(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0, 0) {
const Array& result = Array::Handle(Array::New(3));
result.SetAt(0, SendPort::Handle(SendPort::New(isolate->main_port())));
result.SetAt(
1, Capability::Handle(Capability::New(isolate->pause_capability())));
result.SetAt(
2, Capability::Handle(Capability::New(isolate->terminate_capability())));
return result.ptr();
}
DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) {
const Library& root_lib =
Library::Handle(zone, isolate->group()->object_store()->root_library());
return root_lib.url();
}
DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1));
// Make sure to route this request to the isolate library OOB mesage handler.
msg.SetAt(0, Smi::Handle(Smi::New(Message::kIsolateLibOOBMsg)));
// Ensure message writer (and it's resources, e.g. forwarding tables) are
// cleaned up before handling interrupts.
{
MessageWriter writer(false);
PortMap::PostMessage(
writer.WriteMessage(msg, port.Id(), Message::kOOBPriority));
}
// Drain interrupts before running so any IMMEDIATE operations on the current
// isolate happen synchronously.
const Error& error = Error::Handle(thread->HandleInterrupts());
if (!error.IsNull()) {
Exceptions::PropagateError(error);
UNREACHABLE();
}
return Object::null();
}
static void ExternalTypedDataFinalizer(void* isolate_callback_data,
void* peer) {
free(peer);
}
static intptr_t GetTypedDataSizeOrThrow(const Instance& instance) {
// From the Dart side we are guaranteed that the type of [instance] is a
// subtype of TypedData.
if (instance.IsTypedDataBase()) {
return TypedDataBase::Cast(instance).LengthInBytes();
}
// This can happen if [instance] is `null` or an instance of a 3rd party class
// which implements [TypedData].
Exceptions::ThrowArgumentError(instance);
}
DEFINE_NATIVE_ENTRY(TransferableTypedData_factory, 0, 2) {
ASSERT(
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
GET_NON_NULL_NATIVE_ARGUMENT(Instance, array_instance,
arguments->NativeArgAt(1));
Array& array = Array::Handle();
intptr_t array_length;
if (array_instance.IsGrowableObjectArray()) {
const auto& growable_array = GrowableObjectArray::Cast(array_instance);
array ^= growable_array.data();
array_length = growable_array.Length();
} else if (array_instance.IsArray()) {
array ^= Array::Cast(array_instance).ptr();
array_length = array.Length();
} else {
Exceptions::ThrowArgumentError(array_instance);
UNREACHABLE();
}
Instance& instance = Instance::Handle();
uint64_t total_bytes = 0;
const uint64_t kMaxBytes = TypedData::MaxElements(kTypedDataUint8ArrayCid);
for (intptr_t i = 0; i < array_length; i++) {
instance ^= array.At(i);
total_bytes += static_cast<uintptr_t>(GetTypedDataSizeOrThrow(instance));
if (total_bytes > kMaxBytes) {
const Array& error_args = Array::Handle(Array::New(3));
error_args.SetAt(0, array);
error_args.SetAt(1, String::Handle(String::New("data")));
error_args.SetAt(
2, String::Handle(String::NewFormatted(
"Aggregated list exceeds max size %" Pu64 "", kMaxBytes)));
Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
UNREACHABLE();
}
}
uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(total_bytes));
if (data == nullptr) {
const Instance& exception = Instance::Handle(
thread->isolate_group()->object_store()->out_of_memory());
Exceptions::Throw(thread, exception);
UNREACHABLE();
}
intptr_t offset = 0;
for (intptr_t i = 0; i < array_length; i++) {
instance ^= array.At(i);
{
NoSafepointScope no_safepoint;
const auto& typed_data = TypedDataBase::Cast(instance);
const intptr_t length_in_bytes = typed_data.LengthInBytes();
void* source = typed_data.DataAddr(0);
// The memory does not overlap.
memcpy(data + offset, source, length_in_bytes); // NOLINT
offset += length_in_bytes;
}
}
ASSERT(static_cast<uintptr_t>(offset) == total_bytes);
return TransferableTypedData::New(data, total_bytes);
}
DEFINE_NATIVE_ENTRY(TransferableTypedData_materialize, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(TransferableTypedData, t,
arguments->NativeArgAt(0));
void* peer;
{
NoSafepointScope no_safepoint;
peer = thread->heap()->GetPeer(t.ptr());
// Assume that object's Peer is only used to track transferrability state.
ASSERT(peer != nullptr);
}
TransferableTypedDataPeer* tpeer =
reinterpret_cast<TransferableTypedDataPeer*>(peer);
const intptr_t length = tpeer->length();
uint8_t* data = tpeer->data();
if (data == nullptr) {
const auto& error = String::Handle(String::New(
"Attempt to materialize object that was transferred already."));
Exceptions::ThrowArgumentError(error);
UNREACHABLE();
}
tpeer->handle()->EnsureFreedExternal(IsolateGroup::Current());
tpeer->ClearData();
const ExternalTypedData& typed_data = ExternalTypedData::Handle(
ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length,
thread->heap()->SpaceForExternal(length)));
FinalizablePersistentHandle::New(thread->isolate_group(), typed_data,
/* peer= */ data,
&ExternalTypedDataFinalizer, length,
/*auto_delete=*/true);
return typed_data.ptr();
}
} // namespace dart