Merge "Move StackMapStream deduplication maps to arena."
diff --git a/build/Android.bp b/build/Android.bp
index b1553c7..6c9f1d4 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -146,6 +146,7 @@
"external/valgrind",
"external/vixl/src",
"external/zlib",
+ "libnativehelper/platform_include"
],
tidy_checks: [
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e6b7930..dded966 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1541,10 +1541,10 @@
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 @@
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 @@
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 @@
// 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 2d85e8f..562d948 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -55,19 +55,67 @@
"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 @@
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 491e739..18a6670 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 a519631..e15a6bc 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -44,17 +44,7 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 3d51fdd..99d7a49 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -223,13 +223,10 @@
}
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 0325535..a5bb91a 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -327,7 +327,7 @@
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 @@
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 cf63b30..48a8742 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -97,7 +97,8 @@
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 29bab01..7b1e2b8 100644
--- a/runtime/gc/accounting/remembered_set.cc
+++ b/runtime/gc/accounting/remembered_set.cc
@@ -74,7 +74,7 @@
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 f18ffb4..3d2fd0b 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -109,12 +109,29 @@
}
}
-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 844bb45..073326d 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -176,7 +176,8 @@
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 14fd332..1e4196b 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -65,7 +65,8 @@
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 @@
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 @@
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 95601d7..14d0499 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -199,12 +199,17 @@
// 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 @@
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 85e6783..0039388 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -260,7 +260,8 @@
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 6d52d5d..85727c2 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -170,7 +170,8 @@
// 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 f00da73..f591cf0 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -532,7 +532,8 @@
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 a6e2d61..5a9b9f8 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -216,7 +216,8 @@
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 cb9e7e2..4c0f317 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -606,7 +606,8 @@
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 52b5e5f..9d6e74d 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -110,7 +110,8 @@
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 34afa2a..53be30e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3332,7 +3332,7 @@
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 86b1522..65a550e 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -139,6 +139,14 @@
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 @@
// 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 734caea..fd5dcf9 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -67,6 +67,11 @@
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 @@
}
}
}
- return ref;
}
void ReferenceQueue::Dump(std::ostream& os) const {
@@ -140,6 +144,9 @@
}
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 @@
}
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 @@
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 b5ec1e5..b73a880 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -63,8 +63,15 @@
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 adb7d8a..f6720bd 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -250,7 +250,7 @@
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 @@
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 af0478c..80554c2 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1330,17 +1330,18 @@
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 6fc7989..e9435e4 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 c226a38..b1ba952 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1264,12 +1264,27 @@
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 547b5b8..5418d35 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2265,7 +2265,18 @@
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 b3837c4..580a42b 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 c2407d7..57b20a1 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -36,7 +36,7 @@
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 @@
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 0ab0bd6..884b88a 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -79,14 +79,55 @@
}
}
-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);
- } else {
- GetValue()[index] = 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 {
+ 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 38f6dd4..35ce98e 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -94,7 +94,10 @@
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 @@
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 ae6b31d..461f870 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -45,6 +45,9 @@
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 @@
// 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 0d24587..f6a73a8 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -537,14 +537,14 @@
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 @@
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 6bfccdc..efc42fd 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -642,7 +642,7 @@
}
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 @@
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 @@
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 be6f7f2..0dfafa4 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -139,11 +139,11 @@
}
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 256787b..c8431c0 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -713,36 +713,36 @@
}
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 ee6dda5..8fda4df 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -95,11 +95,11 @@
}
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 6493865..6989244 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 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 f1d6ff5..2e561ff 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -99,9 +99,11 @@
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 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 e0738a4..ec3c7c2 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -87,9 +87,9 @@
}
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 7f8da80..d7c9cd0 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -237,16 +237,16 @@
}
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 195091f..346bd30 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -187,16 +187,16 @@
}
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 ff3e044..654b8a8 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -36,8 +36,8 @@
}
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 a8fa7db..54ab861 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -136,7 +136,7 @@
}
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 ecafd0e..afedc5e 100644
--- a/runtime/native/java_lang_ref_FinalizerReference.cc
+++ b/runtime/native/java_lang_ref_FinalizerReference.cc
@@ -40,8 +40,8 @@
}
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 c778068..b1cb2f2 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -40,8 +40,8 @@
}
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 d827f81..54c2109 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -72,8 +72,8 @@
}
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 66a5359..fb78046 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -124,9 +124,9 @@
}
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 2a39428..bc23bed 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -195,14 +195,14 @@
}
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 374eeb5..9cf80a5 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -493,30 +493,30 @@
}
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 a6589bc..6e5e3d9 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -84,9 +84,9 @@
}
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 0bb9e38..37aa16c 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -63,9 +63,9 @@
}
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 70cd6aa..0279b5f 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -35,7 +35,7 @@
}
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 e51b6d2..4138ccc 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -249,11 +249,11 @@
}
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 5356498..5809708 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 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 ca17c26..69ef59e 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 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 1d73813..50a554c 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 @@
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 644df07..cc5a41a 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -492,69 +492,69 @@
}
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"),
+ 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"),
- 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, 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 4d726ec..ea5e698 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -43,7 +43,8 @@
// 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 a815a60..77ca9ce 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1188,13 +1188,13 @@
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 @@
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 106165c..99139a1 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 @@
return ret_value;
}
+template <typename T>
class JvmtiDeleter {
public:
JvmtiDeleter() : env_(nullptr) {}
@@ -95,9 +97,9 @@
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(ptr);
+ jvmtiError ret = env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
CHECK(ret == ERR(NONE));
}
@@ -105,12 +107,65 @@
mutable jvmtiEnv* env_;
};
-using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+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(reinterpret_cast<unsigned char*>(ptr));
+ CHECK(ret == ERR(NONE));
+ }
+
+ private:
+ mutable jvmtiEnv* env_;
+};
+
+template <typename T>
+using JvmtiUniquePtr = std::unique_ptr<T, JvmtiDeleter<T>>;
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> 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<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 @@
}
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 a8a0ded..4282e38 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -673,18 +673,17 @@
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 @@
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 dbe5da2..3c251d4 100644
--- a/runtime/openjdkjvmti/ti_class_definition.h
+++ b/runtime/openjdkjvmti/ti_class_definition.h
@@ -46,7 +46,7 @@
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 d05f579..66357eb 100644
--- a/runtime/openjdkjvmti/ti_class_loader.cc
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -105,7 +105,6 @@
// 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 @@
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 @@
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 131e6c3..8c3f2ff 100644
--- a/runtime/openjdkjvmti/ti_field.cc
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -63,31 +63,29 @@
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 @@
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 a6cfcc1..bc73029 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -110,35 +110,32 @@
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 @@
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 46b9e71..4f4f013 100644
--- a/runtime/openjdkjvmti/ti_properties.cc
+++ b/runtime/openjdkjvmti/ti_properties.cc
@@ -82,71 +82,69 @@
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 8436045..60ce898 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -170,10 +170,6 @@
// 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 @@
// 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 @@
*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 @@
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 @@
// 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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 f8f8fa6..788ac30 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -186,17 +186,17 @@
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 @@
} 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 1423874..df14333 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -116,11 +116,12 @@
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 @@
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 dd89d64..e2a1001 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -33,7 +33,7 @@
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 dc58819..c2976c8 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 @@
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 @@
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 7522a65..340cd70 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 --(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 5a11a5b..7f9c8fc 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 @@
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 @@
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.
- 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.
+ setTag(I1.class, 2000);
- 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.
+ setTag(I2.class, 2001);
+ v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class.
+ }
- 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.
+ 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 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.
+ 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.
- 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.
+ 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 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.
+ 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.
- cInst.baz2 = aInst;
- v.add(cInstStr, aInstStr); // C -->(field) --> A.
+ 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.
- return aInst;
+ 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 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 @@
}
}
+ 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 @@
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 3ff97ae..21fe972 100644
--- a/test/946-obsolete-throw/src/Main.java
+++ b/test/946-obsolete-throw/src/Main.java
@@ -71,7 +71,7 @@
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 0000000..1264c94
--- /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 0000000..c19d2b4
--- /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 0000000..e92b873
--- /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 0000000..30cd3ab
--- /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 0000000..0ada4a6
--- /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 f327974..4336d77 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -176,6 +176,8 @@
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 748ec31..8c0b928 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -44,10 +44,10 @@
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 @@
"""
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 @@
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')
+ 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()[0]
+ 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 08d41f0..133426f 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -75,6 +75,9 @@
* 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 87a82b9..20245f3 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
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 08abdb3..6529640 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -105,12 +105,6 @@
names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"]
},
{
- description: "Error decoding digital signature bytes.",
- result: EXEC_FAILED,
- name: "org.apache.harmony.security.tests.java.security.Signature2Test#test_verify$BII",
- bug: 18869265
-},
-{
description: "Test sometimes timeouts on volantis, and on most modes in debug mode",
result: EXEC_TIMEOUT,
names: ["libcore.java.lang.SystemTest#testArrayCopyConcurrentModification"],