serialize.h 21 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef V8_SERIALIZE_H_
#define V8_SERIALIZE_H_

#include "hashmap.h"

33 34
namespace v8 {
namespace internal {
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

// A TypeCode is used to distinguish different kinds of external reference.
// It is a single bit to make testing for types easy.
enum TypeCode {
  UNCLASSIFIED,        // One-of-a-kind references.
  BUILTIN,
  RUNTIME_FUNCTION,
  IC_UTILITY,
  DEBUG_ADDRESS,
  STATS_COUNTER,
  TOP_ADDRESS,
  C_BUILTIN,
  EXTENSION,
  ACCESSOR,
  RUNTIME_ENTRY,
50 51
  STUB_CACHE_TABLE,
  LAZY_DEOPTIMIZATION
52 53
};

54
const int kTypeCodeCount = LAZY_DEOPTIMIZATION + 1;
55 56 57 58 59 60 61 62
const int kFirstTypeCode = UNCLASSIFIED;

const int kReferenceIdBits = 16;
const int kReferenceIdMask = (1 << kReferenceIdBits) - 1;
const int kReferenceTypeShift = kReferenceIdBits;
const int kDebugRegisterBits = 4;
const int kDebugIdShift = kDebugRegisterBits;

63
const int kDeoptTableSerializeEntryCount = 8;
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
// ExternalReferenceTable is a helper class that defines the relationship
// between external references and their encodings. It is used to build
// hashmaps in ExternalReferenceEncoder and ExternalReferenceDecoder.
class ExternalReferenceTable {
 public:
  static ExternalReferenceTable* instance(Isolate* isolate);

  ~ExternalReferenceTable() { }

  int size() const { return refs_.length(); }

  Address address(int i) { return refs_[i].address; }

  uint32_t code(int i) { return refs_[i].code; }

  const char* name(int i) { return refs_[i].name; }

  int max_id(int code) { return max_id_[code]; }

 private:
  explicit ExternalReferenceTable(Isolate* isolate) : refs_(64) {
      PopulateTable(isolate);
  }

  struct ExternalReferenceEntry {
    Address address;
    uint32_t code;
    const char* name;
  };

  void PopulateTable(Isolate* isolate);

  // For a few types of references, we can get their address from their id.
  void AddFromId(TypeCode type,
                 uint16_t id,
                 const char* name,
                 Isolate* isolate);

  // For other types of references, the caller will figure out the address.
  void Add(Address address, TypeCode type, uint16_t id, const char* name);

  List<ExternalReferenceEntry> refs_;
  int max_id_[kTypeCodeCount];
};


111 112 113 114 115 116 117 118 119 120 121
class ExternalReferenceEncoder {
 public:
  ExternalReferenceEncoder();

  uint32_t Encode(Address key) const;

  const char* NameOfAddress(Address key) const;

 private:
  HashMap encodings_;
  static uint32_t Hash(Address key) {
122
    return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key) >> 2);
123 124 125 126 127 128 129
  }

  int IndexOf(Address key) const;

  static bool Match(void* key1, void* key2) { return key1 == key2; }

  void Put(Address key, int index);
130 131

  Isolate* isolate_;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
};


class ExternalReferenceDecoder {
 public:
  ExternalReferenceDecoder();
  ~ExternalReferenceDecoder();

  Address Decode(uint32_t key) const {
    if (key == 0) return NULL;
    return *Lookup(key);
  }

 private:
  Address** encodings_;

  Address* Lookup(uint32_t key) const {
    int type = key >> kReferenceTypeShift;
    ASSERT(kFirstTypeCode <= type && type < kTypeCodeCount);
    int id = key & kReferenceIdMask;
    return &encodings_[type][id];
  }

  void Put(uint32_t key, Address value) {
    *Lookup(key) = value;
  }
158 159

  Isolate* isolate_;
160 161 162
};


163 164 165 166 167 168 169 170 171 172 173 174
class SnapshotByteSource {
 public:
  SnapshotByteSource(const byte* array, int length)
    : data_(array), length_(length), position_(0) { }

  bool HasMore() { return position_ < length_; }

  int Get() {
    ASSERT(position_ < length_);
    return data_[position_++];
  }

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  int32_t GetUnalignedInt() {
#if defined(V8_HOST_CAN_READ_UNALIGNED) &&  __BYTE_ORDER == __LITTLE_ENDIAN
    int32_t answer;
    ASSERT(position_ + sizeof(answer) <= length_ + 0u);
    answer = *reinterpret_cast<const int32_t*>(data_ + position_);
#else
    int32_t answer = data_[position_];
    answer |= data_[position_ + 1] << 8;
    answer |= data_[position_ + 2] << 16;
    answer |= data_[position_ + 3] << 24;
#endif
    return answer;
  }

