diff options
| -rw-r--r-- | build/Android.common_test.mk | 8 | ||||
| -rw-r--r-- | compiler/dex/quick/arm/fp_arm.cc | 11 | ||||
| -rw-r--r-- | compiler/image_writer.cc | 2 | ||||
| -rw-r--r-- | compiler/jni/jni_compiler_test.cc | 100 | ||||
| -rw-r--r-- | runtime/arch/arm/quick_entrypoints_arm.S | 24 | ||||
| -rw-r--r-- | runtime/arch/x86/fault_handler_x86.cc | 4 | ||||
| -rw-r--r-- | runtime/arch/x86/quick_entrypoints_x86.S | 6 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 235 | ||||
| -rw-r--r-- | runtime/class_linker.h | 8 | ||||
| -rw-r--r-- | runtime/common_runtime_test.h | 6 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 10 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_VMStack.cc | 8 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 33 | ||||
| -rw-r--r-- | runtime/oat_file.h | 40 | ||||
| -rw-r--r-- | runtime/quick_exception_handler.cc | 22 | ||||
| -rw-r--r-- | runtime/quick_exception_handler.h | 8 | ||||
| -rw-r--r-- | runtime/stack.h | 4 | ||||
| -rw-r--r-- | runtime/thread.cc | 4 | ||||
| -rw-r--r-- | test/004-JniTest/jni_test.cc | 232 | ||||
| -rw-r--r-- | test/004-JniTest/src/Main.java | 14 | ||||
| -rwxr-xr-x | test/116-nodex2oat/run | 3 | ||||
| -rw-r--r-- | test/MyClassNatives/MyClassNatives.java | 12 | ||||
| -rwxr-xr-x | test/run-test | 8 |
23 files changed, 518 insertions, 284 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index 52d1ee31a5..e2f39496bd 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -33,7 +33,13 @@ ART_TEST_KNOWN_BROKEN := \ test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \ test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \ test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \ - test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 + test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 \ + test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC32 \ + test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC32 \ + test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC32 \ + test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC64 \ + test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC64 \ + test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC64 # List of known failing tests that when executed won't cause test execution to not finish. # The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64. diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index 2ad11da964..3eb7c83c11 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -383,8 +383,17 @@ bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) { RegLocation rl_result = EvalLoc(rl_dest, reg_class, true); if (reg_class == kFPReg) { NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); + } else if (rl_result.reg.GetLow().GetReg() != rl_src.reg.GetHigh().GetReg()) { + // No inconvenient overlap. + OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow()); + OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x7fffffff); } else { - OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff); + // Inconvenient overlap, use a temp register to preserve the high word of the source. + RegStorage rs_tmp = AllocTemp(); + OpRegCopy(rs_tmp, rl_src.reg.GetHigh()); + OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow()); + OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rs_tmp, 0x7fffffff); + FreeTemp(rs_tmp); } StoreValueWide(rl_dest, rl_result); return true; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index ba7e13f815..9c9cdf2700 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -232,7 +232,7 @@ bool ImageWriter::AllocMemory() { size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize); std::string error_msg; image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE, - true, &error_msg)); + false, &error_msg)); if (UNLIKELY(image_.get() == nullptr)) { LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; return false; diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 75d3030baf..deefdecba0 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -16,6 +16,8 @@ #include <memory> +#include <math.h> + #include "class_linker.h" #include "common_compiler_test.h" #include "dex_file.h" @@ -47,7 +49,7 @@ namespace art { class JniCompilerTest : public CommonCompilerTest { protected: void CompileForTest(jobject class_loader, bool direct, - const char* method_name, const char* method_sig) { + const char* method_name, const char* method_sig, bool generic = false) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> loader( @@ -61,25 +63,29 @@ class JniCompilerTest : public CommonCompilerTest { method = c->FindVirtualMethod(method_name, method_sig); } ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; - if (method->GetEntryPointFromQuickCompiledCode() == nullptr || - method->GetEntryPointFromQuickCompiledCode() == class_linker_->GetQuickGenericJniTrampoline()) { - CompileMethod(method); - ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr) - << method_name << " " << method_sig; - ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr) - << method_name << " " << method_sig; + if (generic) { + method->SetEntryPointFromQuickCompiledCode(class_linker_->GetQuickGenericJniTrampoline()); + } else { + if (method->GetEntryPointFromQuickCompiledCode() == nullptr || + method->GetEntryPointFromQuickCompiledCode() == class_linker_->GetQuickGenericJniTrampoline()) { + CompileMethod(method); + ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr) + << method_name << " " << method_sig; + ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr) + << method_name << " " << method_sig; + } } } void SetUpForTest(bool direct, const char* method_name, const char* method_sig, - void* native_fnptr) { + void* native_fnptr, bool generic = false) { // Initialize class loader and compile method when runtime not started. if (!runtime_->IsStarted()) { { ScopedObjectAccess soa(Thread::Current()); class_loader_ = LoadDex("MyClassNatives"); } - CompileForTest(class_loader_, direct, method_name, method_sig); + CompileForTest(class_loader_, direct, method_name, method_sig, generic); // Start runtime. Thread::Current()->TransitionFromSuspendedToRunnable(); bool started = runtime_->Start(); @@ -424,6 +430,80 @@ TEST_F(JniCompilerTest, CompileAndRunStaticDoubleDoubleMethod) { EXPECT_EQ(2, gJava_MyClassNatives_fooSDD_calls); } +// The x86 generic JNI code had a bug where it assumed a floating +// point return value would be in xmm0. We use log, to somehow ensure +// the compiler will use the floating point stack. + +jdouble Java_MyClassNatives_logD(JNIEnv* env, jclass klass, jdouble x) { + return log(x); +} + +TEST_F(JniCompilerTest, RunGenericStaticLogDoubleethod) { + TEST_DISABLED_FOR_PORTABLE(); + TEST_DISABLED_FOR_MIPS(); + SetUpForTest(true, "logD", "(D)D", + reinterpret_cast<void*>(&Java_MyClassNatives_logD), true); + + jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0); + EXPECT_EQ(log(2.0), result); +} + +jfloat Java_MyClassNatives_logF(JNIEnv* env, jclass klass, jfloat x) { + return logf(x); +} + +TEST_F(JniCompilerTest, RunGenericStaticLogFloatMethod) { + TEST_DISABLED_FOR_PORTABLE(); + TEST_DISABLED_FOR_MIPS(); + SetUpForTest(true, "logF", "(F)F", + reinterpret_cast<void*>(&Java_MyClassNatives_logF), true); + + jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0); + EXPECT_EQ(logf(2.0), result); +} + +jboolean Java_MyClassNatives_returnTrue(JNIEnv* env, jclass klass) { + return JNI_TRUE; +} + +jboolean Java_MyClassNatives_returnFalse(JNIEnv* env, jclass klass) { + return JNI_FALSE; +} + +jint Java_MyClassNatives_returnInt(JNIEnv* env, jclass klass) { + return 42; +} + +TEST_F(JniCompilerTest, RunGenericStaticReturnTrue) { + TEST_DISABLED_FOR_PORTABLE(); + TEST_DISABLED_FOR_MIPS(); + SetUpForTest(true, "returnTrue", "()Z", + reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue), true); + + jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_); + EXPECT_TRUE(result); +} + +TEST_F(JniCompilerTest, RunGenericStaticReturnFalse) { + TEST_DISABLED_FOR_PORTABLE(); + TEST_DISABLED_FOR_MIPS(); + SetUpForTest(true, "returnFalse", "()Z", + reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse), true); + + jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_); + EXPECT_FALSE(result); +} + +TEST_F(JniCompilerTest, RunGenericStaticReturnInt) { + TEST_DISABLED_FOR_PORTABLE(); + TEST_DISABLED_FOR_MIPS(); + SetUpForTest(true, "returnInt", "()I", + reinterpret_cast<void*>(&Java_MyClassNatives_returnInt), true); + + jint result = env_->CallStaticIntMethod(jklass_, jmethod_); + EXPECT_EQ(42, result); +} + int gJava_MyClassNatives_fooSIOO_calls = 0; jobject Java_MyClassNatives_fooSIOO(JNIEnv* env, jclass klass, jint x, jobject y, jobject z) { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index dd1f04a9b4..9f0db8c716 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1034,14 +1034,13 @@ ENTRY art_quick_generic_jni_trampoline // result sign extension is handled in C code // prepare for artQuickGenericJniEndTrampoline call // (Thread*, result, result_f) - // r0 r1,r2 r3,stack <= C calling convention + // r0 r2,r3 stack <= C calling convention // r11 r0,r1 r0,r1 <= where they are - sub sp, sp, #12 // Stack alignment. + sub sp, sp, #8 // Stack alignment. - push {r1} - mov r3, r0 - mov r2, r1 - mov r1, r0 + push {r0-r1} + mov r3, r1 + mov r2, r0 mov r0, r11 blx artQuickGenericJniEndTrampoline @@ -1058,7 +1057,18 @@ ENTRY art_quick_generic_jni_trampoline cbnz r2, .Lexception_in_native // Tear down the callee-save frame. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + add sp, #12 @ rewind sp + // Do not pop r0 and r1, they contain the return value. + pop {r2-r3, r5-r8, r10-r11, lr} @ 9 words of callee saves + .cfi_restore r2 + .cfi_restore r3 + .cfi_restore r5 + .cfi_restore r6 + .cfi_restore r7 + .cfi_restore r8 + .cfi_restore r10 + .cfi_restore r11 + .cfi_adjust_cfa_offset -48 bx lr // ret diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index c143c5d825..65a48f6b20 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -66,7 +66,7 @@ namespace art { #if defined(__APPLE__) && defined(__x86_64__) // mac symbols have a prefix of _ on x86_64 extern "C" void _art_quick_throw_null_pointer_exception(); -extern "C" void _art_quick_throw_stack_overflow_from_signal(); +extern "C" void _art_quick_throw_stack_overflow(); extern "C" void _art_quick_test_suspend(); #define EXT_SYM(sym) _ ## sym #else @@ -395,7 +395,7 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { // the previous frame. // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. - uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow); + uc->CTX_EIP = reinterpret_cast<uintptr_t>(EXT_SYM(art_quick_throw_stack_overflow)); return true; } diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 75ec49deb0..084846abf0 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1168,10 +1168,10 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline // prepare for artQuickGenericJniEndTrampoline call // (Thread*, result, result_f) // (esp) 4(esp) 12(esp) <= C calling convention - // fs:... eax:edx xmm0 <= where they are + // fs:... eax:edx fp0 <= where they are subl LITERAL(20), %esp // Padding & pass float result. - movsd %xmm0, (%esp) + fstpl (%esp) pushl %edx // Pass int result. pushl %eax pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). @@ -1196,7 +1196,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline POP ebp // Restore callee saves POP esi POP edi - // store into fpr, for when it's a fpr return... + // Quick expects the return value to be in xmm0. movd %eax, %xmm0 movd %edx, %xmm1 punpckldq %xmm1, %xmm0 diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index db42146ffe..1e91145caa 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2022,21 +2022,20 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref); } -bool ClassLinker::FindOatClass(const DexFile& dex_file, - uint16_t class_def_idx, - OatFile::OatClass* oat_class) { - DCHECK(oat_class != nullptr); +OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, + bool* found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file); if (oat_file == nullptr) { - return false; + *found = false; + return OatFile::OatClass::Invalid(); } uint dex_location_checksum = dex_file.GetLocationChecksum(); const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), &dex_location_checksum); CHECK(oat_dex_file != NULL) << dex_file.GetLocation(); - *oat_class = oat_dex_file->GetOatClass(class_def_idx); - return true; + *found = true; + return oat_dex_file->GetOatClass(class_def_idx); } static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, @@ -2073,8 +2072,7 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16 return 0; } -bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) { - DCHECK(oat_method != nullptr); +const OatFile::OatMethod ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, bool* found) { // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); @@ -2101,15 +2099,14 @@ bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(), method->GetDeclaringClass()->GetDexClassDefIndex(), method->GetDexMethodIndex())); - OatFile::OatClass oat_class; - if (!FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), - declaring_class->GetDexClassDefIndex(), - &oat_class)) { - return false; - } - - *oat_method = oat_class.GetOatMethod(oat_method_index); - return true; + OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(), + declaring_class->GetDexClassDefIndex(), + found); + if (!found) { + return OatFile::OatMethod::Invalid(); + } + *found = true; + return oat_class.GetOatMethod(oat_method_index); } // Special case to get oat code without overwriting a trampoline. @@ -2118,9 +2115,10 @@ const void* ClassLinker::GetQuickOatCodeFor(mirror::ArtMethod* method) { if (method->IsProxyMethod()) { return GetQuickProxyInvokeHandler(); } - OatFile::OatMethod oat_method; + bool found; + OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); const void* result = nullptr; - if (FindOatMethodFor(method, &oat_method)) { + if (found) { result = oat_method.GetQuickCode(); } @@ -2146,10 +2144,11 @@ const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method, if (method->IsProxyMethod()) { return GetPortableProxyInvokeHandler(); } - OatFile::OatMethod oat_method; + bool found; + OatFile::OatMethod oat_method = FindOatMethodFor(method, &found); const void* result = nullptr; const void* quick_code = nullptr; - if (FindOatMethodFor(method, &oat_method)) { + if (found) { result = oat_method.GetPortableCode(); quick_code = oat_method.GetQuickCode(); } @@ -2170,8 +2169,9 @@ const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method, const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - OatFile::OatClass oat_class; - if (!FindOatClass(dex_file, class_def_idx, &oat_class)) { + bool found; + OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); + if (!found) { return nullptr; } uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); @@ -2180,8 +2180,9 @@ const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t cl const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - OatFile::OatClass oat_class; - if (!FindOatClass(dex_file, class_def_idx, &oat_class)) { + bool found; + OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); + if (!found) { return nullptr; } uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); @@ -2234,8 +2235,9 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { while (it.HasNextInstanceField()) { it.Next(); } - OatFile::OatClass oat_class; - bool has_oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class); + bool has_oat_class; + OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), + &has_oat_class); // Link the code of methods skipped by LinkCode. for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { mirror::ArtMethod* method = klass->GetDirectMethod(method_index); @@ -2386,12 +2388,16 @@ void ClassLinker::LoadClass(const DexFile& dex_file, return; // no fields or methods - for example a marker interface } - OatFile::OatClass oat_class; - if (Runtime::Current()->IsStarted() - && !Runtime::Current()->UseCompileTimeClassPath() - && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) { - LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class); - } else { + + bool has_oat_class = false; + if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) { + OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), + &has_oat_class); + if (has_oat_class) { + LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class); + } + } + if (!has_oat_class) { LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr); } } @@ -4853,7 +4859,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t Handle<mirror::ClassLoader> class_loader, Handle<mirror::ArtMethod> referrer, InvokeType type) { - DCHECK(dex_cache.Get() != NULL); + DCHECK(dex_cache.Get() != nullptr); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); if (resolved != nullptr && !resolved->IsRuntimeMethod()) { @@ -4862,9 +4868,9 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); - if (klass == NULL) { + if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); - return NULL; + return nullptr; } // Scan using method_idx, this saves string compares but will only hit for matching dex // caches/files. @@ -4875,7 +4881,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; case kInterface: resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx); - DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); + DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: @@ -4884,7 +4890,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t default: LOG(FATAL) << "Unreachable - invocation type: " << type; } - if (resolved == NULL) { + if (resolved == nullptr) { // Search by name, which works across dex files. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); const Signature signature = dex_file.GetMethodSignature(method_id); @@ -4895,7 +4901,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; case kInterface: resolved = klass->FindInterfaceMethod(name, signature); - DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); + DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: @@ -4903,94 +4909,97 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; } } - if (resolved != NULL) { - // We found a method, check for incompatible class changes. - if (resolved->CheckIncompatibleClassChange(type)) { - resolved = NULL; - } - } - if (resolved != NULL) { + // If we found a method, check for incompatible class changes. + if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) { // Be a good citizen and update the dex cache to speed subsequent calls. dex_cache->SetResolvedMethod(method_idx, resolved); return resolved; } else { - // We failed to find the method which means either an access error, an incompatible class - // change, or no such method. First try to find the method among direct and virtual methods. - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: - case kStatic: - resolved = klass->FindVirtualMethod(name, signature); - break; - case kInterface: - case kVirtual: - case kSuper: - resolved = klass->FindDirectMethod(name, signature); - break; - } + // If we had a method, it's an incompatible-class-change error. + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer.Get()); + } else { + // We failed to find the method which means either an access error, an incompatible class + // change, or no such method. First try to find the method among direct and virtual methods. + const char* name = dex_file.StringDataByIdx(method_id.name_idx_); + const Signature signature = dex_file.GetMethodSignature(method_id); + switch (type) { + case kDirect: + case kStatic: + resolved = klass->FindVirtualMethod(name, signature); + // Note: kDirect and kStatic are also mutually exclusive, but in that case we would + // have had a resolved method before, which triggers the "true" branch above. + break; + case kInterface: + case kVirtual: + case kSuper: + resolved = klass->FindDirectMethod(name, signature); + break; + } - // If we found something, check that it can be accessed by the referrer. - if (resolved != NULL && referrer.Get() != NULL) { - mirror::Class* methods_class = resolved->GetDeclaringClass(); - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (!referring_class->CanAccess(methods_class)) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, - resolved, type); - return NULL; - } else if (!referring_class->CanAccessMember(methods_class, - resolved->GetAccessFlags())) { - ThrowIllegalAccessErrorMethod(referring_class, resolved); - return NULL; + // If we found something, check that it can be accessed by the referrer. + if (resolved != nullptr && referrer.Get() != nullptr) { + mirror::Class* methods_class = resolved->GetDeclaringClass(); + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CanAccess(methods_class)) { + ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, + resolved, type); + return nullptr; + } else if (!referring_class->CanAccessMember(methods_class, + resolved->GetAccessFlags())) { + ThrowIllegalAccessErrorMethod(referring_class, resolved); + return nullptr; + } } - } - // Otherwise, throw an IncompatibleClassChangeError if we found something, and check interface - // methods and throw if we find the method there. If we find nothing, throw a NoSuchMethodError. - switch (type) { - case kDirect: - case kStatic: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + // Otherwise, throw an IncompatibleClassChangeError if we found something, and check interface + // methods and throw if we find the method there. If we find nothing, throw a + // NoSuchMethodError. + switch (type) { + case kDirect: + case kStatic: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindInterfaceMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kInterface: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindVirtualMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); + break; + case kInterface: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindVirtualMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kSuper: - ThrowNoSuchMethodError(type, klass, name, signature); - break; - case kVirtual: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + break; + case kSuper: + ThrowNoSuchMethodError(type, klass, name, signature); + break; + case kVirtual: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindInterfaceMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; + break; + } } DCHECK(Thread::Current()->IsExceptionPending()); - return NULL; + return nullptr; } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 6fc0f0e2f2..d2c9b4070d 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -392,7 +392,7 @@ class ClassLinker { } private: - bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) + const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); OatFile& GetImageOatFile(gc::space::ImageSpace* space) @@ -461,9 +461,9 @@ class ClassLinker { void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Finds the associated oat class for a dex_file and descriptor. Returns whether the class - // was found, and sets the data in oat_class. - bool FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, OatFile::OatClass* oat_class) + // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on + // error and sets found to false. + OatFile::OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 12c1241270..ddb6c81712 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -161,6 +161,12 @@ class CheckJniAbortCatcher { return; \ } +#define TEST_DISABLED_FOR_MIPS() \ + if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { \ + printf("WARNING: TEST DISABLED FOR MIPS\n"); \ + return; \ + } + } // namespace art namespace std { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 4730701f2c..dfd2e11fc2 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1699,7 +1699,15 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, jvalue result, artQuickGenericJniEndJNINonRef(self, cookie, lock); switch (return_shorty_char) { - case 'F': // Fall-through. + case 'F': { + if (kRuntimeISA == kX86) { + // Convert back the result to float. + double d = bit_cast<uint64_t, double>(result_f); + return bit_cast<float, uint32_t>(static_cast<float>(d)); + } else { + return result_f; + } + } case 'D': return result_f; case 'Z': diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 5f718ba213..b0792293e0 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -76,6 +76,10 @@ static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { ScopedFastNativeObjectAccess soa(env); NthCallerVisitor visitor(soa.Self(), 2); visitor.WalkStack(); + if (UNLIKELY(visitor.caller == nullptr)) { + // The caller is an attached native thread. + return nullptr; + } return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader()); } @@ -113,6 +117,10 @@ static jclass VMStack_getStackClass2(JNIEnv* env, jclass) { ScopedFastNativeObjectAccess soa(env); NthCallerVisitor visitor(soa.Self(), 3); visitor.WalkStack(); + if (UNLIKELY(visitor.caller == nullptr)) { + // The caller is an attached native thread. + return nullptr; + } return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass()); } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 971daf8bbf..b74b10fee7 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -460,15 +460,17 @@ OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) con uint32_t bitmap_size = 0; const byte* bitmap_pointer = nullptr; const byte* methods_pointer = nullptr; - if (type == kOatClassSomeCompiled) { - bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer)); - bitmap_pointer = after_type_pointer + sizeof(bitmap_size); - CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation(); - methods_pointer = bitmap_pointer + bitmap_size; - } else { - methods_pointer = after_type_pointer; + if (type != kOatClassNoneCompiled) { + if (type == kOatClassSomeCompiled) { + bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer)); + bitmap_pointer = after_type_pointer + sizeof(bitmap_size); + CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation(); + methods_pointer = bitmap_pointer + bitmap_size; + } else { + methods_pointer = after_type_pointer; + } + CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation(); } - CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation(); return OatClass(oat_file_, status, @@ -486,22 +488,23 @@ OatFile::OatClass::OatClass(const OatFile* oat_file, const OatMethodOffsets* methods_pointer) : oat_file_(oat_file), status_(status), type_(type), bitmap_(bitmap_pointer), methods_pointer_(methods_pointer) { - CHECK(methods_pointer != nullptr); switch (type_) { case kOatClassAllCompiled: { CHECK_EQ(0U, bitmap_size); CHECK(bitmap_pointer == nullptr); + CHECK(methods_pointer != nullptr); break; } case kOatClassSomeCompiled: { CHECK_NE(0U, bitmap_size); CHECK(bitmap_pointer != nullptr); + CHECK(methods_pointer != nullptr); break; } case kOatClassNoneCompiled: { CHECK_EQ(0U, bitmap_size); CHECK(bitmap_pointer == nullptr); - methods_pointer_ = nullptr; + CHECK(methods_pointer_ == nullptr); break; } case kOatClassMax: { @@ -536,16 +539,6 @@ const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) oat_method_offsets.gc_map_offset_); } -OatFile::OatMethod::OatMethod(const byte* base, - const uint32_t code_offset, - const uint32_t gc_map_offset) - : begin_(base), - code_offset_(code_offset), - native_gc_map_offset_(gc_map_offset) { -} - -OatFile::OatMethod::~OatMethod() {} - uint32_t OatFile::OatMethod::GetQuickCodeSize() const { uintptr_t code = reinterpret_cast<uintptr_t>(GetQuickCode()); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 810eccb2d1..508bfc229e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -126,14 +126,19 @@ class OatFile { const uint8_t* GetMappingTable() const; const uint8_t* GetVmapTable() const; - ~OatMethod(); - // Create an OatMethod with offsets relative to the given base address - OatMethod(const byte* base, - const uint32_t code_offset, - const uint32_t gc_map_offset); + OatMethod(const byte* base, const uint32_t code_offset, const uint32_t gc_map_offset) + : begin_(base), + code_offset_(code_offset), + native_gc_map_offset_(gc_map_offset) { + } + ~OatMethod() {} - OatMethod() {} + // A representation of an invalid OatMethod, used when an OatMethod or OatClass can't be found. + // See ClassLinker::FindOatMethodFor. + static const OatMethod Invalid() { + return OatMethod(nullptr, -1, -1); + } private: template<class T> @@ -144,10 +149,10 @@ class OatFile { return reinterpret_cast<T>(begin_ + offset); } - const byte* begin_; + const byte* const begin_; - uint32_t code_offset_; - uint32_t native_gc_map_offset_; + const uint32_t code_offset_; + const uint32_t native_gc_map_offset_; friend class OatClass; }; @@ -168,7 +173,12 @@ class OatFile { // methods are not included. const OatMethod GetOatMethod(uint32_t method_index) const; - OatClass() {} + // A representation of an invalid OatClass, used when an OatClass can't be found. + // See ClassLinker::FindOatClass. + static OatClass Invalid() { + return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr, + nullptr); + } private: OatClass(const OatFile* oat_file, @@ -178,15 +188,15 @@ class OatFile { const uint32_t* bitmap_pointer, const OatMethodOffsets* methods_pointer); - const OatFile* oat_file_; + const OatFile* const oat_file_; - mirror::Class::Status status_; + const mirror::Class::Status status_; - OatClassType type_; + const OatClassType type_; - const uint32_t* bitmap_; + const uint32_t* const bitmap_; - const OatMethodOffsets* methods_pointer_; + const OatMethodOffsets* const methods_pointer_; friend class OatDexFile; }; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 41d69894d5..98eeda7263 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -29,14 +29,14 @@ namespace art { static constexpr bool kDebugExceptionDelivery = false; -static constexpr size_t kInvalidFrameId = 0xffffffff; +static constexpr size_t kInvalidFrameDepth = 0xffffffff; QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimization) : self_(self), context_(self->GetLongJumpContext()), is_deoptimization_(is_deoptimization), method_tracing_active_(is_deoptimization || Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()), handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_method_(nullptr), - handler_dex_pc_(0), clear_exception_(false), handler_frame_id_(kInvalidFrameId) { + handler_dex_pc_(0), clear_exception_(false), handler_frame_depth_(kInvalidFrameDepth) { } // Finds catch handler or prepares for deoptimization. @@ -51,7 +51,7 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method = GetMethod(); - exception_handler_->SetHandlerFrameId(GetFrameId()); + exception_handler_->SetHandlerFrameDepth(GetFrameDepth()); if (method == nullptr) { // This is the upcall, we remember the frame and last pc so that we may long jump to them. exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc()); @@ -177,7 +177,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - exception_handler_->SetHandlerFrameId(GetFrameId()); + exception_handler_->SetHandlerFrameDepth(GetFrameDepth()); mirror::ArtMethod* method = GetMethod(); if (method == nullptr) { // This is the upcall, we remember the frame and last pc so that we may long jump to them. @@ -295,17 +295,17 @@ void QuickExceptionHandler::DeoptimizeStack() { // Unwinds all instrumentation stack frame prior to catch handler or upcall. class InstrumentationStackVisitor : public StackVisitor { public: - InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_id) + InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : StackVisitor(self, nullptr), - self_(self), frame_id_(frame_id), + self_(self), frame_depth_(frame_depth), instrumentation_frames_to_pop_(0) { - CHECK_NE(frame_id_, kInvalidFrameId); + CHECK_NE(frame_depth_, kInvalidFrameDepth); } bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t current_frame_id = GetFrameId(); - if (current_frame_id > frame_id_) { + size_t current_frame_depth = GetFrameDepth(); + if (current_frame_depth < frame_depth_) { CHECK(GetMethod() != nullptr); if (UNLIKELY(GetQuickInstrumentationExitPc() == GetReturnPc())) { ++instrumentation_frames_to_pop_; @@ -323,7 +323,7 @@ class InstrumentationStackVisitor : public StackVisitor { private: Thread* const self_; - const size_t frame_id_; + const size_t frame_depth_; size_t instrumentation_frames_to_pop_; DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); @@ -331,7 +331,7 @@ class InstrumentationStackVisitor : public StackVisitor { void QuickExceptionHandler::UpdateInstrumentationStack() { if (method_tracing_active_) { - InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_id_); + InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_depth_); visitor.WalkStack(true); size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop(); diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 1d600ed697..b93769cb97 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -77,8 +77,8 @@ class QuickExceptionHandler { clear_exception_ = clear_exception; } - void SetHandlerFrameId(size_t frame_id) { - handler_frame_id_ = frame_id; + void SetHandlerFrameDepth(size_t frame_depth) { + handler_frame_depth_ = frame_depth; } private: @@ -97,8 +97,8 @@ class QuickExceptionHandler { uint32_t handler_dex_pc_; // Should the exception be cleared as the catch block has no move-exception? bool clear_exception_; - // Frame id of the catch handler or the upcall. - size_t handler_frame_id_; + // Frame depth of the catch handler or the upcall. + size_t handler_frame_depth_; DISALLOW_COPY_AND_ASSIGN(QuickExceptionHandler); }; diff --git a/runtime/stack.h b/runtime/stack.h index 578f569c43..e58caeee79 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -553,6 +553,10 @@ class StackVisitor { return num_frames_; } + size_t GetFrameDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return cur_depth_; + } + // Get the method and dex pc immediately after the one that's currently being visited. bool GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/thread.cc b/runtime/thread.cc index 7aabfceb2b..f06d0812a8 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -505,9 +505,7 @@ void Thread::InitStackHwm() { } // TODO: move this into the Linux GetThreadStack implementation. -#if defined(__APPLE__) - bool is_main_thread = false; -#else +#if !defined(__APPLE__) // If we're the main thread, check whether we were run with an unlimited stack. In that case, // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection // will be broken because we'll die long before we get close to 2GB. diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index 554712aa55..f5a1d65ac1 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -28,162 +28,133 @@ static JavaVM* jvm = NULL; extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { - assert(vm != NULL); - assert(jvm == NULL); + assert(vm != nullptr); + assert(jvm == nullptr); jvm = vm; return JNI_VERSION_1_6; } -static void* testFindClassOnAttachedNativeThread(void*) { - assert(jvm != NULL); +static void* AttachHelper(void* arg) { + assert(jvm != nullptr); - JNIEnv* env = NULL; + JNIEnv* env = nullptr; JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; int attach_result = jvm->AttachCurrentThread(&env, &args); assert(attach_result == 0); - jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); - assert(!env->ExceptionCheck()); - - jobjectArray array = env->NewObjectArray(0, clazz, NULL); - assert(array != NULL); - assert(!env->ExceptionCheck()); + typedef void (*Fn)(JNIEnv*); + Fn fn = reinterpret_cast<Fn>(arg); + fn(env); int detach_result = jvm->DetachCurrentThread(); assert(detach_result == 0); - return NULL; + return nullptr; } -// http://b/10994325 -extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, - jclass) { +static void PthreadHelper(void (*fn)(JNIEnv*)) { pthread_t pthread; - int pthread_create_result = pthread_create(&pthread, - NULL, - testFindClassOnAttachedNativeThread, - NULL); + int pthread_create_result = pthread_create(&pthread, nullptr, AttachHelper, + reinterpret_cast<void*>(fn)); assert(pthread_create_result == 0); - int pthread_join_result = pthread_join(pthread, NULL); + int pthread_join_result = pthread_join(pthread, nullptr); assert(pthread_join_result == 0); } -static void* testFindFieldOnAttachedNativeThread(void*) { - assert(jvm != NULL); +static void testFindClassOnAttachedNativeThread(JNIEnv* env) { + jclass clazz = env->FindClass("Main"); + assert(clazz != nullptr); + assert(!env->ExceptionCheck()); - JNIEnv* env = NULL; - JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; - int attach_result = jvm->AttachCurrentThread(&env, &args); - assert(attach_result == 0); + jobjectArray array = env->NewObjectArray(0, clazz, nullptr); + assert(array != nullptr); + assert(!env->ExceptionCheck()); +} + +// http://b/10994325 +extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { + PthreadHelper(&testFindClassOnAttachedNativeThread); +} +static void testFindFieldOnAttachedNativeThread(JNIEnv* env) { jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); + assert(clazz != nullptr); assert(!env->ExceptionCheck()); jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z"); - assert(field != NULL); + assert(field != nullptr); 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_Main_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); + jclass) { + PthreadHelper(&testFindFieldOnAttachedNativeThread); } -static void* testReflectFieldGetFromAttachedNativeThread(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); - +static void testReflectFieldGetFromAttachedNativeThread(JNIEnv* env) { jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); + assert(clazz != nullptr); assert(!env->ExceptionCheck()); jclass class_clazz = env->FindClass("java/lang/Class"); - assert(class_clazz != NULL); + assert(class_clazz != nullptr); assert(!env->ExceptionCheck()); jmethodID getFieldMetodId = env->GetMethodID(class_clazz, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); - assert(getFieldMetodId != NULL); + assert(getFieldMetodId != nullptr); assert(!env->ExceptionCheck()); jstring field_name = env->NewStringUTF("testReflectFieldGetFromAttachedNativeThreadField"); - assert(field_name != NULL); + assert(field_name != nullptr); assert(!env->ExceptionCheck()); jobject field = env->CallObjectMethod(clazz, getFieldMetodId, field_name); - assert(field != NULL); + assert(field != nullptr); assert(!env->ExceptionCheck()); jclass field_clazz = env->FindClass("java/lang/reflect/Field"); - assert(field_clazz != NULL); + assert(field_clazz != nullptr); assert(!env->ExceptionCheck()); jmethodID getBooleanMetodId = env->GetMethodID(field_clazz, "getBoolean", "(Ljava/lang/Object;)Z"); - assert(getBooleanMetodId != NULL); + assert(getBooleanMetodId != nullptr); assert(!env->ExceptionCheck()); jboolean value = env->CallBooleanMethod(field, getBooleanMetodId, /* ignored */ clazz); assert(value == false); assert(!env->ExceptionCheck()); - - int detach_result = jvm->DetachCurrentThread(); - assert(detach_result == 0); - return NULL; } // http://b/15539150 extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativeThreadNative( JNIEnv*, jclass) { - pthread_t pthread; - int pthread_create_result = pthread_create(&pthread, - NULL, - testReflectFieldGetFromAttachedNativeThread, - NULL); - assert(pthread_create_result == 0); - int pthread_join_result = pthread_join(pthread, NULL); - assert(pthread_join_result == 0); + PthreadHelper(&testReflectFieldGetFromAttachedNativeThread); } // http://b/11243757 extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env, - jclass) { + jclass) { jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass"); - assert(super_class != NULL); + assert(super_class != nullptr); jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V"); - assert(execute != NULL); + assert(execute != nullptr); jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass"); - assert(sub_class != NULL); + assert(sub_class != nullptr); env->CallStaticVoidMethod(sub_class, execute); } extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) { jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract"); - assert(abstract_class != NULL); + assert(abstract_class != nullptr); jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z"); - assert(miranda_method != NULL); + assert(miranda_method != nullptr); return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE); } @@ -191,7 +162,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) { std::vector<uint8_t> buffer(1); jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0); - assert(byte_buffer != NULL); + assert(byte_buffer != nullptr); assert(!env->ExceptionCheck()); assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]); @@ -202,8 +173,8 @@ constexpr size_t kByteReturnSize = 7; jbyte byte_returns[kByteReturnSize] = { 0, 1, 2, 127, -1, -2, -128 }; extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2, - jbyte b3, jbyte b4, jbyte b5, jbyte b6, - jbyte b7, jbyte b8, jbyte b9, jbyte b10) { + jbyte b3, jbyte b4, jbyte b5, jbyte b6, + jbyte b7, jbyte b8, jbyte b9, jbyte b10) { // We use b1 to drive the output. assert(b2 == 2); assert(b3 == -3); @@ -227,8 +198,8 @@ jshort short_returns[kShortReturnSize] = { 0, 1, 2, 127, 32767, -1, -2, -128, // The weird static_cast is because short int is only guaranteed down to -32767, not Java's -32768. extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2, - jshort s3, jshort s4, jshort s5, jshort s6, - jshort s7, jshort s8, jshort s9, jshort s10) { + jshort s3, jshort s4, jshort s5, jshort s6, + jshort s7, jshort s8, jshort s9, jshort s10) { // We use s1 to drive the output. assert(s2 == 2); assert(s3 == -3); @@ -247,9 +218,9 @@ extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshor } extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1, - jboolean b2, jboolean b3, jboolean b4, - jboolean b5, jboolean b6, jboolean b7, - jboolean b8, jboolean b9, jboolean b10) { + jboolean b2, jboolean b3, jboolean b4, + jboolean b5, jboolean b6, jboolean b7, + jboolean b8, jboolean b9, jboolean b10) { // We use b1 to drive the output. assert(b2 == JNI_TRUE); assert(b3 == JNI_FALSE); @@ -269,8 +240,8 @@ constexpr size_t kCharReturnSize = 8; jchar char_returns[kCharReturnSize] = { 0, 1, 2, 127, 255, 256, 15000, 34000 }; extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2, - jchar c3, jchar c4, jchar c5, jchar c6, - jchar c7, jchar c8, jchar c9, jchar c10) { + jchar c3, jchar c4, jchar c5, jchar c6, jchar c7, + jchar c8, jchar c9, jchar c10) { // We use c1 to drive the output. assert(c2 == 'a'); assert(c3 == 'b'); @@ -291,3 +262,94 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_nativeIsAssignableFrom(JNIEnv* e jclass from, jclass to) { return env->IsAssignableFrom(from, to); } + +static void testShallowGetCallingClassLoader(JNIEnv* env) { + // Test direct call. + { + jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack"); + assert(vmstack_clazz != nullptr); + assert(!env->ExceptionCheck()); + + jmethodID getCallingClassLoaderMethodId = env->GetStaticMethodID(vmstack_clazz, + "getCallingClassLoader", + "()Ljava/lang/ClassLoader;"); + assert(getCallingClassLoaderMethodId != nullptr); + assert(!env->ExceptionCheck()); + + jobject class_loader = env->CallStaticObjectMethod(vmstack_clazz, + getCallingClassLoaderMethodId); + assert(class_loader == nullptr); + assert(!env->ExceptionCheck()); + } + + // Test one-level call. Use System.loadLibrary(). + { + jclass system_clazz = env->FindClass("java/lang/System"); + assert(system_clazz != nullptr); + assert(!env->ExceptionCheck()); + + jmethodID loadLibraryMethodId = env->GetStaticMethodID(system_clazz, "loadLibrary", + "(Ljava/lang/String;)V"); + assert(loadLibraryMethodId != nullptr); + assert(!env->ExceptionCheck()); + + // Create a string object. + jobject library_string = env->NewStringUTF("arttest"); + assert(library_string != nullptr); + assert(!env->ExceptionCheck()); + + env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string); + if (env->ExceptionCheck()) { + // At most we expect UnsatisfiedLinkError. + jthrowable thrown = env->ExceptionOccurred(); + env->ExceptionClear(); + + jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError"); + jclass thrown_class = env->GetObjectClass(thrown); + assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class)); + } + } +} + +// http://b/16867274 +extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv* env, + jclass) { + PthreadHelper(&testShallowGetCallingClassLoader); +} + +static void testShallowGetStackClass2(JNIEnv* env) { + jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack"); + assert(vmstack_clazz != nullptr); + assert(!env->ExceptionCheck()); + + // Test direct call. + { + jmethodID getStackClass2MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass2", + "()Ljava/lang/Class;"); + assert(getStackClass2MethodId != nullptr); + assert(!env->ExceptionCheck()); + + jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass2MethodId); + assert(caller_class == nullptr); + assert(!env->ExceptionCheck()); + } + + // Test one-level call. Use VMStack.getStackClass1(). + { + jmethodID getStackClass1MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass1", + "()Ljava/lang/Class;"); + assert(getStackClass1MethodId != nullptr); + assert(!env->ExceptionCheck()); + + jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass1MethodId); + assert(caller_class == nullptr); + assert(!env->ExceptionCheck()); + } + + // For better testing we would need to compile against libcore and have a two-deep stack + // ourselves. +} + +extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) { + PthreadHelper(&testShallowGetStackClass2); +} diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index ae133becbc..5884bc0e2b 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -30,6 +30,8 @@ public class Main { testBooleanMethod(); testCharMethod(); testIsAssignableFromOnPrimitiveTypes(); + testShallowGetCallingClassLoader(); + testShallowGetStackClass2(); } private static native void testFindClassOnAttachedNativeThread(); @@ -167,4 +169,16 @@ public class Main { } native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to); + + static void testShallowGetCallingClassLoader() { + nativeTestShallowGetCallingClassLoader(); + } + + native static void nativeTestShallowGetCallingClassLoader(); + + static void testShallowGetStackClass2() { + nativeTestShallowGetStackClass2(); + } + + native static void nativeTestShallowGetStackClass2(); } diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run index 5ffeecdbb1..2df670575d 100755 --- a/test/116-nodex2oat/run +++ b/test/116-nodex2oat/run @@ -17,6 +17,9 @@ # Remove prebuild from the flags, this test is for testing not having oat files. flags="${@/--prebuild/}" +# Use the non-prebuild script. +RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}" + # Make sure we can run without an oat file, echo "Run -Xnodex2oat" ${RUN} ${flags} --runtime-option -Xnodex2oat diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java index b5e0204ab8..fab153b624 100644 --- a/test/MyClassNatives/MyClassNatives.java +++ b/test/MyClassNatives/MyClassNatives.java @@ -80,16 +80,22 @@ class MyClassNatives { Object o248, Object o249, Object o250, Object o251, Object o252, Object o253); native void withoutImplementation(); - + native static void stackArgsIntsFirst(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9, float f10); - + native static void stackArgsFloatsFirst(float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9, float f10, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10); - + native static void stackArgsMixed(int i1, float f1, int i2, float f2, int i3, float f3, int i4, float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9, float f9, int i10, float f10); + + static native double logD(double d); + static native float logF(float f); + static native boolean returnTrue(); + static native boolean returnFalse(); + static native int returnInt(); } diff --git a/test/run-test b/test/run-test index 496f7d1b53..b8e40d9328 100755 --- a/test/run-test +++ b/test/run-test @@ -206,6 +206,14 @@ while true; do break fi done + +# tmp_dir may be relative, resolve. +# +# Cannot use realpath, as it does not exist on Mac. +# Cannot us a simple "cd", as the path might not be created yet. +# Use -m option of readlink: canonicalizes, but allows non-existing components. +tmp_dir="`cd $oldwd ; readlink -m $tmp_dir`" + mkdir -p $tmp_dir if [ "$basic_verify" = "true" ]; then |
