/*
 * 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.
 */

#pragma once

#include "dex_format.h"

#include <stddef.h>

// .dex bytecode definitions and helpers:
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

namespace dex {

// The number of Dalvik opcodes
constexpr size_t kNumPackedOpcodes = 0x100;

// Switch table and array data signatures are a code unit consisting
// of "NOP" (0x00) in the low-order byte and a non-zero identifying
// code in the high-order byte. (A true NOP is 0x0000.)
constexpr u2 kPackedSwitchSignature = 0x0100;
constexpr u2 kSparseSwitchSignature = 0x0200;
constexpr u2 kArrayDataSignature = 0x0300;

// Enumeration of all Dalvik opcodes
enum Opcode : u1 {
#define INSTRUCTION_ENUM(opcode, cname, ...) OP_##cname = (opcode),
#include "dex_instruction_list.h"
  DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_ENUM
};

// Instruction formats associated with Dalvik opcodes
enum InstructionFormat : u1 {
  k10x,   // op
  k12x,   // op vA, vB
  k11n,   // op vA, #+B
  k11x,   // op vAA
  k10t,   // op +AA
  k20t,   // op +AAAA
  k20bc,  // [opt] op AA, thing@BBBB
  k22x,   // op vAA, vBBBB
  k21t,   // op vAA, +BBBB
  k21s,   // op vAA, #+BBBB
  k21h,   // op vAA, #+BBBB00000[00000000]
  k21c,   // op vAA, thing@BBBB
  k23x,   // op vAA, vBB, vCC
  k22b,   // op vAA, vBB, #+CC
  k22t,   // op vA, vB, +CCCC
  k22s,   // op vA, vB, #+CCCC
  k22c,   // op vA, vB, thing@CCCC
  k22cs,  // [opt] op vA, vB, field offset CCCC
  k30t,   // op +AAAAAAAA
  k32x,   // op vAAAA, vBBBB
  k31i,   // op vAA, #+BBBBBBBB
  k31t,   // op vAA, +BBBBBBBB
  k31c,   // op vAA, string@BBBBBBBB
  k35c,   // op {vC,vD,vE,vF,vG}, thing@BBBB
  k35ms,  // [opt] invoke-virtual+super
  k3rc,   // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
  k3rms,  // [opt] invoke-virtual+super/range
  k35mi,  // [opt] inline invoke
  k3rmi,  // [opt] inline invoke/range
  k45cc,  // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH
  k4rcc,  // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH
  k51l,   // op vAA, #+BBBBBBBBBBBBBBBB
};

using OpcodeFlags = u1;
enum : OpcodeFlags {
  kBranch = 0x01,         // conditional or unconditional branch
  kContinue = 0x02,       // flow can continue to next statement
  kSwitch = 0x04,         // switch statement
  kThrow = 0x08,          // could cause an exception to be thrown
  kReturn = 0x10,         // returns, no additional statements
  kInvoke = 0x20,         // a flavor of invoke
  kUnconditional = 0x40,  // unconditional branch
  kExperimental = 0x80,   // is an experimental opcode
};

using VerifyFlags = u4;
enum : VerifyFlags {
  kVerifyNothing = 0x0000000,
  kVerifyRegA = 0x0000001,
  kVerifyRegAWide = 0x0000002,
  kVerifyRegB = 0x0000004,
  kVerifyRegBField = 0x0000008,
  kVerifyRegBMethod = 0x0000010,
  kVerifyRegBNewInstance = 0x0000020,
  kVerifyRegBString = 0x0000040,
  kVerifyRegBType = 0x0000080,
  kVerifyRegBWide = 0x0000100,
  kVerifyRegC = 0x0000200,
  kVerifyRegCField = 0x0000400,
  kVerifyRegCNewArray = 0x0000800,
  kVerifyRegCType = 0x0001000,
  kVerifyRegCWide = 0x0002000,
  kVerifyArrayData = 0x0004000,
  kVerifyBranchTarget = 0x0008000,
  kVerifySwitchTargets = 0x0010000,
  kVerifyVarArg = 0x0020000,
  kVerifyVarArgNonZero = 0x0040000,
  kVerifyVarArgRange = 0x0080000,
  kVerifyVarArgRangeNonZero = 0x0100000,
  kVerifyRuntimeOnly = 0x0200000,
  kVerifyError = 0x0400000,
  kVerifyRegHPrototype = 0x0800000,
  kVerifyRegBCallSite = 0x1000000,
  kVerifyRegBMethodHandle = 0x2000000,
  kVerifyRegBPrototype = 0x4000000,
};

// Types of indexed reference that are associated with opcodes whose
// formats include such an indexed reference (e.g., 21c and 35c).
enum InstructionIndexType : u1 {
  kIndexUnknown = 0,
  kIndexNone,               // has no index
  kIndexVaries,             // "It depends." Used for throw-verification-error
  kIndexTypeRef,            // type reference index
  kIndexStringRef,          // string reference index
  kIndexMethodRef,          // method reference index
  kIndexFieldRef,           // field reference index
  kIndexInlineMethod,       // inline method index (for inline linked methods)
  kIndexVtableOffset,       // vtable offset (for static linked methods)
  kIndexFieldOffset,        // field offset (for static linked fields)
  kIndexMethodAndProtoRef,  // method index and proto index
  kIndexCallSiteRef,        // call site index
  kIndexMethodHandleRef,    // constant method handle reference index
  kIndexProtoRef,           // constant prototype reference index
};

// Holds the contents of a decoded instruction.
struct Instruction {
  u4 vA;          // the A field of the instruction
  u4 vB;          // the B field of the instruction
  u8 vB_wide;     // 64bit version of the B field (for k51l)
  u4 vC;          // the C field of the instruction
  u4 arg[5];      // vC/D/E/F/G in invoke or filled-new-array
  Opcode opcode;  // instruction opcode
};

// "packed-switch-payload" format
struct PackedSwitchPayload {
  u2 ident;
  u2 size;
  s4 first_key;
  s4 targets[];
};

// "sparse-switch-payload" format
struct SparseSwitchPayload {
  u2 ident;
  u2 size;
  s4 data[];
};

// "fill-array-data-payload" format
struct ArrayData {
  u2 ident;
  u2 element_width;
  u4 size;
  u1 data[];
};

// Collect the enums in a struct for better locality.
struct InstructionDescriptor {
  u4 verify_flags;  // Set of VerifyFlag.
  InstructionFormat format;
  InstructionIndexType index_type;
  u1 flags;  // Set of Flags.
};

// Extracts the opcode from a Dalvik code unit (bytecode)
Opcode OpcodeFromBytecode(u2 bytecode);

// Returns the name of an opcode
const char* GetOpcodeName(Opcode opcode);

// Returns the index type associated with the specified opcode
InstructionIndexType GetIndexTypeFromOpcode(Opcode opcode);

// Returns the format associated with the specified opcode
InstructionFormat GetFormatFromOpcode(Opcode opcode);

// Returns the flags for the specified opcode
OpcodeFlags GetFlagsFromOpcode(Opcode opcode);

// Returns the verify flags for the specified opcode
VerifyFlags GetVerifyFlagsFromOpcode(Opcode opcode);

// Returns the instruction width for the specified opcode format
size_t GetWidthFromFormat(InstructionFormat format);

// Return the width of the specified instruction, or 0 if not defined.  Also
// works for special OP_NOP entries, including switch statement data tables
// and array data.
size_t GetWidthFromBytecode(const u2* bytecode);

// Decode a .dex bytecode
Instruction DecodeInstruction(const u2* bytecode);

}  // namespace dex