  void Advance(int by) { position_ += by; }

191
  inline void CopyRaw(byte* to, int number_of_bytes);
192

193
  inline int GetInt();
194

195
  bool AtEOF();
196

197
  int position() { return position_; }
198

199 200 201 202 203 204
 private:
  const byte* data_;
  int length_;
  int position_;
};

205

206 207 208 209 210
// The Serializer/Deserializer class is a common superclass for Serializer and
// Deserializer which is used to store common constants and methods used by
// both.
class SerializerDeserializer: public ObjectVisitor {
 public:
211
  static void Iterate(ObjectVisitor* visitor);
212

213 214
  static int nop() { return kNop; }

215
 protected:
216 217 218
  // Where the pointed-to object can be found:
  enum Where {
    kNewObject = 0,                 // Object is next in snapshot.
219
    // 1-6                             One per space.
220 221 222
    kRootArray = 0x9,               // Object is found in root array.
    kPartialSnapshotCache = 0xa,    // Object is in the cache.
    kExternalReference = 0xb,       // Pointer to an external reference.
223 224 225 226 227 228 229 230
    kSkip = 0xc,                    // Skip n bytes.
    kNop = 0xd,                     // Does nothing, used to pad.
    // 0xe-0xf                         Free.
    kBackref = 0x10,                // Object is described relative to end.
    // 0x11-0x16                       One per space.
    kBackrefWithSkip = 0x18,        // Object is described relative to end.
    // 0x19-0x1e                       One per space.
    // 0x20-0x3f                       Used by misc. tags below.
231
    kPointedToMask = 0x3f
232
  };
233 234 235 236 237 238 239 240 241

  // How to code the pointer to the object.
  enum HowToCode {
    kPlain = 0,                          // Straight pointer.
    // What this means depends on the architecture:
    kFromCode = 0x40,                    // A pointer inlined in code.
    kHowToCodeMask = 0x40
  };

242 243 244 245 246 247 248
  // For kRootArrayConstants
  enum WithSkip {
    kNoSkipDistance = 0,
    kHasSkipDistance = 0x40,
    kWithSkipMask = 0x40
  };

249 250 251
  // Where to point within the object.
  enum WhereToPoint {
    kStartOfObject = 0,
252
    kInnerPointer = 0x80,  // First insn in code object or payload of cell.
253 254 255 256
    kWhereToPointMask = 0x80
  };

  // Misc.
257 258 259 260 261 262
  // Raw data to be copied from the snapshot.  This byte code does not advance
  // the current pointer, which is used for code objects, where we write the
  // entire code in one memcpy, then fix up stuff with kSkip and other byte
  // codes that overwrite data.
  static const int kRawData = 0x20;
  // Some common raw lengths: 0x21-0x3f.  These autoadvance the current pointer.
263 264 265 266 267
  // A tag emitted at strategic points in the snapshot to delineate sections.
  // If the deserializer does not find these at the expected moments then it
  // is an indication that the snapshot and the VM do not fit together.
  // Examine the build process for architecture, version or configuration
  // mismatches.
268
  static const int kSynchronize = 0x70;
269 270
  // Used for the source code of the natives, which is in the executable, but
  // is referred to from external strings in the snapshot.
271
  static const int kNativesStringResource = 0x71;
272 273 274 275
  static const int kRepeat = 0x72;
  static const int kConstantRepeat = 0x73;
  // 0x73-0x7f            Repeat last word (subtract 0x72 to get the count).
  static const int kMaxRepeats = 0x7f - 0x72;
276 277
  static int CodeForRepeats(int repeats) {
    ASSERT(repeats >= 1 && repeats <= kMaxRepeats);
278
    return 0x72 + repeats;
279 280 281
  }
  static int RepeatsForCode(int byte_code) {
    ASSERT(byte_code >= kConstantRepeat && byte_code <= 0x7f);
282
    return byte_code - 0x72;
283
  }
284 285
  static const int kRootArrayConstants = 0xa0;
  // 0xa0-0xbf            Things from the first 32 elements of the root array.
286 287
  static const int kRootArrayNumberOfConstantEncodings = 0x20;
  static int RootArrayConstantFromByteCode(int byte_code) {
288
    return byte_code & 0x1f;
289
  }
290

291
  static const int kNumberOfSpaces = LO_SPACE;
292
  static const int kAnyOldSpace = -1;
293

294
  // A bitmask for getting the space out of an instruction.
295
  static const int kSpaceMask = 7;
296 297 298
};


