summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2015-06-12 14:52:33 +0100
committer Nicolas Geoffray <ngeoffray@google.com> 2015-06-12 15:53:50 +0100
commit32c9ea5c03b1b22f36233ec6c24eca87c77f9157 (patch)
treea10130c6ab7795772c413f543beeece6e31a3d04
parent10176757836db987e11fb3723e4cb07bd9ea7e95 (diff)
Use the caller's class loader when walking inlined frames.
We should not use the outer most class loader. JLS specification is to use the caller's class loader. Change-Id: I736f36c9b6a44fab213ad0c01cf1efc975b9b3a6
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h25
-rw-r--r--runtime/stack.cc5
-rw-r--r--test/497-inlining-and-class-loader/clear_dex_cache.cc46
-rw-r--r--test/497-inlining-and-class-loader/expected.txt7
-rw-r--r--test/497-inlining-and-class-loader/info.txt2
-rw-r--r--test/497-inlining-and-class-loader/src/Main.java145
-rw-r--r--test/Android.libarttest.mk3
-rw-r--r--test/Android.run-test.mk1
8 files changed, 221 insertions, 13 deletions
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index b0cbd02880..298b826307 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -39,9 +39,12 @@
namespace art {
inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
- uint32_t method_index,
- InvokeType invoke_type)
+ const InlineInfo& inline_info,
+ uint8_t inlining_depth)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ uint32_t method_index = inline_info.GetMethodIndexAtDepth(inlining_depth);
+ InvokeType invoke_type = static_cast<InvokeType>(
+ inline_info.GetInvokeTypeAtDepth(inlining_depth));
ArtMethod* caller = outer_method->GetDexCacheResolvedMethod(method_index, sizeof(void*));
if (!caller->IsRuntimeMethod()) {
return caller;
@@ -50,11 +53,20 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
// The method in the dex cache can be the runtime method responsible for invoking
// the stub that will then update the dex cache. Therefore, we need to do the
// resolution ourselves.
-
+
+ // We first find the class loader of our caller. If it is the outer method, we can directly
+ // use its class loader. Otherwise, we also need to resolve our caller.
StackHandleScope<2> hs(Thread::Current());
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(outer_method->GetClassLoader()));
+ MutableHandle<mirror::ClassLoader> class_loader(hs.NewHandle<mirror::Class>(nullptr));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(outer_method->GetDexCache()));
+ if (inlining_depth == 0) {
+ class_loader.Assign(outer_method->GetClassLoader());
+ } else {
+ caller = GetResolvedMethod(outer_method, inline_info, inlining_depth - 1);
+ class_loader.Assign(caller->GetClassLoader());
+ }
+
return class_linker->ResolveMethod(
*outer_method->GetDexFile(), method_index, dex_cache, class_loader, nullptr, invoke_type);
}
@@ -82,10 +94,7 @@ inline ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp,
DCHECK(stack_map.IsValid());
if (stack_map.HasInlineInfo(encoding)) {
InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
- uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info.GetDepth() - 1);
- InvokeType invoke_type = static_cast<InvokeType>(
- inline_info.GetInvokeTypeAtDepth(inline_info.GetDepth() - 1));
- caller = GetResolvedMethod(outer_method, method_index, invoke_type);
+ caller = GetResolvedMethod(outer_method, inline_info, inline_info.GetDepth() - 1);
}
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 5aeca98a88..11c94dbbb8 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -126,10 +126,7 @@ ArtMethod* StackVisitor::GetMethod() const {
if (IsInInlinedFrame()) {
size_t depth_in_stack_map = current_inlining_depth_ - 1;
InlineInfo inline_info = GetCurrentInlineInfo();
- uint32_t method_index = inline_info.GetMethodIndexAtDepth(depth_in_stack_map);
- InvokeType invoke_type =
- static_cast<InvokeType>(inline_info.GetInvokeTypeAtDepth(depth_in_stack_map));
- return GetResolvedMethod(*GetCurrentQuickFrame(), method_index, invoke_type);
+ return GetResolvedMethod(*GetCurrentQuickFrame(), inline_info, depth_in_stack_map);
} else {
return *cur_quick_frame_;
}
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
new file mode 100644
index 0000000000..f9b33a2874
--- /dev/null
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "art_method-inl.h"
+#include "jni.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv*, jclass, jclass cls) {
+ ScopedObjectAccess soa(Thread::Current());
+ return soa.Vm()->AddGlobalRef(
+ soa.Self(),
+ soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods()->Clone(soa.Self()));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods(
+ JNIEnv*, jclass, jclass cls, jobject old_cache) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::PointerArray* now = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+ mirror::PointerArray* old = soa.Decode<mirror::PointerArray*>(old_cache);
+ for (size_t i = 0, e = old->GetLength(); i < e; ++i) {
+ now->SetElementPtrSize(i, old->GetElementPtrSize<void*>(i, sizeof(void*)), sizeof(void*));
+ }
+}
+
+} // namespace
+
+} // namespace art
diff --git a/test/497-inlining-and-class-loader/expected.txt b/test/497-inlining-and-class-loader/expected.txt
new file mode 100644
index 0000000000..18d2f2da12
--- /dev/null
+++ b/test/497-inlining-and-class-loader/expected.txt
@@ -0,0 +1,7 @@
+java.lang.Exception
+ at Main.$noinline$bar(Main.java:139)
+ at Level2.$inline$bar(Main.java:88)
+ at Level1.$inline$bar(Main.java:82)
+ at LoadedByMyClassLoader.bar(Main.java:94)
+ at java.lang.reflect.Method.invoke(Native Method)
+ at Main.main(Main.java:113)
diff --git a/test/497-inlining-and-class-loader/info.txt b/test/497-inlining-and-class-loader/info.txt
new file mode 100644
index 0000000000..e7f02aaf34
--- /dev/null
+++ b/test/497-inlining-and-class-loader/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing to ensure it is using
+the correct class loader when walking inlined frames.
diff --git a/test/497-inlining-and-class-loader/src/Main.java b/test/497-inlining-and-class-loader/src/Main.java
new file mode 100644
index 0000000000..bb8da6d3a0
--- /dev/null
+++ b/test/497-inlining-and-class-loader/src/Main.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+class MyClassLoader extends ClassLoader {
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
+
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
+
+ // Some magic to get access to the dexField field of pathList.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ dexElements = (Object[]) f.get(pathList);
+ dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+ dexFileField.setAccessible(true);
+ }
+
+ Object[] dexElements;
+ Field dexFileField;
+
+ static ClassLoader level1ClassLoader;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ if (this != level1ClassLoader) {
+ if (className.equals("Level1")) {
+ return level1ClassLoader.loadClass(className);
+ } else if (className.equals("Level2")) {
+ throw new ClassNotFoundException("None of my methods require Level2!");
+ } else if (!className.equals("LoadedByMyClassLoader")) {
+ // We're only going to handle LoadedByMyClassLoader.
+ return getParent().loadClass(className);
+ }
+ } else {
+ if (className != "Level1" && className != "Level2") {
+ return getParent().loadClass(className);
+ }
+ }
+
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object element : dexElements) {
+ Object dex = dexFileField.get(element);
+ Method method = dex.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dex != null) {
+ Class clazz = (Class)method.invoke(dex, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return null;
+ }
+}
+
+class Level1 {
+ public static void $inline$bar() {
+ Level2.$inline$bar();
+ }
+}
+
+class Level2 {
+ public static void $inline$bar() {
+ Main.$noinline$bar();
+ }
+}
+
+class LoadedByMyClassLoader {
+ public static void bar() {
+ Level1.$inline$bar();
+ }
+}
+
+class Main {
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Clone resolved methods, to restore the original version just
+ // before we walk the stack in $noinline$bar.
+ savedResolvedMethods = cloneResolvedMethods(Main.class);
+
+ MyClassLoader o = new MyClassLoader();
+ MyClassLoader.level1ClassLoader = new MyClassLoader();
+ Class foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("bar");
+ try {
+ m.invoke(null);
+ } catch (Error e) { /* Ignore */ }
+ }
+
+ public static void $inline$bar() {
+ }
+
+ public static void $noinline$bar() {
+ try {
+ // Be evil and clear all dex cache entries.
+ Field f = Class.class.getDeclaredField("dexCache");
+ f.setAccessible(true);
+ Object dexCache = f.get(Main.class);
+ f = dexCache.getClass().getDeclaredField("resolvedTypes");
+ f.setAccessible(true);
+ Object[] array = (Object[]) f.get(dexCache);
+ for (int i = 0; i < array.length; i++) {
+ array[i] = null;
+ }
+ restoreResolvedMethods(Main.class, savedResolvedMethods);
+ } catch (Throwable t) { /* Ignore */ }
+
+ // This will walk the stack, trying to resolve methods in it.
+ // Because we cleared dex cache entries, we will have to find
+ // classes again, which require to use the correct class loader
+ // in the presence of inlining.
+ new Exception().printStackTrace();
+ }
+ static Object savedResolvedMethods;
+
+ static native Object cloneResolvedMethods(Class<?> cls);
+ static native void restoreResolvedMethods(Class<?> cls, Object saved);
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 847ad0d2f7..fcb9f8a779 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -34,7 +34,8 @@ LIBARTTEST_COMMON_SRC_FILES := \
455-set-vreg/set_vreg_jni.cc \
457-regs/regs_jni.cc \
461-get-reference-vreg/get_reference_vreg_jni.cc \
- 466-get-live-vreg/get_live_vreg_jni.cc
+ 466-get-live-vreg/get_live_vreg_jni.cc \
+ 497-inlining-and-class-loader/clear_dex_cache.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 82a62953d5..469df1f2b6 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -338,6 +338,7 @@ TEST_ART_BROKEN_NDEBUG_TESTS := \
457-regs \
461-get-reference-vreg \
466-get-live-vreg \
+ 497-inlining-and-class-loader \
ifneq (,$(filter ndebug,$(RUN_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \