反射简单地使用类似如下的代码执行类中的方法
1 | TestMethod test = new TestMethod(); |
如无意外就能正确调起TestMethod中的a方法。
如前篇《DEX文件格式分析》中的类加载中一样,getDeclaredMethod的过程就是从ClassObject对象中获取到Method结构体对象,然后再将之转换成reflect对象的一个过程。
然后 Java 层就可以获取到一个java.lang.reflect.Method的对象,执行Method对象的invoke方法,实际上执行的就是对应Native中的invokeNative方法。
1 | // dalvik/vm/native/java_lang_reflect_Method.cpp |
在解释好参数后,先根据传入的slot值与class来找出对应的Method对象,查找是根据slot来操作的,slot的生成及根据slot来查找对应的Method方法对象的过程如下。
1 | // dalvik/vm/reflect/Reflect.cpp |
根据slot获取到对应的native的Method对象后,就可以调用对应的dvmInvokeMethod方法来调起具体的方法逻辑了。
真正执行方法的是在dvmInvokeMethod方法中,方法及注释如下。
1 | // dalvik/vm/interp/Stack.cpp |
其中,callPrep方法内进行的是检查方法是否有权限能被外部invoke执行等等,也进行了新帧的初始化,新帧的初始化在Stack::dvmPushInterpFrame内。
1 | // dalvik/vm/interp/Stack.cpp |
创建帧的过程及指针的变化情况可以参考下图:
由此,容易看出其后写入参数的过程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/* "ins" for new frame start at frame pointer plus locals */
ins = ((s4*)self->interpSave.curFrame) + //interpSave是定义在/dalvik/vm/interp/InterpState.h上的InterpSaveState结构体
//interpSave.curFrame指向的是当前Dalvik虚拟机线程的帧栈顶部
(method->registersSize - method->insSize);
......
/*
* 将参数入栈,ins是一个参数栈
*/
DataObject** args = (DataObject**)(void*)argList->contents; //获取到参数对象的起始指针对象
ClassObject** types = (ClassObject**)(void*)params->contents; //获取到参数对象的类型的起始指针对象
for (int i = 0; i < argListLength; i++) { //遍历参数
int width = dvmConvertArgument(*args++, *types++, ins); //转换参数类型,该原生转内部的或者装装箱的,顺便检查类型是否匹配,ins指向当前的参数对象
if (width < 0) {
dvmPopFrame(self); // throw wants to pull PC out of stack
needPop = false;
throwArgumentTypeMismatch(i, *(types-1), *(args-1));
goto bail;
}
ins += width;
verifyCount += width;
}
实际上是把 self-interpSave.curFrame指针往上移动( method->registersSize - method->insSize )个位置,然后写入传入的参数。
接着,对于非Native方法,执行dvmInterpret函数,interpret包含了选定执行直译器与执行具体代码的功能。
1 | //dalvik/vm/interp/Interp.cpp |
如此,需要执行的方法就完成执行,并返回结果了。
以上只分析了执行java方法的过程,执行native方法的还未进行分析,以上暂当记录,日后补充。
参考
http://blog.csdn.net/powq2009/article/details/8949887
http://blog.csdn.net/luoshengyang/article/details/41822747
1 | dalvik/vm/oo/Object.h |