299
int SnapshotByteSource::GetInt() {
300 301 302 303 304 305 306 307 308 309
  // This way of variable-length encoding integers does not suffer from branch
  // mispredictions.
  uint32_t answer = GetUnalignedInt();
  int bytes = answer & 3;
  Advance(bytes);
  uint32_t mask = 0xffffffffu;
  mask >>= 32 - (bytes << 3);
  answer &= mask;
  answer >>= 2;
  return answer;
310 311 312 313
}


void SnapshotByteSource::CopyRaw(byte* to, int number_of_bytes) {
314
  OS::MemCopy(to, data_ + position_, number_of_bytes);
315 316 317
  position_ += number_of_bytes;
}

318 319

// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
320
class Deserializer: public SerializerDeserializer {
321 322
 public:
  // Create a deserializer from a snapshot byte source.
323
  explicit Deserializer(SnapshotByteSource* source);
324

325
  virtual ~Deserializer();
326 327 328

  // Deserialize the snapshot into an empty heap.
  void Deserialize();
329 330 331 332

  // Deserialize a single object and the objects reachable from it.
  void DeserializePartial(Object** root);

333
  void set_reservation(int space_number, int reservation) {
334 335 336 337 338
    ASSERT(space_number >= 0);
    ASSERT(space_number <= LAST_SPACE);
    reservations_[space_number] = reservation;
  }

339 340 341 342 343 344 345 346 347 348 349
 private:
  virtual void VisitPointers(Object** start, Object** end);

  virtual void VisitExternalReferences(Address* start, Address* end) {
    UNREACHABLE();
  }

  virtual void VisitRuntimeEntry(RelocInfo* rinfo) {
    UNREACHABLE();
  }

350 351 352
  // Fills in some heap data in an area from start to end (non-inclusive).  The
  // space id is used for the write barrier.  The object_address is the address
  // of the object we are writing into, or NULL if we are not writing into an
353 354
  // object, i.e. if we are writing a series of tagged values that are not on
  // the heap.
355 356
  void ReadChunk(
      Object** start, Object** end, int space, Address object_address);
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
  void ReadObject(int space_number, Object** write_back);

  // This routine both allocates a new object, and also keeps
  // track of where objects have been allocated so that we can
  // fix back references when deserializing.
  Address Allocate(int space_index, int size) {
    Address address = high_water_[space_index];
    high_water_[space_index] = address + size;
    return address;
  }

  // This returns the address of an object that has been described in the
  // snapshot as being offset bytes back in a particular space.
  HeapObject* GetAddressFromEnd(int space) {
    int offset = source_->GetInt();
    offset <<= kObjectAlignmentBits;
    return HeapObject::FromAddress(high_water_[space] - offset);
  }

376

377 378 379
  // Cached current isolate.
  Isolate* isolate_;

380
  SnapshotByteSource* source_;
381 382 383
  // This is the address of the next object that will be allocated in each
  // space.  It is used to calculate the addresses of back-references.
  Address high_water_[LAST_SPACE + 1];
384

385
  int reservations_[LAST_SPACE + 1];
386
  static const intptr_t kUninitializedReservation = -1;
387

388 389
  ExternalReferenceDecoder* external_reference_decoder_;

390
  DISALLOW_COPY_AND_ASSIGN(Deserializer);
391 392 393 394 395 396 397
};


class SnapshotByteSink {
 public:
  virtual ~SnapshotByteSink() { }
  virtual void Put(int byte, const char* description) = 0;
398 399 400
  virtual void PutSection(int byte, const char* description) {
    Put(byte, description);
  }
401
  void PutInt(uintptr_t integer, const char* description);
402
  virtual int Position() = 0;
403 404 405
};


406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
// Mapping objects to their location after deserialization.
// This is used during building, but not at runtime by V8.
class SerializationAddressMapper {
 public:
  SerializationAddressMapper()
      : serialization_map_(new HashMap(&SerializationMatchFun)),
        no_allocation_(new AssertNoAllocation()) { }

  ~SerializationAddressMapper() {
    delete serialization_map_;
    delete no_allocation_;
  }

  bool IsMapped(HeapObject* obj) {
    return serialization_map_->Lookup(Key(obj), Hash(obj), false) != NULL;
  }

