<commit_before>// 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/exceptions.h"
#include "vm/heap.h"
#include "vm/lockers.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/snapshot_ids.h"
#include "vm/symbols.h"
#include "vm/version.h"

namespace dart {

static const int kNumInitialReferencesInFullSnapshot = 160 * KB;
static const int kNumInitialReferences = 64;


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 >= kNullCid && class_id <= kVoidCid));
}


static bool IsObjectStoreClassId(intptr_t class_id) {
  // Check if this is a class which is stored in the object store.
  return (class_id == kObjectCid ||
          (class_id >= kInstanceCid && class_id <= kUserTagCid) ||
          class_id == kArrayCid || class_id == kImmutableArrayCid ||
          RawObject::IsStringClassId(class_id) ||
          RawObject::IsTypedDataClassId(class_id) ||
          RawObject::IsExternalTypedDataClassId(class_id) ||
          class_id == kNullCid);
}


static bool IsObjectStoreTypeId(intptr_t index) {
  // Check if this is a type which is stored in the object store.
  return (index >= kObjectType && index <= kArrayType);
}


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 ||
         RawObject::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));
  ASSERT(!(RawObject::IsImplicitFieldClassId(class_id)));
  return (class_id + kClassIdsOffset);
}


static RawType* GetType(ObjectStore* object_store, intptr_t index) {
  switch (index) {
    case kObjectType: return object_store->object_type();
    case kNullType: return object_store->null_type();
    case kFunctionType: return object_store->function_type();
    case kNumberType: return object_store->number_type();
    case kSmiType: return object_store->smi_type();
    case kMintType: return object_store->mint_type();
    case kDoubleType: return object_store->double_type();
    case kIntType: return object_store->int_type();
    case kBoolType: return object_store->bool_type();
    case kStringType: return object_store->string_type();
    case kArrayType: return object_store->array_type();
    default: break;
  }
  UNREACHABLE();
  return Type::null();
}


static intptr_t GetTypeIndex(
    ObjectStore* object_store, const RawType* raw_type) {
  ASSERT(raw_type->IsHeapObject());
  if (raw_type == object_store->object_type()) {
    return kObjectType;
  } else if (raw_type == object_store->null_type()) {
    return kNullType;
  } else if (raw_type == object_store->function_type()) {
    return kFunctionType;
  } else if (raw_type == object_store->number_type()) {
    return kNumberType;
  } else if (raw_type == object_store->smi_type()) {
    return kSmiType;
  } else if (raw_type == object_store->mint_type()) {
    return kMintType;
  } else if (raw_type == object_store->double_type()) {
    return kDoubleType;
  } else if (raw_type == object_store->int_type()) {
    return kIntType;
  } else if (raw_type == object_store->bool_type()) {
    return kBoolType;
  } else if (raw_type == object_store->string_type()) {
    return kStringType;
  } else if (raw_type == object_store->array_type()) {
    return kArrayType;
  }
  return kInvalidIndex;
}


// TODO(5411462): Temporary setup of snapshot for testing purposes,
// the actual creation of a snapshot maybe done differently.
const Snapshot* Snapshot::SetupFromBuffer(const void* raw_memory) {
  ASSERT(raw_memory != NULL);
  ASSERT(kHeaderSize == sizeof(Snapshot));
  ASSERT(kLengthIndex == length_offset());
  ASSERT((kSnapshotFlagIndex * sizeof(int64_t)) == kind_offset());
  ASSERT((kHeapObjectTag & kInlined));
  // The kWatchedBit and kMarkBit are only set during GC operations. This
  // allows the two low bits in the header to be used for snapshotting.
  ASSERT(kObjectId ==
         ((1 << RawObject::kWatchedBit) | (1 << RawObject::kMarkBit)));
  ASSERT((kObjectAlignmentMask & kObjectId) == kObjectId);
  const Snapshot* snapshot = reinterpret_cast<const Snapshot*>(raw_memory);
  // If the raw length is negative or greater than what the local machine can
  // handle, then signal an error.
  int64_t snapshot_length = ReadUnaligned(&snapshot->unaligned_length_);
  if ((snapshot_length < 0) || (snapshot_length > kIntptrMax)) {
    return NULL;
  }
  return snapshot;
}


RawSmi* BaseReader::ReadAsSmi() {
  intptr_t value = Read<int32_t>();
  ASSERT((value & kSmiTagMask) == kSmiTag);
  return reinterpret_cast<RawSmi*>(value);
}


intptr_t BaseReader::ReadSmiValue() {
  return Smi::Value(ReadAsSmi());
}


SnapshotReader::SnapshotReader(const uint8_t* buffer,
                               intptr_t size,
                               Snapshot::Kind kind,
                               Isolate* isolate)
    : BaseReader(buffer, size),
      kind_(kind),
      isolate_(isolate),
      heap_(isolate->heap()),
      old_space_(isolate->heap()->old_space()),
      cls_(Class::Handle(isolate)),
      obj_(Object::Handle(isolate)),
      pobj_(PassiveObject::Handle(isolate)),
      array_(Array::Handle(isolate)),
      field_(Field::Handle(isolate)),
      str_(String::Handle(isolate)),
      library_(Library::Handle(isolate)),
      type_(AbstractType::Handle(isolate)),
      type_arguments_(TypeArguments::Handle(isolate)),
      tokens_(Array::Handle(isolate)),
      stream_(TokenStream::Handle(isolate)),
      data_(ExternalTypedData::Handle(isolate)),
      error_(UnhandledException::Handle(isolate)),
      backward_references_((kind == Snapshot::kFull) ?
                           kNumInitialReferencesInFullSnapshot :
                           kNumInitialReferences) {
}


RawObject* 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(isolate(), ReadObjectImpl());
    for (intptr_t i = 0; i < backward_references_.length(); i++) {
      if (!backward_references_[i].is_deserialized()) {
        ReadObjectImpl();
        backward_references_[i].set_state(kIsDeserialized);
      }
    }
    return obj.raw();
  } else {
    // An error occurred while reading, return the error object.
    const Error& err = Error::Handle(isolate()->object_store()->sticky_error());
    isolate()->object_store()->clear_sticky_error();
    return err.raw();
  }
}


