use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use scroll::{self, ctx, Pread, Uleb128, LE};
use crate::{
annotation::EncodedAnnotation,
byte,
error::Error,
field::{FieldId, FieldIdItem},
int,
jtype::{Type, TypeId},
long,
method::{MethodHandleId, MethodHandleItem, MethodId, MethodIdItem, ProtoId, ProtoIdItem},
short,
string::{DexString, StringId},
ubyte, uint, ushort, Result,
};
#[derive(Debug, PartialEq)]
pub enum EncodedValue {
Byte(byte),
Short(short),
Char(ushort),
Int(int),
Long(long),
Type(Type),
Float(f32),
Double(f64),
MethodType(ProtoIdItem),
MethodHandle(MethodHandleItem),
String(DexString),
Field(FieldIdItem),
Method(MethodIdItem),
Annotation(EncodedAnnotation),
Array(Vec<EncodedValue>),
Enum(FieldIdItem),
Null,
Boolean(bool),
}
impl PartialEq<ushort> for EncodedValue {
fn eq(&self, other: &ushort) -> bool {
match self {
EncodedValue::Char(us) => us == other,
_ => false,
}
}
}
impl PartialEq<int> for EncodedValue {
fn eq(&self, other: &int) -> bool {
match self {
EncodedValue::Int(val) => val == other,
_ => false,
}
}
}
impl PartialEq<long> for EncodedValue {
fn eq(&self, other: &long) -> bool {
match self {
EncodedValue::Long(l) => l == other,
_ => false,
}
}
}
impl PartialEq<short> for EncodedValue {
fn eq(&self, other: &short) -> bool {
match self {
EncodedValue::Short(b) => b == other,
_ => false,
}
}
}
impl PartialEq<byte> for EncodedValue {
fn eq(&self, other: &byte) -> bool {
match self {
EncodedValue::Byte(b) => b == other,
_ => false,
}
}
}
impl PartialEq<f64> for EncodedValue {
fn eq(&self, other: &f64) -> bool {
match self {
EncodedValue::Double(d) => d == other,
_ => false,
}
}
}
impl PartialEq<f32> for EncodedValue {
fn eq(&self, other: &f32) -> bool {
match self {
EncodedValue::Float(f) => f == other,
_ => false,
}
}
}
impl PartialEq<bool> for EncodedValue {
fn eq(&self, other: &bool) -> bool {
match self {
EncodedValue::Boolean(b) => b == other,
_ => false,
}
}
}
impl PartialEq<Type> for EncodedValue {
fn eq(&self, other: &Type) -> bool {
match self {
EncodedValue::Type(t) => t == other,
_ => false,
}
}
}
impl PartialEq<DexString> for EncodedValue {
fn eq(&self, other: &DexString) -> bool {
match self {
EncodedValue::String(s) => s == other,
_ => false,
}
}
}
impl PartialEq<str> for EncodedValue {
fn eq(&self, other: &str) -> bool {
match self {
EncodedValue::String(s) => s == other,
_ => false,
}
}
}
macro_rules! gen_is_type_method {
($name: ident, $match_value: pat, $doc: literal) => {
#[doc = $doc]
pub fn $name(&self) -> bool {
match self {
$match_value => true,
_ => false
}
}
}
}
impl EncodedValue {
gen_is_type_method!(
is_byte,
EncodedValue::Byte(_),
"Returns `true` if the value is a byte"
);
gen_is_type_method!(
is_short,
EncodedValue::Short(_),
"Returns `true` if the value is a short"
);
gen_is_type_method!(
is_char,
EncodedValue::Char(_),
"Returns `true` if the value is a char"
);
gen_is_type_method!(
is_int,
EncodedValue::Int(_),
"Returns `true` if the value is a int"
);
gen_is_type_method!(
is_long,
EncodedValue::Long(_),
"Returns `true` if the value is a long"
);
gen_is_type_method!(
is_type,
EncodedValue::Type(_),
"Returns `true` if the value is a `Type`"
);
gen_is_type_method!(
is_float,
EncodedValue::Float(_),
"Returns `true` if the value is a float"
);
gen_is_type_method!(
is_double,
EncodedValue::Double(_),
"Returns `true` if the value is a double"
);
gen_is_type_method!(
is_method_handle,
EncodedValue::MethodHandle(_),
"Returns `true` if the value is a method handle"
);
gen_is_type_method!(
is_method_type,
EncodedValue::MethodType(_),
"Returns `true` if the value is a method type"
);
gen_is_type_method!(
is_string,
EncodedValue::String(_),
"Returns `true` if the value is a string"
);
gen_is_type_method!(
is_field,
EncodedValue::Field(_),
"Returns `true` if the value is a field"
);
gen_is_type_method!(
is_method,
EncodedValue::Method(_),
"Returns `true` if the value is a method"
);
gen_is_type_method!(
is_annotation,
EncodedValue::Annotation(_),
"Returns `true` if the value is a annotation"
);
gen_is_type_method!(
is_array,
EncodedValue::Array(_),
"Returns `true` if the value is an array"
);
gen_is_type_method!(
is_enum,
EncodedValue::Enum(_),
"Returns `true` if the value is an enum"
);
gen_is_type_method!(
is_bool,
EncodedValue::Boolean(_),
"Returns `true` if the value is a bool"
);
gen_is_type_method!(
is_null,
EncodedValue::Null,
"Returns `true` if the value is null"
);
}
#[derive(FromPrimitive, Debug)]
enum ValueType {
Byte = 0x00,
Short = 0x02,
Char = 0x03,
Int = 0x04,
Long = 0x06,
Float = 0x10,
Double = 0x11,
MethodType = 0x15,
MethodHandle = 0x16,
String = 0x17,
Type = 0x18,
Field = 0x19,
Method = 0x1a,
Enum = 0x1b,
Array = 0x1c,
Annotation = 0x1d,
Null = 0x1e,
Boolean = 0x1f,
}
macro_rules! try_extended_gread {
($source:expr,$offset:expr,$value_arg:expr,$size:expr,$sign_extended:literal) => {{
if *$offset + $value_arg >= $source.len() {
return Err(Error::Scroll(scroll::Error::TooBig {
size: *$offset + $value_arg,
len: $source.len()
}));
}
let mut bytes = [0x0; $size];
let (mut i, mut last_byte_is_neg) = (0, false);
for value in $source[*$offset..=*$offset+$value_arg].iter() {
bytes[i] = *value;
i += 1;
last_byte_is_neg = (*value as byte) < 0;
}
if $sign_extended && last_byte_is_neg {
while i < $size {
bytes[i] = 0xFF;
i += 1;
}
}
debug!(target: "encoded-value", "bytes: {:?}", bytes);
let value = bytes.pread_with(0, LE)?;
*$offset += 1 + $value_arg;
value
}};
($source:expr, $offset:expr, $value_arg:expr, $size:expr, ZERO) => {{
try_extended_gread!($source, $offset, $value_arg, $size, false)
}};
($source:expr, $offset:expr, $value_arg:expr, $size:expr, SIGN) => {{
try_extended_gread!($source, $offset, $value_arg, $size, true)
}};
($source:expr, $offset:expr, $value_arg:expr, $size:expr) => {{
try_extended_gread!($source, $offset, $value_arg, $size, ZERO)
}};
}
impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for EncodedValue
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
#[allow(clippy::cognitive_complexity)]
fn try_from_ctx(source: &'a [u8], dex: &super::Dex<S>) -> Result<(Self, Self::Size)> {
let offset = &mut 0;
let header: ubyte = source.gread(offset)?;
let value_arg = (header >> 5) as usize;
let value_type = 0b0001_1111 & header;
let value_type = ValueType::from_u8(value_type)
.ok_or_else(|| Error::InvalidId(format!("Invalid value type {}", value_type)))?;
debug!(target: "encoded-value", "encoded value type: {:?}, value_arg: {}", value_type, value_arg);
let value = match value_type {
ValueType::Byte => {
debug_assert_eq!(value_arg, 0);
EncodedValue::Byte(try_extended_gread!(source, offset, value_arg, 1))
}
ValueType::Short => {
debug_assert!(value_arg < 2);
EncodedValue::Short(try_extended_gread!(source, offset, value_arg, 2, SIGN))
}
ValueType::Char => {
debug_assert!(value_arg < 2);
EncodedValue::Char(try_extended_gread!(source, offset, value_arg, 2))
}
ValueType::Int => {
debug_assert!(value_arg < 4);
EncodedValue::Int(try_extended_gread!(source, offset, value_arg, 4, SIGN))
}
ValueType::Long => {
debug_assert!(value_arg < 8);
EncodedValue::Long(try_extended_gread!(source, offset, value_arg, 8, SIGN))
}
ValueType::Float => {
debug_assert!(value_arg < 4);
EncodedValue::Float(try_extended_gread!(source, offset, value_arg, 4))
}
ValueType::Double => {
debug_assert!(value_arg < 8);
EncodedValue::Double(try_extended_gread!(source, offset, value_arg, 8))
}
ValueType::MethodType => {
debug_assert!(value_arg < 4);
let proto_id: uint = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::MethodType(dex.get_proto_item(ProtoId::from(proto_id))?)
}
ValueType::MethodHandle => {
debug_assert!(value_arg < 4);
let index: MethodHandleId = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::MethodHandle(dex.get_method_handle_item(index)?)
}
ValueType::String => {
debug_assert!(value_arg < 4);
let string_id: StringId = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::String(dex.get_string(string_id)?)
}
ValueType::Type => {
debug_assert!(value_arg < 4);
let type_id: TypeId = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::Type(dex.get_type(type_id)?)
}
ValueType::Field => {
debug_assert!(value_arg < 4);
let index: uint = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::Field(dex.get_field_item(FieldId::from(index))?)
}
ValueType::Method => {
debug_assert!(value_arg < 4);
let index: uint = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::Method(dex.get_method_item(MethodId::from(index))?)
}
ValueType::Enum => {
debug_assert!(value_arg < 4);
let index: uint = try_extended_gread!(source, offset, value_arg, 4);
EncodedValue::Enum(dex.get_field_item(FieldId::from(index))?)
}
ValueType::Array => {
debug_assert!(value_arg == 0);
let encoded_array: EncodedArray = source.gread_with(offset, dex)?;
EncodedValue::Array(encoded_array.into_inner())
}
ValueType::Annotation => {
debug_assert!(value_arg == 0);
EncodedValue::Annotation(source.gread_with(offset, dex)?)
}
ValueType::Null => {
debug_assert!(value_arg == 0);
EncodedValue::Null
}
ValueType::Boolean => {
debug_assert!(value_arg < 2);
EncodedValue::Boolean(value_arg == 1)
}
};
Ok((value, *offset))
}
}
#[derive(Debug, Default)]
pub struct EncodedArray {
values: Vec<EncodedValue>,
}
impl EncodedArray {
pub(crate) fn into_inner(self) -> Vec<EncodedValue> {
self.values
}
}
impl<'a, S> ctx::TryFromCtx<'a, &super::Dex<S>> for EncodedArray
where
S: AsRef<[u8]>,
{
type Error = Error;
type Size = usize;
fn try_from_ctx(source: &'a [u8], ctx: &super::Dex<S>) -> super::Result<(Self, Self::Size)> {
let offset = &mut 0;
let size = Uleb128::read(source, offset)?;
debug!(target: "encoded-array", "encoded array size: {}", size);
let mut values = Vec::with_capacity(size as usize);
for _ in 0..size {
values.push(source.gread_with(offset, ctx)?);
}
Ok((Self { values }, *offset))
}
}