// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_SNAPSHOT_CODE_SERIALIZER_H_
#define V8_SNAPSHOT_CODE_SERIALIZER_H_

#include "src/snapshot/serializer.h"

namespace v8 {
namespace internal {

class ScriptData {
 public:
  ScriptData(const byte* data, int length);
  ~ScriptData() {
    if (owns_data_) DeleteArray(data_);
  }

  const byte* data() const { return data_; }
  int length() const { return length_; }
  bool rejected() const { return rejected_; }

  void Reject() { rejected_ = true; }

  void AcquireDataOwnership() {
    DCHECK(!owns_data_);
    owns_data_ = true;
  }

  void ReleaseDataOwnership() {
    DCHECK(owns_data_);
    owns_data_ = false;
  }

 private:
  bool owns_data_ : 1;
  bool rejected_ : 1;
  const byte* data_;
  int length_;

  DISALLOW_COPY_AND_ASSIGN(ScriptData);
};

class CodeSerializer : public Serializer<> {
 public:
  static ScriptCompiler::CachedData* Serialize(Handle<SharedFunctionInfo> info);

  ScriptData* SerializeSharedFunctionInfo(Handle<SharedFunctionInfo> info);

  V8_WARN_UNUSED_RESULT static MaybeHandle<SharedFunctionInfo> Deserialize(
      Isolate* isolate, ScriptData* cached_data, Handle<String> source,
      ScriptOriginOptions origin_options);

  const std::vector<uint32_t>* stub_keys() const { return &stub_keys_; }

  uint32_t source_hash() const { return source_hash_; }

 protected:
  CodeSerializer(Isolate* isolate, uint32_t source_hash);
  ~CodeSerializer() override { OutputStatistics("CodeSerializer"); }

  virtual void SerializeCodeObject(Code* code_object, HowToCode how_to_code,
                                   WhereToPoint where_to_point) {
    UNREACHABLE();
  }

  virtual bool ElideObject(Object* obj) { return false; }
  void SerializeGeneric(HeapObject* heap_object, HowToCode how_to_code,
                        WhereToPoint where_to_point);

 private:
  void SerializeObject(HeapObject* o, HowToCode how_to_code,
                       WhereToPoint where_to_point, int skip) override;

  void SerializeCodeStub(Code* code_stub, HowToCode how_to_code,
                         WhereToPoint where_to_point);

  bool SerializeReadOnlyObject(HeapObject* obj, HowToCode how_to_code,
                               WhereToPoint where_to_point, int skip);

  DisallowHeapAllocation no_gc_;
  uint32_t source_hash_;
  std::vector<uint32_t> stub_keys_;
  DISALLOW_COPY_AND_ASSIGN(CodeSerializer);
};

// Wrapper around ScriptData to provide code-serializer-specific functionality.
class SerializedCodeData : public SerializedData {
 public:
  enum SanityCheckResult {
    CHECK_SUCCESS = 0,
    MAGIC_NUMBER_MISMATCH = 1,
    VERSION_MISMATCH = 2,
    SOURCE_MISMATCH = 3,
    CPU_FEATURES_MISMATCH = 4,
    FLAGS_MISMATCH = 5,
    CHECKSUM_MISMATCH = 6,
    INVALID_HEADER = 7,
    LENGTH_MISMATCH = 8
  };

  // The data header consists of uint32_t-sized entries:
  // [0] magic number and (internally provided) external reference count
  // [1] extra (API-provided) external reference count
  // [2] version hash
  // [3] source hash
  // [4] cpu features
  // [5] flag hash
  // [6] number of code stub keys
  // [7] number of reservation size entries
  // [8] payload length
  // [9] payload checksum part 1
  // [10] payload checksum part 2
  // ...  reservations
  // ...  code stub keys
  // ...  serialized payload
  static const uint32_t kVersionHashOffset = kMagicNumberOffset + kUInt32Size;
  static const uint32_t kSourceHashOffset = kVersionHashOffset + kUInt32Size;
  static const uint32_t kCpuFeaturesOffset = kSourceHashOffset + kUInt32Size;
  static const uint32_t kFlagHashOffset = kCpuFeaturesOffset + kUInt32Size;
  static const uint32_t kNumReservationsOffset = kFlagHashOffset + kUInt32Size;
  static const uint32_t kNumCodeStubKeysOffset =
      kNumReservationsOffset + kUInt32Size;
  static const uint32_t kPayloadLengthOffset =
      kNumCodeStubKeysOffset + kUInt32Size;
  static const uint32_t kChecksum1Offset = kPayloadLengthOffset + kUInt32Size;
  static const uint32_t kChecksum2Offset = kChecksum1Offset + kUInt32Size;
  static const uint32_t kUnalignedHeaderSize = kChecksum2Offset + kUInt32Size;
  static const uint32_t kHeaderSize = POINTER_SIZE_ALIGN(kUnalignedHeaderSize);

  // Used when consuming.
  static SerializedCodeData FromCachedData(Isolate* isolate,
                                           ScriptData* cached_data,
                                           uint32_t expected_source_hash,
                                           SanityCheckResult* rejection_result);

  // Used when producing.
  SerializedCodeData(const std::vector<byte>* payload,
                     const CodeSerializer* cs);

  // Return ScriptData object and relinquish ownership over it to the caller.
  ScriptData* GetScriptData();

  std::vector<Reservation> Reservations() const;
  Vector<const byte> Payload() const;

  Vector<const uint32_t> CodeStubKeys() const;

  static uint32_t SourceHash(Handle<String> source,
                             ScriptOriginOptions origin_options);

 private:
  explicit SerializedCodeData(ScriptData* data);
  SerializedCodeData(const byte* data, int size)
      : SerializedData(const_cast<byte*>(data), size) {}

  Vector<const byte> DataWithoutHeader() const {
    return Vector<const byte>(data_ + kHeaderSize, size_ - kHeaderSize);
  }

  SanityCheckResult SanityCheck(Isolate* isolate,
                                uint32_t expected_source_hash) const;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_SNAPSHOT_CODE_SERIALIZER_H_