RawClass* SnapshotReader::ReadClassId(intptr_t object_id) {
  ASSERT(kind_ != Snapshot::kFull);
  // 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) ||
         !IsObjectStoreClassId(SerializedHeaderData::decode(class_header)));
  Class& cls = Class::ZoneHandle(isolate(), Class::null());
  AddBackRef(object_id, &cls, kIsDeserialized);
  // Read the library/class information and lookup the class.
  str_ ^= ReadObjectImpl(class_header);
  library_ = Library::LookupLibrary(str_);
  ASSERT(!library_.IsNull());
  str_ ^= ReadObjectImpl();
  cls = library_.LookupClass(str_);
  cls.EnsureIsFinalized(isolate());
  ASSERT(!cls.IsNull());
  return cls.raw();
}


RawObject* SnapshotReader::ReadObjectImpl() {
  int64_t value = Read<int64_t>();
  if ((value & kSmiTagMask) == kSmiTag) {
    return NewInteger(value);
  }
  ASSERT((value <= kIntptrMax) && (value >= kIntptrMin));
  return ReadObjectImpl(static_cast<intptr_t>(value));
}


intptr_t SnapshotReader::NextAvailableObjectId() const {
  return backward_references_.length() + kMaxPredefinedObjectIds;
}


RawObject* SnapshotReader::ReadObjectImpl(intptr_t header_value) {
  if (IsVMIsolateObject(header_value)) {
    return ReadVMIsolateObject(header_value);
  } else {
    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();
    }
    return ReadInlinedObject(object_id);
  }
}


RawObject* SnapshotReader::ReadObjectRef() {
  int64_t header_value = Read<int64_t>();
  if ((header_value & kSmiTagMask) == kSmiTag) {
    return NewInteger(header_value);
  }
  ASSERT((header_value <= kIntptrMax) && (header_value >= kIntptrMin));
  intptr_t value = static_cast<intptr_t>(header_value);
  if (IsVMIsolateObject(value)) {
    return ReadVMIsolateObject(value);
  } else if (SerializedHeaderTag::decode(value) == kObjectId) {
    return ReadIndexedObject(SerializedHeaderData::decode(value));
  }
  ASSERT(SerializedHeaderTag::decode(value) == kInlined);
  intptr_t object_id = SerializedHeaderData::decode(value);
  if (object_id == kOmittedObjectId) {
    object_id = NextAvailableObjectId();
  }
  ASSERT(GetBackRef(object_id) == NULL);

  // Read the class header information and lookup the class.
  intptr_t class_header = Read<int32_t>();

  // Since we are only reading an object reference, If it is an instance kind
  // then we only need to figure out the class of the object and allocate an
  // instance of it. The individual fields will be read later.
  if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
    Instance& result = Instance::ZoneHandle(isolate(), Instance::null());
    AddBackRef(object_id, &result, kIsNotDeserialized);

    cls_ ^= ReadObjectImpl();  // Read class information.
    ASSERT(!cls_.IsNull());
    intptr_t instance_size = cls_.instance_size();
    ASSERT(instance_size > 0);
    if (kind_ == Snapshot::kFull) {
      result ^= AllocateUninitialized(cls_.id(), instance_size);
    } else {
      result ^= Object::Allocate(cls_.id(), instance_size, HEAP_SPACE(kind_));
    }
    return result.raw();
  }
  ASSERT((class_header & kSmiTagMask) != kSmiTag);
  cls_ = LookupInternalClass(class_header);
  ASSERT(!cls_.IsNull());

  // Similarly Array and ImmutableArray objects are also similarly only
  // allocated here, the individual array elements are read later.
  intptr_t class_id = cls_.id();
  if (class_id == kArrayCid) {
    // Read the length and allocate an object based on the len.
    intptr_t len = ReadSmiValue();
    Array& array = Array::ZoneHandle(
        isolate(),
        ((kind_ == Snapshot::kFull) ?
         NewArray(len) : Array::New(len, HEAP_SPACE(kind_))));
    AddBackRef(object_id, &array, kIsNotDeserialized);

    return array.raw();
  }
  if (class_id == kImmutableArrayCid) {
    // Read the length and allocate an object based on the len.
    intptr_t len = ReadSmiValue();
    Array& array = Array::ZoneHandle(
        isolate(),
        (kind_ == Snapshot::kFull) ?
        NewImmutableArray(len) : ImmutableArray::New(len, HEAP_SPACE(kind_)));
    AddBackRef(object_id, &array, kIsNotDeserialized);

    return array.raw();
  }

  // For all other internal VM classes we read the object inline.
  intptr_t tags = ReadTags();
  switch (class_id) {
#define SNAPSHOT_READ(clazz)                                                   \
    case clazz::kClassId: {                                                    \
      pobj_ = clazz::ReadFrom(this, object_id, tags, kind_);                   \
      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 = RawObject::ClassIdTag::update(class_id, tags);
      pobj_ = TypedData::ReadFrom(this, object_id, tags, kind_);
      break;
    }
#undef SNAPSHOT_READ
#define SNAPSHOT_READ(clazz)                                                   \
    case kExternalTypedData##clazz##Cid:                                       \

    CLASS_LIST_TYPED_DATA(SNAPSHOT_READ) {
      tags = RawObject::ClassIdTag::update(class_id, tags);
      pobj_ = ExternalTypedData::ReadFrom(this, object_id, tags, kind_);
      break;
    }
#undef SNAPSHOT_READ
    default: UNREACHABLE(); break;
  }
  if (kind_ == Snapshot::kFull) {
    pobj_.SetCreatedFromSnapshot();
  }
  return pobj_.raw();
}


void SnapshotReader::AddBackRef(intptr_t id,
                                Object* obj,
                                DeserializeState state) {
  intptr_t index = (id - kMaxPredefinedObjectIds);
  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);
  if (index < backward_references_.length()) {
    return backward_references_[index].reference();
  }
  return NULL;
}


class HeapLocker : public StackResource {
 public:
  HeapLocker(Isolate* isolate, PageSpace* page_space)
      : StackResource(isolate), page_space_(page_space) {
        page_space_->AcquireDataLock();
  }
  ~HeapLocker() {
    page_space_->ReleaseDataLock();
  }

 private:
  PageSpace* page_space_;
};


