bf4bb95308
The new implementation moves away from desugaring of async functions on kernel AST, state machine generated in the flow graph and capturing all local variables in the context. Instead, async/await is implemented using a few stubs (InitSuspendableFunction, Suspend, Resume, Return and AsyncExceptionHandler). The stubs are implemented in a platform-independent way using (macro-)assembler helpers. When suspending a function, its frame is copied into a SuspendState object, and when resuming a function it is copied back onto the stack. No extra code is generated for accessing local variables. Callback closures are created lazily on the first await. Design doc: go/compact-async-await. Part 1 (kernel): https://dart-review.googlesource.com/c/sdk/+/241842 TEST=ci Issue: https://github.com/dart-lang/sdk/issues/48378 Change-Id: Ibad757035b7cc438ebdff80b460728b1d3eff1f5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242000 Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
1291 lines
48 KiB
C++
1291 lines
48 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/message_snapshot.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_graph_copy.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();
|
|
// We have to check whether the receiver has the same isolate group (e.g.
|
|
// native message handlers such as an IOService handler does not but does
|
|
// share the same origin port).
|
|
const bool same_group = PortMap::IsReceiverInThisIsolateGroup(
|
|
destination_port_id, isolate->group());
|
|
// TODO(turnidge): Throw an exception when the return value is false?
|
|
PortMap::PostMessage(WriteMessage(can_send_any_object, same_group, 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);
|
|
Class& erroneous_finalizable_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();
|
|
// Keep the list in sync with the one in runtime/vm/object_graph_copy.cc
|
|
switch (cid) {
|
|
// Can be shared.
|
|
case kOneByteStringCid:
|
|
case kTwoByteStringCid:
|
|
case kExternalOneByteStringCid:
|
|
case kExternalTwoByteStringCid:
|
|
case kMintCid:
|
|
case kImmutableArrayCid:
|
|
case kNeverCid:
|
|
case kSentinelCid:
|
|
case kInt32x4Cid:
|
|
case kSendPortCid:
|
|
case kCapabilityCid:
|
|
case kRegExpCid:
|
|
case kStackTraceCid:
|
|
continue;
|
|
// Cannot be shared due to possibly being mutable boxes for unboxed
|
|
// fields in JIT, but can be transferred via Isolate.exit()
|
|
case kDoubleCid:
|
|
case kFloat32x4Cid:
|
|
case kFloat64x2Cid:
|
|
continue;
|
|
|
|
case kClosureCid:
|
|
closure ^= raw;
|
|
// Only context has to be checked.
|
|
working_set.Add(closure.context());
|
|
continue;
|
|
|
|
#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);
|
|
// TODO(http://dartbug.com/47777): Send and exit support: remove this.
|
|
MESSAGE_SNAPSHOT_ILLEGAL(Finalizer);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(NativeFinalizer);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(Pointer);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(UserTag);
|
|
MESSAGE_SNAPSHOT_ILLEGAL(SuspendState);
|
|
|
|
default:
|
|
if (cid >= kNumPredefinedCids) {
|
|
klass = class_table->At(cid);
|
|
if (klass.num_native_fields() != 0) {
|
|
erroneous_nativewrapper_class = klass.ptr();
|
|
error_found = true;
|
|
break;
|
|
}
|
|
if (klass.implements_finalizable()) {
|
|
erroneous_finalizable_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 if (!erroneous_nativewrapper_class.IsNull()) {
|
|
exception_message =
|
|
OS::SCreate(zone,
|
|
"Illegal argument in isolate message"
|
|
" : (object extends NativeWrapper - %s)",
|
|
erroneous_nativewrapper_class.ToCString());
|
|
} else {
|
|
ASSERT(!erroneous_finalizable_class.IsNull());
|
|
exception_message = OS::SCreate(zone,
|
|
"Illegal argument in isolate message"
|
|
" : (object implements Finalizable - %s)",
|
|
erroneous_finalizable_class.ToCString());
|
|
}
|
|
return Exceptions::CreateUnhandledException(
|
|
zone, Exceptions::kArgumentValue, exception_message);
|
|
}
|
|
isolate->set_forward_table_new(nullptr);
|
|
return obj.ptr();
|
|
}
|
|
|
|
// TODO(http://dartbug.com/47777): Add support for Finalizers.
|
|
DEFINE_NATIVE_ENTRY(Isolate_exit_, 0, 2) {
|
|
GET_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
if (!port.IsNull()) {
|
|
GET_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
|
|
if (!PortMap::IsReceiverInThisIsolateGroup(port.Id(), isolate->group())) {
|
|
const auto& error =
|
|
String::Handle(String::New("exit with final message is only allowed "
|
|
"for isolates in one isolate group."));
|
|
Exceptions::ThrowArgumentError(error);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
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>,
|
|
// <collection-lib-objects-to-rehash>,
|
|
// <core-lib-objects-to-rehash>,
|
|
// ]
|
|
const Array& msg_array = Array::Handle(zone, Array::New(3));
|
|
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())));
|
|
}
|
|
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,
|
|
PersistentHandle* closure_tuple_handle,
|
|
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 && // No top-level entrypoint.
|
|
closure_tuple_handle_ == nullptr; // No closure entrypoint.
|
|
}
|
|
bool paused() const { return paused_; }
|
|
bool errors_are_fatal() const { return errors_are_fatal_; }
|
|
Dart_IsolateFlags* isolate_flags() { return &isolate_flags_; }
|
|
PersistentHandle* closure_tuple_handle() const {
|
|
return closure_tuple_handle_;
|
|
}
|
|
|
|
ObjectPtr ResolveFunction();
|
|
ObjectPtr BuildArgs(Thread* thread);
|
|
ObjectPtr 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_;
|
|
PersistentHandle* closure_tuple_handle_ = nullptr;
|
|
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,
|
|
PersistentHandle* closure_tuple_handle,
|
|
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),
|
|
closure_tuple_handle_(closure_tuple_handle),
|
|
isolate_group_(isolate_group),
|
|
serialized_args_(nullptr),
|
|
serialized_message_(message_buffer->StealMessage()),
|
|
paused_(paused),
|
|
errors_are_fatal_(errors_are_fatal) {
|
|
ASSERT(closure_tuple_handle_ != nullptr);
|
|
|
|
auto thread = Thread::Current();
|
|
auto isolate = thread->isolate();
|
|
|
|
// 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* isolate_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_(isolate_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 ObjectPtr DeserializeMessage(Thread* thread, Message* message) {
|
|
if (message == NULL) {
|
|
return Object::null();
|
|
}
|
|
if (message->IsRaw()) {
|
|
return Object::RawCast(message->raw_obj());
|
|
} else {
|
|
return ReadMessage(thread, message);
|
|
}
|
|
}
|
|
|
|
ObjectPtr IsolateSpawnState::BuildArgs(Thread* thread) {
|
|
const Object& result =
|
|
Object::Handle(DeserializeMessage(thread, serialized_args_.get()));
|
|
serialized_args_.reset();
|
|
return result.ptr();
|
|
}
|
|
|
|
ObjectPtr IsolateSpawnState::BuildMessage(Thread* thread) {
|
|
const Object& result =
|
|
Object::Handle(DeserializeMessage(thread, serialized_message_.get()));
|
|
serialized_message_.reset();
|
|
return result.ptr();
|
|
}
|
|
|
|
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 (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());
|
|
api_flags.is_system_isolate = false;
|
|
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, /*has_current_isolate=*/false);
|
|
free(error);
|
|
return;
|
|
}
|
|
Dart_EnterIsolate(isolate);
|
|
Run(reinterpret_cast<Isolate*>(isolate));
|
|
}
|
|
|
|
void RunLightweight(const char* name) {
|
|
// The create isolate initialize callback is mandatory.
|
|
auto initialize_callback = Isolate::InitializeCallback();
|
|
if (initialize_callback == nullptr) {
|
|
FailedSpawn(
|
|
"Lightweight isolate spawn is not supported by this Dart embedder\n",
|
|
/*has_current_isolate=*/false);
|
|
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, /*has_current_isolate=*/false);
|
|
free(error);
|
|
return;
|
|
}
|
|
|
|
void* child_isolate_data = nullptr;
|
|
const bool success = initialize_callback(&child_isolate_data, &error);
|
|
if (!success) {
|
|
FailedSpawn(error);
|
|
Dart_ShutdownIsolate();
|
|
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);
|
|
if (state_->origin_id() != ILLEGAL_PORT) {
|
|
// origin_id is set to parent isolate main port id when spawning via
|
|
// spawnFunction.
|
|
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) {
|
|
state_ = nullptr;
|
|
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();
|
|
const bool is_spawn_uri = state_->is_spawn_uri();
|
|
|
|
// Step 1) Resolve the entrypoint function.
|
|
auto& entrypoint_closure = Closure::Handle(zone);
|
|
if (state_->closure_tuple_handle() != nullptr) {
|
|
const auto& result = Object::Handle(
|
|
zone,
|
|
ReadObjectGraphCopyMessage(thread, state_->closure_tuple_handle()));
|
|
if (result.IsError()) {
|
|
ReportError(
|
|
"Failed to deserialize the passed entrypoint to the new isolate.");
|
|
return false;
|
|
}
|
|
entrypoint_closure = Closure::RawCast(result.ptr());
|
|
} else {
|
|
const auto& result = Object::Handle(zone, state_->ResolveFunction());
|
|
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();
|
|
entrypoint_closure = func.ImplicitStaticClosure();
|
|
}
|
|
|
|
// Step 2) Enqueue delayed invocation of entrypoint callback.
|
|
const auto& args_obj = Object::Handle(zone, state_->BuildArgs(thread));
|
|
if (args_obj.IsError()) {
|
|
ReportError(
|
|
"Failed to deserialize the passed arguments to the new isolate.");
|
|
return false;
|
|
}
|
|
ASSERT(args_obj.IsNull() || args_obj.IsInstance());
|
|
const auto& message_obj =
|
|
Object::Handle(zone, state_->BuildMessage(thread));
|
|
if (message_obj.IsError()) {
|
|
ReportError(
|
|
"Failed to deserialize the passed arguments to the new isolate.");
|
|
return false;
|
|
}
|
|
ASSERT(message_obj.IsNull() || message_obj.IsInstance());
|
|
const Array& args = Array::Handle(zone, Array::New(4));
|
|
args.SetAt(0, entrypoint_closure);
|
|
args.SetAt(1, args_obj);
|
|
args.SetAt(2, message_obj);
|
|
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());
|
|
const auto& result =
|
|
Object::Handle(zone, 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();
|
|
}
|
|
{
|
|
// If parent isolate died, we ignore the fact that we cannot notify it.
|
|
PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
|
|
/* same_group */ false, message,
|
|
state_->parent_port(),
|
|
Message::kNormalPriority));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FailedSpawn(const char* error, bool has_current_isolate = true) {
|
|
ReportError(error != nullptr
|
|
? error
|
|
: "Unknown error occured during Isolate spawning.");
|
|
// Destruction of [IsolateSpawnState] may cause destruction of [Message]
|
|
// which make need to delete persistent handles (which requires a current
|
|
// isolate group).
|
|
if (has_current_isolate) {
|
|
ASSERT(IsolateGroup::Current() == state_->isolate_group());
|
|
state_ = nullptr;
|
|
} else if (state_->isolate_group() != nullptr) {
|
|
ASSERT(IsolateGroup::Current() == nullptr);
|
|
const bool kBypassSafepoint = false;
|
|
const bool result = Thread::EnterIsolateGroupAsHelper(
|
|
state_->isolate_group(), Thread::kUnknownTask, kBypassSafepoint);
|
|
ASSERT(result);
|
|
state_ = nullptr;
|
|
Thread::ExitIsolateGroupAsHelper(kBypassSafepoint);
|
|
} else {
|
|
// The state won't need a current isolate group, because it belongs to a
|
|
// [Isolate.spawnUri] call.
|
|
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, 10) {
|
|
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(Closure, 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(String, debugName, arguments->NativeArgAt(9));
|
|
|
|
PersistentHandle* closure_tuple_handle = nullptr;
|
|
// We have a non-toplevel closure that we might need to copy.
|
|
// Result will be [<closure-copy>, <objects-in-msg-to-rehash>]
|
|
const auto& closure_copy_tuple = Object::Handle(
|
|
zone, CopyMutableObjectGraph(closure)); // Throws if it fails.
|
|
ASSERT(closure_copy_tuple.IsArray());
|
|
ASSERT(
|
|
Object::Handle(zone, Array::Cast(closure_copy_tuple).At(0)).IsClosure());
|
|
closure_tuple_handle =
|
|
isolate->group()->api_state()->AllocatePersistentHandle();
|
|
closure_tuple_handle->set_ptr(closure_copy_tuple.ptr());
|
|
|
|
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;
|
|
message_buffer.set_message(WriteMessage(
|
|
/*can_send_any_object=*/true,
|
|
/*same_group=*/true, 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);
|
|
if (closure_tuple_handle != nullptr && utf8_debug_name == nullptr) {
|
|
const auto& closure_function = Function::Handle(zone, closure.function());
|
|
utf8_debug_name =
|
|
NewConstChar(closure_function.QualifiedUserVisibleNameCString());
|
|
}
|
|
|
|
std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
|
|
port.Id(), isolate->origin_id(), String2UTF8(script_uri),
|
|
closure_tuple_handle, &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;
|
|
|
|
isolate->group()->thread_pool()->Run<SpawnIsolateTask>(isolate,
|
|
std::move(state));
|
|
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;
|
|
{
|
|
arguments_buffer.set_message(WriteMessage(
|
|
/* can_send_any_object */ false,
|
|
/* same_group */ false, args, ILLEGAL_PORT, Message::kNormalPriority));
|
|
}
|
|
{
|
|
message_buffer.set_message(WriteMessage(/* can_send_any_object */ false,
|
|
/* same_group */ false, 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_registerKernelBlob, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(TypedData, kernel_blob,
|
|
arguments->NativeArgAt(0));
|
|
auto register_kernel_blob_callback = Isolate::RegisterKernelBlobCallback();
|
|
if (register_kernel_blob_callback == nullptr) {
|
|
const auto& error =
|
|
String::Handle(zone, String::New("Registration of kernel blobs is not "
|
|
"supported by this Dart embedder.\n"));
|
|
Exceptions::ThrowArgumentError(error);
|
|
UNREACHABLE();
|
|
}
|
|
bool is_kernel = false;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
is_kernel =
|
|
Dart_IsKernel(reinterpret_cast<uint8_t*>(kernel_blob.DataAddr(0)),
|
|
kernel_blob.LengthInBytes());
|
|
}
|
|
if (!is_kernel) {
|
|
const auto& error = String::Handle(
|
|
zone, String::New("kernelBlob doesn\'t contain a valid kernel.\n"));
|
|
Exceptions::ThrowArgumentError(error);
|
|
UNREACHABLE();
|
|
}
|
|
const char* uri = nullptr;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
uri = register_kernel_blob_callback(
|
|
reinterpret_cast<uint8_t*>(kernel_blob.DataAddr(0)),
|
|
kernel_blob.LengthInBytes());
|
|
}
|
|
if (uri == nullptr) {
|
|
const Instance& exception = Instance::Handle(
|
|
thread->isolate_group()->object_store()->out_of_memory());
|
|
Exceptions::Throw(thread, exception);
|
|
UNREACHABLE();
|
|
}
|
|
return String::New(uri);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_unregisterKernelBlob, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, kernel_blob_uri,
|
|
arguments->NativeArgAt(0));
|
|
auto unregister_kernel_blob_callback =
|
|
Isolate::UnregisterKernelBlobCallback();
|
|
if (unregister_kernel_blob_callback == nullptr) {
|
|
const auto& error =
|
|
String::Handle(zone, String::New("Registration of kernel blobs is not "
|
|
"supported by this Dart embedder.\n"));
|
|
Exceptions::ThrowArgumentError(error);
|
|
UNREACHABLE();
|
|
}
|
|
unregister_kernel_blob_callback(kernel_blob_uri.ToCString());
|
|
return Object::null();
|
|
}
|
|
|
|
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.
|
|
{
|
|
PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
|
|
/* same_group */ false, 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
|