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",