RawApiError* SnapshotReader::ReadFullSnapshot() {
  ASSERT(kind_ == Snapshot::kFull);
  Isolate* isolate = Isolate::Current();
  ASSERT(isolate != NULL);
  ObjectStore* object_store = isolate->object_store();
  ASSERT(object_store != NULL);

  // First read the version string, and check that it matches.
  RawApiError* error = VerifyVersion();
  if (error != ApiError::null()) {
    return error;
  }

  // The version string matches. Read the rest of the snapshot.

  // TODO(asiva): Add a check here to ensure we have the right heap
  // size for the full snapshot being read.
  {
    NoGCScope no_gc;
    HeapLocker hl(isolate, old_space());

    // Read in all the objects stored in the object store.
    intptr_t num_flds = (object_store->to() - object_store->from());
    for (intptr_t i = 0; i <= num_flds; i++) {
      *(object_store->from() + i) = ReadObjectImpl();
    }
    for (intptr_t i = 0; i < backward_references_.length(); i++) {
      if (!backward_references_[i].is_deserialized()) {
        ReadObjectImpl();
        backward_references_[i].set_state(kIsDeserialized);
      }
    }

    // Validate the class table.
#if defined(DEBUG)
    isolate->ValidateClassTable();
#endif

    // Setup native resolver for bootstrap impl.
    Bootstrap::SetupNativeResolver();
    return ApiError::null();
  }
}


RawObject* SnapshotReader::ReadScriptSnapshot() {
  ASSERT(kind_ == Snapshot::kScript);

  // First read the version string, and check that it matches.
  RawApiError* error = VerifyVersion();
  if (error != ApiError::null()) {
    return error;
  }

  // The version string matches. Read the rest of the snapshot.
  obj_ = ReadObject();
  if (!obj_.IsLibrary()) {
    if (!obj_.IsError()) {
      const intptr_t kMessageBufferSize = 128;
      char message_buffer[kMessageBufferSize];
      OS::SNPrint(message_buffer,
                  kMessageBufferSize,
                  "Invalid object %s found in script snapshot",
                  obj_.ToCString());
      const String& msg = String::Handle(String::New(message_buffer));
      obj_ = ApiError::New(msg);
    }
  }
  return obj_.raw();
}


RawApiError* SnapshotReader::VerifyVersion() {
  // 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];
    OS::SNPrint(message_buffer,
                kMessageBufferSize,
                "No full snapshot version found, expected '%s'",
                Version::SnapshotString());
    const String& msg = String::Handle(String::New(message_buffer));
    return ApiError::New(msg);
  }

  const char* version = reinterpret_cast<const char*>(CurrentBufferAddress());
  ASSERT(version != NULL);
  if (strncmp(version, expected_version, version_len)) {
    const intptr_t kMessageBufferSize = 256;
    char message_buffer[kMessageBufferSize];
    char* actual_version = OS::StrNDup(version, version_len);
    OS::SNPrint(message_buffer,
                kMessageBufferSize,
                "Wrong %s snapshot version, expected '%s' found '%s'",
                (kind_ == Snapshot::kFull) ? "full" : "script",
                Version::SnapshotString(),
                actual_version);
    free(actual_version);
    const String& msg = String::Handle(String::New(message_buffer));
    return ApiError::New(msg);
  }
  Advance(version_len);
  return ApiError::null();
}


#define ALLOC_NEW_OBJECT_WITH_LEN(type, length)                                \
  ASSERT(kind_ == Snapshot::kFull);                                            \
  ASSERT(isolate()->no_gc_scope_depth() != 0);                                 \
  Raw##type* obj = reinterpret_cast<Raw##type*>(                               \
      AllocateUninitialized(k##type##Cid, type::InstanceSize(length)));        \
  obj->ptr()->length_ = Smi::New(length);                                      \
  return obj;                                                                  \


RawArray* SnapshotReader::NewArray(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(Array, len);
}


RawImmutableArray* SnapshotReader::NewImmutableArray(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(ImmutableArray, len);
}


RawOneByteString* SnapshotReader::NewOneByteString(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(OneByteString, len);
}


RawTwoByteString* SnapshotReader::NewTwoByteString(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(TwoByteString, len);
}


RawTypeArguments* SnapshotReader::NewTypeArguments(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(TypeArguments, len);
}


RawTokenStream* SnapshotReader::NewTokenStream(intptr_t len) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  stream_ = reinterpret_cast<RawTokenStream*>(
      AllocateUninitialized(kTokenStreamCid, TokenStream::InstanceSize()));
  uint8_t* array = const_cast<uint8_t*>(CurrentBufferAddress());
  ASSERT(array != NULL);
  Advance(len);
  data_ = reinterpret_cast<RawExternalTypedData*>(
      AllocateUninitialized(kExternalTypedDataUint8ArrayCid,
                            ExternalTypedData::InstanceSize()));
  data_.SetData(array);
  data_.SetLength(len);
  stream_.SetStream(data_);
  return stream_.raw();
}


RawContext* SnapshotReader::NewContext(intptr_t num_variables) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawContext* obj = reinterpret_cast<RawContext*>(
      AllocateUninitialized(kContextCid, Context::InstanceSize(num_variables)));
  obj->ptr()->num_variables_ = num_variables;
  return obj;
}


RawClass* SnapshotReader::NewClass(intptr_t class_id) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  if (class_id < kNumPredefinedCids) {
    ASSERT((class_id >= kInstanceCid) &&
           (class_id <= kNullCid));
    return isolate()->class_table()->At(class_id);
  }
  RawClass* obj = reinterpret_cast<RawClass*>(
      AllocateUninitialized(kClassCid, Class::InstanceSize()));
  Instance fake;
  obj->ptr()->handle_vtable_ = fake.vtable();
  cls_ = obj;
  cls_.set_id(class_id);
  isolate()->RegisterClassAt(class_id, cls_);
  return cls_.raw();
}


RawInstance* SnapshotReader::NewInstance() {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawInstance* obj = reinterpret_cast<RawInstance*>(
      AllocateUninitialized(kObjectCid, Instance::InstanceSize()));
  return obj;
}


RawMint* SnapshotReader::NewMint(int64_t value) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawMint* obj = reinterpret_cast<RawMint*>(
      AllocateUninitialized(kMintCid, Mint::InstanceSize()));
  obj->ptr()->value_ = value;
  return obj;
}


RawDouble* SnapshotReader::NewDouble(double value) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawDouble* obj = reinterpret_cast<RawDouble*>(
      AllocateUninitialized(kDoubleCid, Double::InstanceSize()));
  obj->ptr()->value_ = value;
  return obj;
}


RawTypedData* SnapshotReader::NewTypedData(intptr_t class_id, intptr_t len) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  const intptr_t lengthInBytes = len * TypedData::ElementSizeInBytes(class_id);
  RawTypedData* obj = reinterpret_cast<RawTypedData*>(
      AllocateUninitialized(class_id, TypedData::InstanceSize(lengthInBytes)));
  obj->ptr()->length_ = Smi::New(len);
  return obj;
}


