// 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 #include #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(id >> 32); int32_t lo = static_cast(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(id >> 32); int32_t lo = static_cast(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(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* 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* 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 working_set; std::unique_ptr 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(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 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((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(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 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(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 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( 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 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( 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(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(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(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(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