blob: 436e895382e83e4c090688711e8262c761f7e36b [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/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();
}
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_) {
parent_isolate_->DecrementSpawnCount();
}
}
void Run() override {
// Create a new isolate.
char* error = NULL;
Dart_IsolateCreateCallback callback = Isolate::CreateCallback();
if (callback == NULL) {
ReportError(
"Isolate spawn is not supported by this Dart implementation\n");
return;
}
// Make a copy of the state's isolate flags and hand it to the callback.
Dart_IsolateFlags api_flags = *(state_->isolate_flags());
const char* name = (state_->debug_name() == NULL) ? state_->function_name()
: state_->debug_name();
ASSERT(name != NULL);
Isolate* isolate = reinterpret_cast<Isolate*>((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 == NULL) {
ReportError(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 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, packageRoot, arguments->NativeArgAt(8));
GET_NATIVE_ARGUMENT(String, packageConfig, 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));
// Since this is a call to Isolate.spawn, copy the parent isolate's code.
state->isolate_flags()->copy_parent_code = true;
Dart::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();
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, 13) {
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, packageRoot, arguments->NativeArgAt(10));
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(11));
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(12));
if (Dart::vm_snapshot_kind() == Snapshot::kFullAOT) {
const Array& args = Array::Handle(Array::New(1));
args.SetAt(
0,
String::Handle(String::New(
"Isolate.spawnUri is not supported when using AOT compilation")));
Exceptions::ThrowByType(Exceptions::kUnsupported, args);
UNREACHABLE();
}
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));
// 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;
Dart::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));
Isolate* isolate_lookup = Isolate::LookupIsolateByPort(port.Id());
if (isolate_lookup == NULL) {
return String::null();
}
return String::New(isolate_lookup->name());
}
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,
Dart_WeakPersistentHandle handle,
void* peer) {
free(peer);
}
static intptr_t GetUint8SizeOrThrow(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();
unsigned long long total_bytes = 0;
const unsigned long kMaxBytes =
TypedData::MaxElements(kTypedDataUint8ArrayCid);
for (intptr_t i = 0; i < array_length; i++) {
instance ^= array.At(i);
total_bytes += GetUint8SizeOrThrow(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 %ld", 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);
offset += length_in_bytes;
}
}
ASSERT(static_cast<unsigned long>(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);
return typed_data.raw();
}
} // namespace dart