#define ALLOC_NEW_OBJECT(type)                                                 \
  ASSERT(kind_ == Snapshot::kFull);                                            \
  ASSERT(isolate()->no_gc_scope_depth() != 0);                                 \
  return reinterpret_cast<Raw##type*>(                                         \
      AllocateUninitialized(k##type##Cid, type::InstanceSize()));              \


RawBigint* SnapshotReader::NewBigint() {
  ALLOC_NEW_OBJECT(Bigint);
}


RawUnresolvedClass* SnapshotReader::NewUnresolvedClass() {
  ALLOC_NEW_OBJECT(UnresolvedClass);
}


RawType* SnapshotReader::NewType() {
  ALLOC_NEW_OBJECT(Type);
}


RawTypeRef* SnapshotReader::NewTypeRef() {
  ALLOC_NEW_OBJECT(TypeRef);
}


RawTypeParameter* SnapshotReader::NewTypeParameter() {
  ALLOC_NEW_OBJECT(TypeParameter);
}


RawBoundedType* SnapshotReader::NewBoundedType() {
  ALLOC_NEW_OBJECT(BoundedType);
}


RawMixinAppType* SnapshotReader::NewMixinAppType() {
  ALLOC_NEW_OBJECT(MixinAppType);
}


RawPatchClass* SnapshotReader::NewPatchClass() {
  ALLOC_NEW_OBJECT(PatchClass);
}


RawClosureData* SnapshotReader::NewClosureData() {
  ALLOC_NEW_OBJECT(ClosureData);
}


RawRedirectionData* SnapshotReader::NewRedirectionData() {
  ALLOC_NEW_OBJECT(RedirectionData);
}


RawFunction* SnapshotReader::NewFunction() {
  ALLOC_NEW_OBJECT(Function);
}


RawField* SnapshotReader::NewField() {
  ALLOC_NEW_OBJECT(Field);
}


RawLibrary* SnapshotReader::NewLibrary() {
  ALLOC_NEW_OBJECT(Library);
}


RawLibraryPrefix* SnapshotReader::NewLibraryPrefix() {
  ALLOC_NEW_OBJECT(LibraryPrefix);
}


RawNamespace* SnapshotReader::NewNamespace() {
  ALLOC_NEW_OBJECT(Namespace);
}


RawScript* SnapshotReader::NewScript() {
  ALLOC_NEW_OBJECT(Script);
}


RawLiteralToken* SnapshotReader::NewLiteralToken() {
  ALLOC_NEW_OBJECT(LiteralToken);
}


RawGrowableObjectArray* SnapshotReader::NewGrowableObjectArray() {
  ALLOC_NEW_OBJECT(GrowableObjectArray);
}


RawFloat32x4* SnapshotReader::NewFloat32x4(float v0, float v1, float v2,
                                           float v3) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawFloat32x4* obj = reinterpret_cast<RawFloat32x4*>(
      AllocateUninitialized(kFloat32x4Cid, Float32x4::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  obj->ptr()->value_[2] = v2;
  obj->ptr()->value_[3] = v3;
  return obj;
}


RawInt32x4* SnapshotReader::NewInt32x4(uint32_t v0, uint32_t v1, uint32_t v2,
                                       uint32_t v3) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawInt32x4* obj = reinterpret_cast<RawInt32x4*>(
      AllocateUninitialized(kInt32x4Cid, Int32x4::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  obj->ptr()->value_[2] = v2;
  obj->ptr()->value_[3] = v3;
  return obj;
}


RawFloat64x2* SnapshotReader::NewFloat64x2(double v0, double v1) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawFloat64x2* obj = reinterpret_cast<RawFloat64x2*>(
      AllocateUninitialized(kFloat64x2Cid, Float64x2::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  return obj;
}


RawApiError* SnapshotReader::NewApiError() {
  ALLOC_NEW_OBJECT(ApiError);
}


RawLanguageError* SnapshotReader::NewLanguageError() {
  ALLOC_NEW_OBJECT(LanguageError);
}


RawUnhandledException* SnapshotReader::NewUnhandledException() {
  ALLOC_NEW_OBJECT(UnhandledException);
}


RawObject* SnapshotReader::NewInteger(int64_t value) {
  ASSERT((value & kSmiTagMask) == kSmiTag);
  value = value >> kSmiTagShift;
  if (Smi::IsValid(value)) {
    return Smi::New(static_cast<intptr_t>(value));
  }
  if (kind_ == Snapshot::kFull) {
    return NewMint(value);
  }
  return Mint::NewCanonical(value);
}


RawStacktrace* SnapshotReader::NewStacktrace() {
  ALLOC_NEW_OBJECT(Stacktrace);
}


RawClass* 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);
    if (IsSingletonClassId(class_id)) {
      return isolate()->class_table()->At(class_id);  // get singleton class.
    }
  } else if (SerializedHeaderTag::decode(class_header) == kObjectId) {
    intptr_t class_id = SerializedHeaderData::decode(class_header);
    if (IsObjectStoreClassId(class_id)) {
      return isolate()->class_table()->At(class_id);  // get singleton class.
    }
  }
  return Class::null();
}


RawObject* SnapshotReader::AllocateUninitialized(intptr_t class_id,
                                                 intptr_t size) {
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  ASSERT(Utils::IsAligned(size, kObjectAlignment));

  uword address = old_space()->TryAllocateDataLocked(size,
                                                     PageSpace::kForceGrowth);
  if (address == 0) {
    // Use the preallocated out of memory exception to avoid calling
    // into dart code or allocating any code.
    // We do a longjmp at this point to unwind out of the entire
    // read part and return the error object back.
    const UnhandledException& error = UnhandledException::Handle(
        object_store()->preallocated_unhandled_exception());
    Isolate::Current()->long_jump_base()->Jump(1, error);
  }
#if defined(DEBUG)
  // Zap the uninitialized memory area.
  uword current = address;
  uword end = address + size;
  while (current < end) {
    *reinterpret_cast<intptr_t*>(current) = kZapUninitializedWord;
    current += kWordSize;
  }<commit_msg>Bump-pointer allocation when reading snapshot; 10-15% faster isolate creation.<commit_after>// 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/exceptions.h"
#include "vm/heap.h"
#include "vm/lockers.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/snapshot_ids.h"
#include "vm/symbols.h"
#include "vm/version.h"

namespace dart {

static const int kNumInitialReferencesInFullSnapshot = 160 * KB;
static const int kNumInitialReferences = 64;


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 >= kNullCid && class_id <= kVoidCid));
}


