diff options
92 files changed, 2157 insertions, 723 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7283710bfa..e0f0ae5815 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -61,7 +61,7 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) # Dex file dependencies for each gtest. ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode -ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod +ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives @@ -171,6 +171,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/oat_file_test.cc \ runtime/oat_file_assistant_test.cc \ runtime/parsed_options_test.cc \ + runtime/prebuilt_tools_test.cc \ runtime/reference_table_test.cc \ runtime/thread_pool_test.cc \ runtime/transaction_test.cc \ diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index cdb1b9e9a5..f7501d2dda 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -22,6 +22,7 @@ #include <sstream> #include "arch/instruction_set.h" +#include "dwarf/dwarf_constants.h" #include "dwarf/dwarf_test.h" #include "dwarf/headers.h" #include "disassembler/disassembler.h" @@ -45,7 +46,8 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteEhFrameCIE(is64bit, dwarf::Reg(8), initial_opcodes, &eh_frame_data_); + dwarf::WriteEhFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8), + initial_opcodes, &eh_frame_data_); std::vector<uintptr_t> eh_frame_patches; dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi, &eh_frame_data_, &eh_frame_patches); diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 8ffc86ea3f..05cb8b458e 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -140,6 +140,27 @@ void CommonCompilerTest::MakeExecutable(mirror::ClassLoader* class_loader, const } } +// Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler +// driver assumes ownership of the set, so the test should properly release the set. +std::unordered_set<std::string>* CommonCompilerTest::GetImageClasses() { + // Empty set: by default no classes are retained in the image. + return new std::unordered_set<std::string>(); +} + +// Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler +// driver assumes ownership of the set, so the test should properly release the set. +std::unordered_set<std::string>* CommonCompilerTest::GetCompiledClasses() { + // Null, no selection of compiled-classes. + return nullptr; +} + +// Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler +// driver assumes ownership of the set, so the test should properly release the set. +std::unordered_set<std::string>* CommonCompilerTest::GetCompiledMethods() { + // Null, no selection of compiled-methods. + return nullptr; +} + void CommonCompilerTest::SetUp() { CommonRuntimeTest::SetUp(); { @@ -165,7 +186,10 @@ void CommonCompilerTest::SetUp() { method_inliner_map_.get(), compiler_kind, instruction_set, instruction_set_features_.get(), - true, new std::unordered_set<std::string>, nullptr, + true, + GetImageClasses(), + GetCompiledClasses(), + GetCompiledMethods(), 2, true, true, "", timer_.get(), -1, "")); } // We typically don't generate an image in unit tests, disable this optimization by default. diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index d7b210d571..8d80a2da5c 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -18,6 +18,7 @@ #define ART_COMPILER_COMMON_COMPILER_TEST_H_ #include <list> +#include <unordered_set> #include <vector> #include "common_runtime_test.h" @@ -56,6 +57,18 @@ class CommonCompilerTest : public CommonRuntimeTest { virtual void SetUpRuntimeOptions(RuntimeOptions *options); + // Get the set of image classes given to the compiler-driver in SetUp. Note: the compiler + // driver assumes ownership of the set, so the test should properly release the set. + virtual std::unordered_set<std::string>* GetImageClasses(); + + // Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler + // driver assumes ownership of the set, so the test should properly release the set. + virtual std::unordered_set<std::string>* GetCompiledClasses(); + + // Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler + // driver assumes ownership of the set, so the test should properly release the set. + virtual std::unordered_set<std::string>* GetCompiledMethods(); + virtual void TearDown(); void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index d276457d01..555d5b9cf3 100644 --- a/compiler/dex/quick/quick_cfi_test.cc +++ b/compiler/dex/quick/quick_cfi_test.cc @@ -76,7 +76,7 @@ class QuickCFITest : public CFITest { isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error)); CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map, Compiler::kQuick, isa, isa_features.get(), - false, 0, 0, 0, false, false, "", 0, -1, ""); + false, nullptr, nullptr, nullptr, 0, false, false, "", 0, -1, ""); ClassLinker* linker = nullptr; CompilationUnit cu(&pool, isa, &driver, linker); DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1832647f4e..e665e1d4e8 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -76,8 +76,8 @@ static constexpr bool kTimeCompileMethod = !kIsDebugBuild; // Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now. static constexpr bool kProduce64BitELFFiles = false; -// Whether classes-to-compile is only applied to the boot image, or, when given, too all -// compilations. +// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when +// given, too all compilations. static constexpr bool kRestrictCompilationFiltersToImage = true; static double Percentage(size_t x, size_t y) { @@ -349,6 +349,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, const InstructionSetFeatures* instruction_set_features, bool image, std::unordered_set<std::string>* image_classes, std::unordered_set<std::string>* compiled_classes, + std::unordered_set<std::string>* compiled_methods, size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, CumulativeLogger* timer, int swap_fd, const std::string& profile_file) @@ -369,6 +370,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, image_(image), image_classes_(image_classes), classes_to_compile_(compiled_classes), + methods_to_compile_(compiled_methods), had_hard_verifier_failure_(false), thread_count_(thread_count), stats_(new AOTCompilationStats), @@ -670,6 +672,19 @@ bool CompilerDriver::IsClassToCompile(const char* descriptor) const { return classes_to_compile_->find(descriptor) != classes_to_compile_->end(); } +bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const { + if (kRestrictCompilationFiltersToImage && !IsImage()) { + return true; + } + + if (methods_to_compile_ == nullptr) { + return true; + } + + std::string tmp = PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file, true); + return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end(); +} + static void ResolveExceptionsForMethod(MutableHandle<mirror::ArtMethod> method_handle, std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2232,7 +2247,9 @@ void CompilerDriver::CompileMethod(Thread* self, const DexFile::CodeItem* code_i // Basic checks, e.g., not <clinit>. verification_results_->IsCandidateForCompilation(method_ref, access_flags) && // Did not fail to create VerifiedMethod metadata. - has_verified_method; + has_verified_method && + // Is eligable for compilation by methods-to-compile filter. + IsMethodToCompile(method_ref); if (compile) { // NOTE: if compiler declines to compile this method, it will return nullptr. compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index ce13a17792..50e1fb14e5 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -104,6 +104,7 @@ class CompilerDriver { const InstructionSetFeatures* instruction_set_features, bool image, std::unordered_set<std::string>* image_classes, std::unordered_set<std::string>* compiled_classes, + std::unordered_set<std::string>* compiled_methods, size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, CumulativeLogger* timer, int swap_fd, @@ -428,6 +429,9 @@ class CompilerDriver { // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_. bool IsClassToCompile(const char* descriptor) const; + // Checks whether the provided method should be compiled, i.e., is in method_to_compile_. + bool IsMethodToCompile(const MethodReference& method_ref) const; + void RecordClassStatus(ClassReference ref, mirror::Class::Status status) LOCKS_EXCLUDED(compiled_classes_lock_); @@ -597,6 +601,11 @@ class CompilerDriver { // This option may be restricted to the boot image, depending on a flag in the implementation. std::unique_ptr<std::unordered_set<std::string>> classes_to_compile_; + // Specifies the methods that will be compiled. Note that if methods_to_compile_ is nullptr, + // all methods are eligible for compilation (compilation filters etc. will still apply). + // This option may be restricted to the boot image, depending on a flag in the implementation. + std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_; + bool had_hard_verifier_failure_; size_t thread_count_; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index e78ff9078b..ded50ca105 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -175,6 +175,60 @@ TEST_F(CompilerDriverTest, AbstractMethodErrorStub) { } } +class CompilerDriverMethodsTest : public CompilerDriverTest { + protected: + std::unordered_set<std::string>* GetCompiledMethods() OVERRIDE { + return new std::unordered_set<std::string>({ + "byte StaticLeafMethods.identity(byte)", + "int StaticLeafMethods.sum(int, int, int)", + "double StaticLeafMethods.sum(double, double, double, double)" + }); + } +}; + +TEST_F(CompilerDriverMethodsTest, Selection) { + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("StaticLeafMethods"); + } + ASSERT_NE(class_loader, nullptr); + + // Need to enable dex-file writability. Methods rejected to be compiled will run through the + // dex-to-dex compiler. + for (const DexFile* dex_file : GetDexFiles(class_loader)) { + ASSERT_TRUE(dex_file->EnableWrite()); + } + + CompileAll(class_loader); + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<1> hs(self); + ScopedObjectAccess soa(self); + Handle<mirror::ClassLoader> h_loader(hs.NewHandle( + reinterpret_cast<mirror::ClassLoader*>(self->DecodeJObject(class_loader)))); + mirror::Class* klass = class_linker->FindClass(self, "LStaticLeafMethods;", h_loader); + ASSERT_NE(klass, nullptr); + + std::unique_ptr<std::unordered_set<std::string>> expected(GetCompiledMethods()); + + for (int32_t i = 0; static_cast<uint32_t>(i) < klass->NumDirectMethods(); i++) { + mirror::ArtMethod* m = klass->GetDirectMethod(i); + std::string name = PrettyMethod(m, true); + const void* code = + m->GetEntryPointFromQuickCompiledCodePtrSize(InstructionSetPointerSize(kRuntimeISA)); + ASSERT_NE(code, nullptr); + if (expected->find(name) != expected->end()) { + expected->erase(name); + EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code)); + } else { + EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code)); + } + } + EXPECT_TRUE(expected->empty()); +} + // TODO: need check-cast test (when stub complete & we can throw/catch } // namespace art diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/dwarf/dwarf_constants.h index 8e39ca703b..61a44cdabc 100644 --- a/compiler/dwarf/dwarf_constants.h +++ b/compiler/dwarf/dwarf_constants.h @@ -658,6 +658,28 @@ enum CallFrameInstruction : uint8_t { DW_CFA_hi_user = 0x3f }; +enum ExceptionHeaderValueFormat : uint8_t { + DW_EH_PE_native = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_omit = 0xFF, +}; + +enum ExceptionHeaderValueApplication : uint8_t { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, +}; + } // namespace dwarf } // namespace art diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc index 98f691a7a1..edba00aeaa 100644 --- a/compiler/dwarf/dwarf_test.cc +++ b/compiler/dwarf/dwarf_test.cc @@ -16,6 +16,7 @@ #include "dwarf_test.h" +#include "dwarf/dwarf_constants.h" #include "dwarf/debug_frame_opcode_writer.h" #include "dwarf/debug_info_entry_writer.h" #include "dwarf/debug_line_opcode_writer.h" @@ -119,7 +120,8 @@ TEST_F(DwarfTest, DebugFrame) { DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)"); DebugFrameOpCodeWriter<> initial_opcodes; - WriteEhFrameCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &eh_frame_data_); + WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8), + initial_opcodes, &eh_frame_data_); std::vector<uintptr_t> eh_frame_patches; std::vector<uintptr_t> expected_patches { 28 }; // NOLINT WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(), @@ -132,7 +134,8 @@ TEST_F(DwarfTest, DebugFrame) { TEST_F(DwarfTest, DebugFrame64) { constexpr bool is64bit = true; DebugFrameOpCodeWriter<> initial_opcodes; - WriteEhFrameCIE(is64bit, Reg(16), initial_opcodes, &eh_frame_data_); + WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16), + initial_opcodes, &eh_frame_data_); DebugFrameOpCodeWriter<> opcodes; std::vector<uintptr_t> eh_frame_patches; std::vector<uintptr_t> expected_patches { 32 }; // NOLINT @@ -170,7 +173,8 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) { DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)"); DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)"); DebugFrameOpCodeWriter<> initial_opcodes; - WriteEhFrameCIE(is64bit, Reg(16), initial_opcodes, &eh_frame_data_); + WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16), + initial_opcodes, &eh_frame_data_); std::vector<uintptr_t> eh_frame_patches; WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, opcodes.data(), &eh_frame_data_, &eh_frame_patches); diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h index dd5e0c286e..d31cfa59af 100644 --- a/compiler/dwarf/dwarf_test.h +++ b/compiler/dwarf/dwarf_test.h @@ -55,36 +55,6 @@ class DwarfTest : public CommonRuntimeTest { expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line}); } - static std::string GetObjdumpPath() { - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - std::string host_prebuilts = std::string(android_build_top) + - "/prebuilts/gcc/linux-x86/host/"; - // Read the content of the directory. - std::set<std::string> entries; - DIR* dir = opendir(host_prebuilts.c_str()); - if (dir != nullptr) { - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - if (strstr(entry->d_name, "linux-glibc")) { - entries.insert(host_prebuilts + entry->d_name); - } - } - closedir(dir); - } - // Strings are sorted so the last one should be the most recent version. - if (!entries.empty()) { - std::string path = *entries.rbegin() + "/x86_64-linux/bin/objdump"; - struct stat st; - if (stat(path.c_str(), &st) == 0) { - return path; // File exists. - } - } - } - ADD_FAILURE() << "Can not find prebuild objdump."; - return "objdump"; // Use the system objdump as fallback. - } - // Pretty-print the generated DWARF data using objdump. template<typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> @@ -100,38 +70,38 @@ class DwarfTest : public CommonRuntimeTest { Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> builder( &code, file.GetFile(), isa, 0, 0, 0, 0, 0, 0, false, false); typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> Section; + Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); if (!debug_info_data_.empty()) { - Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); debug_info.SetBuffer(debug_info_data_); - builder.RegisterRawSection(debug_info); + builder.RegisterRawSection(&debug_info); } if (!debug_abbrev_data_.empty()) { - Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); debug_abbrev.SetBuffer(debug_abbrev_data_); - builder.RegisterRawSection(debug_abbrev); + builder.RegisterRawSection(&debug_abbrev); } if (!debug_str_data_.empty()) { - Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); debug_str.SetBuffer(debug_str_data_); - builder.RegisterRawSection(debug_str); + builder.RegisterRawSection(&debug_str); } if (!debug_line_data_.empty()) { - Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); debug_line.SetBuffer(debug_line_data_); - builder.RegisterRawSection(debug_line); + builder.RegisterRawSection(&debug_line); } if (!eh_frame_data_.empty()) { - Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); eh_frame.SetBuffer(eh_frame_data_); - builder.RegisterRawSection(eh_frame); + builder.RegisterRawSection(&eh_frame); } builder.Init(); builder.Write(); // Read the elf file back using objdump. std::vector<std::string> lines; - std::string cmd = GetObjdumpPath(); - cmd = cmd + " " + args + " " + file.GetFilename() + " 2>&1"; + std::string cmd = GetAndroidHostToolsDir(); + cmd = cmd + "objdump " + args + " " + file.GetFilename() + " 2>&1"; FILE* output = popen(cmd.data(), "r"); char buffer[1024]; const char* line; diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h index 760f53c6e4..9f64766e18 100644 --- a/compiler/dwarf/headers.h +++ b/compiler/dwarf/headers.h @@ -22,6 +22,7 @@ #include "dwarf/debug_frame_opcode_writer.h" #include "dwarf/debug_info_entry_writer.h" #include "dwarf/debug_line_opcode_writer.h" +#include "dwarf/dwarf_constants.h" #include "dwarf/register.h" #include "dwarf/writer.h" @@ -36,7 +37,9 @@ namespace dwarf { // Write common information entry (CIE) to .eh_frame section. template<typename Allocator> -void WriteEhFrameCIE(bool is64bit, Reg return_address_register, +void WriteEhFrameCIE(bool is64bit, + ExceptionHeaderValueApplication address_type, + Reg return_address_register, const DebugFrameOpCodeWriter<Allocator>& opcodes, std::vector<uint8_t>* eh_frame) { Writer<> writer(eh_frame); @@ -50,9 +53,9 @@ void WriteEhFrameCIE(bool is64bit, Reg return_address_register, writer.PushUleb128(return_address_register.num()); // ubyte in DWARF2. writer.PushUleb128(1); // z: Augmentation data size. if (is64bit) { - writer.PushUint8(0x04); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8). + writer.PushUint8(address_type | DW_EH_PE_udata8); // R: Pointer encoding. } else { - writer.PushUint8(0x03); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4). + writer.PushUint8(address_type | DW_EH_PE_udata4); // R: Pointer encoding. } writer.PushData(opcodes.data()); writer.Pad(is64bit ? 8 : 4); diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 124ed03c21..323c933ab1 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -584,11 +584,12 @@ class ElfBuilder FINAL { // | Elf_Ehdr | // +-------------------------+ // | Elf_Phdr PHDR | - // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata + // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .eh_frame .eh_frame_hdr .rodata // | Elf_Phdr LOAD R X | .text // | Elf_Phdr LOAD RW | .bss (Optional) // | Elf_Phdr LOAD RW | .dynamic // | Elf_Phdr DYNAMIC | .dynamic + // | Elf_Phdr EH_FRAME R | .eh_frame_hdr // +-------------------------+ // | .dynsym | // | Elf_Sym STN_UNDEF | @@ -615,6 +616,10 @@ class ElfBuilder FINAL { // | ... | // | Elf_Word chain[c - 1] | // +-------------------------+ + // | .eh_frame | (Optional) + // +-------------------------+ + // | .eh_frame_hdr | (Optional) + // +-------------------------+ // | .rodata | // | oatdata..oatexec-4 | // +-------------------------+ @@ -648,22 +653,21 @@ class ElfBuilder FINAL { // | .shstrtab\0 | // | .symtab\0 | (Optional) // | .strtab\0 | (Optional) - // | .debug_str\0 | (Optional) - // | .debug_info\0 | (Optional) // | .eh_frame\0 | (Optional) - // | .debug_line\0 | (Optional) + // | .eh_frame_hdr\0 | (Optional) + // | .debug_info\0 | (Optional) // | .debug_abbrev\0 | (Optional) + // | .debug_str\0 | (Optional) + // | .debug_line\0 | (Optional) // +-------------------------+ (Optional) // | .debug_info | (Optional) // +-------------------------+ (Optional) // | .debug_abbrev | (Optional) // +-------------------------+ (Optional) - // | .eh_frame | (Optional) + // | .debug_str | (Optional) // +-------------------------+ (Optional) // | .debug_line | (Optional) // +-------------------------+ (Optional) - // | .debug_str | (Optional) - // +-------------------------+ (Optional) // | Elf_Shdr NULL | // | Elf_Shdr .dynsym | // | Elf_Shdr .dynstr | @@ -673,11 +677,12 @@ class ElfBuilder FINAL { // | Elf_Shdr .bss | (Optional) // | Elf_Shdr .dynamic | // | Elf_Shdr .shstrtab | + // | Elf_Shdr .eh_frame | (Optional) + // | Elf_Shdr .eh_frame_hdr | (Optional) // | Elf_Shdr .debug_info | (Optional) // | Elf_Shdr .debug_abbrev | (Optional) - // | Elf_Shdr .eh_frame | (Optional) - // | Elf_Shdr .debug_line | (Optional) // | Elf_Shdr .debug_str | (Optional) + // | Elf_Shdr .debug_line | (Optional) // +-------------------------+ if (fatal_error_) { @@ -718,6 +723,9 @@ class ElfBuilder FINAL { program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC; program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W; + program_headers_[PH_EH_FRAME_HDR].p_type = PT_NULL; + program_headers_[PH_EH_FRAME_HDR].p_flags = PF_R; + // Get the dynstr string. dynstr_ = dynsym_builder_.GenerateStrtab(); @@ -828,10 +836,37 @@ class ElfBuilder FINAL { hash_builder_.GetSection()->sh_size = hash_.size() * sizeof(Elf_Word); hash_builder_.GetSection()->sh_link = hash_builder_.GetLink(); + // Get the layout of the extra sections with SHF_ALLOC flag. + // This will deal with .eh_frame and .eh_frame_hdr. + // .eh_frame contains relative pointers to .text which we + // want to fixup between the calls to Init() and Write(). + // Therefore we handle those sections here as opposed to Write(). + // It also has the nice side effect of including .eh_frame + // with the rest of LOAD_R segment. It must come before .rodata + // because .rodata and .text must be next to each other. + Elf_Shdr* prev = hash_builder_.GetSection(); + for (auto* it : other_builders_) { + if ((it->GetSection()->sh_flags & SHF_ALLOC) != 0) { + it->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev); + it->GetSection()->sh_addr = it->GetSection()->sh_offset; + it->GetSection()->sh_size = it->GetBuffer()->size(); + it->GetSection()->sh_link = it->GetLink(); + prev = it->GetSection(); + } + } + // If the sections exist, check that they have been handled. + const auto* eh_frame = FindRawSection(".eh_frame"); + if (eh_frame != nullptr) { + DCHECK_NE(eh_frame->GetSection()->sh_offset, 0u); + } + const auto* eh_frame_hdr = FindRawSection(".eh_frame_hdr"); + if (eh_frame_hdr != nullptr) { + DCHECK_NE(eh_frame_hdr->GetSection()->sh_offset, 0u); + } + // Get the layout of the rodata section. rodata_builder_.GetSection()->sh_offset = - NextOffset<Elf_Word, Elf_Shdr>(*rodata_builder_.GetSection(), - *hash_builder_.GetSection()); + NextOffset<Elf_Word, Elf_Shdr>(*rodata_builder_.GetSection(), *prev); rodata_builder_.GetSection()->sh_addr = rodata_builder_.GetSection()->sh_offset; rodata_builder_.GetSection()->sh_size = rodata_builder_.GetSize(); rodata_builder_.GetSection()->sh_link = rodata_builder_.GetLink(); @@ -909,9 +944,7 @@ class ElfBuilder FINAL { } // Setup all the other sections. - for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(), - *end = builder + other_builders_.size(); - builder != end; ++builder) { + for (auto* builder : other_builders_) { section_ptrs_.push_back(builder->GetSection()); AssignSectionStr(builder, &shstrtab_); builder->SetSectionIndex(section_index_); @@ -958,20 +991,22 @@ class ElfBuilder FINAL { } } - // Get the layout of the extra sections. (This will deal with the debug - // sections if they are there) - for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) { - it->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev); - it->GetSection()->sh_addr = 0; - it->GetSection()->sh_size = it->GetBuffer()->size(); - it->GetSection()->sh_link = it->GetLink(); - - // We postpone adding an ElfFilePiece to keep the order in "pieces." - - prev = it->GetSection(); - if (debug_logging_) { - LOG(INFO) << it->GetName() << " off=" << it->GetSection()->sh_offset - << " size=" << it->GetSection()->sh_size; + // Get the layout of the extra sections without SHF_ALLOC flag. + // (This will deal with the debug sections if they are there) + for (auto* it : other_builders_) { + if ((it->GetSection()->sh_flags & SHF_ALLOC) == 0) { + it->GetSection()->sh_offset = NextOffset<Elf_Word, Elf_Shdr>(*it->GetSection(), *prev); + it->GetSection()->sh_addr = 0; + it->GetSection()->sh_size = it->GetBuffer()->size(); + it->GetSection()->sh_link = it->GetLink(); + + // We postpone adding an ElfFilePiece to keep the order in "pieces." + + prev = it->GetSection(); + if (debug_logging_) { + LOG(INFO) << it->GetName() << " off=" << it->GetSection()->sh_offset + << " size=" << it->GetSection()->sh_size; + } } } @@ -1044,6 +1079,26 @@ class ElfBuilder FINAL { program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.GetSection()->sh_size; program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.GetSection()->sh_addralign; + const auto* eh_frame_hdr = FindRawSection(".eh_frame_hdr"); + if (eh_frame_hdr != nullptr) { + const auto* eh_frame = FindRawSection(".eh_frame"); + // Check layout: + // 1) eh_frame is before eh_frame_hdr. + // 2) There's no gap. + CHECK(eh_frame != nullptr); + CHECK_LE(eh_frame->GetSection()->sh_offset, eh_frame_hdr->GetSection()->sh_offset); + CHECK_EQ(eh_frame->GetSection()->sh_offset + eh_frame->GetSection()->sh_size, + eh_frame_hdr->GetSection()->sh_offset); + + program_headers_[PH_EH_FRAME_HDR].p_type = PT_GNU_EH_FRAME; + program_headers_[PH_EH_FRAME_HDR].p_offset = eh_frame_hdr->GetSection()->sh_offset; + program_headers_[PH_EH_FRAME_HDR].p_vaddr = eh_frame_hdr->GetSection()->sh_addr; + program_headers_[PH_EH_FRAME_HDR].p_paddr = eh_frame_hdr->GetSection()->sh_addr; + program_headers_[PH_EH_FRAME_HDR].p_filesz = eh_frame_hdr->GetSection()->sh_size; + program_headers_[PH_EH_FRAME_HDR].p_memsz = eh_frame_hdr->GetSection()->sh_size; + program_headers_[PH_EH_FRAME_HDR].p_align = eh_frame_hdr->GetSection()->sh_addralign; + } + // Finish setup of the Ehdr values. elf_header_.e_phoff = PHDR_OFFSET; elf_header_.e_shoff = sections_offset; @@ -1108,7 +1163,7 @@ class ElfBuilder FINAL { } // Postponed debug info. - for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) { + for (auto* it : other_builders_) { pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(it->GetName(), it->GetSection()->sh_offset, it->GetBuffer()->data(), it->GetBuffer()->size())); @@ -1125,12 +1180,21 @@ class ElfBuilder FINAL { return true; } - // Adds the given raw section to the builder. This will copy it. The caller - // is responsible for deallocating their copy. - void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) { + // Adds the given raw section to the builder. It does not take ownership. + void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* bld) { other_builders_.push_back(bld); } + const ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* + FindRawSection(const char* name) { + for (const auto* other_builder : other_builders_) { + if (other_builder->GetName() == name) { + return other_builder; + } + } + return nullptr; + } + private: void SetISA(InstructionSet isa) { switch (isa) { @@ -1282,7 +1346,8 @@ class ElfBuilder FINAL { PH_LOAD_RW_BSS = 3, PH_LOAD_RW_DYNAMIC = 4, PH_DYNAMIC = 5, - PH_NUM = 6, + PH_EH_FRAME_HDR = 6, + PH_NUM = 7, }; static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM; Elf_Phdr program_headers_[PH_NUM]; @@ -1306,7 +1371,7 @@ class ElfBuilder FINAL { ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_; ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_; ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_; - std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_; + std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>*> other_builders_; DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index 39233ce94a..cf0adae525 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -28,7 +28,9 @@ namespace art { namespace dwarf { -static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) { +static void WriteEhFrameCIE(InstructionSet isa, + ExceptionHeaderValueApplication addr_type, + std::vector<uint8_t>* eh_frame) { // Scratch registers should be marked as undefined. This tells the // debugger that its value in the previous frame is not recoverable. bool is64bit = Is64BitInstructionSet(isa); @@ -53,8 +55,8 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) opcodes.SameValue(Reg::ArmFp(reg)); } } - auto return_address_reg = Reg::ArmCore(14); // R14(LR). - WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); + auto return_reg = Reg::ArmCore(14); // R14(LR). + WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame); return; } case kArm64: { @@ -76,8 +78,8 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) opcodes.SameValue(Reg::Arm64Fp(reg)); } } - auto return_address_reg = Reg::Arm64Core(30); // R30(LR). - WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); + auto return_reg = Reg::Arm64Core(30); // R30(LR). + WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame); return; } case kMips: @@ -92,8 +94,8 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) opcodes.SameValue(Reg::MipsCore(reg)); } } - auto return_address_reg = Reg::MipsCore(31); // R31(RA). - WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); + auto return_reg = Reg::MipsCore(31); // R31(RA). + WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame); return; } case kX86: { @@ -114,8 +116,8 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) for (int reg = 0; reg < 8; reg++) { opcodes.Undefined(Reg::X86Fp(reg)); } - auto return_address_reg = Reg::X86Core(8); // R8(EIP). - WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); + auto return_reg = Reg::X86Core(8); // R8(EIP). + WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame); return; } case kX86_64: { @@ -140,8 +142,8 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) opcodes.SameValue(Reg::X86_64Fp(reg)); } } - auto return_address_reg = Reg::X86_64Core(16); // R16(RIP). - WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); + auto return_reg = Reg::X86_64Core(16); // R16(RIP). + WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame); return; } case kNone: @@ -152,22 +154,37 @@ static void WriteEhFrameCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) } void WriteEhFrame(const CompilerDriver* compiler, - OatWriter* oat_writer, - uint32_t text_section_offset, - std::vector<uint8_t>* eh_frame) { + const OatWriter* oat_writer, + ExceptionHeaderValueApplication address_type, + std::vector<uint8_t>* eh_frame, + std::vector<uintptr_t>* eh_frame_patches, + std::vector<uint8_t>* eh_frame_hdr) { const auto& method_infos = oat_writer->GetMethodDebugInfo(); const InstructionSet isa = compiler->GetInstructionSet(); + + // Write .eh_frame section. size_t cie_offset = eh_frame->size(); - auto* eh_frame_patches = oat_writer->GetAbsolutePatchLocationsFor(".eh_frame"); - WriteEhFrameCIE(isa, eh_frame); + WriteEhFrameCIE(isa, address_type, eh_frame); for (const OatWriter::DebugInfo& mi : method_infos) { const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo(); if (opcodes != nullptr) { WriteEhFrameFDE(Is64BitInstructionSet(isa), cie_offset, - text_section_offset + mi.low_pc_, mi.high_pc_ - mi.low_pc_, + mi.low_pc_, mi.high_pc_ - mi.low_pc_, opcodes, eh_frame, eh_frame_patches); } } + + // Write .eh_frame_hdr section. + Writer<> header(eh_frame_hdr); + header.PushUint8(1); // Version. + header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // Encoding of .eh_frame pointer. + header.PushUint8(DW_EH_PE_omit); // Encoding of binary search table size. + header.PushUint8(DW_EH_PE_omit); // Encoding of binary search table addresses. + // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section, and need to encode + // relative to this location as libunwind doesn't honor datarel for eh_frame_hdr correctly. + header.PushInt32(-static_cast<int32_t>(eh_frame->size() + 4U)); + // Omit binary search table size (number of entries). + // Omit binary search table. } /* @@ -175,17 +192,20 @@ void WriteEhFrame(const CompilerDriver* compiler, * @param oat_writer The Oat file Writer. * @param eh_frame Call Frame Information. * @param debug_info Compilation unit information. + * @param debug_info_patches Address locations to be patched. * @param debug_abbrev Abbreviations used to generate dbg_info. * @param debug_str Debug strings. * @param debug_line Line number table. + * @param debug_line_patches Address locations to be patched. */ void WriteDebugSections(const CompilerDriver* compiler, - OatWriter* oat_writer, - uint32_t text_section_offset, + const OatWriter* oat_writer, std::vector<uint8_t>* debug_info, + std::vector<uintptr_t>* debug_info_patches, std::vector<uint8_t>* debug_abbrev, std::vector<uint8_t>* debug_str, - std::vector<uint8_t>* debug_line) { + std::vector<uint8_t>* debug_line, + std::vector<uintptr_t>* debug_line_patches) { const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo(); const InstructionSet isa = compiler->GetInstructionSet(); @@ -229,8 +249,8 @@ void WriteDebugSections(const CompilerDriver* compiler, info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes); info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str); info.WriteData1(DW_AT_language, DW_LANG_Java); - info.WriteAddr(DW_AT_low_pc, cunit_low_pc + text_section_offset); - info.WriteAddr(DW_AT_high_pc, cunit_high_pc + text_section_offset); + info.WriteAddr(DW_AT_low_pc, cunit_low_pc); + info.WriteAddr(DW_AT_high_pc, cunit_high_pc); info.WriteData4(DW_AT_stmt_list, debug_line->size()); for (auto method_info : compilation_unit) { std::string method_name = PrettyMethod(method_info->dex_method_index_, @@ -240,12 +260,11 @@ void WriteDebugSections(const CompilerDriver* compiler, } info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no); info.WriteStrp(DW_AT_name, method_name.data(), debug_str); - info.WriteAddr(DW_AT_low_pc, method_info->low_pc_ + text_section_offset); - info.WriteAddr(DW_AT_high_pc, method_info->high_pc_ + text_section_offset); + info.WriteAddr(DW_AT_low_pc, method_info->low_pc_); + info.WriteAddr(DW_AT_high_pc, method_info->high_pc_); info.EndTag(); // DW_TAG_subprogram } info.EndTag(); // DW_TAG_compile_unit - auto* debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info"); WriteDebugInfoCU(debug_abbrev_offset, info, debug_info, debug_info_patches); // Write .debug_line section. @@ -272,7 +291,7 @@ void WriteDebugSections(const CompilerDriver* compiler, break; } DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_); - opcodes.SetAddress(text_section_offset + cunit_low_pc); + opcodes.SetAddress(cunit_low_pc); if (dwarf_isa != -1) { opcodes.SetISA(dwarf_isa); } @@ -343,7 +362,6 @@ void WriteDebugSections(const CompilerDriver* compiler, // Generate mapping opcodes from PC to Java lines. const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_; - uint32_t low_pc = text_section_offset + mi->low_pc_; if (file_index != 0 && !dex2line_map.empty()) { bool first = true; for (SrcMapElem pc2dex : mi->compiled_method_->GetSrcMappingTable()) { @@ -359,24 +377,23 @@ void WriteDebugSections(const CompilerDriver* compiler, int first_line = dex2line_map.front().to_; // Prologue is not a sensible place for a breakpoint. opcodes.NegateStmt(); - opcodes.AddRow(low_pc, first_line); + opcodes.AddRow(mi->low_pc_, first_line); opcodes.NegateStmt(); opcodes.SetPrologueEnd(); } - opcodes.AddRow(low_pc + pc, line); + opcodes.AddRow(mi->low_pc_ + pc, line); } else if (line != opcodes.CurrentLine()) { - opcodes.AddRow(low_pc + pc, line); + opcodes.AddRow(mi->low_pc_ + pc, line); } } } } else { // line 0 - instruction cannot be attributed to any source line. - opcodes.AddRow(low_pc, 0); + opcodes.AddRow(mi->low_pc_, 0); } } - opcodes.AdvancePC(text_section_offset + cunit_high_pc); + opcodes.AdvancePC(cunit_high_pc); opcodes.EndSequence(); - auto* debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line"); WriteDebugLineTable(directories, files, opcodes, debug_line, debug_line_patches); } } diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h index 2c03b98777..5bf484185a 100644 --- a/compiler/elf_writer_debug.h +++ b/compiler/elf_writer_debug.h @@ -19,23 +19,27 @@ #include <vector> +#include "dwarf/dwarf_constants.h" #include "oat_writer.h" namespace art { namespace dwarf { void WriteEhFrame(const CompilerDriver* compiler, - OatWriter* oat_writer, - uint32_t text_section_offset, - std::vector<uint8_t>* eh_frame); + const OatWriter* oat_writer, + ExceptionHeaderValueApplication address_type, + std::vector<uint8_t>* eh_frame, + std::vector<uintptr_t>* eh_frame_patches, + std::vector<uint8_t>* eh_frame_hdr); void WriteDebugSections(const CompilerDriver* compiler, - OatWriter* oat_writer, - uint32_t text_section_offset, + const OatWriter* oat_writer, std::vector<uint8_t>* debug_info, + std::vector<uintptr_t>* debug_info_patches, std::vector<uint8_t>* debug_abbrev, std::vector<uint8_t>* debug_str, - std::vector<uint8_t>* debug_line); + std::vector<uint8_t>* debug_line, + std::vector<uintptr_t>* debug_line_patches); } // namespace dwarf } // namespace art diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 429cd851e0..44c14a0739 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -70,8 +70,7 @@ class OatWriterWrapper FINAL : public CodeOutput { template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> -static void WriteDebugSymbols(const CompilerDriver* compiler_driver, - ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, +static void WriteDebugSymbols(ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder, OatWriter* oat_writer); @@ -109,6 +108,19 @@ void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, Elf_Sym, Elf_Ehdr, buffer->push_back(0); // End of sections. } +template<typename AddressType, bool SubtractPatchLocation = false> +static void PatchAddresses(const std::vector<uintptr_t>* patch_locations, + AddressType delta, std::vector<uint8_t>* buffer) { + // Addresses in .debug_* sections are unaligned. + typedef __attribute__((__aligned__(1))) AddressType UnalignedAddressType; + if (patch_locations != nullptr) { + for (uintptr_t patch_location : *patch_locations) { + *reinterpret_cast<UnalignedAddressType*>(buffer->data() + patch_location) += + delta - (SubtractPatchLocation ? patch_location : 0); + } + } +} + template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> @@ -141,33 +153,77 @@ bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(), debug)); - if (!builder->Init()) { - return false; - } + InstructionSet isa = compiler_driver_->GetInstructionSet(); + int alignment = GetInstructionSetPointerSize(isa); + typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> RawSection; + RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, alignment, 0); + RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); + RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, 1, 0); + // Do not add to .oat_patches since we will make the addresses relative. + std::vector<uintptr_t> eh_frame_patches; if (compiler_driver_->GetCompilerOptions().GetIncludeCFI() && !oat_writer->GetMethodDebugInfo().empty()) { - ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame( - ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); dwarf::WriteEhFrame(compiler_driver_, oat_writer, - builder->GetTextBuilder().GetSection()->sh_addr, - eh_frame.GetBuffer()); - builder->RegisterRawSection(eh_frame); + dwarf::DW_EH_PE_pcrel, + eh_frame.GetBuffer(), &eh_frame_patches, + eh_frame_hdr.GetBuffer()); + builder->RegisterRawSection(&eh_frame); + builder->RegisterRawSection(&eh_frame_hdr); } + // Must be done after .eh_frame is created since it is used in the Elf layout. + if (!builder->Init()) { + return false; + } + + std::vector<uintptr_t>* debug_info_patches = nullptr; + std::vector<uintptr_t>* debug_line_patches = nullptr; if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols() && !oat_writer->GetMethodDebugInfo().empty()) { - WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer); + // Add methods to .symtab. + WriteDebugSymbols(builder.get(), oat_writer); + // Generate DWARF .debug_* sections. + debug_info_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_info"); + debug_line_patches = oat_writer->GetAbsolutePatchLocationsFor(".debug_line"); + dwarf::WriteDebugSections(compiler_driver_, oat_writer, + debug_info.GetBuffer(), debug_info_patches, + debug_abbrev.GetBuffer(), + debug_str.GetBuffer(), + debug_line.GetBuffer(), debug_line_patches); + builder->RegisterRawSection(&debug_info); + builder->RegisterRawSection(&debug_abbrev); + builder->RegisterRawSection(&debug_str); + builder->RegisterRawSection(&debug_line); } if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() || // ElfWriter::Fixup will be called regardless and it needs to be able // to patch debug sections so we have to include patches for them. compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) { - ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches( - ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, 1, 0); EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer()); - builder->RegisterRawSection(oat_patches); + builder->RegisterRawSection(&oat_patches); + } + + // We know where .text and .eh_frame will be located, so patch the addresses. + Elf_Addr text_addr = builder->GetTextBuilder().GetSection()->sh_addr; + // TODO: Simplify once we use Elf64 - we can use Elf_Addr instead of branching. + if (Is64BitInstructionSet(compiler_driver_->GetInstructionSet())) { + // relative_address = (text_addr + address) - (eh_frame_addr + patch_location); + PatchAddresses<uint64_t, true>(&eh_frame_patches, + text_addr - eh_frame.GetSection()->sh_addr, eh_frame.GetBuffer()); + PatchAddresses<uint64_t>(debug_info_patches, text_addr, debug_info.GetBuffer()); + PatchAddresses<uint64_t>(debug_line_patches, text_addr, debug_line.GetBuffer()); + } else { + // relative_address = (text_addr + address) - (eh_frame_addr + patch_location); + PatchAddresses<uint32_t, true>(&eh_frame_patches, + text_addr - eh_frame.GetSection()->sh_addr, eh_frame.GetBuffer()); + PatchAddresses<uint32_t>(debug_info_patches, text_addr, debug_info.GetBuffer()); + PatchAddresses<uint32_t>(debug_line_patches, text_addr, debug_line.GetBuffer()); } return builder->Write(); @@ -178,8 +234,7 @@ template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Phdr, typename Elf_Shdr> // Do not inline to avoid Clang stack frame problems. b/18738594 NO_INLINE -static void WriteDebugSymbols(const CompilerDriver* compiler_driver, - ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, +static void WriteDebugSymbols(ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder, OatWriter* oat_writer) { const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo(); @@ -214,25 +269,6 @@ static void WriteDebugSymbols(const CompilerDriver* compiler_driver, 0, STB_LOCAL, STT_NOTYPE); } } - - typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> Section; - Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - - dwarf::WriteDebugSections(compiler_driver, - oat_writer, - builder->GetTextBuilder().GetSection()->sh_addr, - debug_info.GetBuffer(), - debug_abbrev.GetBuffer(), - debug_str.GetBuffer(), - debug_line.GetBuffer()); - - builder->RegisterRawSection(debug_info); - builder->RegisterRawSection(debug_abbrev); - builder->RegisterRawSection(debug_str); - builder->RegisterRawSection(debug_line); } // Explicit instantiations diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 9ff7ab80e0..6a085482ff 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -94,7 +94,7 @@ JitCompiler::JitCompiler() : total_time_(0) { compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), Compiler::kQuick, instruction_set, instruction_set_features_.get(), false, - nullptr, nullptr, 1, false, true, + nullptr, nullptr, nullptr, 1, false, true, std::string(), cumulative_logger_.get(), -1, std::string())); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 70630f366f..1f7500a6ee 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -45,7 +45,7 @@ class RelativePatcherTest : public testing::Test { inliner_map_(), driver_(&compiler_options_, &verification_results_, &inliner_map_, Compiler::kQuick, instruction_set, nullptr, - false, nullptr, nullptr, 1u, + false, nullptr, nullptr, nullptr, 1u, false, false, "", nullptr, -1, ""), error_msg_(), instruction_set_(instruction_set), diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 989b04fa36..925b507e09 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -93,8 +93,8 @@ TEST_F(OatTest, WriteRead) { verification_results_.get(), method_inliner_map_.get(), compiler_kind, insn_set, - insn_features.get(), false, nullptr, nullptr, 2, true, - true, "", timer_.get(), -1, "")); + insn_features.get(), false, nullptr, nullptr, nullptr, + 2, true, true, "", timer_.get(), -1, "")); jobject class_loader = nullptr; if (kCompile) { TimingLogger timings2("OatTest::WriteRead", false, false); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 8ab759d393..b14b69ba39 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -827,7 +827,9 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) { HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves(); - return (first_next_not_move != nullptr) && first_next_not_move->CanDoImplicitNullCheck(); + + return (first_next_not_move != nullptr) + && first_next_not_move->CanDoImplicitNullCheckOn(null_check->InputAt(0)); } void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { @@ -842,7 +844,7 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { return; } - if (!instr->CanDoImplicitNullCheck()) { + if (!instr->CanDoImplicitNullCheckOn(instr->InputAt(0))) { return; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 86e84ac66a..3dcfca6a0c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1556,10 +1556,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimLong: // Processing a Dex `long-to-float' instruction. - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::Any()); break; case Primitive::kPrimDouble: @@ -1589,10 +1587,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimLong: // Processing a Dex `long-to-double' instruction. - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::Any()); break; case Primitive::kPrimFloat: @@ -1813,37 +1809,31 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimLong: { // Processing a Dex `long-to-float' instruction. - Register low = in.AsRegisterPairLow<Register>(); - Register high = in.AsRegisterPairHigh<Register>(); - XmmRegister result = out.AsFpuRegister<XmmRegister>(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); - - // Operations use doubles for precision reasons (each 32-bit - // half of a long fits in the 53-bit mantissa of a double, - // but not in the 24-bit mantissa of a float). This is - // especially important for the low bits. The result is - // eventually converted to float. - - // low = low - 2^31 (to prevent bit 31 of `low` to be - // interpreted as a sign bit) - __ subl(low, Immediate(0x80000000)); - // temp = int-to-double(high) - __ cvtsi2sd(temp, high); - // temp = temp * 2^32 - __ LoadLongConstant(constant, k2Pow32EncodingForDouble); - __ mulsd(temp, constant); - // result = int-to-double(low) - __ cvtsi2sd(result, low); - // result = result + 2^31 (restore the original value of `low`) - __ LoadLongConstant(constant, k2Pow31EncodingForDouble); - __ addsd(result, constant); - // result = result + temp - __ addsd(result, temp); - // result = double-to-float(result) - __ cvtsd2ss(result, result); - // Restore low. - __ addl(low, Immediate(0x80000000)); + size_t adjustment = 0; + + // Create stack space for the call to + // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstps below. + // TODO: enhance register allocator to ask for stack temporaries. + if (!in.IsDoubleStackSlot() || !out.IsStackSlot()) { + adjustment = Primitive::ComponentSize(Primitive::kPrimLong); + __ subl(ESP, Immediate(adjustment)); + } + + // Load the value to the FP stack, using temporaries if needed. + PushOntoFPStack(in, 0, adjustment, false, true); + + if (out.IsStackSlot()) { + __ fstps(Address(ESP, out.GetStackIndex() + adjustment)); + } else { + __ fstps(Address(ESP, 0)); + Location stack_temp = Location::StackSlot(0); + codegen_->Move32(out, stack_temp); + } + + // Remove the temporary stack space we allocated. + if (adjustment != 0) { + __ addl(ESP, Immediate(adjustment)); + } break; } @@ -1872,29 +1862,31 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimLong: { // Processing a Dex `long-to-double' instruction. - Register low = in.AsRegisterPairLow<Register>(); - Register high = in.AsRegisterPairHigh<Register>(); - XmmRegister result = out.AsFpuRegister<XmmRegister>(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); - - // low = low - 2^31 (to prevent bit 31 of `low` to be - // interpreted as a sign bit) - __ subl(low, Immediate(0x80000000)); - // temp = int-to-double(high) - __ cvtsi2sd(temp, high); - // temp = temp * 2^32 - __ LoadLongConstant(constant, k2Pow32EncodingForDouble); - __ mulsd(temp, constant); - // result = int-to-double(low) - __ cvtsi2sd(result, low); - // result = result + 2^31 (restore the original value of `low`) - __ LoadLongConstant(constant, k2Pow31EncodingForDouble); - __ addsd(result, constant); - // result = result + temp - __ addsd(result, temp); - // Restore low. - __ addl(low, Immediate(0x80000000)); + size_t adjustment = 0; + + // Create stack space for the call to + // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstpl below. + // TODO: enhance register allocator to ask for stack temporaries. + if (!in.IsDoubleStackSlot() || !out.IsDoubleStackSlot()) { + adjustment = Primitive::ComponentSize(Primitive::kPrimLong); + __ subl(ESP, Immediate(adjustment)); + } + + // Load the value to the FP stack, using temporaries if needed. + PushOntoFPStack(in, 0, adjustment, false, true); + + if (out.IsDoubleStackSlot()) { + __ fstpl(Address(ESP, out.GetStackIndex() + adjustment)); + } else { + __ fstpl(Address(ESP, 0)); + Location stack_temp = Location::DoubleStackSlot(0); + codegen_->Move64(out, stack_temp); + } + + // Remove the temporary stack space we allocated. + if (adjustment != 0) { + __ addl(ESP, Immediate(adjustment)); + } break; } @@ -2234,24 +2226,43 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { } } -void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, uint32_t temp_offset, - uint32_t stack_adjustment, bool is_float) { +void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, + uint32_t temp_offset, + uint32_t stack_adjustment, + bool is_fp, + bool is_wide) { if (source.IsStackSlot()) { - DCHECK(is_float); - __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + DCHECK(!is_wide); + if (is_fp) { + __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } else { + __ filds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } } else if (source.IsDoubleStackSlot()) { - DCHECK(!is_float); - __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + DCHECK(is_wide); + if (is_fp) { + __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } else { + __ fildl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } } else { // Write the value to the temporary location on the stack and load to FP stack. - if (is_float) { + if (!is_wide) { Location stack_temp = Location::StackSlot(temp_offset); codegen_->Move32(stack_temp, source); - __ flds(Address(ESP, temp_offset)); + if (is_fp) { + __ flds(Address(ESP, temp_offset)); + } else { + __ filds(Address(ESP, temp_offset)); + } } else { Location stack_temp = Location::DoubleStackSlot(temp_offset); codegen_->Move64(stack_temp, source); - __ fldl(Address(ESP, temp_offset)); + if (is_fp) { + __ fldl(Address(ESP, temp_offset)); + } else { + __ fildl(Address(ESP, temp_offset)); + } } } } @@ -2270,8 +2281,9 @@ void InstructionCodeGeneratorX86::GenerateRemFP(HRem *rem) { __ subl(ESP, Immediate(2 * elem_size)); // Load the values to the FP stack in reverse order, using temporaries if needed. - PushOntoFPStack(second, elem_size, 2 * elem_size, is_float); - PushOntoFPStack(first, 0, 2 * elem_size, is_float); + const bool is_wide = !is_float; + PushOntoFPStack(second, elem_size, 2 * elem_size, /* is_fp */ true, is_wide); + PushOntoFPStack(first, 0, 2 * elem_size, /* is_fp */ true, is_wide); // Loop doing FPREM until we stabilize. Label retry; diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 07476c6850..8bd3cd3585 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -174,8 +174,10 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { void GenerateMemoryBarrier(MemBarrierKind kind); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + // Push value to FPU stack. `is_fp` specifies whether the value is floating point or not. + // `is_wide` specifies whether it is long/double or not. void PushOntoFPStack(Location source, uint32_t temp_offset, - uint32_t stack_adjustment, bool is_float); + uint32_t stack_adjustment, bool is_fp, bool is_wide); void GenerateImplicitNullCheck(HNullCheck* instruction); void GenerateExplicitNullCheck(HNullCheck* instruction); diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 94990402e5..8045cc5027 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -41,6 +41,7 @@ void HDeadCodeElimination::Run() { && !inst->IsMemoryBarrier() // If we added an explicit barrier then we should keep it. && !inst->HasUses()) { block->RemoveInstruction(inst); + MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction); } } } diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 3db2c3ff3f..cee9364c84 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -19,6 +19,7 @@ #include "nodes.h" #include "optimization.h" +#include "optimizing_compiler_stats.h" namespace art { @@ -28,8 +29,10 @@ namespace art { */ class HDeadCodeElimination : public HOptimization { public: - explicit HDeadCodeElimination(HGraph* graph) - : HOptimization(graph, true, kDeadCodeEliminationPassName) {} + HDeadCodeElimination(HGraph* graph, + OptimizingCompilerStats* stats = nullptr, + const char* name = kDeadCodeEliminationPassName) + : HOptimization(graph, true, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 2216cecc2b..e743d8eca8 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -276,6 +276,17 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { id)); } } + + // If this is a nested loop, ensure the outer loops contain a superset of the blocks. + for (HLoopInformationOutwardIterator it(*loop_header); !it.Done(); it.Advance()) { + HLoopInformation* outer_info = it.Current(); + if (!loop_blocks.IsSubsetOf(&outer_info->GetBlocks())) { + AddError(StringPrintf("Blocks of loop defined by header %d are not a subset of blocks of " + "an outer loop defined by header %d.", + loop_header->GetBlockId(), + outer_info->GetHeader()->GetBlockId())); + } + } } void SSAChecker::VisitInstruction(HInstruction* instruction) { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 6d2a8d77e2..bffd639e83 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -190,7 +190,7 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, } // Run simple optimizations on the graph. - HDeadCodeElimination dce(callee_graph); + HDeadCodeElimination dce(callee_graph, stats_); HConstantFolding fold(callee_graph); InstructionSimplifier simplify(callee_graph, stats_); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index b8ae1f6369..f30c9a6cef 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -467,7 +467,8 @@ void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) { return; } - if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse()) { + if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse() && + !Primitive::IsFloatingPointType(input->GetType())) { // Replace code looking like // SUB tmp, a, b // NEG dst, tmp @@ -478,6 +479,7 @@ void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) { // worse code. In particular, we do not want the live ranges of `a` and `b` // to be extended if we are not sure the initial 'SUB' instruction can be // removed. + // We do not perform optimization for fp because we could lose the sign of zero. HSub* sub = input->AsSub(); HSub* new_sub = new (GetGraph()->GetArena()) HSub(instruction->GetType(), sub->GetRight(), sub->GetLeft()); diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 9a6062fedf..932192e4fd 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -863,7 +863,7 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = invoke->GetLocations(); // Note that the null check must have been done earlier. - DCHECK(!invoke->CanDoImplicitNullCheck()); + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); Register argument = locations->InAt(1).AsRegister<Register>(); __ cmp(argument, ShifterOperand(0)); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index d3a4e6ca15..117d6a4279 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1007,7 +1007,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = invoke->GetLocations(); // Note that the null check must have been done earlier. - DCHECK(!invoke->CanDoImplicitNullCheck()); + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); Register argument = WRegisterFrom(locations->InAt(1)); __ Cmp(argument, 0); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 95ab90de23..a8e2cdf1f6 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -962,7 +962,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = invoke->GetLocations(); // Note that the null check must have been done earlier. - DCHECK(!invoke->CanDoImplicitNullCheck()); + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); Register argument = locations->InAt(1).AsRegister<Register>(); __ testl(argument, argument); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index c369020c64..5d24d1fbfb 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -873,7 +873,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = invoke->GetLocations(); // Note that the null check must have been done earlier. - DCHECK(!invoke->CanDoImplicitNullCheck()); + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); CpuRegister argument = locations->InAt(1).AsRegister<CpuRegister>(); __ testl(argument, argument); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 5fca4fab22..4b9d4fc26b 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1064,8 +1064,10 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(current); outer_graph->reverse_post_order_.Put(++index_of_at, current); if (info != nullptr) { - info->Add(current); current->SetLoopInformation(info); + for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + loop_it.Current()->Add(current); + } } } } @@ -1075,8 +1077,10 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(to); outer_graph->reverse_post_order_.Put(++index_of_at, to); if (info != nullptr) { - info->Add(to); to->SetLoopInformation(info); + for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + loop_it.Current()->Add(to); + } if (info->IsBackEdge(*at)) { // Only `at` can become a back edge, as the inlined blocks // are predecessors of `at`. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 1565f58977..08fcdbbcdc 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1158,7 +1158,10 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { return true; } - virtual bool CanDoImplicitNullCheck() const { return false; } + virtual bool CanDoImplicitNullCheckOn(HInstruction* obj) const { + UNUSED(obj); + return false; + } void SetReferenceTypeInfo(ReferenceTypeInfo reference_type_info) { DCHECK_EQ(GetType(), Primitive::kPrimNot); @@ -2225,7 +2228,8 @@ class HInvokeStaticOrDirect : public HInvoke { invoke_type_(invoke_type), is_recursive_(is_recursive) {} - bool CanDoImplicitNullCheck() const OVERRIDE { + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + UNUSED(obj); // We access the method via the dex cache so we can't do an implicit null check. // TODO: for intrinsics we can generate implicit null checks. return false; @@ -2257,9 +2261,9 @@ class HInvokeVirtual : public HInvoke { : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), vtable_index_(vtable_index) {} - bool CanDoImplicitNullCheck() const OVERRIDE { + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { // TODO: Add implicit null checks in intrinsics. - return !GetLocations()->Intrinsified(); + return (obj == InputAt(0)) && !GetLocations()->Intrinsified(); } uint32_t GetVTableIndex() const { return vtable_index_; } @@ -2283,9 +2287,9 @@ class HInvokeInterface : public HInvoke { : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), imt_index_(imt_index) {} - bool CanDoImplicitNullCheck() const OVERRIDE { + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { // TODO: Add implicit null checks in intrinsics. - return !GetLocations()->Intrinsified(); + return (obj == InputAt(0)) && !GetLocations()->Intrinsified(); } uint32_t GetImtIndex() const { return imt_index_; } @@ -2855,8 +2859,8 @@ class HInstanceFieldGet : public HExpression<1> { return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue(); } - bool CanDoImplicitNullCheck() const OVERRIDE { - return GetFieldOffset().Uint32Value() < kPageSize; + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + return (obj == InputAt(0)) && GetFieldOffset().Uint32Value() < kPageSize; } size_t ComputeHashCode() const OVERRIDE { @@ -2889,8 +2893,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { SetRawInputAt(1, value); } - bool CanDoImplicitNullCheck() const OVERRIDE { - return GetFieldOffset().Uint32Value() < kPageSize; + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + return (obj == InputAt(0)) && GetFieldOffset().Uint32Value() < kPageSize; } const FieldInfo& GetFieldInfo() const { return field_info_; } @@ -2920,7 +2924,8 @@ class HArrayGet : public HExpression<2> { UNUSED(other); return true; } - bool CanDoImplicitNullCheck() const OVERRIDE { + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + UNUSED(obj); // TODO: We can be smarter here. // Currently, the array access is always preceded by an ArrayLength or a NullCheck // which generates the implicit null check. There are cases when these can be removed @@ -2962,7 +2967,8 @@ class HArraySet : public HTemplateInstruction<3> { return needs_type_check_; } - bool CanDoImplicitNullCheck() const OVERRIDE { + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + UNUSED(obj); // TODO: Same as for ArrayGet. return false; } @@ -3014,7 +3020,9 @@ class HArrayLength : public HExpression<1> { UNUSED(other); return true; } - bool CanDoImplicitNullCheck() const OVERRIDE { return true; } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { + return obj == InputAt(0); + } DECLARE_INSTRUCTION(ArrayLength); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 2ec8536cdf..218894fe02 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -320,7 +320,8 @@ static void RunOptimizations(HGraph* graph, const DexCompilationUnit& dex_compilation_unit, PassInfoPrinter* pass_info_printer, StackHandleScopeCollection* handles) { - HDeadCodeElimination dce(graph); + HDeadCodeElimination dce1(graph, stats); + HDeadCodeElimination dce2(graph, stats, "dead_code_elimination_final"); HConstantFolding fold1(graph); InstructionSimplifier simplify1(graph, stats); HBooleanSimplifier boolean_not(graph); @@ -339,7 +340,7 @@ static void RunOptimizations(HGraph* graph, HOptimization* optimizations[] = { &intrinsics, - &dce, + &dce1, &fold1, &simplify1, // BooleanSimplifier depends on the InstructionSimplifier removing redundant @@ -352,7 +353,8 @@ static void RunOptimizations(HGraph* graph, &licm, &bce, &type_propagation, - &simplify2 + &simplify2, + &dce2, }; RunOptimizations(optimizations, arraysize(optimizations), pass_info_printer); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 9bfa543401..e6508c9851 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -29,6 +29,7 @@ enum MethodCompilationStat { kCompiledBaseline, kCompiledOptimized, kCompiledQuick, + kInstructionSimplifications, kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, @@ -48,8 +49,8 @@ enum MethodCompilationStat { kNotCompiledVerifyAtRuntime, kNotCompiledClassNotVerified, kRemovedCheckedCast, + kRemovedDeadInstruction, kRemovedNullCheck, - kInstructionSimplifications, kLastStat }; @@ -96,6 +97,7 @@ class OptimizingCompilerStats { case kCompiledOptimized : return "kCompiledOptimized"; case kCompiledQuick : return "kCompiledQuick"; case kInlinedInvoke : return "kInlinedInvoke"; + case kInstructionSimplifications: return "kInstructionSimplifications"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod"; @@ -114,8 +116,8 @@ class OptimizingCompilerStats { case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime"; case kNotCompiledClassNotVerified : return "kNotCompiledClassNotVerified"; case kRemovedCheckedCast: return "kRemovedCheckedCast"; + case kRemovedDeadInstruction: return "kRemovedDeadInstruction"; case kRemovedNullCheck: return "kRemovedNullCheck"; - case kInstructionSimplifications: return "kInstructionSimplifications"; default: LOG(FATAL) << "invalid stat"; } return ""; diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index a171e59d98..772fa9aa4b 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -43,8 +43,6 @@ namespace arm { static constexpr bool kPrintResults = false; #endif -static const char* TOOL_PREFIX = "arm-linux-androideabi-"; - void SetAndroidData() { const char* data = getenv("ANDROID_DATA"); if (data == nullptr) { @@ -65,87 +63,6 @@ int CompareIgnoringSpace(const char* s1, const char* s2) { return *s1 - *s2; } -std::string GetAndroidToolsDir() { - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root += android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root += cwd; - free(cwd); - } - - // Look for "prebuilts" - std::string toolsdir = root; - struct stat st; - while (toolsdir != "") { - std::string prebuilts = toolsdir + "/prebuilts"; - if (stat(prebuilts.c_str(), &st) == 0) { - // Found prebuilts. - toolsdir += "/prebuilts/gcc/linux-x86/arm"; - break; - } - // Not present, move up one dir. - size_t slash = toolsdir.rfind('/'); - if (slash == std::string::npos) { - toolsdir = ""; - } else { - toolsdir = toolsdir.substr(0, slash-1); - } - } - bool statok = stat(toolsdir.c_str(), &st) == 0; - if (!statok) { - return ""; // Use path. - } - - DIR* dir = opendir(toolsdir.c_str()); - if (dir == nullptr) { - return ""; // Use path. - } - - struct dirent* entry; - std::string founddir; - double maxversion = 0; - - // Find the latest version of the arm-eabi tools (biggest version number). - // Suffix on toolsdir will be something like "arm-eabi-4.8" - while ((entry = readdir(dir)) != nullptr) { - std::string subdir = toolsdir + std::string("/") + std::string(entry->d_name); - size_t eabi = subdir.find(TOOL_PREFIX); - if (eabi != std::string::npos) { - // Check if "bin/{as,objcopy,objdump}" exist under this folder. - struct stat exec_st; - std::string exec_path; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "as"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "objcopy"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "objdump"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - - std::string suffix = subdir.substr(eabi + strlen(TOOL_PREFIX)); - double version = strtod(suffix.c_str(), nullptr); - if (version > maxversion) { - maxversion = version; - founddir = subdir; - } - } - } - closedir(dir); - bool found = founddir != ""; - if (!found) { - return ""; // Use path. - } - - return founddir + "/bin/"; -} - void dump(std::vector<uint8_t>& code, const char* testname) { // This will only work on the host. There is no as, objcopy or objdump on the // device. @@ -155,7 +72,7 @@ void dump(std::vector<uint8_t>& code, const char* testname) { if (!results_ok) { setup_results(); - toolsdir = GetAndroidToolsDir(); + toolsdir = CommonRuntimeTest::GetAndroidTargetToolsDir(kThumb2); SetAndroidData(); results_ok = true; } @@ -187,19 +104,18 @@ void dump(std::vector<uint8_t>& code, const char* testname) { char cmd[1024]; // Assemble the .S - snprintf(cmd, sizeof(cmd), "%s%sas %s -o %s.o", toolsdir.c_str(), TOOL_PREFIX, filename, filename); + snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename); system(cmd); // Remove the $d symbols to prevent the disassembler dumping the instructions // as .word - snprintf(cmd, sizeof(cmd), "%s%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), TOOL_PREFIX, - filename, filename); + snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename); system(cmd); // Disassemble. - snprintf(cmd, sizeof(cmd), "%s%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'", - toolsdir.c_str(), TOOL_PREFIX, filename); + snprintf(cmd, sizeof(cmd), "%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'", + toolsdir.c_str(), filename); if (kPrintResults) { // Print the results only, don't check. This is used to generate new output for inserting // into the .inc file. diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 7839aa8d4c..fd03002f27 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -50,6 +50,7 @@ static bool IsMethodPublic(JNIEnv* env, jclass c, jmethodID method_id) { int modifiers = env->CallIntMethod(reflected.get(), mid); static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC if ((modifiers & PUBLIC) == 0) { + fprintf(stderr, "Modifiers mismatch\n"); return false; } return true; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 70b4213d56..9c01a0f315 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -445,6 +445,8 @@ class Dex2Oat FINAL { image_classes_filename_(nullptr), compiled_classes_zip_filename_(nullptr), compiled_classes_filename_(nullptr), + compiled_methods_zip_filename_(nullptr), + compiled_methods_filename_(nullptr), image_(false), is_host_(false), dump_stats_(false), @@ -564,6 +566,10 @@ class Dex2Oat FINAL { compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data(); } else if (option.starts_with("--compiled-classes-zip=")) { compiled_classes_zip_filename_ = option.substr(strlen("--compiled-classes-zip=")).data(); + } else if (option.starts_with("--compiled-methods=")) { + compiled_methods_filename_ = option.substr(strlen("--compiled-methods=")).data(); + } else if (option.starts_with("--compiled-methods-zip=")) { + compiled_methods_zip_filename_ = option.substr(strlen("--compiled-methods-zip=")).data(); } else if (option.starts_with("--base=")) { const char* image_base_str = option.substr(strlen("--base=")).data(); char* end; @@ -1092,8 +1098,8 @@ class Dex2Oat FINAL { std::string error_msg; if (image_classes_zip_filename_ != nullptr) { image_classes_.reset(ReadImageClassesFromZip(image_classes_zip_filename_, - image_classes_filename_, - &error_msg)); + image_classes_filename_, + &error_msg)); } else { image_classes_.reset(ReadImageClassesFromFile(image_classes_filename_)); } @@ -1121,9 +1127,29 @@ class Dex2Oat FINAL { << compiled_classes_filename_ << "': " << error_msg; return false; } - } else if (image_) { + } else { compiled_classes_.reset(nullptr); // By default compile everything. } + // If --compiled-methods was specified, read the methods to compile from the given file(s). + if (compiled_methods_filename_ != nullptr) { + std::string error_msg; + if (compiled_methods_zip_filename_ != nullptr) { + compiled_methods_.reset(ReadCommentedInputFromZip(compiled_methods_zip_filename_, + compiled_methods_filename_, + nullptr, // No post-processing. + &error_msg)); + } else { + compiled_methods_.reset(ReadCommentedInputFromFile(compiled_methods_filename_, + nullptr)); // No post-processing. + } + if (compiled_methods_.get() == nullptr) { + LOG(ERROR) << "Failed to create list of compiled methods from '" + << compiled_methods_filename_ << "': " << error_msg; + return false; + } + } else { + compiled_methods_.reset(nullptr); // By default compile everything. + } if (boot_image_option_.empty()) { dex_files_ = Runtime::Current()->GetClassLinker()->GetBootClassPath(); @@ -1258,6 +1284,7 @@ class Dex2Oat FINAL { image_, image_classes_.release(), compiled_classes_.release(), + nullptr, thread_count_, dump_stats_, dump_passes_, @@ -1618,59 +1645,86 @@ class Dex2Oat FINAL { // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) static std::unordered_set<std::string>* ReadImageClassesFromFile( const char* image_classes_filename) { - std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename, - std::ifstream::in)); - if (image_classes_file.get() == nullptr) { - LOG(ERROR) << "Failed to open image classes file " << image_classes_filename; - return nullptr; - } - std::unique_ptr<std::unordered_set<std::string>> result(ReadImageClasses(*image_classes_file)); - image_classes_file->close(); - return result.release(); + std::function<std::string(const char*)> process = DotToDescriptor; + return ReadCommentedInputFromFile(image_classes_filename, &process); } - static std::unordered_set<std::string>* ReadImageClasses(std::istream& image_classes_stream) { - std::unique_ptr<std::unordered_set<std::string>> image_classes( - new std::unordered_set<std::string>); - while (image_classes_stream.good()) { - std::string dot; - std::getline(image_classes_stream, dot); - if (StartsWith(dot, "#") || dot.empty()) { - continue; - } - std::string descriptor(DotToDescriptor(dot.c_str())); - image_classes->insert(descriptor); + // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) + static std::unordered_set<std::string>* ReadImageClassesFromZip( + const char* zip_filename, + const char* image_classes_filename, + std::string* error_msg) { + std::function<std::string(const char*)> process = DotToDescriptor; + return ReadCommentedInputFromZip(zip_filename, image_classes_filename, &process, error_msg); + } + + // Read lines from the given file, dropping comments and empty lines. Post-process each line with + // the given function. + static std::unordered_set<std::string>* ReadCommentedInputFromFile( + const char* input_filename, std::function<std::string(const char*)>* process) { + std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in)); + if (input_file.get() == nullptr) { + LOG(ERROR) << "Failed to open input file " << input_filename; + return nullptr; } - return image_classes.release(); + std::unique_ptr<std::unordered_set<std::string>> result( + ReadCommentedInputStream(*input_file, process)); + input_file->close(); + return result.release(); } - // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - static std::unordered_set<std::string>* ReadImageClassesFromZip( + // Read lines from the given file from the given zip file, dropping comments and empty lines. + // Post-process each line with the given function. + static std::unordered_set<std::string>* ReadCommentedInputFromZip( const char* zip_filename, - const char* image_classes_filename, + const char* input_filename, + std::function<std::string(const char*)>* process, std::string* error_msg) { std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg)); if (zip_archive.get() == nullptr) { return nullptr; } - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg)); + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(input_filename, error_msg)); if (zip_entry.get() == nullptr) { - *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename, + *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", input_filename, zip_filename, error_msg->c_str()); return nullptr; } - std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename, - image_classes_filename, - error_msg)); - if (image_classes_file.get() == nullptr) { - *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename, + std::unique_ptr<MemMap> input_file(zip_entry->ExtractToMemMap(zip_filename, + input_filename, + error_msg)); + if (input_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", input_filename, zip_filename, error_msg->c_str()); return nullptr; } - const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()), - image_classes_file->Size()); - std::istringstream image_classes_stream(image_classes_string); - return ReadImageClasses(image_classes_stream); + const std::string input_string(reinterpret_cast<char*>(input_file->Begin()), + input_file->Size()); + std::istringstream input_stream(input_string); + return ReadCommentedInputStream(input_stream, process); + } + + // Read lines from the given stream, dropping comments and empty lines. Post-process each line + // with the given function. + static std::unordered_set<std::string>* ReadCommentedInputStream( + std::istream& in_stream, + std::function<std::string(const char*)>* process) { + std::unique_ptr<std::unordered_set<std::string>> image_classes( + new std::unordered_set<std::string>); + while (in_stream.good()) { + std::string dot; + std::getline(in_stream, dot); + if (StartsWith(dot, "#") || dot.empty()) { + continue; + } + if (process != nullptr) { + std::string descriptor((*process)(dot.c_str())); + image_classes->insert(descriptor); + } else { + image_classes->insert(dot); + } + } + return image_classes.release(); } void LogCompletionTime() { @@ -1724,8 +1778,11 @@ class Dex2Oat FINAL { const char* image_classes_filename_; const char* compiled_classes_zip_filename_; const char* compiled_classes_filename_; + const char* compiled_methods_zip_filename_; + const char* compiled_methods_filename_; std::unique_ptr<std::unordered_set<std::string>> image_classes_; std::unique_ptr<std::unordered_set<std::string>> compiled_classes_; + std::unique_ptr<std::unordered_set<std::string>> compiled_methods_; bool image_; std::unique_ptr<ImageWriter> image_writer_; bool is_host_; diff --git a/runtime/Android.mk b/runtime/Android.mk index d3488fc71f..86201ba308 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -100,11 +100,13 @@ LIBART_COMMON_SRC_FILES := \ linear_alloc.cc \ mem_map.cc \ memory_region.cc \ + mirror/abstract_method.cc \ mirror/art_method.cc \ mirror/array.cc \ mirror/class.cc \ mirror/dex_cache.cc \ mirror/field.cc \ + mirror/method.cc \ mirror/object.cc \ mirror/reference.cc \ mirror/stack_trace_element.cc \ diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index c3e24a7912..65cb02839a 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -79,6 +79,32 @@ bool BitVector::SameBitsSet(const BitVector *src) const { return (memcmp(storage_, src->GetRawStorage(), our_highest_index * kWordBytes) == 0); } +bool BitVector::IsSubsetOf(const BitVector *other) const { + int this_highest = GetHighestBitSet(); + int other_highest = other->GetHighestBitSet(); + + // If the highest bit set is -1, this is empty and a trivial subset. + if (this_highest < 0) { + return true; + } + + // If the highest bit set is higher, this cannot be a subset. + if (this_highest > other_highest) { + return false; + } + + // Compare each 32-bit word. + size_t this_highest_index = BitsToWords(this_highest + 1); + for (size_t i = 0; i < this_highest_index; ++i) { + uint32_t this_storage = storage_[i]; + uint32_t other_storage = other->storage_[i]; + if ((this_storage | other_storage) != other_storage) { + return false; + } + } + return true; +} + void BitVector::Intersect(const BitVector* src) { uint32_t src_storage_size = src->storage_size_; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 557a2ec110..be4d363bf5 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -173,6 +173,8 @@ class BitVector { */ bool SameBitsSet(const BitVector *src) const; + bool IsSubsetOf(const BitVector *other) const; + // Count the number of bits that are set. uint32_t NumSetBits() const; diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc index fe3313d122..c51b9b0570 100644 --- a/runtime/base/bit_vector_test.cc +++ b/runtime/base/bit_vector_test.cc @@ -167,4 +167,48 @@ TEST(BitVector, UnionIfNotIn) { } } +TEST(BitVector, Subset) { + { + BitVector first(2, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(4); + EXPECT_TRUE(first.IsSubsetOf(&second)); + } + + { + BitVector first(5, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + first.SetBit(5); + EXPECT_FALSE(first.IsSubsetOf(&second)); + second.SetBit(4); + EXPECT_FALSE(first.IsSubsetOf(&second)); + } + + { + BitVector first(5, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + first.SetBit(16); + first.SetBit(32); + first.SetBit(48); + second.SetBit(16); + second.SetBit(32); + second.SetBit(48); + + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(8); + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(40); + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(52); + EXPECT_TRUE(first.IsSubsetOf(&second)); + + first.SetBit(9); + EXPECT_FALSE(first.IsSubsetOf(&second)); + } +} + } // namespace art diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 4e59217af6..85b245ffd5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -59,6 +59,7 @@ #include "mirror/dex_cache-inl.h" #include "mirror/field.h" #include "mirror/iftable-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" @@ -258,8 +259,8 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b CHECK(!init_done_); // java_lang_Class comes first, it's needed for AllocClass - Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); + Thread* const self = Thread::Current(); + gc::Heap* const heap = Runtime::Current()->GetHeap(); // The GC can't handle an object with a null class since we can't get the size of this object. heap->IncrementDisableMovingGC(self); StackHandleScope<64> hs(self); // 64 is picked arbitrarily. @@ -436,20 +437,19 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Object, String and DexCache need to be rerun through FindSystemClass to finish init mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self); - mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;"); - CHECK_EQ(java_lang_Object.Get(), Object_class); + CHECK_EQ(java_lang_Object.Get(), FindSystemClass(self, "Ljava/lang/Object;")); CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self); mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;"); - std::ostringstream os1, os2; - java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); - String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); - CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str(); + if (java_lang_String.Get() != String_class) { + std::ostringstream os1, os2; + java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); + String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); + LOG(FATAL) << os1.str() << "\n\n" << os2.str(); + } CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize()); mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self); - mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;"); - CHECK_EQ(java_lang_String.Get(), String_class); - CHECK_EQ(java_lang_DexCache.Get(), DexCache_class); + CHECK_EQ(java_lang_DexCache.Get(), FindSystemClass(self, "Ljava/lang/DexCache;")); CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); // Setup the primitive array type classes - can't be done until Object has a vtable. @@ -459,17 +459,14 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); - mirror::Class* found_char_array_class = FindSystemClass(self, "[C"); - CHECK_EQ(char_array_class.Get(), found_char_array_class); + CHECK_EQ(char_array_class.Get(), FindSystemClass(self, "[C")); SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); - mirror::Class* found_int_array_class = FindSystemClass(self, "[I"); - CHECK_EQ(int_array_class.Get(), found_int_array_class); + CHECK_EQ(int_array_class.Get(), FindSystemClass(self, "[I")); - mirror::Class* found_long_array_class = FindSystemClass(self, "[J"); - CHECK_EQ(long_array_class.Get(), found_long_array_class); + CHECK_EQ(long_array_class.Get(), FindSystemClass(self, "[J")); SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); @@ -477,97 +474,101 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); - mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;"); - CHECK_EQ(class_array_class.Get(), found_class_array_class); + CHECK_EQ(class_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Class;")); - mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;"); - CHECK_EQ(object_array_class.Get(), found_object_array_class); + CHECK_EQ(object_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Object;")); // Setup the single, global copy of "iftable". - mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;"); - CHECK(java_lang_Cloneable != nullptr); - mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;"); - CHECK(java_io_Serializable != nullptr); + auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;")); + CHECK(java_lang_Cloneable.Get() != nullptr); + auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;")); + CHECK(java_io_Serializable.Get() != nullptr); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. - { - mirror::IfTable* array_iftable = array_iftable_.Read(); - array_iftable->SetInterface(0, java_lang_Cloneable); - array_iftable->SetInterface(1, java_io_Serializable); - } - - // Sanity check Class[] and Object[]'s interfaces. - CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, class_array_class, 0)); - CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, class_array_class, 1)); - CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, object_array_class, 0)); - CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, object_array_class, 1)); + array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get()); + array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get()); + + // Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread + // suspension. + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class, 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class, 1)); + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class, 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class, 1)); // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their // dex_cache_ fields and register them in class_table_. - mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;"); - CHECK_EQ(java_lang_Class.Get(), Class_class); + CHECK_EQ(java_lang_Class.Get(), FindSystemClass(self, "Ljava/lang/Class;")); mirror::Class::SetStatus(java_lang_reflect_ArtMethod, mirror::Class::kStatusNotReady, self); - mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;"); - CHECK_EQ(java_lang_reflect_ArtMethod.Get(), Art_method_class); - - mirror::Class* String_array_class = - FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass)); - CHECK_EQ(object_array_string.Get(), String_array_class); - - mirror::Class* Art_method_array_class = - FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass)); - CHECK_EQ(object_array_art_method.Get(), Art_method_array_class); + CHECK_EQ(java_lang_reflect_ArtMethod.Get(), + FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;")); + CHECK_EQ(object_array_string.Get(), + FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); + CHECK_EQ(object_array_art_method.Get(), + FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass))); // End of special init trickery, subsequent classes may be loaded via FindSystemClass. // Create java.lang.reflect.Proxy root. - mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;"); - SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); + SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); // Create java.lang.reflect.Field.class root. - mirror::Class* java_lang_reflect_Field = FindSystemClass(self, "Ljava/lang/reflect/Field;"); - CHECK(java_lang_reflect_Field != nullptr); - SetClassRoot(kJavaLangReflectField, java_lang_reflect_Field); - mirror::Field::SetClass(java_lang_reflect_Field); + auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectField, class_root); + mirror::Field::SetClass(class_root); // Create java.lang.reflect.Field array root. - mirror::Class* java_lang_reflect_Field_array = - FindSystemClass(self, "[Ljava/lang/reflect/Field;"); - CHECK(java_lang_reflect_Field_array != nullptr); - SetClassRoot(kJavaLangReflectFieldArrayClass, java_lang_reflect_Field_array); - mirror::Field::SetArrayClass(java_lang_reflect_Field_array); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectFieldArrayClass, class_root); + mirror::Field::SetArrayClass(class_root); + + // Create java.lang.reflect.Constructor.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectConstructor, class_root); + mirror::Constructor::SetClass(class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root); + mirror::Constructor::SetArrayClass(class_root); + + // Create java.lang.reflect.Method.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectMethod, class_root); + mirror::Method::SetClass(class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangReflectMethodArrayClass, class_root); + mirror::Method::SetArrayClass(class_root); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self); - mirror::Class* Reference_class = FindSystemClass(self, "Ljava/lang/ref/Reference;"); - CHECK_EQ(java_lang_ref_Reference.Get(), Reference_class); + CHECK_EQ(java_lang_ref_Reference.Get(), FindSystemClass(self, "Ljava/lang/ref/Reference;")); CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize()); - mirror::Class* java_lang_ref_FinalizerReference = - FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); - java_lang_ref_FinalizerReference->SetAccessFlags( - java_lang_ref_FinalizerReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsFinalizerReference); - mirror::Class* java_lang_ref_PhantomReference = - FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); - java_lang_ref_PhantomReference->SetAccessFlags( - java_lang_ref_PhantomReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsPhantomReference); - mirror::Class* java_lang_ref_SoftReference = - FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); - java_lang_ref_SoftReference->SetAccessFlags( - java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference); - mirror::Class* java_lang_ref_WeakReference = - FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); - java_lang_ref_WeakReference->SetAccessFlags( - java_lang_ref_WeakReference->GetAccessFlags() | - kAccClassIsReference | kAccClassIsWeakReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | + kAccClassIsReference | kAccClassIsFinalizerReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | + kAccClassIsPhantomReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); + class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | + kAccClassIsWeakReference); // Setup the ClassLoader, verifying the object_size_. - mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;"); - CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize()); - SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader); + class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); + CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); + SetClassRoot(kJavaLangClassLoader, class_root); // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and // java.lang.StackTraceElement as a convenience. @@ -911,6 +912,10 @@ void ClassLinker::InitFromImage() { // String class root was set above mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); + mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); + mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); + mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); + mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); @@ -1096,22 +1101,26 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar } ClassLinker::~ClassLinker() { - mirror::Class::ResetClass(); - mirror::String::ResetClass(); - mirror::Reference::ResetClass(); mirror::ArtMethod::ResetClass(); + mirror::Class::ResetClass(); + mirror::Constructor::ResetClass(); mirror::Field::ResetClass(); - mirror::Field::ResetArrayClass(); + mirror::Method::ResetClass(); + mirror::Reference::ResetClass(); + mirror::StackTraceElement::ResetClass(); + mirror::String::ResetClass(); + mirror::Throwable::ResetClass(); mirror::BooleanArray::ResetArrayClass(); mirror::ByteArray::ResetArrayClass(); mirror::CharArray::ResetArrayClass(); + mirror::Constructor::ResetArrayClass(); mirror::DoubleArray::ResetArrayClass(); + mirror::Field::ResetArrayClass(); mirror::FloatArray::ResetArrayClass(); + mirror::Method::ResetArrayClass(); mirror::IntArray::ResetArrayClass(); mirror::LongArray::ResetArrayClass(); mirror::ShortArray::ResetArrayClass(); - mirror::Throwable::ResetClass(); - mirror::StackTraceElement::ResetClass(); STLDeleteElements(&oat_files_); } @@ -2947,7 +2956,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& jobjectArray interfaces, jobject loader, jobjectArray methods, jobjectArray throws) { Thread* self = soa.Self(); - StackHandleScope<8> hs(self); + StackHandleScope<9> hs(self); MutableHandle<mirror::Class> klass(hs.NewHandle( AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); if (klass.Get() == nullptr) { @@ -3001,8 +3010,10 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& } // Create virtual method using specified prototypes. - size_t num_virtual_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods)->GetLength(); + auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods)); + DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass()) + << PrettyClass(h_methods->GetClass()); + const size_t num_virtual_methods = h_methods->GetLength(); { mirror::ObjectArray<mirror::ArtMethod>* virtuals = AllocArtMethodArray(self, num_virtual_methods); @@ -3014,9 +3025,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& } for (size_t i = 0; i < num_virtual_methods; ++i) { StackHandleScope<1> hs2(self); - mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); - Handle<mirror::ArtMethod> prototype(hs2.NewHandle(decoded_methods->Get(i))); + Handle<mirror::ArtMethod> prototype(hs2.NewHandle(h_methods->Get(i)->GetArtMethod())); mirror::ArtMethod* clone = CreateProxyMethod(self, klass, prototype); if (UNLIKELY(clone == nullptr)) { CHECK(self->IsExceptionPending()); // OOME. @@ -3066,9 +3075,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& CheckProxyConstructor(klass->GetDirectMethod(0)); for (size_t i = 0; i < num_virtual_methods; ++i) { StackHandleScope<2> hs2(self); - mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); - Handle<mirror::ArtMethod> prototype(hs2.NewHandle(decoded_methods->Get(i))); + Handle<mirror::ArtMethod> prototype(hs2.NewHandle(h_methods->Get(i)->GetArtMethod())); Handle<mirror::ArtMethod> virtual_method(hs2.NewHandle(klass->GetVirtualMethod(i))); CheckProxyMethod(virtual_method, prototype); } @@ -3104,23 +3111,22 @@ mirror::ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, mirror::ArtMethod* proxy_method) { DCHECK(proxy_class->IsProxyClass()); DCHECK(proxy_method->IsProxyMethod()); - // Locate the dex cache of the original interface/Object - mirror::DexCache* dex_cache = nullptr; { ReaderMutexLock mu(Thread::Current(), dex_lock_); - for (size_t i = 0; i != dex_caches_.size(); ++i) { - mirror::DexCache* a_dex_cache = GetDexCache(i); - if (proxy_method->HasSameDexCacheResolvedTypes(a_dex_cache->GetResolvedTypes())) { - dex_cache = a_dex_cache; - break; + // Locate the dex cache of the original interface/Object + for (const GcRoot<mirror::DexCache>& root : dex_caches_) { + auto* dex_cache = root.Read(); + if (proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes())) { + mirror::ArtMethod* resolved_method = dex_cache->GetResolvedMethod( + proxy_method->GetDexMethodIndex()); + CHECK(resolved_method != nullptr); + return resolved_method; } } } - CHECK(dex_cache != nullptr); - uint32_t method_idx = proxy_method->GetDexMethodIndex(); - mirror::ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx); - CHECK(resolved_method != nullptr); - return resolved_method; + LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " " + << PrettyMethod(proxy_method); + UNREACHABLE(); } @@ -3163,8 +3169,11 @@ mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, Handle<mirror::ArtMethod> prototype) { // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden // prototype method - prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(), - prototype.Get()); + auto* dex_cache = prototype->GetDeclaringClass()->GetDexCache(); + // Avoid dirtying the dex cache unless we need to. + if (dex_cache->GetResolvedMethod(prototype->GetDexMethodIndex()) != prototype.Get()) { + dex_cache->SetResolvedMethod(prototype->GetDexMethodIndex(), prototype.Get()); + } // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize // as necessary mirror::ArtMethod* method = down_cast<mirror::ArtMethod*>(prototype->Clone(self)); @@ -3198,6 +3207,7 @@ static void CheckProxyMethod(Handle<mirror::ArtMethod> method, // interface prototype. The exception to this are Constructors and the Class of the Proxy itself. CHECK(prototype->HasSameDexCacheResolvedMethods(method.Get())); CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get())); + CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), method->GetDexCache()); CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex()); CHECK_STREQ(method->GetName(), prototype->GetName()); @@ -5210,11 +5220,15 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { "Ljava/lang/DexCache;", "Ljava/lang/ref/Reference;", "Ljava/lang/reflect/ArtMethod;", + "Ljava/lang/reflect/Constructor;", "Ljava/lang/reflect/Field;", + "Ljava/lang/reflect/Method;", "Ljava/lang/reflect/Proxy;", "[Ljava/lang/String;", "[Ljava/lang/reflect/ArtMethod;", + "[Ljava/lang/reflect/Constructor;", "[Ljava/lang/reflect/Field;", + "[Ljava/lang/reflect/Method;", "Ljava/lang/ClassLoader;", "Ljava/lang/Throwable;", "Ljava/lang/ClassNotFoundException;", diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 68624b08b6..d7c625da83 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -71,11 +71,15 @@ class ClassLinker { kJavaLangDexCache, kJavaLangRefReference, kJavaLangReflectArtMethod, + kJavaLangReflectConstructor, kJavaLangReflectField, + kJavaLangReflectMethod, kJavaLangReflectProxy, kJavaLangStringArrayClass, kJavaLangReflectArtMethodArrayClass, + kJavaLangReflectConstructorArrayClass, kJavaLangReflectFieldArrayClass, + kJavaLangReflectMethodArrayClass, kJavaLangClassLoader, kJavaLangThrowable, kJavaLangClassNotFoundException, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index a31a7852c7..7bee98f8f1 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -25,6 +25,7 @@ #include "dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" +#include "mirror/abstract_method.h" #include "mirror/accessible_object.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" @@ -463,6 +464,10 @@ struct CheckOffsets { return !error; }; + void addOffset(size_t offset, const char* name) { + offsets.push_back(CheckOffset(offset, name)); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(CheckOffsets); }; @@ -472,142 +477,162 @@ struct CheckOffsets { struct ObjectOffsets : public CheckOffsets<mirror::Object> { ObjectOffsets() : CheckOffsets<mirror::Object>(false, "Ljava/lang/Object;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, klass_), "shadow$_klass_")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, monitor_), "shadow$_monitor_")); + addOffset(OFFSETOF_MEMBER(mirror::Object, klass_), "shadow$_klass_"); + addOffset(OFFSETOF_MEMBER(mirror::Object, monitor_), "shadow$_monitor_"); #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, x_rb_ptr_), "shadow$_x_rb_ptr_")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Object, x_xpadding_), "shadow$_x_xpadding_")); + addOffset(OFFSETOF_MEMBER(mirror::Object, x_rb_ptr_), "shadow$_x_rb_ptr_"); + addOffset(OFFSETOF_MEMBER(mirror::Object, x_xpadding_), "shadow$_x_xpadding_"); #endif }; }; struct ArtMethodOffsets : public CheckOffsets<mirror::ArtMethod> { ArtMethodOffsets() : CheckOffsets<mirror::ArtMethod>(false, "Ljava/lang/reflect/ArtMethod;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_), "accessFlags")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, declaring_class_), "declaringClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_methods_), "dexCacheResolvedMethods")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_types_), "dexCacheResolvedTypes")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_code_item_offset_), "dexCodeItemOffset")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_method_index_), "dexMethodIndex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, method_index_), "methodIndex")); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_), "accessFlags"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, declaring_class_), "declaringClass"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_methods_), + "dexCacheResolvedMethods"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_types_), + "dexCacheResolvedTypes"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_code_item_offset_), "dexCodeItemOffset"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_method_index_), "dexMethodIndex"); + addOffset(OFFSETOF_MEMBER(mirror::ArtMethod, method_index_), "methodIndex"); }; }; struct ClassOffsets : public CheckOffsets<mirror::Class> { ClassOffsets() : CheckOffsets<mirror::Class>(false, "Ljava/lang/Class;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_), "accessFlags")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_loader_), "classLoader")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_), "clinitThreadId")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_instance_fields_), "numInstanceFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_instance_fields_), "numReferenceInstanceFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_static_fields_), "numReferenceStaticFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_static_fields_), "numStaticFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, object_size_), "objectSize")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, primitive_type_), "primitiveType")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_instance_offsets_), "referenceInstanceOffsets")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, status_), "status")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_class_), "verifyErrorClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_), "virtualMethods")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, vtable_), "vtable")); + addOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_), "accessFlags"); + addOffset(OFFSETOF_MEMBER(mirror::Class, class_loader_), "classLoader"); + addOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize"); + addOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_), "clinitThreadId"); + addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType"); + addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache"); + addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings"); + addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex"); + addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex"); + addOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods"); + addOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable"); + addOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name"); + addOffset(OFFSETOF_MEMBER(mirror::Class, num_instance_fields_), "numInstanceFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_instance_fields_), + "numReferenceInstanceFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_static_fields_), + "numReferenceStaticFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, num_static_fields_), "numStaticFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, object_size_), "objectSize"); + addOffset(OFFSETOF_MEMBER(mirror::Class, primitive_type_), "primitiveType"); + addOffset(OFFSETOF_MEMBER(mirror::Class, reference_instance_offsets_), + "referenceInstanceOffsets"); + addOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields"); + addOffset(OFFSETOF_MEMBER(mirror::Class, status_), "status"); + addOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass"); + addOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_class_), "verifyErrorClass"); + addOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_), "virtualMethods"); + addOffset(OFFSETOF_MEMBER(mirror::Class, vtable_), "vtable"); }; }; struct StringOffsets : public CheckOffsets<mirror::String> { StringOffsets() : CheckOffsets<mirror::String>(false, "Ljava/lang/String;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, count_), "count")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hashCode")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, offset_), "offset")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::String, array_), "value")); + addOffset(OFFSETOF_MEMBER(mirror::String, count_), "count"); + addOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hashCode"); + addOffset(OFFSETOF_MEMBER(mirror::String, offset_), "offset"); + addOffset(OFFSETOF_MEMBER(mirror::String, array_), "value"); }; }; struct ThrowableOffsets : public CheckOffsets<mirror::Throwable> { ThrowableOffsets() : CheckOffsets<mirror::Throwable>(false, "Ljava/lang/Throwable;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, cause_), "cause")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, detail_message_), "detailMessage")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_state_), "stackState")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_trace_), "stackTrace")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Throwable, suppressed_exceptions_), "suppressedExceptions")); + addOffset(OFFSETOF_MEMBER(mirror::Throwable, cause_), "cause"); + addOffset(OFFSETOF_MEMBER(mirror::Throwable, detail_message_), "detailMessage"); + addOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_state_), "stackState"); + addOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_trace_), "stackTrace"); + addOffset(OFFSETOF_MEMBER(mirror::Throwable, suppressed_exceptions_), "suppressedExceptions"); }; }; struct StackTraceElementOffsets : public CheckOffsets<mirror::StackTraceElement> { - StackTraceElementOffsets() : CheckOffsets<mirror::StackTraceElement>(false, "Ljava/lang/StackTraceElement;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, declaring_class_), "declaringClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, file_name_), "fileName")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, line_number_), "lineNumber")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, method_name_), "methodName")); + StackTraceElementOffsets() : CheckOffsets<mirror::StackTraceElement>( + false, "Ljava/lang/StackTraceElement;") { + addOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, declaring_class_), "declaringClass"); + addOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, file_name_), "fileName"); + addOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, line_number_), "lineNumber"); + addOffset(OFFSETOF_MEMBER(mirror::StackTraceElement, method_name_), "methodName"); }; }; struct ClassLoaderOffsets : public CheckOffsets<mirror::ClassLoader> { ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, packages_), "packages")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, parent_), "parent")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassLoader, proxyCache_), "proxyCache")); + addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, packages_), "packages"); + addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, parent_), "parent"); + addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, proxyCache_), "proxyCache"); }; }; struct ProxyOffsets : public CheckOffsets<mirror::Proxy> { ProxyOffsets() : CheckOffsets<mirror::Proxy>(false, "Ljava/lang/reflect/Proxy;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Proxy, h_), "h")); + addOffset(OFFSETOF_MEMBER(mirror::Proxy, h_), "h"); }; }; struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> { DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings")); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings"); }; }; struct ReferenceOffsets : public CheckOffsets<mirror::Reference> { ReferenceOffsets() : CheckOffsets<mirror::Reference>(false, "Ljava/lang/ref/Reference;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, pending_next_), "pendingNext")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, queue_), "queue")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, queue_next_), "queueNext")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Reference, referent_), "referent")); + addOffset(OFFSETOF_MEMBER(mirror::Reference, pending_next_), "pendingNext"); + addOffset(OFFSETOF_MEMBER(mirror::Reference, queue_), "queue"); + addOffset(OFFSETOF_MEMBER(mirror::Reference, queue_next_), "queueNext"); + addOffset(OFFSETOF_MEMBER(mirror::Reference, referent_), "referent"); }; }; struct FinalizerReferenceOffsets : public CheckOffsets<mirror::FinalizerReference> { - FinalizerReferenceOffsets() : CheckOffsets<mirror::FinalizerReference>(false, "Ljava/lang/ref/FinalizerReference;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, next_), "next")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, prev_), "prev")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, zombie_), "zombie")); + FinalizerReferenceOffsets() : CheckOffsets<mirror::FinalizerReference>( + false, "Ljava/lang/ref/FinalizerReference;") { + addOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, next_), "next"); + addOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, prev_), "prev"); + addOffset(OFFSETOF_MEMBER(mirror::FinalizerReference, zombie_), "zombie"); }; }; struct AccessibleObjectOffsets : public CheckOffsets<mirror::AccessibleObject> { - AccessibleObjectOffsets() : CheckOffsets<mirror::AccessibleObject>(false, "Ljava/lang/reflect/AccessibleObject;") { - offsets.push_back(CheckOffset(mirror::AccessibleObject::FlagOffset().Uint32Value(), "flag")); + AccessibleObjectOffsets() : CheckOffsets<mirror::AccessibleObject>( + false, "Ljava/lang/reflect/AccessibleObject;") { + addOffset(mirror::AccessibleObject::FlagOffset().Uint32Value(), "flag"); }; }; struct FieldOffsets : public CheckOffsets<mirror::Field> { FieldOffsets() : CheckOffsets<mirror::Field>(false, "Ljava/lang/reflect/Field;") { - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, access_flags_), "accessFlags")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, declaring_class_), "declaringClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, dex_field_index_), "dexFieldIndex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, offset_), "offset")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, type_), "type")); + addOffset(OFFSETOF_MEMBER(mirror::Field, access_flags_), "accessFlags"); + addOffset(OFFSETOF_MEMBER(mirror::Field, declaring_class_), "declaringClass"); + addOffset(OFFSETOF_MEMBER(mirror::Field, dex_field_index_), "dexFieldIndex"); + addOffset(OFFSETOF_MEMBER(mirror::Field, offset_), "offset"); + addOffset(OFFSETOF_MEMBER(mirror::Field, type_), "type"); + }; +}; + +struct AbstractMethodOffsets : public CheckOffsets<mirror::AbstractMethod> { + AbstractMethodOffsets() : CheckOffsets<mirror::AbstractMethod>( + false, "Ljava/lang/reflect/AbstractMethod;") { + addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, access_flags_), "accessFlags"); + addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, art_method_), "artMethod"); + addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, declaring_class_), "declaringClass"); + addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, declaring_class_of_overridden_method_), + "declaringClassOfOverriddenMethod"); + addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, dex_method_index_), "dexMethodIndex"); }; }; @@ -629,6 +654,7 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(FinalizerReferenceOffsets().Check()); EXPECT_TRUE(AccessibleObjectOffsets().Check()); EXPECT_TRUE(FieldOffsets().Check()); + EXPECT_TRUE(AbstractMethodOffsets().Check()); } TEST_F(ClassLinkerTest, FindClassNonexistent) { diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 60b7fa218f..e17b885f21 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -16,6 +16,7 @@ #include "common_runtime_test.h" +#include <cstdio> #include <dirent.h> #include <dlfcn.h> #include <fcntl.h> @@ -188,6 +189,82 @@ void CommonRuntimeTest::TearDownAndroidData(const std::string& android_data, boo } } +// Helper - find directory with the following format: +// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ +static std::string GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3) { + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root = android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root = cwd; + free(cwd); + } + + std::string toolsdir = root + "/" + subdir1; + std::string founddir; + DIR* dir; + if ((dir = opendir(toolsdir.c_str())) != nullptr) { + float maxversion = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string format = subdir2 + "-%f"; + float version; + if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { + if (version > maxversion) { + maxversion = version; + founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; + } + } + } + closedir(dir); + } + + if (founddir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory."; + } + return founddir; +} + +std::string CommonRuntimeTest::GetAndroidHostToolsDir() { + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", + "x86_64-linux-glibc2.15", + "x86_64-linux"); +} + +std::string CommonRuntimeTest::GetAndroidTargetToolsDir(InstructionSet isa) { + switch (isa) { + case kArm: + case kThumb2: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/arm", + "arm-linux-androideabi", + "arm-linux-androideabi"); + case kArm64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/aarch64", + "aarch64-linux-android", + "aarch64-linux-android"); + case kX86: + case kX86_64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/x86", + "x86_64-linux-android", + "x86_64-linux-android"); + case kMips: + case kMips64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/mips", + "mips64el-linux-android", + "mips64el-linux-android"); + case kNone: + break; + } + ADD_FAILURE() << "Invalid isa " << isa; + return ""; +} + std::string CommonRuntimeTest::GetCoreArtLocation() { return GetCoreFileLocation("art"); } diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 5fbc2ee680..991737893a 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -22,6 +22,7 @@ #include <string> +#include "arch/instruction_set.h" #include "base/mutex.h" #include "globals.h" #include "os.h" @@ -79,6 +80,12 @@ class CommonRuntimeTest : public testing::Test { // Gets the path of the libcore dex file. static std::string GetLibCoreDexFileName(); + // Returns bin directory which contains host's prebuild tools. + static std::string GetAndroidHostToolsDir(); + + // Returns bin directory which contains target's prebuild tools. + static std::string GetAndroidTargetToolsDir(InstructionSet isa); + protected: static bool IsHost() { return !kIsTargetBuild; diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index f2b013f864..37e391d49f 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1606,9 +1606,6 @@ bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word, if (base_address_delta == 0) { return true; } - if (!ApplyOatPatchesTo(".eh_frame", base_address_delta)) { - return false; - } if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) { return false; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 1d8df68994..768f5052d4 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -23,6 +23,7 @@ #include "gc/accounting/card_table-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "reflection.h" @@ -257,7 +258,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons } } - // Call Proxy.invoke(Proxy proxy, ArtMethod method, Object[] args). + // Call Proxy.invoke(Proxy proxy, Method method, Object[] args). jvalue invocation_args[3]; invocation_args[0].l = rcvr_jobj; invocation_args[1].l = interface_method_jobj; @@ -274,10 +275,9 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons return zero; } else { StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ArtMethod> h_interface_method( - hs.NewHandle(soa.Decode<mirror::ArtMethod*>(interface_method_jobj))); + auto h_interface_method(hs.NewHandle(soa.Decode<mirror::Method*>(interface_method_jobj))); // This can cause thread suspension. - mirror::Class* result_type = h_interface_method->GetReturnType(); + mirror::Class* result_type = h_interface_method->GetArtMethod()->GetReturnType(); mirror::Object* result_ref = soa.Decode<mirror::Object*>(result); JValue result_unboxed; if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) { @@ -293,10 +293,9 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons if (exception->IsCheckedException()) { mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj); mirror::Class* proxy_class = rcvr->GetClass(); - mirror::ArtMethod* interface_method = - soa.Decode<mirror::ArtMethod*>(interface_method_jobj); + mirror::Method* interface_method = soa.Decode<mirror::Method*>(interface_method_jobj); mirror::ArtMethod* proxy_method = - rcvr->GetClass()->FindVirtualMethodForInterface(interface_method); + rcvr->GetClass()->FindVirtualMethodForInterface(interface_method->GetArtMethod()); int throws_index = -1; size_t num_virt_methods = proxy_class->NumVirtualMethods(); for (size_t i = 0; i < num_virt_methods; i++) { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2e813c808a..2e7e2dfd74 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -26,6 +26,7 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "runtime.h" @@ -760,11 +761,12 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, mirror::ArtMethod* interface_method = proxy_method->FindOverriddenMethod(); DCHECK(interface_method != nullptr) << PrettyMethod(proxy_method); DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); - jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method); + self->EndAssertNoThreadSuspension(old_cause); + jobject interface_method_jobj = soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod(soa.Self(), interface_method)); // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code // that performs allocations. - self->EndAssertNoThreadSuspension(old_cause); JValue result = InvokeProxyInvocationHandler(soa, shorty, rcvr_jobj, interface_method_jobj, args); // Restore references which might have moved. local_ref_visitor.FixupReferences(); diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 1f1f9e8b96..4c5fc8184f 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -236,11 +236,6 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { if (strong != nullptr) { return strong; } - // Check the image for a match. - mirror::String* image = LookupStringFromImage(s); - if (image != nullptr) { - return is_strong ? InsertStrong(image) : InsertWeak(image); - } // There is no match in the strong table, check the weak table. mirror::String* weak = LookupWeak(s); if (weak != nullptr) { @@ -251,6 +246,11 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { } return weak; } + // Check the image for a match. + mirror::String* image = LookupStringFromImage(s); + if (image != nullptr) { + return is_strong ? InsertStrong(image) : InsertWeak(image); + } // No match in the strong table or the weak table. Insert into the strong / weak table. return is_strong ? InsertStrong(s) : InsertWeak(s); } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 8a5461baee..554a28d3f4 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -42,6 +42,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -361,19 +362,13 @@ class JNI { ScopedObjectAccess soa(env); mirror::ArtMethod* m = soa.DecodeMethod(mid); CHECK(!kMovingMethods); - ScopedLocalRef<jobject> art_method(env, soa.AddLocalReference<jobject>(m)); - jobject reflect_method; + mirror::AbstractMethod* method; if (m->IsConstructor()) { - reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor); + method = mirror::Constructor::CreateFromArtMethod(soa.Self(), m); } else { - reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); + method = mirror::Method::CreateFromArtMethod(soa.Self(), m); } - if (env->ExceptionCheck()) { - return nullptr; - } - SetObjectField(env, reflect_method, - WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method.get()); - return reflect_method; + return soa.AddLocalReference<jobject>(method); } static jobject ToReflectedField(JNIEnv* env, jclass, jfieldID fid, jboolean) { diff --git a/runtime/mirror/abstract_method.cc b/runtime/mirror/abstract_method.cc new file mode 100644 index 0000000000..81c656b6fa --- /dev/null +++ b/runtime/mirror/abstract_method.cc @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "abstract_method.h" + +#include "mirror/art_method-inl.h" + +namespace art { +namespace mirror { + +bool AbstractMethod::CreateFromArtMethod(mirror::ArtMethod* method) { + auto* interface_method = method->GetInterfaceMethodIfProxy(); + SetFieldObject<false>(ArtMethodOffset(), method); + SetFieldObject<false>(DeclaringClassOffset(), method->GetDeclaringClass()); + SetFieldObject<false>( + DeclaringClassOfOverriddenMethodOffset(), interface_method->GetDeclaringClass()); + SetField32<false>(AccessFlagsOffset(), method->GetAccessFlags()); + SetField32<false>(DexMethodIndexOffset(), method->GetDexMethodIndex()); + return true; +} + +mirror::ArtMethod* AbstractMethod::GetArtMethod() { + return GetFieldObject<mirror::ArtMethod>(ArtMethodOffset()); +} + +mirror::Class* AbstractMethod::GetDeclaringClass() { + return GetFieldObject<mirror::Class>(DeclaringClassOffset()); +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/abstract_method.h b/runtime/mirror/abstract_method.h new file mode 100644 index 0000000000..ef51d7f4ae --- /dev/null +++ b/runtime/mirror/abstract_method.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_ +#define ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_ + +#include "accessible_object.h" +#include "gc_root.h" +#include "object.h" +#include "object_callbacks.h" +#include "read_barrier_option.h" + +namespace art { + +struct AbstractMethodOffsets; + +namespace mirror { + +class ArtMethod; + +// C++ mirror of java.lang.reflect.AbstractMethod. +class MANAGED AbstractMethod : public AccessibleObject { + public: + // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod. + bool CreateFromArtMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::ArtMethod* GetArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Class* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + static MemberOffset ArtMethodOffset() { + return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, art_method_)); + } + static MemberOffset DeclaringClassOffset() { + return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, declaring_class_)); + } + static MemberOffset DeclaringClassOfOverriddenMethodOffset() { + return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, declaring_class_of_overridden_method_)); + } + static MemberOffset AccessFlagsOffset() { + return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, access_flags_)); + } + static MemberOffset DexMethodIndexOffset() { + return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, dex_method_index_)); + } + + HeapReference<mirror::ArtMethod> art_method_; + HeapReference<mirror::Class> declaring_class_; + HeapReference<mirror::Class> declaring_class_of_overridden_method_; + uint32_t access_flags_; + uint32_t dex_method_index_; + + friend struct art::AbstractMethodOffsets; // for verifying offset information + DISALLOW_IMPLICIT_CONSTRUCTORS(AbstractMethod); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_ diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index a300d5237e..5fc96ad961 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -36,7 +36,7 @@ namespace art { namespace mirror { inline uint32_t ArtMethod::ClassSize() { - uint32_t vtable_entries = Object::kVTableLength + 7; + uint32_t vtable_entries = Object::kVTableLength; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0); } diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 92aea1f3cc..9483ba6b4b 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -16,6 +16,7 @@ #include "art_method.h" +#include "abstract_method.h" #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -53,14 +54,11 @@ GcRoot<Class> ArtMethod::java_lang_reflect_ArtMethod_; ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { - ArtField* f = - soa.DecodeField(WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod); - mirror::ArtMethod* method = f->GetObject(soa.Decode<mirror::Object*>(jlr_method))->AsArtMethod(); - DCHECK(method != nullptr); - return method; + auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(jlr_method); + DCHECK(abstract_method != nullptr); + return abstract_method->GetArtMethod(); } - void ArtMethod::VisitRoots(RootVisitor* visitor) { java_lang_reflect_ArtMethod_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); } @@ -547,5 +545,31 @@ void ArtMethod::UnregisterNative() { RegisterNative(GetJniDlsymLookupStub(), false); } +bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) { + auto* dex_cache = GetDexCache(); + auto* dex_file = dex_cache->GetDexFile(); + const auto& method_id = dex_file->GetMethodId(GetDexMethodIndex()); + const auto& proto_id = dex_file->GetMethodPrototype(method_id); + const DexFile::TypeList* proto_params = dex_file->GetProtoParameters(proto_id); + auto count = proto_params != nullptr ? proto_params->Size() : 0u; + auto param_len = params.Get() != nullptr ? params->GetLength() : 0u; + if (param_len != count) { + return false; + } + auto* cl = Runtime::Current()->GetClassLinker(); + for (size_t i = 0; i < count; ++i) { + auto type_idx = proto_params->GetTypeItem(i).type_idx_; + auto* type = cl->ResolveType(type_idx, this); + if (type == nullptr) { + Thread::Current()->AssertPendingException(); + return false; + } + if (type != params->GetWithoutChecks(i)) { + return false; + } + } + return true; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 55b8068d99..b899b25cc9 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -541,6 +541,10 @@ class MANAGED ArtMethod FINAL : public Object { ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // May cause thread suspension due to class resolution. + bool EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static size_t SizeWithoutPointerFields(size_t pointer_size) { size_t total = sizeof(ArtMethod) - sizeof(PtrSizedFields); #ifdef ART_METHOD_HAS_PADDING_FIELD_ON_64_BIT diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 2afb4af0e3..1739019755 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -25,6 +25,7 @@ #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" +#include "method.h" #include "object_array-inl.h" #include "object-inl.h" #include "runtime.h" @@ -876,5 +877,26 @@ bool Class::ProxyDescriptorEquals(const char* match) { return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this) == match; } +mirror::ArtMethod* Class::GetDeclaredConstructor( + Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args) { + auto* direct_methods = GetDirectMethods(); + size_t count = direct_methods != nullptr ? direct_methods->GetLength() : 0u; + for (size_t i = 0; i < count; ++i) { + auto* m = direct_methods->GetWithoutChecks(i); + // Skip <clinit> which is a static constructor, as well as non constructors. + if (m->IsStatic() || !m->IsConstructor()) { + continue; + } + // May cause thread suspension and exceptions. + if (m->EqualParameters(args)) { + return m; + } + if (self->IsExceptionPending()) { + return nullptr; + } + } + return nullptr; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 20f23877e6..50053468db 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -47,6 +47,7 @@ namespace mirror { class ArtMethod; class ClassLoader; +class Constructor; class DexCache; class IfTable; @@ -1052,6 +1053,11 @@ class MANAGED Class FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_); } + // May cause thread suspension due to EqualParameters. + mirror::ArtMethod* GetDeclaredConstructor( + Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore // fence. class InitializeClassVisitor { diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h index 9988f84a72..d927f0c258 100644 --- a/runtime/mirror/field.h +++ b/runtime/mirror/field.h @@ -82,15 +82,12 @@ class MANAGED Field : public AccessibleObject { } static void SetClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - static void SetArrayClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void ResetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void SetArrayClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void ResetArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Slow, try to use only for PrettyField and such. ArtField* GetArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc new file mode 100644 index 0000000000..81530bb130 --- /dev/null +++ b/runtime/mirror/method.cc @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "method.h" + +#include "mirror/art_method.h" +#include "mirror/object-inl.h" + +namespace art { +namespace mirror { + +GcRoot<Class> Method::static_class_; +GcRoot<Class> Method::array_class_; +GcRoot<Class> Constructor::static_class_; +GcRoot<Class> Constructor::array_class_; + +void Method::SetClass(Class* klass) { + CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; + CHECK(klass != nullptr); + static_class_ = GcRoot<Class>(klass); +} + +void Method::ResetClass() { + CHECK(!static_class_.IsNull()); + static_class_ = GcRoot<Class>(nullptr); +} + +void Method::SetArrayClass(Class* klass) { + CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; + CHECK(klass != nullptr); + array_class_ = GcRoot<Class>(klass); +} + +void Method::ResetArrayClass() { + CHECK(!array_class_.IsNull()); + array_class_ = GcRoot<Class>(nullptr); +} + +Method* Method::CreateFromArtMethod(Thread* self, mirror::ArtMethod* method) { + DCHECK(!method->IsConstructor()) << PrettyMethod(method); + auto* ret = down_cast<Method*>(StaticClass()->AllocObject(self)); + if (LIKELY(ret != nullptr)) { + static_cast<AbstractMethod*>(ret)->CreateFromArtMethod(method); + } + return ret; +} + +void Method::VisitRoots(RootVisitor* visitor) { + static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); + array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); +} + +void Constructor::SetClass(Class* klass) { + CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; + CHECK(klass != nullptr); + static_class_ = GcRoot<Class>(klass); +} + +void Constructor::ResetClass() { + CHECK(!static_class_.IsNull()); + static_class_ = GcRoot<Class>(nullptr); +} + +void Constructor::SetArrayClass(Class* klass) { + CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; + CHECK(klass != nullptr); + array_class_ = GcRoot<Class>(klass); +} + +void Constructor::ResetArrayClass() { + CHECK(!array_class_.IsNull()); + array_class_ = GcRoot<Class>(nullptr); +} + +void Constructor::VisitRoots(RootVisitor* visitor) { + static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); + array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); +} + +Constructor* Constructor::CreateFromArtMethod(Thread* self, mirror::ArtMethod* method) { + DCHECK(method->IsConstructor()) << PrettyMethod(method); + auto* ret = down_cast<Constructor*>(StaticClass()->AllocObject(self)); + if (LIKELY(ret != nullptr)) { + static_cast<AbstractMethod*>(ret)->CreateFromArtMethod(method); + } + return ret; +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h new file mode 100644 index 0000000000..88100f08e2 --- /dev/null +++ b/runtime/mirror/method.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_METHOD_H_ +#define ART_RUNTIME_MIRROR_METHOD_H_ + +#include "abstract_method.h" +#include "gc_root.h" + +namespace art { +namespace mirror { + +class Class; + +// C++ mirror of java.lang.reflect.Method. +class MANAGED Method : public AbstractMethod { + public: + static Method* CreateFromArtMethod(Thread* self, mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static mirror::Class* StaticClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return static_class_.Read(); + } + + static void SetClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static mirror::Class* ArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return array_class_.Read(); + } + + static void SetArrayClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + static GcRoot<Class> static_class_; // java.lang.reflect.Method.class. + static GcRoot<Class> array_class_; // [java.lang.reflect.Method.class. + + DISALLOW_COPY_AND_ASSIGN(Method); +}; + +// C++ mirror of java.lang.reflect.Constructor. +class MANAGED Constructor: public AbstractMethod { + public: + static Constructor* CreateFromArtMethod(Thread* self, mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static mirror::Class* StaticClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return static_class_.Read(); + } + + static void SetClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static mirror::Class* ArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return array_class_.Read(); + } + + static void SetArrayClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + static GcRoot<Class> static_class_; // java.lang.reflect.Constructor.class. + static GcRoot<Class> array_class_; // [java.lang.reflect.Constructor.class. + + DISALLOW_COPY_AND_ASSIGN(Constructor); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_METHOD_H_ diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 2cdc68f5b2..17fbc4f85d 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -81,33 +81,26 @@ static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader()); } -static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject javaBootstrap, - jobject javaSystem) { +static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass) { struct ClosestUserClassLoaderVisitor : public StackVisitor { - ClosestUserClassLoaderVisitor(Thread* thread, mirror::Object* bootstrap_in, - mirror::Object* system_in) - : StackVisitor(thread, NULL), bootstrap(bootstrap_in), system(system_in), - class_loader(NULL) {} + explicit ClosestUserClassLoaderVisitor(Thread* thread) + : StackVisitor(thread, nullptr), class_loader(nullptr) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(class_loader == NULL); + DCHECK(class_loader == nullptr); mirror::Class* c = GetMethod()->GetDeclaringClass(); mirror::Object* cl = c->GetClassLoader(); - if (cl != NULL && cl != bootstrap && cl != system) { + if (cl != nullptr) { class_loader = cl; return false; } return true; } - mirror::Object* bootstrap; - mirror::Object* system; mirror::Object* class_loader; }; ScopedFastNativeObjectAccess soa(env); - mirror::Object* bootstrap = soa.Decode<mirror::Object*>(javaBootstrap); - mirror::Object* system = soa.Decode<mirror::Object*>(javaSystem); - ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system); + ClosestUserClassLoaderVisitor visitor(soa.Self()); visitor.WalkStack(); return soa.AddLocalReference<jobject>(visitor.class_loader); } @@ -136,7 +129,7 @@ static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject jav static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMStack, fillStackTraceElements, "!(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"), NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"), - NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!()Ljava/lang/ClassLoader;"), NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"), NATIVE_METHOD(VMStack, getThreadStackTrace, "!(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), }; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 5ad18f87ed..51a897d346 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -25,6 +25,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -91,18 +92,6 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean return soa.AddLocalReference<jclass>(c.Get()); } -static jobject Class_findOverriddenMethodIfProxy(JNIEnv* env, jclass, jobject art_method) { - ScopedFastNativeObjectAccess soa(env); - mirror::ArtMethod* method = soa.Decode<mirror::ArtMethod*>(art_method); - mirror::Class* declaring_klass = method->GetDeclaringClass(); - if (!declaring_klass->IsProxyClass()) { - return art_method; - } - uint32_t dex_method_index = method->GetDexMethodIndex(); - mirror::ArtMethod* overriden_method = method->GetDexCacheResolvedMethods()->Get(dex_method_index); - return soa.AddLocalReference<jobject>(overriden_method); -} - static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); @@ -252,7 +241,7 @@ static jobject Class_getDeclaredFieldInternal(JNIEnv* env, jobject javaThis, jst static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { ScopedFastNativeObjectAccess soa(env); auto* name_string = soa.Decode<mirror::String*>(name); - if (name == nullptr) { + if (name_string == nullptr) { ThrowNullPointerException("name == null"); return nullptr; } @@ -269,17 +258,222 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam return soa.AddLocalReference<jobject>(result); } +static jobject Class_getDeclaredConstructorInternal( + JNIEnv* env, jobject javaThis, jobjectArray args) { + ScopedFastNativeObjectAccess soa(env); + auto* klass = DecodeClass(soa, javaThis); + auto* params = soa.Decode<mirror::ObjectArray<mirror::Class>*>(args); + StackHandleScope<1> hs(soa.Self()); + auto* declared_constructor = klass->GetDeclaredConstructor(soa.Self(), hs.NewHandle(params)); + if (declared_constructor != nullptr) { + return soa.AddLocalReference<jobject>( + mirror::Constructor::CreateFromArtMethod(soa.Self(), declared_constructor)); + } + return nullptr; +} + +static ALWAYS_INLINE inline bool MethodMatchesConstructor(mirror::ArtMethod* m, bool public_only) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(m != nullptr); + return (!public_only || m->IsPublic()) && !m->IsStatic() && m->IsConstructor(); +} + +static jobjectArray Class_getDeclaredConstructorsInternal( + JNIEnv* env, jobject javaThis, jboolean publicOnly) { + ScopedFastNativeObjectAccess soa(env); + auto* klass = DecodeClass(soa, javaThis); + StackHandleScope<2> hs(soa.Self()); + auto h_direct_methods = hs.NewHandle(klass->GetDirectMethods()); + size_t constructor_count = 0; + auto count = h_direct_methods.Get() != nullptr ? h_direct_methods->GetLength() : 0u; + // Two pass approach for speed. + for (size_t i = 0; i < count; ++i) { + constructor_count += MethodMatchesConstructor(h_direct_methods->GetWithoutChecks(i), + publicOnly != JNI_FALSE) ? 1u : 0u; + } + auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc( + soa.Self(), mirror::Constructor::ArrayClass(), constructor_count)); + if (UNLIKELY(h_constructors.Get() == nullptr)) { + soa.Self()->AssertPendingException(); + return nullptr; + } + constructor_count = 0; + for (size_t i = 0; i < count; ++i) { + auto* method = h_direct_methods->GetWithoutChecks(i); + if (MethodMatchesConstructor(method, publicOnly != JNI_FALSE)) { + auto* constructor = mirror::Constructor::CreateFromArtMethod(soa.Self(), method); + if (UNLIKELY(constructor == nullptr)) { + soa.Self()->AssertPendingException(); + return nullptr; + } + h_constructors->SetWithoutChecks<false>(constructor_count++, constructor); + } + } + return soa.AddLocalReference<jobjectArray>(h_constructors.Get()); +} + +static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, + jobject name, jobjectArray args) { + // Covariant return types permit the class to define multiple + // methods with the same name and parameter types. Prefer to + // return a non-synthetic method in such situations. We may + // still return a synthetic method to handle situations like + // escalated visibility. We never return miranda methods that + // were synthesized by the runtime. + constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<5> hs(soa.Self()); + auto h_method_name = hs.NewHandle(soa.Decode<mirror::String*>(name)); + if (UNLIKELY(h_method_name.Get() == nullptr)) { + ThrowNullPointerException("name == null"); + return nullptr; + } + auto h_args = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(args)); + auto* klass = DecodeClass(soa, javaThis); + mirror::ArtMethod* result = nullptr; + auto* virtual_methods = klass->GetVirtualMethods(); + if (virtual_methods != nullptr) { + auto h_virtual_methods = hs.NewHandle(virtual_methods); + for (size_t i = 0, count = virtual_methods->GetLength(); i < count; ++i) { + auto* m = h_virtual_methods->GetWithoutChecks(i); + auto* np_method = m->GetInterfaceMethodIfProxy(); + // May cause thread suspension. + mirror::String* np_name = np_method->GetNameAsString(soa.Self()); + if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { + if (UNLIKELY(soa.Self()->IsExceptionPending())) { + return nullptr; + } + continue; + } + auto modifiers = m->GetAccessFlags(); + if ((modifiers & kSkipModifiers) == 0) { + return soa.AddLocalReference<jobject>(mirror::Method::CreateFromArtMethod(soa.Self(), m)); + } + if ((modifiers & kAccMiranda) == 0) { + result = m; // Remember as potential result if it's not a miranda method. + } + } + } + if (result == nullptr) { + auto* direct_methods = klass->GetDirectMethods(); + if (direct_methods != nullptr) { + auto h_direct_methods = hs.NewHandle(direct_methods); + for (size_t i = 0, count = direct_methods->GetLength(); i < count; ++i) { + auto* m = h_direct_methods->GetWithoutChecks(i); + auto modifiers = m->GetAccessFlags(); + if ((modifiers & kAccConstructor) != 0) { + continue; + } + auto* np_method = m->GetInterfaceMethodIfProxy(); + // May cause thread suspension. + mirror::String* np_name = np_method ->GetNameAsString(soa.Self()); + if (np_name == nullptr) { + soa.Self()->AssertPendingException(); + return nullptr; + } + if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { + if (UNLIKELY(soa.Self()->IsExceptionPending())) { + return nullptr; + } + continue; + } + if ((modifiers & kSkipModifiers) == 0) { + return soa.AddLocalReference<jobject>(mirror::Method::CreateFromArtMethod( + soa.Self(), m)); + } + // Direct methods cannot be miranda methods, so this potential result must be synthetic. + result = m; + } + } + } + return result != nullptr ? + soa.AddLocalReference<jobject>(mirror::Method::CreateFromArtMethod(soa.Self(), result)) : + nullptr; +} + +jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, + jboolean publicOnly) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<5> hs(soa.Self()); + auto* klass = DecodeClass(soa, javaThis); + auto virtual_methods = hs.NewHandle(klass->GetVirtualMethods()); + auto direct_methods = hs.NewHandle(klass->GetDirectMethods()); + size_t num_methods = 0; + if (virtual_methods.Get() != nullptr) { + for (size_t i = 0, count = virtual_methods->GetLength(); i < count; ++i) { + auto* m = virtual_methods->GetWithoutChecks(i); + auto modifiers = m->GetAccessFlags(); + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccMiranda) == 0) { + ++num_methods; + } + } + } + if (direct_methods.Get() != nullptr) { + for (size_t i = 0, count = direct_methods->GetLength(); i < count; ++i) { + auto* m = direct_methods->GetWithoutChecks(i); + auto modifiers = m->GetAccessFlags(); + // Add non-constructor direct/static methods. + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccConstructor) == 0) { + ++num_methods; + } + } + } + auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc( + soa.Self(), mirror::Method::ArrayClass(), num_methods)); + num_methods = 0; + if (virtual_methods.Get() != nullptr) { + for (size_t i = 0, count = virtual_methods->GetLength(); i < count; ++i) { + auto* m = virtual_methods->GetWithoutChecks(i); + auto modifiers = m->GetAccessFlags(); + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccMiranda) == 0) { + auto* method = mirror::Method::CreateFromArtMethod(soa.Self(), m); + if (method == nullptr) { + soa.Self()->AssertPendingException(); + return nullptr; + } + ret->SetWithoutChecks<false>(num_methods++, method); + } + } + } + if (direct_methods.Get() != nullptr) { + for (size_t i = 0, count = direct_methods->GetLength(); i < count; ++i) { + auto* m = direct_methods->GetWithoutChecks(i); + auto modifiers = m->GetAccessFlags(); + // Add non-constructor direct/static methods. + if ((publicOnly == JNI_FALSE || (modifiers & kAccPublic) != 0) && + (modifiers & kAccConstructor) == 0) { + auto* method = mirror::Method::CreateFromArtMethod(soa.Self(), m); + if (method == nullptr) { + soa.Self()->AssertPendingException(); + return nullptr; + } + ret->SetWithoutChecks<false>(num_methods++, method); + } + } + } + return soa.AddLocalReference<jobjectArray>(ret.Get()); +} + static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Class, classForName, "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), - NATIVE_METHOD(Class, findOverriddenMethodIfProxy, - "!(Ljava/lang/reflect/ArtMethod;)Ljava/lang/reflect/ArtMethod;"), + NATIVE_METHOD(Class, classForName, + "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), + NATIVE_METHOD(Class, getDeclaredConstructorInternal, + "!([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"), + NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "!(Z)[Ljava/lang/reflect/Constructor;"), + NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredMethodInternal, + "!(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"), + NATIVE_METHOD(Class, getDeclaredMethodsUnchecked, + "!(Z)[Ljava/lang/reflect/Method;"), NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), - NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"), NATIVE_METHOD(Class, getPublicDeclaredFields, "!()[Ljava/lang/reflect/Field;"), - NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"), - NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), - NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), }; void register_java_lang_Class(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 5e1a4c59f7..c33f81a211 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -21,6 +21,7 @@ #include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "reflection.h" #include "scoped_fast_native_object_access.h" @@ -28,17 +29,10 @@ namespace art { -/* - * We get here through Constructor.newInstance(). The Constructor object - * would not be available if the constructor weren't public (per the - * definition of Class.getConstructor), so we can skip the method access - * check. We can also safely assume the constructor isn't associated - * with an interface, array, or primitive class. - */ -static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs, - jboolean accessible) { +static ALWAYS_INLINE inline jobject NewInstanceHelper( + JNIEnv* env, jobject javaMethod, jobjectArray javaArgs, size_t num_frames) { ScopedFastNativeObjectAccess soa(env); - mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod); + mirror::Method* m = soa.Decode<mirror::Method*>(javaMethod); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); if (UNLIKELY(c->IsAbstract())) { @@ -67,14 +61,31 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA } jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); - InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, (accessible == JNI_TRUE)); + InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, num_frames); // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. return javaReceiver; } +/* + * We get here through Constructor.newInstance(). The Constructor object + * would not be available if the constructor weren't public (per the + * definition of Class.getConstructor), so we can skip the method access + * check. We can also safely assume the constructor isn't associated + * with an interface, array, or primitive class. + */ +static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { + return NewInstanceHelper(env, javaMethod, javaArgs, 1); +} + +static jobject Constructor_newInstanceTwoFrames(JNIEnv* env, jobject javaMethod, + jobjectArray javaArgs) { + return NewInstanceHelper(env, javaMethod, javaArgs, 2); +} + static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;Z)Ljava/lang/Object;"), + NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Constructor, newInstanceTwoFrames, "!([Ljava/lang/Object;)Ljava/lang/Object;"), }; void register_java_lang_reflect_Constructor(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 9859746563..c20d83245c 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -30,9 +30,9 @@ namespace art { static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, - jobject javaArgs, jboolean accessible) { + jobject javaArgs) { ScopedFastNativeObjectAccess soa(env); - return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, (accessible == JNI_TRUE)); + return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); } static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) { @@ -55,7 +55,7 @@ static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;Z)Ljava/lang/Object;"), + NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), NATIVE_METHOD(Method, getExceptionTypesNative, "!()[Ljava/lang/Class;"), }; diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index baf8b24207..4a6ab404f2 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -30,13 +30,12 @@ static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArra jobject loader, jobjectArray methods, jobjectArray throws) { ScopedFastNativeObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* result = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods, - throws); - return soa.AddLocalReference<jclass>(result); + return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass( + soa, name, interfaces, loader, methods, throws)); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/ArtMethod;[[Ljava/lang/Class;)Ljava/lang/Class;"), + NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"), }; void register_java_lang_reflect_Proxy(JNIEnv* env) { diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc new file mode 100644 index 0000000000..53bc87665a --- /dev/null +++ b/runtime/prebuilt_tools_test.cc @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common_runtime_test.h" + +#include <cstdio> + +#include "gtest/gtest.h" + +namespace art { + +// Run the tests only on host. +#ifndef HAVE_ANDROID_OS + +class PrebuiltToolsTest : public CommonRuntimeTest { +}; + +static void CheckToolsExist(const std::string& tools_dir) { + const char* tools[] { "as", "objcopy", "objdump" }; // NOLINT + for (const char* tool : tools) { + struct stat exec_st; + std::string exec_path = tools_dir + tool; + if (stat(exec_path.c_str(), &exec_st) != 0) { + ADD_FAILURE() << "Can not find " << tool << " in " << tools_dir; + } + } +} + +TEST_F(PrebuiltToolsTest, CheckHostTools) { + std::string tools_dir = GetAndroidHostToolsDir(); + if (tools_dir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory for host"; + } else { + CheckToolsExist(tools_dir); + } +} + +TEST_F(PrebuiltToolsTest, CheckTargetTools) { + // Other prebuilts are missing from the build server's repo manifest. + InstructionSet isas[] = { kThumb2 }; // NOLINT + for (InstructionSet isa : isas) { + std::string tools_dir = GetAndroidTargetToolsDir(isa); + if (tools_dir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory for " << isa; + } else { + CheckToolsExist(tools_dir); + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace art diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 6061f73c19..b471293c09 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -20,6 +20,7 @@ #include "art_field-inl.h" #include "class_linker-inl.h" #include "common_compiler_test.h" +#include "mirror/method.h" #include "scoped_thread_state_change.h" namespace art { @@ -53,41 +54,34 @@ class ProxyTest : public CommonCompilerTest { mirror::ObjectArray<mirror::ArtMethod>* virtual_methods = interface->GetVirtualMethods(); methods_count += (virtual_methods == nullptr) ? 0 : virtual_methods->GetLength(); } - jclass javaLangReflectArtMethod = - soa.AddLocalReference<jclass>(mirror::ArtMethod::GetJavaLangReflectArtMethod()); - jobjectArray proxyClassMethods = soa.Env()->NewObjectArray(methods_count, - javaLangReflectArtMethod, nullptr); + jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( + methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr); soa.Self()->AssertNoPendingException(); - // Fill the method array - mirror::ArtMethod* equalsMethod = javaLangObject->FindDeclaredVirtualMethod("equals", - "(Ljava/lang/Object;)Z"); - mirror::ArtMethod* hashCodeMethod = javaLangObject->FindDeclaredVirtualMethod("hashCode", - "()I"); - mirror::ArtMethod* toStringMethod = javaLangObject->FindDeclaredVirtualMethod("toString", - "()Ljava/lang/String;"); - CHECK(equalsMethod != nullptr); - CHECK(hashCodeMethod != nullptr); - CHECK(toStringMethod != nullptr); - jsize array_index = 0; - // Adds Object methods. - soa.Env()->SetObjectArrayElement(proxyClassMethods, array_index++, - soa.AddLocalReference<jobject>(equalsMethod)); - soa.Env()->SetObjectArrayElement(proxyClassMethods, array_index++, - soa.AddLocalReference<jobject>(hashCodeMethod)); - soa.Env()->SetObjectArrayElement(proxyClassMethods, array_index++, - soa.AddLocalReference<jobject>(toStringMethod)); - + // Fill the method array + mirror::ArtMethod* method = javaLangObject->FindDeclaredVirtualMethod( + "equals", "(Ljava/lang/Object;)Z"); + CHECK(method != nullptr); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); + method = javaLangObject->FindDeclaredVirtualMethod("hashCode", "()I"); + CHECK(method != nullptr); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); + method = javaLangObject->FindDeclaredVirtualMethod("toString", "()Ljava/lang/String;"); + CHECK(method != nullptr); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); // Now adds all interfaces virtual methods. for (mirror::Class* interface : interfaces) { - mirror::ObjectArray<mirror::ArtMethod>* virtual_methods = interface->GetVirtualMethods(); - if (virtual_methods != nullptr) { - for (int32_t mth_index = 0; mth_index < virtual_methods->GetLength(); ++mth_index) { - mirror::ArtMethod* method = virtual_methods->Get(mth_index); - soa.Env()->SetObjectArrayElement(proxyClassMethods, array_index++, - soa.AddLocalReference<jobject>(method)); - } + for (int32_t i = 0, count = interface->NumVirtualMethods(); i < count; ++i) { + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod(soa.Self(), interface->GetVirtualMethod(i)))); } } CHECK_EQ(array_index, methods_count); @@ -96,10 +90,9 @@ class ProxyTest : public CommonCompilerTest { jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); soa.Self()->AssertNoPendingException(); - mirror::Class* proxyClass = class_linker_->CreateProxyClass(soa, - soa.Env()->NewStringUTF(className), - proxyClassInterfaces, jclass_loader, - proxyClassMethods, proxyClassThrows); + mirror::Class* proxyClass = class_linker_->CreateProxyClass( + soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, + proxyClassMethods, proxyClassThrows); soa.Self()->AssertNoPendingException(); return proxyClass; } diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 3e1315c73e..e54673831f 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -22,6 +22,7 @@ #include "dex_file-inl.h" #include "entrypoints/entrypoint_utils.h" #include "jni_internal.h" +#include "mirror/abstract_method.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" @@ -537,7 +538,7 @@ void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg } jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, - jobject javaReceiver, jobject javaArgs, bool accessible) { + jobject javaReceiver, jobject javaArgs, size_t num_frames) { // We want to make sure that the stack is not within a small distance from the // protected region in case we are calling into a leaf function whose stack // check has been elided. @@ -547,7 +548,9 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM return nullptr; } - mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod); + auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(javaMethod); + const bool accessible = abstract_method->IsAccessible(); + mirror::ArtMethod* m = abstract_method->GetArtMethod(); mirror::Class* declaring_class = m->GetDeclaringClass(); if (UNLIKELY(!declaring_class->IsInitialized())) { @@ -572,8 +575,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM } // Get our arrays of arguments and their types, and check they're the same size. - mirror::ObjectArray<mirror::Object>* objects = - soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs); + auto* objects = soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs); const DexFile::TypeList* classes = m->GetParameterTypeList(); uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0; @@ -586,7 +588,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM // If method is not set to be accessible, verify it can be accessed by the caller. mirror::Class* calling_class = nullptr; if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags(), - &calling_class, 2)) { + &calling_class, num_frames)) { ThrowIllegalAccessException( StringPrintf("Class %s cannot access %s method %s of class %s", calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(), diff --git a/runtime/reflection.h b/runtime/reflection.h index c2d406a2db..c63f858129 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -65,8 +65,9 @@ void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +// num_frames is number of frames we look up for access check. jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver, - jobject args, bool accessible) + jobject args, size_t num_frames = 1) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); ALWAYS_INLINE bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7bebb965f4..2fc8d2045d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -77,6 +77,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field.h" +#include "mirror/method.h" #include "mirror/stack_trace_element.h" #include "mirror/throwable.h" #include "monitor.h" @@ -1308,7 +1309,9 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { // need to be visited once per GC since they never change. mirror::ArtMethod::VisitRoots(visitor); mirror::Class::VisitRoots(visitor); + mirror::Constructor::VisitRoots(visitor); mirror::Reference::VisitRoots(visitor); + mirror::Method::VisitRoots(visitor); mirror::StackTraceElement::VisitRoots(visitor); mirror::String::VisitRoots(visitor); mirror::Throwable::VisitRoots(visitor); diff --git a/runtime/thread.h b/runtime/thread.h index b095e22163..719668bf47 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -25,6 +25,7 @@ #include <setjmp.h> #include <string> +#include "arch/context.h" #include "arch/instruction_set.h" #include "atomic.h" #include "base/macros.h" @@ -354,7 +355,18 @@ class Thread { Context* GetLongJumpContext(); void ReleaseLongJumpContext(Context* context) { - DCHECK(tlsPtr_.long_jump_context == nullptr); + if (tlsPtr_.long_jump_context != nullptr) { + // Each QuickExceptionHandler gets a long jump context and uses + // it for doing the long jump, after finding catch blocks/doing deoptimization. + // Both finding catch blocks and deoptimization can trigger another + // exception such as a result of class loading. So there can be nested + // cases of exception handling and multiple contexts being used. + // ReleaseLongJumpContext tries to save the context in tlsPtr_.long_jump_context + // for reuse so there is no need to always allocate a new one each time when + // getting a context. Since we only keep one context for reuse, delete the + // existing one since the passed in context is yet to be used for longjump. + delete tlsPtr_.long_jump_context; + } tlsPtr_.long_jump_context = context; } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index d389244f47..f57f9c485d 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -212,7 +212,7 @@ void WellKnownClasses::Init(JNIEnv* env) { ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue")); java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V"); - java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/ArtMethod;[Ljava/lang/Object;)Ljava/lang/Object;"); + java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V"); java_lang_Thread__UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread__UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V"); diff --git a/test/068-classloader/expected.txt b/test/068-classloader/expected.txt index bf131eee63..8725799fe1 100644 --- a/test/068-classloader/expected.txt +++ b/test/068-classloader/expected.txt @@ -11,3 +11,5 @@ Ctor: doubled implement, type 1 DoubledImplement one Got LinkageError on DI (early) Got LinkageError on IDI (early) +class Main +Got expected ClassNotFoundException diff --git a/test/068-classloader/src-ex/MutationTarget.java b/test/068-classloader/src-ex/MutationTarget.java new file mode 100644 index 0000000000..b02a236aa0 --- /dev/null +++ b/test/068-classloader/src-ex/MutationTarget.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Mutator target, see Mutator.java. + */ +public class MutationTarget { + public static int value = 0; +}
\ No newline at end of file diff --git a/test/068-classloader/src-ex/Mutator.java b/test/068-classloader/src-ex/Mutator.java new file mode 100644 index 0000000000..6bcd5b8554 --- /dev/null +++ b/test/068-classloader/src-ex/Mutator.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Simple mutator to change a static field of the mutator target. This will require a dex-cache + * access, so this setup allows the correct disambiguation between multiple class-loaders. + */ +public class Mutator { + public static void mutate(int v) { + MutationTarget.value = v; + } +}
\ No newline at end of file diff --git a/test/068-classloader/src/Main.java b/test/068-classloader/src/Main.java index 7dfb6f507d..361e2938e3 100644 --- a/test/068-classloader/src/Main.java +++ b/test/068-classloader/src/Main.java @@ -21,7 +21,7 @@ public class Main { /** * Main entry point. */ - public static void main(String[] args) { + public static void main(String[] args) throws Exception { FancyLoader loader; loader = new FancyLoader(ClassLoader.getSystemClassLoader()); @@ -58,6 +58,65 @@ public class Main { testAbstract(loader); testImplement(loader); testIfaceImplement(loader); + + testSeparation(); + + testClassForName(); + } + + static void testSeparation() { + FancyLoader loader1 = new FancyLoader(ClassLoader.getSystemClassLoader()); + FancyLoader loader2 = new FancyLoader(ClassLoader.getSystemClassLoader()); + + try { + Class target1 = loader1.loadClass("MutationTarget"); + Class target2 = loader2.loadClass("MutationTarget"); + + if (target1 == target2) { + throw new RuntimeException("target1 should not be equal to target2"); + } + + Class mutator1 = loader1.loadClass("Mutator"); + Class mutator2 = loader2.loadClass("Mutator"); + + if (mutator1 == mutator2) { + throw new RuntimeException("mutator1 should not be equal to mutator2"); + } + + runMutator(mutator1, 1); + + int value = getMutationTargetValue(target1); + if (value != 1) { + throw new RuntimeException("target 1 has unexpected value " + value); + } + value = getMutationTargetValue(target2); + if (value != 0) { + throw new RuntimeException("target 2 has unexpected value " + value); + } + + runMutator(mutator2, 2); + + value = getMutationTargetValue(target1); + if (value != 1) { + throw new RuntimeException("target 1 has unexpected value " + value); + } + value = getMutationTargetValue(target2); + if (value != 2) { + throw new RuntimeException("target 2 has unexpected value " + value); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + private static void runMutator(Class c, int v) throws Exception { + java.lang.reflect.Method m = c.getDeclaredMethod("mutate", int.class); + m.invoke(null, v); + } + + private static int getMutationTargetValue(Class c) throws Exception { + java.lang.reflect.Field f = c.getDeclaredField("value"); + return f.getInt(null); } /** @@ -422,4 +481,13 @@ public class Main { DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2(); di2.one(); } + + static void testClassForName() throws Exception { + System.out.println(Class.forName("Main").toString()); + try { + System.out.println(Class.forName("Main", false, null).toString()); + } catch (ClassNotFoundException expected) { + System.out.println("Got expected ClassNotFoundException"); + } + } } diff --git a/test/474-fp-sub-neg/expected.txt b/test/474-fp-sub-neg/expected.txt new file mode 100644 index 0000000000..e6ffe0d430 --- /dev/null +++ b/test/474-fp-sub-neg/expected.txt @@ -0,0 +1,2 @@ +-0.0 +-0.0 diff --git a/test/474-fp-sub-neg/info.txt b/test/474-fp-sub-neg/info.txt new file mode 100644 index 0000000000..eced93fef5 --- /dev/null +++ b/test/474-fp-sub-neg/info.txt @@ -0,0 +1,5 @@ +Regression check for optimizing simplify instruction pass. +A pair (sub, neg) should not be transforemd to (sub) for +fp calculation because we can lose the sign of zero for +the following expression: + - ( A - B ) != B - A ; if B == A diff --git a/test/474-fp-sub-neg/src/Main.java b/test/474-fp-sub-neg/src/Main.java new file mode 100644 index 0000000000..e6bce6793f --- /dev/null +++ b/test/474-fp-sub-neg/src/Main.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void floatTest() { + float f = 0; + float fc = 1f; + for (int i = 0; i < 2; i++) { + f -= fc; + f = -f; + } + + System.out.println(f); + } + + public static void doubleTest() { + double d = 0; + double dc = 1f; + for (int i = 0; i < 2; i++) { + d -= dc; + d = -d; + } + + System.out.println(d); + } + + public static void main(String[] args) { + doubleTest(); + floatTest(); + } + +} diff --git a/test/477-long-to-float-conversion-precision/expected.txt b/test/477-long-to-float-conversion-precision/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/477-long-to-float-conversion-precision/expected.txt diff --git a/test/477-long-to-float-conversion-precision/info.txt b/test/477-long-to-float-conversion-precision/info.txt new file mode 100644 index 0000000000..d9d41d70ba --- /dev/null +++ b/test/477-long-to-float-conversion-precision/info.txt @@ -0,0 +1 @@ +Tests for type conversions precision. diff --git a/test/477-long-to-float-conversion-precision/src/Main.java b/test/477-long-to-float-conversion-precision/src/Main.java new file mode 100644 index 0000000000..bc17053e20 --- /dev/null +++ b/test/477-long-to-float-conversion-precision/src/Main.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 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. + */ + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. +public class Main { + + public static void assertFloatEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main(String[] args) { + // Generate, compile and check long-to-float Dex instructions. + longToFloat(); + } + + private static void longToFloat() { + // The result for this test case is slightly less accurate on ARM, + // due to the implementation of long-to-float type conversions for + // this architecture (both in Quick and Optimizing). + assertFloatEquals(Float.intBitsToFloat(-555858671), $opt$LongToFloat(-8008112895877447681L)); + } + + // This method produces a long-to-float Dex instruction. + static float $opt$LongToFloat(long a) { return (float)a; } +} diff --git a/test/478-checker-inliner-nested-loop/expected.txt b/test/478-checker-inliner-nested-loop/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/expected.txt diff --git a/test/478-checker-inliner-nested-loop/info.txt b/test/478-checker-inliner-nested-loop/info.txt new file mode 100644 index 0000000000..c221e37285 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/info.txt @@ -0,0 +1,2 @@ +Tests inlining into a nested loop. SSAChecker should verify that +loop information was updated correctly. diff --git a/test/478-checker-inliner-nested-loop/src/Main.java b/test/478-checker-inliner-nested-loop/src/Main.java new file mode 100644 index 0000000000..df583d9302 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/src/Main.java @@ -0,0 +1,57 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +public class Main { + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static int Inline(int x, int y) { + int result; + if (x <= y) { + result = x * y; + } else { + result = 0; + } + return result; + } + + // CHECK-START: int Main.NestedLoop(int, int) inliner (before) + // CHECK-NOT: Mul + + // CHECK-START: int Main.NestedLoop(int, int) inliner (after) + // CHECK: Mul + // CHECK-NOT: Mul + + public static int NestedLoop(int max_x, int max_y) { + int total = 0; + for (int x = 0; x < max_x; ++x) { + for (int y = 0; y < max_y; ++y) { + total += Inline(x, y); + } + } + return total; + } + + public static void main(String[] args) { + assertIntEquals(0, NestedLoop(1, 1)); + assertIntEquals(3, NestedLoop(2, 3)); + } +} diff --git a/test/479-regression-implicit-null-check/expected.txt b/test/479-regression-implicit-null-check/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/479-regression-implicit-null-check/expected.txt diff --git a/test/479-regression-implicit-null-check/info.txt b/test/479-regression-implicit-null-check/info.txt new file mode 100644 index 0000000000..0bfca8cbad --- /dev/null +++ b/test/479-regression-implicit-null-check/info.txt @@ -0,0 +1,2 @@ +Tests a regression in which we moved the null check to an instruction which +checked a different object. This lead to valid null checks being elided. diff --git a/test/479-regression-implicit-null-check/src/Main.java b/test/479-regression-implicit-null-check/src/Main.java new file mode 100644 index 0000000000..6b6f2e4d2a --- /dev/null +++ b/test/479-regression-implicit-null-check/src/Main.java @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +public class Main { + public int x = 0; + + public Main(Main c) { + // After inlining the graph will look like: + // NullCheck c + // InstanceFieldGet c + // InstanceFieldSet this 3 + // The dead code will eliminate the InstanceFieldGet and we'll end up with: + // NullCheck c + // InstanceFieldSet this 3 + // At codegen, when verifying if we can move the null check to the user, + // we should check that we actually have the same user (not only that the + // next instruction can do implicit null checks). + // In this case we should generate code for the NullCheck since the next + // instruction checks a different object. + c.willBeInlined(); + x = 3; + } + + private int willBeInlined() { + return x; + } + + public static void main(String[] args) { + try { + new Main(null); + throw new RuntimeException("Failed to throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 39afc6785b..731c040027 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -335,6 +335,19 @@ endif TEST_ART_BROKEN_DEFAULT_RUN_TESTS := +# Known broken tests for Quick's and Optimizing's ARM back ends. +TEST_ART_BROKEN_ARM_RUN_TESTS := 477-long-to-float-conversion-precision # b/20413424 + +ifeq ($(TARGET_ARCH),arm) + ifneq (,$(filter 32,$(ALL_ADDRESS_SIZES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_ARM_RUN_TESTS),32) + endif +endif + +TEST_ART_BROKEN_ARM_RUN_TESTS := + # Known broken tests for the arm64 optimizing compiler backend. TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 301708ba38..503ec71293 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -81,7 +81,7 @@ vogar $vm_command \ --vm-arg -Djpda.settings.verbose=true \ --vm-arg -Djpda.settings.syncPort=34016 \ --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ - --vm-arg -Djpda.settings.debuggeeJavaPath="$art $image $debuggee_args" \ + --vm-arg -Djpda.settings.debuggeeJavaPath="\"$art $image $debuggee_args\"" \ --classpath $test_jar \ --classpath $junit_jar \ --vm-arg -Xcompiler-option --vm-arg --compiler-backend=Optimizing \ |