// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "instruction_decoder.h"

#include "dex/dex_instruction_list.h"

#include <android-base/logging.h>

namespace titrace {

class ClassInstructionDecoder : public InstructionDecoder {
 public:
  size_t GetMaximumOpcode() override {
    return 0xff;
  }

  const char* GetName(size_t opcode) override {
    Bytecode::Opcode op = static_cast<Bytecode::Opcode>(opcode);
    return Bytecode::ToString(op);
  }

  size_t LocationToOffset(size_t j_location) override {
    return j_location;
  }

 private:
  class Bytecode {
   public:
    enum Opcode {
      // Java bytecode opcodes from 0x00 to 0xFF.
      kNop = 0x00,
      kAconst_null = 0x01,
      kIconst_m1 = 0x02,
      kIconst_0 = 0x03,
      kIconst_1 = 0x04,
      kIconst_2 = 0x05,
      kIconst_3 = 0x06,
      kIconst_4 = 0x07,
      kIconst_5 = 0x08,
      kLconst_0 = 0x09,
      kLconst_1 = 0x0a,
      kFconst_0 = 0x0b,
      kFconst_1 = 0x0c,
      kFconst_2 = 0x0d,
      kDconst_0 = 0x0e,
      kDconst_1 = 0x0f,
      kBipush = 0x10,
      kSipush = 0x11,
      kLdc = 0x12,
      kLdc_w = 0x13,
      kLdc2_w = 0x14,
      kIload = 0x15,
      kLload = 0x16,
      kFload = 0x17,
      kDload = 0x18,
      kAload = 0x19,
      kIload_0 = 0x1a,
      kIload_1 = 0x1b,
      kIload_2 = 0x1c,
      kIload_3 = 0x1d,
      kLload_0 = 0x1e,
      kLload_1 = 0x1f,
      kLload_2 = 0x20,
      kLload_3 = 0x21,
      kFload_0 = 0x22,
      kFload_1 = 0x23,
      kFload_2 = 0x24,
      kFload_3 = 0x25,
      kDload_0 = 0x26,
      kDload_1 = 0x27,
      kDload_2 = 0x28,
      kDload_3 = 0x29,
      kAload_0 = 0x2a,
      kAload_1 = 0x2b,
      kAload_2 = 0x2c,
      kAload_3 = 0x2d,
      kIaload = 0x2e,
      kLaload = 0x2f,
      kFaload = 0x30,
      kDaload = 0x31,
      kAaload = 0x32,
      kBaload = 0x33,
      kCaload = 0x34,
      kSaload = 0x35,
      kIstore = 0x36,
      kLstore = 0x37,
      kFstore = 0x38,
      kDstore = 0x39,
      kAstore = 0x3a,
      kIstore_0 = 0x3b,
      kIstore_1 = 0x3c,
      kIstore_2 = 0x3d,
      kIstore_3 = 0x3e,
      kLstore_0 = 0x3f,
      kLstore_1 = 0x40,
      kLstore_2 = 0x41,
      kLstore_3 = 0x42,
      kFstore_0 = 0x43,
      kFstore_1 = 0x44,
      kFstore_2 = 0x45,
      kFstore_3 = 0x46,
      kDstore_0 = 0x47,
      kDstore_1 = 0x48,
      kDstore_2 = 0x49,
      kDstore_3 = 0x4a,
      kAstore_0 = 0x4b,
      kAstore_1 = 0x4c,
      kAstore_2 = 0x4d,
      kAstore_3 = 0x4e,
      kIastore = 0x4f,
      kLastore = 0x50,
      kFastore = 0x51,
      kDastore = 0x52,
      kAastore = 0x53,
      kBastore = 0x54,
      kCastore = 0x55,
      kSastore = 0x56,
      kPop = 0x57,
      kPop2 = 0x58,
      kDup = 0x59,
      kDup_x1 = 0x5a,
      kDup_x2 = 0x5b,
      kDup2 = 0x5c,
      kDup2_x1 = 0x5d,
      kDup2_x2 = 0x5e,
      kSwap = 0x5f,
      kIadd = 0x60,
      kLadd = 0x61,
      kFadd = 0x62,
      kDadd = 0x63,
      kIsub = 0x64,
      kLsub = 0x65,
      kFsub = 0x66,
      kDsub = 0x67,
      kImul = 0x68,
      kLmul = 0x69,
      kFmul = 0x6a,
      kDmul = 0x6b,
      kIdiv = 0x6c,
      kLdiv = 0x6d,
      kFdiv = 0x6e,
      kDdiv = 0x6f,
      kIrem = 0x70,
      kLrem = 0x71,
      kFrem = 0x72,
      kDrem = 0x73,
      kIneg = 0x74,
      kLneg = 0x75,
      kFneg = 0x76,
      kDneg = 0x77,
      kIshl = 0x78,
      kLshl = 0x79,
      kIshr = 0x7a,
      kLshr = 0x7b,
      kIushr = 0x7c,
      kLushr = 0x7d,
      kIand = 0x7e,
      kLand = 0x7f,
      kIor = 0x80,
      kLor = 0x81,
      kIxor = 0x82,
      kLxor = 0x83,
      kIinc = 0x84,
      kI2l = 0x85,
      kI2f = 0x86,
      kI2d = 0x87,
      kL2i = 0x88,
      kL2f = 0x89,
      kL2d = 0x8a,
      kF2i = 0x8b,
      kF2l = 0x8c,
      kF2d = 0x8d,
      kD2i = 0x8e,
      kD2l = 0x8f,
      kD2f = 0x90,
      kI2b = 0x91,
      kI2c = 0x92,
      kI2s = 0x93,
      kLcmp = 0x94,
      kFcmpl = 0x95,
      kFcmpg = 0x96,
      kDcmpl = 0x97,
      kDcmpg = 0x98,
      kIfeq = 0x99,
      kIfne = 0x9a,
      kIflt = 0x9b,
      kIfge = 0x9c,
      kIfgt = 0x9d,
      kIfle = 0x9e,
      kIf_icmpeq = 0x9f,
      kIf_icmpne = 0xa0,
      kIf_icmplt = 0xa1,
      kIf_icmpge = 0xa2,
      kIf_icmpgt = 0xa3,
      kIf_icmple = 0xa4,
      kIf_acmpeq = 0xa5,
      kIf_acmpne = 0xa6,
      kGoto = 0xa7,
      kJsr = 0xa8,
      kRet = 0xa9,
      kTableswitch = 0xaa,
      kLookupswitch = 0xab,
      kIreturn = 0xac,
      kLreturn = 0xad,
      kFreturn = 0xae,
      kDreturn = 0xaf,
      kAreturn = 0xb0,
      kReturn = 0xb1,
      kGetstatic = 0xb2,
      kPutstatic = 0xb3,
      kGetfield = 0xb4,
      kPutfield = 0xb5,
      kInvokevirtual = 0xb6,
      kInvokespecial = 0xb7,
      kInvokestatic = 0xb8,
      kInvokeinterface = 0xb9,
      kInvokedynamic = 0xba,
      kNew = 0xbb,
      kNewarray = 0xbc,
      kAnewarray = 0xbd,
      kArraylength = 0xbe,
      kAthrow = 0xbf,
      kCheckcast = 0xc0,
      kInstanceof = 0xc1,
      kMonitorenter = 0xc2,
      kMonitorexit = 0xc3,
      kWide = 0xc4,
      kMultianewarray = 0xc5,
      kIfnull = 0xc6,
      kIfnonnull = 0xc7,
      kGoto_w = 0xc8,
      kJsr_w = 0xc9,
      kBreakpoint = 0xca,
      // Instructions 0xcb-0xfd are undefined.
          kImpdep1 = 0xfe,
      kImpdep2 = 0xff,
    };