static bool IsObjectStoreClassId(intptr_t class_id) {
  // Check if this is a class which is stored in the object store.
  return (class_id == kObjectCid ||
          (class_id >= kInstanceCid && class_id <= kUserTagCid) ||
          class_id == kArrayCid || class_id == kImmutableArrayCid ||
          RawObject::IsStringClassId(class_id) ||
          RawObject::IsTypedDataClassId(class_id) ||
          RawObject::IsExternalTypedDataClassId(class_id) ||
          class_id == kNullCid);
}


static bool IsObjectStoreTypeId(intptr_t index) {
  // Check if this is a type which is stored in the object store.
  return (index >= kObjectType && index <= kArrayType);
}


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 ||
         RawObject::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));
  ASSERT(!(RawObject::IsImplicitFieldClassId(class_id)));
  return (class_id + kClassIdsOffset);
}


static RawType* GetType(ObjectStore* object_store, intptr_t index) {
  switch (index) {
    case kObjectType: return object_store->object_type();
    case kNullType: return object_store->null_type();
    case kFunctionType: return object_store->function_type();
    case kNumberType: return object_store->number_type();
    case kSmiType: return object_store->smi_type();
    case kMintType: return object_store->mint_type();
    case kDoubleType: return object_store->double_type();
    case kIntType: return object_store->int_type();
    case kBoolType: return object_store->bool_type();
    case kStringType: return object_store->string_type();
    case kArrayType: return object_store->array_type();
    default: break;
  }
  UNREACHABLE();
  return Type::null();
}


static intptr_t GetTypeIndex(
    ObjectStore* object_store, const RawType* raw_type) {
  ASSERT(raw_type->IsHeapObject());
  if (raw_type == object_store->object_type()) {
    return kObjectType;
  } else if (raw_type == object_store->null_type()) {
    return kNullType;
  } else if (raw_type == object_store->function_type()) {
    return kFunctionType;
  } else if (raw_type == object_store->number_type()) {
    return kNumberType;
  } else if (raw_type == object_store->smi_type()) {
    return kSmiType;
  } else if (raw_type == object_store->mint_type()) {
    return kMintType;
  } else if (raw_type == object_store->double_type()) {
    return kDoubleType;
  } else if (raw_type == object_store->int_type()) {
    return kIntType;
  } else if (raw_type == object_store->bool_type()) {
    return kBoolType;
  } else if (raw_type == object_store->string_type()) {
    return kStringType;
  } else if (raw_type == object_store->array_type()) {
    return kArrayType;
  }
  return kInvalidIndex;
}


// TODO(5411462): Temporary setup of snapshot for testing purposes,
// the actual creation of a snapshot maybe done differently.
const Snapshot* Snapshot::SetupFromBuffer(const void* raw_memory) {
  ASSERT(raw_memory != NULL);
  ASSERT(kHeaderSize == sizeof(Snapshot));
  ASSERT(kLengthIndex == length_offset());
  ASSERT((kSnapshotFlagIndex * sizeof(int64_t)) == kind_offset());
  ASSERT((kHeapObjectTag & kInlined));
  // The kWatchedBit and kMarkBit are only set during GC operations. This
  // allows the two low bits in the header to be used for snapshotting.
  ASSERT(kObjectId ==
         ((1 << RawObject::kWatchedBit) | (1 << RawObject::kMarkBit)));
  ASSERT((kObjectAlignmentMask & kObjectId) == kObjectId);
  const Snapshot* snapshot = reinterpret_cast<const Snapshot*>(raw_memory);
  // If the raw length is negative or greater than what the local machine can
  // handle, then signal an error.
  int64_t snapshot_length = ReadUnaligned(&snapshot->unaligned_length_);
  if ((snapshot_length < 0) || (snapshot_length > kIntptrMax)) {
    return NULL;
  }
  return snapshot;
}


RawSmi* BaseReader::ReadAsSmi() {
  intptr_t value = Read<int32_t>();
  ASSERT((value & kSmiTagMask) == kSmiTag);
  return reinterpret_cast<RawSmi*>(value);
}


intptr_t BaseReader::ReadSmiValue() {
  return Smi::Value(ReadAsSmi());
}


SnapshotReader::SnapshotReader(const uint8_t* buffer,
                               intptr_t size,
                               Snapshot::Kind kind,
                               Isolate* isolate)
    : BaseReader(buffer, size),
      kind_(kind),
      isolate_(isolate),
      heap_(isolate->heap()),
      old_space_(isolate->heap()->old_space()),
      cls_(Class::Handle(isolate)),
      obj_(Object::Handle(isolate)),
      pobj_(PassiveObject::Handle(isolate)),
      array_(Array::Handle(isolate)),
      field_(Field::Handle(isolate)),
      str_(String::Handle(isolate)),
      library_(Library::Handle(isolate)),
      type_(AbstractType::Handle(isolate)),
      type_arguments_(TypeArguments::Handle(isolate)),
      tokens_(Array::Handle(isolate)),
      stream_(TokenStream::Handle(isolate)),
      data_(ExternalTypedData::Handle(isolate)),
      error_(UnhandledException::Handle(isolate)),
      backward_references_((kind == Snapshot::kFull) ?
                           kNumInitialReferencesInFullSnapshot :
                           kNumInitialReferences) {
}


RawObject* 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(isolate(), ReadObjectImpl());
    for (intptr_t i = 0; i < backward_references_.length(); i++) {
      if (!backward_references_[i].is_deserialized()) {
        ReadObjectImpl();
        backward_references_[i].set_state(kIsDeserialized);
      }
    }
    return obj.raw();
  } else {
    // An error occurred while reading, return the error object.
    const Error& err = Error::Handle(isolate()->object_store()->sticky_error());
    isolate()->object_store()->clear_sticky_error();
    return err.raw();
  }
}


RawClass* SnapshotReader::ReadClassId(intptr_t object_id) {
  ASSERT(kind_ != Snapshot::kFull);
  // 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) ||
         !IsObjectStoreClassId(SerializedHeaderData::decode(class_header)));
  Class& cls = Class::ZoneHandle(isolate(), Class::null());
  AddBackRef(object_id, &cls, kIsDeserialized);
  // Read the library/class information and lookup the class.
  str_ ^= ReadObjectImpl(class_header);
  library_ = Library::LookupLibrary(str_);
  ASSERT(!library_.IsNull());
  str_ ^= ReadObjectImpl();
  cls = library_.LookupClass(str_);
  cls.EnsureIsFinalized(isolate());
  ASSERT(!cls.IsNull());
  return cls.raw();
}


