d8c5f19e01
This was originally added to prevent fork bombs in our test suite. Early versions of AOT for the standalone VM loaded the AOT program based on LD_LIBRARY_PATH instead of the command line arguments, so tests the tried to spawn a second script spawned themselves. The VM snapshot has since also been changed to be the same for every app, and some embedders already load multiple apps into the same process using the C API. Embedders can still block any Isolate.spawn or spawnUri in the C callbacks if they don't want to implement loading for secondary isolates. Change-Id: I4a4beda2a797ce26367350298eaafd7e53256b3b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152592 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Aprelev <aam@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
723 lines
26 KiB
C++
723 lines
26 KiB
C++
// 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().raw() : Bool::False().raw();
|
|
}
|
|
|
|
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, 1) {
|
|
ASSERT(
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
|
|
Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
|
|
return ReceivePort::New(port_id, 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(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.raw())) {
|
|
PortMap::PostMessage(
|
|
Message::New(destination_port_id, obj.raw(), 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 ObjectPtrSetTraitsLayout {
|
|
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* raw = from; raw <= to; raw++) {
|
|
if (!(*raw)->IsHeapObject() || (*raw)->ptr()->IsCanonical()) {
|
|
continue;
|
|
}
|
|
if (visited_->GetValueExclusive(*raw) == 1) {
|
|
continue;
|
|
}
|
|
visited_->SetValueExclusive(*raw, 1);
|
|
working_set_->Add(*raw);
|
|
}
|
|
}
|
|
|
|
WeakTable* visited_;
|
|
MallocGrowableArray<ObjectPtr>* const working_set_;
|
|
};
|
|
if (!obj.raw()->IsHeapObject() || obj.raw()->ptr()->IsCanonical()) {
|
|
return obj.raw();
|
|
}
|
|
ClassTable* class_table = isolate->class_table();
|
|
|
|
Class& klass = Class::Handle(zone);
|
|
Closure& closure = Closure::Handle(zone);
|
|
|
|
MallocGrowableArray<ObjectPtr> working_set;
|
|
std::unique_ptr<WeakTable> visited(new WeakTable());
|
|
|
|
NoSafepointScope no_safepoint;
|
|
SendMessageValidator visitor(isolate->group(), visited.get(), &working_set);
|
|
|
|
visited->SetValueExclusive(obj.raw(), 1);
|
|
working_set.Add(obj.raw());
|
|
|
|
while (!working_set.is_empty()) {
|
|
ObjectPtr raw = working_set.RemoveLast();
|
|
|
|
if (visited->GetValueExclusive(raw) > 0) {
|
|
continue;
|
|
}
|
|
visited->SetValueExclusive(raw, 1);
|
|
|
|
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: \
|
|
return Exceptions::CreateUnhandledException( \
|
|
zone, Exceptions::kArgumentValue, \
|
|
"Illegal argument in isolate message : (object is a " #type ")");
|
|
|
|
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)) {
|
|
return Exceptions::CreateUnhandledException(
|
|
zone, Exceptions::kArgumentValue, "Closures are not allowed");
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if (cid >= kNumPredefinedCids) {
|
|
klass = class_table->At(cid);
|
|
if (klass.num_native_fields() != 0) {
|
|
return Exceptions::CreateUnhandledException(
|
|
zone, Exceptions::kArgumentValue,
|
|
"Objects that extend NativeWrapper are not allowed");
|
|
}
|
|
}
|
|
}
|
|
raw->ptr()->VisitPointers(&visitor);
|
|
}
|
|
isolate->set_forward_table_new(nullptr);
|
|
return obj.raw();
|
|
}
|
|
|
|
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);
|
|
Object& msg_obj = Object::Handle(zone, obj.raw());
|
|
validated_result = ValidateMessageObject(zone, isolate, msg_obj);
|
|
if (validated_result.IsUnhandledException()) {
|
|
Exceptions::PropagateError(Error::Cast(validated_result));
|
|
UNREACHABLE();
|
|
}
|
|
PersistentHandle* handle =
|
|
isolate->group()->api_state()->AllocatePersistentHandle();
|
|
handle->set_raw(msg_obj);
|
|
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();
|
|
}
|
|
|
|
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,
|
|
bool in_new_isolate_group)
|
|
: parent_isolate_(parent_isolate),
|
|
state_(std::move(state)),
|
|
in_new_isolate_group_(in_new_isolate_group) {
|
|
parent_isolate->IncrementSpawnCount();
|
|
}
|
|
|
|
~SpawnIsolateTask() override {
|
|
if (parent_isolate_ != nullptr) {
|
|
parent_isolate_->DecrementSpawnCount();
|
|
}
|
|
}
|
|
|
|
void Run() override {
|
|
auto group = state_->isolate_group();
|
|
|
|
// The create isolate group call back is mandatory. If not provided we
|
|
// cannot spawn isolates.
|
|
Dart_IsolateGroupCreateCallback create_group_callback =
|
|
Isolate::CreateGroupCallback();
|
|
if (create_group_callback == nullptr) {
|
|
FailedSpawn("Isolate spawn is not supported by this Dart embedder\n");
|
|
return;
|
|
}
|
|
|
|
// The initialize callback is optional atm, we fall back to creating isolate
|
|
// groups if it was not provided.
|
|
Dart_InitializeIsolateCallback initialize_callback =
|
|
Isolate::InitializeCallback();
|
|
|
|
const char* name = (state_->debug_name() == NULL) ? state_->function_name()
|
|
: state_->debug_name();
|
|
ASSERT(name != NULL);
|
|
|
|
// Create a new isolate.
|
|
char* error = nullptr;
|
|
Isolate* isolate = nullptr;
|
|
if (!FLAG_enable_isolate_groups || group == nullptr ||
|
|
initialize_callback == nullptr || in_new_isolate_group_) {
|
|
// Make a copy of the state's isolate flags and hand it to the callback.
|
|
Dart_IsolateFlags api_flags = *(state_->isolate_flags());
|
|
isolate = reinterpret_cast<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;
|
|
} else {
|
|
if (initialize_callback == nullptr) {
|
|
FailedSpawn("Isolate spawn is not supported by this embedder.");
|
|
return;
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
isolate = CreateWithinExistingIsolateGroupAOT(group, name, &error);
|
|
#else
|
|
isolate = CreateWithinExistingIsolateGroup(group, name, &error);
|
|
#endif
|
|
parent_isolate_->DecrementSpawnCount();
|
|
parent_isolate_ = nullptr;
|
|
if (isolate == nullptr) {
|
|
FailedSpawn(error);
|
|
free(error);
|
|
return;
|
|
}
|
|
|
|
void* child_isolate_data = nullptr;
|
|
bool success = initialize_callback(&child_isolate_data, &error);
|
|
isolate->set_init_callback_data(child_isolate_data);
|
|
if (!success) {
|
|
Dart_ShutdownIsolate();
|
|
FailedSpawn(error);
|
|
free(error);
|
|
return;
|
|
}
|
|
Dart_ExitIsolate();
|
|
}
|
|
|
|
if (isolate == nullptr) {
|
|
FailedSpawn(error);
|
|
free(error);
|
|
return;
|
|
}
|
|
|
|
if (state_->origin_id() != ILLEGAL_PORT) {
|
|
// For isolates spawned using spawnFunction we set the origin_id
|
|
// to the origin_id of the parent isolate.
|
|
isolate->set_origin_id(state_->origin_id());
|
|
}
|
|
MutexLocker ml(isolate->mutex());
|
|
state_->set_isolate(isolate);
|
|
isolate->set_spawn_state(std::move(state_));
|
|
if (isolate->is_runnable()) {
|
|
isolate->Run();
|
|
}
|
|
}
|
|
|
|
private:
|
|
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_;
|
|
bool in_new_isolate_group_;
|
|
|
|
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);
|
|
|
|
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, isolate->group()));
|
|
|
|
// Since this is a call to Isolate.spawn, copy the parent isolate's code.
|
|
state->isolate_flags()->copy_parent_code = true;
|
|
|
|
const bool in_new_isolate_group = newIsolateGroup.value();
|
|
isolate->group()->thread_pool()->Run<SpawnIsolateTask>(
|
|
isolate, std::move(state), in_new_isolate_group);
|
|
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();
|
|
Isolate* isolate = thread->isolate();
|
|
if (isolate->HasTagHandler()) {
|
|
const Object& obj = Object::Handle(
|
|
isolate->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.raw();
|
|
*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->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;
|
|
|
|
const bool in_new_isolate_group = false;
|
|
isolate->group()->thread_pool()->Run<SpawnIsolateTask>(
|
|
isolate, std::move(state), in_new_isolate_group);
|
|
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.raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) {
|
|
const Library& root_lib =
|
|
Library::Handle(zone, isolate->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)));
|
|
|
|
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).raw();
|
|
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()->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.raw());
|
|
// 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->ClearData();
|
|
|
|
const ExternalTypedData& typed_data = ExternalTypedData::Handle(
|
|
ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length,
|
|
thread->heap()->SpaceForExternal(length)));
|
|
FinalizablePersistentHandle::New(thread->isolate(), typed_data,
|
|
/* peer= */ data,
|
|
&ExternalTypedDataFinalizer, length,
|
|
/*auto_delete=*/true);
|
|
return typed_data.raw();
|
|
}
|
|
|
|
} // namespace dart
|