    static const char* ToString(Bytecode::Opcode op) {
      switch (op) {
        case kNop: return "nop";
        case kAconst_null: return "aconst_null";
        case kIconst_m1: return "iconst_m1";
        case kIconst_0: return "iconst_0";
        case kIconst_1: return "iconst_1";
        case kIconst_2: return "iconst_2";
        case kIconst_3: return "iconst_3";
        case kIconst_4: return "iconst_4";
        case kIconst_5: return "iconst_5";
        case kLconst_0: return "lconst_0";
        case kLconst_1: return "lconst_1";
        case kFconst_0: return "fconst_0";
        case kFconst_1: return "fconst_1";
        case kFconst_2: return "fconst_2";
        case kDconst_0: return "dconst_0";
        case kDconst_1: return "dconst_1";
        case kBipush: return "bipush";
        case kSipush: return "sipush";
        case kLdc: return "ldc";
        case kLdc_w: return "ldc_w";
        case kLdc2_w: return "ldc2_w";
        case kIload: return "iload";
        case kLload: return "lload";
        case kFload: return "fload";
        case kDload: return "dload";
        case kAload: return "aload";
        case kIload_0: return "iload_0";
        case kIload_1: return "iload_1";
        case kIload_2: return "iload_2";
        case kIload_3: return "iload_3";
        case kLload_0: return "lload_0";
        case kLload_1: return "lload_1";
        case kLload_2: return "lload_2";
        case kLload_3: return "lload_3";
        case kFload_0: return "fload_0";
        case kFload_1: return "fload_1";
        case kFload_2: return "fload_2";
        case kFload_3: return "fload_3";
        case kDload_0: return "dload_0";
        case kDload_1: return "dload_1";
        case kDload_2: return "dload_2";
        case kDload_3: return "dload_3";
        case kAload_0: return "aload_0";
        case kAload_1: return "aload_1";
        case kAload_2: return "aload_2";
        case kAload_3: return "aload_3";
        case kIaload: return "iaload";
        case kLaload: return "laload";
        case kFaload: return "faload";
        case kDaload: return "daload";
        case kAaload: return "aaload";
        case kBaload: return "baload";
        case kCaload: return "caload";
        case kSaload: return "saload";
        case kIstore: return "istore";
        case kLstore: return "lstore";
        case kFstore: return "fstore";
        case kDstore: return "dstore";
        case kAstore: return "astore";
        case kIstore_0: return "istore_0";
        case kIstore_1: return "istore_1";
        case kIstore_2: return "istore_2";
        case kIstore_3: return "istore_3";
        case kLstore_0: return "lstore_0";
        case kLstore_1: return "lstore_1";
        case kLstore_2: return "lstore_2";
        case kLstore_3: return "lstore_3";
        case kFstore_0: return "fstore_0";
        case kFstore_1: return "fstore_1";
        case kFstore_2: return "fstore_2";
        case kFstore_3: return "fstore_3";
        case kDstore_0: return "dstore_0";
        case kDstore_1: return "dstore_1";
        case kDstore_2: return "dstore_2";
        case kDstore_3: return "dstore_3";
        case kAstore_0: return "astore_0";
        case kAstore_1: return "astore_1";
        case kAstore_2: return "astore_2";
        case kAstore_3: return "astore_3";
        case kIastore: return "iastore";
        case kLastore: return "lastore";
        case kFastore: return "fastore";
        case kDastore: return "dastore";
        case kAastore: return "aastore";
        case kBastore: return "bastore";
        case kCastore: return "castore";
        case kSastore: return "sastore";
        case kPop: return "pop";
        case kPop2: return "pop2";
        case kDup: return "dup";
        case kDup_x1: return "dup_x1";
        case kDup_x2: return "dup_x2";
        case kDup2: return "dup2";
        case kDup2_x1: return "dup2_x1";
        case kDup2_x2: return "dup2_x2";
        case kSwap: return "swap";
        case kIadd: return "iadd";
        case kLadd: return "ladd";
        case kFadd: return "fadd";
        case kDadd: return "dadd";
        case kIsub: return "isub";
        case kLsub: return "lsub";
        case kFsub: return "fsub";
        case kDsub: return "dsub";
        case kImul: return "imul";
        case kLmul: return "lmul";
        case kFmul: return "fmul";
        case kDmul: return "dmul";
        case kIdiv: return "idiv";
        case kLdiv: return "ldiv";
        case kFdiv: return "fdiv";
        case kDdiv: return "ddiv";
        case kIrem: return "irem";
        case kLrem: return "lrem";
        case kFrem: return "frem";
        case kDrem: return "drem";
        case kIneg: return "ineg";
        case kLneg: return "lneg";
        case kFneg: return "fneg";
        case kDneg: return "dneg";
        case kIshl: return "ishl";
        case kLshl: return "lshl";
        case kIshr: return "ishr";
        case kLshr: return "lshr";
        case kIushr: return "iushr";
        case kLushr: return "lushr";
        case kIand: return "iand";
        case kLand: return "land";
        case kIor: return "ior";
        case kLor: return "lor";
        case kIxor: return "ixor";
        case kLxor: return "lxor";
        case kIinc: return "iinc";
        case kI2l: return "i2l";
        case kI2f: return "i2f";
        case kI2d: return "i2d";
        case kL2i: return "l2i";
        case kL2f: return "l2f";
        case kL2d: return "l2d";
        case kF2i: return "f2i";
        case kF2l: return "f2l";
        case kF2d: return "f2d";
        case kD2i: return "d2i";
        case kD2l: return "d2l";
        case kD2f: return "d2f";
        case kI2b: return "i2b";
        case kI2c: return "i2c";
        case kI2s: return "i2s";
        case kLcmp: return "lcmp";
        case kFcmpl: return "fcmpl";
        case kFcmpg: return "fcmpg";
        case kDcmpl: return "dcmpl";
        case kDcmpg: return "dcmpg";
        case kIfeq: return "ifeq";
        case kIfne: return "ifne";
        case kIflt: return "iflt";
        case kIfge: return "ifge";
        case kIfgt: return "ifgt";
        case kIfle: return "ifle";
        case kIf_icmpeq: return "if_icmpeq";
        case kIf_icmpne: return "if_icmpne";
        case kIf_icmplt: return "if_icmplt";
        case kIf_icmpge: return "if_icmpge";
        case kIf_icmpgt: return "if_icmpgt";
        case kIf_icmple: return "if_icmple";
        case kIf_acmpeq: return "if_acmpeq";
        case kIf_acmpne: return "if_acmpne";
        case kGoto: return "goto";
        case kJsr: return "jsr";
        case kRet: return "ret";
        case kTableswitch: return "tableswitch";
        case kLookupswitch: return "lookupswitch";
        case kIreturn: return "ireturn";
        case kLreturn: return "lreturn";
        case kFreturn: return "freturn";
        case kDreturn: return "dreturn";
        case kAreturn: return "areturn";
        case kReturn: return "return";
        case kGetstatic: return "getstatic";
        case kPutstatic: return "putstatic";
        case kGetfield: return "getfield";
        case kPutfield: return "putfield";
        case kInvokevirtual: return "invokevirtual";
        case kInvokespecial: return "invokespecial";
        case kInvokestatic: return "invokestatic";
        case kInvokeinterface: return "invokeinterface";
        case kInvokedynamic: return "invokedynamic";
        case kNew: return "new";
        case kNewarray: return "newarray";
        case kAnewarray: return "anewarray";
        case kArraylength: return "arraylength";
        case kAthrow: return "athrow";
        case kCheckcast: return "checkcast";
        case kInstanceof: return "instanceof";
        case kMonitorenter: return "monitorenter";
        case kMonitorexit: return "monitorexit";
        case kWide: return "wide";
        case kMultianewarray: return "multianewarray";
        case kIfnull: return "ifnull";
        case kIfnonnull: return "ifnonnull";
        case kGoto_w: return "goto_w";
        case kJsr_w: return "jsr_w";
        case kBreakpoint: return "breakpoint";
        case kImpdep1: return "impdep1";
        case kImpdep2: return "impdep2";
        default:
          LOG(FATAL) << "Unknown opcode " << op;
          __builtin_unreachable();
       }
     }
  };
};

class DexInstructionDecoder : public InstructionDecoder {
 public:
  size_t GetMaximumOpcode() override {
    return 0xff;
  }