RawObject* SnapshotReader::ReadObjectImpl() {
  int64_t value = Read<int64_t>();
  if ((value & kSmiTagMask) == kSmiTag) {
    return NewInteger(value);
  }
  ASSERT((value <= kIntptrMax) && (value >= kIntptrMin));
  return ReadObjectImpl(static_cast<intptr_t>(value));
}


intptr_t SnapshotReader::NextAvailableObjectId() const {
  return backward_references_.length() + kMaxPredefinedObjectIds;
}


RawObject* SnapshotReader::ReadObjectImpl(intptr_t header_value) {
  if (IsVMIsolateObject(header_value)) {
    return ReadVMIsolateObject(header_value);
  } else {
    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();
    }
    return ReadInlinedObject(object_id);
  }
}


RawObject* SnapshotReader::ReadObjectRef() {
  int64_t header_value = Read<int64_t>();
  if ((header_value & kSmiTagMask) == kSmiTag) {
    return NewInteger(header_value);
  }
  ASSERT((header_value <= kIntptrMax) && (header_value >= kIntptrMin));
  intptr_t value = static_cast<intptr_t>(header_value);
  if (IsVMIsolateObject(value)) {
    return ReadVMIsolateObject(value);
  } else if (SerializedHeaderTag::decode(value) == kObjectId) {
    return ReadIndexedObject(SerializedHeaderData::decode(value));
  }
  ASSERT(SerializedHeaderTag::decode(value) == kInlined);
  intptr_t object_id = SerializedHeaderData::decode(value);
  if (object_id == kOmittedObjectId) {
    object_id = NextAvailableObjectId();
  }
  ASSERT(GetBackRef(object_id) == NULL);

  // Read the class header information and lookup the class.
  intptr_t class_header = Read<int32_t>();

  // Since we are only reading an object reference, If it is an instance kind
  // then we only need to figure out the class of the object and allocate an
  // instance of it. The individual fields will be read later.
  if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
    Instance& result = Instance::ZoneHandle(isolate(), Instance::null());
    AddBackRef(object_id, &result, kIsNotDeserialized);

    cls_ ^= ReadObjectImpl();  // Read class information.
    ASSERT(!cls_.IsNull());
    intptr_t instance_size = cls_.instance_size();
    ASSERT(instance_size > 0);
    if (kind_ == Snapshot::kFull) {
      result ^= AllocateUninitialized(cls_.id(), instance_size);
    } else {
      result ^= Object::Allocate(cls_.id(), instance_size, HEAP_SPACE(kind_));
    }
    return result.raw();
  }
  ASSERT((class_header & kSmiTagMask) != kSmiTag);
  cls_ = LookupInternalClass(class_header);
  ASSERT(!cls_.IsNull());

  // Similarly Array and ImmutableArray objects are also similarly only
  // allocated here, the individual array elements are read later.
  intptr_t class_id = cls_.id();
  if (class_id == kArrayCid) {
    // Read the length and allocate an object based on the len.
    intptr_t len = ReadSmiValue();
    Array& array = Array::ZoneHandle(
        isolate(),
        ((kind_ == Snapshot::kFull) ?
         NewArray(len) : Array::New(len, HEAP_SPACE(kind_))));
    AddBackRef(object_id, &array, kIsNotDeserialized);

    return array.raw();
  }
  if (class_id == kImmutableArrayCid) {
    // Read the length and allocate an object based on the len.
    intptr_t len = ReadSmiValue();
    Array& array = Array::ZoneHandle(
        isolate(),
        (kind_ == Snapshot::kFull) ?
        NewImmutableArray(len) : ImmutableArray::New(len, HEAP_SPACE(kind_)));
    AddBackRef(object_id, &array, kIsNotDeserialized);

    return array.raw();
  }

  // For all other internal VM classes we read the object inline.
  intptr_t tags = ReadTags();
  switch (class_id) {
#define SNAPSHOT_READ(clazz)                                                   \
    case clazz::kClassId: {                                                    \
      pobj_ = clazz::ReadFrom(this, object_id, tags, kind_);                   \
      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 = RawObject::ClassIdTag::update(class_id, tags);
      pobj_ = TypedData::ReadFrom(this, object_id, tags, kind_);
      break;
    }
#undef SNAPSHOT_READ
#define SNAPSHOT_READ(clazz)                                                   \
    case kExternalTypedData##clazz##Cid:                                       \

    CLASS_LIST_TYPED_DATA(SNAPSHOT_READ) {
      tags = RawObject::ClassIdTag::update(class_id, tags);
      pobj_ = ExternalTypedData::ReadFrom(this, object_id, tags, kind_);
      break;
    }
#undef SNAPSHOT_READ
    default: UNREACHABLE(); break;
  }
  if (kind_ == Snapshot::kFull) {
    pobj_.SetCreatedFromSnapshot();
  }
  return pobj_.raw();
}


void SnapshotReader::AddBackRef(intptr_t id,
                                Object* obj,
                                DeserializeState state) {
  intptr_t index = (id - kMaxPredefinedObjectIds);
  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);
  if (index < backward_references_.length()) {
    return backward_references_[index].reference();
  }
  return NULL;
}


class HeapLocker : public StackResource {
 public:
  HeapLocker(Isolate* isolate, PageSpace* page_space)
      : StackResource(isolate), page_space_(page_space) {
        page_space_->AcquireDataLock();
  }
  ~HeapLocker() {
    page_space_->ReleaseDataLock();
  }

 private:
  PageSpace* page_space_;
};


RawApiError* SnapshotReader::ReadFullSnapshot() {
  ASSERT(kind_ == Snapshot::kFull);
  Isolate* isolate = Isolate::Current();
  ASSERT(isolate != NULL);
  ObjectStore* object_store = isolate->object_store();
  ASSERT(object_store != NULL);

  // First read the version string, and check that it matches.
  RawApiError* error = VerifyVersion();
  if (error != ApiError::null()) {
    return error;
  }

  // The version string matches. Read the rest of the snapshot.

  // TODO(asiva): Add a check here to ensure we have the right heap
  // size for the full snapshot being read.
  {
    NoGCScope no_gc;
    HeapLocker hl(isolate, old_space());

    // Read in all the objects stored in the object store.
    intptr_t num_flds = (object_store->to() - object_store->from());
    for (intptr_t i = 0; i <= num_flds; i++) {
      *(object_store->from() + i) = ReadObjectImpl();
    }
    for (intptr_t i = 0; i < backward_references_.length(); i++) {
      if (!backward_references_[i].is_deserialized()) {
        ReadObjectImpl();
        backward_references_[i].set_state(kIsDeserialized);
      }
    }

    // Validate the class table.
#if defined(DEBUG)
    isolate->ValidateClassTable();
#endif

    // Setup native resolver for bootstrap impl.
    Bootstrap::SetupNativeResolver();
    return ApiError::null();
  }
}