  int MappedTo(HeapObject* obj) {
    ASSERT(IsMapped(obj));
    return static_cast<int>(reinterpret_cast<intptr_t>(
        serialization_map_->Lookup(Key(obj), Hash(obj), false)->value));
  }

  void AddMapping(HeapObject* obj, int to) {
    ASSERT(!IsMapped(obj));
    HashMap::Entry* entry =
        serialization_map_->Lookup(Key(obj), Hash(obj), true);
    entry->value = Value(to);
  }

 private:
437
  static bool SerializationMatchFun(void* key1, void* key2) {
438 439 440
    return key1 == key2;
  }

441
  static uint32_t Hash(HeapObject* obj) {
442 443 444
    return static_cast<int32_t>(reinterpret_cast<intptr_t>(obj->address()));
  }

445
  static void* Key(HeapObject* obj) {
446 447 448
    return reinterpret_cast<void*>(obj->address());
  }

449
  static void* Value(int v) {
450 451 452 453 454 455 456 457 458
    return reinterpret_cast<void*>(v);
  }

  HashMap* serialization_map_;
  AssertNoAllocation* no_allocation_;
  DISALLOW_COPY_AND_ASSIGN(SerializationAddressMapper);
};


459
// There can be only one serializer per V8 process.
460
class Serializer : public SerializerDeserializer {
461
 public:
462
  explicit Serializer(SnapshotByteSink* sink);
463
  ~Serializer();
464
  void VisitPointers(Object** start, Object** end);
465 466 467
  // You can call this after serialization to find out how much space was used
  // in each space.
  int CurrentAllocationAddress(int space) {
468
    ASSERT(space < kNumberOfSpaces);
469 470
    return fullness_[space];
  }
471

472
  static void Enable() {
473 474 475 476 477 478
    if (!serialization_enabled_) {
      ASSERT(!too_late_to_enable_now_);
    }
    serialization_enabled_ = true;
  }

479
  static void Disable() { serialization_enabled_ = false; }
480 481
  // Call this when you have made use of the fact that there is no serialization
  // going on.
482 483
  static void TooLateToEnableNow() { too_late_to_enable_now_ = true; }
  static bool enabled() { return serialization_enabled_; }
484
  SerializationAddressMapper* address_mapper() { return &address_mapper_; }
485 486 487 488 489
  void PutRoot(int index,
               HeapObject* object,
               HowToCode how,
               WhereToPoint where,
               int skip);
490

491
 protected:
492
  static const int kInvalidRootIndex = -1;
493

494
  int RootIndex(HeapObject* heap_object, HowToCode from);
495
  virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) = 0;
496 497 498 499 500
  intptr_t root_index_wave_front() { return root_index_wave_front_; }
  void set_root_index_wave_front(intptr_t value) {
    ASSERT(value >= root_index_wave_front_);
    root_index_wave_front_ = value;
  }
501

502 503
  class ObjectSerializer : public ObjectVisitor {
   public:
504
    ObjectSerializer(Serializer* serializer,
505 506
                     Object* o,
                     SnapshotByteSink* sink,
507 508
                     HowToCode how_to_code,
                     WhereToPoint where_to_point)
509 510 511
      : serializer_(serializer),
        object_(HeapObject::cast(o)),
        sink_(sink),
512
        reference_representation_(how_to_code + where_to_point),
513 514 515
        bytes_processed_so_far_(0),
        code_object_(o->IsCode()),
        code_has_been_output_(false) { }
516 517
    void Serialize();
    void VisitPointers(Object** start, Object** end);
518
    void VisitEmbeddedPointer(RelocInfo* target);
519
    void VisitExternalReferences(Address* start, Address* end);
520
    void VisitExternalReference(RelocInfo* rinfo);
521
    void VisitCodeTarget(RelocInfo* target);
522
    void VisitCodeEntry(Address entry_address);
523
    void VisitGlobalPropertyCell(RelocInfo* rinfo);
524
    void VisitRuntimeEntry(RelocInfo* reloc);
525 526 527 528 529 530 531 532
    // Used for seralizing the external strings that hold the natives source.
    void VisitExternalAsciiString(
        v8::String::ExternalAsciiStringResource** resource);
    // We can't serialize a heap with external two byte strings.
    void VisitExternalTwoByteString(
        v8::String::ExternalStringResource** resource) {
      UNREACHABLE();
    }
533 534

   private:
535 536 537 538 539 540
    enum ReturnSkip { kCanReturnSkipInsteadOfSkipping, kIgnoringReturn };
    // This function outputs or skips the raw data between the last pointer and
    // up to the current position.  It optionally can just return the number of
    // bytes to skip instead of performing a skip instruction, in case the skip
    // can be merged into the next instruction.
    int OutputRawData(Address up_to, ReturnSkip return_skip = kIgnoringReturn);
541

542
    Serializer* serializer_;
543 544
    HeapObject* object_;
    SnapshotByteSink* sink_;
545
    int reference_representation_;
546
    int bytes_processed_so_far_;
547 548
    bool code_object_;
    bool code_has_been_output_;
549 550
  };

