6761 lines
253 KiB
Rust
6761 lines
253 KiB
Rust
//! Representation of the instructions.
|
|
//!
|
|
//! Instruction at a sligthly higher level than
|
|
//! <https://source.android.com/docs/core/runtime/dalvik-bytecode>
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::hashmap_vectorize;
|
|
use crate::{
|
|
DexString, DexValue, FieldIdCollector, IdField, IdMethod, IdMethodType, IdType, MethodHandle,
|
|
MethodHandleCollector, MethodIdCollector, MethodTypeCollector, Result, StringCollector,
|
|
TypeCollector, Visitable, VisitableMut, Visitor, VisitorMut,
|
|
};
|
|
use androscalpel_serializer::Instruction as InsFormat;
|
|
use androscalpel_serializer::Serializable;
|
|
|
|
use anyhow::{anyhow, bail};
|
|
use pyo3::prelude::*;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
const I8_MIN_AS_I16: i16 = i8::MIN as i16;
|
|
const I8_MAX_AS_I16: i16 = i8::MAX as i16;
|
|
const I8_MIN_AS_I32: i32 = i8::MIN as i32;
|
|
const I8_MAX_AS_I32: i32 = i8::MAX as i32;
|
|
const I16_MIN_AS_I32: i32 = i16::MIN as i32;
|
|
const I16_MAX_AS_I32: i32 = i16::MAX as i32;
|
|
const I16_MIN_AS_I64: i64 = i16::MIN as i64;
|
|
const I16_MAX_AS_I64: i64 = i16::MAX as i64;
|
|
const I32_MIN_AS_I64: i64 = i32::MIN as i64;
|
|
const I32_MAX_AS_I64: i64 = i32::MAX as i64;
|
|
const U16_MAX_AS_USIZE: usize = u16::MAX as usize;
|
|
|
|
#[pyclass(eq)]
|
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
pub enum Instruction {
|
|
/// Waste a cycle.
|
|
Nop {},
|
|
/// Move contents of a non object register to another.
|
|
/// Can represent several dalvik instructions :
|
|
/// move vA, vB
|
|
/// move/from16 vAA, vBBBB
|
|
/// move/16 vAAAA, vBBBB
|
|
Move { from: u16, to: u16 },
|
|
/// Move contents of a register pair to another.
|
|
/// Can represent several dalvik instructions :
|
|
/// move-wide vA, vB
|
|
/// move-wide/from16 vAA, vBBBB
|
|
/// move-wide/16 vAAAA, vBBBB
|
|
MoveWide { from: u16, to: u16 },
|
|
/// Move contents of an object bearing register to another.
|
|
/// Can represent several dalvik instructions :
|
|
/// move-object vA, vB
|
|
/// move-object/from16 vAA, vBBBB
|
|
/// move-object/16 vAAAA, vBBBB
|
|
MoveObject { from: u16, to: u16 },
|
|
/// Move the single word non object result of the preciding invoke-kind into a register.
|
|
MoveResult { to: u8 },
|
|
/// Move the double word non object result of the preciding invoke-kind into a register pair.
|
|
MoveResultWide { to: u8 },
|
|
/// Move the just caught exception into a register.
|
|
MoveException { to: u8 },
|
|
/// Move the object result of the preciding invoke-kind or filled-new-array into a register.
|
|
MoveResultObject { to: u8 },
|
|
/// Return a void method
|
|
ReturnVoid {},
|
|
/// Return the 32 bits non object value from the method.
|
|
Return { reg: u8 },
|
|
/// Return the 64 bits non object value from the method.
|
|
ReturnWide { reg: u8 },
|
|
/// Return the object value from the method.
|
|
ReturnObject { reg: u8 },
|
|
/// Move a literal to a register.
|
|
/// Can represent several dalvik instructions :
|
|
/// const/4 vA #+B
|
|
/// const/16 vAA #+BBBB
|
|
/// const vAA #+BBBBBBBB
|
|
/// const/high 16 vAA #+BBBB0000
|
|
Const { reg: u8, lit: i32 },
|
|
/// Move a literal to a register pair (64bits).
|
|
/// Can represent several dalvik instructions :
|
|
/// const-wide/16 vAA #+BBBB
|
|
/// const-wide/32 vAA #+BBBBBBBB
|
|
/// const-wide vAA #+BBBBBBBBBBBBBBBB
|
|
/// const-wide/hight 16 vAA #+BBBB000000000000
|
|
ConstWide { reg: u8, lit: i64 },
|
|
/// Move a reference to a string in a register.
|
|
/// Can represent several dalvik instructions :
|
|
/// const-string vAA string@BBBB
|
|
/// const-string/jumbo vAA string@BBBBBBBB
|
|
ConstString { reg: u8, lit: DexString },
|
|
/// Move a reference to a class in a register.
|
|
ConstClass { reg: u8, lit: IdType },
|
|
/// Acquire the monitor for the object in the register.
|
|
MonitorEnter { reg: u8 },
|
|
/// Release the monitor for the object in the register.
|
|
MonitorExit { reg: u8 },
|
|
/// Check if the object in the register can be cast to the type.
|
|
/// (Raise a `ClassCastException` if not)
|
|
CheckCast { reg: u8, lit: IdType },
|
|
/// Check if an object if an instance of a type.
|
|
/// (put 1 in the dest register if yes, else 0)
|
|
InstanceOf { dest: u8, obj: u8, lit: IdType },
|
|
/// Get the number of item in an array.
|
|
/// (put the lenght in the dest register)
|
|
ArrayLength { dest: u8, arr: u8 },
|
|
/// Construct a new instance of the indicated type and store a reference to it.
|
|
NewInstance { reg: u8, lit: IdType },
|
|
/// Construct a new array of the indicated type and size in size_reg and store a reference to it.
|
|
NewArray { reg: u8, size_reg: u8, lit: IdType },
|
|
/// Construct a new array of the indicated type and size fill it with the values of the
|
|
/// given registers.
|
|
///
|
|
/// The elements of the array must fit in 32bits.
|
|
///
|
|
/// The registers must either be less to 5 and their number must fit on 4 bits each, or
|
|
/// be less than 256, their the number of the smallest must fit on 16 bits and they must
|
|
/// be sorted an directly adjascent.
|
|
///
|
|
/// The newly created array can be retreived with a move-result-object instruction.
|
|
FilledNewArray { type_: IdType, reg_values: Vec<u16> },
|
|
FillArrayData {
|
|
arr: u8,
|
|
elt_width: u16,
|
|
data: Vec<u8>,
|
|
},
|
|
/// Throws the exception in the register.
|
|
Throw { reg: u8 },
|
|
/// Jump to the label.
|
|
Goto { label: String },
|
|
/// Jump to a label depending on the value of a register. If the value
|
|
/// is not matched, continue the extecution at the next instruction.
|
|
Switch {
|
|
reg: u8,
|
|
#[serde(with = "hashmap_vectorize")]
|
|
branches: HashMap<i32, String>,
|
|
},
|
|
/// Store the result of the comparison between the registers.
|
|
///
|
|
/// - b < c: a = -1
|
|
/// - b == c: a = 0
|
|
/// - b > c : a = 1
|
|
/// - b == c == Nan: a = -1
|
|
CmpLFloat { dest: u8, b: u8, c: u8 },
|
|
/// Store the result of the comparison between the registers.
|
|
///
|
|
/// - b < c: a = -1
|
|
/// - b == c: a = 0
|
|
/// - b > c : a = 1
|
|
/// - b == c == Nan: a = -1
|
|
CmpGFloat { dest: u8, b: u8, c: u8 },
|
|
/// Store the result of the comparison between the registers.
|
|
///
|
|
/// - b < c: a = -1
|
|
/// - b == c: a = 0
|
|
/// - b > c : a = 1
|
|
/// - b == c == Nan: a = -1
|
|
CmpLDouble { dest: u8, b: u8, c: u8 },
|
|
/// Store the result of the comparison between the registers.
|
|
///
|
|
/// - b < c: a = -1
|
|
/// - b == c: a = 0
|
|
/// - b > c : a = 1
|
|
/// - b == c == Nan: a = -1
|
|
CmpGDouble { dest: u8, b: u8, c: u8 },
|
|
/// Store the result of the comparison between the registers.
|
|
///
|
|
/// - b < c: a = -1
|
|
/// - b == c: a = 0
|
|
/// - b > c : a = 1
|
|
/// - b == c == Nan: a = -1
|
|
CmpLong { dest: u8, b: u8, c: u8 },
|
|
/// Jump to the label if a == b
|
|
IfEq { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a != b
|
|
IfNe { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a < b
|
|
IfLt { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a >= b
|
|
IfGe { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a > b
|
|
IfGt { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a <= b
|
|
IfLe { a: u8, b: u8, label: String },
|
|
/// Jump to the label if a == 0
|
|
IfEqZ { a: u8, label: String },
|
|
/// Jump to the label if a != 0
|
|
IfNeZ { a: u8, label: String },
|
|
/// Jump to the label if a < 0
|
|
IfLtZ { a: u8, label: String },
|
|
/// Jump to the label if a >= 0
|
|
IfGeZ { a: u8, label: String },
|
|
/// Jump to the label if a > 0
|
|
IfGtZ { a: u8, label: String },
|
|
/// Jump to the label if a <= 0
|
|
IfLeZ { a: u8, label: String },
|
|
/// Put the value at `arr[idx]` in register dest (all values are in registers)
|
|
AGet { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the value at `arr[idx]` in register pair dest (all values are in registers)
|
|
AGetWide { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the reference at `arr[idx]` in register (all values are in registers)
|
|
AGetObject { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the boolean at `arr[idx]` in register dest (all values are in registers)
|
|
AGetBoolean { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the byte at `arr[idx]` in register dest (all values are in registers)
|
|
AGetByte { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the char at `arr[idx]` in register dest (all values are in registers)
|
|
AGetChar { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the short at `arr[idx]` in register dest (all values are in registers)
|
|
AGetShort { dest: u8, arr: u8, idx: u8 },
|
|
/// Put the value of register 'from' in `arr[idx]` (all values are in registers)
|
|
APut { from: u8, arr: u8, idx: u8 },
|
|
/// Put the value of the register pair 'from' in `arr[idx]` (all values are in registers)
|
|
APutWide { from: u8, arr: u8, idx: u8 },
|
|
/// Put the object reference in 'from' in `arr[idx]` (all values are in registers)
|
|
APutObject { from: u8, arr: u8, idx: u8 },
|
|
/// Put the boolean in 'from' in `arr[idx]` (all values are in registers)
|
|
APutBoolean { from: u8, arr: u8, idx: u8 },
|
|
/// Put the byte in 'from' in `arr[idx]` (all values are in registers)
|
|
APutByte { from: u8, arr: u8, idx: u8 },
|
|
/// Put the char in 'from' in `arr[idx]` (all values are in registers)
|
|
APutChar { from: u8, arr: u8, idx: u8 },
|
|
/// Put the short in 'from' in `arr[idx]` (all values are in registers)
|
|
APutShort { from: u8, arr: u8, idx: u8 },
|
|
/// Put the value in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGet { to: u8, obj: u8, field: IdField },
|
|
/// Put the value in the register pair 'to' in the instance field 'field' of 'obj' ('to'
|
|
/// and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetWide { to: u8, obj: u8, field: IdField },
|
|
/// Put the object reference 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetObject { to: u8, obj: u8, field: IdField },
|
|
/// Put the boolean in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetBoolean { to: u8, obj: u8, field: IdField },
|
|
/// Put the byte in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetByte { to: u8, obj: u8, field: IdField },
|
|
/// Put the char in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetChar { to: u8, obj: u8, field: IdField },
|
|
/// Put the short in 'to' in the instance field 'field' of 'obj' ('to' and 'obj' are register)
|
|
/// The registers 'to' and 'obj' are idexed on 4 bits.
|
|
IGetShort { to: u8, obj: u8, field: IdField },
|
|
/// Put the value in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPut { from: u8, obj: u8, field: IdField },
|
|
/// Put the value in the instance field 'field' of 'obj' in the register pair 'from'
|
|
/// ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutWide { from: u8, obj: u8, field: IdField },
|
|
/// Put the object reference in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutObject { from: u8, obj: u8, field: IdField },
|
|
/// Put the boolean in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutBoolean { from: u8, obj: u8, field: IdField },
|
|
/// Put the byte in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutByte { from: u8, obj: u8, field: IdField },
|
|
/// Put the char in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutChar { from: u8, obj: u8, field: IdField },
|
|
/// Put the short in the instance field 'field' of 'obj' in 'from' ('from' and 'obj' are register)
|
|
/// The registers 'from' and 'obj' are idexed on 4 bits.
|
|
IPutShort { from: u8, obj: u8, field: IdField },
|
|
/// Put the value in 'to' in the static field 'field' ('to' is a register)
|
|
SGet { to: u8, field: IdField },
|
|
/// Put the value in the register pair 'to' in the static field 'field' ('to' is a register)
|
|
SGetWide { to: u8, field: IdField },
|
|
/// Put the object reference 'to' in the static field 'field' ('to' is a register)
|
|
SGetObject { to: u8, field: IdField },
|
|
/// Put the boolean in 'to' in the static field 'field' ('to' is a register)
|
|
SGetBoolean { to: u8, field: IdField },
|
|
/// Put the byte in 'to' in the static field 'field' ('to' is a register)
|
|
SGetByte { to: u8, field: IdField },
|
|
/// Put the char in 'to' in the static field 'field' ('to' is a register)
|
|
SGetChar { to: u8, field: IdField },
|
|
/// Put the short in 'to' in the static field 'field' ('to' is a register)
|
|
SGetShort { to: u8, field: IdField },
|
|
/// Put the value in the static field 'field' in 'from' ('from' is a register)
|
|
SPut { from: u8, field: IdField },
|
|
/// Put the value in the static field 'field' in the register pair 'from'
|
|
/// ('from' is a register)
|
|
SPutWide { from: u8, field: IdField },
|
|
/// Put the object reference in the static field 'field' in 'from' ('from' is a register)
|
|
SPutObject { from: u8, field: IdField },
|
|
/// Put the boolean in the static field 'field' in 'from' ('from' is a register)
|
|
SPutBoolean { from: u8, field: IdField },
|
|
/// Put the byte in the static field 'field' in 'from' ('from' is a register)
|
|
SPutByte { from: u8, field: IdField },
|
|
/// Put the char in the static field 'field' in 'from' ('from' is a register)
|
|
SPutChar { from: u8, field: IdField },
|
|
/// Put the short in the static field 'field' in 'from' ('from' is a register)
|
|
SPutShort { from: u8, field: IdField },
|
|
/// Call a normal virtual method.
|
|
InvokeVirtual { method: IdMethod, args: Vec<u16> },
|
|
/// Call the closest superclass's virtual method of a non interface class.
|
|
InvokeSuper { method: IdMethod, args: Vec<u16> },
|
|
/// Call a direct method (non static non overridable, like private).
|
|
InvokeDirect { method: IdMethod, args: Vec<u16> },
|
|
/// Call a static method.
|
|
InvokeStatic { method: IdMethod, args: Vec<u16> },
|
|
/// Call a interface method (method from an interface on an object whose class is unknown)
|
|
InvokeInterface { method: IdMethod, args: Vec<u16> },
|
|
/// Put -val in dest.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NegInt { dest: u8, val: u8 },
|
|
/// Put ~val in dest
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NotInt { dest: u8, val: u8 },
|
|
/// Put -val in dest. dest and val are pair registers.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NegLong { dest: u8, val: u8 },
|
|
/// Put ~val in dest. dest and val are pair registers.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NotLong { dest: u8, val: u8 },
|
|
/// Put -val in dest.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NegFloat { dest: u8, val: u8 },
|
|
/// Put -val in dest.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
NegDouble { dest: u8, val: u8 },
|
|
/// Convert copy val to dest an convert to long.
|
|
/// TODO: dest is probably a pair register, but is val to?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToLong { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the integer value to a float.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToFloat { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the integer value to a double
|
|
/// TODO: dest is probably a pair register, but is val to?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToDouble { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the long value to an integer.
|
|
/// TODO: val is probably a pair register, but is dest to?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
LongToInt { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the long value to a float.
|
|
/// TODO: val is probably a pair register, but is dest to?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
LongToFloat { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the long value to a double.
|
|
/// dest and val are pair registers.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
LongToDouble { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the float value to an integer.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
FloatToInt { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the float value to a long.
|
|
/// TODO: dest is probably a pair register, but is val too?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
FloatToLong { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the float value to a double.
|
|
/// TODO: dest is probably a pair register, but is val too?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
FloatToDouble { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the double value to an integer.
|
|
/// TODO: val is probably a pair register, but is dest too?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
DoubleToInt { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the double value to a long.
|
|
/// val and dest are pair registers.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
DoubleToLong { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the double value to a float.
|
|
/// TODO: val is probably a pair register, but is dest too?
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
DoubleToFloat { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the integer value to a byte.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToByte { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the integer value to a char.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToChar { dest: u8, val: u8 },
|
|
/// Copy val to dest and convert the integer value to a short.
|
|
/// `dest` and `val` are registered indexed on 4 bits.
|
|
IntToShort { dest: u8, val: u8 },
|
|
/// Put a + b in dest.
|
|
AddInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a - b in dest.
|
|
SubInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a * b in dest.
|
|
MulInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a / b in dest.
|
|
DivInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a % b in dest.
|
|
RemInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a & b in dest.
|
|
AndInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a | b in dest.
|
|
OrInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a ^ b in dest.
|
|
XorInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a << b in dest.
|
|
ShlInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a >> b (signed) in dest.
|
|
ShrInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a >> b in dest.
|
|
UshrInt { dest: u8, b: u8, c: u8 },
|
|
/// Put a + b in dest.
|
|
/// dest, b and c are register pairs
|
|
AddLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a - b in dest.
|
|
/// dest, b and c are register pairs
|
|
SubLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a * b in dest.
|
|
/// dest, b and c are register pairs
|
|
MulLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a / b in dest.
|
|
/// dest, b and c are register pairs
|
|
DivLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a % b in dest.
|
|
/// dest, b and c are register pairs
|
|
RemLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a & b in dest.
|
|
/// dest, b and c are register pairs
|
|
AndLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a | b in dest.
|
|
/// dest, b and c are register pairs
|
|
OrLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a ^ b in dest.
|
|
/// dest, b and c are register pairs
|
|
XorLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a << b in dest.
|
|
/// dest, and b are register pairs, and c is a single register
|
|
ShlLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a >> b (signed) in dest.
|
|
/// dest, and b are register pairs, and c is a single register
|
|
ShrLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a >> b in dest.
|
|
/// dest, and b are register pairs, and c is a single register
|
|
UshrLong { dest: u8, b: u8, c: u8 },
|
|
/// Put a + b in dest.
|
|
AddFloat { dest: u8, b: u8, c: u8 },
|
|
/// Put a - b in dest.
|
|
SubFloat { dest: u8, b: u8, c: u8 },
|
|
/// Put a * b in dest.
|
|
MulFloat { dest: u8, b: u8, c: u8 },
|
|
/// Put a / b in dest.
|
|
DivFloat { dest: u8, b: u8, c: u8 },
|
|
/// Put a % b in dest.
|
|
RemFloat { dest: u8, b: u8, c: u8 },
|
|
/// Put a + b in dest.
|
|
/// dest, b and c are register pairs
|
|
AddDouble { dest: u8, b: u8, c: u8 },
|
|
/// Put a - b in dest.
|
|
/// dest, b and c are register pairs
|
|
SubDouble { dest: u8, b: u8, c: u8 },
|
|
/// Put a * b in dest.
|
|
/// dest, b and c are register pairs
|
|
MulDouble { dest: u8, b: u8, c: u8 },
|
|
/// Put a / b in dest.
|
|
/// dest, b and c are register pairs
|
|
DivDouble { dest: u8, b: u8, c: u8 },
|
|
/// Put a % b in dest.
|
|
/// dest, b and c are register pairs
|
|
RemDouble { dest: u8, b: u8, c: u8 },
|
|
/// Put dest + b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AddInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest - b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
SubInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest * b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
MulInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest / b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
DivInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest % b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
RemInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest & b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AndInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest | b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
OrInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest ^ b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
XorInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest << b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
ShlInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest >> b (signed) in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
ShrInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest >> b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
UshrInt2Addr { dest: u8, b: u8 },
|
|
/// Put dest + b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AddLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest - b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
SubLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest * b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
MulLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest / b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
DivLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest % b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
RemLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest & b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AndLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest | b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
OrLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest ^ b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
XorLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest << b in dest.
|
|
/// dest is a register pair, and b is a single register
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
ShlLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest >> b (signed) in dest.
|
|
/// dest is a register pair, and b is a single register
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
ShrLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest >> b in dest.
|
|
/// dest is a register pair, and b is a single register
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
UshrLong2Addr { dest: u8, b: u8 },
|
|
/// Put dest + b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AddFloat2Addr { dest: u8, b: u8 },
|
|
/// Put dest - b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
SubFloat2Addr { dest: u8, b: u8 },
|
|
/// Put dest * b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
MulFloat2Addr { dest: u8, b: u8 },
|
|
/// Put dest / b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
DivFloat2Addr { dest: u8, b: u8 },
|
|
/// Put dest % b in dest.
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
RemFloat2Addr { dest: u8, b: u8 },
|
|
/// Put dest + b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
AddDouble2Addr { dest: u8, b: u8 },
|
|
/// Put dest - b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
SubDouble2Addr { dest: u8, b: u8 },
|
|
/// Put dest * b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
MulDouble2Addr { dest: u8, b: u8 },
|
|
/// Put dest / b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
DivDouble2Addr { dest: u8, b: u8 },
|
|
/// Put dest % b in dest.
|
|
/// dest and b are register pairs
|
|
/// `dest` and `b` are registers indexed on 4 bits
|
|
RemDouble2Addr { dest: u8, b: u8 },
|
|
/// Put b + lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
AddIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put lit - b in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
RsubIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b * lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
MulIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b / lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
DivIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b % lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
RemIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b & lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
AndIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b | lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
OrIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b ^ lit in dest.
|
|
/// Either `dest` and `b` are registers indexed on 4 bits and lit is encoded in 16 bits
|
|
/// or `dest` and `b` are registers indexed on 8 bits and lit is encoded in 8 bits
|
|
XorIntLit { dest: u8, b: u8, lit: i16 },
|
|
/// Put b << lit in dest.
|
|
ShlIntLit { dest: u8, b: u8, lit: i8 },
|
|
/// Put b >> lit (signed) in dest.
|
|
ShrIntLit { dest: u8, b: u8, lit: i8 },
|
|
/// Put b >> lit in dest.
|
|
UshrIntLit { dest: u8, b: u8, lit: i8 },
|
|
/// Call a polymorphic method.
|
|
InvokePolymorphic {
|
|
method: IdMethod,
|
|
proto: IdMethodType,
|
|
args: Vec<u16>,
|
|
},
|
|
/// Invoke a method from a call site.
|
|
InvokeCustom { call_site: CallSite, args: Vec<u16> },
|
|
/// Put contents reference to the method handle in the register.
|
|
ConstMethodHandle { handle: MethodHandle, to: u8 },
|
|
/// Put contents reference to the prototype in the register.
|
|
ConstMethodType { proto: IdMethodType, to: u8 },
|
|
/// Try block. It does not match an dalvik instruction but is derived from the code item struct.
|
|
Try {
|
|
end_label: String,
|
|
/// The list of exceptions and their associated handler label.
|
|
///
|
|
///
|
|
/// The handler are sorted: if severat Exception Type match an exceptions, the
|
|
/// the handler used is the first in the list.
|
|
handlers: Vec<(IdType, String)>,
|
|
default_handler: Option<String>,
|
|
},
|
|
/// Label marker. It does not match an dalvik instruction, but it's use as a marker for a
|
|
/// jump destination.
|
|
Label { name: String },
|
|
/// Debug information. Define a local variable associated with a register.
|
|
DebugLocal {
|
|
reg: u32,
|
|
name: Option<String>,
|
|
type_: Option<IdType>,
|
|
signature: Option<String>,
|
|
},
|
|
/// Debug information. Undefine a local variable associated with a register.
|
|
DebugEndLocal { reg: u32 },
|
|
/// Debug information. Indicate the end of the prologue.
|
|
DebugEndPrologue {},
|
|
/// Debug information. Indicate the beginning of the Epilogue
|
|
DebugBeginEpilogue {},
|
|
/// Debug information. Indicate the source file of the following instructions.
|
|
DebugSourceFile { file: Option<String> },
|
|
/// Debug information. Indicate the line number of the following instructions.
|
|
DebugLine { number: usize },
|
|
}
|
|
|
|
impl<V: Visitor> Visitable<V> for Instruction {
|
|
fn default_visit(&self, v: &mut V) -> Result<()> {
|
|
// TODO wildcard?
|
|
match self {
|
|
Self::Nop {} => Ok(()),
|
|
Self::Move { from: _, to: _ } => Ok(()),
|
|
Self::MoveWide { from: _, to: _ } => Ok(()),
|
|
Self::MoveObject { from: _, to: _ } => Ok(()),
|
|
Self::MoveResult { to: _ } => Ok(()),
|
|
Self::MoveResultWide { to: _ } => Ok(()),
|
|
Self::MoveException { to: _ } => Ok(()),
|
|
Self::MoveResultObject { to: _ } => Ok(()),
|
|
Self::ReturnVoid {} => Ok(()),
|
|
Self::Return { reg: _ } => Ok(()),
|
|
Self::ReturnWide { reg: _ } => Ok(()),
|
|
Self::ReturnObject { reg: _ } => Ok(()),
|
|
Self::Const { reg: _, lit: _ } => Ok(()),
|
|
Self::ConstWide { reg: _, lit: _ } => Ok(()),
|
|
Self::ConstString { reg: _, lit } => v.visit_string(lit),
|
|
Self::ConstClass { reg: _, lit } => v.visit_type(lit),
|
|
Self::MonitorEnter { reg: _ } => Ok(()),
|
|
Self::MonitorExit { reg: _ } => Ok(()),
|
|
Self::CheckCast { reg: _, lit } => v.visit_type(lit),
|
|
Self::InstanceOf {
|
|
dest: _,
|
|
obj: _,
|
|
lit,
|
|
} => v.visit_type(lit),
|
|
Self::ArrayLength { dest: _, arr: _ } => Ok(()),
|
|
Self::NewInstance { reg: _, lit } => v.visit_type(lit),
|
|
Self::NewArray {
|
|
reg: _,
|
|
size_reg: _,
|
|
lit,
|
|
} => v.visit_type(lit),
|
|
Self::FilledNewArray {
|
|
type_,
|
|
reg_values: _,
|
|
} => v.visit_type(type_),
|
|
Self::FillArrayData {
|
|
arr: _,
|
|
elt_width: _,
|
|
data: _,
|
|
} => Ok(()),
|
|
Self::Throw { reg: _ } => Ok(()),
|
|
Self::Goto { label: _ } => Ok(()),
|
|
Self::Switch {
|
|
reg: _,
|
|
branches: _,
|
|
} => Ok(()),
|
|
Self::CmpLFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::CmpGFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::CmpLDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::CmpGDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::CmpLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::IfEq {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfNe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfLt {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfGe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfGt {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfLe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(()),
|
|
Self::IfEqZ { a: _, label: _ } => Ok(()),
|
|
Self::IfNeZ { a: _, label: _ } => Ok(()),
|
|
Self::IfLtZ { a: _, label: _ } => Ok(()),
|
|
Self::IfGeZ { a: _, label: _ } => Ok(()),
|
|
Self::IfGtZ { a: _, label: _ } => Ok(()),
|
|
Self::IfLeZ { a: _, label: _ } => Ok(()),
|
|
Self::AGet {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetWide {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetObject {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetBoolean {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetByte {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetChar {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::AGetShort {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APut {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutWide {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutObject {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutBoolean {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutByte {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutChar {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::APutShort {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(()),
|
|
Self::IGet {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetWide {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetObject {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetBoolean {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetByte {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetChar {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IGetShort {
|
|
to: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPut {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutWide {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutObject {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutBoolean {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutByte {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutChar {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::IPutShort {
|
|
from: _,
|
|
obj: _,
|
|
field,
|
|
} => v.visit_field_id(field),
|
|
Self::SGet { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetWide { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetObject { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetBoolean { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetByte { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetChar { to: _, field } => v.visit_field_id(field),
|
|
Self::SGetShort { to: _, field } => v.visit_field_id(field),
|
|
Self::SPut { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutWide { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutObject { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutBoolean { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutByte { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutChar { from: _, field } => v.visit_field_id(field),
|
|
Self::SPutShort { from: _, field } => v.visit_field_id(field),
|
|
Self::InvokeVirtual { method, args: _ } => v.visit_method_id(method),
|
|
Self::InvokeSuper { method, args: _ } => v.visit_method_id(method),
|
|
Self::InvokeDirect { method, args: _ } => v.visit_method_id(method),
|
|
Self::InvokeStatic { method, args: _ } => v.visit_method_id(method),
|
|
Self::InvokeInterface { method, args: _ } => v.visit_method_id(method),
|
|
Self::NegInt { dest: _, val: _ } => Ok(()),
|
|
Self::NotInt { dest: _, val: _ } => Ok(()),
|
|
Self::NegLong { dest: _, val: _ } => Ok(()),
|
|
Self::NotLong { dest: _, val: _ } => Ok(()),
|
|
Self::NegFloat { dest: _, val: _ } => Ok(()),
|
|
Self::NegDouble { dest: _, val: _ } => Ok(()),
|
|
Self::IntToLong { dest: _, val: _ } => Ok(()),
|
|
Self::IntToFloat { dest: _, val: _ } => Ok(()),
|
|
Self::IntToDouble { dest: _, val: _ } => Ok(()),
|
|
Self::LongToInt { dest: _, val: _ } => Ok(()),
|
|
Self::LongToFloat { dest: _, val: _ } => Ok(()),
|
|
Self::LongToDouble { dest: _, val: _ } => Ok(()),
|
|
Self::FloatToInt { dest: _, val: _ } => Ok(()),
|
|
Self::FloatToLong { dest: _, val: _ } => Ok(()),
|
|
Self::FloatToDouble { dest: _, val: _ } => Ok(()),
|
|
Self::DoubleToInt { dest: _, val: _ } => Ok(()),
|
|
Self::DoubleToLong { dest: _, val: _ } => Ok(()),
|
|
Self::DoubleToFloat { dest: _, val: _ } => Ok(()),
|
|
Self::IntToByte { dest: _, val: _ } => Ok(()),
|
|
Self::IntToChar { dest: _, val: _ } => Ok(()),
|
|
Self::IntToShort { dest: _, val: _ } => Ok(()),
|
|
Self::AddInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::SubInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::MulInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::DivInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::RemInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AndInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::OrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::XorInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::ShlInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::ShrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::UshrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AddLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::SubLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::MulLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::DivLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::RemLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AndLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::OrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::XorLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::ShlLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::ShrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::UshrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AddFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::SubFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::MulFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::DivFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::RemFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AddDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::SubDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::MulDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::DivDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::RemDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(()),
|
|
Self::AddInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::SubInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::MulInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::DivInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::RemInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AndInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::OrInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::XorInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::ShlInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::ShrInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::UshrInt2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AddLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::SubLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::MulLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::DivLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::RemLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AndLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::OrLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::XorLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::ShlLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::ShrLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::UshrLong2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AddFloat2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::SubFloat2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::MulFloat2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::DivFloat2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::RemFloat2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AddDouble2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::SubDouble2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::MulDouble2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::DivDouble2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::RemDouble2Addr { dest: _, b: _ } => Ok(()),
|
|
Self::AddIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::RsubIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::MulIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::DivIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::RemIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::AndIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::OrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::XorIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::ShlIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::ShrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::UshrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(()),
|
|
Self::InvokePolymorphic {
|
|
method,
|
|
proto,
|
|
args: _,
|
|
} => {
|
|
v.visit_method_id(method)?;
|
|
v.visit_method_type(proto)
|
|
}
|
|
Self::InvokeCustom { call_site, args: _ } => v.visit_call_site(call_site),
|
|
Self::ConstMethodHandle { handle, to: _ } => v.visit_method_handle(handle),
|
|
Self::ConstMethodType { proto, to: _ } => v.visit_method_type(proto),
|
|
Self::Try {
|
|
end_label: _,
|
|
handlers,
|
|
default_handler: _,
|
|
} => {
|
|
for (ty, _) in handlers {
|
|
v.visit_type(ty)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
Self::Label { name: _ } => Ok(()),
|
|
Self::DebugLocal {
|
|
reg: _,
|
|
name: _,
|
|
type_: Some(type_),
|
|
signature: _,
|
|
} => v.visit_type(type_),
|
|
Self::DebugLocal {
|
|
reg: _,
|
|
name: _,
|
|
type_: None,
|
|
signature: _,
|
|
} => Ok(()),
|
|
Self::DebugEndLocal { reg: _ } => Ok(()),
|
|
Self::DebugEndPrologue {} => Ok(()),
|
|
Self::DebugBeginEpilogue {} => Ok(()),
|
|
Self::DebugSourceFile { file: Some(file) } => v.visit_string(&(file.as_str().into())),
|
|
Self::DebugSourceFile { file: None } => Ok(()),
|
|
Self::DebugLine { number: _ } => Ok(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<V: VisitorMut> VisitableMut<V> for Instruction {
|
|
fn default_visit_mut(self, v: &mut V) -> Result<Self> {
|
|
match self {
|
|
Self::Nop {} => Ok(self),
|
|
Self::Move { from: _, to: _ } => Ok(self),
|
|
Self::MoveWide { from: _, to: _ } => Ok(self),
|
|
Self::MoveObject { from: _, to: _ } => Ok(self),
|
|
Self::MoveResult { to: _ } => Ok(self),
|
|
Self::MoveResultWide { to: _ } => Ok(self),
|
|
Self::MoveException { to: _ } => Ok(self),
|
|
Self::MoveResultObject { to: _ } => Ok(self),
|
|
Self::ReturnVoid {} => Ok(self),
|
|
Self::Return { reg: _ } => Ok(self),
|
|
Self::ReturnWide { reg: _ } => Ok(self),
|
|
Self::ReturnObject { reg: _ } => Ok(self),
|
|
Self::Const { reg: _, lit: _ } => Ok(self),
|
|
Self::ConstWide { reg: _, lit: _ } => Ok(self),
|
|
Self::ConstString { reg, lit } => Ok(Self::ConstString {
|
|
reg,
|
|
lit: v.visit_string(lit)?,
|
|
}),
|
|
Self::ConstClass { reg, lit } => Ok(Self::ConstClass {
|
|
reg,
|
|
lit: v.visit_type(lit)?,
|
|
}),
|
|
Self::MonitorEnter { reg: _ } => Ok(self),
|
|
Self::MonitorExit { reg: _ } => Ok(self),
|
|
Self::CheckCast { reg, lit } => Ok(Self::CheckCast {
|
|
reg,
|
|
lit: v.visit_type(lit)?,
|
|
}),
|
|
Self::InstanceOf { dest, obj, lit } => Ok(Self::InstanceOf {
|
|
dest,
|
|
obj,
|
|
lit: v.visit_type(lit)?,
|
|
}),
|
|
Self::ArrayLength { dest: _, arr: _ } => Ok(self),
|
|
Self::NewInstance { reg, lit } => Ok(Self::NewInstance {
|
|
reg,
|
|
lit: v.visit_type(lit)?,
|
|
}),
|
|
Self::NewArray { reg, size_reg, lit } => Ok(Self::NewArray {
|
|
reg,
|
|
size_reg,
|
|
lit: v.visit_type(lit)?,
|
|
}),
|
|
Self::FilledNewArray { type_, reg_values } => Ok(Self::FilledNewArray {
|
|
type_: v.visit_type(type_)?,
|
|
reg_values,
|
|
}),
|
|
Self::FillArrayData {
|
|
arr: _,
|
|
elt_width: _,
|
|
data: _,
|
|
} => Ok(self),
|
|
Self::Throw { reg: _ } => Ok(self),
|
|
Self::Goto { label: _ } => Ok(self),
|
|
Self::Switch {
|
|
reg: _,
|
|
branches: _,
|
|
} => Ok(self),
|
|
Self::CmpLFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::CmpGFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::CmpLDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::CmpGDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::CmpLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::IfEq {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfNe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfLt {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfGe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfGt {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfLe {
|
|
a: _,
|
|
b: _,
|
|
label: _,
|
|
} => Ok(self),
|
|
Self::IfEqZ { a: _, label: _ } => Ok(self),
|
|
Self::IfNeZ { a: _, label: _ } => Ok(self),
|
|
Self::IfLtZ { a: _, label: _ } => Ok(self),
|
|
Self::IfGeZ { a: _, label: _ } => Ok(self),
|
|
Self::IfGtZ { a: _, label: _ } => Ok(self),
|
|
Self::IfLeZ { a: _, label: _ } => Ok(self),
|
|
Self::AGet {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetWide {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetObject {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetBoolean {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetByte {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetChar {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::AGetShort {
|
|
dest: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APut {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutWide {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutObject {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutBoolean {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutByte {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutChar {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::APutShort {
|
|
from: _,
|
|
arr: _,
|
|
idx: _,
|
|
} => Ok(self),
|
|
Self::IGet { to, obj, field } => Ok(Self::IGet {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetWide { to, obj, field } => Ok(Self::IGetWide {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetObject { to, obj, field } => Ok(Self::IGetObject {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetBoolean { to, obj, field } => Ok(Self::IGetBoolean {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetByte { to, obj, field } => Ok(Self::IGetByte {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetChar { to, obj, field } => Ok(Self::IGetChar {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IGetShort { to, obj, field } => Ok(Self::IGetShort {
|
|
to,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPut { from, obj, field } => Ok(Self::IPut {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutWide { from, obj, field } => Ok(Self::IPutWide {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutObject { from, obj, field } => Ok(Self::IPutObject {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutBoolean { from, obj, field } => Ok(Self::IPutBoolean {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutByte { from, obj, field } => Ok(Self::IPutByte {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutChar { from, obj, field } => Ok(Self::IPutChar {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::IPutShort { from, obj, field } => Ok(Self::IPutShort {
|
|
from,
|
|
obj,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGet { to, field } => Ok(Self::SGet {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetWide { to, field } => Ok(Self::SGetWide {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetObject { to, field } => Ok(Self::SGetObject {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetBoolean { to, field } => Ok(Self::SGetBoolean {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetByte { to, field } => Ok(Self::SGetByte {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetChar { to, field } => Ok(Self::SGetChar {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SGetShort { to, field } => Ok(Self::SGetShort {
|
|
to,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPut { from, field } => Ok(Self::SPut {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutWide { from, field } => Ok(Self::SPutWide {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutObject { from, field } => Ok(Self::SPutObject {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutBoolean { from, field } => Ok(Self::SPutBoolean {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutByte { from, field } => Ok(Self::SPutByte {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutChar { from, field } => Ok(Self::SPutChar {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::SPutShort { from, field } => Ok(Self::SPutShort {
|
|
from,
|
|
field: v.visit_field_id(field)?,
|
|
}),
|
|
Self::InvokeVirtual { method, args } => Ok(Self::InvokeVirtual {
|
|
method: v.visit_method_id(method)?,
|
|
args,
|
|
}),
|
|
Self::InvokeSuper { method, args } => Ok(Self::InvokeSuper {
|
|
method: v.visit_method_id(method)?,
|
|
args,
|
|
}),
|
|
Self::InvokeDirect { method, args } => Ok(Self::InvokeDirect {
|
|
method: v.visit_method_id(method)?,
|
|
args,
|
|
}),
|
|
Self::InvokeStatic { method, args } => Ok(Self::InvokeStatic {
|
|
method: v.visit_method_id(method)?,
|
|
args,
|
|
}),
|
|
Self::InvokeInterface { method, args } => Ok(Self::InvokeInterface {
|
|
method: v.visit_method_id(method)?,
|
|
args,
|
|
}),
|
|
Self::NegInt { dest: _, val: _ } => Ok(self),
|
|
Self::NotInt { dest: _, val: _ } => Ok(self),
|
|
Self::NegLong { dest: _, val: _ } => Ok(self),
|
|
Self::NotLong { dest: _, val: _ } => Ok(self),
|
|
Self::NegFloat { dest: _, val: _ } => Ok(self),
|
|
Self::NegDouble { dest: _, val: _ } => Ok(self),
|
|
Self::IntToLong { dest: _, val: _ } => Ok(self),
|
|
Self::IntToFloat { dest: _, val: _ } => Ok(self),
|
|
Self::IntToDouble { dest: _, val: _ } => Ok(self),
|
|
Self::LongToInt { dest: _, val: _ } => Ok(self),
|
|
Self::LongToFloat { dest: _, val: _ } => Ok(self),
|
|
Self::LongToDouble { dest: _, val: _ } => Ok(self),
|
|
Self::FloatToInt { dest: _, val: _ } => Ok(self),
|
|
Self::FloatToLong { dest: _, val: _ } => Ok(self),
|
|
Self::FloatToDouble { dest: _, val: _ } => Ok(self),
|
|
Self::DoubleToInt { dest: _, val: _ } => Ok(self),
|
|
Self::DoubleToLong { dest: _, val: _ } => Ok(self),
|
|
Self::DoubleToFloat { dest: _, val: _ } => Ok(self),
|
|
Self::IntToByte { dest: _, val: _ } => Ok(self),
|
|
Self::IntToChar { dest: _, val: _ } => Ok(self),
|
|
Self::IntToShort { dest: _, val: _ } => Ok(self),
|
|
Self::AddInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::SubInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::MulInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::DivInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::RemInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AndInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::OrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::XorInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::ShlInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::ShrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::UshrInt {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AddLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::SubLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::MulLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::DivLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::RemLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AndLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::OrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::XorLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::ShlLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::ShrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::UshrLong {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AddFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::SubFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::MulFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::DivFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::RemFloat {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AddDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::SubDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::MulDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::DivDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::RemDouble {
|
|
dest: _,
|
|
b: _,
|
|
c: _,
|
|
} => Ok(self),
|
|
Self::AddInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::SubInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::MulInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::DivInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::RemInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AndInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::OrInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::XorInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::ShlInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::ShrInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::UshrInt2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AddLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::SubLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::MulLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::DivLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::RemLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AndLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::OrLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::XorLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::ShlLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::ShrLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::UshrLong2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AddFloat2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::SubFloat2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::MulFloat2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::DivFloat2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::RemFloat2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AddDouble2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::SubDouble2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::MulDouble2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::DivDouble2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::RemDouble2Addr { dest: _, b: _ } => Ok(self),
|
|
Self::AddIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::RsubIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::MulIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::DivIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::RemIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::AndIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::OrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::XorIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::ShlIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::ShrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::UshrIntLit {
|
|
dest: _,
|
|
b: _,
|
|
lit: _,
|
|
} => Ok(self),
|
|
Self::InvokePolymorphic {
|
|
method,
|
|
proto,
|
|
args,
|
|
} => Ok(Self::InvokePolymorphic {
|
|
method: v.visit_method_id(method)?,
|
|
proto: v.visit_method_type(proto)?,
|
|
args,
|
|
}),
|
|
Self::InvokeCustom { call_site, args } => Ok(Self::InvokeCustom {
|
|
call_site: v.visit_call_site(call_site)?,
|
|
args,
|
|
}),
|
|
Self::ConstMethodHandle { handle, to } => Ok(Self::ConstMethodHandle {
|
|
handle: v.visit_method_handle(handle)?,
|
|
to,
|
|
}),
|
|
Self::ConstMethodType { proto, to } => Ok(Self::ConstMethodType {
|
|
proto: v.visit_method_type(proto)?,
|
|
to,
|
|
}),
|
|
Self::Try {
|
|
end_label,
|
|
handlers,
|
|
default_handler,
|
|
} => Ok(Self::Try {
|
|
end_label,
|
|
handlers: handlers
|
|
.into_iter()
|
|
.map(|(ty, lab)| v.visit_type(ty).map(|ty| (ty, lab)))
|
|
.collect::<Result<_>>()?,
|
|
default_handler,
|
|
}),
|
|
Self::Label { name: _ } => Ok(self),
|
|
Self::DebugLocal {
|
|
reg,
|
|
name,
|
|
type_,
|
|
signature,
|
|
} => Ok(Self::DebugLocal {
|
|
reg,
|
|
name,
|
|
type_: type_.map(|type_| v.visit_type(type_)).transpose()?,
|
|
signature,
|
|
}),
|
|
Self::DebugEndLocal { reg: _ } => Ok(self),
|
|
Self::DebugEndPrologue {} => Ok(self),
|
|
Self::DebugBeginEpilogue {} => Ok(self),
|
|
Self::DebugSourceFile { file: Some(file) } => {
|
|
v.visit_string(file.as_str().into())
|
|
.map(|file| Self::DebugSourceFile {
|
|
file: Some(file.into()),
|
|
})
|
|
}
|
|
Self::DebugSourceFile { file: None } => Ok(self),
|
|
Self::DebugLine { number: _ } => Ok(self),
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! sanity_check_4_bit_reg {
|
|
($ins_name:tt, $r1:ident, $r2:ident) => {{
|
|
if $r1 & 0b1111_0000 != 0 {
|
|
Err(anyhow!(
|
|
"{} uses registers indexed on 4 bits, found {}",
|
|
$ins_name,
|
|
$r1
|
|
))
|
|
} else if $r2 & &0b1111_0000 != 0 {
|
|
Err(anyhow!(
|
|
"{} uses registers indexed on 4 bits, found {}",
|
|
$ins_name,
|
|
$r1
|
|
))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}};
|
|
}
|
|
|
|
macro_rules! sanity_check_22b_or_22s {
|
|
($ins_name:tt, $r1:ident, $r2:ident, $lit:ident) => {{
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if $r1 & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if $r2 & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *$lit < I8_MIN_AS_I16 || *$lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
Err(anyhow!(
|
|
"{}/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits ({}/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits ({}/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
$ins_name,
|
|
$ins_name,
|
|
$ins_name,
|
|
$r1,
|
|
$r2,
|
|
$lit,
|
|
))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}};
|
|
}
|
|
|
|
macro_rules! raw_ins_if {
|
|
($ins_op:tt, $label_addrs:ident, $label:ident, $addr:ident, $r1:ident, $r2:ident) => {{
|
|
let label_addr = $label_addrs.get($label).ok_or(anyhow!(
|
|
"Label {} not found in code, but found `if` with this label",
|
|
$label,
|
|
))?;
|
|
let branch_offset = *label_addr as i32 - $addr as i32;
|
|
if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 {
|
|
bail!("Found an `if` that jump to far from the instruction",);
|
|
}
|
|
Ok(InsFormat::Format22T {
|
|
op: $ins_op,
|
|
va: *$r1,
|
|
vb: *$r2,
|
|
c: branch_offset as i16,
|
|
})
|
|
}};
|
|
}
|
|
|
|
macro_rules! raw_ins_ifz {
|
|
($ins_op:tt, $label_addrs:ident, $label:ident, $addr:ident, $r1:ident) => {{
|
|
let label_addr = $label_addrs.get($label).ok_or(anyhow!(
|
|
"Label {} not found in code, but found `if` with this label",
|
|
$label,
|
|
))?;
|
|
let branch_offset = *label_addr as i32 - $addr as i32;
|
|
if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 {
|
|
bail!("Found an `if` that jump to far from the instruction",);
|
|
}
|
|
Ok(InsFormat::Format21T {
|
|
op: $ins_op,
|
|
va: *$r1,
|
|
b: branch_offset as i16,
|
|
})
|
|
}};
|
|
}
|
|
|
|
macro_rules! raw_ins_iget_put {
|
|
($ins_op:tt, $field_ids:ident, $field:ident, $r1:ident, $r2:ident) => {{
|
|
let field_idx = $field_ids.get($field).ok_or(anyhow!(
|
|
"Field {} (field of class {}) not found in dex builder",
|
|
$field.__repr__(),
|
|
$field.class_.__repr__(),
|
|
))?;
|
|
Ok(InsFormat::Format22C {
|
|
op: $ins_op,
|
|
va: *$r1,
|
|
vb: *$r2,
|
|
c: *field_idx as u16,
|
|
})
|
|
}};
|
|
}
|
|
|
|
macro_rules! raw_ins_sget_put {
|
|
($ins_op:tt, $field_ids:ident, $field:ident, $r1:ident) => {{
|
|
let field_idx = $field_ids.get($field).ok_or(anyhow!(
|
|
"Field {} (field of class {}) not found in dex builder",
|
|
$field.__repr__(),
|
|
$field.class_.__repr__(),
|
|
))?;
|
|
Ok(InsFormat::Format21C {
|
|
op: $ins_op,
|
|
va: *$r1,
|
|
b: *field_idx as u16,
|
|
})
|
|
}};
|
|
}
|
|
|
|
macro_rules! raw_ins_invoke {
|
|
($ins_op_35c:tt, $ins_op_3rc:tt, $method_ids:ident, $meth:ident, $args:ident) => {{
|
|
let meth_idx = $method_ids.get($meth).ok_or(anyhow!(
|
|
"Method {} (method of class {}) not found in dex builder",
|
|
$meth.__repr__(),
|
|
$meth.class_.__repr__(),
|
|
))?;
|
|
debug_assert!(
|
|
*meth_idx <= u16::MAX as usize,
|
|
"methode id too big for invoke instruction"
|
|
);
|
|
let meth_idx = *meth_idx as u16;
|
|
let mut last = None;
|
|
let mut first = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = $args.len();
|
|
for r in $args.iter().cloned() {
|
|
if first.is_none() {
|
|
first = Some(r);
|
|
}
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if four_bites && len <= 5 {
|
|
let mut regs = vec![];
|
|
for reg in $args.iter().cloned() {
|
|
regs.push(reg);
|
|
}
|
|
while regs.len() != 5 {
|
|
regs.push(0);
|
|
}
|
|
let [vc, vd, ve, vf, vg]: [u8; 5] = regs
|
|
.into_iter()
|
|
.map(|r| r as u8)
|
|
.collect::<Vec<_>>()
|
|
.try_into()
|
|
.ok()
|
|
.unwrap();
|
|
let a = $args.len() as u8;
|
|
Ok(InsFormat::Format35C {
|
|
op: $ins_op_35c,
|
|
a,
|
|
vc,
|
|
ve,
|
|
vd,
|
|
vf,
|
|
vg,
|
|
b: meth_idx,
|
|
})
|
|
} else if consec && len <= 255 {
|
|
let a = $args.len() as u8;
|
|
let vc = if let Some(vc) = first { vc } else { 0 };
|
|
Ok(InsFormat::Format3RC {
|
|
op: $ins_op_3rc,
|
|
a,
|
|
vc,
|
|
b: meth_idx,
|
|
})
|
|
} else {
|
|
// Not supposed to happend with a sanitized invoke
|
|
Err(anyhow!("Invalid Invoke instruction"))
|
|
}
|
|
}};
|
|
}
|
|
|
|
#[pymethods]
|
|
impl Instruction {
|
|
pub fn to_json(&self) -> Result<String> {
|
|
Ok(serde_json::to_string(self)?)
|
|
}
|
|
|
|
#[staticmethod]
|
|
pub fn from_json(json: &str) -> Result<Self> {
|
|
Ok(serde_json::from_str(json)?)
|
|
}
|
|
|
|
pub fn __str__(&self) -> String {
|
|
match self {
|
|
Self::Nop {} => "nop".into(),
|
|
Self::Move { from, to } => {
|
|
format!("move {} {}", to, from)
|
|
}
|
|
Self::MoveWide { from, to } => {
|
|
format!("move-wide {} {}", to, from)
|
|
}
|
|
Self::MoveObject { from, to } => {
|
|
format!("move-object {} {}", to, from)
|
|
}
|
|
Self::MoveResult { to } => {
|
|
format!("move-result {}", to)
|
|
}
|
|
Self::MoveResultWide { to } => {
|
|
format!("move-result-wide {}", to)
|
|
}
|
|
Self::MoveResultObject { to } => {
|
|
format!("move-result-object {}", to)
|
|
}
|
|
Self::MoveException { to } => {
|
|
format!("move-exception {}", to)
|
|
}
|
|
Self::ReturnVoid {} => "return-void".into(),
|
|
Self::Return { reg } => {
|
|
format!("return {}", reg)
|
|
}
|
|
Self::ReturnWide { reg } => {
|
|
format!("return-wide {}", reg)
|
|
}
|
|
Self::ReturnObject { reg } => {
|
|
format!("return-object {}", reg)
|
|
}
|
|
Self::Const { lit, reg } => {
|
|
format!("const {} {}", reg, lit)
|
|
}
|
|
Self::ConstWide { lit, reg } => {
|
|
format!("const-wide {} {}", reg, lit)
|
|
}
|
|
Self::ConstString { lit, reg } => {
|
|
format!("const-string {} \"{}\"", reg, lit.__str__())
|
|
}
|
|
Self::ConstClass { lit, reg } => {
|
|
format!("const-class {} \"{}\"", reg, lit.__str__())
|
|
}
|
|
Self::MonitorEnter { reg } => {
|
|
format!("monitor-enter {}", reg)
|
|
}
|
|
Self::MonitorExit { reg } => {
|
|
format!("monitor-exit {}", reg)
|
|
}
|
|
Self::CheckCast { lit, reg } => {
|
|
format!("check-cast {} {}", reg, lit.__str__())
|
|
}
|
|
Self::InstanceOf { lit, dest, obj } => {
|
|
format!("instance-of {} {} {}", dest, obj, lit.__str__())
|
|
}
|
|
Self::ArrayLength { arr, dest } => {
|
|
format!("array-length{} {}", dest, arr,)
|
|
}
|
|
Self::NewInstance { lit, reg } => {
|
|
format!("new-instance {} {}", reg, lit.__str__())
|
|
}
|
|
Self::NewArray { lit, size_reg, reg } => {
|
|
format!("new-array {} {} {}", reg, size_reg, lit.__str__())
|
|
}
|
|
Self::FilledNewArray { reg_values, type_ } => {
|
|
let args = if reg_values.len() >= 5 {
|
|
format!("{} .. {}", reg_values[0], reg_values[reg_values.len() - 1])
|
|
} else {
|
|
reg_values
|
|
.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("filled-new-array {{{}}} {}", args, type_.__str__())
|
|
}
|
|
Self::FillArrayData {
|
|
arr,
|
|
data,
|
|
elt_width,
|
|
} => {
|
|
let data: String = if data.len() / *elt_width as usize == 0 {
|
|
"".into()
|
|
} else if data.len() / *elt_width as usize <= 2 {
|
|
let mut arr = "".into();
|
|
for (i, v) in data.iter().enumerate() {
|
|
if i == 0 {
|
|
arr += "0x"
|
|
} else if i % *elt_width as usize == 0 {
|
|
arr += " 0x"
|
|
}
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
arr
|
|
} else {
|
|
let mut arr = "0x".into();
|
|
for v in &data[..*elt_width as usize] {
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
arr += " ... 0x";
|
|
for v in &data[data.len() - *elt_width as usize..] {
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
|
|
arr
|
|
};
|
|
format!("fill-array-data {} {}", arr, data)
|
|
}
|
|
Self::Throw { reg } => {
|
|
format!("throw {}", reg)
|
|
}
|
|
Self::Goto { label } => {
|
|
format!("goto {}", label)
|
|
}
|
|
Self::Switch { branches, reg } => {
|
|
let mut branches_str: String = "".into();
|
|
let mut branches: Vec<_> = branches.iter().collect();
|
|
branches.sort_by_key(|(key, _)| **key);
|
|
for (key, label) in branches {
|
|
branches_str += &format!("\n {key}: goto {label}");
|
|
}
|
|
format!("switch {} {}", reg, branches_str)
|
|
}
|
|
Self::CmpLFloat { b, dest, c } => {
|
|
format!("cmpl-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::CmpGFloat { b, dest, c } => {
|
|
format!("cmpg-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::CmpLDouble { b, dest, c } => {
|
|
format!("cmpl-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::CmpGDouble { b, dest, c } => {
|
|
format!("cmpg-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::CmpLong { b, dest, c } => {
|
|
format!("cmp-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::IfEq { label, a, b } => {
|
|
format!("if-eq {} {} {}", a, b, label)
|
|
}
|
|
Self::IfNe { label, a, b } => {
|
|
format!("if-neq {} {} {}", a, b, label)
|
|
}
|
|
Self::IfLt { label, a, b } => {
|
|
format!("if-lt {} {} {}", a, b, label)
|
|
}
|
|
Self::IfGe { label, a, b } => {
|
|
format!("if-ge {} {} {}", a, b, label)
|
|
}
|
|
Self::IfGt { label, a, b } => {
|
|
format!("if-gt {} {} {}", a, b, label)
|
|
}
|
|
Self::IfLe { label, a, b } => {
|
|
format!("if-le {} {} {}", a, b, label)
|
|
}
|
|
Self::IfEqZ { label, a } => {
|
|
format!("if-eqz {} {}", a, label)
|
|
}
|
|
Self::IfNeZ { label, a } => {
|
|
format!("if-nez {} {}", a, label)
|
|
}
|
|
Self::IfLtZ { label, a } => {
|
|
format!("if-ltz {} {}", a, label)
|
|
}
|
|
Self::IfGeZ { label, a } => {
|
|
format!("if-gez {} {}", a, label)
|
|
}
|
|
Self::IfGtZ { label, a } => {
|
|
format!("if-gtz {} {}", a, label)
|
|
}
|
|
Self::IfLeZ { label, a } => {
|
|
format!("if-lez {} {}", a, label)
|
|
}
|
|
Self::AGet { arr, idx, dest } => {
|
|
format!("aget {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetWide { arr, idx, dest } => {
|
|
format!("aget-wide {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetObject { arr, idx, dest } => {
|
|
format!("aget-object {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetBoolean { arr, idx, dest } => {
|
|
format!("aget-boolean {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetByte { arr, idx, dest } => {
|
|
format!("aget-byte {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetChar { arr, idx, dest } => {
|
|
format!("aget-char {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::AGetShort { arr, idx, dest } => {
|
|
format!("aget-short {} {} {}", dest, arr, idx)
|
|
}
|
|
Self::APut { arr, from, idx } => {
|
|
format!("aput {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutWide { arr, from, idx } => {
|
|
format!("aput-wide {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutObject { arr, from, idx } => {
|
|
format!("aput-object {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutBoolean { arr, from, idx } => {
|
|
format!("aput-boolean {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutByte { arr, from, idx } => {
|
|
format!("aput-byte {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutChar { arr, from, idx } => {
|
|
format!("aput-char {} {} {}", from, arr, idx)
|
|
}
|
|
Self::APutShort { arr, from, idx } => {
|
|
format!("aput-short {} {} {}", from, arr, idx)
|
|
}
|
|
Self::IGet { field, to, obj } => {
|
|
format!("iget {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetWide { field, to, obj } => {
|
|
format!("iget-wide {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetObject { field, to, obj } => {
|
|
format!("iget-object {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetBoolean { field, to, obj } => {
|
|
format!("iget-boolean {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetByte { field, to, obj } => {
|
|
format!("iget-byte {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetChar { field, to, obj } => {
|
|
format!("iget-char {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IGetShort { field, to, obj } => {
|
|
format!("iget-short {} {} {}", to, obj, field.__str__())
|
|
}
|
|
Self::IPut { field, from, obj } => {
|
|
format!("iput {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutWide { field, from, obj } => {
|
|
format!("iput-wide {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutObject { field, from, obj } => {
|
|
format!("iput-object {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutBoolean { field, from, obj } => {
|
|
format!("iput-boolean {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutByte { field, from, obj } => {
|
|
format!("iput-byte {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutChar { field, from, obj } => {
|
|
format!("iput-char {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::IPutShort { field, from, obj } => {
|
|
format!("iput-short {} {} {}", from, obj, field.__str__())
|
|
}
|
|
Self::SGet { field, to } => {
|
|
format!("sget {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetWide { field, to } => {
|
|
format!("sget-wide {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetObject { field, to } => {
|
|
format!("sget-object {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetBoolean { field, to } => {
|
|
format!("sget-boolean {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetByte { field, to } => {
|
|
format!("sget-byte {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetChar { field, to } => {
|
|
format!("sget-char {} {}", to, field.__str__())
|
|
}
|
|
Self::SGetShort { field, to } => {
|
|
format!("sget-short {} {}", to, field.__str__())
|
|
}
|
|
Self::SPut { field, from } => {
|
|
format!("sput {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutWide { field, from } => {
|
|
format!("sput-wide {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutObject { field, from } => {
|
|
format!("sput-object {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutBoolean { field, from } => {
|
|
format!("sput-boolean {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutByte { field, from } => {
|
|
format!("sput-byte {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutChar { field, from } => {
|
|
format!("sput-char {} {}", from, field.__str__())
|
|
}
|
|
Self::SPutShort { field, from } => {
|
|
format!("sput-short {} {}", from, field.__str__())
|
|
}
|
|
Self::InvokeVirtual { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-virtual {{{}}} {}", args, method.__str__())
|
|
}
|
|
Self::InvokeSuper { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-super {{{}}} {}", args, method.__str__())
|
|
}
|
|
Self::InvokeDirect { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-direct {{{}}} {}", args, method.__str__())
|
|
}
|
|
Self::InvokeStatic { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-static {{{}}} {}", args, method.__str__())
|
|
}
|
|
Self::InvokeInterface { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-interface {{{}}} {}", args, method.__str__())
|
|
}
|
|
Self::NegInt { val, dest } => {
|
|
format!("neg-int {} {}", dest, val)
|
|
}
|
|
Self::NotInt { val, dest } => {
|
|
format!("not-int {} {}", dest, val)
|
|
}
|
|
Self::NegLong { val, dest } => {
|
|
format!("neg-long {} {}", dest, val)
|
|
}
|
|
Self::NotLong { val, dest } => {
|
|
format!("not-long {} {}", dest, val)
|
|
}
|
|
Self::NegFloat { val, dest } => {
|
|
format!("neg-float {} {}", dest, val)
|
|
}
|
|
Self::NegDouble { val, dest } => {
|
|
format!("neg-double {} {}", dest, val)
|
|
}
|
|
Self::IntToLong { val, dest } => {
|
|
format!("int-to-long {} {}", dest, val)
|
|
}
|
|
Self::IntToFloat { val, dest } => {
|
|
format!("int-to-float {} {}", dest, val)
|
|
}
|
|
Self::IntToDouble { val, dest } => {
|
|
format!("int-to-double {} {}", dest, val)
|
|
}
|
|
Self::LongToInt { val, dest } => {
|
|
format!("long-to-int {} {}", dest, val)
|
|
}
|
|
Self::LongToFloat { val, dest } => {
|
|
format!("long-to-float {} {}", dest, val)
|
|
}
|
|
Self::LongToDouble { val, dest } => {
|
|
format!("long-to-double {} {}", dest, val)
|
|
}
|
|
Self::FloatToInt { val, dest } => {
|
|
format!("float-to-int {} {}", dest, val)
|
|
}
|
|
Self::FloatToLong { val, dest } => {
|
|
format!("float-to-long {} {}", dest, val)
|
|
}
|
|
Self::FloatToDouble { val, dest } => {
|
|
format!("float-to-double {} {}", dest, val)
|
|
}
|
|
Self::DoubleToInt { val, dest } => {
|
|
format!("double-to-int {} {}", dest, val)
|
|
}
|
|
Self::DoubleToLong { val, dest } => {
|
|
format!("double-to-long {} {}", dest, val)
|
|
}
|
|
Self::DoubleToFloat { val, dest } => {
|
|
format!("double-to-float {} {}", dest, val)
|
|
}
|
|
Self::IntToByte { val, dest } => {
|
|
format!("int-to-byte {} {}", dest, val)
|
|
}
|
|
Self::IntToChar { val, dest } => {
|
|
format!("int-to-char {} {}", dest, val)
|
|
}
|
|
Self::IntToShort { val, dest } => {
|
|
format!("int-to-short {} {}", dest, val)
|
|
}
|
|
Self::AddInt { b, dest, c } => {
|
|
format!("add-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::SubInt { b, dest, c } => {
|
|
format!("sub-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::MulInt { b, dest, c } => {
|
|
format!("mul-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::DivInt { b, dest, c } => {
|
|
format!("div-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::RemInt { b, dest, c } => {
|
|
format!("rem-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::AndInt { b, dest, c } => {
|
|
format!("and-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::OrInt { b, dest, c } => {
|
|
format!("or-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::XorInt { b, dest, c } => {
|
|
format!("xor-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::ShlInt { b, dest, c } => {
|
|
format!("shl-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::ShrInt { b, dest, c } => {
|
|
format!("shr-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::UshrInt { b, dest, c } => {
|
|
format!("ushr-int {} {} {}", dest, b, c)
|
|
}
|
|
Self::AddLong { b, dest, c } => {
|
|
format!("add-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::SubLong { b, dest, c } => {
|
|
format!("sub-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::MulLong { b, dest, c } => {
|
|
format!("mul-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::DivLong { b, dest, c } => {
|
|
format!("div-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::RemLong { b, dest, c } => {
|
|
format!("rem-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::AndLong { b, dest, c } => {
|
|
format!("and-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::OrLong { b, dest, c } => {
|
|
format!("or-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::XorLong { b, dest, c } => {
|
|
format!("xor-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::ShlLong { b, dest, c } => {
|
|
format!("shl-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::ShrLong { b, dest, c } => {
|
|
format!("shr-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::UshrLong { b, dest, c } => {
|
|
format!("ushr-long {} {} {}", dest, b, c)
|
|
}
|
|
Self::AddFloat { b, dest, c } => {
|
|
format!("add-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::SubFloat { b, dest, c } => {
|
|
format!("sub-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::MulFloat { b, dest, c } => {
|
|
format!("mul-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::DivFloat { b, dest, c } => {
|
|
format!("div-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::RemFloat { b, dest, c } => {
|
|
format!("rem-float {} {} {}", dest, b, c)
|
|
}
|
|
Self::AddDouble { b, dest, c } => {
|
|
format!("add-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::SubDouble { b, dest, c } => {
|
|
format!("sub-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::MulDouble { b, dest, c } => {
|
|
format!("mul-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::DivDouble { b, dest, c } => {
|
|
format!("div-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::RemDouble { b, dest, c } => {
|
|
format!("rem-double {} {} {}", dest, b, c)
|
|
}
|
|
Self::AddInt2Addr { b, dest } => {
|
|
format!("add-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::SubInt2Addr { b, dest } => {
|
|
format!("sub-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::MulInt2Addr { b, dest } => {
|
|
format!("mul-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::DivInt2Addr { b, dest } => {
|
|
format!("div-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::RemInt2Addr { b, dest } => {
|
|
format!("rem-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::AndInt2Addr { b, dest } => {
|
|
format!("and-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::OrInt2Addr { b, dest } => {
|
|
format!("or-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::XorInt2Addr { b, dest } => {
|
|
format!("xor-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::ShlInt2Addr { b, dest } => {
|
|
format!("shl-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::ShrInt2Addr { b, dest } => {
|
|
format!("shr-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::UshrInt2Addr { b, dest } => {
|
|
format!("ushr-int/2addr {} {}", dest, b)
|
|
}
|
|
Self::AddLong2Addr { b, dest } => {
|
|
format!("add-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::SubLong2Addr { b, dest } => {
|
|
format!("sub-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::MulLong2Addr { b, dest } => {
|
|
format!("mul-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::DivLong2Addr { b, dest } => {
|
|
format!("div-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::RemLong2Addr { b, dest } => {
|
|
format!("rem-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::AndLong2Addr { b, dest } => {
|
|
format!("and-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::OrLong2Addr { b, dest } => {
|
|
format!("or-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::XorLong2Addr { b, dest } => {
|
|
format!("xor-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::ShlLong2Addr { b, dest } => {
|
|
format!("shl-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::ShrLong2Addr { b, dest } => {
|
|
format!("shr-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::UshrLong2Addr { b, dest } => {
|
|
format!("ushr-long/2addr {} {}", dest, b)
|
|
}
|
|
Self::AddFloat2Addr { b, dest } => {
|
|
format!("add-float/2addr {} {}", dest, b)
|
|
}
|
|
Self::SubFloat2Addr { b, dest } => {
|
|
format!("sub-float/2addr {} {}", dest, b)
|
|
}
|
|
Self::MulFloat2Addr { b, dest } => {
|
|
format!("mul-float/2addr {} {}", dest, b)
|
|
}
|
|
Self::DivFloat2Addr { b, dest } => {
|
|
format!("div-float/2addr {} {}", dest, b)
|
|
}
|
|
Self::RemFloat2Addr { b, dest } => {
|
|
format!("rem-float/2addr {} {}", dest, b)
|
|
}
|
|
Self::AddDouble2Addr { b, dest } => {
|
|
format!("add-double/2addr {} {}", dest, b)
|
|
}
|
|
Self::SubDouble2Addr { b, dest } => {
|
|
format!("sub-double/2addr {} {}", dest, b)
|
|
}
|
|
Self::MulDouble2Addr { b, dest } => {
|
|
format!("mul-double/2addr {} {}", dest, b)
|
|
}
|
|
Self::DivDouble2Addr { b, dest } => {
|
|
format!("div-double/2addr {} {}", dest, b)
|
|
}
|
|
Self::RemDouble2Addr { b, dest } => {
|
|
format!("rem-double/2addr {} {}", dest, b)
|
|
}
|
|
Self::AddIntLit { lit, b, dest } => {
|
|
format!("add-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::RsubIntLit { lit, b, dest } => {
|
|
format!("rsub-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::MulIntLit { lit, b, dest } => {
|
|
format!("mul-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::DivIntLit { lit, b, dest } => {
|
|
format!("div-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::RemIntLit { lit, b, dest } => {
|
|
format!("rem-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::AndIntLit { lit, b, dest } => {
|
|
format!("and-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::OrIntLit { lit, b, dest } => {
|
|
format!("or-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::XorIntLit { lit, b, dest } => {
|
|
format!("xor-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::ShlIntLit { lit, b, dest } => {
|
|
format!("shl-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::ShrIntLit { lit, b, dest } => {
|
|
format!("shr-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::UshrIntLit { lit, b, dest } => {
|
|
format!("ushr-int/lit {} {} {}", dest, b, lit)
|
|
}
|
|
Self::InvokePolymorphic {
|
|
args,
|
|
method,
|
|
proto,
|
|
} => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!(
|
|
"invoke-polymorphic {{{}}} {} {}",
|
|
args,
|
|
method.__str__(),
|
|
proto.__str__()
|
|
)
|
|
}
|
|
Self::InvokeCustom { args, call_site } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("invoke-custom {{{}}} {}", args, call_site.__str__(),)
|
|
}
|
|
Self::ConstMethodHandle { to, handle } => {
|
|
format!("const-method-handle {} {}", to, handle.__str__())
|
|
}
|
|
Self::ConstMethodType { to, proto } => {
|
|
format!("const-method-type {} {}", to, proto.__str__())
|
|
}
|
|
Self::Try {
|
|
handlers,
|
|
end_label,
|
|
default_handler,
|
|
} => {
|
|
let handlers = handlers
|
|
.iter()
|
|
.map(|(ty, label)| format!(" {}: {label}", ty.__str__()))
|
|
.collect::<Vec<_>>()
|
|
.join("\n ");
|
|
let default_handler = if let Some(label) = default_handler {
|
|
format!(" default: {label}")
|
|
} else {
|
|
"".into()
|
|
};
|
|
|
|
format!("try until {}{}{}", end_label, handlers, default_handler)
|
|
}
|
|
Self::Label { name } => {
|
|
format!("{}:", name)
|
|
}
|
|
Self::DebugLocal {
|
|
reg,
|
|
name,
|
|
type_,
|
|
signature,
|
|
} => {
|
|
// TODO: check if/how apktool/smali handles empty name and type
|
|
let name = name.clone().unwrap_or(String::new());
|
|
let ty = type_.as_ref().map(IdType::__str__).unwrap_or(String::new());
|
|
if let Some(signature) = signature {
|
|
format!(".local {reg}, \"{name}\":{ty}, \"{signature}\"")
|
|
} else {
|
|
format!(".local {reg}, \"{name}\":{ty}")
|
|
}
|
|
}
|
|
Self::DebugEndLocal { reg } => format!(".end local {reg}"),
|
|
Self::DebugEndPrologue {} => ".prologue".into(),
|
|
Self::DebugBeginEpilogue {} => ".epilogue".into(),
|
|
// TODO: check if/how apktool/smali handles empty change of src file
|
|
Self::DebugSourceFile { file: Some(file) } => format!(".source_file {file}"),
|
|
// TODO: find a better representation
|
|
Self::DebugSourceFile { file: None } => ".source_file unknown".into(),
|
|
Self::DebugLine { number } => format!(".line {number}"),
|
|
}
|
|
}
|
|
|
|
pub fn __repr__(&self) -> String {
|
|
match self {
|
|
Self::Nop {} => "Instruction(Nop)".into(),
|
|
Self::Move { from, to } => {
|
|
format!("Instruction::Move({}, {})", to, from)
|
|
}
|
|
Self::MoveWide { from, to } => {
|
|
format!("Instruction::MoveWide({}, {})", to, from)
|
|
}
|
|
Self::MoveObject { from, to } => {
|
|
format!("Instruction::MoveObject({}, {})", to, from)
|
|
}
|
|
Self::MoveResult { to } => {
|
|
format!("Instruction::MoveResult({})", to)
|
|
}
|
|
Self::MoveResultWide { to } => {
|
|
format!("Instruction::MoveResultWide({})", to)
|
|
}
|
|
Self::MoveResultObject { to } => {
|
|
format!("Instruction::MoveResultObject({})", to)
|
|
}
|
|
Self::MoveException { to } => {
|
|
format!("Instruction::MoveException({})", to)
|
|
}
|
|
Self::ReturnVoid {} => "Instruction::ReturnVoid)".into(),
|
|
Self::Return { reg } => {
|
|
format!("Instruction::Return({})", reg)
|
|
}
|
|
Self::ReturnWide { reg } => {
|
|
format!("Instruction::ReturnWide({})", reg)
|
|
}
|
|
Self::ReturnObject { reg } => {
|
|
format!("Instruction::ReturnObject({})", reg)
|
|
}
|
|
Self::Const { reg, lit } => {
|
|
format!("Instruction::Const({}, {})", reg, lit)
|
|
}
|
|
Self::ConstWide { reg, lit } => {
|
|
format!("Instruction::ConstWide({}, {})", reg, lit)
|
|
}
|
|
Self::ConstString { reg, lit } => {
|
|
format!("Instruction::ConstString({}, {})", reg, lit.__repr__())
|
|
}
|
|
Self::ConstClass { reg, lit } => {
|
|
format!("Instruction::ConstClass({}, {})", reg, lit.__repr__())
|
|
}
|
|
Self::MonitorEnter { reg } => {
|
|
format!("Instruction::MonitorEnter({})", reg)
|
|
}
|
|
Self::MonitorExit { reg } => {
|
|
format!("Instruction::MonitorExit({})", reg)
|
|
}
|
|
Self::CheckCast { reg, lit } => {
|
|
format!("Instruction::CheckCast({}, {})", reg, lit.__repr__())
|
|
}
|
|
Self::InstanceOf { obj, lit, dest } => {
|
|
format!(
|
|
"Instruction::InstanceOf({}, {}, {})",
|
|
dest,
|
|
obj,
|
|
lit.__repr__()
|
|
)
|
|
}
|
|
Self::ArrayLength { arr, dest } => {
|
|
format!("Instruction::ArrayLength({}, {})", dest, arr,)
|
|
}
|
|
Self::NewInstance { reg, lit } => {
|
|
format!("Instruction::NewInstance({}, {})", reg, lit.__repr__())
|
|
}
|
|
Self::NewArray { reg, lit, size_reg } => {
|
|
format!(
|
|
"Instruction::NewArray({}, {}, {})",
|
|
reg,
|
|
size_reg,
|
|
lit.__repr__()
|
|
)
|
|
}
|
|
Self::FilledNewArray { type_, reg_values } => {
|
|
let args = if reg_values.len() >= 5 {
|
|
format!(
|
|
"{}, ..., {}",
|
|
reg_values[0],
|
|
reg_values[reg_values.len() - 1]
|
|
)
|
|
} else {
|
|
reg_values
|
|
.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(", ")
|
|
};
|
|
format!(
|
|
"Instruction::FilledNewArray([{}], {})",
|
|
args,
|
|
type_.__repr__()
|
|
)
|
|
}
|
|
Self::FillArrayData {
|
|
elt_width,
|
|
arr,
|
|
data,
|
|
} => {
|
|
let data: String = if data.len() / *elt_width as usize == 0 {
|
|
"".into()
|
|
} else if data.len() / *elt_width as usize <= 2 {
|
|
let mut arr = "".into();
|
|
for (i, v) in data.iter().enumerate() {
|
|
if i == 0 {
|
|
arr += "0x"
|
|
} else if i % *elt_width as usize == 0 {
|
|
arr += ", 0x"
|
|
}
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
arr
|
|
} else {
|
|
let mut arr = "0x".into();
|
|
for v in &data[..*elt_width as usize] {
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
arr += ", ..., 0x";
|
|
for v in &data[data.len() - *elt_width as usize..] {
|
|
arr += format!("{v:02x}").as_str();
|
|
}
|
|
|
|
arr
|
|
};
|
|
format!("Instruction::FillArrayData({}, [{}])", arr, data)
|
|
}
|
|
Self::Throw { reg } => {
|
|
format!("Instruction::Throw({})", reg)
|
|
}
|
|
Self::Goto { label } => {
|
|
format!("Instruction::Goto({})", label)
|
|
}
|
|
Self::Switch { reg, .. } => {
|
|
format!("Instruction::Switch({}, ...)", reg)
|
|
}
|
|
Self::CmpLFloat { b, c, dest } => {
|
|
format!("Instruction::CmpLFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::CmpGFloat { b, c, dest } => {
|
|
format!("Instruction::CmpGFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::CmpLDouble { b, c, dest } => {
|
|
format!("Instruction::CmpLDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::CmpGDouble { b, c, dest } => {
|
|
format!("Instruction::CmpGDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::CmpLong { b, c, dest } => {
|
|
format!("Instruction::CmpLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::IfEq { a, b, label } => {
|
|
format!("Instruction::IfEq({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfNe { a, b, label } => {
|
|
format!("Instruction::IfNe({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfLt { a, b, label } => {
|
|
format!("Instruction::IfLt({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfGe { a, b, label } => {
|
|
format!("Instruction::IfGe({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfGt { a, b, label } => {
|
|
format!("Instruction::IfGt({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfLe { a, b, label } => {
|
|
format!("Instruction::IfLe({}, {}, {})", a, b, label)
|
|
}
|
|
Self::IfEqZ { a, label } => {
|
|
format!("Instruction::IfEqZ({}, {})", a, label)
|
|
}
|
|
Self::IfNeZ { a, label } => {
|
|
format!("Instruction::IfNeZ({}, {})", a, label)
|
|
}
|
|
Self::IfLtZ { a, label } => {
|
|
format!("Instruction::IfLtZ({}, {})", a, label)
|
|
}
|
|
Self::IfGeZ { a, label } => {
|
|
format!("Instruction::IfGeZ({}, {})", a, label)
|
|
}
|
|
Self::IfGtZ { a, label } => {
|
|
format!("Instruction::IfGtZ({}, {})", a, label)
|
|
}
|
|
Self::IfLeZ { a, label } => {
|
|
format!("Instruction::IfLeZ({}, {})", a, label)
|
|
}
|
|
Self::AGet { idx, arr, dest } => {
|
|
format!("Instruction::AGet({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetWide { idx, arr, dest } => {
|
|
format!("Instruction::AGetWide({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetObject { idx, arr, dest } => {
|
|
format!("Instruction::AGetObject({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetBoolean { idx, arr, dest } => {
|
|
format!("Instruction::AGetBoolean({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetByte { idx, arr, dest } => {
|
|
format!("Instruction::AGetByte({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetChar { idx, arr, dest } => {
|
|
format!("Instruction::AGetChar({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::AGetShort { idx, arr, dest } => {
|
|
format!("Instruction::AGetShort({}, {}, {})", dest, arr, idx)
|
|
}
|
|
Self::APut { idx, arr, from } => {
|
|
format!("Instruction::APut({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutWide { idx, arr, from } => {
|
|
format!("Instruction::APutWide({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutObject { idx, arr, from } => {
|
|
format!("Instruction::APutObject({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutBoolean { idx, arr, from } => {
|
|
format!("Instruction::APutBoolean({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutByte { idx, arr, from } => {
|
|
format!("Instruction::APutByte({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutChar { idx, arr, from } => {
|
|
format!("Instruction::APutChar({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::APutShort { idx, arr, from } => {
|
|
format!("Instruction::APutShort({}, {}, {})", from, arr, idx)
|
|
}
|
|
Self::IGet { obj, field, to } => {
|
|
format!("Instruction::IGet({}, {}, {})", to, obj, field.__repr__())
|
|
}
|
|
Self::IGetWide { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetWide({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IGetObject { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetObject({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IGetBoolean { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetBoolean({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IGetByte { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetByte({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IGetChar { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetChar({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IGetShort { obj, field, to } => {
|
|
format!(
|
|
"Instruction::IGetShort({}, {}, {})",
|
|
to,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPut { obj, field, from } => {
|
|
format!("Instruction::IPut({}, {}, {})", from, obj, field.__repr__())
|
|
}
|
|
Self::IPutWide { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutWide({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPutObject { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutObject({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPutBoolean { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutBoolean({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPutByte { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutByte({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPutChar { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutChar({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::IPutShort { obj, field, from } => {
|
|
format!(
|
|
"Instruction::IPutShort({}, {}, {})",
|
|
from,
|
|
obj,
|
|
field.__repr__()
|
|
)
|
|
}
|
|
Self::SGet { field, to } => {
|
|
format!("Instruction::SGet({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetWide { field, to } => {
|
|
format!("Instruction::SGetWide({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetObject { field, to } => {
|
|
format!("Instruction::SGetObject({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetBoolean { field, to } => {
|
|
format!("Instruction::SGetBoolean({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetByte { field, to } => {
|
|
format!("Instruction::SGetByte({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetChar { field, to } => {
|
|
format!("Instruction::SGetChar({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SGetShort { field, to } => {
|
|
format!("Instruction::SGetShort({}, {})", to, field.__repr__())
|
|
}
|
|
Self::SPut { field, from } => {
|
|
format!("Instruction::SPut({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutWide { field, from } => {
|
|
format!("Instruction::SPutWide({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutObject { field, from } => {
|
|
format!("Instruction::SPutObject({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutBoolean { field, from } => {
|
|
format!("Instruction::SPutBoolean({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutByte { field, from } => {
|
|
format!("Instruction::SPutByte({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutChar { field, from } => {
|
|
format!("Instruction::SPutChar({}, {})", from, field.__repr__())
|
|
}
|
|
Self::SPutShort { field, from } => {
|
|
format!("Instruction::SPutShort({}, {})", from, field.__repr__())
|
|
}
|
|
Self::InvokeVirtual { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("Instruction::InvokeVirtual({}, {})", args, method.__str__())
|
|
}
|
|
Self::InvokeSuper { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("Instruction::InvokeSuper({}, {})", args, method.__str__())
|
|
}
|
|
Self::InvokeDirect { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("Instruction::InvokeDirect({}, {})", args, method.__str__())
|
|
}
|
|
Self::InvokeStatic { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!("Instruction::InvokeStatic({}, {})", args, method.__str__())
|
|
}
|
|
Self::InvokeInterface { args, method } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!(
|
|
"Instruction::InvokeInterface({}, {})",
|
|
args,
|
|
method.__str__()
|
|
)
|
|
}
|
|
Self::NegInt { val, dest } => {
|
|
format!("Instruction::NegInt({}, {})", dest, val)
|
|
}
|
|
Self::NotInt { val, dest } => {
|
|
format!("Instruction::NotInt({}, {})", dest, val)
|
|
}
|
|
Self::NegLong { val, dest } => {
|
|
format!("Instruction::NegLong({}, {})", dest, val)
|
|
}
|
|
Self::NotLong { val, dest } => {
|
|
format!("Instruction::NotLong({}, {})", dest, val)
|
|
}
|
|
Self::NegFloat { val, dest } => {
|
|
format!("Instruction::NegFloat({}, {})", dest, val)
|
|
}
|
|
Self::NegDouble { val, dest } => {
|
|
format!("Instruction::NegDouble({}, {})", dest, val)
|
|
}
|
|
Self::IntToLong { val, dest } => {
|
|
format!("Instruction::IntToLong({}, {})", dest, val)
|
|
}
|
|
Self::IntToFloat { val, dest } => {
|
|
format!("Instruction::IntToFloat({}, {})", dest, val)
|
|
}
|
|
Self::IntToDouble { val, dest } => {
|
|
format!("Instruction::IntToDouble({}, {})", dest, val)
|
|
}
|
|
Self::LongToInt { val, dest } => {
|
|
format!("Instruction::LongToInt({}, {})", dest, val)
|
|
}
|
|
Self::LongToFloat { val, dest } => {
|
|
format!("Instruction::LongToFloat({}, {})", dest, val)
|
|
}
|
|
Self::LongToDouble { val, dest } => {
|
|
format!("Instruction::LongToDouble({}, {})", dest, val)
|
|
}
|
|
Self::FloatToInt { val, dest } => {
|
|
format!("Instruction::FloatToInt({}, {})", dest, val)
|
|
}
|
|
Self::FloatToLong { val, dest } => {
|
|
format!("Instruction::FloatToLong({}, {})", dest, val)
|
|
}
|
|
Self::FloatToDouble { val, dest } => {
|
|
format!("Instruction::FloatToDouble({}, {})", dest, val)
|
|
}
|
|
Self::DoubleToInt { val, dest } => {
|
|
format!("Instruction::DoubleToInt({}, {})", dest, val)
|
|
}
|
|
Self::DoubleToLong { val, dest } => {
|
|
format!("Instruction::DoubleToLong({}, {})", dest, val)
|
|
}
|
|
Self::DoubleToFloat { val, dest } => {
|
|
format!("Instruction::DoubleToFloat({}, {})", dest, val)
|
|
}
|
|
Self::IntToByte { val, dest } => {
|
|
format!("Instruction::IntToByte({}, {})", dest, val)
|
|
}
|
|
Self::IntToChar { val, dest } => {
|
|
format!("Instruction::IntToChar({}, {})", dest, val)
|
|
}
|
|
Self::IntToShort { val, dest } => {
|
|
format!("Instruction::IntToShort({}, {})", dest, val)
|
|
}
|
|
Self::AddInt { b, c, dest } => {
|
|
format!("Instruction::AddInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::SubInt { b, c, dest } => {
|
|
format!("Instruction::SubInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::MulInt { b, c, dest } => {
|
|
format!("Instruction::MulInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::DivInt { b, c, dest } => {
|
|
format!("Instruction::DivInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::RemInt { b, c, dest } => {
|
|
format!("Instruction::RemInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AndInt { b, c, dest } => {
|
|
format!("Instruction::AndInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::OrInt { b, c, dest } => {
|
|
format!("Instruction::OrInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::XorInt { b, c, dest } => {
|
|
format!("Instruction::XorInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::ShlInt { b, c, dest } => {
|
|
format!("Instruction::ShlInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::ShrInt { b, c, dest } => {
|
|
format!("Instruction::ShrInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::UshrInt { b, c, dest } => {
|
|
format!("Instruction::UshrInt({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AddLong { b, c, dest } => {
|
|
format!("Instruction::AddLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::SubLong { b, c, dest } => {
|
|
format!("Instruction::SubLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::MulLong { b, c, dest } => {
|
|
format!("Instruction::MulLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::DivLong { b, c, dest } => {
|
|
format!("Instruction::DivLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::RemLong { b, c, dest } => {
|
|
format!("Instruction::RemLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AndLong { b, c, dest } => {
|
|
format!("Instruction::AndLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::OrLong { b, c, dest } => {
|
|
format!("Instruction::OrLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::XorLong { b, c, dest } => {
|
|
format!("Instruction::XorLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::ShlLong { b, c, dest } => {
|
|
format!("Instruction::ShlLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::ShrLong { b, c, dest } => {
|
|
format!("Instruction::ShrLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::UshrLong { b, c, dest } => {
|
|
format!("Instruction::UshrLong({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AddFloat { b, c, dest } => {
|
|
format!("Instruction::AddFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::SubFloat { b, c, dest } => {
|
|
format!("Instruction::SubFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::MulFloat { b, c, dest } => {
|
|
format!("Instruction::MulFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::DivFloat { b, c, dest } => {
|
|
format!("Instruction::DivFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::RemFloat { b, c, dest } => {
|
|
format!("Instruction::RemFloat({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AddDouble { b, c, dest } => {
|
|
format!("Instruction::AddDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::SubDouble { b, c, dest } => {
|
|
format!("Instruction::SubDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::MulDouble { b, c, dest } => {
|
|
format!("Instruction::MulDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::DivDouble { b, c, dest } => {
|
|
format!("Instruction::DivDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::RemDouble { b, c, dest } => {
|
|
format!("Instruction::RemDouble({}, {}, {})", dest, b, c)
|
|
}
|
|
Self::AddInt2Addr { b, dest } => {
|
|
format!("Instruction::AddInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::SubInt2Addr { b, dest } => {
|
|
format!("Instruction::SubInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::MulInt2Addr { b, dest } => {
|
|
format!("Instruction::MulInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::DivInt2Addr { b, dest } => {
|
|
format!("Instruction::DivInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::RemInt2Addr { b, dest } => {
|
|
format!("Instruction::RemInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AndInt2Addr { b, dest } => {
|
|
format!("Instruction::AndInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::OrInt2Addr { b, dest } => {
|
|
format!("Instruction::OrInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::XorInt2Addr { b, dest } => {
|
|
format!("Instruction::XorInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::ShlInt2Addr { b, dest } => {
|
|
format!("Instruction::ShlInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::ShrInt2Addr { b, dest } => {
|
|
format!("Instruction::ShrInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::UshrInt2Addr { b, dest } => {
|
|
format!("Instruction::UshrInt2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AddLong2Addr { b, dest } => {
|
|
format!("Instruction::AddLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::SubLong2Addr { b, dest } => {
|
|
format!("Instruction::SubLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::MulLong2Addr { b, dest } => {
|
|
format!("Instruction::MulLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::DivLong2Addr { b, dest } => {
|
|
format!("Instruction::DivLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::RemLong2Addr { b, dest } => {
|
|
format!("Instruction::RemLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AndLong2Addr { b, dest } => {
|
|
format!("Instruction::AndLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::OrLong2Addr { b, dest } => {
|
|
format!("Instruction::OrLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::XorLong2Addr { b, dest } => {
|
|
format!("Instruction::XorLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::ShlLong2Addr { b, dest } => {
|
|
format!("Instruction::ShlLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::ShrLong2Addr { b, dest } => {
|
|
format!("Instruction::ShrLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::UshrLong2Addr { b, dest } => {
|
|
format!("Instruction::UshrLong2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AddFloat2Addr { b, dest } => {
|
|
format!("Instruction::AddFloat2Addr({}, {})", dest, b)
|
|
}
|
|
Self::SubFloat2Addr { b, dest } => {
|
|
format!("Instruction::SubFloat2Addr({}, {})", dest, b)
|
|
}
|
|
Self::MulFloat2Addr { b, dest } => {
|
|
format!("Instruction::MulFloat2Addr({}, {})", dest, b)
|
|
}
|
|
Self::DivFloat2Addr { b, dest } => {
|
|
format!("Instruction::DivFloat2Addr({}, {})", dest, b)
|
|
}
|
|
Self::RemFloat2Addr { b, dest } => {
|
|
format!("Instruction::RemFloat2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AddDouble2Addr { b, dest } => {
|
|
format!("Instruction::AddDouble2Addr({}, {})", dest, b)
|
|
}
|
|
Self::SubDouble2Addr { b, dest } => {
|
|
format!("Instruction::SubDouble2Addr({}, {})", dest, b)
|
|
}
|
|
Self::MulDouble2Addr { b, dest } => {
|
|
format!("Instruction::MulDouble2Addr({}, {})", dest, b)
|
|
}
|
|
Self::DivDouble2Addr { b, dest } => {
|
|
format!("Instruction::DivDouble2Addr({}, {})", dest, b)
|
|
}
|
|
Self::RemDouble2Addr { b, dest } => {
|
|
format!("Instruction::RemDouble2Addr({}, {})", dest, b)
|
|
}
|
|
Self::AddIntLit { b, lit, dest } => {
|
|
format!("Instruction::AddIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::RsubIntLit { b, lit, dest } => {
|
|
format!("Instruction::RsubIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::MulIntLit { b, lit, dest } => {
|
|
format!("Instruction::MulIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::DivIntLit { b, lit, dest } => {
|
|
format!("Instruction::DivIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::RemIntLit { b, lit, dest } => {
|
|
format!("Instruction::RemIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::AndIntLit { b, lit, dest } => {
|
|
format!("Instruction::AndIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::OrIntLit { b, lit, dest } => {
|
|
format!("Instruction::OrIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::XorIntLit { b, lit, dest } => {
|
|
format!("Instruction::XorIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::ShlIntLit { b, lit, dest } => {
|
|
format!("Instruction::ShlIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::ShrIntLit { b, lit, dest } => {
|
|
format!("Instruction::ShrIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::UshrIntLit { b, lit, dest } => {
|
|
format!("Instruction::UshrIntLit({}, {}, {})", dest, b, lit)
|
|
}
|
|
Self::InvokePolymorphic {
|
|
args,
|
|
proto,
|
|
method,
|
|
} => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!(
|
|
"Instruction::InvokePolymorphic({}, {}, {})",
|
|
args,
|
|
method.__str__(),
|
|
proto.__str__()
|
|
)
|
|
}
|
|
Self::InvokeCustom { args, call_site } => {
|
|
let args = if args.len() >= 5 {
|
|
format!("{} .. {}", args[0], args[args.len() - 1])
|
|
} else {
|
|
args.iter()
|
|
.map(|v| format!("{v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
};
|
|
format!(
|
|
"Instruction::InvokeCustom({}, {})",
|
|
args,
|
|
call_site.__str__(),
|
|
)
|
|
}
|
|
Self::ConstMethodHandle { handle, to } => {
|
|
format!(
|
|
"Instruction::ConstMethodHandle({}, {})",
|
|
to,
|
|
handle.__repr__()
|
|
)
|
|
}
|
|
Self::ConstMethodType { proto, to } => {
|
|
format!("Instruction::ConstMethodType({}, {})", to, proto.__repr__())
|
|
}
|
|
Self::Try {
|
|
end_label,
|
|
default_handler,
|
|
handlers,
|
|
} => {
|
|
let handlers = handlers
|
|
.iter()
|
|
.map(|(ty, label)| format!("{}, {label}", ty.__repr__()))
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
format!(
|
|
"Instruction::Try({}, [{}], {:?})",
|
|
end_label, handlers, default_handler,
|
|
)
|
|
}
|
|
Self::Label { name } => {
|
|
format!("Instruction::Label({})", name)
|
|
}
|
|
Self::DebugLocal {
|
|
reg,
|
|
name,
|
|
type_,
|
|
signature,
|
|
} => {
|
|
let name = name.clone().unwrap_or("None".into());
|
|
let type_ = type_
|
|
.as_ref()
|
|
.map(IdType::__repr__)
|
|
.unwrap_or("None".into());
|
|
let signature = signature.clone().unwrap_or("None".into());
|
|
format!("Instruction::DebugLoca({reg}, {name}, {type_}, {signature}")
|
|
}
|
|
Self::DebugEndLocal { reg } => format!("Instruction::DebugEndLocal({reg})"),
|
|
Self::DebugEndPrologue {} => "Instruction::DebugEndPrologue".into(),
|
|
Self::DebugBeginEpilogue {} => "Instruction::DebugBeginEpilogue".into(),
|
|
// TODO: check if/how apktool/smali handles empty change of src file
|
|
Self::DebugSourceFile { file: Some(file) } => {
|
|
format!("Instruction::DebugSourceFile({file})")
|
|
}
|
|
Self::DebugSourceFile { file: None } => "Instruction::DebugSourceFile(None)".into(),
|
|
Self::DebugLine { number } => format!("Instruction::DebugLine({number})"),
|
|
}
|
|
}
|
|
|
|
/// Return the smallest size possible for the associated instruction.
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// The returned size is the size in bytes (`u8`) (the same as the value return
|
|
/// by the `Serializable::size()` method), but instructions in the bytecode
|
|
/// count addresses by unit of `u16`.
|
|
pub fn min_ins_size(&self) -> usize {
|
|
match self {
|
|
Self::ConstString { .. } => 4,
|
|
Self::Goto { .. } => 2,
|
|
_ => self.ins_size().unwrap(),
|
|
}
|
|
}
|
|
|
|
/// Return the bigest size possible for the associated instruction.
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// The returned size is the size in bytes (`u8`) (the same as the value return
|
|
/// by the `Serializable::size()` method), but instructions in the bytecode
|
|
/// count addresses by unit of `u16`.
|
|
pub fn max_ins_size(&self) -> usize {
|
|
match self {
|
|
Self::ConstString { .. } => 6,
|
|
Self::Goto { .. } => 6,
|
|
_ => self.ins_size().unwrap(),
|
|
}
|
|
}
|
|
|
|
/// Return the actual size of the instruction.
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// The returned size is the size in bytes (`u8`) (the same as the value return
|
|
/// by the `Serializable::size()` method), but instructions in the bytecode
|
|
/// count addresses by unit of `u16`.
|
|
pub fn ins_size(&self) -> Result<usize> {
|
|
match self {
|
|
Self::ConstString { .. } => Err(anyhow!(
|
|
"Cannot get the size of a const-string size without knowing the string idx"
|
|
)),
|
|
Self::Goto { .. } => Err(anyhow!(
|
|
"Cannot get the size of a goto without knowing the branch offset"
|
|
)),
|
|
Self::ConstClass { .. } => Ok(4),
|
|
Self::CheckCast { .. } => Ok(4),
|
|
Self::InstanceOf { .. } => Ok(4),
|
|
Self::NewInstance { .. } => Ok(4),
|
|
Self::NewArray { .. } => Ok(4),
|
|
Self::FilledNewArray { .. } => Ok(6),
|
|
Self::FillArrayData { .. } => Ok(6),
|
|
Self::Switch { .. } => Ok(6),
|
|
Self::IfEq { .. } => Ok(4),
|
|
Self::IfNe { .. } => Ok(4),
|
|
Self::IfLt { .. } => Ok(4),
|
|
Self::IfGe { .. } => Ok(4),
|
|
Self::IfGt { .. } => Ok(4),
|
|
Self::IfLe { .. } => Ok(4),
|
|
Self::IfEqZ { .. } => Ok(4),
|
|
Self::IfNeZ { .. } => Ok(4),
|
|
Self::IfLtZ { .. } => Ok(4),
|
|
Self::IfGeZ { .. } => Ok(4),
|
|
Self::IfGtZ { .. } => Ok(4),
|
|
Self::IfLeZ { .. } => Ok(4),
|
|
Self::IGet { .. } => Ok(4),
|
|
Self::IGetWide { .. } => Ok(4),
|
|
Self::IGetObject { .. } => Ok(4),
|
|
Self::IGetBoolean { .. } => Ok(4),
|
|
Self::IGetByte { .. } => Ok(4),
|
|
Self::IGetChar { .. } => Ok(4),
|
|
Self::IGetShort { .. } => Ok(4),
|
|
Self::IPut { .. } => Ok(4),
|
|
Self::IPutWide { .. } => Ok(4),
|
|
Self::IPutObject { .. } => Ok(4),
|
|
Self::IPutBoolean { .. } => Ok(4),
|
|
Self::IPutByte { .. } => Ok(4),
|
|
Self::IPutChar { .. } => Ok(4),
|
|
Self::IPutShort { .. } => Ok(4),
|
|
Self::SGet { .. } => Ok(4),
|
|
Self::SGetWide { .. } => Ok(4),
|
|
Self::SGetObject { .. } => Ok(4),
|
|
Self::SGetBoolean { .. } => Ok(4),
|
|
Self::SGetByte { .. } => Ok(4),
|
|
Self::SGetChar { .. } => Ok(4),
|
|
Self::SGetShort { .. } => Ok(4),
|
|
Self::SPut { .. } => Ok(4),
|
|
Self::SPutWide { .. } => Ok(4),
|
|
Self::SPutObject { .. } => Ok(4),
|
|
Self::SPutBoolean { .. } => Ok(4),
|
|
Self::SPutByte { .. } => Ok(4),
|
|
Self::SPutChar { .. } => Ok(4),
|
|
Self::SPutShort { .. } => Ok(4),
|
|
Self::InvokeVirtual { .. } => Ok(6),
|
|
Self::InvokeSuper { .. } => Ok(6),
|
|
Self::InvokeDirect { .. } => Ok(6),
|
|
Self::InvokeStatic { .. } => Ok(6),
|
|
Self::InvokeInterface { .. } => Ok(6),
|
|
Self::InvokePolymorphic { .. } => Ok(8),
|
|
Self::InvokeCustom { .. } => Ok(6),
|
|
Self::ConstMethodHandle { .. } => Ok(4),
|
|
Self::ConstMethodType { .. } => Ok(4),
|
|
Self::Try { .. } => Ok(0),
|
|
Self::Label { .. } => Ok(0),
|
|
Self::DebugLocal { .. } => Ok(0),
|
|
Self::DebugEndLocal { .. } => Ok(0),
|
|
Self::DebugEndPrologue {} => Ok(0),
|
|
Self::DebugBeginEpilogue {} => Ok(0),
|
|
Self::DebugSourceFile { .. } => Ok(0),
|
|
Self::DebugLine { .. } => Ok(0),
|
|
_ => self
|
|
.get_raw_ins(GetRawInsParam::default())
|
|
.map(|ins| ins.size()),
|
|
}
|
|
}
|
|
|
|
pub fn sanity_check(&self) -> Result<()> {
|
|
match self {
|
|
Self::InstanceOf { dest, obj, .. } => sanity_check_4_bit_reg!("instance-of", dest, obj),
|
|
Self::ArrayLength { dest, arr } => sanity_check_4_bit_reg!("array-length", dest, arr),
|
|
Self::NewArray { reg, size_reg, .. } => {
|
|
sanity_check_4_bit_reg!("new-array", reg, size_reg)
|
|
}
|
|
Self::FilledNewArray { reg_values, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = reg_values.len();
|
|
for r in reg_values.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"filled-new-array instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::IfEq { a, b, .. } => sanity_check_4_bit_reg!("if-eq", a, b),
|
|
Self::IfNe { a, b, .. } => sanity_check_4_bit_reg!("if-neq", a, b),
|
|
Self::IfLt { a, b, .. } => sanity_check_4_bit_reg!("if-lt", a, b),
|
|
Self::IfGe { a, b, .. } => sanity_check_4_bit_reg!("if-ge", a, b),
|
|
Self::IfGt { a, b, .. } => sanity_check_4_bit_reg!("if-gt", a, b),
|
|
Self::IfLe { a, b, .. } => sanity_check_4_bit_reg!("if-le", a, b),
|
|
Self::IGet { to, obj, .. } => sanity_check_4_bit_reg!("iget", to, obj),
|
|
Self::IGetWide { to, obj, .. } => sanity_check_4_bit_reg!("iget-wide", to, obj),
|
|
Self::IGetObject { to, obj, .. } => sanity_check_4_bit_reg!("iget-object", to, obj),
|
|
Self::IGetBoolean { to, obj, .. } => sanity_check_4_bit_reg!("iget-boolean", to, obj),
|
|
Self::IGetByte { to, obj, .. } => sanity_check_4_bit_reg!("iget-byte", to, obj),
|
|
Self::IGetChar { to, obj, .. } => sanity_check_4_bit_reg!("iget-char", to, obj),
|
|
Self::IGetShort { to, obj, .. } => sanity_check_4_bit_reg!("iget-short", to, obj),
|
|
Self::IPut { from, obj, .. } => sanity_check_4_bit_reg!("iput", from, obj),
|
|
Self::IPutWide { from, obj, .. } => sanity_check_4_bit_reg!("iput-wide", from, obj),
|
|
Self::IPutObject { from, obj, .. } => sanity_check_4_bit_reg!("iput-object", from, obj),
|
|
Self::IPutBoolean { from, obj, .. } => {
|
|
sanity_check_4_bit_reg!("iput-boolean", from, obj)
|
|
}
|
|
Self::IPutByte { from, obj, .. } => sanity_check_4_bit_reg!("iput-byte", from, obj),
|
|
Self::IPutChar { from, obj, .. } => sanity_check_4_bit_reg!("iput-char", from, obj),
|
|
Self::IPutShort { from, obj, .. } => sanity_check_4_bit_reg!("iput-short", from, obj),
|
|
Self::InvokeVirtual { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-virtual instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::InvokeSuper { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-super instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::InvokeDirect { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-direct instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::InvokeStatic { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-static instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::InvokeInterface { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-interface instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::NegInt { dest, val } => sanity_check_4_bit_reg!("neg-int", dest, val),
|
|
Self::NotInt { dest, val } => sanity_check_4_bit_reg!("not-int", dest, val),
|
|
Self::NegLong { dest, val } => sanity_check_4_bit_reg!("neg-long", dest, val),
|
|
Self::NotLong { dest, val } => sanity_check_4_bit_reg!("not-long", dest, val),
|
|
Self::NegFloat { dest, val } => sanity_check_4_bit_reg!("neg-float", dest, val),
|
|
Self::NegDouble { dest, val } => sanity_check_4_bit_reg!("neg-double", dest, val),
|
|
Self::IntToLong { dest, val } => sanity_check_4_bit_reg!("int-to-long", dest, val),
|
|
Self::IntToFloat { dest, val } => sanity_check_4_bit_reg!("int-to-float", dest, val),
|
|
Self::IntToDouble { dest, val } => sanity_check_4_bit_reg!("int-to-double", dest, val),
|
|
Self::LongToInt { dest, val } => sanity_check_4_bit_reg!("long-to-int", dest, val),
|
|
Self::LongToFloat { dest, val } => sanity_check_4_bit_reg!("long-to-float", dest, val),
|
|
Self::LongToDouble { dest, val } => {
|
|
sanity_check_4_bit_reg!("long-to-double", dest, val)
|
|
}
|
|
Self::FloatToInt { dest, val } => sanity_check_4_bit_reg!("float-to-int", dest, val),
|
|
Self::FloatToLong { dest, val } => sanity_check_4_bit_reg!("float-to-long", dest, val),
|
|
Self::FloatToDouble { dest, val } => {
|
|
sanity_check_4_bit_reg!("float-to-double", dest, val)
|
|
}
|
|
Self::DoubleToInt { dest, val } => sanity_check_4_bit_reg!("double-to-int", dest, val),
|
|
Self::DoubleToLong { dest, val } => {
|
|
sanity_check_4_bit_reg!("double-to-long", dest, val)
|
|
}
|
|
Self::DoubleToFloat { dest, val } => {
|
|
sanity_check_4_bit_reg!("double-to-float", dest, val)
|
|
}
|
|
Self::IntToByte { dest, val } => sanity_check_4_bit_reg!("int-to-byte", dest, val),
|
|
Self::IntToChar { dest, val } => sanity_check_4_bit_reg!("int-to-char", dest, val),
|
|
Self::IntToShort { dest, val } => sanity_check_4_bit_reg!("int-to-short", dest, val),
|
|
Self::AddInt2Addr { dest, b } => sanity_check_4_bit_reg!("add-int/2addr", dest, b),
|
|
Self::SubInt2Addr { dest, b } => sanity_check_4_bit_reg!("sub-int/2addr", dest, b),
|
|
Self::MulInt2Addr { dest, b } => sanity_check_4_bit_reg!("mul-int/2addr", dest, b),
|
|
Self::DivInt2Addr { dest, b } => sanity_check_4_bit_reg!("div-int/2addr", dest, b),
|
|
Self::RemInt2Addr { dest, b } => sanity_check_4_bit_reg!("rem-int/2addr", dest, b),
|
|
Self::AndInt2Addr { dest, b } => sanity_check_4_bit_reg!("and-int/2addr", dest, b),
|
|
Self::OrInt2Addr { dest, b } => sanity_check_4_bit_reg!("or-int/2addr", dest, b),
|
|
Self::XorInt2Addr { dest, b } => sanity_check_4_bit_reg!("xor-int/2addr", dest, b),
|
|
Self::ShlInt2Addr { dest, b } => sanity_check_4_bit_reg!("shl-int/2addr", dest, b),
|
|
Self::ShrInt2Addr { dest, b } => sanity_check_4_bit_reg!("shr-int/2addr", dest, b),
|
|
Self::UshrInt2Addr { dest, b } => sanity_check_4_bit_reg!("ushr-int/2addr", dest, b),
|
|
Self::AddLong2Addr { dest, b } => sanity_check_4_bit_reg!("add-long/2addr", dest, b),
|
|
Self::SubLong2Addr { dest, b } => sanity_check_4_bit_reg!("sub-long/2addr", dest, b),
|
|
Self::MulLong2Addr { dest, b } => sanity_check_4_bit_reg!("mul-long/2addr", dest, b),
|
|
Self::DivLong2Addr { dest, b } => sanity_check_4_bit_reg!("div-long/2addr", dest, b),
|
|
Self::RemLong2Addr { dest, b } => sanity_check_4_bit_reg!("rem-long/2addr", dest, b),
|
|
Self::AndLong2Addr { dest, b } => sanity_check_4_bit_reg!("and-long/2addr", dest, b),
|
|
Self::OrLong2Addr { dest, b } => sanity_check_4_bit_reg!("or-long/2addr", dest, b),
|
|
Self::XorLong2Addr { dest, b } => sanity_check_4_bit_reg!("xor-long/2addr", dest, b),
|
|
Self::ShlLong2Addr { dest, b } => sanity_check_4_bit_reg!("shl-long/2addr", dest, b),
|
|
Self::ShrLong2Addr { dest, b } => sanity_check_4_bit_reg!("shr-long/2addr", dest, b),
|
|
Self::UshrLong2Addr { dest, b } => sanity_check_4_bit_reg!("ushr-long/2addr", dest, b),
|
|
Self::AddFloat2Addr { dest, b } => sanity_check_4_bit_reg!("add-float/2addr", dest, b),
|
|
Self::SubFloat2Addr { dest, b } => sanity_check_4_bit_reg!("sub-float/2addr", dest, b),
|
|
Self::MulFloat2Addr { dest, b } => sanity_check_4_bit_reg!("mul-float/2addr", dest, b),
|
|
Self::DivFloat2Addr { dest, b } => sanity_check_4_bit_reg!("div-float/2addr", dest, b),
|
|
Self::RemFloat2Addr { dest, b } => sanity_check_4_bit_reg!("rem-float/2addr", dest, b),
|
|
Self::AddDouble2Addr { dest, b } => {
|
|
sanity_check_4_bit_reg!("add-double/2addr", dest, b)
|
|
}
|
|
Self::SubDouble2Addr { dest, b } => {
|
|
sanity_check_4_bit_reg!("sub-double/2addr", dest, b)
|
|
}
|
|
Self::MulDouble2Addr { dest, b } => {
|
|
sanity_check_4_bit_reg!("mul-double/2addr", dest, b)
|
|
}
|
|
Self::DivDouble2Addr { dest, b } => {
|
|
sanity_check_4_bit_reg!("div-double/2addr", dest, b)
|
|
}
|
|
Self::RemDouble2Addr { dest, b } => {
|
|
sanity_check_4_bit_reg!("rem-double/2addr", dest, b)
|
|
}
|
|
Self::AddIntLit { dest, b, lit } => sanity_check_22b_or_22s!("add-int", dest, b, lit),
|
|
Self::RsubIntLit { dest, b, lit } => sanity_check_22b_or_22s!("rsub-int", dest, b, lit),
|
|
Self::MulIntLit { dest, b, lit } => sanity_check_22b_or_22s!("mul-int", dest, b, lit),
|
|
Self::DivIntLit { dest, b, lit } => sanity_check_22b_or_22s!("div-int", dest, b, lit),
|
|
Self::RemIntLit { dest, b, lit } => sanity_check_22b_or_22s!("rem-int", dest, b, lit),
|
|
Self::AndIntLit { dest, b, lit } => sanity_check_22b_or_22s!("and-int", dest, b, lit),
|
|
Self::OrIntLit { dest, b, lit } => sanity_check_22b_or_22s!("or-int", dest, b, lit),
|
|
Self::XorIntLit { dest, b, lit } => sanity_check_22b_or_22s!("xor-int", dest, b, lit),
|
|
Self::InvokePolymorphic { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-polymorphic instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
Self::InvokeCustom { args, .. } => {
|
|
let mut last = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if (four_bites && len <= 5) || (consec && len <= 255) {
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!(
|
|
"invoke-custom instruction must either use 5 or \
|
|
less register indexed on 4 bites or less than 255 \
|
|
consecutive registers"
|
|
))
|
|
}
|
|
}
|
|
_ => Ok(()),
|
|
}
|
|
}
|
|
/// Return all strings referenced in the instruction.
|
|
pub fn get_all_strings(&self) -> HashSet<DexString> {
|
|
let mut visitor = StringCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
|
|
/// Return all types referenced in the instruction.
|
|
pub fn get_all_types(&self) -> HashSet<IdType> {
|
|
let mut visitor = TypeCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
|
|
/// Return all prototypes referenced in the instruction.
|
|
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
|
|
let mut visitor = MethodTypeCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
|
|
/// Return all field ids referenced in the instruction.
|
|
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
|
|
let mut visitor = FieldIdCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
|
|
/// Return all method ids referenced in the instruction.
|
|
pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
|
|
let mut visitor = MethodIdCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
|
|
/// Return all method handles referenced in the instruction.
|
|
pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
|
|
let mut visitor = MethodHandleCollector::default();
|
|
visitor.visit_instruction(self).unwrap();
|
|
visitor.result()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct GetRawInsParam<'a> {
|
|
pub strings: Option<&'a HashMap<DexString, usize>>,
|
|
pub type_ids: Option<&'a HashMap<IdType, usize>>,
|
|
pub field_ids: Option<&'a HashMap<IdField, usize>>,
|
|
pub method_ids: Option<&'a HashMap<IdMethod, usize>>,
|
|
pub proto_ids: Option<&'a HashMap<IdMethodType, usize>>,
|
|
pub call_site_idx: Option<usize>,
|
|
pub method_handle_idx: Option<usize>,
|
|
pub jump_data: Option<(usize, &'a HashMap<String, usize>)>,
|
|
pub goto_size: Option<usize>,
|
|
pub data_offset: Option<i32>,
|
|
}
|
|
|
|
impl Instruction {
|
|
/// Return the raw instruction ([`InsFormat`]) when it does not need additionnal data.
|
|
///
|
|
/// Some instruction require additional data, provided as options. If the required information
|
|
/// is not provided, the method will return an error.
|
|
///
|
|
/// The parameters of the functions are pass as a [`GetRawInsParam`] to make is easier track
|
|
/// which parameter is which and make it easier to pass only one or two values.
|
|
///
|
|
/// # Variants that require `strings`
|
|
///
|
|
/// `strings` is a lookup table that associate its idx to a string.
|
|
///
|
|
/// - `ConstString`
|
|
///
|
|
/// # Variants that require `types_ids`
|
|
///
|
|
/// `types_ids` is a lookup table that associate its idx to a type.
|
|
///
|
|
/// - `ConstClass`
|
|
/// - `CheckCast`
|
|
/// - `InstanceOf`
|
|
/// - `NewInstance`
|
|
/// - `NewArray`
|
|
/// - `FilledNewArray`
|
|
///
|
|
/// # Variants that require `field_ids`
|
|
///
|
|
/// `field_ids` is a lookup table that associate its idx to a field.
|
|
///
|
|
/// - `IGet`
|
|
/// - `IGetWide`
|
|
/// - `IGetObject`
|
|
/// - `IGetBoolean`
|
|
/// - `IGetByte`
|
|
/// - `IGetChar`
|
|
/// - `IGetShort`
|
|
/// - `IPut`
|
|
/// - `IPutWide`
|
|
/// - `IPutObject`
|
|
/// - `IPutBoolean`
|
|
/// - `IPutByte`
|
|
/// - `IPutChar`
|
|
/// - `IPutShort`
|
|
/// - `SGet`
|
|
/// - `SGetWide`
|
|
/// - `SGetObject`
|
|
/// - `SGetBoolean`
|
|
/// - `SGetByte`
|
|
/// - `SGetChar`
|
|
/// - `SGetShort`
|
|
/// - `SPut`
|
|
/// - `SPutWide`
|
|
/// - `SPutObject`
|
|
/// - `SPutBoolean`
|
|
/// - `SPutByte`
|
|
/// - `SPutChar`
|
|
/// - `SPutShort`
|
|
///
|
|
/// # Variants that require `method_ids`
|
|
///
|
|
/// `method_ids` is a lookup table that associate its idx to a method.
|
|
///
|
|
/// - `InvokeVirtual`
|
|
/// - `InvokeSuper`
|
|
/// - `InvokeDirect`
|
|
/// - `InvokeStatic`
|
|
/// - `InvokeInterface`
|
|
/// - `InvokePolymorphic`
|
|
///
|
|
/// # Variants that require `proto_ids`
|
|
///
|
|
/// `proto_ids` is a lookup table that associate its idx to a method type.
|
|
///
|
|
/// - `InvokePolymorphic`
|
|
/// - `ConstMethodType`
|
|
///
|
|
/// # Variants that require `call_site_idx`
|
|
///
|
|
/// `call_site_idx` is the idx of the call site used by the instruction
|
|
///
|
|
/// - `InvokeCustom`
|
|
///
|
|
/// # Variants that require `method_handle_idx`
|
|
///
|
|
/// `method_handle_idx` is the idx of the method handle used by the instruction
|
|
///
|
|
/// - `ConstMethodHandle`
|
|
///
|
|
/// # Variants that require `data_offset`
|
|
///
|
|
/// `data_offset` is the offset from the instruction to the location of the data table.
|
|
///
|
|
/// - `FillArrayData`
|
|
/// - `Switch`
|
|
///
|
|
/// # Variants that require `goto_size`
|
|
///
|
|
/// `goto_size` is the size of the goto instruction (wich can be greater than the best size
|
|
/// beause the algo used to compute offset trash)
|
|
///
|
|
/// - `Goto`
|
|
///
|
|
/// # Variants that require `jump_data`
|
|
///
|
|
/// `jump_data` is the address of the current instruction and the lookup table that associate a
|
|
/// label with its addresse.
|
|
///
|
|
/// - `Goto`
|
|
/// - `IfEq`
|
|
/// - `IfNe`
|
|
/// - `IfLt`
|
|
/// - `IfGe`
|
|
/// - `IfGt`
|
|
/// - `IfLe`
|
|
/// - `IfEqZ`
|
|
/// - `IfNeZ`
|
|
/// - `IfLtZ`
|
|
/// - `IfGeZ`
|
|
/// - `IfGtZ`
|
|
/// - `IfLeZ`
|
|
///
|
|
/// # Variants that do not implement this method:
|
|
///
|
|
/// - `Try`
|
|
/// - `Label`
|
|
/// - `DebugLocal`
|
|
/// - `DebugEndLocal`
|
|
/// - `DebugEndPrologue`
|
|
/// - `DebugBeginEpilogue`
|
|
/// - `DebugSourceFile`
|
|
/// - `DebugLine`
|
|
///
|
|
pub fn get_raw_ins(&self, param: GetRawInsParam) -> Result<InsFormat> {
|
|
match (self, param) {
|
|
(Self::ConstString { .. }, GetRawInsParam { strings: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a const-string without knowing the string idx"
|
|
)),
|
|
(
|
|
Self::ConstString { reg, lit },
|
|
GetRawInsParam {
|
|
strings: Some(strings),
|
|
..
|
|
},
|
|
) => {
|
|
let string_idx = strings.get(lit).ok_or(anyhow!(
|
|
"String {} not found in dex builder",
|
|
lit.__repr__(),
|
|
))?;
|
|
match string_idx {
|
|
0..=U16_MAX_AS_USIZE => Ok(InsFormat::Format21C {
|
|
op: 0x1a,
|
|
va: *reg,
|
|
b: *string_idx as u16,
|
|
}),
|
|
_ => Ok(InsFormat::Format31C {
|
|
op: 0x1b,
|
|
va: *reg,
|
|
b: *string_idx as u32,
|
|
}),
|
|
}
|
|
}
|
|
(Self::ConstClass { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a const-class without knowing the class idx"
|
|
)),
|
|
(
|
|
Self::ConstClass { reg, lit },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format21C {
|
|
op: 0x1c,
|
|
va: *reg,
|
|
b: *types
|
|
.get(lit)
|
|
.ok_or(anyhow!("Class {} not found in dex builder", lit.__repr__(),))?
|
|
as u16,
|
|
}),
|
|
(Self::CheckCast { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a check-cast without knowing the type idx"
|
|
)),
|
|
(
|
|
Self::CheckCast { reg, lit },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format21C {
|
|
op: 0x1f,
|
|
va: *reg,
|
|
b: *types
|
|
.get(lit)
|
|
.ok_or(anyhow!("Class {} not found in dex builder", lit.__repr__(),))?
|
|
as u16,
|
|
}),
|
|
(Self::InstanceOf { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a instance-of without knowing the type idx"
|
|
)),
|
|
(
|
|
Self::InstanceOf { dest, obj, lit },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format22C {
|
|
op: 0x20,
|
|
va: *dest,
|
|
vb: *obj,
|
|
c: *types
|
|
.get(lit)
|
|
.ok_or(anyhow!("Class {} not found in dex builder", lit.__repr__(),))?
|
|
as u16,
|
|
}),
|
|
(Self::NewInstance { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a new-instance without knowing the class idx"
|
|
)),
|
|
(
|
|
Self::NewInstance { reg, lit },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format21C {
|
|
op: 0x22,
|
|
va: *reg,
|
|
b: *types
|
|
.get(lit)
|
|
.ok_or(anyhow!("Class {} not found in dex builder", lit.__repr__(),))?
|
|
as u16,
|
|
}),
|
|
(Self::NewArray { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a new-array without knowing the type idx"
|
|
)),
|
|
(
|
|
Self::NewArray { reg, size_reg, lit },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format22C {
|
|
op: 0x23,
|
|
va: *reg,
|
|
vb: *size_reg,
|
|
c: *types
|
|
.get(lit)
|
|
.ok_or(anyhow!("Type {} not found in dex builder", lit.__repr__(),))?
|
|
as u16,
|
|
}),
|
|
(Self::FilledNewArray { .. }, GetRawInsParam { type_ids: None, .. }) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a filled-new-array without knowing the type idx"
|
|
)),
|
|
(
|
|
Self::FilledNewArray { reg_values, type_ },
|
|
GetRawInsParam {
|
|
type_ids: Some(types),
|
|
..
|
|
},
|
|
) => {
|
|
let mut last = None;
|
|
let mut first = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = reg_values.len();
|
|
for r in reg_values.iter().cloned() {
|
|
if first.is_none() {
|
|
first = Some(r);
|
|
}
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if four_bites && len <= 5 {
|
|
let mut regs = vec![];
|
|
for reg in reg_values.iter().cloned() {
|
|
regs.push(reg);
|
|
}
|
|
while regs.len() != 5 {
|
|
regs.push(0);
|
|
}
|
|
let [vc, vd, ve, vf, vg]: [u8; 5] = regs
|
|
.into_iter()
|
|
.map(|r| r as u8)
|
|
.collect::<Vec<_>>()
|
|
.try_into()
|
|
.ok()
|
|
.unwrap();
|
|
let a = reg_values.len() as u8;
|
|
Ok(InsFormat::Format35C {
|
|
op: 0x24,
|
|
a,
|
|
vc,
|
|
ve,
|
|
vd,
|
|
vf,
|
|
vg,
|
|
b: *types.get(type_).ok_or(anyhow!(
|
|
"Type {} not found in dex builder",
|
|
type_.__repr__(),
|
|
))? as u16,
|
|
})
|
|
} else if consec && len <= 255 {
|
|
let a = reg_values.len() as u8;
|
|
let vc = if let Some(vc) = first { vc } else { 0 };
|
|
Ok(InsFormat::Format3RC {
|
|
op: 0x25,
|
|
a,
|
|
vc,
|
|
b: *types.get(type_).ok_or(anyhow!(
|
|
"Type {} not found in dex builder",
|
|
type_.__repr__(),
|
|
))? as u16,
|
|
})
|
|
} else {
|
|
// Not supposed to happend with a sanitized array
|
|
panic!("Invalid NewArrayInstruction {self:?}")
|
|
}
|
|
}
|
|
(
|
|
Self::FillArrayData { .. },
|
|
GetRawInsParam {
|
|
data_offset: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a fill-array-data without knowing the offset \
|
|
to the -fill-array-data-payload"
|
|
)),
|
|
(
|
|
Self::FillArrayData { arr, .. },
|
|
GetRawInsParam {
|
|
data_offset: Some(data_offset),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format31T {
|
|
op: 0x26,
|
|
va: *arr,
|
|
b: data_offset,
|
|
}),
|
|
(
|
|
Self::Goto { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the size of a goto without knowing the branch offset"
|
|
)),
|
|
(
|
|
Self::Goto { .. },
|
|
GetRawInsParam {
|
|
goto_size: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the size of a goto without knowing the size of the instruction"
|
|
)),
|
|
(
|
|
Self::Goto { label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
goto_size: Some(goto_size),
|
|
..
|
|
},
|
|
) => {
|
|
let label_addr = label_addrs.get(label).ok_or(anyhow!(
|
|
"Label {} not found, but found goto with this label",
|
|
label,
|
|
))?;
|
|
let branch_offset = *label_addr as i32 - addr as i32;
|
|
if goto_size == 2 && (I8_MIN_AS_I32..=I8_MAX_AS_I32).contains(&branch_offset) {
|
|
Ok(InsFormat::Format10T {
|
|
op: 0x28,
|
|
a: branch_offset as i8,
|
|
})
|
|
} else if goto_size == 4
|
|
&& (I16_MIN_AS_I32..=I16_MAX_AS_I32).contains(&branch_offset)
|
|
{
|
|
Ok(InsFormat::Format20T {
|
|
op: 0x29,
|
|
a: branch_offset as i16,
|
|
})
|
|
} else if goto_size == 6 {
|
|
Ok(InsFormat::Format30T {
|
|
op: 0x2a,
|
|
a: branch_offset,
|
|
})
|
|
} else {
|
|
Err(anyhow!("Invalid goto_size and/or data_offset value"))
|
|
}
|
|
}
|
|
(
|
|
Self::Switch { .. },
|
|
GetRawInsParam {
|
|
data_offset: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a switch without knowing the offset \
|
|
to the packed/sparse-switch-payload"
|
|
)),
|
|
(
|
|
Self::Switch { reg, .. },
|
|
GetRawInsParam {
|
|
data_offset: Some(data_offset),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format31T {
|
|
op: if self.is_packed()? { 0x2b } else { 0x2c },
|
|
va: *reg,
|
|
b: data_offset,
|
|
}),
|
|
(
|
|
Self::IfEq { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-eq without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfNe { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-ne without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfLt { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-lt without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfGe { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-ge without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfGt { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-gt without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfLe { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-le without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfEqZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-eqz without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfNeZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-nez without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfLtZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-ltz without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfGeZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-gez without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfGtZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-gtz without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfLeZ { .. },
|
|
GetRawInsParam {
|
|
jump_data: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a if-lez without knowing \
|
|
the offset to the branch"
|
|
)),
|
|
(
|
|
Self::IfEq { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x32, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfNe { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x33, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfLt { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x34, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfGe { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x35, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfGt { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x36, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfLe { a, b, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_if!(0x37, label_addrs, label, addr, a, b),
|
|
(
|
|
Self::IfEqZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x38, label_addrs, label, addr, a),
|
|
(
|
|
Self::IfNeZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x39, label_addrs, label, addr, a),
|
|
(
|
|
Self::IfLtZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x3a, label_addrs, label, addr, a),
|
|
(
|
|
Self::IfGeZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x3b, label_addrs, label, addr, a),
|
|
(
|
|
Self::IfGtZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x3c, label_addrs, label, addr, a),
|
|
(
|
|
Self::IfLeZ { a, label },
|
|
GetRawInsParam {
|
|
jump_data: Some((addr, label_addrs)),
|
|
..
|
|
},
|
|
) => raw_ins_ifz!(0x3d, label_addrs, label, addr, a),
|
|
(
|
|
Self::IGet { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetWide { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-wide without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetObject { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-object without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetBoolean { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-boolean without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetByte { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-byte without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetChar { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-char without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGetShort { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iget-short without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPut { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutWide { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-wide without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutObject { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-object without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutBoolean { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-boolean without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutByte { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-byte without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutChar { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-char without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IPutShort { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a iput-short without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::IGet { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x52, fields, field, to, obj),
|
|
(
|
|
Self::IGetWide { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x53, fields, field, to, obj),
|
|
(
|
|
Self::IGetObject { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x54, fields, field, to, obj),
|
|
(
|
|
Self::IGetBoolean { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x55, fields, field, to, obj),
|
|
(
|
|
Self::IGetByte { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x56, fields, field, to, obj),
|
|
(
|
|
Self::IGetChar { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x57, fields, field, to, obj),
|
|
(
|
|
Self::IGetShort { to, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x58, fields, field, to, obj),
|
|
(
|
|
Self::IPut { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x59, fields, field, from, obj),
|
|
(
|
|
Self::IPutWide { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5a, fields, field, from, obj),
|
|
(
|
|
Self::IPutObject { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5b, fields, field, from, obj),
|
|
(
|
|
Self::IPutBoolean { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5c, fields, field, from, obj),
|
|
(
|
|
Self::IPutByte { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5d, fields, field, from, obj),
|
|
(
|
|
Self::IPutChar { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5e, fields, field, from, obj),
|
|
(
|
|
Self::IPutShort { from, obj, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_iget_put!(0x5f, fields, field, from, obj),
|
|
(
|
|
Self::SGet { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetWide { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-wide without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetObject { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-object without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetBoolean { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-boolean without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetByte { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-byte without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetChar { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-char without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGetShort { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sget-short without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPut { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutWide { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-wide without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutObject { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-object without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutBoolean { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-boolean without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutByte { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-byte without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutChar { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-char without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SPutShort { .. },
|
|
GetRawInsParam {
|
|
field_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a sput-short without knowing the field idx"
|
|
)),
|
|
(
|
|
Self::SGet { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x60, fields, field, to),
|
|
(
|
|
Self::SGetWide { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x61, fields, field, to),
|
|
(
|
|
Self::SGetObject { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x62, fields, field, to),
|
|
(
|
|
Self::SGetBoolean { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x63, fields, field, to),
|
|
(
|
|
Self::SGetByte { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x64, fields, field, to),
|
|
(
|
|
Self::SGetChar { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x65, fields, field, to),
|
|
(
|
|
Self::SGetShort { to, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x66, fields, field, to),
|
|
(
|
|
Self::SPut { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x67, fields, field, from),
|
|
(
|
|
Self::SPutWide { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x68, fields, field, from),
|
|
(
|
|
Self::SPutObject { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x69, fields, field, from),
|
|
(
|
|
Self::SPutBoolean { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x6a, fields, field, from),
|
|
(
|
|
Self::SPutByte { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x6b, fields, field, from),
|
|
(
|
|
Self::SPutChar { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x6c, fields, field, from),
|
|
(
|
|
Self::SPutShort { from, field },
|
|
GetRawInsParam {
|
|
field_ids: Some(fields),
|
|
..
|
|
},
|
|
) => raw_ins_sget_put!(0x6d, fields, field, from),
|
|
(
|
|
Self::InvokeVirtual { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-virtual without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokeSuper { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-super without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokeDirect { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-direct without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokeStatic { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-static without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokeInterface { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-interface without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokeVirtual { args, method },
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
..
|
|
},
|
|
) => raw_ins_invoke!(0x6e, 0x74, methods, method, args),
|
|
(
|
|
Self::InvokeSuper { args, method },
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
..
|
|
},
|
|
) => raw_ins_invoke!(0x6f, 0x75, methods, method, args),
|
|
(
|
|
Self::InvokeDirect { args, method },
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
..
|
|
},
|
|
) => raw_ins_invoke!(0x70, 0x76, methods, method, args),
|
|
(
|
|
Self::InvokeStatic { args, method },
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
..
|
|
},
|
|
) => raw_ins_invoke!(0x71, 0x77, methods, method, args),
|
|
(
|
|
Self::InvokeInterface { args, method },
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
..
|
|
},
|
|
) => raw_ins_invoke!(0x72, 0x78, methods, method, args),
|
|
(
|
|
Self::InvokePolymorphic { .. },
|
|
GetRawInsParam {
|
|
method_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-polymorphic without knowing \
|
|
the method idx"
|
|
)),
|
|
(
|
|
Self::InvokePolymorphic { .. },
|
|
GetRawInsParam {
|
|
proto_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-polymorphic without knowing \
|
|
the proto idx"
|
|
)),
|
|
(
|
|
Self::InvokePolymorphic {
|
|
args,
|
|
method,
|
|
proto,
|
|
},
|
|
GetRawInsParam {
|
|
method_ids: Some(methods),
|
|
proto_ids: Some(protos),
|
|
..
|
|
},
|
|
) => {
|
|
let meth_idx = methods.get(method).ok_or(anyhow!(
|
|
"Method {} (method of class {}) not found in dex builder",
|
|
method.__repr__(),
|
|
method.class_.__repr__(),
|
|
))?;
|
|
let proto_idx = protos.get(proto).ok_or(anyhow!(
|
|
"Prototype {} not found in dex builder",
|
|
proto.__repr__(),
|
|
))?;
|
|
debug_assert!(
|
|
*meth_idx <= u16::MAX as usize,
|
|
"methode id too big for invoke instruction"
|
|
);
|
|
debug_assert!(
|
|
*proto_idx <= u16::MAX as usize,
|
|
"proto id too big for invoke instruction"
|
|
);
|
|
let meth_idx = *meth_idx as u16;
|
|
let proto_idx = *proto_idx as u16;
|
|
let mut last = None;
|
|
let mut first = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if first.is_none() {
|
|
first = Some(r);
|
|
}
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if four_bites && len <= 5 {
|
|
let mut regs = vec![];
|
|
for reg in args.iter().cloned() {
|
|
regs.push(reg);
|
|
}
|
|
while regs.len() != 5 {
|
|
regs.push(0);
|
|
}
|
|
let [vc, vd, ve, vf, vg]: [u8; 5] = regs
|
|
.into_iter()
|
|
.map(|r| r as u8)
|
|
.collect::<Vec<_>>()
|
|
.try_into()
|
|
.ok()
|
|
.unwrap();
|
|
let a = args.len() as u8;
|
|
Ok(InsFormat::Format45CC {
|
|
op: 0xfa,
|
|
a,
|
|
vc,
|
|
ve,
|
|
vd,
|
|
vf,
|
|
vg,
|
|
b: meth_idx,
|
|
h: proto_idx,
|
|
})
|
|
} else if consec && len <= 255 {
|
|
let a = args.len() as u8;
|
|
let vc = if let Some(vc) = first { vc } else { 0 };
|
|
Ok(InsFormat::Format4RCC {
|
|
op: 0xfb,
|
|
a,
|
|
vc,
|
|
b: meth_idx,
|
|
h: proto_idx,
|
|
})
|
|
} else {
|
|
// Not supposed to happend with a sanitized invoke
|
|
bail!("Invalid Invoke instruction {self:?}")
|
|
}
|
|
}
|
|
(
|
|
Self::InvokeCustom { .. },
|
|
GetRawInsParam {
|
|
call_site_idx: None,
|
|
..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a invoke-custom without knowing the \
|
|
call site idx"
|
|
)),
|
|
(
|
|
Self::InvokeCustom { args, .. },
|
|
GetRawInsParam {
|
|
call_site_idx: Some(call_site_idx),
|
|
..
|
|
},
|
|
) => {
|
|
let mut last = None;
|
|
let mut first = None;
|
|
let mut consec = true;
|
|
let mut four_bites = true;
|
|
let len = args.len();
|
|
for r in args.iter().cloned() {
|
|
if first.is_none() {
|
|
first = Some(r);
|
|
}
|
|
if let Some(last) = last {
|
|
if r != last + 1 {
|
|
consec = false;
|
|
}
|
|
}
|
|
if r & 0b1111_0000 != 0 {
|
|
four_bites = false;
|
|
}
|
|
last = Some(r);
|
|
}
|
|
if four_bites && len <= 5 {
|
|
let mut regs = vec![];
|
|
for reg in args.iter().cloned() {
|
|
regs.push(reg);
|
|
}
|
|
while regs.len() != 5 {
|
|
regs.push(0);
|
|
}
|
|
let [vc, vd, ve, vf, vg]: [u8; 5] = regs
|
|
.into_iter()
|
|
.map(|r| r as u8)
|
|
.collect::<Vec<_>>()
|
|
.try_into()
|
|
.ok()
|
|
.unwrap();
|
|
let a = args.len() as u8;
|
|
Ok(InsFormat::Format35C {
|
|
op: 0xfc,
|
|
a,
|
|
vc,
|
|
ve,
|
|
vd,
|
|
vf,
|
|
vg,
|
|
b: call_site_idx as u16,
|
|
})
|
|
} else if consec && len <= 255 {
|
|
let a = args.len() as u8;
|
|
let vc = if let Some(vc) = first { vc } else { 0 };
|
|
Ok(InsFormat::Format3RC {
|
|
op: 0xfd,
|
|
a,
|
|
vc,
|
|
b: call_site_idx as u16,
|
|
})
|
|
} else {
|
|
// Not supposed to happend with a sanitized invoke
|
|
bail!("Invalid Invoke instruction {self:?}")
|
|
}
|
|
}
|
|
(
|
|
Self::ConstMethodHandle { .. },
|
|
GetRawInsParam {
|
|
method_handle_idx: None,
|
|
..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a const-method-handle \
|
|
without knowing the method handle idx"
|
|
)),
|
|
(
|
|
Self::ConstMethodHandle { to, .. },
|
|
GetRawInsParam {
|
|
method_handle_idx: Some(handle_idx),
|
|
..
|
|
},
|
|
) => Ok(InsFormat::Format21C {
|
|
op: 0xfe,
|
|
va: *to,
|
|
b: handle_idx as u16,
|
|
}),
|
|
(
|
|
Self::ConstMethodType { .. },
|
|
GetRawInsParam {
|
|
proto_ids: None, ..
|
|
},
|
|
) => Err(anyhow!(
|
|
"Cannot get the raw instruction of a const-method-type \
|
|
without knowing the proto idx"
|
|
)),
|
|
(
|
|
Self::ConstMethodType { to, proto },
|
|
GetRawInsParam {
|
|
proto_ids: Some(protos),
|
|
..
|
|
},
|
|
) => {
|
|
let proto_idx = protos.get(proto).ok_or(anyhow!(
|
|
"Prototype {} not found in dex builder",
|
|
proto.__repr__(),
|
|
))?;
|
|
Ok(InsFormat::Format21C {
|
|
op: 0xff,
|
|
va: *to,
|
|
b: *proto_idx as u16,
|
|
})
|
|
}
|
|
(Self::Try { .. }, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Try instruction cannot be converted to raw instruction"
|
|
)),
|
|
(Self::Label { .. }, GetRawInsParam { .. }) => {
|
|
Err(anyhow!("Label cannot be converted to raw instruction"))
|
|
}
|
|
(Self::DebugLocal { .. }, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::DebugEndLocal { .. }, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::DebugEndPrologue {}, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::DebugBeginEpilogue {}, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::DebugSourceFile { .. }, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::DebugLine { .. }, GetRawInsParam { .. }) => Err(anyhow!(
|
|
"Debug information cannot be converted to raw instruction"
|
|
)),
|
|
(Self::Nop {}, GetRawInsParam { .. }) => Ok(InsFormat::Format10X { op: 0x00 }),
|
|
(Self::Move { from, to }, GetRawInsParam { .. }) => match (to, from) {
|
|
(0..=0b0000_1111, 0..=0b0000_1111) => Ok(InsFormat::Format12X {
|
|
op: 0x01,
|
|
va: *to as u8,
|
|
vb: *from as u8,
|
|
}),
|
|
(0..=0xff, _) => Ok(InsFormat::Format22X {
|
|
op: 0x02,
|
|
va: *to as u8,
|
|
vb: *from,
|
|
}),
|
|
(_, _) => Ok(InsFormat::Format32X {
|
|
op: 0x03,
|
|
va: *to,
|
|
vb: *from,
|
|
}),
|
|
},
|
|
(Self::MoveWide { from, to }, GetRawInsParam { .. }) => match (to, from) {
|
|
(0..=0b0000_1111, 0..=0b0000_1111) => Ok(InsFormat::Format12X {
|
|
op: 0x04,
|
|
va: *to as u8,
|
|
vb: *from as u8,
|
|
}),
|
|
(0..=0xff, _) => Ok(InsFormat::Format22X {
|
|
op: 0x05,
|
|
va: *to as u8,
|
|
vb: *from,
|
|
}),
|
|
(_, _) => Ok(InsFormat::Format32X {
|
|
op: 0x06,
|
|
va: *to,
|
|
vb: *from,
|
|
}),
|
|
},
|
|
(Self::MoveObject { from, to }, GetRawInsParam { .. }) => match (to, from) {
|
|
(0..=0b0000_1111, 0..=0b0000_1111) => Ok(InsFormat::Format12X {
|
|
op: 0x07,
|
|
va: *to as u8,
|
|
vb: *from as u8,
|
|
}),
|
|
(0..=0xff, _) => Ok(InsFormat::Format22X {
|
|
op: 0x08,
|
|
va: *to as u8,
|
|
vb: *from,
|
|
}),
|
|
(_, _) => Ok(InsFormat::Format32X {
|
|
op: 0x09,
|
|
va: *to,
|
|
vb: *from,
|
|
}),
|
|
},
|
|
(Self::MoveResult { to }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x0a, va: *to })
|
|
}
|
|
(Self::MoveResultWide { to }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x0b, va: *to })
|
|
}
|
|
(Self::MoveResultObject { to }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x0c, va: *to })
|
|
}
|
|
(Self::MoveException { to }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x0d, va: *to })
|
|
}
|
|
(Self::ReturnVoid {}, GetRawInsParam { .. }) => Ok(InsFormat::Format10X { op: 0x0e }),
|
|
(Self::Return { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x0f, va: *reg })
|
|
}
|
|
(Self::ReturnWide { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x10, va: *reg })
|
|
}
|
|
(Self::ReturnObject { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x11, va: *reg })
|
|
}
|
|
(Self::Const { lit, reg }, GetRawInsParam { .. }) => match (reg, lit) {
|
|
(0..=0b0000_1111, -8..=7) => Ok(InsFormat::Format11N {
|
|
op: 0x12,
|
|
va: *reg,
|
|
b: *lit as i8,
|
|
}),
|
|
(_, I16_MIN_AS_I32..=I16_MAX_AS_I32) => Ok(InsFormat::Format21S {
|
|
op: 0x13,
|
|
va: *reg,
|
|
b: *lit as i16,
|
|
}),
|
|
(_, lit) if lit % 0x1_0000 == 0 => Ok(InsFormat::Format21H {
|
|
op: 0x15,
|
|
va: *reg,
|
|
b: (*lit / 0x1_0000) as i16,
|
|
}),
|
|
(_, _) => Ok(InsFormat::Format31I {
|
|
op: 0x14,
|
|
va: *reg,
|
|
b: *lit,
|
|
}),
|
|
},
|
|
(Self::ConstWide { lit, reg }, GetRawInsParam { .. }) => match lit {
|
|
I16_MIN_AS_I64..=I16_MAX_AS_I64 => Ok(InsFormat::Format21S {
|
|
op: 0x16,
|
|
va: *reg,
|
|
b: *lit as i16,
|
|
}),
|
|
I32_MIN_AS_I64..=I32_MAX_AS_I64 => Ok(InsFormat::Format31I {
|
|
op: 0x17,
|
|
va: *reg,
|
|
b: *lit as i32,
|
|
}),
|
|
lit if lit % 0x1_0000_0000_0000 == 0 => Ok(InsFormat::Format21H {
|
|
op: 0x19,
|
|
va: *reg,
|
|
b: (*lit / 0x1_0000_0000_0000) as i16,
|
|
}),
|
|
_ => Ok(InsFormat::Format51L {
|
|
op: 0x18,
|
|
va: *reg,
|
|
b: *lit,
|
|
}),
|
|
},
|
|
(Self::MonitorEnter { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x1d, va: *reg })
|
|
}
|
|
(Self::MonitorExit { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x1e, va: *reg })
|
|
}
|
|
(Self::ArrayLength { dest, arr }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x21,
|
|
va: *dest,
|
|
vb: *arr,
|
|
}),
|
|
(Self::Throw { reg }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format11X { op: 0x27, va: *reg })
|
|
}
|
|
(Self::CmpLFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x2d,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::CmpGFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x2e,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::CmpLDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x2f,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::CmpGDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x30,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::CmpLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x31,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AGet { dest, arr, idx }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x44,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
}),
|
|
(Self::AGetWide { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x45,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::AGetObject { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x46,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::AGetBoolean { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x47,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::AGetByte { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x48,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::AGetChar { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x49,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::AGetShort { dest, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x4a,
|
|
va: *dest,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APut { from, arr, idx }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x4b,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
}),
|
|
(Self::APutWide { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x4c,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APutObject { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x4d,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APutBoolean { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x4e,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APutByte { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x4f,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APutChar { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x50,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::APutShort { from, arr, idx }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format23X {
|
|
op: 0x51,
|
|
va: *from,
|
|
vb: *arr,
|
|
vc: *idx,
|
|
})
|
|
}
|
|
(Self::NegInt { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x7b,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::NotInt { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x7c,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::NegLong { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x7d,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::NotLong { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x7e,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::NegFloat { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x7f,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::NegDouble { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x80,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::IntToLong { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x81,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::IntToFloat { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x82,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::IntToDouble { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x83,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::LongToInt { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x84,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::LongToFloat { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x85,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::LongToDouble { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x86,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::FloatToInt { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x87,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::FloatToLong { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x88,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::FloatToDouble { dest, val }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format12X {
|
|
op: 0x89,
|
|
va: *dest,
|
|
vb: *val,
|
|
})
|
|
}
|
|
(Self::DoubleToInt { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x8a,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::DoubleToLong { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x8b,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::DoubleToFloat { dest, val }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format12X {
|
|
op: 0x8c,
|
|
va: *dest,
|
|
vb: *val,
|
|
})
|
|
}
|
|
(Self::IntToByte { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x8d,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::IntToChar { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x8e,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::IntToShort { dest, val }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0x8f,
|
|
va: *dest,
|
|
vb: *val,
|
|
}),
|
|
(Self::AddInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x90,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::SubInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x91,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::MulInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x92,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::DivInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x93,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::RemInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x94,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AndInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x95,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::OrInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x96,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::XorInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x97,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::ShlInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x98,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::ShrInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x99,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::UshrInt { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9a,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AddLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9b,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::SubLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9c,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::MulLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9d,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::DivLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9e,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::RemLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0x9f,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AndLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa0,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::OrLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa1,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::XorLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa2,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::ShlLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa3,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::ShrLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa4,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::UshrLong { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa5,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AddFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa6,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::SubFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa7,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::MulFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa8,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::DivFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xa9,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::RemFloat { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xaa,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AddDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xab,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::SubDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xac,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::MulDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xad,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::DivDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xae,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::RemDouble { dest, b, c }, GetRawInsParam { .. }) => Ok(InsFormat::Format23X {
|
|
op: 0xaf,
|
|
va: *dest,
|
|
vb: *b,
|
|
vc: *c,
|
|
}),
|
|
(Self::AddInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb0,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::SubInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb1,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::MulInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb2,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::DivInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb3,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::RemInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb4,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AndInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb5,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::OrInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb6,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::XorInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb7,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::ShlInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb8,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::ShrInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xb9,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::UshrInt2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xba,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AddLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xbb,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::SubLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xbc,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::MulLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xbd,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::DivLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xbe,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::RemLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xbf,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AndLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc0,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::OrLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc1,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::XorLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc2,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::ShlLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc3,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::ShrLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc4,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::UshrLong2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc5,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AddFloat2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc6,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::SubFloat2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc7,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::MulFloat2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc8,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::DivFloat2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xc9,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::RemFloat2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xca,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AddDouble2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xcb,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::SubDouble2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xcc,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::MulDouble2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xcd,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::DivDouble2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xce,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::RemDouble2Addr { b, dest }, GetRawInsParam { .. }) => Ok(InsFormat::Format12X {
|
|
op: 0xcf,
|
|
va: *dest,
|
|
vb: *b,
|
|
}),
|
|
(Self::AddIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"add-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (add-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (add-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xd8,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd0,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::RsubIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"sub-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (sub-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (sub-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xd9,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd1,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::MulIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"mul-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (mul-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (mul-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xda,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd2,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::DivIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"div-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (div-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (div-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xdb,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd3,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::RemIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"rem-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (rem-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (rem-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xdc,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd4,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::AndIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"and-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (and-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (and-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xdd,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd5,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::OrIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"or-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (or-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (or-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xde,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd6,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::XorIntLit { lit, b, dest }, GetRawInsParam { .. }) => {
|
|
let mut reg_on_4_bit = true;
|
|
let mut lit_on_8_bits = true;
|
|
if dest & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if b & 0b1111_0000 != 0 {
|
|
reg_on_4_bit = false;
|
|
}
|
|
if *lit < I8_MIN_AS_I16 || *lit > I8_MAX_AS_I16 {
|
|
lit_on_8_bits = false;
|
|
}
|
|
if !reg_on_4_bit && !lit_on_8_bits {
|
|
// Should not happen on a sanitized op
|
|
panic!(
|
|
"xor-int/lit uses either registers indexed on 4 bits, and a literal \
|
|
encoded on 16 bits (xor-int/lit16), or registers indexed on 8 bits and \
|
|
a literal encoded on 8 bits (xor-int/lit8). Found reg {} and {}, and lit \
|
|
{}",
|
|
dest, b, lit
|
|
)
|
|
} else if lit_on_8_bits {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xdf,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit as i8,
|
|
})
|
|
} else {
|
|
Ok(InsFormat::Format22S {
|
|
op: 0xd7,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
(Self::ShlIntLit { dest, b, lit }, GetRawInsParam { .. }) => Ok(InsFormat::Format22B {
|
|
op: 0xe0,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
}),
|
|
(Self::ShrIntLit { dest, b, lit }, GetRawInsParam { .. }) => Ok(InsFormat::Format22B {
|
|
op: 0xe1,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
}),
|
|
(Self::UshrIntLit { dest, b, lit }, GetRawInsParam { .. }) => {
|
|
Ok(InsFormat::Format22B {
|
|
op: 0xe2,
|
|
va: *dest,
|
|
vb: *b,
|
|
c: *lit,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Test is a switch instruction is packed or not.
|
|
///
|
|
/// Return an error if the instruction is not a `Switch`.
|
|
pub fn is_packed(&self) -> Result<bool> {
|
|
match self {
|
|
Self::Switch { branches, .. } => {
|
|
let mut last = None;
|
|
let mut keys = branches.keys().collect::<Vec<_>>();
|
|
keys.sort();
|
|
for key in keys.into_iter().cloned() {
|
|
if let Some(last) = &last {
|
|
if *last != key - 1 {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
last = Some(key);
|
|
}
|
|
Ok(true)
|
|
}
|
|
_ => Err(anyhow!("Instruction is not a Switch")),
|
|
}
|
|
}
|
|
|
|
/// Return the size of a goto instruction from its address and an interval for
|
|
/// the destination address in worst case senario.
|
|
pub fn goto_size_from_branch_offset_interval(
|
|
addr_goto: usize,
|
|
min_addr_branch: usize,
|
|
max_addr_branch: usize,
|
|
) -> Result<usize> {
|
|
let worst_offset = if max_addr_branch as i32 - addr_goto as i32
|
|
> addr_goto as i32 - min_addr_branch as i32
|
|
{
|
|
max_addr_branch as i32 - addr_goto as i32
|
|
} else {
|
|
min_addr_branch as i32 - addr_goto as i32
|
|
};
|
|
match worst_offset {
|
|
I8_MIN_AS_I32..=I8_MAX_AS_I32 => Ok(2),
|
|
I16_MIN_AS_I32..=I16_MAX_AS_I32 => Ok(4),
|
|
_ => Ok(6),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[pyclass(eq)]
|
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
pub struct CallSite {
|
|
#[pyo3(get)]
|
|
pub method_handle: MethodHandle,
|
|
#[pyo3(get)]
|
|
pub name: DexString,
|
|
#[pyo3(get)]
|
|
pub type_: IdMethodType,
|
|
#[pyo3(get)]
|
|
pub args: Vec<DexValue>,
|
|
}
|
|
|
|
#[pymethods]
|
|
impl CallSite {
|
|
pub fn to_json(&self) -> Result<String> {
|
|
Ok(serde_json::to_string(self)?)
|
|
}
|
|
|
|
#[staticmethod]
|
|
pub fn from_json(json: &str) -> Result<Self> {
|
|
Ok(serde_json::from_str(json)?)
|
|
}
|
|
|
|
#[new]
|
|
pub fn new(
|
|
method_handle: MethodHandle,
|
|
name: DexString,
|
|
type_: IdMethodType,
|
|
args: Vec<DexValue>,
|
|
) -> Self {
|
|
let args = args.to_vec();
|
|
Self {
|
|
method_handle,
|
|
name,
|
|
type_,
|
|
args,
|
|
}
|
|
}
|
|
|
|
pub fn __str__(&self) -> String {
|
|
let args = self
|
|
.args
|
|
.iter()
|
|
.map(|arg| arg.__str__())
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
format!(
|
|
"call({} {} {} ({})",
|
|
self.method_handle.__str__(),
|
|
self.type_.__str__(),
|
|
self.name.__str__(),
|
|
args
|
|
)
|
|
}
|
|
|
|
pub fn __repr__(&self) -> String {
|
|
let args = self
|
|
.args
|
|
.iter()
|
|
.map(|arg| arg.__str__())
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
format!(
|
|
"CallSite({}, {}, {}, [{}]",
|
|
self.method_handle.__repr__(),
|
|
self.type_.__repr__(),
|
|
self.name.__repr__(),
|
|
args
|
|
)
|
|
}
|
|
|
|
/// Return all strings referenced in the call site.
|
|
pub fn get_all_strings(&self) -> HashSet<DexString> {
|
|
let mut strings = HashSet::new();
|
|
strings.insert(self.name.clone());
|
|
strings.extend(self.method_handle.get_all_strings());
|
|
strings.extend(self.type_.get_all_strings());
|
|
for arg in &self.args {
|
|
strings.extend(arg.get_all_strings());
|
|
}
|
|
strings
|
|
}
|
|
|
|
/// Return all types referenced in the call site.
|
|
pub fn get_all_types(&self) -> HashSet<IdType> {
|
|
let mut type_ids = HashSet::new();
|
|
type_ids.extend(self.method_handle.get_all_types());
|
|
type_ids.extend(self.type_.get_all_types());
|
|
for arg in &self.args {
|
|
type_ids.extend(arg.get_all_types());
|
|
}
|
|
type_ids
|
|
}
|
|
|
|
/// Return all prototypes referenced in the call site.
|
|
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
|
|
let mut protos = HashSet::new();
|
|
protos.extend(self.method_handle.get_all_protos());
|
|
protos.insert(self.type_.clone());
|
|
for arg in &self.args {
|
|
protos.extend(arg.get_all_protos());
|
|
}
|
|
protos
|
|
}
|
|
|
|
/// Return all field ids referenced in the call site.
|
|
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
|
|
let mut fields = HashSet::new();
|
|
fields.extend(self.method_handle.get_all_field_ids());
|
|
for arg in &self.args {
|
|
fields.extend(arg.get_all_field_ids());
|
|
}
|
|
fields
|
|
}
|
|
|
|
/// Return all method ids referenced in the call site.
|
|
pub fn get_all_method_ids(&self) -> HashSet<IdMethod> {
|
|
let mut methods = HashSet::new();
|
|
methods.extend(self.method_handle.get_all_method_ids());
|
|
for arg in &self.args {
|
|
methods.extend(arg.get_all_method_ids());
|
|
}
|
|
methods
|
|
}
|
|
|
|
/// Return all method handles referenced in the call site.
|
|
pub fn get_all_method_handles(&self) -> HashSet<MethodHandle> {
|
|
let mut handles = HashSet::new();
|
|
handles.insert(self.method_handle.clone());
|
|
for arg in &self.args {
|
|
handles.extend(arg.get_all_method_handles());
|
|
}
|
|
handles
|
|
}
|
|
}
|
|
|
|
impl<V: Visitor> Visitable<V> for CallSite {
|
|
fn default_visit(&self, v: &mut V) -> Result<()> {
|
|
v.visit_method_handle(&self.method_handle)?;
|
|
v.visit_string(&self.name)?;
|
|
v.visit_method_type(&self.type_)?;
|
|
for arg in &self.args {
|
|
v.visit_value(arg)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<V: VisitorMut> VisitableMut<V> for CallSite {
|
|
fn default_visit_mut(self, v: &mut V) -> Result<Self> {
|
|
Ok(Self {
|
|
method_handle: v.visit_method_handle(self.method_handle)?,
|
|
name: v.visit_string(self.name)?,
|
|
type_: v.visit_method_type(self.type_)?,
|
|
args: self
|
|
.args
|
|
.into_iter()
|
|
.map(|arg| v.visit_value(arg))
|
|
.collect::<Result<_>>()?,
|
|
})
|
|
}
|
|
}
|