RawObject* SnapshotReader::ReadScriptSnapshot() {
  ASSERT(kind_ == Snapshot::kScript);

  // First read the version string, and check that it matches.
  RawApiError* error = VerifyVersion();
  if (error != ApiError::null()) {
    return error;
  }

  // The version string matches. Read the rest of the snapshot.
  obj_ = ReadObject();
  if (!obj_.IsLibrary()) {
    if (!obj_.IsError()) {
      const intptr_t kMessageBufferSize = 128;
      char message_buffer[kMessageBufferSize];
      OS::SNPrint(message_buffer,
                  kMessageBufferSize,
                  "Invalid object %s found in script snapshot",
                  obj_.ToCString());
      const String& msg = String::Handle(String::New(message_buffer));
      obj_ = ApiError::New(msg);
    }
  }
  return obj_.raw();
}


RawApiError* SnapshotReader::VerifyVersion() {
  // 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];
    OS::SNPrint(message_buffer,
                kMessageBufferSize,
                "No full snapshot version found, expected '%s'",
                Version::SnapshotString());
    const String& msg = String::Handle(String::New(message_buffer));
    return ApiError::New(msg);
  }

  const char* version = reinterpret_cast<const char*>(CurrentBufferAddress());
  ASSERT(version != NULL);
  if (strncmp(version, expected_version, version_len)) {
    const intptr_t kMessageBufferSize = 256;
    char message_buffer[kMessageBufferSize];
    char* actual_version = OS::StrNDup(version, version_len);
    OS::SNPrint(message_buffer,
                kMessageBufferSize,
                "Wrong %s snapshot version, expected '%s' found '%s'",
                (kind_ == Snapshot::kFull) ? "full" : "script",
                Version::SnapshotString(),
                actual_version);
    free(actual_version);
    const String& msg = String::Handle(String::New(message_buffer));
    return ApiError::New(msg);
  }
  Advance(version_len);
  return ApiError::null();
}


#define ALLOC_NEW_OBJECT_WITH_LEN(type, length)                                \
  ASSERT(kind_ == Snapshot::kFull);                                            \
  ASSERT(isolate()->no_gc_scope_depth() != 0);                                 \
  Raw##type* obj = reinterpret_cast<Raw##type*>(                               \
      AllocateUninitialized(k##type##Cid, type::InstanceSize(length)));        \
  obj->ptr()->length_ = Smi::New(length);                                      \
  return obj;                                                                  \


RawArray* SnapshotReader::NewArray(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(Array, len);
}


RawImmutableArray* SnapshotReader::NewImmutableArray(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(ImmutableArray, len);
}


RawOneByteString* SnapshotReader::NewOneByteString(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(OneByteString, len);
}


RawTwoByteString* SnapshotReader::NewTwoByteString(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(TwoByteString, len);
}


RawTypeArguments* SnapshotReader::NewTypeArguments(intptr_t len) {
  ALLOC_NEW_OBJECT_WITH_LEN(TypeArguments, len);
}


RawTokenStream* SnapshotReader::NewTokenStream(intptr_t len) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  stream_ = reinterpret_cast<RawTokenStream*>(
      AllocateUninitialized(kTokenStreamCid, TokenStream::InstanceSize()));
  uint8_t* array = const_cast<uint8_t*>(CurrentBufferAddress());
  ASSERT(array != NULL);
  Advance(len);
  data_ = reinterpret_cast<RawExternalTypedData*>(
      AllocateUninitialized(kExternalTypedDataUint8ArrayCid,
                            ExternalTypedData::InstanceSize()));
  data_.SetData(array);
  data_.SetLength(len);
  stream_.SetStream(data_);
  return stream_.raw();
}


RawContext* SnapshotReader::NewContext(intptr_t num_variables) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawContext* obj = reinterpret_cast<RawContext*>(
      AllocateUninitialized(kContextCid, Context::InstanceSize(num_variables)));
  obj->ptr()->num_variables_ = num_variables;
  return obj;
}


RawClass* SnapshotReader::NewClass(intptr_t class_id) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  if (class_id < kNumPredefinedCids) {
    ASSERT((class_id >= kInstanceCid) &&
           (class_id <= kNullCid));
    return isolate()->class_table()->At(class_id);
  }
  RawClass* obj = reinterpret_cast<RawClass*>(
      AllocateUninitialized(kClassCid, Class::InstanceSize()));
  Instance fake;
  obj->ptr()->handle_vtable_ = fake.vtable();
  cls_ = obj;
  cls_.set_id(class_id);
  isolate()->RegisterClassAt(class_id, cls_);
  return cls_.raw();
}


RawInstance* SnapshotReader::NewInstance() {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawInstance* obj = reinterpret_cast<RawInstance*>(
      AllocateUninitialized(kObjectCid, Instance::InstanceSize()));
  return obj;
}


RawMint* SnapshotReader::NewMint(int64_t value) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawMint* obj = reinterpret_cast<RawMint*>(
      AllocateUninitialized(kMintCid, Mint::InstanceSize()));
  obj->ptr()->value_ = value;
  return obj;
}


RawDouble* SnapshotReader::NewDouble(double value) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawDouble* obj = reinterpret_cast<RawDouble*>(
      AllocateUninitialized(kDoubleCid, Double::InstanceSize()));
  obj->ptr()->value_ = value;
  return obj;
}


RawTypedData* SnapshotReader::NewTypedData(intptr_t class_id, intptr_t len) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  const intptr_t lengthInBytes = len * TypedData::ElementSizeInBytes(class_id);
  RawTypedData* obj = reinterpret_cast<RawTypedData*>(
      AllocateUninitialized(class_id, TypedData::InstanceSize(lengthInBytes)));
  obj->ptr()->length_ = Smi::New(len);
  return obj;
}


