mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:51:50 +00:00
22e9b612a1
Fixes https://github.com/dart-lang/sdk/issues/41629 TEST=isolate_send_regex_test.dart Change-Id: Ia8e00e175c9bca5c1c16cc2718f8b2d16cd37104 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193755 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
1655 lines
62 KiB
C++
1655 lines
62 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "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<const Snapshot*>(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<SmiPtr>(Read<intptr_t>());
|
|
ASSERT((static_cast<uword>(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<BackRefNode>* 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<int32_t>();
|
|
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<int64_t>();
|
|
if ((header_value & kSmiTagMask) == kSmiTag) {
|
|
return NewInteger(header_value);
|
|
}
|
|
ASSERT((header_value <= kIntptrMax) && (header_value >= kIntptrMin));
|
|
return ReadObjectImpl(static_cast<intptr_t>(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<int32_t>();
|
|
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<Instance*>(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<uword*>(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<const char*>(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<const char*>(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<char*>(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<char*>(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<intptr_t>(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<BackRefNode>(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<clazz##Ptr>(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<ClassPtr>(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<TypedDataPtr>(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<intptr_t>(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<int64_t>(static_cast<intptr_t>(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<int64_t>(static_cast<intptr_t>(rawobj) << 32 >> 32);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
intptr_t cid = rawobj->GetClassId();
|
|
|
|
if ((kind_ == Snapshot::kMessage) && (cid == kDoubleCid)) {
|
|
WriteVMIsolateObject(kDoubleObject);
|
|
DoublePtr rd = static_cast<DoublePtr>(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<ClassPtr>(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<clazz##Ptr>(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<TypedDataPtr>(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<ExternalTypedDataPtr>(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<TypedDataViewPtr>(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<int32_t>(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<ObjectPtr>(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<ObjectPtr>(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<ClassPtr>(owner);
|
|
}
|
|
ASSERT(class_id == kPatchClassCid);
|
|
return static_cast<PatchClassPtr>(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<int32_t>(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<int32_t>(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<uword*>(
|
|
reinterpret_cast<uword>(raw->untag()) + offset);
|
|
WriteWordWith32BitWrites(value);
|
|
} else {
|
|
ObjectPtr raw_obj = *reinterpret_cast<ObjectPtr*>(
|
|
reinterpret_cast<uword>(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<const uint8_t*>(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<const uint8_t*>(expected_features),
|
|
features_len + 1);
|
|
free(const_cast<char*>(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<Message> 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
|