diff options
85 files changed, 1459 insertions, 1314 deletions
diff --git a/build/Android.bp b/build/Android.bp index b1553c759c..6c9f1d4dd1 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -146,6 +146,7 @@ art_global_defaults { "external/valgrind", "external/vixl/src", "external/zlib", + "libnativehelper/platform_include" ], tidy_checks: [ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index e6b793011f..dded966cb7 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1541,10 +1541,10 @@ class Dex2Oat FINAL { std::unique_ptr<MemMap> opened_dex_files_map; std::vector<std::unique_ptr<const DexFile>> opened_dex_files; // No need to verify the dex file for: - // 1) kSpeedProfile, since it includes dexlayout, which does the verification. + // 1) Dexlayout since it does the verification. It also may not pass the verification since + // we don't update the dex checksum. // 2) when we have a vdex file, which means it was already verified. - bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kSpeedProfile && - (input_vdex_file_ == nullptr); + const bool verify = !DoDexLayoutOptimizations() && (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), rodata_.back(), @@ -2094,12 +2094,20 @@ class Dex2Oat FINAL { return is_host_; } - bool UseProfileGuidedCompilation() const { + bool UseProfile() const { return profile_file_fd_ != -1 || !profile_file_.empty(); } + bool DoProfileGuidedOptimizations() const { + return UseProfile() && compiler_options_->GetCompilerFilter() != CompilerFilter::kVerifyProfile; + } + + bool DoDexLayoutOptimizations() const { + return DoProfileGuidedOptimizations(); + } + bool LoadProfile() { - DCHECK(UseProfileGuidedCompilation()); + DCHECK(UseProfile()); profile_compilation_info_.reset(new ProfileCompilationInfo()); ScopedFlock flock; @@ -2356,7 +2364,7 @@ class Dex2Oat FINAL { compiler_options_.get(), oat_file.get())); elf_writers_.back()->Start(); - bool do_dexlayout = compiler_options_->GetCompilerFilter() == CompilerFilter::kSpeedProfile; + const bool do_dexlayout = DoDexLayoutOptimizations(); oat_writers_.emplace_back(new OatWriter( IsBootImage(), timings_, do_dexlayout ? profile_compilation_info_.get() : nullptr)); } @@ -2873,7 +2881,7 @@ static int dex2oat(int argc, char** argv) { // If needed, process profile information for profile guided compilation. // This operation involves I/O. - if (dex2oat->UseProfileGuidedCompilation()) { + if (dex2oat->UseProfile()) { if (!dex2oat->LoadProfile()) { LOG(ERROR) << "Failed to process profile file"; return EXIT_FAILURE; diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 2d85e8fe7b..562d948c5a 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -55,19 +55,67 @@ static const char kDexFileLayoutExpectedOutputDex[] = "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC" "AAAAdQEAAAAQAAABAAAAjAEAAA=="; -static void WriteFileBase64(const char* base64, const char* location) { +// Dex file with multiple code items that have the same debug_info_off_. Constructed by a modified +// dexlayout on XandY. +static const char kDexFileDuplicateOffset[] = + "ZGV4CjAzNwAQfXfPCB8qCxo7MqdFhmHZQwCv8+udHD8MBAAAcAAAAHhWNBIAAAAAAAAAAFQDAAAT" + "AAAAcAAAAAgAAAC8AAAAAQAAANwAAAABAAAA6AAAAAUAAADwAAAAAwAAABgBAACUAgAAeAEAABQC" + "AAAeAgAAJgIAACsCAAAyAgAANwIAAFsCAAB7AgAAngIAALICAAC1AgAAvQIAAMUCAADIAgAA1QIA" + "AOkCAADvAgAA9QIAAPwCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAkAAAAHAAAA" + "AAAAAAIAAQASAAAAAAAAAAEAAAABAAAAAQAAAAIAAAAAAAAAAgAAAAEAAAAGAAAAAQAAAAAAAAAA" + "AAAABgAAAAAAAAAKAAAAAAAAACsDAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAsAAAD0AQAANQMAAAAA" + "AAACAAAAAAAAAAAAAAAAAAAACwAAAAQCAAA/AwAAAAAAAAIAAAAUAwAAGgMAAAEAAAAjAwAAAQAB" + "AAEAAAAFAAAABAAAAHAQBAAAAA4AAQABAAEAAAAFAAAABAAAAHAQBAAAAA4AAQAAAAEAAAAFAAAA" + "CAAAACIAAQBwEAEAAABpAAAADgABAAEAAQAAAAUAAAAEAAAAcBAAAAAADgB4AQAAAAAAAAAAAAAA" + "AAAAhAEAAAAAAAAAAAAAAAAAAAg8Y2xpbml0PgAGPGluaXQ+AANMWDsABUxZJFo7AANMWTsAIkxk" + "YWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5l" + "ckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNzZXM7ABJMamF2YS9sYW5nL09i" + "amVjdDsAAVYABlguamF2YQAGWS5qYXZhAAFaAAthY2Nlc3NGbGFncwASZW1pdHRlcjogamFjay00" + "LjI1AARuYW1lAAR0aGlzAAV2YWx1ZQABegARAAcOABMABw4AEgAHDnYAEQAHDgACAwERGAICBAIN" + "BAgPFwwCBQERHAEYAQAAAQAAgIAEjAMAAAEAAYCABKQDAQACAAAIAoiABLwDAYCABNwDAAAADwAA" + "AAAAAAABAAAAAAAAAAEAAAATAAAAcAAAAAIAAAAIAAAAvAAAAAMAAAABAAAA3AAAAAQAAAABAAAA" + "6AAAAAUAAAAFAAAA8AAAAAYAAAADAAAAGAEAAAMQAAACAAAAeAEAAAEgAAAEAAAAjAEAAAYgAAAC" + "AAAA9AEAAAIgAAATAAAAFAIAAAMgAAAEAAAA/wIAAAQgAAADAAAAFAMAAAAgAAADAAAAKwMAAAAQ" + "AAABAAAAVAMAAA=="; + +// Dex file with null value for annotations_off in the annotation_set_ref_list. +// Constructed by building a dex file with annotations and hex editing. +static const char kNullSetRefListElementInputDex[] = + "ZGV4CjAzNQB1iA+7ZwgkF+7E6ZesYFc2lRAR3qnRAanwAwAAcAAAAHhWNBIAAAAAAAAAACADAAAS" + "AAAAcAAAAAgAAAC4AAAAAwAAANgAAAABAAAA/AAAAAQAAAAEAQAAAgAAACQBAACMAgAAZAEAAOgB" + "AADwAQAAAAIAAAMCAAAQAgAAIAIAADQCAABIAgAAawIAAI0CAAC1AgAAyAIAANECAADUAgAA2QIA" + "ANwCAADjAgAA6QIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAAAgAAAAMAAAAAAAAA" + "DAAAAAcAAAAAAAAADQAAAAcAAADgAQAABgAGAAsAAAAAAAEAAAAAAAAAAgAOAAAAAQAAABAAAAAC" + "AAEAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAsAEAAAgDAAAAAAAAAQAAAAEmAAACAAAA2AEAAAoA" + "AADIAQAAFgMAAAAAAAACAAAAAAAAAHwBAAABAAAA/AIAAAAAAAABAAAAAgMAAAEAAQABAAAA8AIA" + "AAQAAABwEAMAAAAOAAIAAgAAAAAA9QIAAAEAAAAOAAAAAAAAAAAAAAAAAAAAAQAAAAEAAABkAQAA" + "cAEAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAgAAAAMAAwAGPGluaXQ+AA5Bbm5vQ2xhc3MuamF2YQAB" + "TAALTEFubm9DbGFzczsADkxNeUFubm90YXRpb247ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" + "L2xhbmcvU3RyaW5nOwAhTGphdmEvbGFuZy9hbm5vdGF0aW9uL0Fubm90YXRpb247ACBMamF2YS9s" + "YW5nL2Fubm90YXRpb24vUmV0ZW50aW9uOwAmTGphdmEvbGFuZy9hbm5vdGF0aW9uL1JldGVudGlv" + "blBvbGljeTsAEU15QW5ub3RhdGlvbi5qYXZhAAdSVU5USU1FAAFWAANWTEwAAWEABWFOYW1lAARu" + "YW1lAAV2YWx1ZQABAAcOAAICAAAHDgABBQERGwABAQEQFw8AAAIAAICABIQDAQmcAwAAAAECgQgA" + "AAARAAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAgAAAC4AAAAAwAAAAMAAADYAAAABAAA" + "AAEAAAD8AAAABQAAAAQAAAAEAQAABgAAAAIAAAAkAQAAAhAAAAEAAABkAQAAAxAAAAMAAABwAQAA" + "ASAAAAIAAACEAQAABiAAAAIAAACwAQAAARAAAAIAAADYAQAAAiAAABIAAADoAQAAAyAAAAIAAADw" + "AgAABCAAAAIAAAD8AgAAACAAAAIAAAAIAwAAABAAAAEAAAAgAwAA"; + +static void WriteBase64ToFile(const char* base64, File* file) { // Decode base64. CHECK(base64 != nullptr); size_t length; std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length)); - CHECK(bytes.get() != nullptr); - - // Write to provided file. - std::unique_ptr<File> file(OS::CreateEmptyFile(location)); - CHECK(file.get() != nullptr); + CHECK(bytes != nullptr); if (!file->WriteFully(bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as file"; } +} + +static void WriteFileBase64(const char* base64, const char* location) { + // Write to provided file. + std::unique_ptr<File> file(OS::CreateEmptyFile(location)); + CHECK(file != nullptr); + WriteBase64ToFile(base64, file.get()); if (file->FlushCloseOrErase() != 0) { PLOG(FATAL) << "Could not flush and close test file."; } @@ -212,4 +260,41 @@ TEST_F(DexLayoutTest, DexFileLayout) { ASSERT_TRUE(DexFileLayoutExec(&error_msg)) << error_msg; } +TEST_F(DexLayoutTest, DuplicateOffset) { + ScratchFile temp; + WriteBase64ToFile(kDexFileDuplicateOffset, temp.GetFile()); + EXPECT_EQ(temp.GetFile()->Flush(), 0); + std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; + EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + std::vector<std::string> dexlayout_exec_argv = { + dexlayout, + "-a", + "-i", + "-o", + "/dev/null", + temp.GetFilename()}; + std::string error_msg; + const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg); + EXPECT_TRUE(result); + if (!result) { + LOG(ERROR) << "Error " << error_msg; + } +} + +TEST_F(DexLayoutTest, NullSetRefListElement) { + ScratchFile temp; + WriteBase64ToFile(kNullSetRefListElementInputDex, temp.GetFile()); + EXPECT_EQ(temp.GetFile()->Flush(), 0); + std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; + EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + std::vector<std::string> dexlayout_exec_argv = + { dexlayout, "-o", "/dev/null", temp.GetFilename() }; + std::string error_msg; + const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg); + EXPECT_TRUE(result); + if (!result) { + LOG(ERROR) << "Error " << error_msg; + } +} + } // namespace art diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 491e73980f..18a667015d 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -54,48 +54,6 @@ namespace art { -static bool LocationToFilename(const std::string& location, InstructionSet isa, - std::string* filename) { - bool has_system = false; - bool has_cache = false; - // image_location = /system/framework/boot.art - // system_image_filename = /system/framework/<image_isa>/boot.art - std::string system_filename(GetSystemImageFilename(location.c_str(), isa)); - if (OS::FileExists(system_filename.c_str())) { - has_system = true; - } - - bool have_android_data = false; - bool dalvik_cache_exists = false; - bool is_global_cache = false; - std::string dalvik_cache; - GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache, - &have_android_data, &dalvik_cache_exists, &is_global_cache); - - std::string cache_filename; - if (have_android_data && dalvik_cache_exists) { - // Always set output location even if it does not exist, - // so that the caller knows where to create the image. - // - // image_location = /system/framework/boot.art - // *image_filename = /data/dalvik-cache/<image_isa>/boot.art - std::string error_msg; - if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), - &cache_filename, &error_msg)) { - has_cache = true; - } - } - if (has_system) { - *filename = system_filename; - return true; - } else if (has_cache) { - *filename = cache_filename; - return true; - } else { - return false; - } -} - static const OatHeader* GetOatHeader(const ElfFile* elf_file) { uint64_t off = 0; if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) { @@ -106,28 +64,10 @@ static const OatHeader* GetOatHeader(const ElfFile* elf_file) { return oat_header; } -// This function takes an elf file and reads the current patch delta value -// encoded in its oat header value -static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string* error_msg) { - const OatHeader* oat_header = GetOatHeader(elf_file); - if (oat_header == nullptr) { - *error_msg = "Unable to get oat header from elf file."; - return false; - } - if (!oat_header->IsValid()) { - *error_msg = "Elf file has an invalid oat header"; - return false; - } - *delta = oat_header->GetImagePatchDelta(); - return true; -} - -static File* CreateOrOpen(const char* name, bool* created) { +static File* CreateOrOpen(const char* name) { if (OS::FileExists(name)) { - *created = false; return OS::OpenFileReadWrite(name); } else { - *created = true; std::unique_ptr<File> f(OS::CreateEmptyFile(name)); if (f.get() != nullptr) { if (fchmod(f->Fd(), 0644) != 0) { @@ -206,12 +146,11 @@ bool PatchOat::Patch(const std::string& image_location, Thread::Current()->TransitionFromRunnableToSuspended(kNative); ScopedObjectAccess soa(Thread::Current()); - t.NewTiming("Image and oat Patching setup"); + t.NewTiming("Image Patching setup"); std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map; std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map; std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map; - std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map; for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; @@ -255,8 +194,7 @@ bool PatchOat::Patch(const std::string& image_location, space_to_memmap_map.emplace(space, std::move(image)); } - // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and - // prepare PatchOat instances for the rest. + // Symlink PIC oat and vdex files and patch the image spaces in memory. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; std::string input_image_filename = space->GetImageFilename(); @@ -277,14 +215,17 @@ bool PatchOat::Patch(const std::string& image_location, return false; } - bool skip_patching_oat = false; MaybePic is_oat_pic = IsOatPic(elf.get()); if (is_oat_pic >= ERROR_FIRST) { // Error logged by IsOatPic return false; - } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. + } else if (is_oat_pic == NOT_PIC) { + LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath(); + return false; + } else { + CHECK(is_oat_pic == PIC); + // Create a symlink. std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + @@ -296,23 +237,16 @@ bool PatchOat::Patch(const std::string& image_location, ImageHeader::GetOatLocationFromImageLocation(output_image_filename); if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(), - output_oat_filename, - false, - true) || + output_oat_filename) || !SymlinkFile(input_vdex_filename, output_vdex_filename)) { // Errors already logged by above call. return false; } - // Don't patch the OAT, since we just symlinked it. Image still needs patching. - skip_patching_oat = true; - } else { - CHECK(is_oat_pic == NOT_PIC); } PatchOat& p = space_to_patchoat_map.emplace(space, PatchOat( isa, - elf.release(), space_to_memmap_map.find(space)->second.get(), space->GetLiveBitmap(), space->GetMemMap(), @@ -320,36 +254,24 @@ bool PatchOat::Patch(const std::string& image_location, &space_to_memmap_map, timings)).first->second; - t.NewTiming("Patching files"); - if (!skip_patching_oat && !p.PatchElf()) { - LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath(); - return false; - } + t.NewTiming("Patching image"); if (!p.PatchImage(i == 0)) { LOG(ERROR) << "Failed to patch image file " << input_image_filename; return false; } - - space_to_skip_patching_map.emplace(space, skip_patching_oat); } - // Do a second pass over the image spaces. Patch image files, non-PIC oat files - // and symlink their corresponding vdex files. + // Write the patched image spaces. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; - std::string input_image_filename = space->GetImageFilename(); - std::string input_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(input_image_filename); - t.NewTiming("Writing files"); + t.NewTiming("Writing image"); std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") + converted_image_filename; - bool new_oat_out; - std::unique_ptr<File> - output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out)); + std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str())); if (output_image_file.get() == nullptr) { LOG(ERROR) << "Failed to open output image file at " << output_image_filename; return false; @@ -362,48 +284,10 @@ bool PatchOat::Patch(const std::string& image_location, if (!success) { return false; } - - bool skip_patching_oat = space_to_skip_patching_map.find(space)->second; - if (!skip_patching_oat) { - std::string output_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); - std::string output_oat_filename = - ImageHeader::GetOatLocationFromImageLocation(output_image_filename); - - std::unique_ptr<File> - output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); - if (output_oat_file.get() == nullptr) { - LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename; - return false; - } - success = p.WriteElf(output_oat_file.get()); - success = FinishFile(output_oat_file.get(), success); - if (success) { - success = SymlinkFile(input_vdex_filename, output_vdex_filename); - } - if (!success) { - return false; - } - } } return true; } -bool PatchOat::WriteElf(File* out) { - TimingLogger::ScopedTiming t("Writing Elf File", timings_); - - CHECK(oat_file_.get() != nullptr); - CHECK(out != nullptr); - size_t expect = oat_file_->Size(); - if (out->WriteFully(reinterpret_cast<char*>(oat_file_->Begin()), expect) && - out->SetLength(expect) == 0) { - return true; - } else { - LOG(ERROR) << "Writing to oat file " << out->GetPath() << " failed."; - return false; - } -} - bool PatchOat::WriteImage(File* out) { TimingLogger::ScopedTiming t("Writing image File", timings_); std::string error_msg; @@ -466,22 +350,7 @@ PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) { } bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out) { - // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. - if (output_oat_opened_from_fd) { - // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? - LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; - return false; - } - - // Image was PIC. Create symlink where the oat is supposed to go. - if (!new_oat_out) { - LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; - return false; - } - + const std::string& output_oat_filename) { // Delete the original file, since we won't need it. unlink(output_oat_filename.c_str()); @@ -807,133 +676,6 @@ void PatchOat::FixupMethod(ArtMethod* object, ArtMethod* copy) { object->GetDataPtrSize(pointer_size)), pointer_size); } -bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings, - bool output_oat_opened_from_fd, bool new_oat_out) { - CHECK(input_oat != nullptr); - CHECK(output_oat != nullptr); - CHECK_GE(input_oat->Fd(), 0); - CHECK_GE(output_oat->Fd(), 0); - TimingLogger::ScopedTiming t("Setup Oat File Patching", timings); - - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat, - PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg)); - if (elf.get() == nullptr) { - LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg; - return false; - } - - MaybePic is_oat_pic = IsOatPic(elf.get()); - if (is_oat_pic >= ERROR_FIRST) { - // Error logged by IsOatPic - return false; - } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the rest. - // Any errors will be logged by the function call. - return ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out); - } else { - CHECK(is_oat_pic == NOT_PIC); - } - - PatchOat p(elf.release(), delta, timings); - t.NewTiming("Patch Oat file"); - if (!p.PatchElf()) { - return false; - } - - t.NewTiming("Writing oat file"); - if (!p.WriteElf(output_oat)) { - return false; - } - return true; -} - -template <typename ElfFileImpl> -bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) { - auto rodata_sec = oat_file->FindSectionByName(".rodata"); - if (rodata_sec == nullptr) { - return false; - } - OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file->Begin() + rodata_sec->sh_offset); - if (!oat_header->IsValid()) { - LOG(ERROR) << "Elf file " << oat_file->GetFilePath() << " has an invalid oat header"; - return false; - } - oat_header->RelocateOat(delta_); - return true; -} - -bool PatchOat::PatchElf() { - if (oat_file_->Is64Bit()) { - return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64()); - } else { - return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32()); - } -} - -template <typename ElfFileImpl> -bool PatchOat::PatchElf(ElfFileImpl* oat_file) { - TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_); - - // Fix up absolute references to locations within the boot image. - if (!oat_file->ApplyOatPatchesTo(".text", delta_)) { - return false; - } - - // Update the OatHeader fields referencing the boot image. - if (!PatchOatHeader<ElfFileImpl>(oat_file)) { - return false; - } - - bool need_boot_oat_fixup = true; - for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) { - auto hdr = oat_file->GetProgramHeader(i); - if (hdr->p_type == PT_LOAD && hdr->p_vaddr == 0u) { - need_boot_oat_fixup = false; - break; - } - } - if (!need_boot_oat_fixup) { - // This is an app oat file that can be loaded at an arbitrary address in memory. - // Boot image references were patched above and there's nothing else to do. - return true; - } - - // This is a boot oat file that's loaded at a particular address and we need - // to patch all absolute addresses, starting with ELF program headers. - - t.NewTiming("Fixup Elf Headers"); - // Fixup Phdr's - oat_file->FixupProgramHeaders(delta_); - - t.NewTiming("Fixup Section Headers"); - // Fixup Shdr's - oat_file->FixupSectionHeaders(delta_); - - t.NewTiming("Fixup Dynamics"); - oat_file->FixupDynamic(delta_); - - t.NewTiming("Fixup Elf Symbols"); - // Fixup dynsym - if (!oat_file->FixupSymbols(delta_, true)) { - return false; - } - // Fixup symtab - if (!oat_file->FixupSymbols(delta_, false)) { - return false; - } - - t.NewTiming("Fixup Debug Sections"); - if (!oat_file->FixupDebugSections(delta_)) { - return false; - } - - return true; -} - static int orig_argc; static char** orig_argv; @@ -968,32 +710,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError("Usage: patchoat [options]..."); UsageError(""); UsageError(" --instruction-set=<isa>: Specifies the instruction set the patched code is"); - UsageError(" compiled for. Required if you use --input-oat-location"); - UsageError(""); - UsageError(" --input-oat-file=<file.oat>: Specifies the exact filename of the oat file to be"); - UsageError(" patched."); - UsageError(""); - UsageError(" --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file"); - UsageError(" to be patched."); - UsageError(""); - UsageError(" --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file"); - UsageError(" associated with the oat file."); - UsageError(""); - UsageError(" --input-oat-location=<file.oat>: Specifies the 'location' to read the patched"); - UsageError(" oat file from. If used one must also supply the --instruction-set"); + UsageError(" compiled for (required)."); UsageError(""); UsageError(" --input-image-location=<file.art>: Specifies the 'location' of the image file to"); - UsageError(" be patched. If --instruction-set is not given it will use the instruction set"); - UsageError(" extracted from the --input-oat-file."); - UsageError(""); - UsageError(" --output-oat-file=<file.oat>: Specifies the exact file to write the patched oat"); - UsageError(" file to."); - UsageError(""); - UsageError(" --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the"); - UsageError(" patched oat file to."); - UsageError(""); - UsageError(" --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the"); - UsageError(" the vdex file associated with the patch oat file to."); + UsageError(" be patched."); UsageError(""); UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched"); UsageError(" image file to."); @@ -1001,15 +721,6 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by."); UsageError(" This value may be negative."); UsageError(""); - UsageError(" --patched-image-location=<file.art>: Relocate the oat file to be the same as the"); - UsageError(" image at the given location. If used one must also specify the"); - UsageError(" --instruction-set flag. It will search for this image in the same way that"); - UsageError(" is done when loading one."); - UsageError(""); - UsageError(" --lock-output: Obtain a flock on output oat file before starting."); - UsageError(""); - UsageError(" --no-lock-output: Do not attempt to obtain a flock on output oat file."); - UsageError(""); UsageError(" --dump-timings: dump out patch timing information"); UsageError(""); UsageError(" --no-dump-timings: do not dump out patch timing information"); @@ -1018,34 +729,6 @@ NO_RETURN static void Usage(const char *fmt, ...) { exit(EXIT_FAILURE); } -static bool ReadBaseDelta(const char* name, off_t* delta, std::string* error_msg) { - CHECK(name != nullptr); - CHECK(delta != nullptr); - std::unique_ptr<File> file; - if (OS::FileExists(name)) { - file.reset(OS::OpenFileForReading(name)); - if (file.get() == nullptr) { - *error_msg = "Failed to open file %s for reading"; - return false; - } - } else { - *error_msg = "File %s does not exist"; - return false; - } - CHECK(file.get() != nullptr); - ImageHeader hdr; - if (sizeof(hdr) != file->Read(reinterpret_cast<char*>(&hdr), sizeof(hdr), 0)) { - *error_msg = "Failed to read file %s"; - return false; - } - if (!hdr.IsValid()) { - *error_msg = "%s does not contain a valid image header."; - return false; - } - *delta = hdr.GetPatchDelta(); - return true; -} - static int patchoat_image(TimingLogger& timings, InstructionSet isa, const std::string& input_image_location, @@ -1084,293 +767,6 @@ static int patchoat_image(TimingLogger& timings, return ret ? EXIT_SUCCESS : EXIT_FAILURE; } -static int patchoat_oat(TimingLogger& timings, - InstructionSet isa, - const std::string& patched_image_location, - off_t base_delta, - bool base_delta_set, - int input_oat_fd, - int input_vdex_fd, - const std::string& input_oat_location, - std::string input_oat_filename, - bool have_input_oat, - int output_oat_fd, - int output_vdex_fd, - std::string output_oat_filename, - bool have_output_oat, - bool lock_output, - bool debug) { - { - // Only 1 of these may be set. - uint32_t cnt = 0; - cnt += (base_delta_set) ? 1 : 0; - cnt += (!patched_image_location.empty()) ? 1 : 0; - if (cnt > 1) { - Usage("Only one of --base-offset-delta or --patched-image-location may be used."); - } else if (cnt == 0) { - Usage("Must specify --base-offset-delta or --patched-image-location."); - } - } - - if (!have_input_oat || !have_output_oat) { - Usage("Both input and output oat must be supplied to patch an app odex."); - } - - if (!input_oat_location.empty()) { - if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) { - Usage("Unable to find filename for input oat location %s", input_oat_location.c_str()); - } - if (debug) { - LOG(INFO) << "Using input-oat-file " << input_oat_filename; - } - } - - if ((input_oat_fd == -1) != (input_vdex_fd == -1)) { - Usage("Either both input oat and vdex have to be passed as file descriptors or none of them"); - } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) { - Usage("Either both output oat and vdex have to be passed as file descriptors or none of them"); - } - - bool match_delta = false; - if (!patched_image_location.empty()) { - std::string system_filename; - bool has_system = false; - std::string cache_filename; - bool has_cache = false; - bool has_android_data_unused = false; - bool is_global_cache = false; - if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa, - &system_filename, &has_system, &cache_filename, - &has_android_data_unused, &has_cache, - &is_global_cache)) { - Usage("Unable to determine image file for location %s", patched_image_location.c_str()); - } - std::string patched_image_filename; - if (has_cache) { - patched_image_filename = cache_filename; - } else if (has_system) { - LOG(WARNING) << "Only image file found was in /system for image location " - << patched_image_location; - patched_image_filename = system_filename; - } else { - Usage("Unable to determine image file for location %s", patched_image_location.c_str()); - } - if (debug) { - LOG(INFO) << "Using patched-image-file " << patched_image_filename; - } - - base_delta_set = true; - match_delta = true; - std::string error_msg; - if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) { - Usage(error_msg.c_str(), patched_image_filename.c_str()); - } - } - - if (!IsAligned<kPageSize>(base_delta)) { - Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize); - } - - // We can symlink VDEX only if we have both input and output specified as filenames. - // Store that piece of information before we possibly create bogus filenames for - // files passed as file descriptors. - bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty(); - - // Infer names of VDEX files. - std::string input_vdex_filename; - std::string output_vdex_filename; - if (!input_oat_filename.empty()) { - input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex"); - } - if (!output_oat_filename.empty()) { - output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex"); - } - - // Do we need to cleanup output files if we fail? - bool new_oat_out = false; - bool new_vdex_out = false; - - std::unique_ptr<File> input_oat; - std::unique_ptr<File> output_oat; - - if (input_oat_fd != -1) { - if (input_oat_filename.empty()) { - input_oat_filename = "input-oat-file"; - } - input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); - if (input_oat_fd == output_oat_fd) { - input_oat.get()->DisableAutoClose(); - } - if (input_oat == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd; - return EXIT_FAILURE; - } - } else { - CHECK(!input_oat_filename.empty()); - input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str())); - if (input_oat == nullptr) { - int err = errno; - LOG(ERROR) << "Failed to open input oat file " << input_oat_filename - << ": " << strerror(err) << "(" << err << ")"; - return EXIT_FAILURE; - } - } - - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg)); - if (elf.get() == nullptr) { - LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg; - return EXIT_FAILURE; - } - if (!elf->HasSection(".text.oat_patches")) { - LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath(); - return EXIT_FAILURE; - } - - if (output_oat_fd != -1) { - if (output_oat_filename.empty()) { - output_oat_filename = "output-oat-file"; - } - output_oat.reset(new File(output_oat_fd, output_oat_filename, true)); - if (output_oat == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd; - } - } else { - CHECK(!output_oat_filename.empty()); - output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); - if (output_oat == nullptr) { - int err = errno; - LOG(ERROR) << "Failed to open output oat file " << output_oat_filename - << ": " << strerror(err) << "(" << err << ")"; - } - } - - // Open VDEX files if we are not symlinking them. - std::unique_ptr<File> input_vdex; - std::unique_ptr<File> output_vdex; - if (symlink_vdex) { - new_vdex_out = !OS::FileExists(output_vdex_filename.c_str()); - } else { - if (input_vdex_fd != -1) { - input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true)); - if (input_vdex == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd; - } - } else { - input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str())); - if (input_vdex == nullptr) { - PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename; - return EXIT_FAILURE; - } - } - if (output_vdex_fd != -1) { - output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true)); - if (output_vdex == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd; - } - } else { - output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out)); - if (output_vdex == nullptr) { - PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename; - return EXIT_FAILURE; - } - } - } - - // TODO: get rid of this. - auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out] - (bool success) { - if (!success) { - if (new_oat_out) { - CHECK(!output_oat_filename.empty()); - unlink(output_oat_filename.c_str()); - } - if (new_vdex_out) { - CHECK(!output_vdex_filename.empty()); - unlink(output_vdex_filename.c_str()); - } - } - - if (kIsDebugBuild) { - LOG(INFO) << "Cleaning up.. success? " << success; - } - }; - - if (output_oat.get() == nullptr) { - cleanup(false); - return EXIT_FAILURE; - } - - if (match_delta) { - // Figure out what the current delta is so we can match it to the desired delta. - off_t current_delta = 0; - if (!ReadOatPatchDelta(elf.get(), ¤t_delta, &error_msg)) { - LOG(ERROR) << "Unable to get current delta: " << error_msg; - cleanup(false); - return EXIT_FAILURE; - } - // Before this line base_delta is the desired final delta. We need it to be the actual amount to - // change everything by. We subtract the current delta from it to make it this. - base_delta -= current_delta; - if (!IsAligned<kPageSize>(base_delta)) { - LOG(ERROR) << "Given image file was relocated by an illegal delta"; - cleanup(false); - return false; - } - } - - if (debug) { - LOG(INFO) << "moving offset by " << base_delta - << " (0x" << std::hex << base_delta << ") bytes or " - << std::dec << (base_delta/kPageSize) << " pages."; - } - - ScopedFlock output_oat_lock; - if (lock_output) { - if (!output_oat_lock.Init(output_oat.get(), &error_msg)) { - LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg; - cleanup(false); - return EXIT_FAILURE; - } - } - - TimingLogger::ScopedTiming pt("patch oat", &timings); - bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, - output_oat_fd >= 0, // was it opened from FD? - new_oat_out); - ret = FinishFile(output_oat.get(), ret); - - if (ret) { - if (symlink_vdex) { - ret = SymlinkFile(input_vdex_filename, output_vdex_filename); - } else { - ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get()); - } - } - - if (kIsDebugBuild) { - LOG(INFO) << "Exiting with return ... " << ret; - } - cleanup(ret); - return ret ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int ParseFd(const StringPiece& option, const char* cmdline_arg) { - int fd; - const char* fd_str = option.substr(strlen(cmdline_arg)).data(); - if (!ParseInt(fd_str, &fd)) { - Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str); - } - if (fd < 0) { - Usage("%s pass a negative value %d", cmdline_arg, fd); - } - return fd; -} - static int patchoat(int argc, char **argv) { InitLogging(argv, Runtime::Aborter); MemMap::Init(); @@ -1392,23 +788,11 @@ static int patchoat(int argc, char **argv) { // cmd line args bool isa_set = false; InstructionSet isa = kNone; - std::string input_oat_filename; - std::string input_oat_location; - int input_oat_fd = -1; - int input_vdex_fd = -1; - bool have_input_oat = false; std::string input_image_location; - std::string output_oat_filename; - int output_oat_fd = -1; - int output_vdex_fd = -1; - bool have_output_oat = false; std::string output_image_filename; off_t base_delta = 0; bool base_delta_set = false; - std::string patched_image_filename; - std::string patched_image_location; bool dump_timings = kIsDebugBuild; - bool lock_output = true; for (int i = 0; i < argc; ++i) { const StringPiece option(argv[i]); @@ -1423,42 +807,8 @@ static int patchoat(int argc, char **argv) { if (isa == kNone) { Usage("Unknown or invalid instruction set %s", isa_str); } - } else if (option.starts_with("--input-oat-location=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_location = option.substr(strlen("--input-oat-location=")).data(); - } else if (option.starts_with("--input-oat-file=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_filename = option.substr(strlen("--input-oat-file=")).data(); - } else if (option.starts_with("--input-oat-fd=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_fd = ParseFd(option, "--input-oat-fd="); - } else if (option.starts_with("--input-vdex-fd=")) { - input_vdex_fd = ParseFd(option, "--input-vdex-fd="); } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); - } else if (option.starts_with("--output-oat-file=")) { - if (have_output_oat) { - Usage("Only one of --output-oat-file, and --output-oat-fd may be used."); - } - have_output_oat = true; - output_oat_filename = option.substr(strlen("--output-oat-file=")).data(); - } else if (option.starts_with("--output-oat-fd=")) { - if (have_output_oat) { - Usage("Only one of --output-oat-file, --output-oat-fd may be used."); - } - have_output_oat = true; - output_oat_fd = ParseFd(option, "--output-oat-fd="); - } else if (option.starts_with("--output-vdex-fd=")) { - output_vdex_fd = ParseFd(option, "--output-vdex-fd="); } else if (option.starts_with("--output-image-file=")) { output_image_filename = option.substr(strlen("--output-image-file=")).data(); } else if (option.starts_with("--base-offset-delta=")) { @@ -1467,12 +817,6 @@ static int patchoat(int argc, char **argv) { if (!ParseInt(base_delta_str, &base_delta)) { Usage("Failed to parse --base-offset-delta argument '%s' as an off_t", base_delta_str); } - } else if (option.starts_with("--patched-image-location=")) { - patched_image_location = option.substr(strlen("--patched-image-location=")).data(); - } else if (option == "--lock-output") { - lock_output = true; - } else if (option == "--no-lock-output") { - lock_output = false; } else if (option == "--dump-timings") { dump_timings = true; } else if (option == "--no-dump-timings") { @@ -1487,33 +831,13 @@ static int patchoat(int argc, char **argv) { Usage("Instruction set must be set."); } - int ret; - if (!input_image_location.empty()) { - ret = patchoat_image(timings, - isa, - input_image_location, - output_image_filename, - base_delta, - base_delta_set, - debug); - } else { - ret = patchoat_oat(timings, - isa, - patched_image_location, - base_delta, - base_delta_set, - input_oat_fd, - input_vdex_fd, - input_oat_location, - input_oat_filename, - have_input_oat, - output_oat_fd, - output_vdex_fd, - output_oat_filename, - have_output_oat, - lock_output, - debug); - } + int ret = patchoat_image(timings, + isa, + input_image_location, + output_image_filename, + base_delta, + base_delta_set, + debug); timings.EndTiming(); if (dump_timings) { diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index a51963127a..e15a6bc695 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -44,17 +44,7 @@ class Class; class PatchOat { public: - // Patch only the oat file - static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, - bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? - - // Patch only the image (art file) - static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, - TimingLogger* timings); - - // Patch both the image and the oat file - static bool Patch(const std::string& art_location, + static bool Patch(const std::string& image_location, off_t delta, const std::string& output_directory, InstructionSet isa, @@ -64,18 +54,11 @@ class PatchOat { PatchOat(PatchOat&&) = default; private: - // Takes ownership only of the ElfFile. All other pointers are only borrowed. - PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) - : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), - isa_(kNone), space_map_(nullptr), timings_(timings) {} - PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, - MemMap* heap, off_t delta, TimingLogger* timings) - : image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {} - PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image, + // All pointers are only borrowed. + PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings) - : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap), + : image_(image), bitmap_(bitmap), heap_(heap), delta_(delta), isa_(isa), space_map_(map), timings_(timings) {} // Was the .art image at image_path made with --compile-pic ? @@ -94,9 +77,7 @@ class PatchOat { // Attempt to replace the file with a symlink // Returns false if it fails static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out); // Output oat was newly created? + const std::string& output_oat_filename); static void BitmapCallback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -108,13 +89,6 @@ class PatchOat { void FixupMethod(ArtMethod* object, ArtMethod* copy) REQUIRES_SHARED(Locks::mutator_lock_); - // Patches oat in place, modifying the oat_file given to the constructor. - bool PatchElf(); - template <typename ElfFileImpl> - bool PatchElf(ElfFileImpl* oat_file); - template <typename ElfFileImpl> - bool PatchOatHeader(ElfFileImpl* oat_file); - bool PatchImage(bool primary_image) REQUIRES_SHARED(Locks::mutator_lock_); void PatchArtFields(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); void PatchArtMethods(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); @@ -128,7 +102,6 @@ class PatchOat { void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots) REQUIRES_SHARED(Locks::mutator_lock_); - bool WriteElf(File* out); bool WriteImage(File* out); template <typename T> @@ -175,19 +148,6 @@ class PatchOat { return reinterpret_cast<T*>(ret); } - template <typename T> - T RelocatedAddressOfIntPointer(T obj) const { - if (obj == 0) { - return obj; - } - T ret = obj + delta_; - // Trim off high bits in case negative relocation with 64 bit patchoat. - if (Is32BitISA()) { - ret = static_cast<T>(static_cast<uint32_t>(ret)); - } - return ret; - } - bool Is32BitISA() const { return InstructionSetPointerSize(isa_) == PointerSize::k32; } @@ -213,8 +173,6 @@ class PatchOat { mirror::Object* const copy_; }; - // The elf file we are patching. - std::unique_ptr<ElfFile> oat_file_; // A mmap of the image we are patching. This is modified. const MemMap* const image_; // The bitmap over the image within the heap we are patching. This is not modified. diff --git a/runtime/art_method.h b/runtime/art_method.h index 3d51fdde94..99d7a49dc9 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -223,13 +223,10 @@ class ArtMethod FINAL { } bool IsObsolete() { - // TODO Should maybe make this IsIntrinsic check not needed - return !IsIntrinsic() && (GetAccessFlags() & kAccObsoleteMethod) != 0; + return (GetAccessFlags() & kAccObsoleteMethod) != 0; } void SetIsObsolete() { - // TODO We should really support redefining intrinsic if possible. - DCHECK(!IsIntrinsic()); AddAccessFlags(kAccObsoleteMethod); } diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 0325535a1b..a5bb91a71e 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -327,7 +327,7 @@ class ModUnionCheckReferences { class EmptyMarkObjectVisitor : public MarkObjectVisitor { public: mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE {return obj;} - void MarkHeapReference(mirror::HeapReference<mirror::Object>*) OVERRIDE {} + void MarkHeapReference(mirror::HeapReference<mirror::Object>*, bool) OVERRIDE {} }; void ModUnionTable::FilterCards() { @@ -459,7 +459,7 @@ void ModUnionTableReferenceCache::UpdateAndMarkReferences(MarkObjectVisitor* vis for (mirror::HeapReference<mirror::Object>* obj_ptr : references) { if (obj_ptr->AsMirrorPtr() != nullptr) { all_null = false; - visitor->MarkHeapReference(obj_ptr); + visitor->MarkHeapReference(obj_ptr, /*do_atomic_update*/ false); } } count += references.size(); diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index cf63b30d43..48a8742cc8 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -97,7 +97,8 @@ class ModUnionTableTest : public CommonRuntimeTest { class CollectVisitedVisitor : public MarkObjectVisitor { public: explicit CollectVisitedVisitor(std::set<mirror::Object*>* out) : out_(out) {} - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* ref) OVERRIDE + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* ref, + bool do_atomic_update ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(ref != nullptr); MarkObject(ref->AsMirrorPtr()); diff --git a/runtime/gc/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc index 29bab01934..7b1e2b83c3 100644 --- a/runtime/gc/accounting/remembered_set.cc +++ b/runtime/gc/accounting/remembered_set.cc @@ -74,7 +74,7 @@ class RememberedSetReferenceVisitor { mirror::HeapReference<mirror::Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset); if (target_space_->HasAddress(ref_ptr->AsMirrorPtr())) { *contains_reference_to_target_space_ = true; - collector_->MarkHeapReference(ref_ptr); + collector_->MarkHeapReference(ref_ptr, /*do_atomic_update*/ false); DCHECK(!target_space_->HasAddress(ref_ptr->AsMirrorPtr())); } } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index f18ffb4aef..3d2fd0bb07 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -109,12 +109,29 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, } } -void ConcurrentCopying::MarkHeapReference(mirror::HeapReference<mirror::Object>* from_ref) { - // Used for preserving soft references, should be OK to not have a CAS here since there should be - // no other threads which can trigger read barriers on the same referent during reference - // processing. - from_ref->Assign(Mark(from_ref->AsMirrorPtr())); - DCHECK(!from_ref->IsNull()); +void ConcurrentCopying::MarkHeapReference(mirror::HeapReference<mirror::Object>* field, + bool do_atomic_update) { + if (UNLIKELY(do_atomic_update)) { + // Used to mark the referent in DelayReferenceReferent in transaction mode. + mirror::Object* from_ref = field->AsMirrorPtr(); + if (from_ref == nullptr) { + return; + } + mirror::Object* to_ref = Mark(from_ref); + if (from_ref != to_ref) { + do { + if (field->AsMirrorPtr() != from_ref) { + // Concurrently overwritten by a mutator. + break; + } + } while (!field->CasWeakRelaxed(from_ref, to_ref)); + } + } else { + // Used for preserving soft references, should be OK to not have a CAS here since there should be + // no other threads which can trigger read barriers on the same referent during reference + // processing. + field->Assign(Mark(field->AsMirrorPtr())); + } } ConcurrentCopying::~ConcurrentCopying() { diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 844bb450cc..073326de2d 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -176,7 +176,8 @@ class ConcurrentCopying : public GarbageCollector { virtual mirror::Object* MarkObject(mirror::Object* from_ref) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* from_ref) OVERRIDE + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* from_ref, + bool do_atomic_update) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); virtual mirror::Object* IsMarked(mirror::Object* from_ref) OVERRIDE diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 14fd332b57..1e4196b1ac 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -65,7 +65,8 @@ GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) name_(name), pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount), cumulative_timings_(name), - pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true) { + pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true), + is_transaction_active_(false) { ResetCumulativeStatistics(); } @@ -88,6 +89,9 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { uint64_t start_time = NanoTime(); Iteration* current_iteration = GetCurrentIteration(); current_iteration->Reset(gc_cause, clear_soft_references); + // Note transaction mode is single-threaded and there's no asynchronous GC and this flag doesn't + // change in the middle of a GC. + is_transaction_active_ = Runtime::Current()->IsActiveTransaction(); RunPhases(); // Run all the GC phases. // Add the current timings to the cumulative timings. cumulative_timings_.AddLogger(*GetTimings()); @@ -109,6 +113,7 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { MutexLock mu(self, pause_histogram_lock_); pause_histogram_.AdjustAndAddValue(pause_time); } + is_transaction_active_ = false; } void GarbageCollector::SwapBitmaps() { diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 95601d736d..14d049971f 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -199,12 +199,17 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark // Force mark an object. virtual mirror::Object* MarkObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) = 0; - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj) + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj, + bool do_atomic_update) REQUIRES_SHARED(Locks::mutator_lock_) = 0; virtual void DelayReferenceReferent(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> reference) REQUIRES_SHARED(Locks::mutator_lock_) = 0; + bool IsTransactionActive() const { + return is_transaction_active_; + } + protected: // Run all of the GC phases. virtual void RunPhases() = 0; @@ -223,6 +228,7 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark int64_t total_freed_bytes_; CumulativeLogger cumulative_timings_; mutable Mutex pause_histogram_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + bool is_transaction_active_; private: DISALLOW_IMPLICIT_CONSTRUCTORS(GarbageCollector); diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 85e6783599..00393881e9 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -260,7 +260,8 @@ inline void MarkCompact::MarkStackPush(mirror::Object* obj) { mark_stack_->PushBack(obj); } -void MarkCompact::MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr) { +void MarkCompact::MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr, + bool do_atomic_update ATTRIBUTE_UNUSED) { if (updating_references_) { UpdateHeapReference(obj_ptr); } else { diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 6d52d5d515..85727c25c2 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -170,7 +170,8 @@ class MarkCompact : public GarbageCollector { // Mark a single object. virtual mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr) OVERRIDE + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr, + bool do_atomic_update) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE REQUIRES_SHARED(Locks::heap_bitmap_lock_) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index f00da73458..f591cf09ca 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -532,7 +532,8 @@ inline bool MarkSweep::MarkObjectParallel(mirror::Object* obj) { return !mark_bitmap_->AtomicTestAndSet(obj, visitor); } -void MarkSweep::MarkHeapReference(mirror::HeapReference<mirror::Object>* ref) { +void MarkSweep::MarkHeapReference(mirror::HeapReference<mirror::Object>* ref, + bool do_atomic_update ATTRIBUTE_UNUSED) { MarkObject(ref->AsMirrorPtr(), nullptr, MemberOffset(0)); } diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index a6e2d61f6d..5a9b9f8765 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -216,7 +216,8 @@ class MarkSweep : public GarbageCollector { REQUIRES(!mark_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* ref) OVERRIDE + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* ref, + bool do_atomic_update) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_) REQUIRES(!mark_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index cb9e7e2c15..4c0f317aca 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -606,7 +606,8 @@ mirror::Object* SemiSpace::MarkObject(mirror::Object* root) { return ref.AsMirrorPtr(); } -void SemiSpace::MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr) { +void SemiSpace::MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr, + bool do_atomic_update ATTRIBUTE_UNUSED) { MarkObject(obj_ptr); } diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 52b5e5fe30..9d6e74dde4 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -110,7 +110,8 @@ class SemiSpace : public GarbageCollector { virtual mirror::Object* MarkObject(mirror::Object* root) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr) OVERRIDE + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj_ptr, + bool do_atomic_update) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); void ScanObject(mirror::Object* obj) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 34afa2aa7e..53be30eafc 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3332,7 +3332,7 @@ struct IdentityMarkHeapReferenceVisitor : public MarkObjectVisitor { virtual mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE { return obj; } - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>*) OVERRIDE { + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>*, bool) OVERRIDE { } }; diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 86b152211c..65a550ee5c 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -139,6 +139,14 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, CHECK_EQ(!self->GetWeakRefAccessEnabled(), concurrent); } } + if (kIsDebugBuild && collector->IsTransactionActive()) { + // In transaction mode, we shouldn't enqueue any Reference to the queues. + // See DelayReferenceReferent(). + DCHECK(soft_reference_queue_.IsEmpty()); + DCHECK(weak_reference_queue_.IsEmpty()); + DCHECK(finalizer_reference_queue_.IsEmpty()); + DCHECK(phantom_reference_queue_.IsEmpty()); + } // Unless required to clear soft references with white references, preserve some white referents. if (!clear_soft_references) { TimingLogger::ScopedTiming split(concurrent ? "ForwardSoftReferences" : @@ -206,6 +214,15 @@ void ReferenceProcessor::DelayReferenceReferent(ObjPtr<mirror::Class> klass, // do_atomic_update needs to be true because this happens outside of the reference processing // phase. if (!collector->IsNullOrMarkedHeapReference(referent, /*do_atomic_update*/true)) { + if (UNLIKELY(collector->IsTransactionActive())) { + // In transaction mode, keep the referent alive and avoid any reference processing to avoid the + // issue of rolling back reference processing. do_atomic_update needs to be true because this + // happens outside of the reference processing phase. + if (!referent->IsNull()) { + collector->MarkHeapReference(referent, /*do_atomic_update*/ true); + } + return; + } Thread* self = Thread::Current(); // TODO: Remove these locks, and use atomic stacks for storing references? // We need to check that the references haven't already been enqueued since we can end up diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc index 734caea371..fd5dcf9de6 100644 --- a/runtime/gc/reference_queue.cc +++ b/runtime/gc/reference_queue.cc @@ -67,6 +67,11 @@ ObjPtr<mirror::Reference> ReferenceQueue::DequeuePendingReference() { list_->SetPendingNext(next); } ref->SetPendingNext(nullptr); + return ref; +} + +// This must be called whenever DequeuePendingReference is called. +void ReferenceQueue::DisableReadBarrierForReference(ObjPtr<mirror::Reference> ref) { Heap* heap = Runtime::Current()->GetHeap(); if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC && heap->ConcurrentCopyingCollector()->IsActive()) { @@ -92,7 +97,6 @@ ObjPtr<mirror::Reference> ReferenceQueue::DequeuePendingReference() { } } } - return ref; } void ReferenceQueue::Dump(std::ostream& os) const { @@ -140,6 +144,9 @@ void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references, } cleared_references->EnqueueReference(ref); } + // Delay disabling the read barrier until here so that the ClearReferent call above in + // transaction mode will trigger the read barrier. + DisableReadBarrierForReference(ref); } } @@ -162,6 +169,9 @@ void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_referenc } cleared_references->EnqueueReference(ref); } + // Delay disabling the read barrier until here so that the ClearReferent call above in + // transaction mode will trigger the read barrier. + DisableReadBarrierForReference(ref->AsReference()); } } @@ -174,7 +184,9 @@ void ReferenceQueue::ForwardSoftReferences(MarkObjectVisitor* visitor) { do { mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr(); if (referent_addr->AsMirrorPtr() != nullptr) { - visitor->MarkHeapReference(referent_addr); + // do_atomic_update is false because mutators can't access the referent due to the weak ref + // access blocking. + visitor->MarkHeapReference(referent_addr, /*do_atomic_update*/ false); } ref = ref->GetPendingNext(); } while (LIKELY(ref != head)); diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index b5ec1e5341..b73a880a8a 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -63,8 +63,15 @@ class ReferenceQueue { void EnqueueReference(ObjPtr<mirror::Reference> ref) REQUIRES_SHARED(Locks::mutator_lock_); // Dequeue a reference from the queue and return that dequeued reference. + // Call DisableReadBarrierForReference for the reference that's returned from this function. ObjPtr<mirror::Reference> DequeuePendingReference() REQUIRES_SHARED(Locks::mutator_lock_); + // If applicable, disable the read barrier for the reference after its referent is handled (see + // ConcurrentCopying::ProcessMarkStackRef.) This must be called for a reference that's dequeued + // from pending queue (DequeuePendingReference). + void DisableReadBarrierForReference(ObjPtr<mirror::Reference> ref) + REQUIRES_SHARED(Locks::mutator_lock_); + // Enqueues finalizer references with white referents. White referents are blackened, moved to // the zombie field, and the referent field is cleared. void EnqueueFinalizerReferences(ReferenceQueue* cleared_references, diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index adb7d8a10c..f6720bd3b2 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -250,7 +250,7 @@ class PACKED(4) FixedSizeHandleScope : public HandleScope { StackReference<mirror::Object> storage_[kNumReferences]; // Position new handles will be created. - size_t pos_ = 0; + uint32_t pos_ = 0; template<size_t kNumRefs> friend class StackHandleScope; friend class VariableSizedHandleScope; @@ -299,12 +299,20 @@ class VariableSizedHandleScope : public BaseHandleScope { void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); private: - static constexpr size_t kNumReferencesPerScope = 4; + static constexpr size_t kLocalScopeSize = 64u; + static constexpr size_t kSizeOfReferencesPerScope = + kLocalScopeSize + - /* BaseHandleScope::link_ */ sizeof(BaseHandleScope*) + - /* BaseHandleScope::number_of_references_ */ sizeof(int32_t) + - /* FixedSizeHandleScope<>::pos_ */ sizeof(uint32_t); + static constexpr size_t kNumReferencesPerScope = + kSizeOfReferencesPerScope / sizeof(StackReference<mirror::Object>); Thread* const self_; // Linked list of fixed size handle scopes. using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>; + static_assert(sizeof(LocalScopeType) == kLocalScopeSize, "Unexpected size of LocalScopeType"); LocalScopeType* current_scope_; DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index af0478c1eb..80554c240d 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1330,17 +1330,18 @@ void UnstartedRuntime::UnstartedStringCharAt( result->SetC(string->CharAt(index)); } -// This allows setting chars from the new style of String objects during compilation. -void UnstartedRuntime::UnstartedStringSetCharAt( - Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) { - jint index = shadow_frame->GetVReg(arg_offset + 1); - jchar c = shadow_frame->GetVReg(arg_offset + 2); - mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString(); +// This allows creating String objects with replaced characters during compilation. +// String.doReplace(char, char) is called from String.replace(char, char) when there is a match. +void UnstartedRuntime::UnstartedStringDoReplace( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + jchar old_c = shadow_frame->GetVReg(arg_offset + 1); + jchar new_c = shadow_frame->GetVReg(arg_offset + 2); + ObjPtr<mirror::String> string = shadow_frame->GetVRegReference(arg_offset)->AsString(); if (string == nullptr) { - AbortTransactionOrFail(self, "String.setCharAt with null object"); + AbortTransactionOrFail(self, "String.replaceWithMatch with null object"); return; } - string->SetCharAt(index, c); + result->SetL(string->DoReplace(self, old_c, new_c)); } // This allows creating the new style of String objects during compilation. diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index 6fc7989acf..e9435e466b 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -63,7 +63,7 @@ V(RuntimeAvailableProcessors, "int java.lang.Runtime.availableProcessors()") \ V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \ V(StringCharAt, "char java.lang.String.charAt(int)") \ - V(StringSetCharAt, "void java.lang.String.setCharAt(int, char)") \ + V(StringDoReplace, "java.lang.String java.lang.String.doReplace(char, char)") \ V(StringFactoryNewStringFromChars, "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])") \ V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \ V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \ diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index c226a38299..b1ba95287b 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1264,12 +1264,27 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca if (cls == nullptr) { break; } - const DexFile& class_dex_file = cls->GetDexFile(); - dex::TypeIndex type_index = cls->GetDexTypeIndex(); - if (ContainsElement(dex_base_locations, class_dex_file.GetBaseLocation())) { + + const DexFile* class_dex_file = nullptr; + dex::TypeIndex type_index; + + if (cls->GetDexCache() == nullptr) { + DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); + class_dex_file = dex_file; + type_index = cls->FindTypeIndexInOtherDexFile(*dex_file); + } else { + class_dex_file = &(cls->GetDexFile()); + type_index = cls->GetDexTypeIndex(); + } + if (!type_index.IsValid()) { + // Could be a proxy class or an array for which we couldn't find the type index. + // TODO(calin): can we really miss the type index for arrays here? + continue; + } + if (ContainsElement(dex_base_locations, class_dex_file->GetBaseLocation())) { // Only consider classes from the same apk (including multidex). profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/ - &class_dex_file, type_index); + class_dex_file, type_index); } } if (!profile_classes.empty()) { diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 547b5b8a2d..5418d3569e 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2265,7 +2265,18 @@ class JNI { VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]"; - is_fast = is_fast || m->IsFastNative(); // Merge with @FastNative state. + if (UNLIKELY(is_fast)) { + // There are a few reasons to switch: + // 1) We don't support !bang JNI anymore, it will turn to a hard error later. + // 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI. + // and switching is super easy, remove ! in C code, add annotation in .java code. + // 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess + // since that checks for presence of @FastNative and not for ! in the descriptor. + LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod(); + is_fast = false; + // TODO: make this a hard register error in the future. + } + m->RegisterNative(fnPtr, is_fast); } return JNI_OK; diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index b3837c409d..580a42bcef 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -19,20 +19,10 @@ #include <jni.h> #include <iosfwd> +#include "nativehelper/jni_macros.h" #include "base/macros.h" -#ifndef NATIVE_METHOD -#define NATIVE_METHOD(className, functionName, signature) \ - { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) } -#endif - -// TODO: Can we do a better job of supporting overloading ? -#ifndef OVERLOADED_NATIVE_METHOD -#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \ - { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) } -#endif - #define REGISTER_NATIVE_METHODS(jni_class_name) \ RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods)) diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index c2407d7772..57b20a193b 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -36,7 +36,7 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize(PointerSize pointer_size) { - uint32_t vtable_entries = Object::kVTableLength + 57; + uint32_t vtable_entries = Object::kVTableLength + 56; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size); } @@ -311,9 +311,7 @@ template<typename MemoryType> inline bool String::AllASCII(const MemoryType* chars, const int length) { static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType"); for (int i = 0; i < length; ++i) { - // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII - // because it would complicate the detection of ASCII strings in Modified-UTF8. - if ((chars[i] - 1u) >= 0x7fu) { + if (!IsASCII(chars[i])) { return false; } } diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 0ab0bd6794..884b88a6c1 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -79,14 +79,55 @@ int32_t String::GetUtfLength() { } } -void String::SetCharAt(int32_t index, uint16_t c) { - DCHECK((index >= 0) && (index < GetLength())); - if (IsCompressed()) { - // TODO: Handle the case where String is compressed and c is non-ASCII - GetValueCompressed()[index] = static_cast<uint8_t>(c); +inline bool String::AllASCIIExcept(const uint16_t* chars, int32_t length, uint16_t non_ascii) { + DCHECK(!IsASCII(non_ascii)); + for (int32_t i = 0; i < length; ++i) { + if (!IsASCII(chars[i]) && chars[i] != non_ascii) { + return false; + } + } + return true; +} + +ObjPtr<String> String::DoReplace(Thread* self, uint16_t old_c, uint16_t new_c) { + DCHECK(IsCompressed() ? ContainsElement(ArrayRef<uint8_t>(value_compressed_, GetLength()), old_c) + : ContainsElement(ArrayRef<uint16_t>(value_, GetLength()), old_c)); + int32_t length = GetLength(); + bool compressible = + kUseStringCompression && + IsASCII(new_c) && + (IsCompressed() || (!IsASCII(old_c) && AllASCIIExcept(value_, length, old_c))); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + const int32_t length_with_flag = String::GetFlaggedCount(GetLength(), compressible); + SetStringCountVisitor visitor(length_with_flag); + ObjPtr<String> string = Alloc<true>(self, length_with_flag, allocator_type, visitor); + if (UNLIKELY(string == nullptr)) { + return nullptr; + } + if (compressible) { + auto replace = [old_c, new_c](uint16_t c) { + return dchecked_integral_cast<uint8_t>((old_c != c) ? c : new_c); + }; + uint8_t* out = string->value_compressed_; + if (LIKELY(IsCompressed())) { // LIKELY(compressible == IsCompressed()) + std::transform(value_compressed_, value_compressed_ + length, out, replace); + } else { + std::transform(value_, value_ + length, out, replace); + } + DCHECK(kUseStringCompression && AllASCII(out, length)); } else { - GetValue()[index] = c; + auto replace = [old_c, new_c](uint16_t c) { + return (old_c != c) ? c : new_c; + }; + uint16_t* out = string->value_; + if (UNLIKELY(IsCompressed())) { // LIKELY(compressible == IsCompressed()) + std::transform(value_compressed_, value_compressed_ + length, out, replace); + } else { + std::transform(value_, value_ + length, out, replace); + } + DCHECK(!kUseStringCompression || !AllASCII(out, length)); } + return string; } String* String::AllocFromStrings(Thread* self, Handle<String> string, Handle<String> string2) { diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 38f6dd4b6f..35ce98efad 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -94,7 +94,10 @@ class MANAGED String FINAL : public Object { uint16_t CharAt(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_); - void SetCharAt(int32_t index, uint16_t c) REQUIRES_SHARED(Locks::mutator_lock_); + // Create a new string where all occurences of `old_c` are replaced with `new_c`. + // String.doReplace(char, char) is called from String.replace(char, char) when there is a match. + ObjPtr<String> DoReplace(Thread* self, uint16_t old_c, uint16_t new_c) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<String> Intern() REQUIRES_SHARED(Locks::mutator_lock_); @@ -229,6 +232,14 @@ class MANAGED String FINAL : public Object { REQUIRES_SHARED(Locks::mutator_lock_); private: + static constexpr bool IsASCII(uint16_t c) { + // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII + // because it would complicate the detection of ASCII strings in Modified-UTF8. + return (c - 1u) < 0x7fu; + } + + static bool AllASCIIExcept(const uint16_t* chars, int32_t length, uint16_t non_ascii); + void SetHashCode(int32_t new_hash_code) REQUIRES_SHARED(Locks::mutator_lock_) { // Hash code is invariant so use non-transactional mode. Also disable check as we may run inside // a transaction. diff --git a/runtime/modifiers.h b/runtime/modifiers.h index ae6b31d2fe..461f870b1c 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -45,6 +45,9 @@ static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sou static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init> static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only) static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only) +// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its +// declaring class. This flag may only be applied to methods. +static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime) // Used by a method to denote that its execution does not need to go through slow path interpreter. static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (dex only) // Used by a class to denote that the verifier has attempted to check it at least once. @@ -67,10 +70,6 @@ static constexpr uint32_t kAccCompileDontBother = 0x01000000; // method (ru // Set by the verifier for a method that could not be verified to follow structured locking. static constexpr uint32_t kAccMustCountLocks = 0x02000000; // method (runtime) -// Set to indicate that the ArtMethod is obsolete and has a different DexCache from its declaring -// class. -// TODO Might want to re-arrange some of these so that we can have obsolete + intrinsic methods. -static constexpr uint32_t kAccObsoleteMethod = 0x04000000; // method (runtime) // Set by the class linker for a method that has only one implementation for a // virtual call. diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 0d24587998..f6a73a8660 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -537,14 +537,14 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), - NATIVE_METHOD(VMDebug, getLoadedClassCount, "!()I"), + FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, infopoint, "(I)V"), - NATIVE_METHOD(VMDebug, isDebuggerConnected, "!()Z"), - NATIVE_METHOD(VMDebug, isDebuggingEnabled, "!()Z"), + FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"), + FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"), NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), - NATIVE_METHOD(VMDebug, lastDebuggerActivity, "!()J"), - NATIVE_METHOD(VMDebug, printLoadedClasses, "!(I)V"), + FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"), + FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"), NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), @@ -557,7 +557,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), - NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"), + FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"), diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 6bfccdc8fb..efc42fdac7 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -642,7 +642,7 @@ static jboolean VMRuntime_didPruneDalvikCache(JNIEnv* env ATTRIBUTE_UNUSED, } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), + FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), @@ -650,11 +650,11 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), - NATIVE_METHOD(VMRuntime, isDebuggerActive, "!()Z"), - NATIVE_METHOD(VMRuntime, isNativeDebuggable, "!()Z"), + FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), + FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), - NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), - NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"), @@ -671,8 +671,8 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmInstructionSet, "()Ljava/lang/String;"), - NATIVE_METHOD(VMRuntime, is64Bit, "!()Z"), - NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"), + FAST_NATIVE_METHOD(VMRuntime, is64Bit, "()Z"), + FAST_NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "()Z"), NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"), diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index be6f7f2204..0dfafa4a7c 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -139,11 +139,11 @@ 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;"), - NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"), - NATIVE_METHOD(VMStack, getThreadStackTrace, "!(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), + FAST_NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"), + FAST_NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"), + FAST_NATIVE_METHOD(VMStack, getClosestUserClassLoader, "()Ljava/lang/ClassLoader;"), + FAST_NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"), + FAST_NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), }; void register_dalvik_system_VMStack(JNIEnv* env) { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 256787b2a1..c8431c0519 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -713,36 +713,36 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Class, classForName, - "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), - NATIVE_METHOD(Class, getDeclaredAnnotation, - "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Class, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Class, getDeclaredClasses, "!()[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, getPublicFieldRecursive, "!(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, getDeclaringClass, "!()Ljava/lang/Class;"), - NATIVE_METHOD(Class, getEnclosingClass, "!()Ljava/lang/Class;"), - NATIVE_METHOD(Class, getEnclosingConstructorNative, "!()Ljava/lang/reflect/Constructor;"), - NATIVE_METHOD(Class, getEnclosingMethodNative, "!()Ljava/lang/reflect/Method;"), - NATIVE_METHOD(Class, getInnerClassFlags, "!(I)I"), - NATIVE_METHOD(Class, getInnerClassName, "!()Ljava/lang/String;"), - NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), - NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), - NATIVE_METHOD(Class, getPublicDeclaredFields, "!()[Ljava/lang/reflect/Field;"), - NATIVE_METHOD(Class, getSignatureAnnotation, "!()[Ljava/lang/String;"), - NATIVE_METHOD(Class, isAnonymousClass, "!()Z"), - NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "!(Ljava/lang/Class;)Z"), - NATIVE_METHOD(Class, newInstance, "!()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Class, classForName, + "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getDeclaredAnnotation, + "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Class, getDeclaredClasses, "()[Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getDeclaredConstructorInternal, + "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"), + FAST_NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "(Z)[Ljava/lang/reflect/Constructor;"), + FAST_NATIVE_METHOD(Class, getDeclaredField, "(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + FAST_NATIVE_METHOD(Class, getPublicFieldRecursive, "(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + FAST_NATIVE_METHOD(Class, getDeclaredFields, "()[Ljava/lang/reflect/Field;"), + FAST_NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "(Z)[Ljava/lang/reflect/Field;"), + FAST_NATIVE_METHOD(Class, getDeclaredMethodInternal, + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"), + FAST_NATIVE_METHOD(Class, getDeclaredMethodsUnchecked, + "(Z)[Ljava/lang/reflect/Method;"), + FAST_NATIVE_METHOD(Class, getDeclaringClass, "()Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getEnclosingClass, "()Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getEnclosingConstructorNative, "()Ljava/lang/reflect/Constructor;"), + FAST_NATIVE_METHOD(Class, getEnclosingMethodNative, "()Ljava/lang/reflect/Method;"), + FAST_NATIVE_METHOD(Class, getInnerClassFlags, "(I)I"), + FAST_NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"), + FAST_NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"), + FAST_NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"), + FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"), + FAST_NATIVE_METHOD(Class, isAnonymousClass, "()Z"), + FAST_NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"), + FAST_NATIVE_METHOD(Class, newInstance, "()Ljava/lang/Object;"), }; void register_java_lang_Class(JNIEnv* env) { diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index ee6dda56a5..8fda4dfaaf 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -95,11 +95,11 @@ static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint s } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(DexCache, getDexNative, "!()Lcom/android/dex/Dex;"), - NATIVE_METHOD(DexCache, getResolvedType, "!(I)Ljava/lang/Class;"), - NATIVE_METHOD(DexCache, getResolvedString, "!(I)Ljava/lang/String;"), - NATIVE_METHOD(DexCache, setResolvedType, "!(ILjava/lang/Class;)V"), - NATIVE_METHOD(DexCache, setResolvedString, "!(ILjava/lang/String;)V"), + FAST_NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"), + FAST_NATIVE_METHOD(DexCache, getResolvedType, "(I)Ljava/lang/Class;"), + FAST_NATIVE_METHOD(DexCache, getResolvedString, "(I)Ljava/lang/String;"), + FAST_NATIVE_METHOD(DexCache, setResolvedType, "(ILjava/lang/Class;)V"), + FAST_NATIVE_METHOD(DexCache, setResolvedString, "(ILjava/lang/String;)V"), }; void register_java_lang_DexCache(JNIEnv* env) { diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index 6493865c99..6989244280 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -20,7 +20,6 @@ #include "mirror/object-inl.h" #include "scoped_fast_native_object_access-inl.h" - namespace art { static jobject Object_internalClone(JNIEnv* env, jobject java_this) { @@ -50,11 +49,11 @@ static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;"), - NATIVE_METHOD(Object, notify, "!()V"), - NATIVE_METHOD(Object, notifyAll, "!()V"), - OVERLOADED_NATIVE_METHOD(Object, wait, "!()V", wait), - OVERLOADED_NATIVE_METHOD(Object, wait, "!(JI)V", waitJI), + FAST_NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Object, notify, "()V"), + FAST_NATIVE_METHOD(Object, notifyAll, "()V"), + OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "()V", wait), + OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "(JI)V", waitJI), }; void register_java_lang_Object(JNIEnv* env) { diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index f1d6ff5f70..2e561ffa46 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -99,9 +99,11 @@ static jstring String_intern(JNIEnv* env, jobject java_this) { return soa.AddLocalReference<jstring>(result); } -static void String_setCharAt(JNIEnv* env, jobject java_this, jint index, jchar c) { +static jstring String_doReplace(JNIEnv* env, jobject java_this, jchar old_c, jchar new_c) { ScopedFastNativeObjectAccess soa(env); - soa.Decode<mirror::String>(java_this)->SetCharAt(index, c); + ObjPtr<mirror::String> result = + soa.Decode<mirror::String>(java_this)->DoReplace(soa.Self(), old_c, new_c); + return soa.AddLocalReference<jstring>(result); } static jcharArray String_toCharArray(JNIEnv* env, jobject java_this) { @@ -111,15 +113,15 @@ static jcharArray String_toCharArray(JNIEnv* env, jobject java_this) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(String, charAt, "!(I)C"), - NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"), - NATIVE_METHOD(String, concat, "!(Ljava/lang/String;)Ljava/lang/String;"), - NATIVE_METHOD(String, fastIndexOf, "!(II)I"), - NATIVE_METHOD(String, fastSubstring, "!(II)Ljava/lang/String;"), - NATIVE_METHOD(String, getCharsNoCheck, "!(II[CI)V"), - NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"), - NATIVE_METHOD(String, setCharAt, "!(IC)V"), - NATIVE_METHOD(String, toCharArray, "!()[C"), + FAST_NATIVE_METHOD(String, charAt, "(I)C"), + FAST_NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"), + FAST_NATIVE_METHOD(String, concat, "(Ljava/lang/String;)Ljava/lang/String;"), + FAST_NATIVE_METHOD(String, doReplace, "(CC)Ljava/lang/String;"), + FAST_NATIVE_METHOD(String, fastIndexOf, "(II)I"), + FAST_NATIVE_METHOD(String, fastSubstring, "(II)Ljava/lang/String;"), + FAST_NATIVE_METHOD(String, getCharsNoCheck, "(II[CI)V"), + FAST_NATIVE_METHOD(String, intern, "()Ljava/lang/String;"), + FAST_NATIVE_METHOD(String, toCharArray, "()[C"), }; void register_java_lang_String(JNIEnv* env) { diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index e0738a49b9..ec3c7c2fdf 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -87,9 +87,9 @@ static jstring StringFactory_newStringFromString(JNIEnv* env, jclass, jstring to } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(StringFactory, newStringFromBytes, "!([BIII)Ljava/lang/String;"), - NATIVE_METHOD(StringFactory, newStringFromChars, "!(II[C)Ljava/lang/String;"), - NATIVE_METHOD(StringFactory, newStringFromString, "!(Ljava/lang/String;)Ljava/lang/String;"), + FAST_NATIVE_METHOD(StringFactory, newStringFromBytes, "([BIII)Ljava/lang/String;"), + FAST_NATIVE_METHOD(StringFactory, newStringFromChars, "(II[C)Ljava/lang/String;"), + FAST_NATIVE_METHOD(StringFactory, newStringFromString, "(Ljava/lang/String;)Ljava/lang/String;"), }; void register_java_lang_StringFactory(JNIEnv* env) { diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 7f8da80ff8..d7c9cd07b5 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -237,16 +237,16 @@ static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"), - NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"), - NATIVE_METHOD(System, arraycopyByteUnchecked, "!([BI[BII)V"), - NATIVE_METHOD(System, arraycopyShortUnchecked, "!([SI[SII)V"), - NATIVE_METHOD(System, arraycopyIntUnchecked, "!([II[III)V"), - NATIVE_METHOD(System, arraycopyLongUnchecked, "!([JI[JII)V"), - NATIVE_METHOD(System, arraycopyFloatUnchecked, "!([FI[FII)V"), - NATIVE_METHOD(System, arraycopyDoubleUnchecked, "!([DI[DII)V"), - NATIVE_METHOD(System, arraycopyBooleanUnchecked, "!([ZI[ZII)V"), - NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"), + FAST_NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"), + FAST_NATIVE_METHOD(System, arraycopyCharUnchecked, "([CI[CII)V"), + FAST_NATIVE_METHOD(System, arraycopyByteUnchecked, "([BI[BII)V"), + FAST_NATIVE_METHOD(System, arraycopyShortUnchecked, "([SI[SII)V"), + FAST_NATIVE_METHOD(System, arraycopyIntUnchecked, "([II[III)V"), + FAST_NATIVE_METHOD(System, arraycopyLongUnchecked, "([JI[JII)V"), + FAST_NATIVE_METHOD(System, arraycopyFloatUnchecked, "([FI[FII)V"), + FAST_NATIVE_METHOD(System, arraycopyDoubleUnchecked, "([DI[DII)V"), + FAST_NATIVE_METHOD(System, arraycopyBooleanUnchecked, "([ZI[ZII)V"), + FAST_NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"), }; void register_java_lang_System(JNIEnv* env) { diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 195091f8ab..346bd30b9d 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -187,16 +187,16 @@ static void Thread_yield(JNIEnv*, jobject) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"), - NATIVE_METHOD(Thread, interrupted, "!()Z"), - NATIVE_METHOD(Thread, isInterrupted, "!()Z"), + FAST_NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"), + FAST_NATIVE_METHOD(Thread, interrupted, "()Z"), + FAST_NATIVE_METHOD(Thread, isInterrupted, "()Z"), NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"), NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), - NATIVE_METHOD(Thread, nativeInterrupt, "!()V"), + FAST_NATIVE_METHOD(Thread, nativeInterrupt, "()V"), NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), - NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"), + FAST_NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"), NATIVE_METHOD(Thread, yield, "()V"), }; diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc index ff3e044c9b..654b8a8e5c 100644 --- a/runtime/native/java_lang_Throwable.cc +++ b/runtime/native/java_lang_Throwable.cc @@ -36,8 +36,8 @@ static jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject j } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Throwable, nativeFillInStackTrace, "!()Ljava/lang/Object;"), - NATIVE_METHOD(Throwable, nativeGetStackTrace, "!(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;"), + FAST_NATIVE_METHOD(Throwable, nativeFillInStackTrace, "()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Throwable, nativeGetStackTrace, "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;"), }; void register_java_lang_Throwable(JNIEnv* env) { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index a8fa7db688..54ab8615c0 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -136,7 +136,7 @@ static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(VMClassLoader, findLoadedClass, "!(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"), + FAST_NATIVE_METHOD(VMClassLoader, findLoadedClass, "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"), NATIVE_METHOD(VMClassLoader, getBootClassPathEntries, "()[Ljava/lang/String;"), }; diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc index ecafd0e241..afedc5e456 100644 --- a/runtime/native/java_lang_ref_FinalizerReference.cc +++ b/runtime/native/java_lang_ref_FinalizerReference.cc @@ -40,8 +40,8 @@ static jobject FinalizerReference_getReferent(JNIEnv* env, jobject javaThis) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(FinalizerReference, makeCircularListIfUnenqueued, "!()Z"), - NATIVE_METHOD(FinalizerReference, getReferent, "!()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(FinalizerReference, makeCircularListIfUnenqueued, "()Z"), + FAST_NATIVE_METHOD(FinalizerReference, getReferent, "()Ljava/lang/Object;"), }; void register_java_lang_ref_FinalizerReference(JNIEnv* env) { diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc index c778068bc4..b1cb2f2e70 100644 --- a/runtime/native/java_lang_ref_Reference.cc +++ b/runtime/native/java_lang_ref_Reference.cc @@ -40,8 +40,8 @@ static void Reference_clearReferent(JNIEnv* env, jobject javaThis) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Reference, getReferent, "!()Ljava/lang/Object;"), - NATIVE_METHOD(Reference, clearReferent, "!()V"), + FAST_NATIVE_METHOD(Reference, getReferent, "()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Reference, clearReferent, "()V"), }; void register_java_lang_ref_Reference(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index d827f818c5..54c21096d3 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -72,8 +72,8 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Array, createMultiArray, "!(Ljava/lang/Class;[I)Ljava/lang/Object;"), - NATIVE_METHOD(Array, createObjectArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), }; void register_java_lang_reflect_Array(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 66a5359132..fb780463ff 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -124,9 +124,9 @@ static jobject Constructor_newInstanceFromSerialization(JNIEnv* env, jclass unus } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Constructor, getExceptionTypes, "!()[Ljava/lang/Class;"), - NATIVE_METHOD(Constructor, newInstance0, "!([Ljava/lang/Object;)Ljava/lang/Object;"), - NATIVE_METHOD(Constructor, newInstanceFromSerialization, "!(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Constructor, getExceptionTypes, "()[Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Constructor, newInstance0, "([Ljava/lang/Object;)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Constructor, newInstanceFromSerialization, "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"), }; void register_java_lang_reflect_Constructor(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index 2a3942829f..bc23bedc77 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -195,14 +195,14 @@ static jboolean Executable_isAnnotationPresentNative(JNIEnv* env, } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Executable, getAnnotationNative, - "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Executable, getDeclaredAnnotationsNative, "!()[Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Executable, getParameterAnnotationsNative, - "!()[[Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Executable, getParameters0, "!()[Ljava/lang/reflect/Parameter;"), - NATIVE_METHOD(Executable, getSignatureAnnotation, "!()[Ljava/lang/String;"), - NATIVE_METHOD(Executable, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"), + FAST_NATIVE_METHOD(Executable, getAnnotationNative, + "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Executable, getDeclaredAnnotationsNative, "()[Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Executable, getParameterAnnotationsNative, + "()[[Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Executable, getParameters0, "()[Ljava/lang/reflect/Parameter;"), + FAST_NATIVE_METHOD(Executable, getSignatureAnnotation, "()[Ljava/lang/String;"), + FAST_NATIVE_METHOD(Executable, isAnnotationPresentNative, "(Ljava/lang/Class;)Z"), }; void register_java_lang_reflect_Executable(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 374eeb5806..9cf80a5bf5 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -493,30 +493,30 @@ static jboolean Field_isAnnotationPresentNative(JNIEnv* env, } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"), - NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"), - NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"), - NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"), - NATIVE_METHOD(Field, getAnnotationNative, - "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Field, getArtField, "!()J"), - NATIVE_METHOD(Field, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), - NATIVE_METHOD(Field, getSignatureAnnotation, "!()[Ljava/lang/String;"), - NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"), - NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"), - NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"), - NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"), - NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"), - NATIVE_METHOD(Field, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"), - NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"), - NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"), - NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"), - NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;C)V"), - NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;D)V"), - NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;F)V"), - NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;I)V"), - NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;J)V"), - NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;S)V"), + FAST_NATIVE_METHOD(Field, get, "(Ljava/lang/Object;)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"), + FAST_NATIVE_METHOD(Field, getByte, "(Ljava/lang/Object;)B"), + FAST_NATIVE_METHOD(Field, getChar, "(Ljava/lang/Object;)C"), + FAST_NATIVE_METHOD(Field, getAnnotationNative, + "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Field, getArtField, "()J"), + FAST_NATIVE_METHOD(Field, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD(Field, getSignatureAnnotation, "()[Ljava/lang/String;"), + FAST_NATIVE_METHOD(Field, getDouble, "(Ljava/lang/Object;)D"), + FAST_NATIVE_METHOD(Field, getFloat, "(Ljava/lang/Object;)F"), + FAST_NATIVE_METHOD(Field, getInt, "(Ljava/lang/Object;)I"), + FAST_NATIVE_METHOD(Field, getLong, "(Ljava/lang/Object;)J"), + FAST_NATIVE_METHOD(Field, getShort, "(Ljava/lang/Object;)S"), + FAST_NATIVE_METHOD(Field, isAnnotationPresentNative, "(Ljava/lang/Class;)Z"), + FAST_NATIVE_METHOD(Field, set, "(Ljava/lang/Object;Ljava/lang/Object;)V"), + FAST_NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"), + FAST_NATIVE_METHOD(Field, setByte, "(Ljava/lang/Object;B)V"), + FAST_NATIVE_METHOD(Field, setChar, "(Ljava/lang/Object;C)V"), + FAST_NATIVE_METHOD(Field, setDouble, "(Ljava/lang/Object;D)V"), + FAST_NATIVE_METHOD(Field, setFloat, "(Ljava/lang/Object;F)V"), + FAST_NATIVE_METHOD(Field, setInt, "(Ljava/lang/Object;I)V"), + FAST_NATIVE_METHOD(Field, setLong, "(Ljava/lang/Object;J)V"), + FAST_NATIVE_METHOD(Field, setShort, "(Ljava/lang/Object;S)V"), }; void register_java_lang_reflect_Field(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index a6589bc073..6e5e3d9337 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -84,9 +84,9 @@ static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiv } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Method, getDefaultValue, "!()Ljava/lang/Object;"), - NATIVE_METHOD(Method, getExceptionTypes, "!()[Ljava/lang/Class;"), - NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Method, getDefaultValue, "()Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Method, getExceptionTypes, "()[Ljava/lang/Class;"), + FAST_NATIVE_METHOD(Method, invoke, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), }; void register_java_lang_reflect_Method(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index 0bb9e382d3..37aa16c3df 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -63,9 +63,9 @@ static jobject Parameter_getAnnotationNative(JNIEnv* env, } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Parameter, + FAST_NATIVE_METHOD(Parameter, getAnnotationNative, - "!(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"), + "(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"), }; void register_java_lang_reflect_Parameter(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index 70cd6aaae2..0279b5f9ce 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -35,7 +35,7 @@ static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArra } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"), + FAST_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/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index e51b6d2a89..4138ccc879 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -249,11 +249,11 @@ static jbyteArray CharsetUtils_toUtf8Bytes(JNIEnv* env, jclass, jstring java_str } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(CharsetUtils, asciiBytesToChars, "!([BII[C)V"), - NATIVE_METHOD(CharsetUtils, isoLatin1BytesToChars, "!([BII[C)V"), - NATIVE_METHOD(CharsetUtils, toAsciiBytes, "!(Ljava/lang/String;II)[B"), - NATIVE_METHOD(CharsetUtils, toIsoLatin1Bytes, "!(Ljava/lang/String;II)[B"), - NATIVE_METHOD(CharsetUtils, toUtf8Bytes, "!(Ljava/lang/String;II)[B"), + FAST_NATIVE_METHOD(CharsetUtils, asciiBytesToChars, "([BII[C)V"), + FAST_NATIVE_METHOD(CharsetUtils, isoLatin1BytesToChars, "([BII[C)V"), + FAST_NATIVE_METHOD(CharsetUtils, toAsciiBytes, "(Ljava/lang/String;II)[B"), + FAST_NATIVE_METHOD(CharsetUtils, toIsoLatin1Bytes, "(Ljava/lang/String;II)[B"), + FAST_NATIVE_METHOD(CharsetUtils, toUtf8Bytes, "(Ljava/lang/String;II)[B"), }; void register_libcore_util_CharsetUtils(JNIEnv* env) { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index 5356498fc8..5809708d64 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -33,7 +33,7 @@ static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(DdmServer, nativeSendChunk, "!(I[BII)V"), + FAST_NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"), }; void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index ca17c26c95..69ef59eb30 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -165,11 +165,11 @@ static void DdmVmInternal_threadNotify(JNIEnv*, jclass, jboolean enable) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"), - NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "!()[B"), - NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "!()Z"), + FAST_NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"), + FAST_NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"), NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"), NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"), - NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "!(I)Z"), + FAST_NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"), NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"), NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"), }; diff --git a/runtime/native/scoped_fast_native_object_access-inl.h b/runtime/native/scoped_fast_native_object_access-inl.h index 1d73813fcd..50a554c930 100644 --- a/runtime/native/scoped_fast_native_object_access-inl.h +++ b/runtime/native/scoped_fast_native_object_access-inl.h @@ -27,7 +27,7 @@ namespace art { inline ScopedFastNativeObjectAccess::ScopedFastNativeObjectAccess(JNIEnv* env) : ScopedObjectAccessAlreadyRunnable(env) { Locks::mutator_lock_->AssertSharedHeld(Self()); - DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative()); + DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsAnnotatedWithFastNative()); // Don't work with raw objects in non-runnable states. DCHECK_EQ(Self()->GetState(), kRunnable); } diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 644df070bc..cc5a41a0de 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -492,69 +492,69 @@ static void Unsafe_fullFence(JNIEnv*, jobject) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"), - NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"), - NATIVE_METHOD(Unsafe, compareAndSwapObject, "!(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"), - NATIVE_METHOD(Unsafe, getIntVolatile, "!(Ljava/lang/Object;J)I"), - NATIVE_METHOD(Unsafe, putIntVolatile, "!(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, getLongVolatile, "!(Ljava/lang/Object;J)J"), - NATIVE_METHOD(Unsafe, putLongVolatile, "!(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, getObjectVolatile, "!(Ljava/lang/Object;J)Ljava/lang/Object;"), - NATIVE_METHOD(Unsafe, putObjectVolatile, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), - NATIVE_METHOD(Unsafe, getInt, "!(Ljava/lang/Object;J)I"), - NATIVE_METHOD(Unsafe, putInt, "!(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, putOrderedInt, "!(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, getLong, "!(Ljava/lang/Object;J)J"), - NATIVE_METHOD(Unsafe, putLong, "!(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, putOrderedLong, "!(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"), - NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), - NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), - NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "!(Ljava/lang/Class;)I"), - NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "!(Ljava/lang/Class;)I"), - NATIVE_METHOD(Unsafe, addressSize, "!()I"), - NATIVE_METHOD(Unsafe, pageSize, "!()I"), - NATIVE_METHOD(Unsafe, allocateMemory, "!(J)J"), - NATIVE_METHOD(Unsafe, freeMemory, "!(J)V"), - NATIVE_METHOD(Unsafe, setMemory, "!(JJB)V"), - NATIVE_METHOD(Unsafe, copyMemory, "!(JJJ)V"), - NATIVE_METHOD(Unsafe, copyMemoryToPrimitiveArray, "!(JLjava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, copyMemoryFromPrimitiveArray, "!(Ljava/lang/Object;JJJ)V"), - NATIVE_METHOD(Unsafe, getBoolean, "!(Ljava/lang/Object;J)Z"), - - NATIVE_METHOD(Unsafe, getByte, "!(Ljava/lang/Object;J)B"), - NATIVE_METHOD(Unsafe, getChar, "!(Ljava/lang/Object;J)C"), - NATIVE_METHOD(Unsafe, getShort, "!(Ljava/lang/Object;J)S"), - NATIVE_METHOD(Unsafe, getFloat, "!(Ljava/lang/Object;J)F"), - NATIVE_METHOD(Unsafe, getDouble, "!(Ljava/lang/Object;J)D"), - NATIVE_METHOD(Unsafe, putBoolean, "!(Ljava/lang/Object;JZ)V"), - NATIVE_METHOD(Unsafe, putByte, "!(Ljava/lang/Object;JB)V"), - NATIVE_METHOD(Unsafe, putChar, "!(Ljava/lang/Object;JC)V"), - NATIVE_METHOD(Unsafe, putShort, "!(Ljava/lang/Object;JS)V"), - NATIVE_METHOD(Unsafe, putFloat, "!(Ljava/lang/Object;JF)V"), - NATIVE_METHOD(Unsafe, putDouble, "!(Ljava/lang/Object;JD)V"), + FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"), + FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"), + FAST_NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"), + FAST_NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"), + FAST_NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"), + FAST_NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"), + FAST_NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"), + FAST_NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + FAST_NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"), + FAST_NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"), + FAST_NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"), + FAST_NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"), + FAST_NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"), + FAST_NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"), + FAST_NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"), + FAST_NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + FAST_NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + FAST_NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "(Ljava/lang/Class;)I"), + FAST_NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "(Ljava/lang/Class;)I"), + FAST_NATIVE_METHOD(Unsafe, addressSize, "()I"), + FAST_NATIVE_METHOD(Unsafe, pageSize, "()I"), + FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"), + FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"), + FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"), + FAST_NATIVE_METHOD(Unsafe, copyMemory, "(JJJ)V"), + FAST_NATIVE_METHOD(Unsafe, copyMemoryToPrimitiveArray, "(JLjava/lang/Object;JJ)V"), + FAST_NATIVE_METHOD(Unsafe, copyMemoryFromPrimitiveArray, "(Ljava/lang/Object;JJJ)V"), + FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"), + + FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"), + FAST_NATIVE_METHOD(Unsafe, getChar, "(Ljava/lang/Object;J)C"), + FAST_NATIVE_METHOD(Unsafe, getShort, "(Ljava/lang/Object;J)S"), + FAST_NATIVE_METHOD(Unsafe, getFloat, "(Ljava/lang/Object;J)F"), + FAST_NATIVE_METHOD(Unsafe, getDouble, "(Ljava/lang/Object;J)D"), + FAST_NATIVE_METHOD(Unsafe, putBoolean, "(Ljava/lang/Object;JZ)V"), + FAST_NATIVE_METHOD(Unsafe, putByte, "(Ljava/lang/Object;JB)V"), + FAST_NATIVE_METHOD(Unsafe, putChar, "(Ljava/lang/Object;JC)V"), + FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"), + FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"), + FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"), // Each of the getFoo variants are overloaded with a call that operates // directively on a native pointer. - OVERLOADED_NATIVE_METHOD(Unsafe, getByte, "!(J)B", getByteJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getChar, "!(J)C", getCharJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getShort, "!(J)S", getShortJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getInt, "!(J)I", getIntJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getLong, "!(J)J", getLongJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getFloat, "!(J)F", getFloatJ), - OVERLOADED_NATIVE_METHOD(Unsafe, getDouble, "!(J)D", getDoubleJ), - OVERLOADED_NATIVE_METHOD(Unsafe, putByte, "!(JB)V", putByteJB), - OVERLOADED_NATIVE_METHOD(Unsafe, putChar, "!(JC)V", putCharJC), - OVERLOADED_NATIVE_METHOD(Unsafe, putShort, "!(JS)V", putShortJS), - OVERLOADED_NATIVE_METHOD(Unsafe, putInt, "!(JI)V", putIntJI), - OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ), - OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF), - OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getByte, "(J)B", getByteJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getChar, "(J)C", getCharJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getShort, "(J)S", getShortJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getInt, "(J)I", getIntJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getLong, "(J)J", getLongJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getFloat, "(J)F", getFloatJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getDouble, "(J)D", getDoubleJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putByte, "(JB)V", putByteJB), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putChar, "(JC)V", putCharJC), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putShort, "(JS)V", putShortJS), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putInt, "(JI)V", putIntJI), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putLong, "(JJ)V", putLongJJ), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putFloat, "(JF)V", putFloatJF), + OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putDouble, "(JD)V", putDoubleJD), // CAS - NATIVE_METHOD(Unsafe, loadFence, "!()V"), - NATIVE_METHOD(Unsafe, storeFence, "!()V"), - NATIVE_METHOD(Unsafe, fullFence, "!()V"), + FAST_NATIVE_METHOD(Unsafe, loadFence, "()V"), + FAST_NATIVE_METHOD(Unsafe, storeFence, "()V"), + FAST_NATIVE_METHOD(Unsafe, fullFence, "()V"), }; void register_sun_misc_Unsafe(JNIEnv* env) { diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h index 4d726ecc4c..ea5e69821b 100644 --- a/runtime/object_callbacks.h +++ b/runtime/object_callbacks.h @@ -43,7 +43,8 @@ class MarkObjectVisitor { // May return the same address as the input if the object did not move. virtual mirror::Object* MarkObject(mirror::Object* obj) = 0; // Mark an object and update the value stored in the heap reference if the object moved. - virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj) = 0; + virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj, + bool do_atomic_update) = 0; }; } // namespace art diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index a815a603a7..77ca9ce2e5 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -1188,13 +1188,13 @@ class JvmtiFunctions { ENSURE_NON_NULL(name_ptr); switch (error) { #define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : do { \ - jvmtiError res = CopyString(env, \ - "JVMTI_ERROR_"#e, \ - reinterpret_cast<unsigned char**>(name_ptr)); \ - if (res != OK) { \ + jvmtiError res; \ + JvmtiUniquePtr<char[]> copy = CopyString(env, "JVMTI_ERROR_"#e, &res); \ + if (copy == nullptr) { \ *name_ptr = nullptr; \ return res; \ } else { \ + *name_ptr = copy.release(); \ return OK; \ } \ } while (false) @@ -1248,13 +1248,13 @@ class JvmtiFunctions { ERROR_CASE(INVALID_ENVIRONMENT); #undef ERROR_CASE default: { - jvmtiError res = CopyString(env, - "JVMTI_ERROR_UNKNOWN", - reinterpret_cast<unsigned char**>(name_ptr)); - if (res != OK) { + jvmtiError res; + JvmtiUniquePtr<char[]> copy = CopyString(env, "JVMTI_ERROR_UNKNOWN", &res); + if (copy == nullptr) { *name_ptr = nullptr; return res; } else { + *name_ptr = copy.release(); return ERR(ILLEGAL_ARGUMENT); } } diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 106165c38b..99139a1f37 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -33,6 +33,7 @@ #define ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_ #include <memory> +#include <type_traits> #include <jni.h> @@ -86,6 +87,7 @@ static inline JNIEnv* GetJniEnv(jvmtiEnv* env) { return ret_value; } +template <typename T> class JvmtiDeleter { public: JvmtiDeleter() : env_(nullptr) {} @@ -95,9 +97,30 @@ class JvmtiDeleter { JvmtiDeleter(JvmtiDeleter&&) = default; JvmtiDeleter& operator=(const JvmtiDeleter&) = default; - void operator()(unsigned char* ptr) const { + void operator()(T* ptr) const { + CHECK(env_ != nullptr); + jvmtiError ret = env_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); + CHECK(ret == ERR(NONE)); + } + + private: + mutable jvmtiEnv* env_; +}; + +template <typename T> +class JvmtiDeleter<T[]> { + public: + JvmtiDeleter() : env_(nullptr) {} + explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {} + + JvmtiDeleter(JvmtiDeleter&) = default; + JvmtiDeleter(JvmtiDeleter&&) = default; + JvmtiDeleter& operator=(const JvmtiDeleter&) = default; + + template <typename U> + void operator()(U* ptr) const { CHECK(env_ != nullptr); - jvmtiError ret = env_->Deallocate(ptr); + jvmtiError ret = env_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); CHECK(ret == ERR(NONE)); } @@ -105,12 +128,44 @@ class JvmtiDeleter { mutable jvmtiEnv* env_; }; -using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>; +template <typename T> +using JvmtiUniquePtr = std::unique_ptr<T, JvmtiDeleter<T>>; + +template <typename T> +ALWAYS_INLINE +static inline JvmtiUniquePtr<T> MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) { + return JvmtiUniquePtr<T>(mem, JvmtiDeleter<T>(env)); +} + +template <typename T> +ALWAYS_INLINE +static inline JvmtiUniquePtr<T> MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* mem) { + return JvmtiUniquePtr<T>(reinterpret_cast<T*>(mem), JvmtiDeleter<T>(env)); +} + +template <typename T> +ALWAYS_INLINE +static inline JvmtiUniquePtr<T> AllocJvmtiUniquePtr(jvmtiEnv* env, jvmtiError* error) { + unsigned char* tmp; + *error = env->Allocate(sizeof(T), &tmp); + if (*error != ERR(NONE)) { + return JvmtiUniquePtr<T>(); + } + return JvmtiUniquePtr<T>(tmp, JvmtiDeleter<T>(env)); +} template <typename T> ALWAYS_INLINE -static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) { - return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env)); +static inline JvmtiUniquePtr<T> AllocJvmtiUniquePtr(jvmtiEnv* env, + size_t count, + jvmtiError* error) { + unsigned char* tmp; + *error = env->Allocate(sizeof(typename std::remove_extent<T>::type) * count, &tmp); + if (*error != ERR(NONE)) { + return JvmtiUniquePtr<T>(); + } + return JvmtiUniquePtr<T>(reinterpret_cast<typename std::remove_extent<T>::type*>(tmp), + JvmtiDeleter<T>(env)); } ALWAYS_INLINE @@ -129,15 +184,12 @@ static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env, } ALWAYS_INLINE -static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) { +static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, jvmtiError* error) { size_t len = strlen(src) + 1; - unsigned char* buf; - jvmtiError ret = env->Allocate(len, &buf); - if (ret != ERR(NONE)) { - return ret; + JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, len, error); + if (ret != nullptr) { + strcpy(ret.get(), src); } - strcpy(reinterpret_cast<char*>(buf), src); - *copy = buf; return ret; } diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index a8a0ded7dc..4282e38b17 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -673,18 +673,17 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, return ERR(INVALID_CLASS); } - JvmtiUniquePtr sig_copy; + JvmtiUniquePtr<char[]> sig_copy; if (signature_ptr != nullptr) { std::string storage; const char* descriptor = klass->GetDescriptor(&storage); - unsigned char* tmp; - jvmtiError ret = CopyString(env, descriptor, &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + sig_copy = CopyString(env, descriptor, &ret); + if (sig_copy == nullptr) { return ret; } - sig_copy = MakeJvmtiUniquePtr(env, tmp); - *signature_ptr = reinterpret_cast<char*>(tmp); + *signature_ptr = sig_copy.get(); } if (generic_ptr != nullptr) { @@ -700,12 +699,12 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, oss << str_array->Get(i)->ToModifiedUtf8(); } std::string output_string = oss.str(); - unsigned char* tmp; - jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + JvmtiUniquePtr<char[]> copy = CopyString(env, output_string.c_str(), &ret); + if (copy == nullptr) { return ret; } - *generic_ptr = reinterpret_cast<char*>(tmp); + *generic_ptr = copy.release(); } else if (soa.Self()->IsExceptionPending()) { // TODO: Should we report an error here? soa.Self()->ClearException(); diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h index dbe5da2d63..3c251d4b44 100644 --- a/runtime/openjdkjvmti/ti_class_definition.h +++ b/runtime/openjdkjvmti/ti_class_definition.h @@ -46,7 +46,7 @@ struct ArtClassDefinition { std::string name; jobject protection_domain; jint dex_len; - JvmtiUniquePtr dex_data; + JvmtiUniquePtr<unsigned char> dex_data; art::ArraySlice<const unsigned char> original_dex_file; ArtClassDefinition() = default; diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc index d05f579407..66357eb32f 100644 --- a/runtime/openjdkjvmti/ti_class_loader.cc +++ b/runtime/openjdkjvmti/ti_class_loader.cc @@ -105,7 +105,6 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::GetDexFileCookie( // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until // the object is finalized. Since they always point to the same array if mCookie is not null we // just use the mInternalCookie field. We will update one or both of these fields later. - // TODO Should I get the class from the classloader or directly? art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField( "mInternalCookie", "Ljava/lang/Object;"); // TODO Add check that mCookie is either null or same as mInternalCookie @@ -113,7 +112,6 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::GetDexFileCookie( return internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray(); } -// TODO Really wishing I had that mirror of java.lang.DexFile now. art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie( art::Thread* self, art::Handle<art::mirror::LongArray> cookie, @@ -128,8 +126,6 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie( return nullptr; } // Copy the oat-dex field at the start. - // TODO Should I clear this field? - // TODO This is a really crappy thing here with the first element being different. new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0)); // This must match the casts in runtime/native/dalvik_system_DexFile.cc:ConvertDexFilesToJavaArray new_cookie->SetWithoutChecks<false>( diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc index 131e6c3632..8c3f2fffbd 100644 --- a/runtime/openjdkjvmti/ti_field.cc +++ b/runtime/openjdkjvmti/ti_field.cc @@ -63,31 +63,29 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env, art::ScopedObjectAccess soa(art::Thread::Current()); art::ArtField* art_field = art::jni::DecodeArtField(field); - JvmtiUniquePtr name_copy; + JvmtiUniquePtr<char[]> name_copy; if (name_ptr != nullptr) { const char* field_name = art_field->GetName(); if (field_name == nullptr) { field_name = "<error>"; } - unsigned char* tmp; - jvmtiError ret = CopyString(env, field_name, &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + name_copy = CopyString(env, field_name, &ret); + if (name_copy == nullptr) { return ret; } - name_copy = MakeJvmtiUniquePtr(env, tmp); - *name_ptr = reinterpret_cast<char*>(tmp); + *name_ptr = name_copy.get(); } - JvmtiUniquePtr signature_copy; + JvmtiUniquePtr<char[]> signature_copy; if (signature_ptr != nullptr) { const char* sig = art_field->GetTypeDescriptor(); - unsigned char* tmp; - jvmtiError ret = CopyString(env, sig, &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + signature_copy = CopyString(env, sig, &ret); + if (signature_copy == nullptr) { return ret; } - signature_copy = MakeJvmtiUniquePtr(env, tmp); - *signature_ptr = reinterpret_cast<char*>(tmp); + *signature_ptr = signature_copy.get(); } // TODO: Support generic signature. @@ -102,12 +100,12 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env, oss << str_array->Get(i)->ToModifiedUtf8(); } std::string output_string = oss.str(); - unsigned char* tmp; - jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + JvmtiUniquePtr<char[]> copy = CopyString(env, output_string.c_str(), &ret); + if (copy == nullptr) { return ret; } - *generic_ptr = reinterpret_cast<char*>(tmp); + *generic_ptr = copy.release(); } else if (soa.Self()->IsExceptionPending()) { // TODO: Should we report an error here? soa.Self()->ClearException(); diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index a6cfcc12bc..bc73029f41 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -110,35 +110,32 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize); - JvmtiUniquePtr name_copy; + JvmtiUniquePtr<char[]> name_copy; if (name_ptr != nullptr) { const char* method_name = art_method->GetName(); if (method_name == nullptr) { method_name = "<error>"; } - unsigned char* tmp; - jvmtiError ret = CopyString(env, method_name, &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + name_copy = CopyString(env, method_name, &ret); + if (name_copy == nullptr) { return ret; } - name_copy = MakeJvmtiUniquePtr(env, tmp); - *name_ptr = reinterpret_cast<char*>(tmp); + *name_ptr = name_copy.get(); } - JvmtiUniquePtr signature_copy; + JvmtiUniquePtr<char[]> signature_copy; if (signature_ptr != nullptr) { const art::Signature sig = art_method->GetSignature(); std::string str = sig.ToString(); - unsigned char* tmp; - jvmtiError ret = CopyString(env, str.c_str(), &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + signature_copy = CopyString(env, str.c_str(), &ret); + if (signature_copy == nullptr) { return ret; } - signature_copy = MakeJvmtiUniquePtr(env, tmp); - *signature_ptr = reinterpret_cast<char*>(tmp); + *signature_ptr = signature_copy.get(); } - // TODO: Support generic signature. if (generic_ptr != nullptr) { *generic_ptr = nullptr; if (!art_method->GetDeclaringClass()->IsProxyClass()) { @@ -150,12 +147,12 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, oss << str_array->Get(i)->ToModifiedUtf8(); } std::string output_string = oss.str(); - unsigned char* tmp; - jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); - if (ret != ERR(NONE)) { + jvmtiError ret; + JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret); + if (generic_copy == nullptr) { return ret; } - *generic_ptr = reinterpret_cast<char*>(tmp); + *generic_ptr = generic_copy.release(); } else if (soa.Self()->IsExceptionPending()) { // TODO: Should we report an error here? soa.Self()->ClearException(); diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc index 46b9e71b13..4f4f01390d 100644 --- a/runtime/openjdkjvmti/ti_properties.cc +++ b/runtime/openjdkjvmti/ti_properties.cc @@ -82,71 +82,69 @@ static constexpr size_t kPropertiesSize = arraysize(kProperties); static constexpr const char* kPropertyLibraryPath = "java.library.path"; static constexpr const char* kPropertyClassPath = "java.class.path"; -static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) { - unsigned char* data = nullptr; - jvmtiError result = CopyString(env, in, &data); - *out = reinterpret_cast<char*>(data); - return result; -} - jvmtiError PropertiesUtil::GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) { if (count_ptr == nullptr || property_ptr == nullptr) { return ERR(NULL_POINTER); } - unsigned char* array_data; - jvmtiError array_alloc_result = env->Allocate((kPropertiesSize + 2) * sizeof(char*), &array_data); - if (array_alloc_result != ERR(NONE)) { + jvmtiError array_alloc_result; + JvmtiUniquePtr<char*[]> array_data_ptr = AllocJvmtiUniquePtr<char*[]>(env, + kPropertiesSize + 2, + &array_alloc_result); + if (array_data_ptr == nullptr) { return array_alloc_result; } - JvmtiUniquePtr array_data_ptr = MakeJvmtiUniquePtr(env, array_data); - char** array = reinterpret_cast<char**>(array_data); - std::vector<JvmtiUniquePtr> property_copies; + std::vector<JvmtiUniquePtr<char[]>> property_copies; { - char* libpath_data; - jvmtiError libpath_result = Copy(env, kPropertyLibraryPath, &libpath_data); - if (libpath_result != ERR(NONE)) { + jvmtiError libpath_result; + JvmtiUniquePtr<char[]> libpath_data = CopyString(env, kPropertyLibraryPath, &libpath_result); + if (libpath_data == nullptr) { return libpath_result; } - array[0] = libpath_data; - property_copies.push_back(MakeJvmtiUniquePtr(env, libpath_data)); + array_data_ptr.get()[0] = libpath_data.get(); + property_copies.push_back(std::move(libpath_data)); } { - char* classpath_data; - jvmtiError classpath_result = Copy(env, kPropertyClassPath, &classpath_data); - if (classpath_result != ERR(NONE)) { + jvmtiError classpath_result; + JvmtiUniquePtr<char[]> classpath_data = CopyString(env, kPropertyClassPath, &classpath_result); + if (classpath_data == nullptr) { return classpath_result; } - array[1] = classpath_data; - property_copies.push_back(MakeJvmtiUniquePtr(env, classpath_data)); + array_data_ptr.get()[1] = classpath_data.get(); + property_copies.push_back(std::move(classpath_data)); } for (size_t i = 0; i != kPropertiesSize; ++i) { - char* data; - jvmtiError data_result = Copy(env, kProperties[i][0], &data); - if (data_result != ERR(NONE)) { + jvmtiError data_result; + JvmtiUniquePtr<char[]> data = CopyString(env, kProperties[i][0], &data_result); + if (data == nullptr) { return data_result; } - array[i + 2] = data; - property_copies.push_back(MakeJvmtiUniquePtr(env, data)); + array_data_ptr.get()[i + 2] = data.get(); + property_copies.push_back(std::move(data)); } // Everything is OK, release the data. - array_data_ptr.release(); + *count_ptr = kPropertiesSize + 2; + *property_ptr = array_data_ptr.release(); for (auto& uptr : property_copies) { uptr.release(); } - *count_ptr = kPropertiesSize + 2; - *property_ptr = array; - return ERR(NONE); } +static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) { + jvmtiError result; + JvmtiUniquePtr<char[]> data = CopyString(env, in, &result); + *out = data.release(); + return result; +} + jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr) { diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 8436045072..60ce898d65 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -170,10 +170,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // We cannot ensure that the right dex file is used in inlined frames so we don't support // redefining them. DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition"; - // TODO We should really support intrinsic obsolete methods. - // TODO We should really support redefining intrinsics. - // We don't support intrinsics so check for them here. - DCHECK(!old_method->IsIntrinsic()); art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method); if (new_obsolete_method == nullptr) { // Create a new Obsolete Method and put it in the list. @@ -323,7 +319,6 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env, // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls // to get the passed in bytes. - // TODO Implement saving the original bytes. unsigned char* class_bytes_copy = nullptr; jvmtiError res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy); if (res != OK) { @@ -396,8 +391,8 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition *error_msg_ = "Unable to get class signature!"; return ret; } - JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused)); - JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr)); + JvmtiUniquePtr<char> generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused)); + JvmtiUniquePtr<char> signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr)); std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location, def.dex_len, def.dex_data.get(), @@ -518,6 +513,11 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader())); // Add all the declared methods to the map for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) { + if (m.IsIntrinsic()) { + LOG(WARNING) << "Redefining intrinsic method " << m.PrettyMethod() << ". This may cause the " + << "unexpected use of the original definition of " << m.PrettyMethod() << "in " + << "methods that have already been compiled."; + } // It is possible to simply filter out some methods where they cannot really become obsolete, // such as native methods and keep their original (possibly optimized) implementations. We don't // do this, however, since we would need to mark these functions (still in the classes @@ -526,8 +526,6 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C // error checking from the interpreter which ensure we don't try to start executing obsolete // methods. ctx.obsolete_methods.insert(&m); - // TODO Allow this or check in IsModifiableClass. - DCHECK(!m.IsIntrinsic()); } { art::MutexLock mu(driver_->self_, *art::Locks::thread_list_lock_); @@ -674,7 +672,6 @@ bool Redefiner::ClassRedefinition::CheckSameFields() { } bool Redefiner::ClassRedefinition::CheckClass() { - // TODO Might just want to put it in a ObjPtr and NoSuspend assert. art::StackHandleScope<1> hs(driver_->self_); // Easy check that only 1 class def is present. if (dex_file_->NumClassDefs() != 1) { @@ -750,7 +747,6 @@ bool Redefiner::ClassRedefinition::CheckClass() { return true; } -// TODO Move this to use IsRedefinable when that function is made. bool Redefiner::ClassRedefinition::CheckRedefinable() { std::string err; art::StackHandleScope<1> hs(driver_->self_); @@ -883,7 +879,6 @@ class RedefinitionDataHolder { DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder); }; -// TODO Stash and update soft failure state bool Redefiner::ClassRedefinition::CheckVerification(int32_t klass_index, const RedefinitionDataHolder& holder) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); @@ -976,7 +971,6 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader))); holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); if (dex_file_obj == nullptr) { - // TODO Better error msg. RecordFailure(ERR(INTERNAL), "Unable to find dex file!"); return false; } @@ -1124,11 +1118,6 @@ jvmtiError Redefiner::Run() { self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative); runtime_->GetThreadList()->SuspendAll( "Final installation of redefined Classes!", /*long_suspend*/true); - // TODO We need to invalidate all breakpoints in the redefined class with the debugger. - // TODO We need to deal with any instrumentation/debugger deoptimized_methods_. - // TODO We need to update all debugger MethodIDs so they note the method they point to is - // obsolete or implement some other well defined semantics. - // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods. counter = 0; for (Redefiner::ClassRedefinition& redef : redefinitions_) { art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition"); @@ -1143,6 +1132,10 @@ jvmtiError Redefiner::Run() { holder.GetOriginalDexFileBytes(counter)); counter++; } + // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any + // are, force a full-world deoptimization before finishing redefinition. If we don't do this then + // methods that have been jitted prior to the current redefinition being applied might continue + // to use the old versions of the intrinsics! // TODO Shrink the obsolete method maps if possible? // TODO Put this into a scoped thing. runtime_->GetThreadList()->ResumeAll(); @@ -1181,18 +1174,18 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> } const art::DexFile::ProtoId* proto_id = dex_file_->FindProtoId(method_return_idx, new_type_list); - // TODO Return false, cleanup. CHECK(proto_id != nullptr || old_type_list == nullptr); const art::DexFile::MethodId* method_id = dex_file_->FindMethodId(declaring_class_id, *new_name_id, *proto_id); - // TODO Return false, cleanup. CHECK(method_id != nullptr); uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id); method.SetDexMethodIndex(dex_method_idx); linker->SetEntryPointsToInterpreter(&method); method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size); + // Clear all the intrinsics related flags. + method.ClearAccessFlags(art::kAccIntrinsic | (~art::kAccFlagsNotUsedByIntrinsic)); // Notify the jit that this method is redefined. art::jit::Jit* jit = driver_->runtime_->GetJit(); if (jit != nullptr) { @@ -1210,7 +1203,6 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name)); const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName()); const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor()); - // TODO Handle error, cleanup. CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr); const art::DexFile::FieldId* new_field_id = dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id); diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index f8f8fa6b25..788ac30873 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -186,17 +186,17 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI return ERR(INVALID_THREAD); } - JvmtiUniquePtr name_uptr; + JvmtiUniquePtr<char[]> name_uptr; if (self != nullptr) { // Have a native thread object, this thread is alive. std::string name; self->GetThreadName(name); - jvmtiError name_result = CopyString( - env, name.c_str(), reinterpret_cast<unsigned char**>(&info_ptr->name)); - if (name_result != ERR(NONE)) { + jvmtiError name_result; + name_uptr = CopyString(env, name.c_str(), &name_result); + if (name_uptr == nullptr) { return name_result; } - name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name); + info_ptr->name = name_uptr.get(); info_ptr->priority = self->GetNativePriority(); @@ -239,12 +239,12 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI } else { name_cstr = ""; } - jvmtiError name_result = CopyString( - env, name_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name)); - if (name_result != ERR(NONE)) { + jvmtiError name_result; + name_uptr = CopyString(env, name_cstr, &name_result); + if (name_uptr == nullptr) { return name_result; } - name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name); + info_ptr->name = name_uptr.get(); } // Priority. diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc index 142387433e..df14333314 100644 --- a/runtime/openjdkjvmti/ti_threadgroup.cc +++ b/runtime/openjdkjvmti/ti_threadgroup.cc @@ -116,11 +116,12 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, tmp_str = name_obj->ToModifiedUtf8(); tmp_cstr = tmp_str.c_str(); } - jvmtiError result = - CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name)); - if (result != ERR(NONE)) { + jvmtiError result; + JvmtiUniquePtr<char[]> copy = CopyString(env, tmp_cstr, &result); + if (copy == nullptr) { return result; } + info_ptr->name = copy.release(); } // Parent. @@ -239,45 +240,38 @@ jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env, std::vector<art::ObjPtr<art::mirror::Object>> thread_groups; GetChildThreadGroups(thread_group, &thread_groups); - jthread* thread_data = nullptr; - JvmtiUniquePtr peers_uptr; + JvmtiUniquePtr<jthread[]> peers_uptr; if (!thread_peers.empty()) { - unsigned char* data; - jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data); - if (res != ERR(NONE)) { + jvmtiError res; + peers_uptr = AllocJvmtiUniquePtr<jthread[]>(env, thread_peers.size(), &res); + if (peers_uptr == nullptr) { return res; } - thread_data = reinterpret_cast<jthread*>(data); - peers_uptr = MakeJvmtiUniquePtr(env, data); } - jthreadGroup* group_data = nullptr; + JvmtiUniquePtr<jthreadGroup[]> group_uptr; if (!thread_groups.empty()) { - unsigned char* data; - jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data); - if (res != ERR(NONE)) { + jvmtiError res; + group_uptr = AllocJvmtiUniquePtr<jthreadGroup[]>(env, thread_groups.size(), &res); + if (group_uptr == nullptr) { return res; } - group_data = reinterpret_cast<jthreadGroup*>(data); } // Can't fail anymore from here on. // Copy data into out buffers. for (size_t i = 0; i != thread_peers.size(); ++i) { - thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]); + peers_uptr[i] = soa.AddLocalReference<jthread>(thread_peers[i]); } for (size_t i = 0; i != thread_groups.size(); ++i) { - group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]); + group_uptr[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]); } *thread_count_ptr = static_cast<jint>(thread_peers.size()); - *threads_ptr = thread_data; + *threads_ptr = peers_uptr.release(); *group_count_ptr = static_cast<jint>(thread_groups.size()); - *groups_ptr = group_data; - - // Everything's fine. - peers_uptr.release(); + *groups_ptr = group_uptr.release(); return ERR(NONE); } diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index dd89d644a8..e2a1001e5b 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -33,7 +33,7 @@ z (class java.lang.Character) 14 (class java.lang.Short) [java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)] [private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER] -[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)] +[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] 0 diff --git a/test/201-built-in-except-detail-messages/src/Main.java b/test/201-built-in-except-detail-messages/src/Main.java index dc58819924..c2976c8a33 100644 --- a/test/201-built-in-except-detail-messages/src/Main.java +++ b/test/201-built-in-except-detail-messages/src/Main.java @@ -411,7 +411,7 @@ public class Main { m.invoke("hello", "world"); // Wrong type. fail(); } catch (IllegalArgumentException iae) { - assertEquals("method java.lang.String.charAt! argument 1 has type int, got java.lang.String", + assertEquals("method java.lang.String.charAt argument 1 has type int, got java.lang.String", iae.getMessage()); } try { @@ -419,7 +419,7 @@ public class Main { m.invoke("hello", (Object) null); // Null for a primitive argument. fail(); } catch (IllegalArgumentException iae) { - assertEquals("method java.lang.String.charAt! argument 1 has type int, got null", + assertEquals("method java.lang.String.charAt argument 1 has type int, got null", iae.getMessage()); } try { diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 7522a659f2..340cd702af 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -1,6 +1,6 @@ --- true true -root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=11,location= 31])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] 0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] @@ -40,9 +40,9 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 6])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 6])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=3,location= 18])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123, length=-1] diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java index 5a11a5b144..7f9c8fcacd 100644 --- a/test/913-heaps/src/Main.java +++ b/test/913-heaps/src/Main.java @@ -15,6 +15,7 @@ */ import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -22,7 +23,7 @@ import java.util.HashSet; public class Main { public static void main(String[] args) throws Exception { doTest(); - doFollowReferencesTest(); + new TestConfig().doFollowReferencesTest(); } public static void doTest() throws Exception { @@ -51,126 +52,136 @@ public class Main { System.out.println((s > 0) + " " + (f > 0)); } - public static void doFollowReferencesTest() throws Exception { - // Force GCs to clean up dirt. - Runtime.getRuntime().gc(); - Runtime.getRuntime().gc(); + private static class TestConfig { + private Class<?> klass = null; + private int heapFilter = 0; - setTag(Thread.currentThread(), 3000); - - { - ArrayList<Object> tmpStorage = new ArrayList<>(); - doFollowReferencesTestNonRoot(tmpStorage); - tmpStorage = null; + public TestConfig() { + } + public TestConfig(Class<?> klass, int heapFilter) { + this.klass = klass; + this.heapFilter = heapFilter; } - // Force GCs to clean up dirt. - Runtime.getRuntime().gc(); - Runtime.getRuntime().gc(); + public void doFollowReferencesTest() throws Exception { + // Force GCs to clean up dirt. + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); - doFollowReferencesTestRoot(); + setTag(Thread.currentThread(), 3000); - // Force GCs to clean up dirt. - Runtime.getRuntime().gc(); - Runtime.getRuntime().gc(); - } + { + ArrayList<Object> tmpStorage = new ArrayList<>(); + doFollowReferencesTestNonRoot(tmpStorage); + tmpStorage = null; + } - private static void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) { - Verifier v = new Verifier(); - tagClasses(v); - A a = createTree(v); - tmpStorage.add(a); - v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a. + // Force GCs to clean up dirt. + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); - doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null); - doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, null, v, "3@1001"); + doFollowReferencesTestRoot(); - tmpStorage.clear(); - } + // Force GCs to clean up dirt. + Runtime.getRuntime().gc(); + Runtime.getRuntime().gc(); + } - private static void doFollowReferencesTestRoot() { - Verifier v = new Verifier(); - tagClasses(v); - A a = createTree(v); + private void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) { + Verifier v = new Verifier(); + tagClasses(v); + A a = createTree(v); + tmpStorage.add(a); + v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a. - doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null); - doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, a, v, "3@1001"); - } + doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null); + doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, null, v, "3@1001"); - private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet, - Object asRoot, Verifier v, String additionalEnabled) { - String[] lines = - followReferences(0, null, root, stopAfter, followSet, asRoot); + tmpStorage.clear(); + } - v.process(lines, additionalEnabled); + private void doFollowReferencesTestRoot() { + Verifier v = new Verifier(); + tagClasses(v); + A a = createTree(v); - // TODO: Test filters. - } + doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null); + doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, a, v, "3@1001"); + } - private static void tagClasses(Verifier v) { - setTag(A.class, 1000); + private void doFollowReferencesTestImpl(A root, int stopAfter, int followSet, + Object asRoot, Verifier v, String additionalEnabled) { + String[] lines = + followReferences(heapFilter, klass, root, stopAfter, followSet, asRoot); - setTag(B.class, 1001); - v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class. + v.process(lines, additionalEnabled, heapFilter != 0 || klass != null); + } - setTag(C.class, 1002); - v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class. - v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class. + private static void tagClasses(Verifier v) { + setTag(A.class, 1000); - setTag(I1.class, 2000); + setTag(B.class, 1001); + v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class. - setTag(I2.class, 2001); - v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class. - } + setTag(C.class, 1002); + v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class. + v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class. + + setTag(I1.class, 2000); - private static A createTree(Verifier v) { - A aInst = new A(); - setTag(aInst, 1); - String aInstStr = "1@1000"; - String aClassStr = "1000@0"; - v.add(aInstStr, aClassStr); // A -->(class) --> A.class. - - A a2Inst = new A(); - setTag(a2Inst, 2); - aInst.foo = a2Inst; - String a2InstStr = "2@1000"; - v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class. - v.add(aInstStr, a2InstStr); // A -->(field) --> A2. - - B bInst = new B(); - setTag(bInst, 3); - aInst.foo2 = bInst; - String bInstStr = "3@1001"; - String bClassStr = "1001@0"; - v.add(bInstStr, bClassStr); // B -->(class) --> B.class. - v.add(aInstStr, bInstStr); // A -->(field) --> B. - - A a3Inst = new A(); - setTag(a3Inst, 4); - bInst.bar = a3Inst; - String a3InstStr = "4@1000"; - v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class. - v.add(bInstStr, a3InstStr); // B -->(field) --> A3. - - C cInst = new C(); - setTag(cInst, 5); - bInst.bar2 = cInst; - String cInstStr = "5@1000"; - String cClassStr = "1002@0"; - v.add(cInstStr, cClassStr); // C -->(class) --> C.class. - v.add(bInstStr, cInstStr); // B -->(field) --> C. - - A a4Inst = new A(); - setTag(a4Inst, 6); - cInst.baz = a4Inst; - String a4InstStr = "6@1000"; - v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class. - v.add(cInstStr, a4InstStr); // C -->(field) --> A4. - - cInst.baz2 = aInst; - v.add(cInstStr, aInstStr); // C -->(field) --> A. - - return aInst; + setTag(I2.class, 2001); + v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class. + } + + private static A createTree(Verifier v) { + A aInst = new A(); + setTag(aInst, 1); + String aInstStr = "1@1000"; + String aClassStr = "1000@0"; + v.add(aInstStr, aClassStr); // A -->(class) --> A.class. + + A a2Inst = new A(); + setTag(a2Inst, 2); + aInst.foo = a2Inst; + String a2InstStr = "2@1000"; + v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class. + v.add(aInstStr, a2InstStr); // A -->(field) --> A2. + + B bInst = new B(); + setTag(bInst, 3); + aInst.foo2 = bInst; + String bInstStr = "3@1001"; + String bClassStr = "1001@0"; + v.add(bInstStr, bClassStr); // B -->(class) --> B.class. + v.add(aInstStr, bInstStr); // A -->(field) --> B. + + A a3Inst = new A(); + setTag(a3Inst, 4); + bInst.bar = a3Inst; + String a3InstStr = "4@1000"; + v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class. + v.add(bInstStr, a3InstStr); // B -->(field) --> A3. + + C cInst = new C(); + setTag(cInst, 5); + bInst.bar2 = cInst; + String cInstStr = "5@1000"; + String cClassStr = "1002@0"; + v.add(cInstStr, cClassStr); // C -->(class) --> C.class. + v.add(bInstStr, cInstStr); // B -->(field) --> C. + + A a4Inst = new A(); + setTag(a4Inst, 6); + cInst.baz = a4Inst; + String a4InstStr = "6@1000"; + v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class. + v.add(cInstStr, a4InstStr); // C -->(field) --> A4. + + cInst.baz2 = aInst; + v.add(cInstStr, aInstStr); // C -->(field) --> A. + + return aInst; + } } public static class A { @@ -243,7 +254,7 @@ public class Main { } } - public void process(String[] lines, String additionalEnabledReferrer) { + public void process(String[] lines, String additionalEnabledReferrer, boolean filtered) { // This method isn't optimal. The loops could be merged. However, it's more readable if // the different parts are separated. @@ -303,6 +314,21 @@ public class Main { } } + if (filtered) { + // If we aren't tracking dependencies, just sort the lines and print. + // TODO: As the verifier is currently using the output lines to track dependencies, we + // cannot verify that output is correct when parts of it are suppressed by filters. + // To correctly track this we need to take node information into account, and + // actually analyze the graph. + Collections.sort(nonRootLines); + for (String l : nonRootLines) { + System.out.println(l); + } + + System.out.println("---"); + return; + } + // Iterate through the lines, keeping track of which referrers are visited, to ensure the // order is acceptable. HashSet<String> enabled = new HashSet<>(); @@ -379,9 +405,9 @@ public class Main { private static native int getGcFinishes(); private static native void forceGarbageCollection(); - private static native void setTag(Object o, long tag); - private static native long getTag(Object o); + public static native void setTag(Object o, long tag); + public static native long getTag(Object o); - private static native String[] followReferences(int heapFilter, Class<?> klassFilter, + public static native String[] followReferences(int heapFilter, Class<?> klassFilter, Object initialObject, int stopAfter, int followSet, Object jniRef); } diff --git a/test/946-obsolete-throw/src/Main.java b/test/946-obsolete-throw/src/Main.java index 3ff97ae2ac..21fe972bab 100644 --- a/test/946-obsolete-throw/src/Main.java +++ b/test/946-obsolete-throw/src/Main.java @@ -71,7 +71,7 @@ public class Main { t.sayHi(new DoRedefinitionClass()); } catch (Throwable e) { System.out.println("Received error : " + e); - e.printStackTrace(); + e.printStackTrace(System.out); } t.sayHi(() -> { System.out.println("Not doing anything here"); }); } diff --git a/test/950-redefine-intrinsic/expected.txt b/test/950-redefine-intrinsic/expected.txt new file mode 100644 index 0000000000..1264c9437d --- /dev/null +++ b/test/950-redefine-intrinsic/expected.txt @@ -0,0 +1 @@ +Finished! diff --git a/test/950-redefine-intrinsic/info.txt b/test/950-redefine-intrinsic/info.txt new file mode 100644 index 0000000000..c19d2b4a2e --- /dev/null +++ b/test/950-redefine-intrinsic/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Tests that we are able to redefine intrinsic functions. diff --git a/test/950-redefine-intrinsic/run b/test/950-redefine-intrinsic/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/950-redefine-intrinsic/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2017 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. + +./default-run "$@" --jvmti diff --git a/test/950-redefine-intrinsic/src/Main.java b/test/950-redefine-intrinsic/src/Main.java new file mode 100644 index 0000000000..30cd3ab031 --- /dev/null +++ b/test/950-redefine-intrinsic/src/Main.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Base64; +import java.util.Random; +import java.util.function.*; +import java.util.stream.*; + +public class Main { + + // The bytes below define the following java program. + // package java.lang; + // import java.math.*; + // public final class Long extends Number implements Comparable<Long> { + // public static final long MIN_VALUE = 0; + // public static final long MAX_VALUE = 0; + // public static final Class<Long> TYPE = null; + // static { } + // // Used for Stream.count for some reason. + // public static long sum(long a, long b) { + // return a + b; + // } + // // Used in stream/lambda functions. + // public Long(long value) { + // this.value = value; + // } + // // Used in stream/lambda functions. + // public static Long valueOf(long l) { + // return new Long(l); + // } + // // Intrinsic! Do something cool. Return i + 1 + // public static long highestOneBit(long i) { + // return i + 1; + // } + // // Intrinsic! Do something cool. Return i - 1 + // public static long lowestOneBit(long i) { + // return i - 1; + // } + // // Intrinsic! Do something cool. Return i + i + // public static int numberOfLeadingZeros(long i) { + // return (int)(i + i); + // } + // // Intrinsic! Do something cool. Return i & (i >>> 1); + // public static int numberOfTrailingZeros(long i) { + // return (int)(i & (i >>> 1)); + // } + // // Intrinsic! Do something cool. Return 5 + // public static int bitCount(long i) { + // return 5; + // } + // // Intrinsic! Do something cool. Return i + // public static long rotateLeft(long i, int distance) { + // return i; + // } + // // Intrinsic! Do something cool. Return 10 * i + // public static long rotateRight(long i, int distance) { + // return 10 * i; + // } + // // Intrinsic! Do something cool. Return -i + // public static long reverse(long i) { + // return -i; + // } + // // Intrinsic! Do something cool. Return 0 + // public static int signum(long i) { + // return 0; + // } + // // Intrinsic! Do something cool. Return 0 + // public static long reverseBytes(long i) { + // return 0; + // } + // public String toString() { + // return "Redefined Long! value (as double)=" + ((double)value); + // } + // public static String toString(long i, int radix) { + // throw new Error("Method redefined away!"); + // } + // public static String toUnsignedString(long i, int radix) { + // throw new Error("Method redefined away!"); + // } + // private static BigInteger toUnsignedBigInteger(long i) { + // throw new Error("Method redefined away!"); + // } + // public static String toHexString(long i) { + // throw new Error("Method redefined away!"); + // } + // public static String toOctalString(long i) { + // throw new Error("Method redefined away!"); + // } + // public static String toBinaryString(long i) { + // throw new Error("Method redefined away!"); + // } + // static String toUnsignedString0(long val, int shift) { + // throw new Error("Method redefined away!"); + // } + // static int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) { + // throw new Error("Method redefined away!"); + // } + // public static String toString(long i) { + // throw new Error("Method redefined away!"); + // } + // public static String toUnsignedString(long i) { + // throw new Error("Method redefined away!"); + // } + // static void getChars(long i, int index, char[] buf) { + // throw new Error("Method redefined away!"); + // } + // static int stringSize(long x) { + // throw new Error("Method redefined away!"); + // } + // public static long parseLong(String s, int radix) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static long parseLong(String s) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static long parseUnsignedLong(String s, int radix) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static long parseUnsignedLong(String s) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static Long valueOf(String s, int radix) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static Long valueOf(String s) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // public static Long decode(String nm) throws NumberFormatException { + // throw new Error("Method redefined away!"); + // } + // private final long value; + // public Long(String s) throws NumberFormatException { + // this(0); + // throw new Error("Method redefined away!"); + // } + // public byte byteValue() { + // throw new Error("Method redefined away!"); + // } + // public short shortValue() { + // throw new Error("Method redefined away!"); + // } + // public int intValue() { + // throw new Error("Method redefined away!"); + // } + // public long longValue() { + // return value; + // } + // public float floatValue() { + // throw new Error("Method redefined away!"); + // } + // public double doubleValue() { + // throw new Error("Method redefined away!"); + // } + // public int hashCode() { + // throw new Error("Method redefined away!"); + // } + // public static int hashCode(long value) { + // throw new Error("Method redefined away!"); + // } + // public boolean equals(Object obj) { + // throw new Error("Method redefined away!"); + // } + // public static Long getLong(String nm) { + // throw new Error("Method redefined away!"); + // } + // public static Long getLong(String nm, long val) { + // throw new Error("Method redefined away!"); + // } + // public static Long getLong(String nm, Long val) { + // throw new Error("Method redefined away!"); + // } + // public int compareTo(Long anotherLong) { + // throw new Error("Method redefined away!"); + // } + // public static int compare(long x, long y) { + // throw new Error("Method redefined away!"); + // } + // public static int compareUnsigned(long x, long y) { + // throw new Error("Method redefined away!"); + // } + // public static long divideUnsigned(long dividend, long divisor) { + // throw new Error("Method redefined away!"); + // } + // public static long remainderUnsigned(long dividend, long divisor) { + // throw new Error("Method redefined away!"); + // } + // public static final int SIZE = 64; + // public static final int BYTES = SIZE / Byte.SIZE; + // public static long max(long a, long b) { + // throw new Error("Method redefined away!"); + // } + // public static long min(long a, long b) { + // throw new Error("Method redefined away!"); + // } + // private static final long serialVersionUID = 0; + // } + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAiQUAAAAAAAAACgcAdQoAAwB2CAB3CgADAHgJAA0AeQoAAwB6CgADAHsHAHwIAH0K" + + "AAoAfgcAfwoADQCACgASAHYKAA0AgQkADQCCBwCDBwCEAQAJTUlOX1ZBTFVFAQABSgEADUNvbnN0" + + "YW50VmFsdWUFAAAAAAAAAAABAAlNQVhfVkFMVUUBAARUWVBFAQARTGphdmEvbGFuZy9DbGFzczsB" + + "AAlTaWduYXR1cmUBACNMamF2YS9sYW5nL0NsYXNzPExqYXZhL2xhbmcvTG9uZzs+OwEABXZhbHVl" + + "AQAEU0laRQEAAUkDAAAAQAEABUJZVEVTAwAAAAgBABBzZXJpYWxWZXJzaW9uVUlEAQANaGlnaGVz" + + "dE9uZUJpdAEABChKKUoBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAMbG93ZXN0T25lQml0AQAU" + + "bnVtYmVyT2ZMZWFkaW5nWmVyb3MBAAQoSilJAQAVbnVtYmVyT2ZUcmFpbGluZ1plcm9zAQAIYml0" + + "Q291bnQBAApyb3RhdGVMZWZ0AQAFKEpJKUoBAAtyb3RhdGVSaWdodAEAB3JldmVyc2UBAAZzaWdu" + + "dW0BAAxyZXZlcnNlQnl0ZXMBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAWKEpJ" + + "KUxqYXZhL2xhbmcvU3RyaW5nOwEAEHRvVW5zaWduZWRTdHJpbmcBABR0b1Vuc2lnbmVkQmlnSW50" + + "ZWdlcgEAGShKKUxqYXZhL21hdGgvQmlnSW50ZWdlcjsBAAt0b0hleFN0cmluZwEAFShKKUxqYXZh" + + "L2xhbmcvU3RyaW5nOwEADXRvT2N0YWxTdHJpbmcBAA50b0JpbmFyeVN0cmluZwEAEXRvVW5zaWdu" + + "ZWRTdHJpbmcwAQASZm9ybWF0VW5zaWduZWRMb25nAQAJKEpJW0NJSSlJAQAIZ2V0Q2hhcnMBAAco" + + "SklbQylWAQAKc3RyaW5nU2l6ZQEACXBhcnNlTG9uZwEAFihMamF2YS9sYW5nL1N0cmluZztJKUoB" + + "AApFeGNlcHRpb25zBwCFAQAVKExqYXZhL2xhbmcvU3RyaW5nOylKAQARcGFyc2VVbnNpZ25lZExv" + + "bmcBAAd2YWx1ZU9mAQAlKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvbGFuZy9Mb25nOwEAJChM" + + "amF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Mb25nOwEAEyhKKUxqYXZhL2xhbmcvTG9uZzsB" + + "AAZkZWNvZGUBAAY8aW5pdD4BAAQoSilWAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAJYnl0ZVZh" + + "bHVlAQADKClCAQAKc2hvcnRWYWx1ZQEAAygpUwEACGludFZhbHVlAQADKClJAQAJbG9uZ1ZhbHVl" + + "AQADKClKAQAKZmxvYXRWYWx1ZQEAAygpRgEAC2RvdWJsZVZhbHVlAQADKClEAQAIaGFzaENvZGUB" + + "AAZlcXVhbHMBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoBAAdnZXRMb25nAQAlKExqYXZhL2xhbmcv" + + "U3RyaW5nO0opTGphdmEvbGFuZy9Mb25nOwEANChMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5n" + + "L0xvbmc7KUxqYXZhL2xhbmcvTG9uZzsBAAljb21wYXJlVG8BABMoTGphdmEvbGFuZy9Mb25nOylJ" + + "AQAHY29tcGFyZQEABShKSilJAQAPY29tcGFyZVVuc2lnbmVkAQAOZGl2aWRlVW5zaWduZWQBAAUo" + + "SkopSgEAEXJlbWFpbmRlclVuc2lnbmVkAQADc3VtAQADbWF4AQADbWluAQAVKExqYXZhL2xhbmcv" + + "T2JqZWN0OylJAQAIPGNsaW5pdD4BAAMoKVYBADpMamF2YS9sYW5nL051bWJlcjtMamF2YS9sYW5n" + + "L0NvbXBhcmFibGU8TGphdmEvbGFuZy9Mb25nOz47AQAKU291cmNlRmlsZQEACUxvbmcuamF2YQEA" + + "F2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABPAHEBACJSZWRlZmluZWQgTG9uZyEgdmFsdWUgKGFz" + + "IGRvdWJsZSk9DACGAIcMAB4AFQwAhgCIDAA0ADUBAA9qYXZhL2xhbmcvRXJyb3IBABZNZXRob2Qg" + + "cmVkZWZpbmVkIGF3YXkhDABPAFEBAA5qYXZhL2xhbmcvTG9uZwwATwBQDABkAGUMABoAGwEAEGph" + + "dmEvbGFuZy9OdW1iZXIBABRqYXZhL2xhbmcvQ29tcGFyYWJsZQEAH2phdmEvbGFuZy9OdW1iZXJG" + + "b3JtYXRFeGNlcHRpb24BAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcv" + + "U3RyaW5nQnVpbGRlcjsBABwoRClMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ADEADQASAAEAEwAH" + + "ABkAFAAVAAEAFgAAAAIAFwAZABkAFQABABYAAAACABcAGQAaABsAAQAcAAAAAgAdABIAHgAVAAAA" + + "GQAfACAAAQAWAAAAAgAhABkAIgAgAAEAFgAAAAIAIwAaACQAFQABABYAAAACABcANwAJACUAJgAB" + + "ACcAAAAcAAQAAgAAAAQeCmGtAAAAAQAoAAAABgABAAAADgAJACkAJgABACcAAAAcAAQAAgAAAAQe" + + "CmWtAAAAAQAoAAAABgABAAAAEwAJACoAKwABACcAAAAdAAQAAgAAAAUeHmGIrAAAAAEAKAAAAAYA" + + "AQAAABgACQAsACsAAQAnAAAAHwAFAAIAAAAHHh4EfX+IrAAAAAEAKAAAAAYAAQAAAB0ACQAtACsA" + + "AQAnAAAAGgABAAIAAAACCKwAAAABACgAAAAGAAEAAAAiAAkALgAvAAEAJwAAABoAAgADAAAAAh6t" + + "AAAAAQAoAAAABgABAAAAJwAJADAALwABACcAAAAeAAQAAwAAAAYUAAEeaa0AAAABACgAAAAGAAEA" + + "AAAsAAkAMQAmAAEAJwAAABsAAgACAAAAAx51rQAAAAEAKAAAAAYAAQAAADEACQAyACsAAQAnAAAA" + + "GgABAAIAAAACA6wAAAABACgAAAAGAAEAAAA2AAkAMwAmAAEAJwAAABoAAgACAAAAAgmtAAAAAQAo" + + "AAAABgABAAAAOwABADQANQABACcAAAAwAAMAAQAAABi7AANZtwAEEgW2AAYqtAAHirYACLYACbAA" + + "AAABACgAAAAGAAEAAAA/AAkANAA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgAAAAG" + + "AAEAAABDAAkANwA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABGAAoA" + + "OAA5AAEAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABKAAkAOgA7AAEAJwAA" + + "ACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABNAAkAPAA7AAEAJwAAACIAAwACAAAA" + + "CrsAClkSC7cADL8AAAABACgAAAAGAAEAAABRAAkAPQA7AAEAJwAAACIAAwACAAAACrsAClkSC7cA" + + "DL8AAAABACgAAAAGAAEAAABVAAgAPgA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgA" + + "AAAGAAEAAABZAAgAPwBAAAEAJwAAACIAAwAGAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABd" + + "AAkANAA7AAEAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABhAAkANwA7AAEA" + + "JwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABlAAgAQQBCAAEAJwAAACIAAwAE" + + "AAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABpAAgAQwArAAEAJwAAACIAAwACAAAACrsAClkS" + + "C7cADL8AAAABACgAAAAGAAEAAABtAAkARABFAAIAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAAB" + + "ACgAAAAGAAEAAABxAEYAAAAEAAEARwAJAEQASAACACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/AAAA" + + "AQAoAAAABgABAAAAdQBGAAAABAABAEcACQBJAEUAAgAnAAAAIgADAAIAAAAKuwAKWRILtwAMvwAA" + + "AAEAKAAAAAYAAQAAAHkARgAAAAQAAQBHAAkASQBIAAIAJwAAACIAAwABAAAACrsAClkSC7cADL8A" + + "AAABACgAAAAGAAEAAAB9AEYAAAAEAAEARwAJAEoASwACACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/" + + "AAAAAQAoAAAABgABAAAAgQBGAAAABAABAEcACQBKAEwAAgAnAAAAIgADAAEAAAAKuwAKWRILtwAM" + + "vwAAAAEAKAAAAAYAAQAAAIQARgAAAAQAAQBHAAkASgBNAAEAJwAAACEABAACAAAACbsADVketwAO" + + "sAAAAAEAKAAAAAYAAQAAAIcACQBOAEwAAgAnAAAAIgADAAEAAAAKuwAKWRILtwAMvwAAAAEAKAAA" + + "AAYAAQAAAIsARgAAAAQAAQBHAAEATwBQAAEAJwAAACoAAwADAAAACiq3AA8qH7UAB7EAAAABACgA" + + "AAAOAAMAAACQAAQAkQAJAJIAAQBPAFEAAgAnAAAAKwADAAIAAAAPKgm3AA67AApZEgu3AAy/AAAA" + + "AQAoAAAACgACAAAAlQAFAJYARgAAAAQAAQBHAAEAUgBTAAEAJwAAACIAAwABAAAACrsAClkSC7cA" + + "DL8AAAABACgAAAAGAAEAAACaAAEAVABVAAEAJwAAACIAAwABAAAACrsAClkSC7cADL8AAAABACgA" + + "AAAGAAEAAACeAAEAVgBXAAEAJwAAACIAAwABAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAACi" + + "AAEAWABZAAEAJwAAAB0AAgABAAAABSq0AAetAAAAAQAoAAAABgABAAAApgABAFoAWwABACcAAAAi" + + "AAMAAQAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAqgABAFwAXQABACcAAAAiAAMAAQAAAAq7" + + "AApZEgu3AAy/AAAAAQAoAAAABgABAAAArgABAF4AVwABACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/" + + "AAAAAQAoAAAABgABAAAAsgAJAF4AKwABACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/AAAAAQAoAAAA" + + "BgABAAAAtgABAF8AYAABACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAugAJ" + + "AGEATAABACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAvgAJAGEAYgABACcA" + + "AAAiAAMAAwAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAwgAJAGEAYwABACcAAAAiAAMAAgAA" + + "AAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAxgABAGQAZQABACcAAAAiAAMAAgAAAAq7AApZEgu3" + + "AAy/AAAAAQAoAAAABgABAAAAyQAJAGYAZwABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAo" + + "AAAABgABAAAAzQAJAGgAZwABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA" + + "0QAJAGkAagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA1QAJAGsAagAB" + + "ACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA2QAJAGwAagABACcAAAAcAAQA" + + "BAAAAAQeIGGtAAAAAQAoAAAABgABAAAA4AAJAG0AagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/" + + "AAAAAQAoAAAABgABAAAA5AAJAG4AagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAA" + + "BgABAAAA5xBBAGQAbwABACcAAAAhAAIAAgAAAAkqK8AADbYAEKwAAAABACgAAAAGAAEAAAAFAAgA" + + "cABxAAEAJwAAACEAAQAAAAAABQGzABGxAAAAAQAoAAAACgACAAAACAAEAAoAAgAcAAAAAgByAHMA" + + "AAACAHQ="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAFtMupmeDN6Ck5nxdemGsp43KmLNpYLrMYFgAAcAAAAHhWNBIAAAAAAAAAAEgVAABl" + + "AAAAcAAAABUAAAAEAgAAIAAAAFgCAAAHAAAA2AMAAD0AAAAQBAAAAQAAAPgFAAAAEAAAGAYAAB4O" + + "AAAhDgAAKw4AADMOAAA3DgAAOg4AAEEOAABEDgAARw4AAEoOAABODgAAVg4AAFsOAABfDgAAYg4A" + + "AGYOAABrDgAAcA4AAHQOAAB5DgAAfA4AAIAOAACEDgAAiQ4AAI0OAACSDgAAlw4AAJwOAAC7DgAA" + + "1w4AAOkOAAD8DgAAEw8AACsPAAA+DwAAUA8AAGQPAACHDwAAmw8AAK8PAADKDwAA4g8AAO0PAAD4" + + "DwAAAxAAABsQAAA/EAAAQhAAAEgQAABOEAAAURAAAFUQAABbEAAAXxAAAGIQAABmEAAAahAAAHIQ" + + "AAB8EAAAhxAAAJAQAACbEAAArBAAALQQAADEEAAA0RAAAOUQAADtEAAA+RAAAA0RAAAXEQAAIBEA" + + "ACoRAAA5EQAAQxEAAE4RAABcEQAAYREAAGYRAAB8EQAAkxEAAJ4RAACxEQAAxBEAAM0RAADbEQAA" + + "5xEAAPQRAAAGEgAAEhIAABoSAAAmEgAAKxIAADsSAABIEgAAVxIAAGESAAB3EgAAiRIAAJwSAACj" + + "EgAABAAAAAYAAAAHAAAACAAAAA0AAAAbAAAAHAAAAB4AAAAgAAAAIQAAACIAAAAjAAAAJAAAACUA" + + "AAAmAAAAJwAAACgAAAAuAAAAMQAAADUAAAA3AAAABAAAAAAAAAAAAAAABgAAAAEAAAAAAAAABwAA" + + "AAIAAAAAAAAACAAAAAMAAAAAAAAACQAAAAMAAAC0DQAACgAAAAMAAAC8DQAACwAAAAMAAADMDQAA" + + "DAAAAAMAAADUDQAADAAAAAMAAADcDQAADQAAAAQAAAAAAAAADgAAAAQAAAC0DQAADwAAAAQAAADk" + + "DQAAEAAAAAQAAADMDQAAEQAAAAQAAADsDQAAEgAAAAQAAAD0DQAAFQAAAAoAAAC0DQAAFwAAAAoA" + + "AADsDQAAGAAAAAoAAAD0DQAAGQAAAAoAAAD8DQAAGgAAAAoAAAAEDgAAEwAAAA4AAAAAAAAAFQAA" + + "AA4AAAC0DQAAFgAAAA4AAADkDQAAFAAAAA8AAAAMDgAAFwAAAA8AAADsDQAAFQAAABAAAAC0DQAA" + + "LgAAABEAAAAAAAAAMQAAABIAAAAAAAAAMgAAABIAAAC0DQAAMwAAABIAAAAUDgAANAAAABIAAADs" + + "DQAANgAAABMAAADcDQAACgADAAUAAAAKAAQAKgAAAAoABAArAAAACgADAC8AAAAKAAcAMAAAAAoA" + + "BABXAAAACgAEAGMAAAAJAB4AAgAAAAoAGwABAAAACgAcAAIAAAAKAB4AAgAAAAoABAA5AAAACgAA" + + "ADoAAAAKAAYAOwAAAAoABwA8AAAACgAIADwAAAAKAAYAPQAAAAoAEAA+AAAACgAMAD8AAAAKAAEA" + + "QAAAAAoAHwBCAAAACgACAEMAAAAKAAUARAAAAAoAHQBFAAAACgAQAEYAAAAKABIARgAAAAoAEwBG" + + "AAAACgADAEcAAAAKAAQARwAAAAoACgBIAAAACgADAEkAAAAKAAkASgAAAAoACgBLAAAACgAMAEwA" + + "AAAKAAwATQAAAAoABABOAAAACgAEAE8AAAAKAA0AUAAAAAoADgBQAAAACgANAFEAAAAKAA4AUQAA" + + "AAoADABSAAAACgAKAFMAAAAKAAoAVAAAAAoACwBVAAAACgALAFYAAAAKABoAWAAAAAoABABZAAAA" + + "CgAEAFoAAAAKAAwAWwAAAAoAFQBcAAAACgAVAF0AAAAKABUAXgAAAAoAFABfAAAACgAVAF8AAAAK" + + "ABYAXwAAAAoAGQBgAAAACgAVAGEAAAAKABYAYQAAAAoAFgBiAAAACgAPAGQAAAAKABAAZAAAAAoA" + + "EQBkAAAACwAbAAIAAAAPABsAAgAAAA8AFwA4AAAADwAYADgAAAAPABQAXwAAAAoAAAARAAAACwAA" + + "AKwNAAApAAAAVA0AAFEUAABIFAAAAQAAACIUAAABAAAAMhQAAAEAAABAFAAAAAAAAAAAAACsEgAA" + + "AQAAAA4AAAAEAAMAAQAAALESAAAGAAAAcBA4AAEAWhIGAA4ABAACAAMAAAC6EgAADgAAABYAAABw" + + "MAIAAgEiAAkAGwEsAAAAcCAAABAAJwADAAIAAAAAAMISAAACAAAAElAPAAYABAACAAAAyBIAAAkA" + + "AAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAACAAAA0BIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAA" + + "AAMAAQACAAAA2BIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAACAAAA3xIAAAkAAAAiAAkA" + + "GwEsAAAAcCAAABAAJwAAAAgABgACAAAA5xIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAAC" + + "AAAA8RIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMAAQACAAAA+RIAAAkAAAAiAAkAGwEsAAAA" + + "cCAAABAAJwAAAAUAAwACAAAAABMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAACBMA" + + "AAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAAEBMAAAkAAAAiAAkAGwEsAAAAcCAAABAA" + + "JwAAAAQAAgAAAAAAFxMAAAQAAAAWAAEAuyAQAAQAAgAAAAAAHRMAAAUAAAAWAAEAnAACABAAAAAG" + + "AAQAAgAAACMTAAAJAAAAIgAJABsBLAAAAHAgAAAQACcAAAAGAAQAAgAAACsTAAAJAAAAIgAJABsB" + + "LAAAAHAgAAAQACcAAAAEAAIAAAAAADMTAAAEAAAAmwACAoQADwAEAAIAAAAAADkTAAAGAAAAEhCl" + + "AAIAwCCEAA8AAwABAAIAAAA/EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAABFEwAA" + + "CQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAABMEwAACQAAACIACQAbASwAAABwIAAAEAAn" + + "AAAABAACAAIAAABSEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABgAEAAIAAABZEwAACQAAACIA" + + "CQAbASwAAABwIAAAEAAnAAAABAACAAAAAABhEwAAAgAAAH0gEAAEAAIAAAAAAGcTAAADAAAAFgAA" + + "ABAAAAADAAMAAAAAAG0TAAABAAAAEAAAAAUAAwAAAAAAdBMAAAQAAAAWAAoAvSAQAAMAAgAAAAAA" + + "exMAAAIAAAASAA8ABAACAAIAAACBEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABgAEAAAAAACH" + + "EwAAAwAAAJsAAgQQAAAABAACAAIAAACPEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIA" + + "AACVEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAACbEwAACQAAACIACQAbASwAAABw" + + "IAAAEAAnAAAABAACAAIAAAChEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQADAAIAAACnEwAA" + + "CQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAACuEwAACQAAACIACQAbASwAAABwIAAAEAAn" + + "AAAABAACAAIAAAC0EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQADAAIAAAC6EwAACQAAACIA" + + "CQAbASwAAABwIAAAEAAnAAAABQADAAIAAADBEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAAC" + + "AAMAAADIEwAABgAAACIACgBwMAIAIAMRAAMAAQACAAAAzxMAAAkAAAAiAAkAGwEsAAAAcCAAABAA" + + "JwAAAAQAAgACAAAA1hMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMAAQACAAAA3hMAAAkAAAAi" + + "AAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAA5BMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMA" + + "AgACAAAA6xMAAAcAAAAfAgoAbiAHACEACgAPAAAAAwABAAIAAADyEwAACQAAACIACQAbASwAAABw" + + "IAAAEAAnAAAABAACAAIAAAD4EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAAD/EwAA" + + "CQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAAAFFAAACQAAACIACQAbASwAAABwIAAAEAAn" + + "AAAAAwABAAIAAAALFAAACQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAAAAAARFAAAAwAAAFMg" + + "BgAQAAAAAwABAAIAAAAXFAAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQABAAMAAAAdFAAAGAAA" + + "ACIADwBwEDkAAAAbAS0AAABuIDsAEAAMAFNCBgCGIm4wOgAgAwwAbhA8AAAADAARABgGAAABAAAA" + + "CAAAAAAAAAAEAAAAIAYAAAMAAAAoBgAACgAAACgGAAAeAAAAKAYAAB8AAAAoBgAAIAAAACgGAAAh" + + "AAAAKAYAADYAAAAoBgAANwAAACgGAAABAAAACAAAAAEAAAAEAAAABQAAAAQAAwAUAAMAAwAAAAIA" + + "AAAEAAQAAQAAAAoAAAABAAAADQAAAAIAAAAEAAMAAQAAAA4AAAACAAAADgADAAIAAAAOAAQAAgAA" + + "AA4ACgABAAAAAQAAAAMAAAAEAAMAFAABPAAIPGNsaW5pdD4ABjxpbml0PgACPjsAAUIABUJZVEVT" + + "AAFEAAFGAAFJAAJJSgAGSUpJTElJAANJSkoAAklMAAFKAAJKSgADSkpJAANKSkoAAkpMAANKTEkA" + + "AUwAAkxEAAJMSgADTEpJAAJMTAADTExJAANMTEoAA0xMTAAdTGRhbHZpay9hbm5vdGF0aW9uL1Np" + + "Z25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABBMamF2YS9sYW5nL0NsYXNzABFM" + + "amF2YS9sYW5nL0NsYXNzOwAVTGphdmEvbGFuZy9Db21wYXJhYmxlABZMamF2YS9sYW5nL0NvbXBh" + + "cmFibGU7ABFMamF2YS9sYW5nL0Vycm9yOwAQTGphdmEvbGFuZy9Mb25nOwASTGphdmEvbGFuZy9O" + + "dW1iZXI7ACFMamF2YS9sYW5nL051bWJlckZvcm1hdEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2Jq" + + "ZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABZMamF2" + + "YS9tYXRoL0JpZ0ludGVnZXI7AAlMb25nLmphdmEACU1BWF9WQUxVRQAJTUlOX1ZBTFVFABZNZXRo" + + "b2QgcmVkZWZpbmVkIGF3YXkhACJSZWRlZmluZWQgTG9uZyEgdmFsdWUgKGFzIGRvdWJsZSk9AAFT" + + "AARTSVpFAARUWVBFAAFWAAJWSgAEVkpJTAACVkwAAVoAAlpMAAJbQwAGYXBwZW5kAAhiaXRDb3Vu" + + "dAAJYnl0ZVZhbHVlAAdjb21wYXJlAAljb21wYXJlVG8AD2NvbXBhcmVVbnNpZ25lZAAGZGVjb2Rl" + + "AA5kaXZpZGVVbnNpZ25lZAALZG91YmxlVmFsdWUAEmVtaXR0ZXI6IGphY2stNC4yNQAGZXF1YWxz" + + "AApmbG9hdFZhbHVlABJmb3JtYXRVbnNpZ25lZExvbmcACGdldENoYXJzAAdnZXRMb25nAAhoYXNo" + + "Q29kZQANaGlnaGVzdE9uZUJpdAAIaW50VmFsdWUACWxvbmdWYWx1ZQAMbG93ZXN0T25lQml0AANt" + + "YXgAA21pbgAUbnVtYmVyT2ZMZWFkaW5nWmVyb3MAFW51bWJlck9mVHJhaWxpbmdaZXJvcwAJcGFy" + + "c2VMb25nABFwYXJzZVVuc2lnbmVkTG9uZwARcmVtYWluZGVyVW5zaWduZWQAB3JldmVyc2UADHJl" + + "dmVyc2VCeXRlcwAKcm90YXRlTGVmdAALcm90YXRlUmlnaHQAEHNlcmlhbFZlcnNpb25VSUQACnNo" + + "b3J0VmFsdWUABnNpZ251bQAKc3RyaW5nU2l6ZQADc3VtAA50b0JpbmFyeVN0cmluZwALdG9IZXhT" + + "dHJpbmcADXRvT2N0YWxTdHJpbmcACHRvU3RyaW5nABR0b1Vuc2lnbmVkQmlnSW50ZWdlcgAQdG9V" + + "bnNpZ25lZFN0cmluZwARdG9VbnNpZ25lZFN0cmluZzAABXZhbHVlAAd2YWx1ZU9mAAUABw4AkAEB" + + "AAcOPC0AlQEBAAcOWgAiAQAHDgDNAQIAAAcOANEBAgAABw4AiwEBAAcOANUBAgAABw4AXQUAAAAA" + + "AAcOAGkDAAAABw4AvgEBAAcOAMIBAgAABw4AxgECAAAHDgC2AQEABw4ADgEABw4AEwEABw4A5AEC" + + "AAAHDgDnAQIAAAcOABgBAAcOAB0BAAcOAHUBAAcOAHECAAAHDgB9AQAHDgB5AgAABw4A2QECAAAH" + + "DgAxAQAHDgA7AQAHDgAnAgAABw4ALAIAAAcOADYBAAcOAG0BAAcOAOABAgAABw4AVQEABw4ATQEA" + + "Bw4AUQEABw4AYQEABw4AQwIAAAcOAEoBAAcOAGUBAAcOAEYCAAAHDgBZAgAABw4AhwEBAAcOAIQB" + + "AQAHDgCBAQIAAAcOAJoBAAcOAMkBAQAHDgDIAQEABw4ArgEABw4AugEBAAcOAKoBAAcOALIBAAcO" + + "AKIBAAcOAKYBAAcOAJ4BAAcOAD8ABw4AAgUBYxwFFyMXHxcAFyIXAwIFAWMcBBcdFwAXIhcDAgYB" + + "YxwBGAwEBAgGAAYABEAGASwLABkBGQEZARkBGQEaBhIBiIAEsAwBgYAExAwBgYAE4AwBCYwNAgmg" + + "DQMJxA0BCegNAQmMDgQIsA4BCNQOAQn4DgEJnA8BCcAPAgnkDwEJiBADCaAQAQm8EAEJ4BABCYQR" + + "AQmcEQEJuBEBCdwRAQmAEgEJpBIBCcgSAQnsEgEJgBMBCZgTAQmsEwIJxBMBCNgTAQn8EwEJlBQB" + + "CbgUAQncFAIJgBUBCaQVAQrIFQEJ7BUBCZAWAQi0FgEJ2BYBCfQWAQmYFwUBvBcCAeAXAcEghBgE" + + "AaQYAQHIGAEB7BgGAZAZAwG0GQEB2BkPAfAZBwGUGgAAEQAAAAAAAAABAAAAAAAAAAEAAABlAAAA" + + "cAAAAAIAAAAVAAAABAIAAAMAAAAgAAAAWAIAAAQAAAAHAAAA2AMAAAUAAAA9AAAAEAQAAAYAAAAB" + + "AAAA+AUAAAMQAAADAAAAGAYAAAEgAAA3AAAAMAYAAAYgAAABAAAAVA0AAAEQAAANAAAArA0AAAIg" + + "AABlAAAAHg4AAAMgAAA3AAAArBIAAAQgAAADAAAAIhQAAAUgAAABAAAASBQAAAAgAAABAAAAURQA" + + "AAAQAAABAAAASBUAAA=="); + + static class FuncCmp implements LongPredicate { + final String name; + final LongPredicate p; + public FuncCmp(String name, LongPredicate p) { + this.name = name; + this.p = p; + } + + public boolean test(long l) { + return p.test(l); + } + } + static FuncCmp l2l(String name, final LongUnaryOperator a, final LongUnaryOperator b) { + return new FuncCmp(name, (v) -> a.applyAsLong(v) == b.applyAsLong(v)); + } + static FuncCmp l2i(String name, final LongToIntFunction a, final LongToIntFunction b) { + return new FuncCmp(name, (v) -> a.applyAsInt(v) == b.applyAsInt(v)); + } + + /** Interface for a long, int -> long function. */ + static interface LI2IFunction { public long applyToLongInt(long a, int b); } + + static FuncCmp li2l(String name, final Random r, final LI2IFunction a, final LI2IFunction b) { + return new FuncCmp(name, new LongPredicate() { + public boolean test(long v) { + int i = r.nextInt(); + return a.applyToLongInt(v, i) == b.applyToLongInt(v, i); + } + }); + } + + public static void main(String[] args) { + doTest(10000); + } + + public static void doTest(int iters) { + // Just transform immediately. + doCommonClassRedefinition(Long.class, CLASS_BYTES, DEX_BYTES); + final Random r = new Random(); + FuncCmp[] comps = new FuncCmp[] { + l2l("highestOneBit", Long::highestOneBit, RedefinedLongIntrinsics::highestOneBit), + l2l("lowestOneBit", Long::lowestOneBit, RedefinedLongIntrinsics::lowestOneBit), + l2i("numberOfLeadingZeros", + Long::numberOfLeadingZeros, + RedefinedLongIntrinsics::numberOfLeadingZeros), + l2i("numberOfTrailingZeros", + Long::numberOfTrailingZeros, + RedefinedLongIntrinsics::numberOfTrailingZeros), + l2i("bitCount", Long::bitCount, RedefinedLongIntrinsics::bitCount), + li2l("rotateLeft", r, Long::rotateLeft, RedefinedLongIntrinsics::rotateLeft), + li2l("rotateRight", r, Long::rotateRight, RedefinedLongIntrinsics::rotateRight), + l2l("reverse", Long::reverse, RedefinedLongIntrinsics::reverse), + l2i("signum", Long::signum, RedefinedLongIntrinsics::signum), + l2l("reverseBytes", Long::reverseBytes, RedefinedLongIntrinsics::reverseBytes), + }; + for (FuncCmp f : comps) { + // Just actually use ints so we can cast them back int the tests to print them (since we + // deleted a bunch of the Long methods needed for printing longs)! + int failures = (int)r.ints(iters) + .mapToLong((v) -> (long)v) + .filter(f.negate()) // Get all the test cases that failed. + .count(); + if (failures != 0) { + double percent = 100.0d*((double)failures/(double)iters); + System.out.println("for intrinsic " + f.name + " " + failures + "/" + iters + + " (" + Double.toString(percent) + "%) tests failed!"); + } + } + System.out.println("Finished!"); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java b/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java new file mode 100644 index 0000000000..0ada4a6abf --- /dev/null +++ b/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * The methods that are intrinsified in Long and their expected redefined implementations. + */ +class RedefinedLongIntrinsics { + // Intrinsic! Do something cool. Return i + 1 + public static long highestOneBit(long i) { + return i + 1; + } + + // Intrinsic! Do something cool. Return i - 1 + public static long lowestOneBit(long i) { + return i - 1; + } + + // Intrinsic! Do something cool. Return i + i + public static int numberOfLeadingZeros(long i) { + return (int)(i + i); + } + + // Intrinsic! Do something cool. Return i & (i >>> 1); + public static int numberOfTrailingZeros(long i) { + return (int)(i & (i >>> 1)); + } + + // Intrinsic! Do something cool. Return 5 + public static int bitCount(long i) { + return 5; + } + + // Intrinsic! Do something cool. Return i + public static long rotateLeft(long i, int distance) { + return i; + } + + // Intrinsic! Do something cool. Return 10 * i + public static long rotateRight(long i, int distance) { + return 10 * i; + } + + // Intrinsic! Do something cool. Return -i + public static long reverse(long i) { + return -i; + } + + // Intrinsic! Do something cool. Return 0 + public static int signum(long i) { + return 0; + } + + // Intrinsic! Do something cool. Return 0 + public static long reverseBytes(long i) { + return 0; + } +} diff --git a/test/testrunner/env.py b/test/testrunner/env.py index f327974dae..4336d7772f 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -176,6 +176,8 @@ ART_TEST_ANDROID_ROOT = env.get('ART_TEST_ANDROID_ROOT') ART_TEST_WITH_STRACE = getEnvBoolean('ART_TEST_DEBUG_GC', False) +EXTRA_DISABLED_TESTS = set(env.get("ART_TEST_RUN_TEST_SKIP", "").split()) + TARGET_2ND_ARCH = get_build_var('TARGET_2ND_ARCH') TARGET_ARCH = get_build_var('TARGET_ARCH') if TARGET_2ND_ARCH: diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 748ec31ae4..8c0b9283b6 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -44,10 +44,10 @@ For eg, for compiler type as optimizing, use --optimizing. In the end, the script will print the failed and skipped tests if any. """ +import argparse import fnmatch import itertools import json -from optparse import OptionParser import os import re import subprocess @@ -596,6 +596,8 @@ def is_test_disabled(test, variant_set): """ if dry_run: return True + if test in env.EXTRA_DISABLED_TESTS: + return True variants_list = DISABLED_TEST_CONTAINER.get(test, {}) for variants in variants_list: variants_present = True @@ -711,23 +713,26 @@ def parse_option(): global gdb global gdb_arg - parser = OptionParser() - parser.add_option('-t', '--test', dest='test', help='name of the test') - parser.add_option('-j', type='int', dest='n_thread') + parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.") + parser.add_argument('-t', '--test', dest='test', help='name of the test') + parser.add_argument('-j', type=int, dest='n_thread') for variant in TOTAL_VARIANTS_SET: flag = '--' + variant flag_dest = variant.replace('-', '_') if variant == '32' or variant == '64': flag_dest = 'n' + flag_dest - parser.add_option(flag, action='store_true', dest=flag_dest) - parser.add_option('--verbose', '-v', action='store_true', dest='verbose') - parser.add_option('--dry-run', action='store_true', dest='dry_run') - parser.add_option('-b', '--build-dependencies', action='store_true', dest='build') - parser.add_option('--gdb', action='store_true', dest='gdb') - parser.add_option('--gdb-arg', dest='gdb_arg') - - options = parser.parse_args()[0] + parser.add_argument(flag, action='store_true', dest=flag_dest) + parser.add_argument('--verbose', '-v', action='store_true', dest='verbose') + parser.add_argument('--dry-run', action='store_true', dest='dry_run') + parser.add_argument("--skip", action="append", dest="skips", default=[], + help="Skip the given test in all circumstances.") + parser.add_argument('-b', '--build-dependencies', action='store_true', dest='build') + parser.add_argument('--gdb', action='store_true', dest='gdb') + parser.add_argument('--gdb-arg', dest='gdb_arg') + + options = parser.parse_args() test = '' + env.EXTRA_DISABLED_TESTS.update(set(options.skips)) if options.test: test = parse_test_name(options.test) if options.pictest: diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index 08d41f0feb..133426f2f0 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -75,6 +75,9 @@ Things to move to perflib: * Instance.isRoot and Instance.getRootTypes. Release History: + 1.1 Feb 21, 2017 + Show java.lang.ref.Reference referents as "unreachable" instead of null. + 1.0 Dec 20, 2016 Add support for diffing two heap dumps. Remove native allocations view. diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt index 87a82b9f99..20245f312d 100644 --- a/tools/ahat/src/manifest.txt +++ b/tools/ahat/src/manifest.txt @@ -1,4 +1,4 @@ Name: ahat/ Implementation-Title: ahat -Implementation-Version: 1.0 +Implementation-Version: 1.1 Main-Class: com.android.ahat.Main |