Add a test and consistency checks in method / field resolution.
Always use the associated class loader of a dex cache.
Test: test.py
Bug: 199246839
Change-Id: Ib512126915cac4af229a5a206a8b4d9d73bdf02a
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index b48debc..c2fbadf 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -759,6 +759,7 @@
}
art::WriterMutexLock mu(driver_->self_, *art::Locks::dex_lock_);
cache->SetLocation(location.Get());
+ cache->SetClassLoader(loader.Get());
cache->InitializeNativeFields(dex_file_.get(),
loader.IsNull() ? driver_->runtime_->GetLinearAlloc()
: loader->GetAllocator());
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index c45f001..1319101 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -164,6 +164,7 @@
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache != nullptr);
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == nullptr) {
@@ -212,6 +213,7 @@
dex::TypeIndex type_idx,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
if (type == nullptr) {
type = DoLookupResolvedType(type_idx, dex_cache, class_loader);
@@ -273,6 +275,7 @@
InvokeType type,
uint32_t method_idx,
ObjPtr<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
return CheckInvokeClassMismatch<kThrow>(
dex_cache,
type,
@@ -288,6 +291,7 @@
inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
if (resolved == nullptr) {
const DexFile& dex_file = *dex_cache->GetDexFile();
@@ -433,6 +437,7 @@
ArtField* field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField(
field_idx);
if (field == nullptr) {
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
ObjPtr<mirror::ClassLoader> class_loader = referrer->GetDeclaringClass()->GetClassLoader();
field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static);
}
@@ -449,6 +454,7 @@
field_idx);
if (UNLIKELY(resolved_field == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referring_class->GetClassLoader()));
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 314aa29..f94bffd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8867,6 +8867,7 @@
ObjPtr<mirror::Class> ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
const DexFile& dex_file = *dex_cache->GetDexFile();
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
ObjPtr<mirror::Class> type = LookupResolvedType(descriptor, class_loader);
@@ -8914,6 +8915,7 @@
ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
Thread* self = Thread::Current();
const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx);
ObjPtr<mirror::Class> resolved = FindClass(self, descriptor, class_loader);
@@ -8944,6 +8946,7 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
uint32_t method_idx) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
// Search for the method using dex_cache and method_idx. The Class::Find*Method()
// functions can optimize the search if the dex_cache is the same as the DexCache
// of the class, with fall-back to name and signature search otherwise.
@@ -9004,6 +9007,7 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
return method == nullptr ||
hiddenapi::ShouldDenyAccessToMember(method,
hiddenapi::AccessContext(class_loader, dex_cache),
@@ -9014,6 +9018,7 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
uint32_t method_idx) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
if (klass->IsInterface()) {
ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_);
return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method;
@@ -9036,6 +9041,7 @@
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer,
InvokeType type) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
DCHECK(dex_cache != nullptr);
DCHECK(referrer == nullptr || !referrer->IsProxyMethod());
@@ -9130,6 +9136,7 @@
ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
@@ -9163,6 +9170,7 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
bool is_static) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
const DexFile& dex_file = *dex_cache->GetDexFile();
const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(field_id.class_idx_);
@@ -9183,6 +9191,7 @@
Handle<mirror::ClassLoader> class_loader,
bool is_static) {
DCHECK(dex_cache != nullptr);
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
ArtField* resolved = dex_cache->GetResolvedField(field_idx);
Thread::PoisonObjectPointersIfDebug();
@@ -9210,6 +9219,7 @@
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache != nullptr);
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
ArtField* resolved = dex_cache->GetResolvedField(field_idx);
Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
@@ -9237,6 +9247,7 @@
ObjPtr<mirror::ClassLoader> class_loader,
uint32_t field_idx,
bool is_static) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
ArtField* resolved = is_static ? klass->FindStaticField(dex_cache, field_idx)
: klass->FindInstanceField(dex_cache, field_idx);
if (resolved != nullptr &&
@@ -9257,6 +9268,7 @@
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
uint32_t field_idx) {
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Ptr());
ArtField* resolved = klass->FindField(dex_cache, field_idx);
if (resolved != nullptr &&
@@ -9280,6 +9292,7 @@
Handle<mirror::ClassLoader> class_loader) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK(dex_cache != nullptr);
+ DCHECK(dex_cache->GetClassLoader().Ptr() == class_loader.Get());
ObjPtr<mirror::MethodType> resolved = dex_cache->GetResolvedMethodType(proto_idx);
if (resolved != nullptr) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 3295364..b59dc18 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -468,7 +468,7 @@
ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
class_idx,
dex_cache,
- access_to->GetClassLoader());
+ GetClassLoader());
DCHECK(dex_access_to != nullptr);
if (UNLIKELY(!this->CanAccess(dex_access_to))) {
if (throw_on_failure) {
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 1b9558e..d100f32 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -247,6 +247,10 @@
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_), class_loader);
}
+ObjPtr<ClassLoader> DexCache::GetClassLoader() {
+ return GetFieldObject<mirror::ClassLoader>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_));
+}
+
#if !defined(__aarch64__) && !defined(__x86_64__)
static pthread_mutex_t dex_cache_slow_atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 03ff221..f02ddc6 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -476,6 +476,7 @@
void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<ClassLoader> GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
private:
void SetNativeArrays(StringDexCacheType* strings,
diff --git a/test/827-resolve-method/expected-stderr.txt b/test/827-resolve-method/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/827-resolve-method/expected-stderr.txt
diff --git a/test/827-resolve-method/expected-stdout.txt b/test/827-resolve-method/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/827-resolve-method/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/827-resolve-method/info.txt b/test/827-resolve-method/info.txt
new file mode 100644
index 0000000..8b7f173
--- /dev/null
+++ b/test/827-resolve-method/info.txt
@@ -0,0 +1 @@
+Regression test for method resolution.
diff --git a/test/827-resolve-method/src-ex/Caller.java b/test/827-resolve-method/src-ex/Caller.java
new file mode 100644
index 0000000..fc1890e
--- /dev/null
+++ b/test/827-resolve-method/src-ex/Caller.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Caller {
+ public static void doCall() {
+ // `callMethod` is declared in a class that isn't visible to `Caller`, however, we are
+ // accessing it through `LocalCall` and that makes it accessible.
+ LocalClass.callMethod();
+ }
+}
+
+class LocalClass extends pkg1.SubClass {
+}
diff --git a/test/827-resolve-method/src/Main.java b/test/827-resolve-method/src/Main.java
new file mode 100644
index 0000000..c1c94da
--- /dev/null
+++ b/test/827-resolve-method/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+final class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ MyLocalClass.callMethod();
+ loadClass();
+ }
+
+ public static void loadClass() throws Exception {
+ Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Couldn't find path class loader class");
+ }
+ Constructor<?> constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+ Class<?> otherClass = loader.loadClass("Caller");
+
+ // Run the method in interpreter / AOT mode.
+ otherClass.getMethod("doCall").invoke(null);
+
+ // Run the method in JIT mode.
+ ensureJitCompiled(otherClass, "doCall");
+ otherClass.getMethod("doCall").invoke(null);
+ }
+
+ private static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+ private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+ private static final String DEX_FILE =
+ new File(DEX_LOCATION, "827-resolve-method-ex.jar").getAbsolutePath();
+
+ private static native void ensureJitCompiled(Class<?> cls, String methodName);
+}
+
+class MyLocalClass extends pkg1.SubClass {
+}
diff --git a/test/827-resolve-method/src/pkg1/SubClass.java b/test/827-resolve-method/src/pkg1/SubClass.java
new file mode 100644
index 0000000..78e9346
--- /dev/null
+++ b/test/827-resolve-method/src/pkg1/SubClass.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pkg1;
+
+// `SuperClass` is only visible to classes in packge `pkg1`.
+class SuperClass {
+ public static void callMethod() {
+ }
+}
+
+// Any class that extends `SubClass` will be able to access `SuperClass.callMethod`.
+public class SubClass extends SuperClass {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 367cfdc..01a7f3b 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1154,6 +1154,7 @@
"820-vdex-multidex",
"821-many-args",
"822-hiddenapi-future",
+ "827-resolve-method",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",