Merge "ART: Improve class initializer and constructor verification."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5bdfbc7..cea38e4 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -120,14 +120,14 @@
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_optimizing_pic_64) \
$(HOST_CORE_IMAGE_optimizing_pic_32) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+ $(HOST_CORE_IMAGE_interpreter_pic_64) \
+ $(HOST_CORE_IMAGE_interpreter_pic_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_pic_64) \
$(TARGET_CORE_IMAGE_optimizing_pic_32) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_64) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5da59f3..faf8b41 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1060,13 +1060,13 @@
virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : c->GetMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m, pointer_size);
+ ResolveExceptionsForMethod(&m);
}
return true;
}
private:
- void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size)
+ void ResolveExceptionsForMethod(ArtMethod* method_handle)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
if (code_item == nullptr) {
@@ -1088,8 +1088,7 @@
dex::TypeIndex encoded_catch_handler_handlers_type_idx =
dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
// Add to set of types to resolve if not already in the dex cache resolved types
- if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
- pointer_size)) {
+ if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
method_handle->GetDexFile());
}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 86d92ff..c69ed31 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -487,7 +487,7 @@
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 05a76e1..ecabc58 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -431,10 +431,12 @@
// TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
// kSaveEverything and use a temporary for the .bss entry address in the fast path,
// so that we can avoid another calculation here.
+ UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, ip);
- __ Str(OutputRegister(cls_), MemOperand(ip));
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(OutputRegister(cls_), MemOperand(temp));
}
__ B(GetExitLabel());
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 50aa442..78a4251 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1538,8 +1538,6 @@
}
}
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-
// Iterate over the list of parameter types and test whether any of the
// actual inputs has a more specific reference type than the type declared in
// the signature.
@@ -1551,9 +1549,9 @@
++param_idx, ++input_idx) {
HInstruction* input = invoke_instruction->InputAt(input_idx);
if (input->GetType() == Primitive::kPrimNot) {
- mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType(
+ mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
param_list->GetTypeItem(param_idx).type_idx_,
- pointer_size);
+ /* resolve */ false);
if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
/* declared_can_be_null */ true,
input)) {
@@ -1602,8 +1600,7 @@
// TODO: we could be more precise by merging the phi inputs but that requires
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
+ mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
}
}
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index a4d59ab..8854a2b 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -840,10 +840,8 @@
}
ScopedObjectAccess soa(Thread::Current());
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = cl->GetImagePointerSize();
ArtMethod* method = instr->GetResolvedMethod();
- mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
+ mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 5e55210..a3fce02 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
" 214: ecbd 8a10 vpop {s16-s31}\n",
" 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
" 21c: 4660 mov r0, ip\n",
- " 21e: f8d9 c2c0 ldr.w ip, [r9, #704] ; 0x2c0\n",
+ " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n",
" 222: 47e0 blx ip\n",
nullptr
};
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 21b03eb..3fbdb89 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1095,6 +1095,8 @@
compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
key_value_store_->Put(OatHeader::kCompilerFilter,
CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
+ key_value_store_->Put(OatHeader::kConcurrentCopying,
+ kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 7ae13a5..a4b414d 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());
@@ -799,133 +668,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;
@@ -960,32 +702,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.");
@@ -993,15 +713,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");
@@ -1010,34 +721,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,
@@ -1076,293 +759,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();
@@ -1384,23 +780,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]);
@@ -1415,42 +799,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=")) {
@@ -1459,12 +809,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") {
@@ -1479,33 +823,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/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 102c313..db1cad6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -856,27 +856,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * r0 = array, r1 = index, r2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- tst r0, r0
- bne art_quick_aput_obj_with_bound_check
- b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
- .hidden art_quick_aput_obj_with_bound_check
-ENTRY art_quick_aput_obj_with_bound_check
- ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET]
- cmp r3, r1
- bhi art_quick_aput_obj
- mov r0, r1
- mov r1, r3
- b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3b3783c..ed24a07 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1404,33 +1404,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * x0 = array, x1 = index, x2 = value
- *
- * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We
- * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by
- * using index-zero-extension in load/stores.
- *
- * Temporaries: x3, x4
- * TODO: x4 OK? ip seems wrong here.
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- tst x0, x0
- bne art_quick_aput_obj_with_bound_check
- b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]
- cmp w3, w1
- bhi art_quick_aput_obj
- mov x0, x1
- mov x1, x3
- b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
@@ -1675,8 +1648,6 @@
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5c56923..36f9ea7 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -142,16 +142,8 @@
static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct.");
// Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck),
- "Non-direct C stub marked direct.");
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck),
- "Non-direct C stub marked direct.");
qpoints->pAputObject = art_quick_aput_obj;
static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct.");
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
- static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct.");
// JNI
qpoints->pJniMethodStart = JniMethodStart;
@@ -262,6 +254,7 @@
art_quick_invoke_virtual_trampoline_with_access_check;
static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
"Non-direct C stub marked direct.");
+ qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
// Thread
qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3acc0a9..76218fb 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1389,28 +1389,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * a0 = array, a1 = index, a2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
- nop
- b art_quick_throw_null_pointer_exception
- nop
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
- sltu $t1, $a1, $t0
- bnez $t1, .Lart_quick_aput_obj_gp_set
- nop
- move $a0, $a1
- b art_quick_throw_array_bounds
- move $a1, $t0
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
@@ -2285,15 +2263,12 @@
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME
move $a2, rSELF # Make $a2 an alias for the current Thread.
- move $a3, $sp # Make $a3 a pointer to the saved frame context.
- addiu $sp, $sp, -24 # Reserve space for JValue result and 4 words for callee.
- .cfi_adjust_cfa_offset 24
+ addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context.
sw $zero, 20($sp) # Initialize JValue result.
sw $zero, 16($sp)
- addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
la $t9, artInvokePolymorphic
jalr $t9 # (result, receiver, Thread*, context)
- nop
+ addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
beq $v0, $t0, \handler
@@ -2307,18 +2282,17 @@
MATCH_RETURN_TYPE 'D', .Lstore_double_result
MATCH_RETURN_TYPE 'F', .Lstore_float_result
MATCH_RETURN_TYPE 'S', .Lstore_int_result
+ MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
.purgem MATCH_RETURN_TYPE
nop
b .Lcleanup_and_return
nop
.Lstore_boolean_result:
+ b .Lcleanup_and_return
lbu $v0, 16($sp) # Move byte from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_char_result:
- lhu $v0, 16($sp) # Move char from JValue result to return value register.
b .Lcleanup_and_return
- nop
+ lhu $v0, 16($sp) # Move char from JValue result to return value register.
.Lstore_double_result:
.Lstore_float_result:
LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register.
@@ -2331,8 +2305,6 @@
lw $v0, 16($sp) # Move lower bits from JValue result to return value register.
// Fall-through to clean up and return.
.Lcleanup_and_return:
- addiu $sp, $sp, 24 # Remove space for JValue result and the 4 words for the callee.
- .cfi_adjust_cfa_offset -24
lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
RESTORE_SAVE_REFS_AND_ARGS_FRAME
bnez $t7, 1f # Success if no exception is pending.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ae786fe..b53fd10 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1360,29 +1360,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * a0 = array, a1 = index, a2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- bne $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set
- nop
- b art_quick_throw_null_pointer_exception
- .cpreturn # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- lwu $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
- sltu $t1, $a1, $t0
- bne $t1, $zero, .Lart_quick_aput_obj_gp_set
- nop
- move $a0, $a1
- move $a1, $t0
- b art_quick_throw_array_bounds
- .cpreturn # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_bound_check
-
ENTRY art_quick_aput_obj
beq $a2, $zero, .Ldo_aput_null
nop
@@ -2132,9 +2109,8 @@
daddiu $sp, $sp, -8 # Reserve space for JValue result.
.cfi_adjust_cfa_offset 8
sd $zero, 0($sp) # Initialize JValue result.
- move $a0, $sp # Make $a0 a pointer to the JValue result
jal artInvokePolymorphic # (result, receiver, Thread*, context)
- nop
+ move $a0, $sp # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
beq $v0, $t0, \handler
@@ -2148,27 +2124,24 @@
MATCH_RETURN_TYPE 'D', .Lstore_double_result
MATCH_RETURN_TYPE 'F', .Lstore_float_result
MATCH_RETURN_TYPE 'S', .Lstore_long_result
+ MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
.purgem MATCH_RETURN_TYPE
nop
b .Lcleanup_and_return
nop
.Lstore_boolean_result:
+ b .Lcleanup_and_return
lbu $v0, 0($sp) # Move byte from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_char_result:
- lhu $v0, 0($sp) # Move char from JValue result to return value register.
b .Lcleanup_and_return
- nop
+ lhu $v0, 0($sp) # Move char from JValue result to return value register.
.Lstore_double_result:
.Lstore_float_result:
+ b .Lcleanup_and_return
l.d $f0, 0($sp) # Move double/float from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_ref_result:
- lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register.
b .Lcleanup_and_return
- nop
+ lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register.
.Lstore_long_result:
ld $v0, 0($sp) # Move long from JValue result to return value register.
// Fall-through to clean up and return.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index abd9046..3959cbe 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -29,10 +29,6 @@
// Called by managed code to allocate an array when the caller doesn't know whether it has access
// to the created type.
THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate a string from bytes
FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate a string from chars
@@ -71,10 +67,6 @@
THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
@@ -95,8 +87,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -110,8 +100,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -129,8 +117,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
@@ -141,8 +127,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -154,8 +138,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
@@ -166,8 +148,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
@@ -178,8 +158,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
@@ -190,8 +168,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -202,8 +178,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
@@ -214,8 +188,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
@@ -226,8 +198,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
@@ -238,8 +208,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index ee65fa8..393dfe6 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -908,139 +908,6 @@
#endif
}
-
-TEST_F(StubTest, APutObj) {
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
- (defined(__x86_64__) && !defined(__APPLE__))
- Thread* self = Thread::Current();
-
- // Do not check non-checked ones, we'd need handlers and stuff...
- const uintptr_t art_quick_aput_obj_with_null_and_bound_check =
- StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck);
-
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
-
- StackHandleScope<5> hs(soa.Self());
- Handle<mirror::Class> c(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- Handle<mirror::Class> ca(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
-
- // Build a string array of size 1
- Handle<mirror::ObjectArray<mirror::Object>> array(
- hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10)));
-
- // Build a string -> should be assignable
- Handle<mirror::String> str_obj(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
-
- // Build a generic object -> should fail assigning
- Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self())));
-
- // Play with it...
-
- // 1) Success cases
- // 1.1) Assign str_obj to array[0..3]
-
- EXPECT_FALSE(self->IsExceptionPending());
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(0));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(1));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(2));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(3));
-
- // 1.2) Assign null to array[0..3]
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(0));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(1));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(2));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(3));
-
- // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order.
-
- // 2) Failure cases (str into str[])
- // 2.1) Array = null
- // TODO: Throwing NPE needs actual DEX code
-
-// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
-//
-// EXPECT_TRUE(self->IsExceptionPending());
-// self->ClearException();
-
- // 2.2) Index < 0
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1),
- reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // 2.3) Index > 0
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // 3) Failure cases (obj into str[])
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // Tests done.
-#else
- LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA;
- // Force-print to std::cout so it's also outside the logcat.
- std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl;
-#endif
-}
-
TEST_F(StubTest, AllocObject) {
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1d979d8..c420259 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1352,26 +1352,6 @@
#endif // USE_READ_BARRIER
END_MACRO
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * eax = array, ecx = index, edx = value
- */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
- testl %eax, %eax
- jnz SYMBOL(art_quick_aput_obj_with_bound_check)
- jmp SYMBOL(art_quick_throw_null_pointer_exception)
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
- movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx
- cmpl %ebx, %ecx
- jb SYMBOL(art_quick_aput_obj)
- mov %ecx, %eax
- mov %ebx, %ecx
- jmp SYMBOL(art_quick_throw_array_bounds)
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
DEFINE_FUNCTION art_quick_aput_obj
test %edx, %edx // store of null
jz .Ldo_aput_null
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 28034c9..550a5e8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -989,8 +989,6 @@
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -1000,8 +998,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -1466,7 +1462,7 @@
* 64b PUSH/POP and 32b argument.
* TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
*
- * As with art_quick_aput_obj* functions, the 64b versions are in comments.
+ * As with art_quick_aput_obj function, the 64b versions are in comments.
*/
MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
#ifdef USE_READ_BARRIER
@@ -1503,46 +1499,6 @@
#endif // USE_READ_BARRIER
END_MACRO
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- *
- * Currently all the parameters should fit into the 32b portions of the registers. Index always
- * will. So we optimize for a tighter encoding. The 64b versions are in comments.
- *
- * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value
- */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-#if defined(__APPLE__)
- int3
- int3
-#else
- testl %edi, %edi
-// testq %rdi, %rdi
- jnz art_quick_aput_obj_with_bound_check
- jmp art_quick_throw_null_pointer_exception
-#endif // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-#if defined(__APPLE__)
- int3
- int3
-#else
- movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx
-// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx)
- cmpl %ecx, %esi
- jb art_quick_aput_obj
- mov %esi, %edi
-// mov %rsi, %rdi
- mov %ecx, %esi
-// mov %rcx, %rsi
- jmp art_quick_throw_array_bounds
-#endif // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
-
DEFINE_FUNCTION art_quick_aput_obj
testl %edx, %edx // store of null
// test %rdx, %rdx
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9c20740..0fd891c 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -180,20 +180,6 @@
pointer_size);
}
-template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
- PointerSize pointer_size) {
- if (kWithCheck) {
- mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
- if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
- ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
- return nullptr;
- }
- }
- mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read();
- return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
-}
-
inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) {
return GetDexCacheResolvedTypes(pointer_size) != nullptr;
}
@@ -207,15 +193,15 @@
return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
}
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx,
- bool resolve,
- PointerSize pointer_size) {
- mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size);
- if (type == nullptr && resolve) {
- type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+ ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+ ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+ if (UNLIKELY(type == nullptr) && resolve) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ type = class_linker->ResolveType(type_idx, this);
CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
}
- return type;
+ return type.Ptr();
}
inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -333,9 +319,9 @@
return GetDexFile()->GetCodeItem(GetCodeItemOffset());
}
-inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
+inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
DCHECK(!IsProxyMethod());
- return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr;
+ return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
}
inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -435,18 +421,13 @@
SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size);
}
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
- mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size);
- if (type == nullptr && resolve) {
- type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
- }
- return type;
+ return GetClassFromTypeIndex(return_type_idx, resolve);
}
inline bool ArtMethod::HasSingleImplementation() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index dfc7837..d7d39af 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -236,7 +236,6 @@
// Default to handler not found.
uint32_t found_dex_pc = DexFile::kDexNoIndex;
// Iterate over the catch handlers associated with dex_pc.
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
// Catch all case
@@ -245,9 +244,7 @@
break;
}
// Does this catch exception type apply?
- mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
- true /* resolve */,
- pointer_size);
+ mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
if (UNLIKELY(iter_exception_type == nullptr)) {
// Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
// removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b80ed9c..d292630 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -351,9 +351,6 @@
bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool kWithCheck = true>
- mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -364,9 +361,7 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the Class* from the type index into this method's dex cache.
- mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx,
- bool resolve,
- PointerSize pointer_size)
+ mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if this method has the same name and signature of the other method.
@@ -558,8 +553,7 @@
const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_);
- bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -580,8 +574,7 @@
// May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
// number of bugs at call sites.
- mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 4b15a22..18a53c9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,7 +104,7 @@
// Offset of field Thread::tlsPtr_.mterp_current_ibase.
#define THREAD_CURRENT_IBASE_OFFSET \
- (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+ (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 9116097..e05a85a 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -46,6 +46,7 @@
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_function_table_lock_ = nullptr;
Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
@@ -957,6 +958,7 @@
DCHECK(verifier_deps_lock_ != nullptr);
DCHECK(host_dlopen_handles_lock_ != nullptr);
DCHECK(intern_table_lock_ != nullptr);
+ DCHECK(jni_function_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
@@ -1098,6 +1100,10 @@
DCHECK(jni_weak_globals_lock_ == nullptr);
jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
+ DCHECK(jni_function_table_lock_ == nullptr);
+ jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2adeb8c..21dd437 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,7 @@
kRosAllocBulkFreeLock,
kMarkSweepMarkStackLock,
kTransactionLogLock,
+ kJniFunctionTableLock,
kJniWeakGlobalsLock,
kJniGlobalsLock,
kReferenceQueueSoftReferencesLock,
@@ -698,8 +699,11 @@
// Guard accesses to the JNI Weak Global Reference table.
static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
+ // Guard accesses to the JNI function table override.
+ static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+
// Have an exclusive aborting thread.
- static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+ static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
// Allow mutual exclusion when manipulating Thread::suspend_count_.
// TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 5fc5f1a..2e17dd8 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,7 +25,6 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable.h"
-#include "mirror/throwable.h"
#include "mirror/object_array.h"
#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -90,25 +89,16 @@
if (kIsDebugBuild) {
Thread::Current()->AssertNoPendingException();
}
- ObjPtr<mirror::Class> resolved_type =
- referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+ ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
if (UNLIKELY(resolved_type == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
- // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
- // ResolveType for this case.
- if (LIKELY(!hs.Self()->IsExceptionPending())) {
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- const DexFile& dex_file = *dex_cache->GetDexFile();
- resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
- } else {
- // Make sure its an array out of bounds exception.
- DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
- "Ljava/lang/ArrayIndexOutOfBoundsException;"));
- }
+ ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+ // Note: We cannot check here to see whether we added the type to the cache. The type
+ // might be an erroneous class, which results in it being hidden from us.
}
return resolved_type.Ptr();
}
@@ -256,8 +246,8 @@
// Locate the dex cache of the original interface/Object
for (const DexCacheData& data : dex_caches_) {
if (!self->IsJWeakCleared(data.weak_root) &&
- proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
- image_pointer_size_)) {
+ proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods,
+ image_pointer_size_)) {
ObjPtr<mirror::DexCache> dex_cache =
ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
if (dex_cache != nullptr) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 448b460..07c6eda 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3303,7 +3303,7 @@
DexCacheData data;
data.weak_root = dex_cache_jweak;
data.dex_file = dex_cache->GetDexFile();
- data.resolved_types = dex_cache->GetResolvedTypes();
+ data.resolved_methods = dex_cache->GetResolvedMethods();
dex_caches_.push_back(data);
}
@@ -4373,8 +4373,7 @@
CHECK_STREQ(np->GetName(), prototype->GetName());
CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
- CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
- prototype->GetReturnType(true /* resolve */, image_pointer_size_));
+ CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
}
bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -4836,7 +4835,6 @@
}
static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
- PointerSize pointer_size,
Handle<mirror::Class> klass,
Handle<mirror::Class> super_klass,
ArtMethod* method1,
@@ -4844,14 +4842,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
{
StackHandleScope<1> hs(self);
- Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
- pointer_size)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
if (UNLIKELY(return_type.Get() == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
- ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */,
- pointer_size);
+ ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
if (UNLIKELY(other_return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
return false;
@@ -4896,7 +4892,7 @@
StackHandleScope<1> hs(self);
dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
- method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
+ method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
if (UNLIKELY(param_type.Get() == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
@@ -4904,7 +4900,7 @@
}
dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
ObjPtr<mirror::Class> other_param_type =
- method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
+ method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
if (UNLIKELY(other_param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method2, i, other_param_type_idx);
@@ -4940,9 +4936,11 @@
auto* m = klass->GetVTableEntry(i, image_pointer_size_);
auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
- klass, super_klass,
- m, super_m))) {
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+ klass,
+ super_klass,
+ m,
+ super_m))) {
self->AssertPendingException();
return false;
}
@@ -4958,9 +4956,11 @@
j, image_pointer_size_);
auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
- klass, super_klass,
- m, super_m))) {
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+ klass,
+ super_klass,
+ m,
+ super_m))) {
self->AssertPendingException();
return false;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cad674e..9b98671 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -649,6 +649,10 @@
ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -657,7 +661,7 @@
// jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with
// class unloading.)
const DexFile* dex_file;
- GcRoot<mirror::Class>* resolved_types;
+ ArtMethod** resolved_methods;
};
private:
@@ -744,9 +748,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index df4413d..006476b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3985,9 +3985,7 @@
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
mirror::Class* parameter_type =
- m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
- true /* resolve */,
- kRuntimePointerSize);
+ m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..7ae9f03 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
// image at GetImageLocation(). This is used for testing mismatched
// image checksums in the oat_file_assistant_tests.
std::string GetImageLocation2() const {
- return GetImageDirectory() + "/core-npic.art";
+ return GetImageDirectory() + "/core-interpreter.art";
}
std::string GetDexSrc1() const {
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 9504e8b..16a447b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -608,7 +608,7 @@
return nullptr;
}
Handle<mirror::Class> method_return(hs.NewHandle(
- annotation_method->GetReturnType(true /* resolve */, pointer_size)));
+ annotation_method->GetReturnType(true /* resolve */)));
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return,
@@ -948,9 +948,7 @@
DexFile::AnnotationValue annotation_value;
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- Handle<mirror::Class> return_type(hs.NewHandle(
- method->GetReturnType(true /* resolve */, pointer_size)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type,
DexFile::kAllObjects)) {
return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7d6f866..87046bc 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -241,10 +241,9 @@
*slow_path = true;
return nullptr; // Failure
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
+ mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx);
if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
klass = class_linker->ResolveType(type_idx, method);
*slow_path = true;
if (klass == nullptr) { // Error
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index b17e1a8..25fd727 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -38,98 +38,13 @@
namespace art {
-static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
- if (UNLIKELY(component_count < 0)) {
- ThrowNegativeArraySizeException(component_count);
- return nullptr; // Failure
- }
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
- if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
- klass = class_linker->ResolveType(type_idx, referrer);
- if (klass == nullptr) { // Error
- DCHECK(self->IsExceptionPending());
- return nullptr; // Failure
- }
- }
- if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
- if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
- ThrowRuntimeException("Bad filled array request for type %s",
- klass->PrettyDescriptor().c_str());
- } else {
- self->ThrowNewExceptionF(
- "Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but 'int'",
- klass->PrettyDescriptor().c_str());
- }
- return nullptr; // Failure
- }
- if (access_check) {
- mirror::Class* referrer_klass = referrer->GetDeclaringClass();
- if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer_klass, klass);
- return nullptr; // Failure
- }
- }
- DCHECK(klass->IsArrayClass()) << klass->PrettyClass();
- return klass;
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
- access_check);
- if (UNLIKELY(klass == nullptr)) {
- return nullptr;
- }
- // Always go slow path for now, filled new array is not common.
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
- // the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<false>(self, klass, component_count,
- klass->GetComponentSizeShift(),
- heap->GetCurrentAllocator());
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(
- dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
- access_check);
- if (UNLIKELY(klass == nullptr)) {
- return nullptr;
- }
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
- // the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<true>(self, klass, component_count,
- klass->GetComponentSizeShift(),
- heap->GetCurrentAllocator());
-}
-
void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
if (o.Get() == nullptr) {
return;
}
// Make sure that the result is an instance of the type this method was expected to return.
ArtMethod* method = self->GetCurrentMethod(nullptr);
- mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize);
+ mirror::Class* return_type = method->GetReturnType(true /* resolve */);
if (!o->InstanceOf(return_type)) {
Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -192,8 +107,7 @@
ArtMethod* interface_method =
soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
// This can cause thread suspension.
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size);
+ mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index d4cf83c..b427c07 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -102,24 +102,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* method,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
-
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* method,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
-
// Type of find field operation for fast and slow case.
enum FindFieldType {
InstanceObjectRead,
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 2d06508..23a99a0 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -109,46 +109,6 @@
self, \
allocator_type); \
} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
- uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- ScopedQuickEntrypointChecks sqec(self); \
- if (!(instrumented_bool)) { \
- return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- false, \
- allocator_type); \
- } else { \
- return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- false, \
- allocator_type); \
- } \
-} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
- uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- ScopedQuickEntrypointChecks sqec(self); \
- if (!(instrumented_bool)) { \
- return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- true, \
- allocator_type); \
- } else { \
- return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- true, \
- allocator_type); \
- } \
-} \
extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \
Thread* self) \
@@ -219,8 +179,6 @@
qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \
qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \
qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \
qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \
@@ -231,8 +189,6 @@
qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \
qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \
qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \
qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8ce61c1..6481b97 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -66,10 +66,7 @@
qpoints->pGetObjStatic = art_quick_get_obj_static;
// Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
qpoints->pJniMethodStart = JniMethodStart;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 4d5d6de..0e75e94 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -26,8 +26,6 @@
V(AllocObjectResolved, void*, mirror::Class*) \
V(AllocObjectInitialized, void*, mirror::Class*) \
V(AllocObjectWithChecks, void*, mirror::Class*) \
- V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \
- V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
V(AllocStringFromChars, void*, int32_t, int32_t, void*) \
V(AllocStringFromString, void*, void*) \
@@ -65,10 +63,7 @@
V(GetObjInstance, void*, uint32_t, void*) \
V(GetObjStatic, void*, uint32_t) \
\
- V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
- V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
- V(HandleFillArrayData, void, void*, void*) \
\
V(JniMethodStart, uint32_t, Thread*) \
V(JniMethodFastStart, uint32_t, Thread*) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 6301f93..b9caf0f 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -161,12 +161,8 @@
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
- sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
- pAllocStringFromBytes, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString,
@@ -205,13 +201,8 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck,
- sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck,
- pAputObjectWithBoundCheck, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized,
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7b86339..6044053 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -846,7 +846,7 @@
// Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
// Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
{
- ReaderMutexLock mu0(self, *Locks::mutator_lock_);
+ ScopedObjectAccess soa(self);
MutexLock mu1(self, *Locks::thread_list_lock_);
for (Thread* thread : thread_list->GetList()) {
uint32_t tid = thread->GetThreadId();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ca26207..76777d9 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -751,16 +751,14 @@
case 'L': {
ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
if (do_assignability_check && o != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
- ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
- pointer_size);
+ ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
if (arg_type == nullptr) {
StackHandleScope<1> hs(self);
// Preserve o since it is used below and GetClassFromTypeIndex may cause thread
// suspension.
HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
- arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size);
+ arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d7dfcd4..a77a3fc 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -285,9 +285,7 @@
const size_t ref_idx = inst->VRegA_11x(inst_data);
ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */,
- pointer_size);
+ ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 5a3fafa..0148a1c 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -29,6 +29,7 @@
#include "mirror/object-inl.h"
#include "nth_caller_visitor.h"
#include "thread-inl.h"
+#include "thread_list.h"
namespace art {
@@ -37,6 +38,8 @@
static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
+const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
+
// Checking "locals" requires the mutator lock, but at creation time we're really only interested
// in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
// with NO_THREAD_SAFETY_ANALYSIS.
@@ -78,10 +81,10 @@
runtime_deleted(false),
critical(0),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
- functions = unchecked_functions = GetJniNativeInterface();
- if (vm->IsCheckJniEnabled()) {
- SetCheckJniEnabled(true);
- }
+ MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+ check_jni = vm->IsCheckJniEnabled();
+ functions = GetFunctionTable(check_jni);
+ unchecked_functions = GetJniNativeInterface();
}
void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
@@ -107,7 +110,12 @@
void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
check_jni = enabled;
- functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+ MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+ functions = GetFunctionTable(enabled);
+ // Check whether this is a no-op because of override.
+ if (enabled && JNIEnvExt::table_override_ != nullptr) {
+ LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional.";
+ }
}
void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
@@ -269,4 +277,33 @@
}
}
+static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
+ REQUIRES(Locks::jni_function_table_lock_) {
+ JNIEnvExt* env = thread->GetJniEnv();
+ bool check_jni = env->check_jni;
+ env->functions = JNIEnvExt::GetFunctionTable(check_jni);
+}
+
+void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_);
+
+ JNIEnvExt::table_override_ = table_override;
+
+ // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install
+ // code), as we'd have to recursively lock the mutex.
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr);
+ }
+}
+
+const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
+ const JNINativeInterface* override = JNIEnvExt::table_override_;
+ if (override != nullptr) {
+ return override;
+ }
+ return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 5cca0ae..4004c45 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,7 +43,7 @@
void DumpReferenceTables(std::ostream& os)
REQUIRES_SHARED(Locks::mutator_lock_);
- void SetCheckJniEnabled(bool enabled);
+ void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_);
void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_);
void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -104,10 +104,27 @@
// Set the functions to the runtime shutdown functions.
void SetFunctionsToRuntimeShutdownFunctions();
+ // Set the function table override. This will install the override (or original table, if null)
+ // to all threads.
+ // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI.
+ // After overriding the JNI function table, CheckJNI toggling is ignored.
+ static void SetTableOverride(const JNINativeInterface* table_override)
+ REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
+
+ // Return either the regular, or the CheckJNI function table. Will return table_override_ instead
+ // if it is not null.
+ static const JNINativeInterface* GetFunctionTable(bool check_jni)
+ REQUIRES(Locks::jni_function_table_lock_);
+
private:
+ // Override of function tables. This applies to both default as well as instrumented (CheckJNI)
+ // function tables.
+ static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
+
// The constructor should not be called directly. It may leave the object in an erroneous state,
// and the result needs to be checked.
- JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg);
+ JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+ REQUIRES(!Locks::jni_function_table_lock_);
// All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
// to ensure that only monitors locked in this native frame are being unlocked, and that at
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4da5e23..08d1eeb 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2346,4 +2346,39 @@
EXPECT_EQ(segment_state_now, segment_state_computed);
}
+static size_t gGlobalRefCount = 0;
+static const JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+ ++gGlobalRefCount;
+ return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+// Test the table override.
+TEST_F(JniInternalTest, JNIEnvExtTableOverride) {
+ JNINativeInterface env_override;
+ memcpy(&env_override, env_->functions, sizeof(JNINativeInterface));
+
+ gOriginalEnv = env_->functions;
+ env_override.NewGlobalRef = CountNewGlobalRef;
+ gGlobalRefCount = 0;
+
+ jclass local = env_->FindClass("java/lang/Object");
+ ASSERT_TRUE(local != nullptr);
+
+ // Set the table, add a global ref, see whether the counter increases.
+ JNIEnvExt::SetTableOverride(&env_override);
+
+ jobject global = env_->NewGlobalRef(local);
+ EXPECT_EQ(1u, gGlobalRefCount);
+ env_->DeleteGlobalRef(global);
+
+ // Reset
+ JNIEnvExt::SetTableOverride(nullptr);
+
+ jobject global2 = env_->NewGlobalRef(local);
+ EXPECT_EQ(1u, gGlobalRefCount);
+ env_->DeleteGlobalRef(global2);
+}
+
} // namespace art
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a6f56ae..6a4ec9d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -306,23 +306,6 @@
}
-TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
- // pretend we are trying to call 'new char[3]' from String.toCharArray
- ScopedObjectAccess soa(Thread::Current());
- Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
- ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize);
- const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
- ASSERT_TRUE(type_id != nullptr);
- dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
- Object* array = CheckAndAllocArrayFromCodeInstrumented(
- type_idx, 3, sort, Thread::Current(), false,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
- EXPECT_TRUE(array->IsArrayInstance());
- EXPECT_EQ(3, array->AsArray()->GetLength());
- EXPECT_TRUE(array->GetClass()->IsArrayClass());
- EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive());
-}
-
TEST_F(ObjectTest, CreateMultiArray) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 1a07cdc..d14b399 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -473,6 +473,10 @@
return IsKeyEnabled(OatHeader::kDebuggableKey);
}
+bool OatHeader::IsConcurrentCopying() const {
+ return IsKeyEnabled(OatHeader::kConcurrentCopying);
+}
+
bool OatHeader::IsNativeDebuggable() const {
return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
}
diff --git a/runtime/oat.h b/runtime/oat.h
index ab03252..f9396ae 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '9', '8', '\0' }; // art::Thread fields reorder
+ static constexpr uint8_t kOatVersion[] = { '0', '9', '9', '\0' }; // Remove old entrypoints
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -43,6 +43,7 @@
static constexpr const char* kCompilerFilter = "compiler-filter";
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPathKey = "bootclasspath";
+ static constexpr const char* kConcurrentCopying = "concurrent-copying";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
@@ -112,6 +113,7 @@
bool IsDebuggable() const;
bool IsNativeDebuggable() const;
CompilerFilter::Filter GetCompilerFilter() const;
+ bool IsConcurrentCopying() const;
private:
bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f12a5e7..8554fa2 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -304,6 +304,13 @@
}
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+ // Verify the ART_USE_READ_BARRIER state.
+ const bool is_cc = file.GetOatHeader().IsConcurrentCopying();
+ constexpr bool kRuntimeIsCC = kUseReadBarrier;
+ if (is_cc != kRuntimeIsCC) {
+ return kOatCannotOpen;
+ }
+
// Verify the dex checksum.
// Note: GetOatDexFile will return null if the dex checksum doesn't match
// what we provide, which verifies the primary dex checksum for us.
@@ -903,4 +910,3 @@
return std::unique_ptr<OatFile>();
}
} // namespace art
-
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index af027f6..d5c6520 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -23,10 +23,12 @@
"ti_class.cc",
"ti_field.cc",
"ti_heap.cc",
+ "ti_jni.cc",
"ti_method.cc",
"ti_monitor.cc",
"ti_object.cc",
"ti_properties.cc",
+ "ti_search.cc",
"ti_stack.cc",
"ti_redefine.cc",
"ti_thread.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index d9aea01..90467db 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -51,11 +51,13 @@
#include "ti_class.h"
#include "ti_field.h"
#include "ti_heap.h"
+#include "ti_jni.h"
#include "ti_method.h"
#include "ti_monitor.h"
#include "ti_object.h"
#include "ti_properties.h"
#include "ti_redefine.h"
+#include "ti_search.h"
#include "ti_stack.h"
#include "ti_thread.h"
#include "ti_threadgroup.h"
@@ -801,11 +803,11 @@
}
static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
- return ERR(NOT_IMPLEMENTED);
+ return JNIUtil::SetJNIFunctionTable(env, function_table);
}
static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
- return ERR(NOT_IMPLEMENTED);
+ return JNIUtil::GetJNIFunctionTable(env, function_table);
}
// TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching
@@ -856,7 +858,8 @@
}
}
- return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode);
+ ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+ return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode);
}
static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) {
@@ -902,11 +905,15 @@
ENSURE_NON_NULL(capabilities_ptr);
ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env);
jvmtiError ret = OK;
+ jvmtiCapabilities changed;
#define ADD_CAPABILITY(e) \
do { \
if (capabilities_ptr->e == 1) { \
if (kPotentialCapabilities.e == 1) { \
- art_env->capabilities.e = 1;\
+ if (art_env->capabilities.e != 1) { \
+ art_env->capabilities.e = 1; \
+ changed.e = 1; \
+ }\
} else { \
ret = ERR(NOT_AVAILABLE); \
} \
@@ -955,6 +962,9 @@
ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events);
ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events);
#undef ADD_CAPABILITY
+ gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ changed,
+ /*added*/true);
return ret;
}
@@ -963,10 +973,14 @@
ENSURE_VALID_ENV(env);
ENSURE_NON_NULL(capabilities_ptr);
ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env);
+ jvmtiCapabilities changed;
#define DEL_CAPABILITY(e) \
do { \
if (capabilities_ptr->e == 1) { \
- art_env->capabilities.e = 0;\
+ if (art_env->capabilities.e == 1) { \
+ art_env->capabilities.e = 0;\
+ changed.e = 1; \
+ } \
} \
} while (false)
@@ -1012,6 +1026,9 @@
DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events);
DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events);
#undef DEL_CAPABILITY
+ gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ changed,
+ /*added*/false);
return OK;
}
@@ -1052,11 +1069,11 @@
}
static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
}
static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
}
static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
@@ -1231,7 +1248,12 @@
}
static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ // Report BCI as jlocation format. We report dex bytecode indices.
+ if (format_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
+ return ERR(NONE);
}
// TODO Remove this once events are working.
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index d027201..1e07bc6 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -17,14 +17,28 @@
#ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
#define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
+#include <array>
+
#include "events.h"
#include "art_jvmti.h"
namespace openjdkjvmti {
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
+ if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
+ if (env->capabilities.can_retransform_classes) {
+ return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ } else {
+ return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+ }
+ } else {
+ return static_cast<ArtJvmtiEvent>(e);
+ }
+}
+
template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) {
+ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
if (env->event_callbacks == nullptr) {
return nullptr;
}
@@ -33,84 +47,80 @@
// function.
switch (event) {
- case JVMTI_EVENT_VM_INIT:
+ case ArtJvmtiEvent::kVmInit:
return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
- case JVMTI_EVENT_VM_DEATH:
+ case ArtJvmtiEvent::kVmDeath:
return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
- case JVMTI_EVENT_THREAD_START:
+ case ArtJvmtiEvent::kThreadStart:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
- case JVMTI_EVENT_THREAD_END:
+ case ArtJvmtiEvent::kThreadEnd:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
- case JVMTI_EVENT_CLASS_LOAD:
+ case ArtJvmtiEvent::kClassLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
- case JVMTI_EVENT_CLASS_PREPARE:
+ case ArtJvmtiEvent::kClassPrepare:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
- case JVMTI_EVENT_VM_START:
+ case ArtJvmtiEvent::kVmStart:
return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
- case JVMTI_EVENT_EXCEPTION:
+ case ArtJvmtiEvent::kException:
return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
- case JVMTI_EVENT_EXCEPTION_CATCH:
+ case ArtJvmtiEvent::kExceptionCatch:
return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
- case JVMTI_EVENT_SINGLE_STEP:
+ case ArtJvmtiEvent::kSingleStep:
return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
- case JVMTI_EVENT_FRAME_POP:
+ case ArtJvmtiEvent::kFramePop:
return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
- case JVMTI_EVENT_BREAKPOINT:
+ case ArtJvmtiEvent::kBreakpoint:
return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
- case JVMTI_EVENT_FIELD_ACCESS:
+ case ArtJvmtiEvent::kFieldAccess:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
- case JVMTI_EVENT_FIELD_MODIFICATION:
+ case ArtJvmtiEvent::kFieldModification:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
- case JVMTI_EVENT_METHOD_ENTRY:
+ case ArtJvmtiEvent::kMethodEntry:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
- case JVMTI_EVENT_METHOD_EXIT:
+ case ArtJvmtiEvent::kMethodExit:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
- case JVMTI_EVENT_NATIVE_METHOD_BIND:
+ case ArtJvmtiEvent::kNativeMethodBind:
return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
- case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+ case ArtJvmtiEvent::kCompiledMethodLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
- case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
- case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+ case ArtJvmtiEvent::kDynamicCodeGenerated:
return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
- case JVMTI_EVENT_DATA_DUMP_REQUEST:
+ case ArtJvmtiEvent::kDataDumpRequest:
return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
- case JVMTI_EVENT_MONITOR_WAIT:
+ case ArtJvmtiEvent::kMonitorWait:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
- case JVMTI_EVENT_MONITOR_WAITED:
+ case ArtJvmtiEvent::kMonitorWaited:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+ case ArtJvmtiEvent::kMonitorContendedEnter:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+ case ArtJvmtiEvent::kMonitorContendedEntered:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
- case JVMTI_EVENT_RESOURCE_EXHAUSTED:
+ case ArtJvmtiEvent::kResourceExhausted:
return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
- case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+ case ArtJvmtiEvent::kGarbageCollectionStart:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
- case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
- case JVMTI_EVENT_OBJECT_FREE:
+ case ArtJvmtiEvent::kObjectFree:
return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
- case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ case ArtJvmtiEvent::kVmObjectAlloc:
return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
}
return nullptr;
}
template <typename ...Args>
-inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) {
+inline void EventHandler::DispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
using FnType = void(jvmtiEnv*, Args...);
for (ArtJvmTiEnv* env : envs) {
- bool dispatch = env->event_masks.global_event_mask.Test(event);
-
- if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
- EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
- dispatch = mask != nullptr && mask->Test(event);
- }
-
- if (dispatch) {
+ if (ShouldDispatch(event, env, thread)) {
FnType* callback = GetCallback<FnType>(env, event);
if (callback != nullptr) {
(*callback)(env, args...);
@@ -119,6 +129,52 @@
}
}
+inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
+ ArtJvmTiEnv* env,
+ art::Thread* thread) {
+ bool dispatch = env->event_masks.global_event_mask.Test(event);
+
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+ EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+ dispatch = mask != nullptr && mask->Test(event);
+ }
+ return dispatch;
+}
+
+inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+ bool union_value = false;
+ for (const ArtJvmTiEnv* stored_env : envs) {
+ union_value |= stored_env->event_masks.global_event_mask.Test(event);
+ union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
+ if (union_value) {
+ break;
+ }
+ }
+ global_mask.Set(event, union_value);
+}
+
+inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ return caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event);
+}
+
+inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added) {
+ if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
+ env->event_masks.HandleChangedCapabilities(caps, added);
+ if (caps.can_retransform_classes == 1) {
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
+ RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ }
+ }
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 12692a1..f38aa86 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -47,6 +47,10 @@
namespace openjdkjvmti {
+bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) {
+ return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event);
+}
+
EventMask& EventMasks::GetEventMask(art::Thread* thread) {
if (thread == nullptr) {
return global_event_mask;
@@ -83,7 +87,7 @@
}
-void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) {
DCHECK(EventMask::EventIsInRange(event));
GetEventMask(thread).Set(event);
if (thread != nullptr) {
@@ -91,7 +95,7 @@
}
}
-void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) {
DCHECK(EventMask::EventIsInRange(event));
GetEventMask(thread).Set(event, false);
if (thread != nullptr) {
@@ -107,20 +111,49 @@
}
}
+void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) {
+ if (UNLIKELY(caps.can_retransform_classes == 1)) {
+ // If we are giving this env the retransform classes cap we need to switch all events of
+ // NonTransformable to Transformable and vice versa.
+ ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+ ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+ : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+ if (global_event_mask.Test(to_remove)) {
+ CHECK(!global_event_mask.Test(to_add));
+ global_event_mask.Set(to_remove, false);
+ global_event_mask.Set(to_add, true);
+ }
+
+ if (unioned_thread_event_mask.Test(to_remove)) {
+ CHECK(!unioned_thread_event_mask.Test(to_add));
+ unioned_thread_event_mask.Set(to_remove, false);
+ unioned_thread_event_mask.Set(to_add, true);
+ }
+ for (auto thread_mask : thread_event_masks) {
+ if (thread_mask.second.Test(to_remove)) {
+ CHECK(!thread_mask.second.Test(to_add));
+ thread_mask.second.Set(to_remove, false);
+ thread_mask.second.Set(to_add, true);
+ }
+ }
+ }
+}
+
void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
envs.push_back(env);
}
-static bool IsThreadControllable(jvmtiEvent event) {
+static bool IsThreadControllable(ArtJvmtiEvent event) {
switch (event) {
- case JVMTI_EVENT_VM_INIT:
- case JVMTI_EVENT_VM_START:
- case JVMTI_EVENT_VM_DEATH:
- case JVMTI_EVENT_THREAD_START:
- case JVMTI_EVENT_COMPILED_METHOD_LOAD:
- case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
- case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
- case JVMTI_EVENT_DATA_DUMP_REQUEST:
+ case ArtJvmtiEvent::kVmInit:
+ case ArtJvmtiEvent::kVmStart:
+ case ArtJvmtiEvent::kVmDeath:
+ case ArtJvmtiEvent::kThreadStart:
+ case ArtJvmtiEvent::kCompiledMethodLoad:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
+ case ArtJvmtiEvent::kDynamicCodeGenerated:
+ case ArtJvmtiEvent::kDataDumpRequest:
return false;
default:
@@ -136,7 +169,7 @@
OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
- if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) {
art::StackHandleScope<1> hs(self);
auto h = hs.NewHandleWrapper(obj);
// jvmtiEventVMObjectAlloc parameters:
@@ -162,7 +195,7 @@
jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
handler_->DispatchEvent(self,
- JVMTI_EVENT_VM_OBJECT_ALLOC,
+ ArtJvmtiEvent::kVmObjectAlloc,
jni_env,
thread.get(),
object.get(),
@@ -196,11 +229,11 @@
finish_enabled_(false) {}
void StartPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+ handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
}
void EndPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+ handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
}
bool IsEnabled() {
@@ -221,10 +254,10 @@
bool finish_enabled_;
};
-static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) {
+static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) {
bool old_state = listener->IsEnabled();
- if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) {
+ if (event == ArtJvmtiEvent::kGarbageCollectionStart) {
listener->SetStartEnabled(enable);
} else {
listener->SetFinishEnabled(enable);
@@ -242,14 +275,14 @@
}
// Handle special work for the given event type, if necessary.
-void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
+void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
- case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ case ArtJvmtiEvent::kVmObjectAlloc:
SetupObjectAllocationTracking(alloc_listener_.get(), enable);
return;
- case JVMTI_EVENT_GARBAGE_COLLECTION_START:
- case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ case ArtJvmtiEvent::kGarbageCollectionStart:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
@@ -260,7 +293,7 @@
jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
art::Thread* thread,
- jvmtiEvent event,
+ ArtJvmtiEvent event,
jvmtiEventMode mode) {
if (thread != nullptr) {
art::ThreadState state = thread->GetState();
@@ -293,17 +326,7 @@
DCHECK_EQ(mode, JVMTI_DISABLE);
env->event_masks.DisableEvent(thread, event);
-
- // Gotta recompute the global mask.
- bool union_value = false;
- for (const ArtJvmTiEnv* stored_env : envs) {
- union_value |= stored_env->event_masks.global_event_mask.Test(event);
- union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
- if (union_value) {
- break;
- }
- }
- global_mask.Set(event, union_value);
+ RecalculateGlobalEventMask(event);
}
bool new_state = global_mask.Test(event);
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 07d6bfd..7990141 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,22 +30,76 @@
class JvmtiAllocationListener;
class JvmtiGcPauseListener;
+// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
+// retransformation capable and incapable loading
+enum class ArtJvmtiEvent {
+ kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
+ kVmInit = JVMTI_EVENT_VM_INIT,
+ kVmDeath = JVMTI_EVENT_VM_DEATH,
+ kThreadStart = JVMTI_EVENT_THREAD_START,
+ kThreadEnd = JVMTI_EVENT_THREAD_END,
+ kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ kClassLoad = JVMTI_EVENT_CLASS_LOAD,
+ kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
+ kVmStart = JVMTI_EVENT_VM_START,
+ kException = JVMTI_EVENT_EXCEPTION,
+ kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH,
+ kSingleStep = JVMTI_EVENT_SINGLE_STEP,
+ kFramePop = JVMTI_EVENT_FRAME_POP,
+ kBreakpoint = JVMTI_EVENT_BREAKPOINT,
+ kFieldAccess = JVMTI_EVENT_FIELD_ACCESS,
+ kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION,
+ kMethodEntry = JVMTI_EVENT_METHOD_ENTRY,
+ kMethodExit = JVMTI_EVENT_METHOD_EXIT,
+ kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND,
+ kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD,
+ kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
+ kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
+ kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST,
+ kMonitorWait = JVMTI_EVENT_MONITOR_WAIT,
+ kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED,
+ kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
+ kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
+ kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED,
+ kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START,
+ kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
+ kObjectFree = JVMTI_EVENT_OBJECT_FREE,
+ kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
+ kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
+ kMaxEventTypeVal = kClassFileLoadHookRetransformable,
+};
+
+// Convert a jvmtiEvent into a ArtJvmtiEvent
+ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
+
+static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+ if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) {
+ return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK;
+ } else {
+ return static_cast<jvmtiEvent>(e);
+ }
+}
+
struct EventMask {
- static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
+ static constexpr size_t kEventsSize =
+ static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) -
+ static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1;
std::bitset<kEventsSize> bit_set;
- static bool EventIsInRange(jvmtiEvent event) {
- return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL;
+ static bool EventIsInRange(ArtJvmtiEvent event) {
+ return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal;
}
- void Set(jvmtiEvent event, bool value = true) {
+ void Set(ArtJvmtiEvent event, bool value = true) {
DCHECK(EventIsInRange(event));
- bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value);
+ bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal),
+ value);
}
- bool Test(jvmtiEvent event) const {
+ bool Test(ArtJvmtiEvent event) const {
DCHECK(EventIsInRange(event));
- return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL);
+ return bit_set.test(
+ static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal));
}
};
@@ -68,8 +122,13 @@
EventMask& GetEventMask(art::Thread* thread);
EventMask* GetEventMaskOrNull(art::Thread* thread);
- void EnableEvent(art::Thread* thread, jvmtiEvent event);
- void DisableEvent(art::Thread* thread, jvmtiEvent event);
+ void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
+ void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
+ bool IsEnabledAnywhere(ArtJvmtiEvent event);
+ // Make any changes to event masks needed for the given capability changes. If caps_added is true
+ // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the
+ // set of all capabilities that were removed from the jvmtiEnv.
+ void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added);
};
// Helper class for event handling.
@@ -82,20 +141,44 @@
// enabled, yet.
void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
- bool IsEventEnabledAnywhere(jvmtiEvent event) {
+ bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
return false;
}
return global_mask.Test(event);
}
- jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode);
+ jvmtiError SetEvent(ArtJvmTiEnv* env,
+ art::Thread* thread,
+ ArtJvmtiEvent event,
+ jvmtiEventMode mode);
template <typename ...Args>
- ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args);
+ ALWAYS_INLINE
+ inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
+
+ // Tell the event handler capabilities were added/lost so it can adjust the sent events.If
+ // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
+ // then caps is the set of all capabilities that were removed from the jvmtiEnv.
+ ALWAYS_INLINE
+ inline void HandleChangedCapabilities(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added);
private:
- void HandleEventType(jvmtiEvent event, bool enable);
+ ALWAYS_INLINE
+ static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+
+ ALWAYS_INLINE
+ inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
+ const jvmtiCapabilities& caps,
+ bool added);
+
+ // Recalculates the event mask for the given event.
+ ALWAYS_INLINE
+ inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+
+ void HandleEventType(ArtJvmtiEvent event, bool enable);
// List of all JvmTiEnv objects that have been created, in their creation order.
std::vector<ArtJvmTiEnv*> envs;
diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h
index ee708cb..de07c16 100644
--- a/runtime/openjdkjvmti/jvmti.h
+++ b/runtime/openjdkjvmti/jvmti.h
@@ -74,7 +74,7 @@
typedef jlong jlocation;
struct _jrawMonitorID;
typedef struct _jrawMonitorID *jrawMonitorID;
-typedef struct JNINativeInterface_ jniNativeInterface;
+typedef struct JNINativeInterface jniNativeInterface;
/* Constants */
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index b983e79..94cb46a 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -177,7 +177,7 @@
}
void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
- if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) {
SweepImpl<true>(visitor);
} else {
SweepImpl<false>(visitor);
@@ -207,7 +207,7 @@
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+ event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
}
template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc
new file mode 100644
index 0000000..88f0395
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.cc
@@ -0,0 +1,91 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_jni.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/mutex.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const jniNativeInterface* function_table) {
+ // While we supporting setting null (which will reset the table), the spec says no.
+ if (function_table == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::JNIEnvExt::SetTableOverride(function_table);
+ return ERR(NONE);
+}
+
+jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
+ if (function_table == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as
+ // this has to work in the start phase.
+
+ // Figure out which table is current. Conservatively assume check-jni is off.
+ bool check_jni = false;
+ art::Runtime* runtime = art::Runtime::Current();
+ if (runtime != nullptr && runtime->GetJavaVM() != nullptr) {
+ check_jni = runtime->GetJavaVM()->IsCheckJniEnabled();
+ }
+
+ // Get that table.
+ const JNINativeInterface* current_table;
+ {
+ art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_);
+ current_table = art::JNIEnvExt::GetFunctionTable(check_jni);
+ }
+
+ // Allocate memory and copy the table.
+ unsigned char* data;
+ jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data);
+ if (data_result != ERR(NONE)) {
+ return data_result;
+ }
+ memcpy(data, current_table, sizeof(JNINativeInterface));
+
+ *function_table = reinterpret_cast<JNINativeInterface*>(data);
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h
new file mode 100644
index 0000000..906aab0
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/
+// CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI,
+// CheckJNI will not be working (as the agent will forward to the non-CheckJNI table).
+//
+// This behavior results from our usage of the function table to avoid a check of the
+// CheckJNI flag. A future implementation may install on loading of this plugin an
+// intermediate function table that explicitly checks the flag, so that switching CheckJNI
+// is transparently handled.
+
+class JNIUtil {
+ public:
+ static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table);
+
+ static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
new file mode 100644
index 0000000..913d2b6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_search.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ if (current->GetClassLinker() == nullptr) {
+ // TODO: Support boot classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ std::string error_msg;
+ std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+ if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+ LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ jobject sys_class_loader = current->GetSystemClassLoader();
+ if (sys_class_loader == nullptr) {
+ // TODO: Support classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+
+ // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+ // exceptions are swallowed.
+
+ art::Thread* self = art::Thread::Current();
+ JNIEnv* env = self->GetJniEnv();
+ if (!env->IsInstanceOf(sys_class_loader,
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ return ERR(INTERNAL);
+ }
+
+ jmethodID add_dex_path_id = env->GetMethodID(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+ "addDexPath",
+ "(Ljava/lang/String;)V");
+ if (add_dex_path_id == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+ if (dex_path.get() == nullptr) {
+ return ERR(INTERNAL);
+ }
+ env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
new file mode 100644
index 0000000..6a52e80
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class SearchUtil {
+ public:
+ static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+ static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4d24501..75176f9 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -227,11 +227,11 @@
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
ObjPtr<mirror::Object> arg(args->Get(args_offset));
if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ // Note: The method's parameter's type must have been previously resolved.
ObjPtr<mirror::Class> dst_class(
m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
- true /* resolve */,
- pointer_size));
+ false /* resolve */));
+ DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i;
if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
@@ -363,12 +363,9 @@
}
// TODO: If args contain object references, it may cause problems.
Thread* const self = Thread::Current();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (uint32_t i = 0; i < num_params; i++) {
dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
- ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
- true /* resolve*/,
- pointer_size));
+ ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 40b6d73..016a379 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2632,8 +2632,6 @@
QUICK_ENTRY_POINT_INFO(pAllocObjectResolved)
QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized)
QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks)
- QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
- QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
QUICK_ENTRY_POINT_INFO(pAllocStringFromChars)
QUICK_ENTRY_POINT_INFO(pAllocStringFromString)
@@ -2667,10 +2665,7 @@
QUICK_ENTRY_POINT_INFO(pGet64Static)
QUICK_ENTRY_POINT_INFO(pGetObjInstance)
QUICK_ENTRY_POINT_INFO(pGetObjStatic)
- QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck)
- QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck)
QUICK_ENTRY_POINT_INFO(pAputObject)
- QUICK_ENTRY_POINT_INFO(pHandleFillArrayData)
QUICK_ENTRY_POINT_INFO(pJniMethodStart)
QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized)
QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 25a179b..b915457 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2901,9 +2901,7 @@
ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
const RegType* return_type = nullptr;
if (called_method != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
return_type_class,
@@ -2946,9 +2944,7 @@
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type = &FromClass(return_type_descriptor,
return_type_class,
@@ -5133,9 +5129,7 @@
const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
if (mirror_method_ != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
return_type_class,
diff --git a/test/928-jni-table/build b/test/928-jni-table/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/928-jni-table/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/928-jni-table/expected.txt b/test/928-jni-table/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/928-jni-table/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/928-jni-table/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
new file mode 100644
index 0000000..5123d3a
--- /dev/null
+++ b/test/928-jni-table/jni_table.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test927JNITable {
+
+// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride.
+
+static size_t gGlobalRefCount = 0;
+static JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+ ++gGlobalRefCount;
+ return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
+ JNIEnv* env, jclass klass) {
+ // Get the current table, as the delegate.
+ jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
+ if (JvmtiErrorToException(env, getorig_result)) {
+ return;
+ }
+
+ // Get the current table, as the override we'll install.
+ JNINativeInterface* env_override;
+ jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
+ if (JvmtiErrorToException(env, getoverride_result)) {
+ return;
+ }
+
+ env_override->NewGlobalRef = CountNewGlobalRef;
+ gGlobalRefCount = 0;
+
+ // Install the override.
+ jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
+ if (JvmtiErrorToException(env, setoverride_result)) {
+ return;
+ }
+
+ jobject global = env->NewGlobalRef(klass);
+ CHECK_EQ(1u, gGlobalRefCount);
+ env->DeleteGlobalRef(global);
+
+ // Install the "original." There is no real reset.
+ jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
+ if (JvmtiErrorToException(env, setoverride2_result)) {
+ return;
+ }
+
+ jobject global2 = env->NewGlobalRef(klass);
+ CHECK_EQ(1u, gGlobalRefCount);
+ env->DeleteGlobalRef(global2);
+
+ // Try to install null. Should return NULL_POINTER error.
+ jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr);
+ if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) {
+ LOG(FATAL) << "Didn't receive NULL_POINTER";
+ }
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override));
+}
+
+} // namespace Test927JNITable
+} // namespace art
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/928-jni-table/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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 "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java
new file mode 100644
index 0000000..b0baea1
--- /dev/null
+++ b/test/928-jni-table/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doJNITableTest();
+
+ System.out.println("Done");
+ }
+
+ public static native void doJNITableTest();
+}
diff --git a/test/929-search/build b/test/929-search/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/929-search/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/929-search/expected.txt b/test/929-search/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/929-search/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/929-search/info.txt b/test/929-search/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/929-search/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/929-search/run b/test/929-search/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/929-search/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti \
+ --no-app-image
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
new file mode 100644
index 0000000..d1c6984
--- /dev/null
+++ b/test/929-search/search.cc
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test929Search {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+} // namespace Test929Search
+} // namespace art
diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/929-search/src-ex/A.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class A {
+}
\ No newline at end of file
diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/929-search/src/B.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class B {
+}
\ No newline at end of file
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
new file mode 100644
index 0000000..d253e6f
--- /dev/null
+++ b/test/929-search/src/Main.java
@@ -0,0 +1,54 @@
+/*
+ * 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.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ private static void doTest() throws Exception {
+ doTest(true, DEX1, "B");
+ doTest(false, DEX2, "A");
+ System.out.println("Done");
+ }
+
+ private static void doTest(boolean boot, String segment, String className) throws Exception {
+ ClassLoader expectedClassLoader;
+ if (boot) {
+ expectedClassLoader = Object.class.getClassLoader();
+ addToBootClassLoader(segment);
+ } else {
+ expectedClassLoader = ClassLoader.getSystemClassLoader();
+ addToSystemClassLoader(segment);
+ }
+
+ Class<?> c = Class.forName(className);
+ if (c.getClassLoader() != expectedClassLoader) {
+ throw new RuntimeException(className + "(" + boot + "/" + segment + "): " +
+ c.getClassLoader() + " vs " + expectedClassLoader);
+ }
+ }
+
+ private static native void addToBootClassLoader(String s);
+ private static native void addToSystemClassLoader(String s);
+
+ private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar";
+ private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar";
+}
diff --git a/test/Android.bp b/test/Android.bp
index c551b9d..965d07a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -267,6 +267,8 @@
"924-threads/threads.cc",
"925-threadgroups/threadgroups.cc",
"927-timers/timers.cc",
+ "928-jni-table/jni_table.cc",
+ "929-search/search.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 11d069a..e604c93 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -280,6 +280,7 @@
# These 9** tests are not supported in current form due to linker
# restrictions. See b/31681198
TEST_ART_BROKEN_TARGET_TESTS += \
+ 901-hello-ti-agent \
902-hello-transformation \
903-hello-tagging \
904-object-allocation \
@@ -306,6 +307,8 @@
925-threadgroups \
926-multi-obsolescence \
927-timers \
+ 928-jni-table \
+ 929-search \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -466,10 +469,12 @@
629-vdex-speed
# This test fails without an image.
-# 964 often times out due to the large number of classes it tries to compile.
+# 018, 961, 964 often time out. b/34369284
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
137-cfi \
138-duplicate-classes-check \
+ 018-stack-overflow \
+ 961-default-iface-resolution-gen \
964-default-iface-init
ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))