#define ALLOC_NEW_OBJECT(type)                                                 \
  ASSERT(kind_ == Snapshot::kFull);                                            \
  ASSERT(isolate()->no_gc_scope_depth() != 0);                                 \
  return reinterpret_cast<Raw##type*>(                                         \
      AllocateUninitialized(k##type##Cid, type::InstanceSize()));              \


RawBigint* SnapshotReader::NewBigint() {
  ALLOC_NEW_OBJECT(Bigint);
}


RawUnresolvedClass* SnapshotReader::NewUnresolvedClass() {
  ALLOC_NEW_OBJECT(UnresolvedClass);
}


RawType* SnapshotReader::NewType() {
  ALLOC_NEW_OBJECT(Type);
}


RawTypeRef* SnapshotReader::NewTypeRef() {
  ALLOC_NEW_OBJECT(TypeRef);
}


RawTypeParameter* SnapshotReader::NewTypeParameter() {
  ALLOC_NEW_OBJECT(TypeParameter);
}


RawBoundedType* SnapshotReader::NewBoundedType() {
  ALLOC_NEW_OBJECT(BoundedType);
}


RawMixinAppType* SnapshotReader::NewMixinAppType() {
  ALLOC_NEW_OBJECT(MixinAppType);
}


RawPatchClass* SnapshotReader::NewPatchClass() {
  ALLOC_NEW_OBJECT(PatchClass);
}


RawClosureData* SnapshotReader::NewClosureData() {
  ALLOC_NEW_OBJECT(ClosureData);
}


RawRedirectionData* SnapshotReader::NewRedirectionData() {
  ALLOC_NEW_OBJECT(RedirectionData);
}


RawFunction* SnapshotReader::NewFunction() {
  ALLOC_NEW_OBJECT(Function);
}


RawField* SnapshotReader::NewField() {
  ALLOC_NEW_OBJECT(Field);
}


RawLibrary* SnapshotReader::NewLibrary() {
  ALLOC_NEW_OBJECT(Library);
}


RawLibraryPrefix* SnapshotReader::NewLibraryPrefix() {
  ALLOC_NEW_OBJECT(LibraryPrefix);
}


RawNamespace* SnapshotReader::NewNamespace() {
  ALLOC_NEW_OBJECT(Namespace);
}


RawScript* SnapshotReader::NewScript() {
  ALLOC_NEW_OBJECT(Script);
}


RawLiteralToken* SnapshotReader::NewLiteralToken() {
  ALLOC_NEW_OBJECT(LiteralToken);
}


RawGrowableObjectArray* SnapshotReader::NewGrowableObjectArray() {
  ALLOC_NEW_OBJECT(GrowableObjectArray);
}


RawFloat32x4* SnapshotReader::NewFloat32x4(float v0, float v1, float v2,
                                           float v3) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawFloat32x4* obj = reinterpret_cast<RawFloat32x4*>(
      AllocateUninitialized(kFloat32x4Cid, Float32x4::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  obj->ptr()->value_[2] = v2;
  obj->ptr()->value_[3] = v3;
  return obj;
}


RawInt32x4* SnapshotReader::NewInt32x4(uint32_t v0, uint32_t v1, uint32_t v2,
                                       uint32_t v3) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawInt32x4* obj = reinterpret_cast<RawInt32x4*>(
      AllocateUninitialized(kInt32x4Cid, Int32x4::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  obj->ptr()->value_[2] = v2;
  obj->ptr()->value_[3] = v3;
  return obj;
}


RawFloat64x2* SnapshotReader::NewFloat64x2(double v0, double v1) {
  ASSERT(kind_ == Snapshot::kFull);
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  RawFloat64x2* obj = reinterpret_cast<RawFloat64x2*>(
      AllocateUninitialized(kFloat64x2Cid, Float64x2::InstanceSize()));
  obj->ptr()->value_[0] = v0;
  obj->ptr()->value_[1] = v1;
  return obj;
}


RawApiError* SnapshotReader::NewApiError() {
  ALLOC_NEW_OBJECT(ApiError);
}


RawLanguageError* SnapshotReader::NewLanguageError() {
  ALLOC_NEW_OBJECT(LanguageError);
}


RawUnhandledException* SnapshotReader::NewUnhandledException() {
  ALLOC_NEW_OBJECT(UnhandledException);
}


RawObject* SnapshotReader::NewInteger(int64_t value) {
  ASSERT((value & kSmiTagMask) == kSmiTag);
  value = value >> kSmiTagShift;
  if (Smi::IsValid(value)) {
    return Smi::New(static_cast<intptr_t>(value));
  }
  if (kind_ == Snapshot::kFull) {
    return NewMint(value);
  }
  return Mint::NewCanonical(value);
}


RawStacktrace* SnapshotReader::NewStacktrace() {
  ALLOC_NEW_OBJECT(Stacktrace);
}


RawClass* 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);
    if (IsSingletonClassId(class_id)) {
      return isolate()->class_table()->At(class_id);  // get singleton class.
    }
  } else if (SerializedHeaderTag::decode(class_header) == kObjectId) {
    intptr_t class_id = SerializedHeaderData::decode(class_header);
    if (IsObjectStoreClassId(class_id)) {
      return isolate()->class_table()->At(class_id);  // get singleton class.
    }
  }
  return Class::null();
}


RawObject* SnapshotReader::AllocateUninitialized(intptr_t class_id,
                                                 intptr_t size) {
  ASSERT(isolate()->no_gc_scope_depth() != 0);
  ASSERT(Utils::IsAligned(size, kObjectAlignment));

  uword address =
      old_space()->TryAllocateDataBumpLocked(size, PageSpace::kForceGrowth);
  if (address == 0) {
    // Use the preallocated out of memory exception to avoid calling
    // into dart code or allocating any code.
    // We do a longjmp at this point to unwind out of the entire
    // read part and return the error object back.
    const UnhandledException& error = UnhandledException::Handle(
        object_store()->preallocated_unhandled_exception());
    Isolate::Current()->long_jump_base()->Jump(1, error);
  }
#if defined(DEBUG)
  // Zap the uninitialized memory area.
  uword current = address;
  uword end = address + size;
  while (current < end) {
    *reinterpret_cast<intptr_t*>(current) = kZapUninitializedWord;
    current += kWordSize;
  }
{'fraction_non_alphanumeric': 0.1247701460248783, 'fraction_numerical': 0.006057328285559762, 'mean_word_length': 3.709313184480856, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 273, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 62, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3270156', 'n_tokens_mistral': 17719, 'n_tokens_neox': 17395, 'n_words': 3532}