art中反射查找方法的流程

art 反射 获取方法实例的流程

java层

java这层就简单一点说了,获取到class对象以后,使用getDeclaredMethod,然后调用getMethod,调用到getDeclaredMethodInternal.

虚机

getDeclaredMethodInternal是一个native方法,在java_lang_Class.cc中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
jstring name, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
//这一句应该就是最关键的,获取到方法的信息
Handle<mirror::Method> result = hs.NewHandle(
mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>(
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}

然后看一下 GetDeclaredMethodInternal的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Method> Class::GetDeclaredMethodInternal(
Thread* self,
ObjPtr<Class> klass, 类
ObjPtr<String> name, 方法名
ObjPtr<ObjectArray<Class>> args) { 参数类型列表
// Covariant return types permit the class to define multiple
// methods with the same name and parameter types. Prefer to
// return a non-synthetic method in such situations. We may
// still return a synthetic method to handle situations like
// escalated visibility. We never return miranda methods that
// were synthesized by the runtime.
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
if (UNLIKELY(h_method_name == nullptr)) {
ThrowNullPointerException("name == null");
return nullptr;
}
auto h_args = hs.NewHandle(args);
Handle<Class> h_klass = hs.NewHandle(klass);
ArtMethod* result = nullptr;
//这里看字面意思就是获取这个类的虚方法的列表,进行遍历,GetDeclaredVirtualMethods的代码我稍微跟了下,最后就是返回一个ArraySlice<ArtMethod>集合,过程涉及到一些偏移什么的,应该相对底层了,这里先不深究了
for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr<String> np_name = np_method->GetNameAsString(self);
//这里的操作也比较直接,就是对比方法名称和参数列表是否相等,下面那个循环的操作也比较类似
if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) {
if (UNLIKELY(self->IsExceptionPending())) {
return nullptr;
}
continue;
}
if (!m.IsMiranda()) {
if (!m.IsSynthetic()) {
return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
}
result = &m; // Remember as potential result if it's not a miranda method.
}
}
if (result == nullptr) {
for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
auto modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) != 0) {
continue;
}
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr<String> np_name = np_method->GetNameAsString(self);
if (np_name == nullptr) {
self->AssertPendingException();
return nullptr;
}
if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) {
if (UNLIKELY(self->IsExceptionPending())) {
return nullptr;
}
continue;
}
DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods.
if ((modifiers & kAccSynthetic) == 0) {
return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
}
result = &m; // Remember as potential result.
}
}
return result != nullptr
? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
: nullptr;
}

这些其实不是我的重点..我的疑问其实出在GetNameAsString中
字面意思其实蛮好理解的,就是获取名称的字符串,然而

1
2
3
4
5
6
7
8
9
ObjPtr<mirror::String> ArtMethod::GetNameAsString(Thread* self) {
CHECK(!IsProxyMethod());
StackHandleScope<1> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(GetDexCache()));
auto* dex_file = dex_cache->GetDexFile();
uint32_t dex_method_idx = GetDexMethodIndex();
const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, dex_cache);
}

在IsProxyMethod中的返回值为true,导致CHECK(!IsProxyMethod())报check failed,导致程序终止了
有些东西涉及到工作的具体内容这里就不说了,但是确实比较玄学..

还是得想办法摸索一下看看

代理方法这个东西,我其实没怎么查到相关信息

至于代理方法,由于是动态生成的(没有对应的DEX字节码),因此即使ART虚拟机运行在解释模式中,它们也不通过解释器来执行(这一点猜测的,还没有确认)。

上文中的GetNameAsString实现的最后的return语句,看起来是向dex缓存来找对应的方法名称,但是如果是代理方法,就是没有对应的dex字节码,就肯定是找不到了,所以在执行这个方法的最前面就会进行这个check
看一下IsProxyMethod的具体实现

1
2
3
4
5
6
inline bool ArtMethod::IsProxyMethod() {
DCHECK(!IsRuntimeMethod()) << "ArtMethod::IsProxyMethod called on a runtime method";
// Avoid read barrier since the from-space version of the class will have the correct proxy class
// flags since they are constant for the lifetime of the class.
return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass();
}

大概是这样,emmm…我刚刚好像找到为什么报错了,公司的代码重写了这个部分,细节这里不贴出来了,可能是那个重写的部分没有对这个方法有正确的返回值,所以会出错.

嗯大概是这样吧,算是比较水的分析了一下反射找到方法的流程,还好找着为什么报错了要不然就要被连人带椅子扔出去了.