dex/
method.rs

1//! Dex `Method` and supporting structures
2use getset::{CopyGetters, Getters};
3use num_derive::FromPrimitive;
4use num_traits::FromPrimitive;
5use scroll::{ctx, Pread, Uleb128};
6
7use crate::{
8    annotation::{AnnotationSetItem, AnnotationSetRefList},
9    code::CodeItem,
10    encoded_item::{EncodedItem, EncodedItemArray},
11    error::Error,
12    field::FieldId,
13    jtype::{Type, TypeId},
14    string::{DexString, StringId},
15    uint, ulong, ushort, utils,
16};
17
18bitflags! {
19    /// Access flags of a `Dex` Method
20    pub struct AccessFlags: ulong {
21        const PUBLIC = 0x1;
22        const PRIVATE = 0x2;
23        const PROTECTED = 0x4;
24        const STATIC = 0x8;
25        const FINAL = 0x10;
26        const SYNCHRONIZED = 0x20;
27        const BRIDGE = 0x40;
28        const VARARGS = 0x80;
29        const NATIVE = 0x100;
30        const ABSTRACT = 0x400;
31        const STRICT = 0x800;
32        const SYNTHETIC = 0x1000;
33        const CONSTRUCTOR = 0x10000;
34        const DECLARED_SYNCHRONIZED = 0x20000;
35    }
36}
37
38/// Represents a `Class` method.
39#[derive(Debug, Getters, CopyGetters)]
40pub struct Method {
41    /// Parent class of the method.
42    #[get = "pub"]
43    class: Type,
44    /// Name of the method.
45    #[get = "pub"]
46    name: DexString,
47    /// Access flags of the method.
48    #[get_copy = "pub"]
49    access_flags: AccessFlags,
50    /// Types of the parameters of the method.
51    #[get = "pub"]
52    params: Vec<Type>,
53    /// Shorty descriptor of the method, as described
54    /// [here](https://source.android.com/devices/tech/dalvik/dex-format#shortydescriptor)
55    #[get = "pub"]
56    shorty: DexString,
57    /// Return type of the method.
58    #[get = "pub"]
59    return_type: Type,
60    /// Code and DebugInfo of the method.
61    code: Option<CodeItem>,
62    /// Annotations of the method.
63    #[get = "pub"]
64    annotations: AnnotationSetItem,
65    /// Annotations of the params.
66    #[get = "pub"]
67    param_annotations: AnnotationSetRefList,
68}
69
70impl Method {
71    gen_is_flag_set!(is_public, PUBLIC);
72    gen_is_flag_set!(is_private, PRIVATE);
73    gen_is_flag_set!(is_protected, PROTECTED);
74    gen_is_flag_set!(is_static, STATIC);
75    gen_is_flag_set!(is_final, FINAL);
76    gen_is_flag_set!(is_synchronized, SYNCHRONIZED);
77    gen_is_flag_set!(is_bridge, BRIDGE);
78    gen_is_flag_set!(is_varargs, VARARGS);
79    gen_is_flag_set!(is_native, NATIVE);
80    gen_is_flag_set!(is_abstract, ABSTRACT);
81    gen_is_flag_set!(is_strict, STRICT);
82    gen_is_flag_set!(is_synthetic, SYNTHETIC);
83    gen_is_flag_set!(is_constructor, CONSTRUCTOR);
84    gen_is_flag_set!(is_declared_synchronized, DECLARED_SYNCHRONIZED);
85
86    /// Returns the value of `dalvik.annotation.Signature`.
87    pub fn signature(&self) -> super::Result<Option<String>> {
88        utils::get_signature(self.annotations())
89    }
90
91    /// Code and DebugInfo of the method.
92    pub fn code(&self) -> Option<&CodeItem> {
93        self.code.as_ref()
94    }
95}
96
97/// Index into the `ProtoId`s list.
98pub type ProtoId = ulong;
99
100/// Method Prototypes.
101/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#proto-id-item)
102#[derive(Pread, Debug, CopyGetters, PartialEq)]
103#[get_copy = "pub"]
104pub struct ProtoIdItem {
105    /// Index into the string_ids list for the short-form descriptor string of this prototype
106    shorty: StringId,
107    /// Index into the type_ids list for the return type of this prototype.
108    return_type: TypeId,
109    /// Offset from the start of the file to the list of parameter types for this prototype, or `0`
110    /// if this prototype has no params. The data at the location should be a list of types.
111    params_off: uint,
112}
113
114impl ProtoIdItem {
115    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
116        dex: &super::Dex<S>,
117        offset: ulong,
118    ) -> super::Result<Self> {
119        let source = dex.source.as_ref();
120        Ok(source.pread_with(offset as usize, dex.get_endian())?)
121    }
122}
123
124impl Method {
125    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
126        dex: &super::Dex<S>,
127        encoded_method: &EncodedMethod,
128        annotations: AnnotationSetItem,
129        param_annotations: AnnotationSetRefList,
130    ) -> super::Result<Method> {
131        debug!(target: "method", "encoded method: {:?}", encoded_method);
132        let source = &dex.source;
133        let method_item = dex.get_method_item(encoded_method.method_id)?;
134        let name = dex.get_string(method_item.name_idx)?;
135        debug!(target: "method", "name: {}, method id item: {:?}", name, method_item);
136        let proto_item = dex.get_proto_item(ProtoId::from(method_item.proto_idx))?;
137        debug!(target: "method", "method proto_item: {:?}", proto_item);
138        let shorty = dex.get_string(proto_item.shorty)?;
139        let return_type = dex.get_type(proto_item.return_type)?;
140        let params = if proto_item.params_off != 0 {
141            if !dex.is_offset_in_data_section(proto_item.params_off) {
142                return Err(Error::BadOffset(
143                    proto_item.params_off as usize,
144                    format!(
145                        "Params offset not in data section for proto_item: {:?}",
146                        proto_item
147                    ),
148                ));
149            }
150            let offset = &mut (proto_item.params_off as usize);
151            let endian = dex.get_endian();
152            let len = source.gread_with::<uint>(offset, endian)?;
153            let type_ids: Vec<ushort> = try_gread_vec_with!(source, offset, len, endian);
154            utils::get_types(dex, &type_ids)?
155        } else {
156            Default::default()
157        };
158        debug!(target: "method", "code item offset: {}", encoded_method.code_offset);
159        let code = dex.get_code_item(encoded_method.code_offset)?;
160        Ok(Self {
161            name,
162            class: dex.get_type(TypeId::from(method_item.class_idx))?,
163            access_flags: AccessFlags::from_bits(encoded_method.access_flags).ok_or_else(|| {
164                Error::InvalidId(format!(
165                    "Invalid access flags for method {}",
166                    method_item.name_idx
167                ))
168            })?,
169            shorty,
170            return_type,
171            params,
172            code,
173            annotations,
174            param_annotations,
175        })
176    }
177}
178
179/// Method identifier.
180/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-id-item)
181#[derive(Pread, Debug, CopyGetters, PartialEq)]
182#[get_copy = "pub"]
183pub struct MethodIdItem {
184    /// Index into the `TypeId`s list for the definer of this method.
185    class_idx: ushort,
186    /// Index into the `ProtoId`s list for the prototype of this method.
187    proto_idx: ushort,
188    /// Index into the `StringId`s list for the name of this method.
189    name_idx: StringId,
190}
191
192impl MethodIdItem {
193    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
194        dex: &super::Dex<S>,
195        offset: ulong,
196    ) -> super::Result<Self> {
197        let source = &dex.source;
198        Ok(source.pread_with(offset as usize, dex.get_endian())?)
199    }
200}
201
202/// Index into the `MethodId`s list.
203pub type MethodId = ulong;
204
205/// Index into the `MethodHandleItem`s list.
206pub type MethodHandleId = uint;
207
208/// Contains a `MethodId` along with its access flags and code.
209/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#encoded-method)
210#[derive(Debug, Getters, CopyGetters)]
211pub struct EncodedMethod {
212    /// Index into the `MethodId`s list for the identity of this method represented as
213    /// a difference from the index of previous element in the list.
214    #[get_copy = "pub(crate)"]
215    pub(crate) method_id: MethodId,
216    /// Access flags for this method.
217    #[get = "pub"]
218    access_flags: ulong,
219    /// Offset from the start of the file to the code structure for this method, or `0` if this
220    /// method is either abstract or native.  The format of the data is specified by `CodeItem`.
221    #[get = "pub"]
222    code_offset: ulong,
223}
224
225impl EncodedItem for EncodedMethod {
226    fn id(&self) -> ulong {
227        self.method_id
228    }
229}
230
231/// List of `EncodedMethod`s
232pub type EncodedMethodArray = EncodedItemArray<EncodedMethod>;
233
234impl<'a> ctx::TryFromCtx<'a, ulong> for EncodedMethod {
235    type Error = Error;
236    type Size = usize;
237
238    fn try_from_ctx(source: &'a [u8], prev_id: ulong) -> super::Result<(Self, Self::Size)> {
239        let offset = &mut 0;
240        let id = Uleb128::read(source, offset)?;
241        let access_flags = Uleb128::read(source, offset)?;
242        let code_offset = Uleb128::read(source, offset)?;
243        Ok((
244            Self {
245                method_id: prev_id + id,
246                code_offset,
247                access_flags,
248            },
249            *offset,
250        ))
251    }
252}
253
254/// Type of the method handle.
255/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-handle-type-codes)
256#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq)]
257pub enum MethodHandleType {
258    StaticPut = 0x00,
259    StaticGet = 0x01,
260    InstancePut = 0x02,
261    InstanceGet = 0x03,
262    InvokeStatic = 0x04,
263    InvokeInstance = 0x05,
264    InvokeConstructor = 0x06,
265    InvokeDirect = 0x07,
266    InvokeInterface = 0x08,
267}
268
269#[derive(Debug, Clone, Copy, PartialEq)]
270pub enum FieldOrMethodId {
271    Field(FieldId),
272    Method(MethodId),
273}
274
275/// A method handle.
276/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-handle-item)
277#[derive(Debug, CopyGetters, PartialEq)]
278#[get_copy = "pub"]
279pub struct MethodHandleItem {
280    ///  The type of this MethodHandleItem.
281    handle_type: MethodHandleType,
282    /// `FieldId` or `MethodId`  depending on whether the method handle type is an accessor or
283    /// a method invoker
284    id: FieldOrMethodId,
285}
286
287impl<'a, S: AsRef<[u8]>> ctx::TryFromCtx<'a, &super::Dex<S>> for MethodHandleItem {
288    type Error = Error;
289    type Size = usize;
290
291    fn try_from_ctx(source: &'a [u8], dex: &super::Dex<S>) -> super::Result<(Self, Self::Size)> {
292        let endian = dex.get_endian();
293        let offset = &mut 0;
294        let handle_type: ushort = source.gread_with(offset, endian)?;
295        let handle_type = MethodHandleType::from_u16(handle_type)
296            .ok_or_else(|| Error::InvalidId(format!("Invalid handle type {}", handle_type)))?;
297        let _: ushort = source.gread_with(offset, endian)?;
298        let id: ushort = source.gread_with(offset, endian)?;
299        let _: ushort = source.gread_with(offset, endian)?;
300        let id = match handle_type {
301            MethodHandleType::StaticPut
302            | MethodHandleType::StaticGet
303            | MethodHandleType::InstancePut
304            | MethodHandleType::InstanceGet => FieldOrMethodId::Field(FieldId::from(id)),
305            _ => FieldOrMethodId::Method(MethodId::from(id)),
306        };
307
308        Ok((Self { handle_type, id }, *offset))
309    }
310}