// 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 "vm/snapshot.h" #include "platform/assert.h" #include "vm/bootstrap.h" #include "vm/class_finalizer.h" #include "vm/dart.h" #include "vm/exceptions.h" #include "vm/heap/heap.h" #include "vm/longjump.h" #include "vm/message.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/snapshot_ids.h" #include "vm/stub_code.h" #include "vm/symbols.h" #include "vm/timeline.h" #include "vm/type_testing_stubs.h" #include "vm/version.h" // We currently only expect the Dart mutator to read snapshots. #define ASSERT_NO_SAFEPOINT_SCOPE() \ isolate()->AssertCurrentThreadIsMutator(); \ ASSERT(thread()->no_safepoint_scope_depth() != 0) namespace dart { static const int kNumInitialReferences = 32; static bool IsSingletonClassId(intptr_t class_id) { // Check if this is a singleton object class which is shared by all isolates. return ((class_id >= kClassCid && class_id <= kUnwindErrorCid) || (class_id == kTypeArgumentsCid) || (class_id >= kNullCid && class_id <= kVoidCid)); } static bool IsBootstrapedClassId(intptr_t class_id) { // Check if this is a class which is created during bootstrapping. return (class_id == kObjectCid || (class_id >= kInstanceCid && class_id <= kUserTagCid) || class_id == kArrayCid || class_id == kImmutableArrayCid || IsStringClassId(class_id) || IsTypedDataClassId(class_id) || IsExternalTypedDataClassId(class_id) || IsTypedDataViewClassId(class_id) || class_id == kNullCid || class_id == kNeverCid || class_id == kTransferableTypedDataCid); } static bool IsObjectStoreTypeId(intptr_t index) { // Check if this is a type which is stored in the object store. static_assert(kFirstTypeArgumentsSnapshotId == kLastTypeSnapshotId + 1, "Type and type arguments snapshot ids should be adjacent"); return index >= kFirstTypeSnapshotId && index <= kLastTypeArgumentsSnapshotId; } static bool IsSplitClassId(intptr_t class_id) { // Return whether this class is serialized in two steps: first a reference, // with sufficient information to allocate a correctly sized object, and then // later inline with complete contents. return class_id >= kNumPredefinedCids || class_id == kArrayCid || class_id == kImmutableArrayCid || class_id == kObjectPoolCid || IsImplicitFieldClassId(class_id); } static intptr_t ClassIdFromObjectId(intptr_t object_id) { ASSERT(object_id > kClassIdsOffset); intptr_t class_id = (object_id - kClassIdsOffset); return class_id; } static intptr_t ObjectIdFromClassId(intptr_t class_id) { ASSERT((class_id > kIllegalCid) && (class_id < kNumPredefinedCids)); return (class_id + kClassIdsOffset); } static ObjectPtr GetType(ObjectStore* object_store, intptr_t index) { switch (index) { case kLegacyObjectType: return object_store->legacy_object_type(); case kNullableObjectType: return object_store->nullable_object_type(); case kNullType: return object_store->null_type(); case kNeverType: return object_store->never_type(); case kLegacyFunctionType: return object_store->legacy_function_type(); case kLegacyNumberType: return object_store->legacy_number_type(); case kLegacySmiType: return object_store->legacy_smi_type(); case kLegacyMintType: return object_store->legacy_mint_type(); case kLegacyDoubleType: return object_store->legacy_double_type(); case kLegacyIntType: return object_store->legacy_int_type(); case kLegacyBoolType: return object_store->legacy_bool_type(); case kLegacyStringType: return object_store->legacy_string_type(); case kLegacyArrayType: return object_store->legacy_array_type(); case kLegacyIntTypeArguments: return object_store->type_argument_legacy_int(); case kLegacyDoubleTypeArguments: return object_store->type_argument_legacy_double(); case kLegacyStringTypeArguments: return object_store->type_argument_legacy_string(); case kLegacyStringDynamicTypeArguments: return object_store->type_argument_legacy_string_dynamic(); case kLegacyStringLegacyStringTypeArguments: return object_store->type_argument_legacy_string_legacy_string(); case kNonNullableObjectType: return object_store->non_nullable_object_type(); case kNonNullableFunctionType: return object_store->non_nullable_function_type(); case kNonNullableNumberType: return object_store->non_nullable_number_type(); case kNonNullableSmiType: return object_store->non_nullable_smi_type(); case kNonNullableMintType: return object_store->non_nullable_mint_type(); case kNonNullableDoubleType: return object_store->non_nullable_double_type(); case kNonNullableIntType: return object_store->non_nullable_int_type(); case kNonNullableBoolType: return object_store->non_nullable_bool_type(); case kNonNullableStringType: return object_store->non_nullable_string_type(); case kNonNullableArrayType: return object_store->non_nullable_array_type(); case kNonNullableIntTypeArguments: return object_store->type_argument_non_nullable_int(); case kNonNullableDoubleTypeArguments: return object_store->type_argument_non_nullable_double(); case kNonNullableStringTypeArguments: return object_store->type_argument_non_nullable_string(); case kNonNullableStringDynamicTypeArguments: return object_store->type_argument_non_nullable_string_dynamic(); case kNonNullableStringNonNullableStringTypeArguments: return object_store ->type_argument_non_nullable_string_non_nullable_string(); default: break; } UNREACHABLE(); return Type::null(); } static intptr_t GetTypeIndex(ObjectStore* object_store, const ObjectPtr raw_type) { if (raw_type == object_store->legacy_object_type()) { return kLegacyObjectType; } else if (raw_type == object_store->nullable_object_type()) { return kNullableObjectType; } else if (raw_type == object_store->null_type()) { return kNullType; } else if (raw_type == object_store->never_type()) { return kNeverType; } else if (raw_type == object_store->legacy_function_type()) { return kLegacyFunctionType; } else if (raw_type == object_store->legacy_number_type()) { return kLegacyNumberType; } else if (raw_type == object_store->legacy_smi_type()) { return kLegacySmiType; } else if (raw_type == object_store->legacy_mint_type()) { return kLegacyMintType; } else if (raw_type == object_store->legacy_double_type()) { return kLegacyDoubleType; } else if (raw_type == object_store->legacy_int_type()) { return kLegacyIntType; } else if (raw_type == object_store->legacy_bool_type()) { return kLegacyBoolType; } else if (raw_type == object_store->legacy_string_type()) { return kLegacyStringType; } else if (raw_type == object_store->legacy_array_type()) { return kLegacyArrayType; } else if (raw_type == object_store->type_argument_legacy_int()) { return kLegacyIntTypeArguments; } else if (raw_type == object_store->type_argument_legacy_double()) { return kLegacyDoubleTypeArguments; } else if (raw_type == object_store->type_argument_legacy_string()) { return kLegacyStringTypeArguments; } else if (raw_type == object_store->type_argument_legacy_string_dynamic()) { return kLegacyStringDynamicTypeArguments; } else if (raw_type == object_store->type_argument_legacy_string_legacy_string()) { return kLegacyStringLegacyStringTypeArguments; } else if (raw_type == object_store->non_nullable_object_type()) { return kNonNullableObjectType; } else if (raw_type == object_store->non_nullable_function_type()) { return kNonNullableFunctionType; } else if (raw_type == object_store->non_nullable_number_type()) { return kNonNullableNumberType; } else if (raw_type == object_store->non_nullable_smi_type()) { return kNonNullableSmiType; } else if (raw_type == object_store->non_nullable_mint_type()) { return kNonNullableMintType; } else if (raw_type == object_store->non_nullable_double_type()) { return kNonNullableDoubleType; } else if (raw_type == object_store->non_nullable_int_type()) { return kNonNullableIntType; } else if (raw_type == object_store->non_nullable_bool_type()) { return kNonNullableBoolType; } else if (raw_type == object_store->non_nullable_string_type()) { return kNonNullableStringType; } else if (raw_type == object_store->non_nullable_array_type()) { return kNonNullableArrayType; } else if (raw_type == object_store->type_argument_non_nullable_int()) { return kNonNullableIntTypeArguments; } else if (raw_type == object_store->type_argument_non_nullable_double()) { return kNonNullableDoubleTypeArguments; } else if (raw_type == object_store->type_argument_non_nullable_string()) { return kNonNullableStringTypeArguments; } else if (raw_type == object_store->type_argument_non_nullable_string_dynamic()) { return kNonNullableStringDynamicTypeArguments; } else if (raw_type == object_store ->type_argument_non_nullable_string_non_nullable_string()) { return kNonNullableStringNonNullableStringTypeArguments; } return kInvalidIndex; } const char* Snapshot::KindToCString(Kind kind) { switch (kind) { case kFull: return "full"; case kFullCore: return "full-core"; case kFullJIT: return "full-jit"; case kFullAOT: return "full-aot"; case kMessage: return "message"; case kNone: return "none"; case kInvalid: default: return "invalid"; } } const Snapshot* Snapshot::SetupFromBuffer(const void* raw_memory) { ASSERT(raw_memory != NULL); const Snapshot* snapshot = reinterpret_cast(raw_memory); if (!snapshot->check_magic()) { return NULL; } // If the raw length is negative or greater than what the local machine can // handle, then signal an error. int64_t length = snapshot->large_length(); if ((length < 0) || (length > kIntptrMax)) { return NULL; } return snapshot; } SmiPtr BaseReader::ReadAsSmi() { SmiPtr value = static_cast(Read()); ASSERT((static_cast(value) & kSmiTagMask) == kSmiTag); return value; } intptr_t BaseReader::ReadSmiValue() { return Smi::Value(ReadAsSmi()); } SnapshotReader::SnapshotReader(const uint8_t* buffer, intptr_t size, Snapshot::Kind kind, ZoneGrowableArray* backward_refs, Thread* thread) : BaseReader(buffer, size), kind_(kind), thread_(thread), zone_(thread->zone()), heap_(isolate_group()->heap()), old_space_(isolate_group()->heap()->old_space()), cls_(Class::Handle(zone_)), code_(Code::Handle(zone_)), instance_(Instance::Handle(zone_)), instructions_(Instructions::Handle(zone_)), obj_(Object::Handle(zone_)), pobj_(PassiveObject::Handle(zone_)), array_(Array::Handle(zone_)), field_(Field::Handle(zone_)), str_(String::Handle(zone_)), library_(Library::Handle(zone_)), type_(AbstractType::Handle(zone_)), type_arguments_(TypeArguments::Handle(zone_)), tokens_(GrowableObjectArray::Handle(zone_)), data_(ExternalTypedData::Handle(zone_)), typed_data_base_(TypedDataBase::Handle(zone_)), typed_data_(TypedData::Handle(zone_)), typed_data_view_(TypedDataView::Handle(zone_)), function_(Function::Handle(zone_)), smi_(Smi::Handle(zone_)), error_(UnhandledException::Handle(zone_)), set_class_(Class::ZoneHandle( zone_, thread_->isolate_group()->object_store()->linked_hash_set_class())), max_vm_isolate_object_id_( (Snapshot::IsFull(kind)) ? Object::vm_isolate_snapshot_object_table().Length() : 0), backward_references_(backward_refs), types_to_postprocess_(GrowableObjectArray::Handle(zone_)), objects_to_rehash_(GrowableObjectArray::Handle(zone_)) {} ObjectPtr SnapshotReader::ReadObject() { // Setup for long jump in case there is an exception while reading. LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { PassiveObject& obj = PassiveObject::Handle(zone(), ReadObjectImpl(kAsInlinedObject)); for (intptr_t i = 0; i < backward_references_->length(); i++) { if (!(*backward_references_)[i].is_deserialized()) { ReadObjectImpl(kAsInlinedObject); (*backward_references_)[i].set_state(kIsDeserialized); } } Object& result = Object::Handle(zone_); if (backward_references_->length() > 0) { result = (*backward_references_)[0].reference()->ptr(); } else { result = obj.ptr(); } RunDelayedTypePostprocessing(); const Object& ok = Object::Handle(zone_, RunDelayedRehashingOfMaps()); objects_to_rehash_ = GrowableObjectArray::null(); if (!ok.IsNull()) { return ok.ptr(); } return result.ptr(); } else { // An error occurred while reading, return the error object. return Thread::Current()->StealStickyError(); } } void SnapshotReader::EnqueueTypePostprocessing(const AbstractType& type) { if (types_to_postprocess_.IsNull()) { types_to_postprocess_ = GrowableObjectArray::New(); } types_to_postprocess_.Add(type); } void SnapshotReader::RunDelayedTypePostprocessing() { if (types_to_postprocess_.IsNull()) { return; } AbstractType& type = AbstractType::Handle(); Code& code = Code::Handle(); for (intptr_t i = 0; i < types_to_postprocess_.Length(); ++i) { type ^= types_to_postprocess_.At(i); code = TypeTestingStubGenerator::DefaultCodeForType(type); type.SetTypeTestingStub(code); } } void SnapshotReader::EnqueueRehashingOfMap(const LinkedHashMap& map) { if (objects_to_rehash_.IsNull()) { objects_to_rehash_ = GrowableObjectArray::New(); } objects_to_rehash_.Add(map); } ObjectPtr SnapshotReader::RunDelayedRehashingOfMaps() { if (!objects_to_rehash_.IsNull()) { const Library& collections_lib = Library::Handle(zone_, Library::CollectionLibrary()); const Function& rehashing_function = Function::Handle( zone_, collections_lib.LookupFunctionAllowPrivate(Symbols::_rehashObjects())); ASSERT(!rehashing_function.IsNull()); const Array& arguments = Array::Handle(zone_, Array::New(1)); arguments.SetAt(0, objects_to_rehash_); return DartEntry::InvokeFunction(rehashing_function, arguments); } return Object::null(); } ClassPtr SnapshotReader::ReadClassId(intptr_t object_id) { ASSERT(!Snapshot::IsFull(kind_)); // Read the class header information and lookup the class. intptr_t class_header = Read(); ASSERT((class_header & kSmiTagMask) != kSmiTag); ASSERT(!IsVMIsolateObject(class_header) || !IsSingletonClassId(GetVMIsolateObjectId(class_header))); ASSERT((SerializedHeaderTag::decode(class_header) != kObjectId) || !IsBootstrapedClassId(SerializedHeaderData::decode(class_header))); Class& cls = Class::ZoneHandle(zone(), Class::null()); AddBackRef(object_id, &cls, kIsDeserialized); // Read the library/class information and lookup the class. str_ ^= ReadObjectImpl(class_header, kAsInlinedObject); library_ = Library::LookupLibrary(thread(), str_); if (library_.IsNull() || !library_.Loaded()) { SetReadException( "Invalid object found in message: library is not found or loaded."); } str_ ^= ReadObjectImpl(kAsInlinedObject); if (str_.ptr() == Symbols::TopLevel().ptr()) { cls = library_.toplevel_class(); } else { str_ = String::New(String::ScrubName(str_)); cls = library_.LookupClassAllowPrivate(str_); } if (cls.IsNull()) { SetReadException("Invalid object found in message: class not found"); } cls.EnsureIsFinalized(thread()); return cls.ptr(); } ObjectPtr SnapshotReader::ReadStaticImplicitClosure(intptr_t object_id, intptr_t class_header) { ASSERT(!Snapshot::IsFull(kind_)); // First create a function object and associate it with the specified // 'object_id'. Function& func = Function::Handle(zone(), Function::null()); Instance& obj = Instance::ZoneHandle(zone(), Instance::null()); AddBackRef(object_id, &obj, kIsDeserialized); // Read the library/class/function information and lookup the function. // Note: WriteStaticImplicitClosure is *not* scrubbing the names before // writing them into the snapshot, because scrubbing requires allocation. // This means that names we read here might be mangled with private // keys. These keys need to be scrubbed before performing lookups // otherwise lookups might fail. str_ ^= ReadObjectImpl(kAsInlinedObject); library_ = Library::LookupLibrary(thread(), str_); if (library_.IsNull() || !library_.Loaded()) { SetReadException("Invalid Library object found in message."); } str_ ^= ReadObjectImpl(kAsInlinedObject); if (str_.Equals(Symbols::TopLevel())) { str_ ^= ReadObjectImpl(kAsInlinedObject); str_ = String::New(String::ScrubName(str_)); func = library_.LookupFunctionAllowPrivate(str_); } else { str_ = String::New(String::ScrubName(str_)); cls_ = library_.LookupClassAllowPrivate(str_); if (cls_.IsNull()) { OS::PrintErr("Name of class not found %s\n", str_.ToCString()); SetReadException("Invalid Class object found in message."); } cls_.EnsureIsFinalized(thread()); str_ ^= ReadObjectImpl(kAsInlinedObject); str_ = String::New(String::ScrubName(str_)); func = cls_.LookupFunctionAllowPrivate(str_); } if (func.IsNull()) { SetReadException("Invalid function object found in message."); } TypeArguments& delayed_type_arguments = TypeArguments::Handle(zone()); delayed_type_arguments ^= ReadObjectImpl(kAsInlinedObject); func = func.ImplicitClosureFunction(); ASSERT(!func.IsNull()); // If delayedtype arguments were provided, create and return new closure with // those, otherwise return associated implicit static closure. // Note that static closures can't have instantiator or function types since // statics can't refer to class type arguments, don't have outer functions. if (!delayed_type_arguments.IsNull()) { const Context& context = Context::Handle(zone()); obj = Closure::New( /*instantiator_type_arguments=*/Object::null_type_arguments(), /*function_type_arguments=*/Object::null_type_arguments(), delayed_type_arguments, func, context, Heap::kOld); } else { obj = func.ImplicitStaticClosure(); } return obj.ptr(); } intptr_t SnapshotReader::NextAvailableObjectId() const { return backward_references_->length() + kMaxPredefinedObjectIds + max_vm_isolate_object_id_; } void SnapshotReader::SetReadException(const char* msg) { const String& error_str = String::Handle(zone(), String::New(msg)); const Array& args = Array::Handle(zone(), Array::New(1)); args.SetAt(0, error_str); Object& result = Object::Handle(zone()); const Library& library = Library::Handle(zone(), Library::CoreLibrary()); result = DartLibraryCalls::InstanceCreate(library, Symbols::ArgumentError(), Symbols::Dot(), args); const StackTrace& stacktrace = StackTrace::Handle(zone()); const UnhandledException& error = UnhandledException::Handle( zone(), UnhandledException::New(Instance::Cast(result), stacktrace)); thread()->long_jump_base()->Jump(1, error); } ObjectPtr SnapshotReader::VmIsolateSnapshotObject(intptr_t index) const { return Object::vm_isolate_snapshot_object_table().At(index); } bool SnapshotReader::is_vm_isolate() const { return isolate_group() == Dart::vm_isolate_group(); } ObjectPtr SnapshotReader::ReadObjectImpl(bool as_reference) { int64_t header_value = Read(); if ((header_value & kSmiTagMask) == kSmiTag) { return NewInteger(header_value); } ASSERT((header_value <= kIntptrMax) && (header_value >= kIntptrMin)); return ReadObjectImpl(static_cast(header_value), as_reference); } ObjectPtr SnapshotReader::ReadObjectImpl(intptr_t header_value, bool as_reference) { if (IsVMIsolateObject(header_value)) { return ReadVMIsolateObject(header_value); } if (SerializedHeaderTag::decode(header_value) == kObjectId) { return ReadIndexedObject(SerializedHeaderData::decode(header_value)); } ASSERT(SerializedHeaderTag::decode(header_value) == kInlined); intptr_t object_id = SerializedHeaderData::decode(header_value); if (object_id == kOmittedObjectId) { object_id = NextAvailableObjectId(); } // Read the class header information. intptr_t class_header = Read(); intptr_t tags = ReadTags(); bool read_as_reference = as_reference && !UntaggedObject::IsCanonical(tags); intptr_t header_id = SerializedHeaderData::decode(class_header); if (header_id == kInstanceObjectId) { return ReadInstance(object_id, tags, read_as_reference); } else if (header_id == kStaticImplicitClosureObjectId) { // We skip the tags that have been written as the implicit static // closure is going to be created in this isolate or the canonical // version already created in the isolate will be used. return ReadStaticImplicitClosure(object_id, class_header); } ASSERT((class_header & kSmiTagMask) != kSmiTag); intptr_t class_id = LookupInternalClass(class_header); switch (class_id) { #define SNAPSHOT_READ(clazz) \ case clazz::kClassId: { \ pobj_ = clazz::ReadFrom(this, object_id, tags, kind_, read_as_reference); \ break; \ } CLASS_LIST_NO_OBJECT(SNAPSHOT_READ) #undef SNAPSHOT_READ #define SNAPSHOT_READ(clazz) case kTypedData##clazz##Cid: CLASS_LIST_TYPED_DATA(SNAPSHOT_READ) { tags = UntaggedObject::ClassIdTag::update(class_id, tags); pobj_ = TypedData::ReadFrom(this, object_id, tags, kind_, read_as_reference); break; } #undef SNAPSHOT_READ #define SNAPSHOT_READ(clazz) case kExternalTypedData##clazz##Cid: CLASS_LIST_TYPED_DATA(SNAPSHOT_READ) { tags = UntaggedObject::ClassIdTag::update(class_id, tags); pobj_ = ExternalTypedData::ReadFrom(this, object_id, tags, kind_, true); break; } #undef SNAPSHOT_READ #define SNAPSHOT_READ(clazz) case kTypedData##clazz##ViewCid: case kByteDataViewCid: CLASS_LIST_TYPED_DATA(SNAPSHOT_READ) { tags = UntaggedObject::ClassIdTag::update(class_id, tags); pobj_ = TypedDataView::ReadFrom(this, object_id, tags, kind_, true); break; } #undef SNAPSHOT_READ #define SNAPSHOT_READ(clazz) case kFfi##clazz##Cid: CLASS_LIST_FFI(SNAPSHOT_READ) { UNREACHABLE(); } #undef SNAPSHOT_READ default: UNREACHABLE(); break; } return pobj_.ptr(); } void SnapshotReader::EnqueueRehashingOfSet(const Object& set) { if (objects_to_rehash_.IsNull()) { objects_to_rehash_ = GrowableObjectArray::New(); } objects_to_rehash_.Add(set); } ObjectPtr SnapshotReader::ReadInstance(intptr_t object_id, intptr_t tags, bool as_reference) { // Object is regular dart instance. intptr_t instance_size = 0; Instance* result = NULL; DeserializeState state; if (!as_reference) { result = reinterpret_cast(GetBackRef(object_id)); state = kIsDeserialized; } else { state = kIsNotDeserialized; } if (result == NULL) { result = &(Instance::ZoneHandle(zone(), Instance::null())); AddBackRef(object_id, result, state); cls_ ^= ReadObjectImpl(kAsInlinedObject); ASSERT(!cls_.IsNull()); // Closure instances are handled by Closure::ReadFrom(). ASSERT(!cls_.IsClosureClass()); instance_size = cls_.host_instance_size(); ASSERT(instance_size > 0); // Allocate the instance and read in all the fields for the object. *result ^= Object::Allocate(cls_.id(), instance_size, Heap::kNew, /*compressed*/ false); } else { cls_ ^= ReadObjectImpl(kAsInlinedObject); ASSERT(!cls_.IsNull()); instance_size = cls_.host_instance_size(); } if (cls_.id() == set_class_.id()) { EnqueueRehashingOfSet(*result); } if (!as_reference) { // Read all the individual fields for inlined objects. intptr_t next_field_offset = cls_.host_next_field_offset(); intptr_t type_argument_field_offset = cls_.host_type_arguments_field_offset(); ASSERT(next_field_offset > 0); // Instance::NextFieldOffset() returns the offset of the first field in // a Dart object. bool read_as_reference = UntaggedObject::IsCanonical(tags) ? false : true; intptr_t offset = Instance::NextFieldOffset(); intptr_t result_cid = result->GetClassId(); const auto unboxed_fields = isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt( result_cid); while (offset < next_field_offset) { if (unboxed_fields.Get(offset / kWordSize)) { uword* p = reinterpret_cast(result->raw_value() - kHeapObjectTag + offset); // Reads 32 bits of the unboxed value at a time *p = ReadWordWith32BitReads(); } else { pobj_ = ReadObjectImpl(read_as_reference); result->SetFieldAtOffset(offset, pobj_); if ((offset != type_argument_field_offset) && (kind_ == Snapshot::kMessage) && isolate_group()->use_field_guards() && (pobj_.ptr() != Object::sentinel().ptr())) { // TODO(fschneider): Consider hoisting these lookups out of the loop. // This would involve creating a handle, since cls_ can't be reused // across the call to ReadObjectImpl. cls_ = isolate_group()->class_table()->At(result_cid); array_ = cls_.OffsetToFieldMap(); field_ ^= array_.At(offset >> kWordSizeLog2); ASSERT(!field_.IsNull()); ASSERT(field_.HostOffset() == offset); obj_ = pobj_.ptr(); field_.RecordStore(obj_); } // TODO(fschneider): Verify the guarded cid and length for other kinds // of snapshot (kFull, kScript) with asserts. } offset += kWordSize; } if (UntaggedObject::IsCanonical(tags)) { *result = result->Canonicalize(thread()); ASSERT(!result->IsNull()); } } return result->ptr(); } void SnapshotReader::AddBackRef(intptr_t id, Object* obj, DeserializeState state) { intptr_t index = (id - kMaxPredefinedObjectIds); ASSERT(index >= max_vm_isolate_object_id_); index -= max_vm_isolate_object_id_; ASSERT(index == backward_references_->length()); BackRefNode node(obj, state); backward_references_->Add(node); } Object* SnapshotReader::GetBackRef(intptr_t id) { ASSERT(id >= kMaxPredefinedObjectIds); intptr_t index = (id - kMaxPredefinedObjectIds); ASSERT(index >= max_vm_isolate_object_id_); index -= max_vm_isolate_object_id_; if (index < backward_references_->length()) { return (*backward_references_)[index].reference(); } return NULL; } ApiErrorPtr SnapshotReader::VerifyVersionAndFeatures( IsolateGroup* isolate_group) { // If the version string doesn't match, return an error. // Note: New things are allocated only if we're going to return an error. const char* expected_version = Version::SnapshotString(); ASSERT(expected_version != NULL); const intptr_t version_len = strlen(expected_version); if (PendingBytes() < version_len) { const intptr_t kMessageBufferSize = 128; char message_buffer[kMessageBufferSize]; Utils::SNPrint(message_buffer, kMessageBufferSize, "No full snapshot version found, expected '%s'", expected_version); // This can also fail while bringing up the VM isolate, so make sure to // allocate the error message in old space. const String& msg = String::Handle(String::New(message_buffer, Heap::kOld)); return ApiError::New(msg, Heap::kOld); } const char* version = reinterpret_cast(CurrentBufferAddress()); ASSERT(version != NULL); if (strncmp(version, expected_version, version_len) != 0) { const intptr_t kMessageBufferSize = 256; char message_buffer[kMessageBufferSize]; char* actual_version = Utils::StrNDup(version, version_len); Utils::SNPrint(message_buffer, kMessageBufferSize, "Wrong %s snapshot version, expected '%s' found '%s'", (Snapshot::IsFull(kind_)) ? "full" : "script", expected_version, actual_version); free(actual_version); // This can also fail while bringing up the VM isolate, so make sure to // allocate the error message in old space. const String& msg = String::Handle(String::New(message_buffer, Heap::kOld)); return ApiError::New(msg, Heap::kOld); } Advance(version_len); const char* expected_features = Dart::FeaturesString(isolate_group, false, kind_); ASSERT(expected_features != NULL); const intptr_t expected_len = strlen(expected_features); const char* features = reinterpret_cast(CurrentBufferAddress()); ASSERT(features != NULL); intptr_t buffer_len = Utils::StrNLen(features, PendingBytes()); if ((buffer_len != expected_len) || (strncmp(features, expected_features, expected_len) != 0)) { const intptr_t kMessageBufferSize = 256; char message_buffer[kMessageBufferSize]; char* actual_features = Utils::StrNDup(features, buffer_len < 128 ? buffer_len : 128); Utils::SNPrint(message_buffer, kMessageBufferSize, "Snapshot not compatible with the current VM configuration: " "the snapshot requires '%s' but the VM has '%s'", actual_features, expected_features); free(const_cast(expected_features)); free(actual_features); // This can also fail while bringing up the VM isolate, so make sure to // allocate the error message in old space. const String& msg = String::Handle(String::New(message_buffer, Heap::kOld)); return ApiError::New(msg, Heap::kOld); } free(const_cast(expected_features)); Advance(expected_len + 1); return ApiError::null(); } ObjectPtr SnapshotReader::NewInteger(int64_t value) { ASSERT((value & kSmiTagMask) == kSmiTag); value = value >> kSmiTagShift; if (Smi::IsValid(value)) { return Smi::New(static_cast(value)); } return Mint::NewCanonical(value); } intptr_t SnapshotReader::LookupInternalClass(intptr_t class_header) { // If the header is an object Id, lookup singleton VM classes or classes // stored in the object store. if (IsVMIsolateObject(class_header)) { intptr_t class_id = GetVMIsolateObjectId(class_header); ASSERT(IsSingletonClassId(class_id)); return class_id; } ASSERT(SerializedHeaderTag::decode(class_header) == kObjectId); intptr_t class_id = SerializedHeaderData::decode(class_header); ASSERT(IsBootstrapedClassId(class_id) || IsSingletonClassId(class_id)); return class_id; } #define READ_VM_SINGLETON_OBJ(id, obj) \ if (object_id == id) { \ return obj; \ } ObjectPtr SnapshotReader::ReadVMIsolateObject(intptr_t header_value) { intptr_t object_id = GetVMIsolateObjectId(header_value); // First check if it is one of the singleton objects. READ_VM_SINGLETON_OBJ(kNullObject, Object::null()); READ_VM_SINGLETON_OBJ(kSentinelObject, Object::sentinel().ptr()); READ_VM_SINGLETON_OBJ(kTransitionSentinelObject, Object::transition_sentinel().ptr()); READ_VM_SINGLETON_OBJ(kEmptyArrayObject, Object::empty_array().ptr()); READ_VM_SINGLETON_OBJ(kZeroArrayObject, Object::zero_array().ptr()); READ_VM_SINGLETON_OBJ(kDynamicType, Object::dynamic_type().ptr()); READ_VM_SINGLETON_OBJ(kVoidType, Object::void_type().ptr()); READ_VM_SINGLETON_OBJ(kEmptyTypeArguments, Object::empty_type_arguments().ptr()); READ_VM_SINGLETON_OBJ(kTrueValue, Bool::True().ptr()); READ_VM_SINGLETON_OBJ(kFalseValue, Bool::False().ptr()); READ_VM_SINGLETON_OBJ(kExtractorParameterTypes, Object::extractor_parameter_types().ptr()); READ_VM_SINGLETON_OBJ(kExtractorParameterNames, Object::extractor_parameter_names().ptr()); READ_VM_SINGLETON_OBJ(kEmptyContextScopeObject, Object::empty_context_scope().ptr()); READ_VM_SINGLETON_OBJ(kEmptyObjectPool, Object::empty_object_pool().ptr()); READ_VM_SINGLETON_OBJ(kEmptyDescriptors, Object::empty_descriptors().ptr()); READ_VM_SINGLETON_OBJ(kEmptyVarDescriptors, Object::empty_var_descriptors().ptr()); READ_VM_SINGLETON_OBJ(kEmptyExceptionHandlers, Object::empty_exception_handlers().ptr()); // Check if it is a double. if (object_id == kDoubleObject) { ASSERT(kind_ == Snapshot::kMessage); return Double::New(ReadDouble()); } // Check it is a singleton class object. intptr_t class_id = ClassIdFromObjectId(object_id); if (IsSingletonClassId(class_id)) { return isolate_group()->class_table()->At( class_id); // get singleton class. } // Check if it is a singleton Argument descriptor object. for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) { if (object_id == (kCachedArgumentsDescriptor0 + i)) { return ArgumentsDescriptor::cached_args_descriptors_[i]; } } // Check if it is a singleton ICData array object. for (intptr_t i = 0; i < ICData::kCachedICDataArrayCount; i++) { if (object_id == (kCachedICDataArray0 + i)) { return ICData::cached_icdata_arrays_[i]; } } ASSERT(Symbols::IsPredefinedSymbolId(object_id)); return Symbols::GetPredefinedSymbol(object_id); // return VM symbol. } ObjectPtr SnapshotReader::ReadIndexedObject(intptr_t object_id) { intptr_t class_id = ClassIdFromObjectId(object_id); if (IsBootstrapedClassId(class_id)) { return isolate_group()->class_table()->At( class_id); // get singleton class. } if (IsObjectStoreTypeId(object_id)) { return GetType(object_store(), object_id); // return type obj. } ASSERT(object_id >= kMaxPredefinedObjectIds); intptr_t index = (object_id - kMaxPredefinedObjectIds); if (index < max_vm_isolate_object_id_) { return VmIsolateSnapshotObject(index); } return GetBackRef(object_id)->ptr(); } void SnapshotReader::ArrayReadFrom(intptr_t object_id, const Array& result, intptr_t len, intptr_t tags) { // Setup the object fields. *TypeArgumentsHandle() ^= ReadObjectImpl(kAsInlinedObject); result.SetTypeArguments(*TypeArgumentsHandle()); bool as_reference = UntaggedObject::IsCanonical(tags) ? false : true; for (intptr_t i = 0; i < len; i++) { *PassiveObjectHandle() = ReadObjectImpl(as_reference); result.SetAt(i, *PassiveObjectHandle()); } } MessageSnapshotReader::MessageSnapshotReader(Message* message, Thread* thread) : SnapshotReader(message->snapshot(), message->snapshot_length(), Snapshot::kMessage, new ZoneGrowableArray(kNumInitialReferences), thread), finalizable_data_(message->finalizable_data()) {} MessageSnapshotReader::~MessageSnapshotReader() { ResetBackwardReferenceTable(); } SnapshotWriter::SnapshotWriter(Thread* thread, Snapshot::Kind kind, intptr_t initial_size, ForwardList* forward_list, bool can_send_any_object) : BaseWriter(initial_size), thread_(thread), kind_(kind), object_store_(isolate_group()->object_store()), class_table_(isolate_group()->class_table()), forward_list_(forward_list), exception_type_(Exceptions::kNone), exception_msg_(NULL), can_send_any_object_(can_send_any_object) { ASSERT(forward_list_ != NULL); } void SnapshotWriter::WriteObject(ObjectPtr rawobj) { WriteObjectImpl(rawobj, kAsInlinedObject); WriteForwardedObjects(); } uint32_t SnapshotWriter::GetObjectTags(ObjectPtr raw) { uword tags = raw->untag()->tags_; #if defined(HASH_IN_OBJECT_HEADER) // Clear hash to make the narrowing cast safe / appease UBSAN. tags = UntaggedObject::HashTag::update(0, tags); #endif return tags; } uint32_t SnapshotWriter::GetObjectTags(UntaggedObject* raw) { uword tags = raw->tags_; #if defined(HASH_IN_OBJECT_HEADER) // Clear hash to make the narrowing cast safe / appease UBSAN. tags = UntaggedObject::HashTag::update(0, tags); #endif return tags; } uword SnapshotWriter::GetObjectTagsAndHash(ObjectPtr raw) { return raw->untag()->tags_; } #define VM_OBJECT_CLASS_LIST(V) \ V(OneByteString) \ V(TwoByteString) \ V(Mint) \ V(Double) \ V(ImmutableArray) #define VM_OBJECT_WRITE(clazz) \ case clazz::kClassId: { \ object_id = forward_list_->AddObject(zone(), rawobj, kIsSerialized); \ clazz##Ptr raw_obj = static_cast(rawobj); \ raw_obj->untag()->WriteTo(this, object_id, kind(), false); \ return true; \ } #define WRITE_VM_SINGLETON_OBJ(obj, id) \ if (rawobj == obj) { \ WriteVMIsolateObject(id); \ return true; \ } bool SnapshotWriter::HandleVMIsolateObject(ObjectPtr rawobj) { // Check if it is one of the singleton VM objects. WRITE_VM_SINGLETON_OBJ(Object::null(), kNullObject); WRITE_VM_SINGLETON_OBJ(Object::sentinel().ptr(), kSentinelObject); WRITE_VM_SINGLETON_OBJ(Object::transition_sentinel().ptr(), kTransitionSentinelObject); WRITE_VM_SINGLETON_OBJ(Object::empty_array().ptr(), kEmptyArrayObject); WRITE_VM_SINGLETON_OBJ(Object::zero_array().ptr(), kZeroArrayObject); WRITE_VM_SINGLETON_OBJ(Object::dynamic_type().ptr(), kDynamicType); WRITE_VM_SINGLETON_OBJ(Object::void_type().ptr(), kVoidType); WRITE_VM_SINGLETON_OBJ(Object::empty_type_arguments().ptr(), kEmptyTypeArguments); WRITE_VM_SINGLETON_OBJ(Bool::True().ptr(), kTrueValue); WRITE_VM_SINGLETON_OBJ(Bool::False().ptr(), kFalseValue); WRITE_VM_SINGLETON_OBJ(Object::extractor_parameter_types().ptr(), kExtractorParameterTypes); WRITE_VM_SINGLETON_OBJ(Object::extractor_parameter_names().ptr(), kExtractorParameterNames); WRITE_VM_SINGLETON_OBJ(Object::empty_context_scope().ptr(), kEmptyContextScopeObject); WRITE_VM_SINGLETON_OBJ(Object::empty_object_pool().ptr(), kEmptyObjectPool); WRITE_VM_SINGLETON_OBJ(Object::empty_descriptors().ptr(), kEmptyDescriptors); WRITE_VM_SINGLETON_OBJ(Object::empty_var_descriptors().ptr(), kEmptyVarDescriptors); WRITE_VM_SINGLETON_OBJ(Object::empty_exception_handlers().ptr(), kEmptyExceptionHandlers); // Check if it is a singleton class object which is shared by // all isolates. intptr_t id = rawobj->GetClassId(); if (id == kClassCid) { ClassPtr raw_class = static_cast(rawobj); intptr_t class_id = raw_class->untag()->id_; if (IsSingletonClassId(class_id)) { intptr_t object_id = ObjectIdFromClassId(class_id); WriteVMIsolateObject(object_id); return true; } } // Check if it is a singleton Argument descriptor object. for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) { if (rawobj == ArgumentsDescriptor::cached_args_descriptors_[i]) { WriteVMIsolateObject(kCachedArgumentsDescriptor0 + i); return true; } } // Check if it is a singleton ICData array object. for (intptr_t i = 0; i < ICData::kCachedICDataArrayCount; i++) { if (rawobj == ICData::cached_icdata_arrays_[i]) { WriteVMIsolateObject(kCachedICDataArray0 + i); return true; } } // In the case of script snapshots or for messages we do not use // the index into the vm isolate snapshot object table, instead we // explicitly write the object out. intptr_t object_id = forward_list_->FindObject(rawobj); if (object_id != -1) { WriteIndexedObject(object_id); return true; } else { // We do this check down here, because it's quite expensive. if (!rawobj->untag()->InVMIsolateHeap()) { return false; } switch (id) { VM_OBJECT_CLASS_LIST(VM_OBJECT_WRITE) case kTypedDataUint32ArrayCid: { object_id = forward_list_->AddObject(zone(), rawobj, kIsSerialized); TypedDataPtr raw_obj = static_cast(rawobj); raw_obj->untag()->WriteTo(this, object_id, kind(), false); return true; } default: OS::PrintErr("class id = %" Pd "\n", id); break; } } const Object& obj = Object::Handle(rawobj); FATAL1("Unexpected reference to object in VM isolate: %s\n", obj.ToCString()); return false; } #undef VM_OBJECT_WRITE ForwardList::ForwardList(Thread* thread, intptr_t first_object_id) : thread_(thread), first_object_id_(first_object_id), nodes_(), first_unprocessed_object_id_(first_object_id) { ASSERT(first_object_id > 0); isolate()->set_forward_table_new(new WeakTable()); isolate()->set_forward_table_old(new WeakTable()); } ForwardList::~ForwardList() { isolate()->set_forward_table_new(nullptr); isolate()->set_forward_table_old(nullptr); } intptr_t ForwardList::AddObject(Zone* zone, ObjectPtr raw, SerializeState state) { NoSafepointScope no_safepoint; intptr_t object_id = next_object_id(); ASSERT(object_id > 0 && object_id <= kMaxObjectId); const Object& obj = Object::ZoneHandle(zone, raw); Node* node = new Node(&obj, state); ASSERT(node != NULL); nodes_.Add(node); ASSERT(object_id != 0); SetObjectId(raw, object_id); return object_id; } intptr_t ForwardList::FindObject(ObjectPtr raw) { NoSafepointScope no_safepoint; intptr_t id = GetObjectId(raw); ASSERT(id == 0 || NodeForObjectId(id)->obj()->ptr() == raw); return (id == 0) ? static_cast(kInvalidIndex) : id; } void ForwardList::SetObjectId(ObjectPtr object, intptr_t id) { if (object->IsNewObject()) { isolate()->forward_table_new()->SetValueExclusive(object, id); } else { isolate()->forward_table_old()->SetValueExclusive(object, id); } } intptr_t ForwardList::GetObjectId(ObjectPtr object) { if (object->IsNewObject()) { return isolate()->forward_table_new()->GetValueExclusive(object); } else { return isolate()->forward_table_old()->GetValueExclusive(object); } } bool SnapshotWriter::CheckAndWritePredefinedObject(ObjectPtr rawobj) { // Check if object can be written in one of the following ways: // - Smi: the Smi value is written as is (last bit is not tagged). // - VM internal class (from VM isolate): (index of class in vm isolate | 0x3) // - Object that has already been written: (negative id in stream | 0x3) NoSafepointScope no_safepoint; // First check if it is a Smi (i.e not a heap object). if (!rawobj->IsHeapObject()) { #if !defined(DART_COMPRESSED_POINTERS) Write(static_cast(rawobj)); #else // One might expect this to be unnecessary because the reader will just // ignore the upper bits, but the upper bits affect the variable-length // encoding and can change lower bits in the variable-length reader. Write(static_cast(rawobj) << 32 >> 32); #endif return true; } intptr_t cid = rawobj->GetClassId(); if ((kind_ == Snapshot::kMessage) && (cid == kDoubleCid)) { WriteVMIsolateObject(kDoubleObject); DoublePtr rd = static_cast(rawobj); WriteDouble(rd->untag()->value_); return true; } // Check if object has already been serialized, in that case just write // the object id out. intptr_t object_id = forward_list_->FindObject(rawobj); if (object_id != kInvalidIndex) { WriteIndexedObject(object_id); return true; } // Check if it is a code object in that case just write a Null object // as we do not want code objects in the snapshot. if (cid == kCodeCid) { WriteVMIsolateObject(kNullObject); return true; } // Now check if it is an object from the VM isolate. These objects are shared // by all isolates. if (HandleVMIsolateObject(rawobj)) { return true; } // Check if classes are not being serialized and it is preinitialized type // or a predefined internal VM class in the object store. // Check if it is an internal VM class which is in the object store. if (cid == kClassCid) { ClassPtr raw_class = static_cast(rawobj); intptr_t class_id = raw_class->untag()->id_; if (IsBootstrapedClassId(class_id)) { intptr_t object_id = ObjectIdFromClassId(class_id); WriteIndexedObject(object_id); return true; } } // Now check it is a preinitialized type object. intptr_t index = GetTypeIndex(object_store(), rawobj); if (index != kInvalidIndex) { WriteIndexedObject(index); return true; } return false; } void SnapshotWriter::WriteObjectImpl(ObjectPtr raw, bool as_reference) { // First check if object can be written as a simple predefined type. if (CheckAndWritePredefinedObject(raw)) { return; } // When we know that we are dealing with leaf or shallow objects we write // these objects inline even when 'as_reference' is true. const bool write_as_reference = as_reference && !raw->untag()->IsCanonical(); uintptr_t tags = GetObjectTagsAndHash(raw); // Add object to the forward ref list and mark it so that future references // to this object in the snapshot will use this object id. Mark the // serialization state so that we do the right thing when we go through // the forward list. intptr_t class_id = raw->GetClassId(); intptr_t object_id; if (write_as_reference && IsSplitClassId(class_id)) { object_id = forward_list_->AddObject(zone(), raw, kIsNotSerialized); } else { object_id = forward_list_->AddObject(zone(), raw, kIsSerialized); } if (write_as_reference || !IsSplitClassId(class_id)) { object_id = kOmittedObjectId; } WriteMarkedObjectImpl(raw, tags, object_id, write_as_reference); } void SnapshotWriter::WriteMarkedObjectImpl(ObjectPtr raw, intptr_t tags, intptr_t object_id, bool as_reference) { NoSafepointScope no_safepoint; ClassPtr cls = class_table_->At(UntaggedObject::ClassIdTag::decode(tags)); intptr_t class_id = cls->untag()->id_; ASSERT(class_id == UntaggedObject::ClassIdTag::decode(tags)); if (class_id >= kNumPredefinedCids || IsImplicitFieldClassId(class_id)) { WriteInstance(raw, cls, tags, object_id, as_reference); return; } switch (class_id) { #define SNAPSHOT_WRITE(clazz) \ case clazz::kClassId: { \ clazz##Ptr raw_obj = static_cast(raw); \ raw_obj->untag()->WriteTo(this, object_id, kind_, as_reference); \ return; \ } CLASS_LIST_NO_OBJECT(SNAPSHOT_WRITE) #undef SNAPSHOT_WRITE #define SNAPSHOT_WRITE(clazz) case kTypedData##clazz##Cid: CLASS_LIST_TYPED_DATA(SNAPSHOT_WRITE) { TypedDataPtr raw_obj = static_cast(raw); raw_obj->untag()->WriteTo(this, object_id, kind_, as_reference); return; } #undef SNAPSHOT_WRITE #define SNAPSHOT_WRITE(clazz) case kExternalTypedData##clazz##Cid: CLASS_LIST_TYPED_DATA(SNAPSHOT_WRITE) { ExternalTypedDataPtr raw_obj = static_cast(raw); raw_obj->untag()->WriteTo(this, object_id, kind_, as_reference); return; } #undef SNAPSHOT_WRITE #define SNAPSHOT_WRITE(clazz) case kTypedData##clazz##ViewCid: case kByteDataViewCid: CLASS_LIST_TYPED_DATA(SNAPSHOT_WRITE) { auto raw_obj = static_cast(raw); raw_obj->untag()->WriteTo(this, object_id, kind_, as_reference); return; } #undef SNAPSHOT_WRITE #define SNAPSHOT_WRITE(clazz) case kFfi##clazz##Cid: CLASS_LIST_FFI(SNAPSHOT_WRITE) { SetWriteException(Exceptions::kArgument, "Native objects (from dart:ffi) such as Pointers and " "Structs cannot be passed between isolates."); UNREACHABLE(); } #undef SNAPSHOT_WRITE default: break; } const Object& obj = Object::Handle(raw); FATAL1("Unexpected object: %s\n", obj.ToCString()); } class WriteInlinedObjectVisitor : public ObjectVisitor { public: explicit WriteInlinedObjectVisitor(SnapshotWriter* writer) : writer_(writer) {} virtual void VisitObject(ObjectPtr obj) { intptr_t object_id = writer_->forward_list_->FindObject(obj); ASSERT(object_id != kInvalidIndex); intptr_t tags = MessageWriter::GetObjectTagsAndHash(ObjectPtr(obj)); writer_->WriteMarkedObjectImpl(obj, tags, object_id, kAsInlinedObject); } private: SnapshotWriter* writer_; }; void SnapshotWriter::WriteForwardedObjects() { WriteInlinedObjectVisitor visitor(this); forward_list_->SerializeAll(&visitor); } void ForwardList::SerializeAll(ObjectVisitor* writer) { // Write out all objects that were added to the forward list and have // not been serialized yet. These would typically be fields of instance // objects, arrays or immutable arrays (this is done in order to avoid // deep recursive calls to WriteObjectImpl). // NOTE: The forward list might grow as we process the list. #ifdef DEBUG for (intptr_t i = first_object_id(); i < first_unprocessed_object_id_; ++i) { ASSERT(NodeForObjectId(i)->is_serialized()); } #endif // DEBUG for (intptr_t id = first_unprocessed_object_id_; id < next_object_id(); ++id) { if (!NodeForObjectId(id)->is_serialized()) { // Write the object out in the stream. ObjectPtr raw = NodeForObjectId(id)->obj()->ptr(); writer->VisitObject(raw); // Mark object as serialized. NodeForObjectId(id)->set_state(kIsSerialized); } } first_unprocessed_object_id_ = next_object_id(); } void SnapshotWriter::WriteClassId(UntaggedClass* cls) { ASSERT(!Snapshot::IsFull(kind_)); int class_id = cls->id_; ASSERT(!IsSingletonClassId(class_id) && !IsBootstrapedClassId(class_id)); // Write out the library url and class name. LibraryPtr library = cls->library(); ASSERT(library != Library::null()); WriteObjectImpl(library->untag()->url(), kAsInlinedObject); WriteObjectImpl(cls->name(), kAsInlinedObject); } void SnapshotWriter::WriteStaticImplicitClosure( intptr_t object_id, FunctionPtr func, intptr_t tags, TypeArgumentsPtr delayed_type_arguments) { // Write out the serialization header value for this object. WriteInlinedObjectHeader(object_id); // Indicate this is a static implicit closure object. Write(SerializedHeaderData::encode(kStaticImplicitClosureObjectId)); // Write out the tags. WriteTags(tags); // Write out the library url, class name and signature function name. ClassPtr cls = GetFunctionOwner(func); ASSERT(cls != Class::null()); LibraryPtr library = cls->untag()->library(); ASSERT(library != Library::null()); WriteObjectImpl(library->untag()->url(), kAsInlinedObject); WriteObjectImpl(cls->untag()->name(), kAsInlinedObject); WriteObjectImpl(func->untag()->name(), kAsInlinedObject); WriteObjectImpl(delayed_type_arguments, kAsInlinedObject); } void SnapshotWriter::ArrayWriteTo(intptr_t object_id, intptr_t array_kind, intptr_t tags, SmiPtr length, TypeArgumentsPtr type_arguments, ObjectPtr data[], bool as_reference) { if (as_reference) { // Write out the serialization header value for this object. WriteInlinedObjectHeader(kOmittedObjectId); // Write out the class information. WriteIndexedObject(array_kind); WriteTags(tags); // Write out the length field. Write(length); } else { intptr_t len = Smi::Value(length); // Write out the serialization header value for this object. WriteInlinedObjectHeader(object_id); // Write out the class and tags information. WriteIndexedObject(array_kind); WriteTags(tags); // Write out the length field. Write(length); // Write out the type arguments. WriteObjectImpl(type_arguments, kAsInlinedObject); // Write out the individual object ids. bool write_as_reference = UntaggedObject::IsCanonical(tags) ? false : true; for (intptr_t i = 0; i < len; i++) { WriteObjectImpl(data[i], write_as_reference); } } } FunctionPtr SnapshotWriter::IsSerializableClosure(ClosurePtr closure) { // Extract the function object to check if this closure // can be sent in an isolate message. FunctionPtr func = closure->untag()->function(); // We only allow closure of top level methods or static functions in a // class to be sent in isolate messages. if (can_send_any_object() && Function::IsImplicitStaticClosureFunction(func)) { return func; } // Not a closure of a top level method or static function, throw an // exception as we do not allow these objects to be serialized. HANDLESCOPE(thread()); const Function& errorFunc = Function::Handle(zone(), func); ASSERT(!errorFunc.IsNull()); // All other closures are errors. char* chars = OS::SCreate( thread()->zone(), "Illegal argument in isolate message : (object is a closure - %s)", errorFunc.ToCString()); SetWriteException(Exceptions::kArgument, chars); return Function::null(); } ClassPtr SnapshotWriter::GetFunctionOwner(FunctionPtr func) { ObjectPtr owner = func->untag()->owner(); uword tags = GetObjectTags(owner); intptr_t class_id = UntaggedObject::ClassIdTag::decode(tags); if (class_id == kClassCid) { return static_cast(owner); } ASSERT(class_id == kPatchClassCid); return static_cast(owner)->untag()->patched_class(); } void SnapshotWriter::CheckForNativeFields(ClassPtr cls) { if (cls->untag()->num_native_fields_ != 0) { // We do not allow objects with native fields in an isolate message. HANDLESCOPE(thread()); const Class& clazz = Class::Handle(zone(), cls); char* chars = OS::SCreate(thread()->zone(), "Illegal argument in isolate message" " : (object extends NativeWrapper - %s)", clazz.ToCString()); SetWriteException(Exceptions::kArgument, chars); } } void SnapshotWriter::SetWriteException(Exceptions::ExceptionType type, const char* msg) { set_exception_type(type); set_exception_msg(msg); // The more specific error is set up in SnapshotWriter::ThrowException(). thread()->long_jump_base()->Jump(1, Object::snapshot_writer_error()); } void SnapshotWriter::WriteInstance(ObjectPtr raw, ClassPtr cls, intptr_t tags, intptr_t object_id, bool as_reference) { // Closure instances are handled by UntaggedClosure::WriteTo(). ASSERT(!Class::IsClosureClass(cls)); // Check if the instance has native fields and throw an exception if it does. CheckForNativeFields(cls); // Object is regular dart instance. if (as_reference) { // Write out the serialization header value for this object. WriteInlinedObjectHeader(kOmittedObjectId); // Indicate this is an instance object. Write(SerializedHeaderData::encode(kInstanceObjectId)); WriteTags(tags); // Write out the class information for this object. WriteObjectImpl(cls, kAsInlinedObject); } else { intptr_t next_field_offset = Class::host_next_field_offset_in_words(cls) << kWordSizeLog2; ASSERT(next_field_offset > 0); // Write out the serialization header value for this object. WriteInlinedObjectHeader(object_id); // Indicate this is an instance object. Write(SerializedHeaderData::encode(kInstanceObjectId)); // Write out the tags. WriteTags(tags); // Write out the class information for this object. WriteObjectImpl(cls, kAsInlinedObject); const auto unboxed_fields = isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt( cls->untag()->id_); // Write out all the fields for the object. // Instance::NextFieldOffset() returns the offset of the first field in // a Dart object. bool write_as_reference = UntaggedObject::IsCanonical(tags) ? false : true; intptr_t offset = Instance::NextFieldOffset(); while (offset < next_field_offset) { if (unboxed_fields.Get(offset / kWordSize)) { // Writes 32 bits of the unboxed value at a time const uword value = *reinterpret_cast( reinterpret_cast(raw->untag()) + offset); WriteWordWith32BitWrites(value); } else { ObjectPtr raw_obj = *reinterpret_cast( reinterpret_cast(raw->untag()) + offset); WriteObjectImpl(raw_obj, write_as_reference); } offset += kWordSize; } } return; } bool SnapshotWriter::AllowObjectsInDartLibrary(LibraryPtr library) { return (library == object_store()->collection_library() || library == object_store()->core_library() || library == object_store()->typed_data_library()); } intptr_t SnapshotWriter::FindVmSnapshotObject(ObjectPtr rawobj) { intptr_t length = Object::vm_isolate_snapshot_object_table().Length(); for (intptr_t i = 0; i < length; i++) { if (Object::vm_isolate_snapshot_object_table().At(i) == rawobj) { return (i + kMaxPredefinedObjectIds); } } return kInvalidIndex; } void SnapshotWriter::ThrowException(Exceptions::ExceptionType type, const char* msg) { { NoSafepointScope no_safepoint; ErrorPtr error = thread()->StealStickyError(); ASSERT(error == Object::snapshot_writer_error().ptr()); } if (msg != NULL) { const String& msg_obj = String::Handle(String::New(msg)); const Array& args = Array::Handle(Array::New(1)); args.SetAt(0, msg_obj); Exceptions::ThrowByType(type, args); } else { Exceptions::ThrowByType(type, Object::empty_array()); } UNREACHABLE(); } void SnapshotWriter::WriteVersionAndFeatures() { const char* expected_version = Version::SnapshotString(); ASSERT(expected_version != NULL); const intptr_t version_len = strlen(expected_version); WriteBytes(reinterpret_cast(expected_version), version_len); const char* expected_features = Dart::FeaturesString(IsolateGroup::Current(), false, kind_); ASSERT(expected_features != NULL); const intptr_t features_len = strlen(expected_features); WriteBytes(reinterpret_cast(expected_features), features_len + 1); free(const_cast(expected_features)); } void SnapshotWriterVisitor::VisitPointers(ObjectPtr* first, ObjectPtr* last) { ASSERT(Utils::IsAligned(first, sizeof(*first))); ASSERT(Utils::IsAligned(last, sizeof(*last))); for (ObjectPtr* current = first; current <= last; current++) { ObjectPtr raw_obj = *current; writer_->WriteObjectImpl(raw_obj, as_references_); } } void SnapshotWriterVisitor::VisitCompressedPointers(uword heap_base, CompressedObjectPtr* first, CompressedObjectPtr* last) { ASSERT(Utils::IsAligned(first, sizeof(*first))); ASSERT(Utils::IsAligned(last, sizeof(*last))); for (CompressedObjectPtr* current = first; current <= last; current++) { ObjectPtr raw_obj = current->Decompress(heap_base); writer_->WriteObjectImpl(raw_obj, as_references_); } } MessageWriter::MessageWriter(bool can_send_any_object) : SnapshotWriter(Thread::Current(), Snapshot::kMessage, kInitialSize, &forward_list_, can_send_any_object), forward_list_(thread(), kMaxPredefinedObjectIds), finalizable_data_(new MessageFinalizableData()) {} MessageWriter::~MessageWriter() { delete finalizable_data_; } std::unique_ptr MessageWriter::WriteMessage( const Object& obj, Dart_Port dest_port, Message::Priority priority) { ASSERT(kind() == Snapshot::kMessage); ASSERT(isolate() != NULL); // Setup for long jump in case there is an exception while writing // the message. volatile bool has_exception = false; { LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { NoSafepointScope no_safepoint; WriteObject(obj.ptr()); } else { FreeBuffer(); has_exception = true; } } if (has_exception) { ThrowException(exception_type(), exception_msg()); } else { finalizable_data_->SerializationSucceeded(); } MessageFinalizableData* finalizable_data = finalizable_data_; finalizable_data_ = nullptr; intptr_t size; uint8_t* buffer = Steal(&size); return Message::New(dest_port, buffer, size, finalizable_data, priority); } } // namespace dart