// Copyright 2019 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.

#include "irregexp/imported/regexp-bytecodes.h"

#include <cctype>

#include "irregexp/imported/regexp-bytecode-analysis.h"
#include "irregexp/imported/regexp-bytecodes-inl.h"

namespace v8 {
namespace internal {
namespace regexp {

namespace {

std::ostream& operator<<(std::ostream& os,
                         RegExpMacroAssembler::StackCheckFlag val) {
  switch (val) {
    case RegExpMacroAssembler::StackCheckFlag::kNoStackLimitCheck:
      return os << "NoCheck";
    case RegExpMacroAssembler::StackCheckFlag::kCheckStackLimit:
      return os << "Check";
  }
  UNREACHABLE();
}

std::ostream& operator<<(std::ostream& os, StandardCharacterSet val) {
  switch (val) {
    case StandardCharacterSet::kWhitespace:
      return os << "Whitespace";
    case StandardCharacterSet::kNotWhitespace:
      return os << "NotWhitespace";
    case StandardCharacterSet::kDigit:
      return os << "Digit";
    case StandardCharacterSet::kNotDigit:
      return os << "NotDigit";
    case StandardCharacterSet::kLineTerminator:
      return os << "LineTerminator";
    case StandardCharacterSet::kNotLineTerminator:
      return os << "NotLineTerminator";
    case StandardCharacterSet::kWord:
      return os << "Word";
    case StandardCharacterSet::kNotWord:
      return os << "NotWord";
    case StandardCharacterSet::kEverything:
      return os << "Everything";
  }
  UNREACHABLE();
}

}  // namespace

void RegExpBytecodeDisassembleSingle(const uint8_t* code_base,
                                     const uint8_t* pc) {
  StdoutStream os;
  DisallowGarbageCollection no_gc;
  Bytecode bytecode = Bytecodes::FromPtr(pc);
  os << Bytecodes::Name(bytecode);

  Bytecodes::DispatchOnBytecode(bytecode, [&]<Bytecode bc>() {
    BytecodeOperands<bc>::ForEachOperand([&]<auto op>() {
      constexpr BytecodeOperandType type = BytecodeOperands<bc>::Type(op);
      os << ", " << BytecodeOperands<bc>::Name(op) << ": ";

      auto val = BytecodeOperands<bc>::template Get<op>(pc, no_gc);
      if constexpr (type == BytecodeOperandType::kBitTable) {
        for (int i = 0; i < BytecodeOperands<bc>::Size(op); i++) {
          os << AsHex(val[i], 2);
        }
      } else if constexpr (type == BytecodeOperandType::kChar) {
        os << AsUC32(val);
      } else if constexpr (std::is_enum_v<decltype(val)>) {
        os << val;
      } else {
        os << AsHex(val, 2);
      }
    });
  });
  os << "\n";
}

void RegExpBytecodeDisassemble(const uint8_t* code_base, uint32_t length,
                               const char* pattern) {
  RegExpBytecodeDisassemble(code_base, length, pattern, nullptr);
}

void RegExpBytecodeDisassemble(const uint8_t* code_base, uint32_t length,
                               const char* pattern,
                               BytecodeAnalysis* analysis) {
  PrintF("[generated bytecode for regexp pattern: '%s']\n", pattern);

  uint32_t offset = 0;

  // TODO(pthier): Consider using the BytecodeIterator.
  while (offset < length) {
    if (analysis) {
      uint32_t block_id = analysis->GetBlockId(offset);
      if (analysis->BlockStart(block_id) == offset) {
        analysis->PrintBlock(block_id);
      }
    }
    const uint8_t* const pc = code_base + offset;
    PrintF("%p  %4x  ", pc, offset);
    RegExpBytecodeDisassembleSingle(code_base, pc);
    offset += Bytecodes::Size(Bytecodes::FromPtr(pc));
  }
}

}  // namespace regexp
}  // namespace internal
}  // namespace v8