551
  virtual void SerializeObject(Object* o,
552
                               HowToCode how_to_code,
553 554
                               WhereToPoint where_to_point,
                               int skip) = 0;
555 556 557
  void SerializeReferenceToPreviousObject(
      int space,
      int address,
558
      HowToCode how_to_code,
559 560
      WhereToPoint where_to_point,
      int skip);
561
  void InitializeAllocators();
562
  // This will return the space for an object.
563
  static int SpaceOfObject(HeapObject* object);
564
  int Allocate(int space, int size);
565 566 567 568
  int EncodeExternalReference(Address addr) {
    return external_reference_encoder_->Encode(addr);
  }

569 570 571
  int SpaceAreaSize(int space);

  Isolate* isolate_;
572
  // Keep track of the fullness of each space in order to generate
573
  // relative addresses for back references.
574 575 576 577
  int fullness_[LAST_SPACE + 1];
  SnapshotByteSink* sink_;
  int current_root_index_;
  ExternalReferenceEncoder* external_reference_encoder_;
578
  static bool serialization_enabled_;
579
  // Did we already make use of the fact that serialization was not enabled?
580
  static bool too_late_to_enable_now_;
581
  SerializationAddressMapper address_mapper_;
582
  intptr_t root_index_wave_front_;
583
  void Pad();
584 585

  friend class ObjectSerializer;
586
  friend class Deserializer;
587

588
 private:
589
  DISALLOW_COPY_AND_ASSIGN(Serializer);
590 591
};

592 593 594 595 596 597 598

class PartialSerializer : public Serializer {
 public:
  PartialSerializer(Serializer* startup_snapshot_serializer,
                    SnapshotByteSink* sink)
    : Serializer(sink),
      startup_serializer_(startup_snapshot_serializer) {
599
    set_root_index_wave_front(Heap::kStrongRootListLength);
600 601 602 603 604
  }

  // Serialize the objects reachable from a single object pointer.
  virtual void Serialize(Object** o);
  virtual void SerializeObject(Object* o,
605
                               HowToCode how_to_code,
606 607
                               WhereToPoint where_to_point,
                               int skip);
608 609 610 611

 protected:
  virtual int PartialSnapshotCacheIndex(HeapObject* o);
  virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) {
612 613 614 615 616
    // Scripts should be referred only through shared function infos.  We can't
    // allow them to be part of the partial snapshot because they contain a
    // unique ID, and deserializing several partial snapshots containing script
    // would cause dupes.
    ASSERT(!o->IsScript());
617
    return o->IsString() || o->IsSharedFunctionInfo() ||
618
           o->IsHeapNumber() || o->IsCode() ||
619
           o->IsScopeInfo() ||
620
           o->map() == HEAP->fixed_cow_array_map();
621 622 623 624 625 626 627 628 629 630 631 632 633
  }

 private:
  Serializer* startup_serializer_;
  DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};


class StartupSerializer : public Serializer {
 public:
  explicit StartupSerializer(SnapshotByteSink* sink) : Serializer(sink) {
    // Clear the cache of objects used by the partial snapshot.  After the
    // strong roots have been serialized we can create a partial snapshot
634
    // which will repopulate the cache with objects needed by that partial
635
    // snapshot.
636
    Isolate::Current()->set_serialize_partial_snapshot_cache_length(0);
637 638 639 640
  }
  // Serialize the current state of the heap.  The order is:
  // 1) Strong references.
  // 2) Partial snapshot cache.
641
  // 3) Weak references (e.g. the string table).
642 643
  virtual void SerializeStrongReferences();
  virtual void SerializeObject(Object* o,
644
                               HowToCode how_to_code,
645 646
                               WhereToPoint where_to_point,
                               int skip);
647 648 649 650
  void SerializeWeakReferences();
  void Serialize() {
    SerializeStrongReferences();
    SerializeWeakReferences();
651
    Pad();
652 653 654 655 656 657 658 659
  }

 private:
  virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) {
    return false;
  }
};

660

661 662 663
} }  // namespace v8::internal

#endif  // V8_SERIALIZE_H_