/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_JitCode_h
#define jit_JitCode_h

#include "mozilla/MemoryReporting.h"  // MallocSizeOf

#include <stddef.h>  // size_t
#include <stdint.h>  // uint8_t, uint32_t

#include "jstypes.h"

#include "gc/Cell.h"       // gc::TenuredCellWithNonGCPointer
#include "gc/GCEnum.h"     // AllowGC
#include "js/TraceKind.h"  // JS::TraceKind
#include "js/UbiNode.h"    // ubi::{TracerConcrete, Size, CourseType}

namespace js {
namespace jit {

class ExecutablePool;
class JitCode;
class MacroAssembler;

enum class CodeKind : uint8_t;

// Header at start of raw code buffer
struct JitCodeHeader {
  // Link back to corresponding gcthing
  JitCode* jitCode_;

  void init(JitCode* jitCode);

  static JitCodeHeader* FromExecutable(uint8_t* buffer) {
    return (JitCodeHeader*)(buffer - sizeof(JitCodeHeader));
  }
};

class JitCode : public gc::TenuredCellWithNonGCPointer<uint8_t> {
  friend class gc::CellAllocator;

 public:
  // Raw code pointer, stored in the cell header.
  uint8_t* raw() const { return headerPtr(); }

  // Raw pointer to the allocated memory to be able to modify the JitCodePointer
  // which is stored above the code.
  uint8_t* allocatedMemory() const { return headerPtr() - headerSize_; }

 protected:
  ExecutablePool* pool_;
  uint32_t bufferSize_;  // Total buffer size. Does not include headerSize_.
  uint32_t insnSize_;    // Instruction stream size.
  uint32_t dataSize_;    // Size of the read-only data area.
  uint32_t jumpRelocTableBytes_;  // Size of the jump relocation table.
  uint32_t dataRelocTableBytes_;  // Size of the data relocation table.
  uint8_t headerSize_ : 5;        // Number of bytes allocated before codeStart.
  uint8_t kind_ : 3;              // jit::CodeKind, for the memory reporters.
  bool invalidated_ : 1;     // Whether the code object has been invalidated.
                             // This is necessary to prevent GC tracing.
  bool hasBytecodeMap_ : 1;  // Whether the code object has been registered with
                             // native=>bytecode mapping tables.
  bool profilerInstrumented_ : 1;  // Whether or not profiling instrumentation
                                   // is on. Used by BaselineScript.
  uint8_t localTracingSlots_;

  JitCode() = delete;
  JitCode(uint8_t* code, uint32_t bufferSize, uint32_t headerSize,
          ExecutablePool* pool, CodeKind kind)
      : TenuredCellWithNonGCPointer(code),
        pool_(pool),
        bufferSize_(bufferSize),
        insnSize_(0),
        dataSize_(0),
        jumpRelocTableBytes_(0),
        dataRelocTableBytes_(0),
        headerSize_(headerSize),
        kind_(uint8_t(kind)),
        invalidated_(false),
        hasBytecodeMap_(false),
        profilerInstrumented_(false),
        localTracingSlots_(0) {
    MOZ_ASSERT(CodeKind(kind_) == kind);
    MOZ_ASSERT(headerSize_ == headerSize);
  }

  uint32_t dataOffset() const { return insnSize_; }
  uint32_t jumpRelocTableOffset() const { return dataOffset() + dataSize_; }
  uint32_t dataRelocTableOffset() const {
    return jumpRelocTableOffset() + jumpRelocTableBytes_;
  }

 public:
  uint8_t* rawEnd() const { return raw() + insnSize_; }
  bool containsNativePC(const void* addr) const {
    const uint8_t* addr_u8 = (const uint8_t*)addr;
    return raw() <= addr_u8 && addr_u8 < rawEnd();
  }
  size_t instructionsSize() const { return insnSize_; }
  size_t bufferSize() const { return bufferSize_; }
  size_t headerSize() const { return headerSize_; }
  size_t allocatedSize() const { return bufferSize_ + headerSize_; }

  void traceChildren(JSTracer* trc);
  void finalize(JS::GCContext* gcx);
  void setInvalidated() { invalidated_ = true; }

  void setHasBytecodeMap() { hasBytecodeMap_ = true; }

  void setLocalTracingSlots(uint8_t localTracingSlots) {
    localTracingSlots_ = localTracingSlots;
  }

  bool isProfilerInstrumented() const { return profilerInstrumented_; }
  void setProfilerInstrumented(bool enable) { profilerInstrumented_ = enable; }

  uint8_t localTracingSlots() { return localTracingSlots_; }

  // If this JitCode object has been, effectively, corrupted due to
  // invalidation patching, then we have to remember this so we don't try and
  // trace relocation entries that may now be corrupt.
  bool invalidated() const { return !!invalidated_; }

  template <typename T>
  T as() const {
    return JS_DATA_TO_FUNC_PTR(T, raw());
  }

  void copyFrom(MacroAssembler& masm);

  static JitCode* FromExecutable(uint8_t* buffer) {
    JitCode* code = JitCodeHeader::FromExecutable(buffer)->jitCode_;
    MOZ_ASSERT(code->raw() == buffer);
    return code;
  }

  static size_t offsetOfCode() { return offsetOfHeaderPtr(); }

  uint8_t* jumpRelocTable() { return raw() + jumpRelocTableOffset(); }

  // Allocates a new JitCode object which will be managed by the GC. If no
  // object can be allocated, nullptr is returned. On failure, |pool| is
  // automatically released, so the code may be freed.
  template <AllowGC allowGC>
  static JitCode* New(JSContext* cx, uint8_t* code, uint32_t totalSize,
                      uint32_t headerSize, ExecutablePool* pool, CodeKind kind);

 public:
  static const JS::TraceKind TraceKind = JS::TraceKind::JitCode;
};

}  // namespace jit
}  // namespace js

// JS::ubi::Nodes can point to js::jit::JitCode instances; they're js::gc::Cell
// instances with no associated compartment.
namespace JS {
namespace ubi {
template <>
class Concrete<js::jit::JitCode> : TracerConcrete<js::jit::JitCode> {
 protected:
  explicit Concrete(js::jit::JitCode* ptr)
      : TracerConcrete<js::jit::JitCode>(ptr) {}

 public:
  static void construct(void* storage, js::jit::JitCode* ptr) {
    new (storage) Concrete(ptr);
  }

  CoarseType coarseType() const final { return CoarseType::Script; }

  Size size(mozilla::MallocSizeOf mallocSizeOf) const override {
    Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
    size += get().bufferSize();
    size += get().headerSize();
    return size;
  }

  const char16_t* typeName() const override { return concreteTypeName; }
  static const char16_t concreteTypeName[];
};

}  // namespace ubi
}  // namespace JS

#endif /* jit_JitCode_h */
