diff options
author | 2015-06-12 14:52:33 +0100 | |
---|---|---|
committer | 2015-06-12 15:53:50 +0100 | |
commit | 32c9ea5c03b1b22f36233ec6c24eca87c77f9157 (patch) | |
tree | a10130c6ab7795772c413f543beeece6e31a3d04 | |
parent | 10176757836db987e11fb3723e4cb07bd9ea7e95 (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.h | 25 | ||||
-rw-r--r-- | runtime/stack.cc | 5 | ||||
-rw-r--r-- | test/497-inlining-and-class-loader/clear_dex_cache.cc | 46 | ||||
-rw-r--r-- | test/497-inlining-and-class-loader/expected.txt | 7 | ||||
-rw-r--r-- | test/497-inlining-and-class-loader/info.txt | 2 | ||||
-rw-r--r-- | test/497-inlining-and-class-loader/src/Main.java | 145 | ||||
-rw-r--r-- | test/Android.libarttest.mk | 3 | ||||
-rw-r--r-- | test/Android.run-test.mk | 1 |
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), \ |