From 8151b8fe2987e01ed915ea2053ed42fac95ff3db Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 10 Dec 2013 17:44:56 -0800 Subject: Fix FindFieldID to use class's classloader to find field type. Also includes a test case, though the test case does not recreate this exact issue because the attached native thread would need to have a null context classloader. Bug: 11737351 (cherry picked from 62509b662929175228bb0d0f014ef4ef4e33be10) Change-Id: I6f15aba990eaf84696c02c37d02e80f88aff59ed --- runtime/jni_internal.cc | 4 ++-- test/JniTest/JniTest.java | 12 ++++++++++++ test/JniTest/jni_test.cc | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 6a0990eec6..812340bf9f 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -291,8 +291,8 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con Class* field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { - ClassLoader* cl = GetClassLoader(soa); - field_type = class_linker->FindClass(sig, cl); + SirtRef class_loader(soa.Self(), c->GetClassLoader()); + field_type = class_linker->FindClass(sig, class_loader.get()); } else { field_type = class_linker->FindPrimitiveClass(*sig); } diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index 7014ef9334..e368223ffb 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -18,11 +18,23 @@ class JniTest { public static void main(String[] args) { System.loadLibrary("arttest"); testFindClassOnAttachedNativeThread(); + testFindFieldOnAttachedNativeThread(); testCallStaticVoidMethodOnSubClass(); } private static native void testFindClassOnAttachedNativeThread(); + private static boolean testFindFieldOnAttachedNativeThreadField; + + private static void testFindFieldOnAttachedNativeThread() { + testFindFieldOnAttachedNativeThreadNative(); + if (!testFindFieldOnAttachedNativeThreadField) { + throw new AssertionError(); + } + } + + private static native void testFindFieldOnAttachedNativeThreadNative(); + private static void testCallStaticVoidMethodOnSubClass() { testCallStaticVoidMethodOnSubClassNative(); if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) { diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index 72a3309d9d..9b01453c8e 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -67,6 +67,42 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThre assert(pthread_join_result == 0); } +static void* testFindFieldOnAttachedNativeThread(void*) { + assert(jvm != NULL); + + JNIEnv* env = NULL; + JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; + int attach_result = jvm->AttachCurrentThread(&env, &args); + assert(attach_result == 0); + + jclass clazz = env->FindClass("JniTest"); + assert(clazz != NULL); + assert(!env->ExceptionCheck()); + + jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z"); + assert(field != NULL); + assert(!env->ExceptionCheck()); + + env->SetStaticBooleanField(clazz, field, JNI_TRUE); + + int detach_result = jvm->DetachCurrentThread(); + assert(detach_result == 0); + return NULL; +} + +extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindFieldOnAttachedNativeThreadNative(JNIEnv*, + jclass) { + pthread_t pthread; + int pthread_create_result = pthread_create(&pthread, + NULL, + testFindFieldOnAttachedNativeThread, + NULL); + assert(pthread_create_result == 0); + int pthread_join_result = pthread_join(pthread, NULL); + assert(pthread_join_result == 0); +} + + // http://b/11243757 extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env, jclass) { -- cgit v1.2.3 From 3addc29228cb3a2d263e1d91a0b7d4e0fe26e246 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 3 Dec 2013 15:00:05 -0800 Subject: Fix stack trace for proxy methods and added test case. Bug: 11861564 Change-Id: I9513359ff9e5d345ee71d84388afed02bd114ecf (cherry picked from 228d6b8a4f0a21c1e9b2372c3104ce4ee19f65b4) --- runtime/thread.cc | 50 ++++++++++++++++++++++----------------- test/044-proxy/expected.txt | 5 ++-- test/044-proxy/src/BasicTest.java | 22 ++++++++++++++++- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 23cafe8707..7d65f5a9f0 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1332,7 +1332,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor { return true; // Ignore runtime frames (in particular callee save). } method_trace_->Set(count_, m); - dex_pc_trace_->Set(count_, GetDexPc()); + dex_pc_trace_->Set(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc()); ++count_; return true; } @@ -1384,7 +1384,6 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job mirror::ObjectArray* method_trace = soa.Decode*>(internal); int32_t depth = method_trace->GetLength() - 1; - mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -1413,19 +1412,32 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job for (int32_t i = 0; i < depth; ++i) { // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) mirror::ArtMethod* method = down_cast(method_trace->Get(i)); - mh.ChangeMethod(method); - uint32_t dex_pc = pc_trace->Get(i); - int32_t line_number = mh.GetLineNumFromDexPC(dex_pc); - // Allocate element, potentially triggering GC - // TODO: reuse class_name_object via Class::name_? - const char* descriptor = mh.GetDeclaringClassDescriptor(); - CHECK(descriptor != NULL); - std::string class_name(PrettyDescriptor(descriptor)); - SirtRef class_name_object(soa.Self(), - mirror::String::AllocFromModifiedUtf8(soa.Self(), - class_name.c_str())); - if (class_name_object.get() == NULL) { - return NULL; + MethodHelper mh(method); + int32_t line_number; + SirtRef class_name_object(soa.Self(), NULL); + SirtRef source_name_object(soa.Self(), NULL); + if (method->IsProxyMethod()) { + line_number = -1; + class_name_object.reset(method->GetDeclaringClass()->GetName()); + // source_name_object intentionally left null for proxy methods + } else { + mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); + uint32_t dex_pc = pc_trace->Get(i); + line_number = mh.GetLineNumFromDexPC(dex_pc); + // Allocate element, potentially triggering GC + // TODO: reuse class_name_object via Class::name_? + const char* descriptor = mh.GetDeclaringClassDescriptor(); + CHECK(descriptor != NULL); + std::string class_name(PrettyDescriptor(descriptor)); + class_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); + if (class_name_object.get() == NULL) { + return NULL; + } + const char* source_file = mh.GetDeclaringClassSourceFile(); + source_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object.get() == NULL) { + return NULL; + } } const char* method_name = mh.GetName(); CHECK(method_name != NULL); @@ -1435,14 +1447,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job if (method_name_object.get() == NULL) { return NULL; } - const char* source_file = mh.GetDeclaringClassSourceFile(); - SirtRef source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(), - source_file)); mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object.get(), - method_name_object.get(), - source_name_object.get(), - line_number); + class_name_object.get(), method_name_object.get(), source_name_object.get(), line_number); if (obj == NULL) { return NULL; } diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt index eafaf1d188..400a1619cd 100644 --- a/test/044-proxy/expected.txt +++ b/test/044-proxy/expected.txt @@ -42,6 +42,7 @@ Invoke public abstract java.lang.String Shapes.blob() (no args) --- blob Success: method blob res=mix +$Proxy1.getTrace null:-1 Invoke public abstract void Shapes.upChuck() (no args) Got expected ioobe @@ -49,8 +50,8 @@ Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedExcepti (no args) Got expected ie -Proxy interfaces: [interface Quads, interface Colors] -Proxy methods: [public final java.lang.String $Proxy1.blob(), public final double $Proxy1.blue(int), public final R0a $Proxy1.checkMe(), public final R0aa $Proxy1.checkMe(), public final R0base $Proxy1.checkMe(), public final void $Proxy1.circle(int), public final boolean $Proxy1.equals(java.lang.Object), public final int $Proxy1.green(double), public final int $Proxy1.hashCode(), public final int $Proxy1.mauve(java.lang.String), public final int $Proxy1.rectangle(int,int), public final int $Proxy1.red(float), public final int $Proxy1.square(int,int), public final java.lang.String $Proxy1.toString(), public final int $Proxy1.trapezoid(int,double,int), public final void $Proxy1.upCheck() throws java.lang.InterruptedException, public final void $Proxy1.upChuck()] +Proxy interfaces: [interface Quads, interface Colors, interface Trace] +Proxy methods: [public final java.lang.String $Proxy1.blob(), public final double $Proxy1.blue(int), public final R0a $Proxy1.checkMe(), public final R0aa $Proxy1.checkMe(), public final R0base $Proxy1.checkMe(), public final void $Proxy1.circle(int), public final boolean $Proxy1.equals(java.lang.Object), public final void $Proxy1.getTrace(), public final int $Proxy1.green(double), public final int $Proxy1.hashCode(), public final int $Proxy1.mauve(java.lang.String), public final int $Proxy1.rectangle(int,int), public final int $Proxy1.red(float), public final int $Proxy1.square(int,int), public final java.lang.String $Proxy1.toString(), public final int $Proxy1.trapezoid(int,double,int), public final void $Proxy1.upCheck() throws java.lang.InterruptedException, public final void $Proxy1.upChuck()] Decl annos: [] Param annos (0) : [] Dupe threw expected exception diff --git a/test/044-proxy/src/BasicTest.java b/test/044-proxy/src/BasicTest.java index 46aa3feea2..ea46f49f2b 100644 --- a/test/044-proxy/src/BasicTest.java +++ b/test/044-proxy/src/BasicTest.java @@ -51,6 +51,8 @@ public class BasicTest { colors.blue(777); colors.mauve("sorry"); colors.blob(); + Trace trace = (Trace) proxy; + trace.getTrace(); try { shapes.upChuck(); @@ -96,7 +98,7 @@ public class BasicTest { /* create the proxy class */ Class proxyClass = Proxy.getProxyClass(Shapes.class.getClassLoader(), - new Class[] { Quads.class, Colors.class }); + new Class[] { Quads.class, Colors.class, Trace.class }); /* create a proxy object, passing the handler object in */ Object proxy = null; @@ -156,6 +158,10 @@ interface Colors { public R0aa checkMe(); } +interface Trace { + public void getTrace(); +} + /* * Some return types. */ @@ -248,6 +254,20 @@ class MyInvocationHandler implements InvocationHandler { throw new RuntimeException("huh?"); } + if (method.getDeclaringClass() == Trace.class) { + if (method.getName().equals("getTrace")) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement ste = stackTrace[i]; + if (ste.getMethodName().equals("getTrace")) { + System.out.println(ste.getClassName() + "." + ste.getMethodName() + " " + + ste.getFileName() + ":" + ste.getLineNumber()); + } + } + return null; + } + } + System.out.println("Invoke " + method); if (args == null || args.length == 0) { System.out.println(" (no args)"); -- cgit v1.2.3 From 7170092b783c35cf48aaeafe903acad4eee81efc Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 20 Nov 2013 18:11:39 -0800 Subject: Search for miranda methods in virtual methods instead of interface. Also added tests that get miranda methods via reflection and jni. Miranda methods can't be found via reflection, and have the interface class as their declaring class when found via jni. Bug: 11736932 (cherry picked from 201803fb1acd15b9daae51d816e1b08aededdc41) Change-Id: If6700303698967b7fd2c0152216461c447cbf2f9 --- runtime/mirror/class-inl.h | 2 +- test/040-miranda/expected.txt | 2 ++ test/040-miranda/src/Main.java | 13 +++++++++++++ test/JniTest/JniTest.java | 22 ++++++++++++++++++++++ test/JniTest/jni_test.cc | 8 ++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 1e1138745d..998ebe1a8f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -241,7 +241,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho if (method->IsDirect()) { return method; } - if (method->GetDeclaringClass()->IsInterface()) { + if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) { return FindVirtualMethodForInterface(method); } return FindVirtualMethodForVirtual(method); diff --git a/test/040-miranda/expected.txt b/test/040-miranda/expected.txt index e22bbd974c..011be2af86 100644 --- a/test/040-miranda/expected.txt +++ b/test/040-miranda/expected.txt @@ -10,3 +10,5 @@ MirandaAbstract / MirandaClass2: inInterface: true inInterface2: 28 inAbstract: true +Test getting miranda method via reflection: + caught expected NoSuchMethodException diff --git a/test/040-miranda/src/Main.java b/test/040-miranda/src/Main.java index 1fd8287ba0..ff5eba0a17 100644 --- a/test/040-miranda/src/Main.java +++ b/test/040-miranda/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + /** * Miranda testing. */ @@ -37,5 +39,16 @@ public class Main { System.out.println(" inInterface: " + mira2.inInterface()); System.out.println(" inInterface2: " + mira2.inInterface2()); System.out.println(" inAbstract: " + mira2.inAbstract()); + + System.out.println("Test getting miranda method via reflection:"); + try { + Class mirandaClass = Class.forName("MirandaAbstract"); + Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface", (Class[]) null); + System.out.println(" did not expect to find miranda method"); + } catch (NoSuchMethodException nsme) { + System.out.println(" caught expected NoSuchMethodException"); + } catch (Exception e) { + System.out.println(" caught unexpected exception " + e); + } } } diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index 7014ef9334..a1b1f0c69d 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -14,11 +14,14 @@ * limitations under the License. */ +import java.lang.reflect.Method; + class JniTest { public static void main(String[] args) { System.loadLibrary("arttest"); testFindClassOnAttachedNativeThread(); testCallStaticVoidMethodOnSubClass(); + testGetMirandaMethod(); } private static native void testFindClassOnAttachedNativeThread(); @@ -42,4 +45,23 @@ class JniTest { private static class testCallStaticVoidMethodOnSubClass_SubClass extends testCallStaticVoidMethodOnSubClass_SuperClass { } + + private static native Method testGetMirandaMethodNative(); + + private static void testGetMirandaMethod() { + Method m = testGetMirandaMethodNative(); + if (m.getDeclaringClass() != testGetMirandaMethod_MirandaInterface.class) { + throw new AssertionError(); + } + } + + private static abstract class testGetMirandaMethod_MirandaAbstract implements testGetMirandaMethod_MirandaInterface { + public boolean inAbstract() { + return true; + } + } + + private static interface testGetMirandaMethod_MirandaInterface { + public boolean inInterface(); + } } diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index 72a3309d9d..cfcbb64f38 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -81,3 +81,11 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClas env->CallStaticVoidMethod(sub_class, execute); } + +extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNIEnv* env, jclass) { + jclass abstract_class = env->FindClass("JniTest$testGetMirandaMethod_MirandaAbstract"); + assert(abstract_class != NULL); + jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z"); + assert(miranda_method != NULL); + return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE); +} -- cgit v1.2.3 From 0c6afa40af7b9f097131126d084d5f19c2accaac Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 19 Dec 2013 14:59:00 -0800 Subject: Fix NewLocalRef, NewGlobalRef to handle cleared weak globals. We were not checking for null after decoding the reference, this meant that we incorrectly created null weak global references instead of returning null. Issue: 63929 Bug: 13400455 (cherry-picked from e8c48db6bb507d7fa20c78481c58c23be0045f67) Change-Id: I9159682e6edad8f415ef8144fc13b9aedd2cceb4 --- runtime/jni_internal.cc | 19 +++++++++++-------- runtime/thread.cc | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 6a0990eec6..412f96e4af 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -831,13 +831,14 @@ class JNI { } static jobject NewGlobalRef(JNIEnv* env, jobject obj) { - if (obj == NULL) { - return NULL; - } ScopedObjectAccess soa(env); + Object* decoded_obj = soa.Decode(obj); + // Check for null after decoding the object to handle cleared weak globals. + if (decoded_obj == nullptr) { + return nullptr; + } JavaVMExt* vm = soa.Vm(); IndirectReferenceTable& globals = vm->globals; - Object* decoded_obj = soa.Decode(obj); WriterMutexLock mu(soa.Self(), vm->globals_lock); IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj); return reinterpret_cast(ref); @@ -871,11 +872,13 @@ class JNI { } static jobject NewLocalRef(JNIEnv* env, jobject obj) { - if (obj == NULL) { - return NULL; - } ScopedObjectAccess soa(env); - return soa.AddLocalReference(soa.Decode(obj)); + mirror::Object* decoded_obj = soa.Decode(obj); + // Check for null after decoding the object to handle cleared weak globals. + if (decoded_obj == nullptr) { + return nullptr; + } + return soa.AddLocalReference(decoded_obj); } static void DeleteLocalRef(JNIEnv* env, jobject obj) { diff --git a/runtime/thread.cc b/runtime/thread.cc index 23cafe8707..60a3bf8196 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1191,7 +1191,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast(this), ref); if (result == kClearedJniWeakGlobal) { // This is a special case where it's okay to return NULL. - return NULL; + return nullptr; } } -- cgit v1.2.3 From c0f96d03a1855fda7d94332331b94860404874dd Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 13 Mar 2014 18:10:08 -0700 Subject: Fix stack overflow for mutual recursion. There was an error where we would have a pc that was in the method which generated the stack overflow. This didn't work however because the stack overflow check was before we stored the method in the stack. The result was that the stack overflow handler had a PC which wasnt necessarily in the method at the top of the stack. This is now fixed by always restoring the link register before branching to the throw entrypoint. Slight code size regression on ARM/Mips (unmeasured). Regression on ARM is 4 bytes of code per stack overflow check. Some of this regression is mitigated by having one less GC safepoint. Also adds test case for StackOverflowError issue (from bdc). Tests passing: ARM, X86, Mips Phone booting: ARM Bug: https://code.google.com/p/android/issues/detail?id=66411 Bug: 12967914 Change-Id: I96fe667799458b58d1f86671e051968f7be78d5d --- compiler/dex/quick/arm/utility_arm.cc | 3 +++ compiler/dex/quick/gen_common.cc | 34 +++++++++++++++++++++++++------ compiler/dex/quick/gen_invoke.cc | 9 +++++---- compiler/dex/quick/mips/mips_lir.h | 2 +- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/dex/quick/x86/assemble_x86.cc | 11 +++++++++- compiler/dex/quick/x86/call_x86.cc | 6 +++--- compiler/dex/quick/x86/utility_x86.cc | 1 + compiler/dex/quick/x86/x86_lir.h | 1 + runtime/oat.cc | 2 +- test/018-stack-overflow/expected.txt | 3 ++- test/018-stack-overflow/src/Main.java | 37 ++++++++++++++++++++++++++++++---- 12 files changed, 89 insertions(+), 22 deletions(-) diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index c63de69284..f29e04b0fa 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -223,6 +223,9 @@ LIR* ArmMir2Lir::OpReg(OpKind op, int r_dest_src) { case kOpBlx: opcode = kThumbBlxR; break; + case kOpBx: + opcode = kThumbBx; + break; default: LOG(FATAL) << "Bad opcode " << op; } diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index f018c61819..8e31a25d92 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -16,6 +16,7 @@ #include "dex/compiler_ir.h" #include "dex/compiler_internals.h" +#include "dex/quick/arm/arm_lir.h" #include "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mirror/array.h" @@ -544,7 +545,9 @@ void Mir2Lir::HandleThrowLaunchPads() { ThreadOffset func_offset(-1); int v1 = lab->operands[2]; int v2 = lab->operands[3]; - bool target_x86 = (cu_->instruction_set == kX86); + const bool target_x86 = cu_->instruction_set == kX86; + const bool target_arm = cu_->instruction_set == kArm || cu_->instruction_set == kThumb2; + const bool target_mips = cu_->instruction_set == kMips; switch (lab->operands[0]) { case kThrowNullPointer: func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowNullPointer); @@ -602,21 +605,40 @@ void Mir2Lir::HandleThrowLaunchPads() { func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowNoSuchMethod); break; - case kThrowStackOverflow: + case kThrowStackOverflow: { func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowStackOverflow); // Restore stack alignment + int r_tgt = 0; + const int spill_size = (num_core_spills_ + num_fp_spills_) * 4; if (target_x86) { - OpRegImm(kOpAdd, TargetReg(kSp), frame_size_); + // - 4 to leave link register on stack. + OpRegImm(kOpAdd, TargetReg(kSp), frame_size_ - 4); + ClobberCalleeSave(); + } else if (target_arm) { + r_tgt = r12; + LoadWordDisp(TargetReg(kSp), spill_size - 4, TargetReg(kLr)); + OpRegImm(kOpAdd, TargetReg(kSp), spill_size); + ClobberCalleeSave(); + LoadWordDisp(rARM_SELF, func_offset.Int32Value(), r_tgt); } else { - OpRegImm(kOpAdd, TargetReg(kSp), (num_core_spills_ + num_fp_spills_) * 4); + DCHECK(target_mips); + DCHECK_EQ(num_fp_spills_, 0); // FP spills currently don't happen on mips. + // LR is offset 0 since we push in reverse order. + LoadWordDisp(TargetReg(kSp), 0, TargetReg(kLr)); + OpRegImm(kOpAdd, TargetReg(kSp), spill_size); + ClobberCalleeSave(); + r_tgt = CallHelperSetup(func_offset); // Doesn't clobber LR. + DCHECK_NE(r_tgt, TargetReg(kLr)); } - break; + CallHelper(r_tgt, func_offset, false /* MarkSafepointPC */, false /* UseLink */); + continue; + } default: LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0]; } ClobberCalleeSave(); int r_tgt = CallHelperSetup(func_offset); - CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */); + CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */, true /* UseLink */); } } diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 2a0a23c7cd..3a39669103 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -33,7 +33,7 @@ namespace art { /* * To save scheduling time, helper calls are broken into two parts: generation of - * the helper target address, and the actuall call to the helper. Because x86 + * the helper target address, and the actual call to the helper. Because x86 * has a memory call operation, part 1 is a NOP for x86. For other targets, * load arguments between the two parts. */ @@ -42,12 +42,13 @@ int Mir2Lir::CallHelperSetup(ThreadOffset helper_offset) { } /* NOTE: if r_tgt is a temp, it will be freed following use */ -LIR* Mir2Lir::CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc) { +LIR* Mir2Lir::CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc, bool use_link) { LIR* call_inst; + OpKind op = use_link ? kOpBlx : kOpBx; if (cu_->instruction_set == kX86) { - call_inst = OpThreadMem(kOpBlx, helper_offset); + call_inst = OpThreadMem(op, helper_offset); } else { - call_inst = OpReg(kOpBlx, r_tgt); + call_inst = OpReg(op, r_tgt); FreeTemp(r_tgt); } if (safepoint_pc) { diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h index 278fcefcbf..dfa837cfbb 100644 --- a/compiler/dex/quick/mips/mips_lir.h +++ b/compiler/dex/quick/mips/mips_lir.h @@ -138,7 +138,6 @@ namespace art { #define r_FRESULT1 r_F1 // Regs not used for Mips. -#define rMIPS_LR INVALID_REG #define rMIPS_PC INVALID_REG // RegisterLocation templates return values (r_V0, or r_V0/r_V1). @@ -278,6 +277,7 @@ enum MipsNativeRegisterPool { #define rMIPS_RET1 r_RESULT1 #define rMIPS_INVOKE_TGT r_T9 #define rMIPS_COUNT INVALID_REG +#define rMIPS_LR r_RA enum MipsShiftEncodings { kMipsLsl = 0x0, diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index a37ebd173f..d41204fc46 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -432,7 +432,7 @@ class Mir2Lir : public Backend { // Shared by all targets - implemented in gen_invoke.cc. int CallHelperSetup(ThreadOffset helper_offset); - LIR* CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc); + LIR* CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_pc, bool use_link = true); void CallRuntimeHelperImm(ThreadOffset helper_offset, int arg0, bool safepoint_pc); void CallRuntimeHelperReg(ThreadOffset helper_offset, int arg0, bool safepoint_pc); void CallRuntimeHelperRegLocation(ThreadOffset helper_offset, RegLocation arg0, diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index e8834320a9..93612dcf50 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -319,6 +319,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Jmp8, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xEB, 0, 0, 0, 0, 0 }, "Jmp8", "!0t" }, { kX86Jmp32, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xE9, 0, 0, 0, 0, 0 }, "Jmp32", "!0t" }, { kX86JmpR, kJmp, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xFF, 0, 0, 4, 0, 0 }, "JmpR", "!0r" }, + { kX86JmpT, kJmp, IS_UNARY_OP | IS_BRANCH | IS_LOAD, { THREAD_PREFIX, 0, 0xFF, 0, 0, 4, 0, 0 }, "JmpT", "fs:[!0d]" }, { kX86CallR, kCall, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xE8, 0, 0, 0, 0, 0 }, "CallR", "!0r" }, { kX86CallM, kCall, IS_BINARY_OP | IS_BRANCH | IS_LOAD | REG_USE0, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallM", "[!0r+!1d]" }, { kX86CallA, kCall, IS_QUAD_OP | IS_BRANCH | IS_LOAD | REG_USE01, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallA", "[!0r+!1r<opcode == kX86Jmp32) { return 5; // opcode + rel32 + } else if (lir->opcode == kX86JmpT) { + return ComputeSize(entry, 0, 0x12345678, false); // displacement size is always 32bit } else { DCHECK(lir->opcode == kX86JmpR); return 2; // opcode + modrm @@ -1349,7 +1352,13 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { EmitRegCond(entry, lir->operands[0], lir->operands[1]); break; case kJmp: // lir operands - 0: rel - EmitJmp(entry, lir->operands[0]); + if (entry->opcode == kX86JmpT) { + // This works since the instruction format for jmp and call is basically the same and + // EmitCallThread loads opcode info. + EmitCallThread(entry, lir->operands[0]); + } else { + EmitJmp(entry, lir->operands[0]); + } break; case kJcc: // lir operands - 0: rel, 1: CC, target assigned EmitJcc(entry, lir->operands[0], lir->operands[1]); diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 2be2aa9a0e..67388666b4 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -224,15 +224,15 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { LockTemp(rX86_ARG2); /* Build frame, return address already on stack */ + // TODO: 64 bit. OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); /* * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = (mir_graph_->MethodIsLeaf() && - (static_cast(frame_size_) < - Thread::kStackOverflowReservedBytes)); + const bool skip_overflow_check = (mir_graph_->MethodIsLeaf() && + (static_cast(frame_size_) < Thread::kStackOverflowReservedBytes)); NewLIR0(kPseudoMethodEntry); /* Spill core callee saves */ SpillCoreRegs(); diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index c519bfec44..d03d4f8577 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -296,6 +296,7 @@ LIR* X86Mir2Lir::OpThreadMem(OpKind op, ThreadOffset thread_offset) { X86OpCode opcode = kX86Bkpt; switch (op) { case kOpBlx: opcode = kX86CallT; break; + case kOpBx: opcode = kX86JmpT; break; default: LOG(FATAL) << "Bad opcode: " << op; break; diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 643a3d5b8f..8a813e88ea 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -362,6 +362,7 @@ enum X86OpCode { kX86Jcc8, kX86Jcc32, // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned kX86Jmp8, kX86Jmp32, // jmp rel8/32; lir operands - 0: rel, target assigned kX86JmpR, // jmp reg; lir operands - 0: reg + kX86JmpT, // jmp fs:[disp]; fs: is equal to Thread::Current(); lir operands - 0: disp kX86CallR, // call reg; lir operands - 0: reg kX86CallM, // call [base + disp]; lir operands - 0: base, 1: disp kX86CallA, // call [base + index * scale + disp] diff --git a/runtime/oat.cc b/runtime/oat.cc index c01f77c364..6fe5d1097b 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '7', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '8', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); diff --git a/test/018-stack-overflow/expected.txt b/test/018-stack-overflow/expected.txt index 7797816785..98b45b7b21 100644 --- a/test/018-stack-overflow/expected.txt +++ b/test/018-stack-overflow/expected.txt @@ -1,2 +1,3 @@ -caught SOE +caught SOE in testSelfRecursion +caught SOE in testMutualRecursion SOE test done diff --git a/test/018-stack-overflow/src/Main.java b/test/018-stack-overflow/src/Main.java index f79c269c85..41adabc9ff 100644 --- a/test/018-stack-overflow/src/Main.java +++ b/test/018-stack-overflow/src/Main.java @@ -19,17 +19,46 @@ */ public class Main { public static void main(String args[]) { + testSelfRecursion(); + testMutualRecursion(); + System.out.println("SOE test done"); + } + + private static void testSelfRecursion() { try { stackOverflowTestSub(0.0, 0.0, 0.0); } catch (StackOverflowError soe) { - System.out.println("caught SOE"); + System.out.println("caught SOE in testSelfRecursion"); } - System.out.println("SOE test done"); } - private static void stackOverflowTestSub(double pad1, double pad2, - double pad3) { + private static void stackOverflowTestSub(double pad1, double pad2, double pad3) { stackOverflowTestSub(pad1, pad2, pad3); } + + private static void testMutualRecursion() { + try { + foo(0.0, 0.0, 0.0); + } + catch (StackOverflowError soe) { + System.out.println("caught SOE in testMutualRecursion"); + } + } + + private static void foo(double pad1, double pad2, double pad3) { + bar(pad1, pad2, pad3); + } + + private static void bar(double pad1, double pad2, double pad3) { + baz(pad1, pad2, pad3); + } + + private static void baz(double pad1, double pad2, double pad3) { + qux(pad1, pad2, pad3); + } + + private static void qux(double pad1, double pad2, double pad3) { + foo(pad1, pad2, pad3); + } } -- cgit v1.2.3 From d5e4ac0abdeeb4dc13bd05a40bf496299a787536 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 31 Jan 2014 18:44:56 -0800 Subject: Properly tolerate missing declaring source files Broken in 228d6b8a4f0a21c1e9b2372c3104ce4ee19f65b4 (cherry picked from commit 7c6aca27dd2df58ac3d83a93ec5848e2b7d3159a) Bug: 13575571 Bug: 12802375 Change-Id: Ia8a3196b15f8ed29810d0b6f09f81c7ae4e7480e --- runtime/thread.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 7fd40c945c..25418a9758 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1414,8 +1414,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job mirror::ArtMethod* method = down_cast(method_trace->Get(i)); MethodHelper mh(method); int32_t line_number; - SirtRef class_name_object(soa.Self(), NULL); - SirtRef source_name_object(soa.Self(), NULL); + SirtRef class_name_object(soa.Self(), nullptr); + SirtRef source_name_object(soa.Self(), nullptr); if (method->IsProxyMethod()) { line_number = -1; class_name_object.reset(method->GetDeclaringClass()->GetName()); @@ -1427,16 +1427,18 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job // Allocate element, potentially triggering GC // TODO: reuse class_name_object via Class::name_? const char* descriptor = mh.GetDeclaringClassDescriptor(); - CHECK(descriptor != NULL); + CHECK(descriptor != nullptr); std::string class_name(PrettyDescriptor(descriptor)); class_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); - if (class_name_object.get() == NULL) { - return NULL; + if (class_name_object.get() == nullptr) { + return nullptr; } const char* source_file = mh.GetDeclaringClassSourceFile(); - source_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - if (source_name_object.get() == NULL) { - return NULL; + if (source_file != nullptr) { + source_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object.get() == nullptr) { + return nullptr; + } } } const char* method_name = mh.GetName(); -- cgit v1.2.3