// Copyright (c) 2011, 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.

#ifndef RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_H_
#define RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_H_

#include "vm/allocation.h"
#include "vm/globals.h"
#include "vm/log.h"
#include "vm/object.h"

#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

namespace dart {

// Forward declaration.
class CodeComments;
class JSONArray;
class MemoryRegion;

// Disassembly formatter interface, which consumes the
// disassembled instructions in any desired form.
class DisassemblyFormatter {
 public:
  DisassemblyFormatter() {}
  virtual ~DisassemblyFormatter() {}

  // Consume the decoded instruction at the given pc.
  virtual void ConsumeInstruction(char* hex_buffer,
                                  intptr_t hex_size,
                                  char* human_buffer,
                                  intptr_t human_size,
                                  Object* object,
                                  uword pc) = 0;

  // Print a formatted message.
  virtual void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) = 0;
};

// Basic disassembly formatter that outputs the disassembled instruction
// to stdout.
class DisassembleToStdout : public DisassemblyFormatter {
 public:
  DisassembleToStdout() : DisassemblyFormatter() {}
  ~DisassembleToStdout() {}

  virtual void ConsumeInstruction(char* hex_buffer,
                                  intptr_t hex_size,
                                  char* human_buffer,
                                  intptr_t human_size,
                                  Object* object,
                                  uword pc);

  virtual void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);

 private:
  DISALLOW_ALLOCATION()
  DISALLOW_COPY_AND_ASSIGN(DisassembleToStdout);
};

#if !defined(PRODUCT)
// Disassemble into a JSONStream.
class DisassembleToJSONStream : public DisassemblyFormatter {
 public:
  explicit DisassembleToJSONStream(const JSONArray& jsarr)
      : DisassemblyFormatter(), jsarr_(jsarr) {}
  ~DisassembleToJSONStream() {}

  virtual void ConsumeInstruction(char* hex_buffer,
                                  intptr_t hex_size,
                                  char* human_buffer,
                                  intptr_t human_size,
                                  Object* object,
                                  uword pc);

  virtual void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);

 private:
  const JSONArray& jsarr_;
  DISALLOW_ALLOCATION();
  DISALLOW_COPY_AND_ASSIGN(DisassembleToJSONStream);
};
#endif  // !defined(PRODUCT)

#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
// Basic disassembly formatter that outputs the disassembled instruction
// to a memory buffer. This is only intended for test writing.
class DisassembleToMemory : public DisassemblyFormatter {
 public:
  DisassembleToMemory(char* buffer, uintptr_t length)
      : DisassemblyFormatter(),
        buffer_(buffer),
        remaining_(length),
        overflowed_(false) {}
  ~DisassembleToMemory() {}

  virtual void ConsumeInstruction(char* hex_buffer,
                                  intptr_t hex_size,
                                  char* human_buffer,
                                  intptr_t human_size,
                                  Object* object,
                                  uword pc);

  virtual void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);

 private:
  char* buffer_;
  int remaining_;
  bool overflowed_;
  DISALLOW_ALLOCATION();
  DISALLOW_COPY_AND_ASSIGN(DisassembleToMemory);
};
#endif

// Disassemble instructions.
class Disassembler : public AllStatic {
 public:
  // Disassemble instructions between start and end.
  // (The assumption is that start is at a valid instruction).
  // Return true if all instructions were successfully decoded, false otherwise.
  static void Disassemble(uword start,
                          uword end,
                          DisassemblyFormatter* formatter,
                          const Code& code,
                          const CodeComments* comments = nullptr);

  static void Disassemble(uword start,
                          uword end,
                          DisassemblyFormatter* formatter) {
    Disassemble(start, end, formatter, Code::Handle());
  }

  static void Disassemble(uword start,
                          uword end,
                          DisassemblyFormatter* formatter,
                          const CodeComments* comments) {
    Disassemble(start, end, formatter, Code::Handle(), comments);
  }

  static void Disassemble(uword start, uword end, const Code& code) {
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
    DisassembleToStdout stdout_formatter;
    LogBlock lb;
    if (!code.IsNull()) {
      Disassemble(start, end, &stdout_formatter, code, &code.comments());
    } else {
      Disassemble(start, end, &stdout_formatter, code);
    }
#else
    UNREACHABLE();
#endif
  }

  static void Disassemble(uword start, uword end) {
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
    DisassembleToStdout stdout_formatter;
    LogBlock lb;
    Disassemble(start, end, &stdout_formatter);
#else
    UNREACHABLE();
#endif
  }

  static void Disassemble(uword start,
                          uword end,
                          char* buffer,
                          uintptr_t buffer_size) {
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
    DisassembleToMemory memory_formatter(buffer, buffer_size);
    LogBlock lb;
    Disassemble(start, end, &memory_formatter);
#else
    UNREACHABLE();
#endif
  }

  // Decodes one instruction.
  // Writes a hexadecimal representation into the hex_buffer and a
  // human-readable representation into the human_buffer.
  // Writes the length of the decoded instruction in bytes in out_instr_len.
  static void DecodeInstruction(char* hex_buffer,
                                intptr_t hex_size,
                                char* human_buffer,
                                intptr_t human_size,
                                int* out_instr_len,
                                const Code& code,
                                Object** object,
                                uword pc);

  static void DisassembleCode(const Function& function,
                              const Code& code,
                              bool optimized);

  static void DisassembleStub(const char* name, const Code& code);

#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
  static void SetExtensions(ExtensionSet extensions) {
    extensions_ = extensions;
  }
#endif

 private:
  static void DisassembleCodeHelper(const char* function_fullname,
                                    const char* function_info,
                                    const Code& code,
                                    bool optimized);

  static constexpr int kHexadecimalBufferSize = 32;
  static constexpr int kUserReadableBufferSize = 256;

#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
  static ExtensionSet extensions_;
#endif
};

}  // namespace dart

#endif  // RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_H_
