diff options
36 files changed, 461 insertions, 222 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 9f0aaa4e10..0caf1b14a6 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -341,10 +341,8 @@ std::vector<uint8_t> DexToDexCompiler::CompilationState::Compile() { DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices()); } - if (GetQuickenedInfo().empty()) { - // No need to create a CompiledMethod if there are no quickened opcodes. - return std::vector<uint8_t>(); - } + // Even if there are no indicies, generate an empty quicken info so that we know the method was + // quickened. std::vector<uint8_t> quicken_data; if (kIsDebugBuild) { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 6d3658f789..b16c56a3ad 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -997,7 +997,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, class_it.GetMethodCodeItem())) { - ASSERT_FALSE(inst->IsQuickened()) << output_; + ASSERT_FALSE(inst->IsQuickened()) << inst->Opcode() << " " << output_; } } } @@ -1871,4 +1871,91 @@ TEST_F(Dex2oatTest, DontExtract) { EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_; } +// Test that dex files with quickened opcodes aren't dequickened. +TEST_F(Dex2oatTest, QuickenedInput) { + std::string error_msg; + ScratchFile temp_dex; + MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [] (DexFile* dex) { + bool mutated_successfully = false; + // Change the dex instructions to make an opcode that spans past the end of the code item. + for (size_t i = 0; i < dex->NumClassDefs(); ++i) { + const DexFile::ClassDef& def = dex->GetClassDef(i); + const uint8_t* data = dex->GetClassData(def); + if (data == nullptr) { + continue; + } + ClassDataItemIterator it(*dex, data); + it.SkipAllFields(); + while (it.HasNextMethod()) { + DexFile::CodeItem* item = const_cast<DexFile::CodeItem*>(it.GetMethodCodeItem()); + if (item != nullptr) { + CodeItemInstructionAccessor instructions(*dex, item); + // Make a quickened instruction that doesn't run past the end of the code item. + if (instructions.InsnsSizeInCodeUnits() > 2) { + const_cast<Instruction&>(instructions.InstructionAt(0)).SetOpcode( + Instruction::IGET_BYTE_QUICK); + mutated_successfully = true; + } + } + it.Next(); + } + } + CHECK(mutated_successfully) + << "Failed to find candidate code item with only one code unit in last instruction."; + }); + + std::string dex_location = temp_dex.GetFilename(); + std::string odex_location = GetOdexDir() + "/quickened.odex"; + std::string vdex_location = GetOdexDir() + "/quickened.vdex"; + std::unique_ptr<File> vdex_output(OS::CreateEmptyFile(vdex_location.c_str())); + // Quicken the dex + { + std::string input_vdex = "--input-vdex-fd=-1"; + std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd()); + GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + // Disable cdex since we want to compare against the original dex file + // after unquickening. + { input_vdex, output_vdex, kDisableCompactDex }, + /* expect_success */ true, + /* use_fd */ true); + } + // Unquicken by running the verify compiler filter on the vdex file and verify it matches. + std::string odex_location2 = GetOdexDir() + "/unquickened.odex"; + std::string vdex_location2 = GetOdexDir() + "/unquickened.vdex"; + std::unique_ptr<File> vdex_unquickened(OS::CreateEmptyFile(vdex_location2.c_str())); + { + std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd()); + std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd()); + GenerateOdexForTest(dex_location, + odex_location2, + CompilerFilter::kVerify, + // Disable cdex to avoid needing to write out the shared section. + { input_vdex, output_vdex, kDisableCompactDex }, + /* expect_success */ true, + /* use_fd */ true); + } + ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file"; + ASSERT_TRUE(success_); + { + // Check that hte vdex has one dex and compare it to the original one. + std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location2.c_str(), + /*writable*/ false, + /*low_4gb*/ false, + /*unquicken*/ false, + &error_msg)); + std::vector<std::unique_ptr<const DexFile>> dex_files; + bool result = vdex->OpenAllDexFiles(&dex_files, &error_msg); + ASSERT_TRUE(result) << error_msg; + ASSERT_EQ(dex_files.size(), 1u) << error_msg; + ScratchFile temp; + ASSERT_TRUE(temp.GetFile()->WriteFully(dex_files[0]->Begin(), dex_files[0]->Size())); + ASSERT_EQ(temp.GetFile()->Flush(), 0) << "Could not flush extracted dex"; + EXPECT_EQ(temp.GetFile()->Compare(temp_dex.GetFile()), 0); + } + ASSERT_EQ(vdex_output->FlushCloseOrErase(), 0) << "Could not flush and close"; + ASSERT_EQ(vdex_unquickened->FlushCloseOrErase(), 0) << "Could not flush and close"; +} + } // namespace art diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index ee872db558..ff0729f6e3 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -2631,45 +2631,37 @@ class OatWriter::WriteQuickeningInfoMethodVisitor { out_(out) {} bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { - std::vector<uint8_t> empty_quicken_info; - { - // Since we need to be able to access by dex method index, put a one byte empty quicken info - // for any method that isn't quickened. - QuickenInfoTable::Builder empty_info(&empty_quicken_info, /*num_elements*/ 0u); - CHECK(!empty_quicken_info.empty()); - } + // Map of offsets for quicken info related to method indices. + SafeMap<const uint8_t*, uint32_t> offset_map; + // Use method index order to minimize the encoded size of the offset table. for (const DexFile* dex_file : dex_files) { std::vector<uint32_t>* const offsets = &quicken_info_offset_indices_.Put(dex_file, std::vector<uint32_t>())->second; - - // Every method needs an index in the table. for (uint32_t method_idx = 0; method_idx < dex_file->NumMethodIds(); ++method_idx) { - ArrayRef<const uint8_t> map(empty_quicken_info); - - // Use the existing quicken info if it exists. + uint32_t offset = 0u; MethodReference method_ref(dex_file, method_idx); CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(method_ref); if (compiled_method != nullptr && HasQuickeningInfo(compiled_method)) { - map = compiled_method->GetVmapTable(); - } - - // The current approach prevents deduplication of quicken infos since each method index - // has one unique quicken info. Deduplication does not provide much savings for dex indices - // since they are rarely duplicated. - const uint32_t length = map.size() * sizeof(map.front()); - - // Record each index if required. written_bytes_ is the offset from the start of the - // quicken info data. - if (QuickenInfoOffsetTableAccessor::IsCoveredIndex(method_idx)) { - offsets->push_back(written_bytes_); - } - - if (!out_->WriteFully(map.data(), length)) { - PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod() - << " to " << out_->GetLocation(); - return false; + ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); + + // Record each index if required. written_bytes_ is the offset from the start of the + // quicken info data. + // May be already inserted for deduplicate items. + // Add offset of one to make sure 0 represents unused. + auto pair = offset_map.emplace(map.data(), written_bytes_ + 1); + offset = pair.first->second; + // Write out the map if it's not already written. + if (pair.second) { + const uint32_t length = map.size() * sizeof(map.front()); + if (!out_->WriteFully(map.data(), length)) { + PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod() + << " to " << out_->GetLocation(); + return false; + } + written_bytes_ += length; + } } - written_bytes_ += length; + offsets->push_back(offset); } } return true; @@ -2683,12 +2675,10 @@ class OatWriter::WriteQuickeningInfoMethodVisitor { return quicken_info_offset_indices_; } - private: OatWriter* const writer_; OutputStream* const out_; size_t written_bytes_ = 0u; - // Map of offsets for quicken info related to method indices. SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_; }; @@ -2712,14 +2702,11 @@ class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor { const std::vector<uint32_t>* const offsets = &it->second; const uint32_t current_offset = start_offset_ + written_bytes_; - CHECK_ALIGNED_PARAM(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + CHECK_ALIGNED_PARAM(current_offset, CompactOffsetTable::kAlignment); // Generate and write the data. std::vector<uint8_t> table_data; - QuickenInfoOffsetTableAccessor::Builder builder(&table_data); - for (uint32_t offset : *offsets) { - builder.AddOffset(offset); - } + CompactOffsetTable::Build(*offsets, &table_data); // Store the offset since we need to put those after the dex file. Table offsets are relative // to the start of the quicken info section. @@ -2780,7 +2767,7 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes(); current_offset = current_offset + quicken_info_offset; uint32_t before_offset = current_offset; - current_offset = RoundUp(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + current_offset = RoundUp(current_offset, CompactOffsetTable::kAlignment); const size_t extra_bytes = current_offset - before_offset; quicken_info_offset += extra_bytes; actual_offset = vdex_out->Seek(current_offset, kSeekSet); diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index d9a93ddf36..981f9010ee 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -321,31 +321,6 @@ class DexLayoutTest : public CommonRuntimeTest { return true; } - template <typename Mutator> - bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { - std::vector<std::unique_ptr<const DexFile>> dex_files; - std::string error_msg; - const ArtDexFileLoader dex_file_loader; - CHECK(dex_file_loader.Open(input_jar.c_str(), - input_jar.c_str(), - /*verify*/ true, - /*verify_checksum*/ true, - &error_msg, - &dex_files)) << error_msg; - EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; - for (const std::unique_ptr<const DexFile>& dex : dex_files) { - CHECK(dex->EnableWrite()) << "Failed to enable write"; - mutator(const_cast<DexFile*>(dex.get())); - if (!output_dex->WriteFully(dex->Begin(), dex->Size())) { - return false; - } - } - if (output_dex->Flush() != 0) { - PLOG(FATAL) << "Could not flush the output file."; - } - return true; - } - // Create a profile with some subset of methods and classes. void CreateProfile(const std::string& input_dex, const std::string& out_profile, diff --git a/libdexfile/dex/compact_offset_table.cc b/libdexfile/dex/compact_offset_table.cc index 8cee0e376a..60a7b61d11 100644 --- a/libdexfile/dex/compact_offset_table.cc +++ b/libdexfile/dex/compact_offset_table.cc @@ -30,6 +30,11 @@ CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin, minimum_offset_(minimum_offset), data_begin_(data_begin) {} +CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin) + : Accessor(data_begin + 2 * sizeof(uint32_t), + reinterpret_cast<const uint32_t*>(data_begin)[0], + reinterpret_cast<const uint32_t*>(data_begin)[1]) {} + uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const { const uint32_t offset = table_[index / kElementsPerIndex]; const size_t bit_index = index % kElementsPerIndex; @@ -56,6 +61,17 @@ uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const { } void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets, + std::vector<uint8_t>* out_data) { + static constexpr size_t kNumOffsets = 2; + uint32_t out_offsets[kNumOffsets] = {}; + CompactOffsetTable::Build(offsets, out_data, &out_offsets[0], &out_offsets[1]); + // Write the offsets at the start of the debug info. + out_data->insert(out_data->begin(), + reinterpret_cast<const uint8_t*>(&out_offsets[0]), + reinterpret_cast<const uint8_t*>(&out_offsets[kNumOffsets])); +} + +void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data, uint32_t* out_min_offset, uint32_t* out_table_offset) { diff --git a/libdexfile/dex/compact_offset_table.h b/libdexfile/dex/compact_offset_table.h index 17e6bb41ed..ec759e200d 100644 --- a/libdexfile/dex/compact_offset_table.h +++ b/libdexfile/dex/compact_offset_table.h @@ -37,9 +37,10 @@ class CompactOffsetTable { class Accessor { public: - Accessor(const uint8_t* data_begin, - uint32_t minimum_offset, - uint32_t table_offset); + // Read the minimum and table offsets from the data pointer. + explicit Accessor(const uint8_t* data_begin); + + Accessor(const uint8_t* data_begin, uint32_t minimum_offset, uint32_t table_offset); // Return the offset for the index. uint32_t GetOffset(uint32_t index) const; @@ -50,6 +51,9 @@ class CompactOffsetTable { const uint8_t* const data_begin_; }; + // Version that also serializes the min offset and table offset. + static void Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data); + // Returned offsets are all relative to out_min_offset. static void Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data, diff --git a/libdexfile/dex/compact_offset_table_test.cc b/libdexfile/dex/compact_offset_table_test.cc index 7eb01569f5..724978dc9e 100644 --- a/libdexfile/dex/compact_offset_table_test.cc +++ b/libdexfile/dex/compact_offset_table_test.cc @@ -74,6 +74,16 @@ TEST(CompactOffsetTableTest, TestBuildAndAccess) { << " table size " << data.size() << " sorted table size " << sorted_data.size(); } + + // Test constructor and accessor that serialize/read offsets. + { + std::vector<uint8_t> data2; + CompactOffsetTable::Build(offsets, /*out*/ &data2); + CompactOffsetTable::Accessor accessor2(&data2[0]); + for (size_t i = 0; i < offsets.size(); ++i) { + EXPECT_EQ(offsets[i], accessor2.GetOffset(i)); + } + } } } // namespace art diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 37f239da23..f9da178de8 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -459,12 +459,13 @@ int FdFile::Compare(FdFile* other) { static const size_t kBufferSize = 4096; std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]); std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]); + size_t offset = 0; while (length > 0) { size_t len = std::min(kBufferSize, static_cast<size_t>(length)); - if (!ReadFully(&buffer1[0], len)) { + if (!PreadFully(&buffer1[0], len, offset)) { return -1; } - if (!other->ReadFully(&buffer2[0], len)) { + if (!other->PreadFully(&buffer2[0], len, offset)) { return 1; } int result = memcmp(&buffer1[0], &buffer2[0], len); @@ -472,6 +473,7 @@ int FdFile::Compare(FdFile* other) { return result; } length -= len; + offset += len; } return 0; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9c0cceeb4b..ad2e7a77dd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7958,17 +7958,18 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, // In case of jmvti, the dex file gets verified before being registered, so first // check if it's registered before checking class tables. const DexFile& dex_file = *dex_cache->GetDexFile(); - CHECK(!IsDexFileRegistered(Thread::Current(), dex_file) || - FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader)) + DCHECK(!IsDexFileRegistered(Thread::Current(), dex_file) || + FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader)) << "DexFile referrer: " << dex_file.GetLocation() << " ClassLoader: " << DescribeLoaders(class_loader, ""); // Be a good citizen and update the dex cache to speed subsequent calls. dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_); - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr) - << "Method: " << resolved->PrettyMethod() << ", " - << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), " - << "DexFile referrer: " << dex_file.GetLocation(); + // Disable the following invariant check as the verifier breaks it. b/73760543 + // const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + // DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr) + // << "Method: " << resolved->PrettyMethod() << ", " + // << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), " + // << "DexFile referrer: " << dex_file.GetLocation(); } return resolved; } @@ -8000,13 +8001,9 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); if (UNLIKELY(klass == nullptr)) { - const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_); - LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: " - << resolved->PrettyMethod() << ";" << resolved - << "/0x" << std::hex << resolved->GetAccessFlags() - << " ReferencedClass: " << descriptor - << " DexFile referrer: " << dex_file.GetLocation() - << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor); + // We normaly should not end up here. However the verifier currently doesn't guarantee + // the invariant of having the klass in the class table. b/73760543 + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); } } else { // The method was not in the DexCache, resolve the declaring class. diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index b2b4d545cb..85b0dbb43c 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -26,6 +26,8 @@ #include "arch/instruction_set.h" #include "base/mutex.h" +#include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" #include "dex/compact_dex_level.h" #include "globals.h" // TODO: Add inl file and avoid including inl. @@ -119,6 +121,32 @@ class CommonRuntimeTestImpl { // A helper to set up a small heap (4M) to make FillHeap faster. static void SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options); + template <typename Mutator> + bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + CHECK(dex_file_loader.Open(input_jar.c_str(), + input_jar.c_str(), + /*verify*/ true, + /*verify_checksum*/ true, + &error_msg, + &dex_files)) << error_msg; + EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; + const std::unique_ptr<const DexFile>& dex = dex_files[0]; + CHECK(dex->EnableWrite()) << "Failed to enable write"; + DexFile* dex_file = const_cast<DexFile*>(dex.get()); + mutator(dex_file); + const_cast<DexFile::Header&>(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum(); + if (!output_dex->WriteFully(dex->Begin(), dex->Size())) { + return false; + } + if (output_dex->Flush() != 0) { + PLOG(FATAL) << "Could not flush the output file."; + } + return true; + } + protected: // Allow subclases such as CommonCompilerTest to add extra options. virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {} diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 4b964f648b..2db8815fdd 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -371,7 +371,7 @@ size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) { return read_count; } // TODO: Disable this for app images if app images have intern tables. - static constexpr bool kCheckDuplicates = true; + static constexpr bool kCheckDuplicates = kIsDebugBuild; if (kCheckDuplicates) { for (GcRoot<mirror::String>& string : set) { CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8(); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 1baa613bb5..6d99ad0046 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -718,7 +718,9 @@ void Jit::MethodEntered(Thread* thread, ArtMethod* method) { Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) { // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, method, /* retry_allocation */ true); + ProfilingInfo::Create(thread, + method->GetInterfaceMethodIfProxy(kRuntimePointerSize), + /* retry_allocation */ true); JitCompileTask compile_task(method, JitCompileTask::kCompile); compile_task.Run(thread); return; diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 6da34bcc60..fc9426650e 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -442,7 +442,7 @@ enum class VMDebugRuntimeStatId { kNumRuntimeStats, }; -static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { +static jstring VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { gc::Heap* heap = Runtime::Current()->GetHeap(); switch (static_cast<VMDebugRuntimeStatId>(statId)) { case VMDebugRuntimeStatId::kArtGcGcCount: { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 38a4a3bfb0..25d50376de 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -533,7 +533,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal( } static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, - jobject name, jobjectArray args) { + jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index 9295ff7071..b5aea7ca7c 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -37,7 +37,7 @@ static jchar String_charAt(JNIEnv* env, jobject java_this, jint index) { return soa.Decode<mirror::String>(java_this)->CharAt(index); } -static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) { +static jint String_compareTo(JNIEnv* env, jobject java_this, jstring java_rhs) { ScopedFastNativeObjectAccess soa(env); if (UNLIKELY(java_rhs == nullptr)) { ThrowNullPointerException("rhs == null"); @@ -48,7 +48,7 @@ static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) { } } -static jstring String_concat(JNIEnv* env, jobject java_this, jobject java_string_arg) { +static jstring String_concat(JNIEnv* env, jobject java_this, jstring java_string_arg) { ScopedFastNativeObjectAccess soa(env); if (UNLIKELY(java_string_arg == nullptr)) { ThrowNullPointerException("string arg == null"); diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 553cbeaabc..390f026588 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -182,50 +182,55 @@ inline void System_arraycopyTUnchecked(JNIEnv* env, jobject javaSrc, jint srcPos AsPrimitiveArray<T>(dstArray)->Memmove(dstPos, AsPrimitiveArray<T>(srcArray), srcPos, count); } -static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jcharArray javaSrc, jint srcPos, + jcharArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::CharArray, Primitive::kPrimChar>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyByteUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyByteUnchecked(JNIEnv* env, jclass, jbyteArray javaSrc, jint srcPos, + jbyteArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::ByteArray, Primitive::kPrimByte>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyShortUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyShortUnchecked(JNIEnv* env, jclass, jshortArray javaSrc, jint srcPos, + jshortArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::ShortArray, Primitive::kPrimShort>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyIntUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyIntUnchecked(JNIEnv* env, jclass, jintArray javaSrc, jint srcPos, + jintArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::IntArray, Primitive::kPrimInt>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyLongUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyLongUnchecked(JNIEnv* env, jclass, jlongArray javaSrc, jint srcPos, + jlongArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::LongArray, Primitive::kPrimLong>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyFloatUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyFloatUnchecked(JNIEnv* env, jclass, jfloatArray javaSrc, jint srcPos, + jfloatArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::FloatArray, Primitive::kPrimFloat>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyDoubleUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyDoubleUnchecked(JNIEnv* env, jclass, jdoubleArray javaSrc, jint srcPos, + jdoubleArray javaDst, jint dstPos, jint count) { System_arraycopyTUnchecked<mirror::DoubleArray, Primitive::kPrimDouble>(env, javaSrc, srcPos, javaDst, dstPos, count); } -static void System_arraycopyBooleanUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, - jobject javaDst, jint dstPos, jint count) { +static void System_arraycopyBooleanUnchecked(JNIEnv* env, + jclass, + jbooleanArray javaSrc, + jint srcPos, + jbooleanArray javaDst, + jint dstPos, + jint count) { System_arraycopyTUnchecked<mirror::BooleanArray, Primitive::kPrimBoolean>(env, javaSrc, srcPos, javaDst, dstPos, count); } diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index 12d400895c..d28f74158e 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -31,7 +31,7 @@ namespace art { static jobject Array_createMultiArray( - JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) { + JNIEnv* env, jclass, jclass javaElementClass, jintArray javaDimArray) { ScopedFastNativeObjectAccess soa(env); DCHECK(javaElementClass != nullptr); StackHandleScope<2> hs(soa.Self()); diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index e37c14b41c..a5e70affa5 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -249,14 +249,14 @@ static jint Executable_compareMethodParametersInternal(JNIEnv* env, return 0; } -static jobject Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) { +static jstring Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - return soa.AddLocalReference<jobject>(method->GetNameAsString(soa.Self())); + return soa.AddLocalReference<jstring>(method->GetNameAsString(soa.Self())); } -static jobject Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) { +static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); @@ -266,7 +266,7 @@ static jobject Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaM return nullptr; } - return soa.AddLocalReference<jobject>(return_type); + return soa.AddLocalReference<jclass>(return_type); } // TODO: Move this to mirror::Class ? Other mirror types that commonly appear diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index f990c0421d..688ae1977e 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -460,10 +460,10 @@ static jlong Field_getArtField(JNIEnv* env, jobject javaField) { return reinterpret_cast<jlong>(field); } -static jobject Field_getNameInternal(JNIEnv* env, jobject javaField) { +static jstring Field_getNameInternal(JNIEnv* env, jobject javaField) { ScopedFastNativeObjectAccess soa(env); ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField(); - return soa.AddLocalReference<jobject>( + return soa.AddLocalReference<jstring>( field->GetStringName(soa.Self(), true /* resolve */)); } diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index b604dc0fa1..4355c06acd 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -82,7 +82,7 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { } static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, - jobject javaArgs) { + jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); } diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index 836637f4f0..fbee7b31a3 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -162,7 +162,7 @@ static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) { return result; } -static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) { +static jboolean DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) { ScopedFastNativeObjectAccess soa(env); return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when)); } diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index b2bdeed5cb..1af65a371b 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -198,14 +198,14 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong obj->SetFieldObject<false>(MemberOffset(offset), newValue); } -static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) { +static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jclass component_class) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class); Primitive::Type primitive_type = component->GetPrimitiveType(); return mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value(); } -static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jobject component_class) { +static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jclass component_class) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class); Primitive::Type primitive_type = component->GetPrimitiveType(); diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h index 32f70054ba..f20aa0cef5 100644 --- a/runtime/quicken_info.h +++ b/runtime/quicken_info.h @@ -18,66 +18,12 @@ #define ART_RUNTIME_QUICKEN_INFO_H_ #include "base/array_ref.h" +#include "dex/compact_offset_table.h" #include "dex/dex_instruction.h" #include "leb128.h" namespace art { -// Table for getting the offset of quicken info. Doesn't have one slot for each index, so a -// combination of iteration and indexing is required to get the quicken info for a given dex method -// index. -class QuickenInfoOffsetTableAccessor { - public: - using TableType = uint32_t; - static constexpr uint32_t kElementsPerIndex = 16; - - class Builder { - public: - explicit Builder(std::vector<uint8_t>* out_data) : out_data_(out_data) {} - - void AddOffset(uint32_t index) { - out_data_->insert(out_data_->end(), - reinterpret_cast<const uint8_t*>(&index), - reinterpret_cast<const uint8_t*>(&index + 1)); - } - - private: - std::vector<uint8_t>* const out_data_; - }; - - // The table only covers every kElementsPerIndex indices. - static bool IsCoveredIndex(uint32_t index) { - return index % kElementsPerIndex == 0; - } - - QuickenInfoOffsetTableAccessor(const ArrayRef<const uint8_t>& data, uint32_t max_index) - : table_(ArrayRef<const TableType>::Cast(data).SubArray( - 0, - RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex)) {} - - size_t SizeInBytes() const { - return NumIndices() * sizeof(table_[0]); - } - - uint32_t NumIndices() const { - return table_.size(); - } - - // Returns the offset for the index at or before the desired index. If the offset is for an index - // before the desired one, remainder is how many elements to traverse to reach the desired index. - TableType ElementOffset(uint32_t index, uint32_t* remainder) const { - *remainder = index % kElementsPerIndex; - return table_[index / kElementsPerIndex]; - } - - static uint32_t Alignment() { - return alignof(TableType); - } - - private: - const ArrayRef<const TableType> table_; -}; - // QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that // is possibly dequickenable. class QuickenInfoTable { diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 443c35f979..34b9fcc8a4 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -222,41 +222,27 @@ uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1]; } -QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( +CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable( const uint8_t* source_dex_begin, - uint32_t num_method_ids, const ArrayRef<const uint8_t>& quickening_info) const { // The offset a is in preheader right before the dex file. const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin); - return QuickenInfoOffsetTableAccessor(quickening_info.SubArray(offset), num_method_ids); + return CompactOffsetTable::Accessor(quickening_info.SubArray(offset).data()); } -QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( +CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable( const DexFile& dex_file, const ArrayRef<const uint8_t>& quickening_info) const { - return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info); + return GetQuickenInfoOffsetTable(dex_file.Begin(), quickening_info); } static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info, uint32_t quickening_offset) { - ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset); + // Subtract offset of one since 0 represents unused and cannot be in the table. + ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset - 1); return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining)); } -static uint32_t GetQuickeningInfoOffset(const QuickenInfoOffsetTableAccessor& table, - uint32_t dex_method_index, - const ArrayRef<const uint8_t>& quickening_info) { - DCHECK(!quickening_info.empty()); - uint32_t remainder; - uint32_t offset = table.ElementOffset(dex_method_index, &remainder); - // Decode the sizes for the remainder offsets (not covered by the table). - while (remainder != 0) { - offset += GetQuickeningInfoAt(quickening_info, offset).size(); - --remainder; - } - return offset; -} - void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, const DexFile& source_dex_file, bool decompile_return_instruction) const { @@ -267,13 +253,15 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, const uint8_t* source_dex_begin, bool decompile_return_instruction) const { ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - if (quickening_info.size() == 0 && !decompile_return_instruction) { - // Bail early if there is no quickening info and no need to decompile - // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. + if (quickening_info.empty()) { + // Bail early if there is no quickening info and no need to decompile. This means there is also + // no RETURN_VOID to decompile since the empty table takes a non zero amount of space. return; } // Make sure to not unquicken the same code item multiple times. std::unordered_set<const DexFile::CodeItem*> unquickened_code_item; + CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, + quickening_info)); for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); const uint8_t* class_data = target_dex_file.GetClassData(class_def); @@ -284,21 +272,16 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, if (class_it.IsAtMethod()) { const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - ArrayRef<const uint8_t> quicken_data; - if (!quickening_info.empty()) { - const uint32_t quickening_offset = GetQuickeningInfoOffset( - GetQuickenInfoOffsetTable(source_dex_begin, - target_dex_file.NumMethodIds(), - quickening_info), - class_it.GetMemberIndex(), - quickening_info); - quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset); + const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef<const uint8_t> quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); } - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); } } DexFile::UnHideAccessFlags(class_it); @@ -313,10 +296,11 @@ ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, if (quickening_info.empty()) { return ArrayRef<const uint8_t>(); } - const uint32_t quickening_offset = GetQuickeningInfoOffset( - GetQuickenInfoOffsetTable(dex_file, quickening_info), - dex_method_idx, - quickening_info); + const uint32_t quickening_offset = + GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx); + if (quickening_offset == 0u) { + return ArrayRef<const uint8_t>(); + } return GetQuickeningInfoAt(quickening_info, quickening_offset); } diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 0f347952c9..d27f431cdc 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -22,6 +22,7 @@ #include "base/array_ref.h" #include "base/macros.h" +#include "dex/compact_offset_table.h" #include "mem_map.h" #include "os.h" #include "quicken_info.h" @@ -87,8 +88,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Fix separate section for compact dex data. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '7', '\0' }; + // Last update: Change quickening info table format. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '8', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -238,13 +239,12 @@ class VdexFile { const uint8_t* source_dex_begin, bool decompile_return_instruction) const; - QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + CompactOffsetTable::Accessor GetQuickenInfoOffsetTable( const DexFile& dex_file, const ArrayRef<const uint8_t>& quickening_info) const; - QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + CompactOffsetTable::Accessor GetQuickenInfoOffsetTable( const uint8_t* source_dex_begin, - uint32_t num_method_ids, const ArrayRef<const uint8_t>& quickening_info) const; bool ContainsDexFile(const DexFile& dex_file) const; diff --git a/test/676-proxy-jit-at-first-use/expected.txt b/test/676-proxy-jit-at-first-use/expected.txt new file mode 100644 index 0000000000..6915b2f261 --- /dev/null +++ b/test/676-proxy-jit-at-first-use/expected.txt @@ -0,0 +1 @@ +Method: public abstract void Interface.foo() diff --git a/test/676-proxy-jit-at-first-use/info.txt b/test/676-proxy-jit-at-first-use/info.txt new file mode 100644 index 0000000000..90b683b46d --- /dev/null +++ b/test/676-proxy-jit-at-first-use/info.txt @@ -0,0 +1 @@ +Regression test for "jit at first use" (-Xjitthreshold:0) crash for proxy methods. b/73718713 diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run new file mode 100644 index 0000000000..16c9f7607a --- /dev/null +++ b/test/676-proxy-jit-at-first-use/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. + +# Enable "jit at first use" (-Xjitthreshold:0). +# Ensure this test is not subject to unexpected code collection. +${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M diff --git a/test/676-proxy-jit-at-first-use/src/Main.java b/test/676-proxy-jit-at-first-use/src/Main.java new file mode 100644 index 0000000000..4ed773f666 --- /dev/null +++ b/test/676-proxy-jit-at-first-use/src/Main.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 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.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public class Main { + public static void main(String[] args) throws Exception { + Interface i = (Interface) Proxy.newProxyInstance(Main.class.getClassLoader(), + new Class<?>[] { Interface.class }, + new Handler()); + i.foo(); + } +} + +interface Interface { + void foo(); +} + +class Handler implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) { + System.out.println("Method: " + method); + return null; + } +} diff --git a/test/676-resolve-field-type/expected.txt b/test/676-resolve-field-type/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/676-resolve-field-type/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/676-resolve-field-type/info.txt b/test/676-resolve-field-type/info.txt new file mode 100644 index 0000000000..a53244df23 --- /dev/null +++ b/test/676-resolve-field-type/info.txt @@ -0,0 +1,2 @@ +Test trying to reproduce class loader issues with the verifier. +See comments in src-ex/ChildClass.java diff --git a/test/676-resolve-field-type/src-art/Foo.java b/test/676-resolve-field-type/src-art/Foo.java new file mode 100644 index 0000000000..3df74d3bc1 --- /dev/null +++ b/test/676-resolve-field-type/src-art/Foo.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Foo { + public static Main mainObject; +} diff --git a/test/676-resolve-field-type/src-art/Main.java b/test/676-resolve-field-type/src-art/Main.java new file mode 100644 index 0000000000..c915df876f --- /dev/null +++ b/test/676-resolve-field-type/src-art/Main.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 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 dalvik.system.PathClassLoader; +import java.io.File; + +public class Main { + public static void main(String[] args) throws Exception { + ClassLoader parentLoader = Main.class.getClassLoader(); + ClassLoader childLoader = new PathClassLoader(DEX_CHILD, parentLoader); + Class.forName("ChildClass", true, childLoader).getDeclaredMethod("runTest").invoke(null); + } + + private static final String DEX_CHILD = + new File(System.getenv("DEX_LOCATION"), "676-resolve-field-type-ex.jar").getAbsolutePath(); + + public static void staticMethod() {} +} diff --git a/test/676-resolve-field-type/src-ex/ChildClass.java b/test/676-resolve-field-type/src-ex/ChildClass.java new file mode 100644 index 0000000000..167d4a689d --- /dev/null +++ b/test/676-resolve-field-type/src-ex/ChildClass.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 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; + +public class ChildClass { + // This method being synchronized means the SIGQUIT code in ART will call + // FindLocksAtDexPc (we check for the presence of try blocks), + // which triggered a DCHECK of an invariant. + public static synchronized void runTest() throws Exception { + Main m = Foo.mainObject; + + SigQuit.doKill(); + + // Sleep some time to get the kill while executing this method. + Thread.sleep(2); + + // The FindLocksAtDexPc method running with the verifier would fail when + // resolving this call, as the verifier didn't register Main from the field + // access above with the current class loader. + Main.staticMethod(); + System.out.println("Done"); + } + + private final static class SigQuit { + private final static int sigquit; + private final static Method kill; + private final static int pid; + + static { + int pidTemp = -1; + int sigquitTemp = -1; + Method killTemp = null; + + try { + Class<?> osClass = Class.forName("android.system.Os"); + Method getpid = osClass.getDeclaredMethod("getpid"); + pidTemp = (Integer)getpid.invoke(null); + + Class<?> osConstants = Class.forName("android.system.OsConstants"); + Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); + sigquitTemp = (Integer)sigquitField.get(null); + + killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); + } catch (Exception e) { + throw new Error(e); + } + + pid = pidTemp; + sigquit = sigquitTemp; + kill = killTemp; + } + + public static void doKill() throws Exception { + kill.invoke(null, pid, sigquit); + } + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index ddf9098c64..fe1e31e759 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -122,6 +122,11 @@ "non-deterministic. Same for 913."] }, { + "tests": ["1946-list-descriptors"], + "variant": "gcverify | trace", + "description": "This test is rather slow and gcverify or trace often cause it to timeout." + }, + { "tests": ["961-default-iface-resolution-gen", "964-default-iface-init-gen", "968-default-partial-compile-gen"], diff --git a/tools/Android.bp b/tools/Android.bp index 5093d7a45b..a7ed9bc0b0 100644 --- a/tools/Android.bp +++ b/tools/Android.bp @@ -19,4 +19,12 @@ python_binary_host { srcs: [ "generate_operator_out.py", ], + version: { + py2: { + enabled: true, + }, + py3: { + enabled: false, + }, + }, } |