  const char* GetName(size_t opcode) override {
    Bytecode::Opcode op = static_cast<Bytecode::Opcode>(opcode);
    return Bytecode::ToString(op);
  }

  size_t LocationToOffset(size_t j_location) override {
    // dex pc is uint16_t*, but offset needs to be in bytes.
    return j_location * (sizeof(uint16_t) / sizeof(uint8_t));
  }

 private:
  class Bytecode {
   public:
    enum Opcode {
#define MAKE_ENUM_DEFINITION(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags) \
      instruction_code = opcode,  /* NOLINT */
DEX_INSTRUCTION_LIST(MAKE_ENUM_DEFINITION)
#undef MAKE_ENUM_DEFINITION
    };

    static_assert(static_cast<uint32_t>(Bytecode::Opcode::NOP) == 0, "");
    static_assert(static_cast<uint32_t>(Bytecode::Opcode::MOVE) == 1, "");

    static const char* ToString(Bytecode::Opcode op) {
      switch (op) {
#define MAKE_ENUM_DEFINITION(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags) \
        case instruction_code: return (name);
DEX_INSTRUCTION_LIST(MAKE_ENUM_DEFINITION)
#undef MAKE_ENUM_DEFINITION
        default: LOG(FATAL) << "Unknown opcode " << op;
      }
      __builtin_unreachable();
    }
  };
};

InstructionDecoder* InstructionDecoder::NewInstance(InstructionFileFormat file_format) {
  switch (file_format) {
    case InstructionFileFormat::kClass:
      return new ClassInstructionDecoder();
    case InstructionFileFormat::kDex:
      return new DexInstructionDecoder();
    default:
      return nullptr;
  }
}
}  // namespace titrace
