androscalpel/androscalpel/src/instructions.rs
2025-01-23 12:36:51 +01:00

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<_>>()?,
})
}
}