Merge "Don't need to block in AddWeakGlobalRef and MonitorList::Add under CC."
diff --git a/Android.mk b/Android.mk
index cf3a9e7..f3ab3c1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -341,56 +341,6 @@
endif # art_test_bother
-########################################################################
-# oat-target and oat-target-sync rules
-
-OAT_TARGET_RULES :=
-
-# $(1): input jar or apk target location
-define declare-oat-target-target
-OUT_OAT_FILE := $(PRODUCT_OUT)/$(basename $(1)).odex
-
-ifeq ($(ONE_SHOT_MAKEFILE),)
-# ONE_SHOT_MAKEFILE is empty for a top level build and we don't want
-# to define the oat-target-* rules there because they will conflict
-# with the build/core/dex_preopt.mk defined rules.
-.PHONY: oat-target-$(1)
-oat-target-$(1):
-
-else
-.PHONY: oat-target-$(1)
-oat-target-$(1): $$(OUT_OAT_FILE)
-
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OAT_DEPENDENCY)
- @mkdir -p $$(dir $$@)
- $(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
- --boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) \
- --dex-location=/$(1) --oat-file=$$@ \
- --instruction-set=$(DEX2OAT_TARGET_ARCH) \
- --instruction-set-variant=$(DEX2OAT_TARGET_CPU_VARIANT) \
- --instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
- --android-root=$(PRODUCT_OUT)/system --include-patch-information \
- --runtime-arg -Xnorelocate
-
-endif
-
-OAT_TARGET_RULES += oat-target-$(1)
-endef
-
-$(foreach file,\
- $(filter-out\
- $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(LIBART_TARGET_BOOT_JARS))),\
- $(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\
- $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file)))))
-
-.PHONY: oat-target
-oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_RULES)
-
-.PHONY: oat-target-sync
-oat-target-sync: oat-target
- $(TEST_ART_ADB_ROOT_AND_REMOUNT)
- adb sync
-
####################################################################################################
# Fake packages to ensure generation of libopenjdkd when one builds with mm/mmm/mmma.
#
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bd7f900..5bdfbc7 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -332,7 +332,7 @@
(adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(4) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
valgrind --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
--suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
- --num-callers=50 \
+ --num-callers=50 --show-mismatched-frees=no \
$$(PRIVATE_TARGET_EXE) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
&& (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
&& $$(call ART_TEST_PASSED,$$@)) \
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 402eeee..f00648f 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1378,28 +1378,21 @@
void CodeGenerator::EmitJitRoots(uint8_t* code,
Handle<mirror::ObjectArray<mirror::Object>> roots,
- const uint8_t* roots_data,
- Handle<mirror::DexCache> outer_dex_cache) {
+ const uint8_t* roots_data) {
DCHECK_EQ(static_cast<size_t>(roots->GetLength()), GetNumberOfJitRoots());
- StackHandleScope<1> hs(Thread::Current());
- MutableHandle<mirror::DexCache> h_dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
size_t index = 0;
for (auto& entry : jit_string_roots_) {
- const DexFile& entry_dex_file = *entry.first.dex_file;
- // Avoid the expensive FindDexCache call by checking if the string is
- // in the compiled method's dex file.
- h_dex_cache.Assign(IsSameDexFile(*outer_dex_cache->GetDexFile(), entry_dex_file)
- ? outer_dex_cache.Get()
- : class_linker->FindDexCache(hs.Self(), entry_dex_file));
- mirror::String* string = class_linker->LookupString(
- entry_dex_file, entry.first.string_index, h_dex_cache);
- DCHECK(string != nullptr) << "JIT roots require strings to have been loaded";
+ // Update the `roots` with the string, and replace the address temporarily
+ // stored to the index in the table.
+ uint64_t address = entry.second;
+ roots->Set(index, reinterpret_cast<StackReference<mirror::String>*>(address)->AsMirrorPtr());
+ DCHECK(roots->Get(index) != nullptr);
+ entry.second = index;
// Ensure the string is strongly interned. This is a requirement on how the JIT
// handles strings. b/32995596
- class_linker->GetInternTable()->InternStrong(string);
- roots->Set(index, string);
- entry.second = index;
+ class_linker->GetInternTable()->InternStrong(
+ reinterpret_cast<mirror::String*>(roots->Get(index)));
++index;
}
for (auto& entry : jit_class_roots_) {
@@ -1407,6 +1400,7 @@
// stored to the index in the table.
uint64_t address = entry.second;
roots->Set(index, reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr());
+ DCHECK(roots->Get(index) != nullptr);
entry.second = index;
++index;
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 2e2c3c0..6366b98 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -351,8 +351,7 @@
// Also emits literal patches.
void EmitJitRoots(uint8_t* code,
Handle<mirror::ObjectArray<mirror::Object>> roots,
- const uint8_t* roots_data,
- Handle<mirror::DexCache> outer_dex_cache)
+ const uint8_t* roots_data)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsLeafMethod() const {
@@ -713,9 +712,9 @@
const ArenaVector<HBasicBlock*>* block_order_;
// Maps a StringReference (dex_file, string_index) to the index in the literal table.
- // Entries are intially added with a 0 index, and `EmitJitRoots` will compute all the
- // indices.
- ArenaSafeMap<StringReference, uint32_t, StringReferenceValueComparator> jit_string_roots_;
+ // Entries are intially added with a pointer in the handle zone, and `EmitJitRoots`
+ // will compute all the indices.
+ ArenaSafeMap<StringReference, uint64_t, StringReferenceValueComparator> jit_string_roots_;
// Maps a ClassReference (dex_file, type_index) to the index in the literal table.
// Entries are intially added with a pointer in the handle zone, and `EmitJitRoots`
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 8a7f6d3..3009103 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5937,7 +5937,9 @@
}
}
-void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
@@ -5962,8 +5964,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
return; // No dex cache slow path.
}
@@ -5987,7 +5990,8 @@
}
case HLoadString::LoadKind::kJitTableAddress: {
__ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
- load->GetStringIndex()));
+ load->GetStringIndex(),
+ load->GetString()));
// /* GcRoot<mirror::String> */ out = *out
GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
return;
@@ -7317,8 +7321,10 @@
}
Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file,
- dex::StringIndex string_index) {
- jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+ dex::StringIndex string_index,
+ Handle<mirror::String> handle) {
+ jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
+ reinterpret_cast64<uint64_t>(handle.GetReference()));
return jit_string_patches_.GetOrCreate(
StringReference(&dex_file, string_index),
[this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 6435851..d5968e0 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -489,7 +489,9 @@
dex::StringIndex string_index);
Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
- Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index);
+ Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index,
+ Handle<mirror::String> handle);
Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
dex::TypeIndex type_index,
uint64_t address);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5c33fe1..4b6a9be 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4137,8 +4137,9 @@
}
vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral(
- const DexFile& dex_file, dex::StringIndex string_index) {
- jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+ const DexFile& dex_file, dex::StringIndex string_index, Handle<mirror::String> handle) {
+ jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
+ reinterpret_cast64<uint64_t>(handle.GetReference()));
return jit_string_patches_.GetOrCreate(
StringReference(&dex_file, string_index),
[this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
@@ -4527,7 +4528,9 @@
}
}
-void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Register out = OutputRegister(load);
Location out_loc = load->GetLocations()->Out();
@@ -4550,8 +4553,10 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK(load->GetAddress() != 0u && IsUint<32>(load->GetAddress()));
- __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(load->GetAddress()));
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
+ __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBssEntry: {
@@ -4582,7 +4587,8 @@
}
case HLoadString::LoadKind::kJitTableAddress: {
__ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
- load->GetStringIndex()));
+ load->GetStringIndex(),
+ load->GetString()));
GenerateGcRootFieldLoad(load,
out_loc,
out.X(),
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 8f33b6b..d6a5f9d 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -567,7 +567,8 @@
dex::TypeIndex type_index);
vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address);
vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file,
- dex::StringIndex string_index);
+ dex::StringIndex string_index,
+ Handle<mirror::String> handle);
vixl::aarch64::Literal<uint32_t>* DeduplicateJitClassLiteral(const DexFile& dex_file,
dex::TypeIndex string_index,
uint64_t address);
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 00ad3e3..b1f6d59 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -6022,7 +6022,9 @@
}
}
-void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
vixl32::Register out = OutputRegister(load);
@@ -6042,8 +6044,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
return; // No dex cache slow path.
}
@@ -6063,7 +6066,8 @@
}
case HLoadString::LoadKind::kJitTableAddress: {
__ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
- load->GetStringIndex()));
+ load->GetStringIndex(),
+ load->GetString()));
// /* GcRoot<mirror::String> */ out = *out
GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
return;
@@ -7444,9 +7448,12 @@
return DeduplicateUint32Literal(address, &uint32_literals_);
}
-VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file,
- dex::StringIndex string_index) {
- jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
+ const DexFile& dex_file,
+ dex::StringIndex string_index,
+ Handle<mirror::String> handle) {
+ jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
+ reinterpret_cast64<uint64_t>(handle.GetReference()));
return jit_string_patches_.GetOrCreate(
StringReference(&dex_file, string_index),
[this]() {
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 297d63c..200a463 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -573,7 +573,8 @@
VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
- dex::StringIndex string_index);
+ dex::StringIndex string_index,
+ Handle<mirror::String> handle);
VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
dex::TypeIndex type_index,
uint64_t address);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 01e0dac..9af03e8 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5625,7 +5625,9 @@
}
}
-void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
HLoadString::LoadKind load_kind = load->GetLoadKind();
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
@@ -5660,8 +5662,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ LoadLiteral(out,
base_or_current_method_reg,
codegen_->DeduplicateBootImageAddressLiteral(address));
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 36690c0..046d59c 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3628,7 +3628,9 @@
}
}
-void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
HLoadString::LoadKind load_kind = load->GetLoadKind();
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
@@ -3650,8 +3652,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ LoadLiteral(out,
kLoadUnsignedWord,
codegen_->DeduplicateBootImageAddressLiteral(address));
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0abe855..f13b60a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6232,15 +6232,19 @@
}
Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file,
- dex::StringIndex dex_index) {
- jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u);
+ dex::StringIndex dex_index,
+ Handle<mirror::String> handle) {
+ jit_string_roots_.Overwrite(
+ StringReference(&dex_file, dex_index), reinterpret_cast64<uint64_t>(handle.GetReference()));
// Add a patch entry and return the label.
jit_string_patches_.emplace_back(dex_file, dex_index.index_);
PatchInfo<Label>* info = &jit_string_patches_.back();
return &info->label;
}
-void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
@@ -6258,8 +6262,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ movl(out, Immediate(address));
codegen_->RecordSimplePatch();
return; // No dex cache slow path.
@@ -6280,7 +6285,7 @@
case HLoadString::LoadKind::kJitTableAddress: {
Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset);
Label* fixup_label = codegen_->NewJitRootStringPatch(
- load->GetDexFile(), load->GetStringIndex());
+ load->GetDexFile(), load->GetStringIndex(), load->GetString());
// /* GcRoot<mirror::String> */ out = *address
GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
return;
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 1af6850..dd1628c 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -415,7 +415,9 @@
void RecordTypePatch(HLoadClass* load_class);
Label* NewStringBssEntryPatch(HLoadString* load_string);
Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
- Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index);
+ Label* NewJitRootStringPatch(const DexFile& dex_file,
+ dex::StringIndex dex_index,
+ Handle<mirror::String> handle);
Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address);
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 903844f..89f4ae0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5631,15 +5631,19 @@
}
Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file,
- dex::StringIndex dex_index) {
- jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u);
+ dex::StringIndex dex_index,
+ Handle<mirror::String> handle) {
+ jit_string_roots_.Overwrite(
+ StringReference(&dex_file, dex_index), reinterpret_cast64<uint64_t>(handle.GetReference()));
// Add a patch entry and return the label.
jit_string_patches_.emplace_back(dex_file, dex_index.index_);
PatchInfo<Label>* info = &jit_string_patches_.back();
return &info->label;
}
-void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
@@ -5651,8 +5655,9 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK_NE(load->GetAddress(), 0u);
- uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ uint32_t address = dchecked_integral_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(load->GetString().Get()));
+ DCHECK_NE(address, 0u);
__ movl(out, Immediate(address)); // Zero-extended.
codegen_->RecordSimplePatch();
return; // No dex cache slow path.
@@ -5673,8 +5678,8 @@
case HLoadString::LoadKind::kJitTableAddress: {
Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
/* no_rip */ true);
- Label* fixup_label =
- codegen_->NewJitRootStringPatch(load->GetDexFile(), load->GetStringIndex());
+ Label* fixup_label = codegen_->NewJitRootStringPatch(
+ load->GetDexFile(), load->GetStringIndex(), load->GetString());
// /* GcRoot<mirror::String> */ out = *address
GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
return;
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index f827e79..32d006c 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -412,7 +412,9 @@
void RecordTypePatch(HLoadClass* load_class);
Label* NewStringBssEntryPatch(HLoadString* load_string);
Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
- Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index);
+ Label* NewJitRootStringPatch(const DexFile& dex_file,
+ dex::StringIndex dex_index,
+ Handle<mirror::String> handle);
Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address);
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 879b4ce..e3f3df0 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -15,6 +15,7 @@
*/
#include <functional>
+#include <memory>
#include "arch/instruction_set.h"
#include "arch/arm/instruction_set_features_arm.h"
@@ -299,8 +300,8 @@
bool has_result,
Expected expected) {
CompilerOptions compiler_options;
- CodeGenerator* codegen = target_config.CreateCodeGenerator(graph, compiler_options);
- RunCode(codegen, graph, hook_before_codegen, has_result, expected);
+ std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph, compiler_options));
+ RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
}
#ifdef ART_ENABLE_CODEGEN_arm
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 437d35c..f8d37bd 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -28,7 +28,6 @@
TEST_F(GVNTest, LocalFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- ScopedNullHandle<mirror::DexCache> dex_cache;
HGraph* graph = CreateGraph(&allocator);
HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -45,53 +44,53 @@
entry->AddSuccessor(block);
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* to_remove = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(43),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* different_offset = block->GetLastInstruction();
// Kill the value.
block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* use_after_kill = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HExit());
@@ -113,7 +112,6 @@
TEST_F(GVNTest, GlobalFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- ScopedNullHandle<mirror::DexCache> dex_cache;
HGraph* graph = CreateGraph(&allocator);
HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -129,13 +127,13 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -152,33 +150,33 @@
else_->AddSuccessor(join);
then->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
then->AddInstruction(new (&allocator) HGoto());
else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
else_->AddInstruction(new (&allocator) HGoto());
join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
join->AddInstruction(new (&allocator) HExit());
@@ -196,7 +194,6 @@
TEST_F(GVNTest, LoopFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- ScopedNullHandle<mirror::DexCache> dex_cache;
HGraph* graph = CreateGraph(&allocator);
HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
@@ -213,13 +210,13 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
block->AddInstruction(new (&allocator) HGoto());
@@ -236,13 +233,13 @@
loop_body->AddSuccessor(loop_header);
loop_header->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -251,35 +248,35 @@
// and the body to be GVN'ed.
loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* field_set = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HGoto());
exit->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
HInstruction* field_get_in_exit = exit->GetLastInstruction();
exit->AddInstruction(new (&allocator) HExit());
@@ -319,7 +316,6 @@
TEST_F(GVNTest, LoopSideEffects) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- ScopedNullHandle<mirror::DexCache> dex_cache;
static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
@@ -376,13 +372,13 @@
// Make one block with a side effect.
entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0));
SideEffectsAnalysis side_effects(graph);
@@ -401,13 +397,13 @@
outer_loop_body->InsertInstructionBefore(
new (&allocator) HInstanceFieldSet(parameter,
parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0),
outer_loop_body->GetLastInstruction());
@@ -427,13 +423,13 @@
inner_loop_body->InsertInstructionBefore(
new (&allocator) HInstanceFieldSet(parameter,
parameter,
+ nullptr,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0),
inner_loop_body->GetLastInstruction());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3b83e95..e5d05e9 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -429,13 +429,13 @@
DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
receiver,
+ field,
Primitive::kPrimNot,
field->GetOffset(),
field->IsVolatile(),
field->GetDexFieldIndex(),
field->GetDeclaringClass()->GetDexClassDefIndex(),
*field->GetDexFile(),
- handles_->NewHandle(field->GetDexCache()),
dex_pc);
// The class of a field is effectively final, and does not have any memory dependencies.
result->SetSideEffects(SideEffects::None());
@@ -618,6 +618,9 @@
} else {
one_target_inlined = true;
+ VLOG(compiler) << "Polymorphic call to " << ArtMethod::PrettyMethod(resolved_method)
+ << " has inlined " << ArtMethod::PrettyMethod(method);
+
// If we have inlined all targets before, and this receiver is the last seen,
// we deoptimize instead of keeping the original invoke instruction.
bool deoptimize = all_targets_inlined &&
@@ -655,6 +658,7 @@
<< " of its targets could be inlined";
return false;
}
+
MaybeRecordStat(kInlinedPolymorphicCall);
// Run type propagation to get the guards typed.
@@ -1161,13 +1165,13 @@
DCHECK(resolved_field != nullptr);
HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
obj,
+ resolved_field,
resolved_field->GetTypeAsPrimitiveType(),
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
*dex_cache->GetDexFile(),
- dex_cache,
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
@@ -1190,13 +1194,13 @@
HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
obj,
value,
+ resolved_field,
resolved_field->GetTypeAsPrimitiveType(),
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
*dex_cache->GetDexFile(),
- dex_cache,
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
@@ -1579,6 +1583,13 @@
/* declared_can_be_null */ true,
return_replacement)) {
return true;
+ } else if (return_replacement->IsInstanceFieldGet()) {
+ HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (field_get->GetFieldInfo().GetField() ==
+ class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0)) {
+ return true;
+ }
}
} else if (return_replacement->IsInstanceOf()) {
// Inlining InstanceOf into an If may put a tighter bound on reference types.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index af8e2c8..768b1d8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1235,13 +1235,13 @@
uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
field_set = new (arena_) HInstanceFieldSet(object,
value,
+ resolved_field,
field_type,
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
class_def_index,
*dex_file_,
- dex_compilation_unit_->GetDexCache(),
dex_pc);
}
AppendInstruction(field_set);
@@ -1256,13 +1256,13 @@
} else {
uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
field_get = new (arena_) HInstanceFieldGet(object,
+ resolved_field,
field_type,
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
class_def_index,
*dex_file_,
- dex_compilation_unit_->GetDexCache(),
dex_pc);
}
AppendInstruction(field_get);
@@ -1311,9 +1311,9 @@
}
void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type field_type) {
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type) {
uint32_t source_or_dest_reg = instruction.VRegA_21c();
uint16_t field_index = instruction.VRegB_21c();
@@ -1400,23 +1400,23 @@
DCHECK_EQ(HPhi::ToPhiType(value->GetType()), HPhi::ToPhiType(field_type));
AppendInstruction(new (arena_) HStaticFieldSet(cls,
value,
+ resolved_field,
field_type,
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
class_def_index,
*dex_file_,
- dex_cache_,
dex_pc));
} else {
AppendInstruction(new (arena_) HStaticFieldGet(cls,
+ resolved_field,
field_type,
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
field_index,
class_def_index,
*dex_file_,
- dex_cache_,
dex_pc));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 439e3b6..911bfb9 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1118,7 +1118,66 @@
VisitCondition(condition);
}
+// Recognize the following pattern:
+// obj.getClass() ==/!= Foo.class
+// And replace it with a constant value if the type of `obj` is statically known.
+static bool RecognizeAndSimplifyClassCheck(HCondition* condition) {
+ HInstruction* input_one = condition->InputAt(0);
+ HInstruction* input_two = condition->InputAt(1);
+ HLoadClass* load_class = input_one->IsLoadClass()
+ ? input_one->AsLoadClass()
+ : input_two->AsLoadClass();
+ if (load_class == nullptr) {
+ return false;
+ }
+
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ if (!class_rti.IsValid()) {
+ // Unresolved class.
+ return false;
+ }
+
+ HInstanceFieldGet* field_get = (load_class == input_one)
+ ? input_two->AsInstanceFieldGet()
+ : input_one->AsInstanceFieldGet();
+ if (field_get == nullptr) {
+ return false;
+ }
+
+ HInstruction* receiver = field_get->InputAt(0);
+ ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+ if (!receiver_type.IsExact()) {
+ return false;
+ }
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+ DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+ if (field_get->GetFieldInfo().GetField() != field) {
+ return false;
+ }
+
+ // We can replace the compare.
+ int value = 0;
+ if (receiver_type.IsEqual(class_rti)) {
+ value = condition->IsEqual() ? 1 : 0;
+ } else {
+ value = condition->IsNotEqual() ? 1 : 0;
+ }
+ condition->ReplaceWith(condition->GetBlock()->GetGraph()->GetIntConstant(value));
+ return true;
+ }
+}
+
void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
+ if (condition->IsEqual() || condition->IsNotEqual()) {
+ if (RecognizeAndSimplifyClassCheck(condition)) {
+ return;
+ }
+ }
+
// Reverse condition if left is constant. Our code generators prefer constant
// on the right hand side.
if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) {
@@ -1843,11 +1902,11 @@
// so create the HArrayLength, HBoundsCheck and HArrayGet.
HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true);
invoke->GetBlock()->InsertInstructionBefore(length, invoke);
- HBoundsCheck* bounds_check =
- new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex());
+ HBoundsCheck* bounds_check = new (arena) HBoundsCheck(
+ index, length, dex_pc, invoke->GetDexMethodIndex());
invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke);
- HArrayGet* array_get =
- new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
+ HArrayGet* array_get = new (arena) HArrayGet(
+ str, bounds_check, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get);
bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment());
GetGraph()->SetHasBoundsChecks(true);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index e9c6615..cda3185 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -744,14 +744,54 @@
GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler());
}
-static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
+static void MathAbsFP(LocationSummary* locations,
+ bool is64bit,
+ bool isR2OrNewer,
+ bool isR6,
+ MipsAssembler* assembler) {
FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
FRegister out = locations->Out().AsFpuRegister<FRegister>();
- if (is64bit) {
- __ AbsD(out, in);
+ // As a "quality of implementation", rather than pure "spec compliance", it is required that
+ // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN.
+ //
+ // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case,
+ // both regular floating point numbers and NAN values are treated alike, only the sign bit is
+ // affected by this instruction.
+ // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any
+ // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be
+ // changed when doing abs(NaN). Because of that, we clear sign bit in a different way.
+ if (isR6) {
+ if (is64bit) {
+ __ AbsD(out, in);
+ } else {
+ __ AbsS(out, in);
+ }
} else {
- __ AbsS(out, in);
+ if (is64bit) {
+ if (in != out) {
+ __ MovD(out, in);
+ }
+ __ MoveFromFpuHigh(TMP, in);
+ // ins instruction is not available for R1.
+ if (isR2OrNewer) {
+ __ Ins(TMP, ZERO, 31, 1);
+ } else {
+ __ Sll(TMP, TMP, 1);
+ __ Srl(TMP, TMP, 1);
+ }
+ __ MoveToFpuHigh(TMP, out);
+ } else {
+ __ Mfc1(TMP, in);
+ // ins instruction is not available for R1.
+ if (isR2OrNewer) {
+ __ Ins(TMP, ZERO, 31, 1);
+ } else {
+ __ Sll(TMP, TMP, 1);
+ __ Srl(TMP, TMP, 1);
+ }
+ __ Mtc1(TMP, out);
+ }
}
}
@@ -761,7 +801,7 @@
}
void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ true, IsR2OrNewer(), IsR6(), GetAssembler());
}
// float java.lang.Math.abs(float)
@@ -770,7 +810,7 @@
}
void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ false, IsR2OrNewer(), IsR6(), GetAssembler());
}
static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 8c34dc6..5bcfa4c 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -111,20 +111,19 @@
BuildLoop();
// Populate the loop with instructions: set/get field with different types.
- ScopedNullHandle<mirror::DexCache> dex_cache;
HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+ nullptr,
Primitive::kPrimLong,
MemberOffset(10),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph_->GetDexFile(),
- dex_cache,
0);
loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
- parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20),
- false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0);
+ parameter_, int_constant_, nullptr, Primitive::kPrimInt, MemberOffset(20),
+ false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), 0);
loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -140,24 +139,24 @@
// Populate the loop with instructions: set/get field with same types.
ScopedNullHandle<mirror::DexCache> dex_cache;
HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
+ nullptr,
Primitive::kPrimLong,
MemberOffset(10),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph_->GetDexFile(),
- dex_cache,
0);
loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
HInstruction* set_field = new (&allocator_) HInstanceFieldSet(parameter_,
get_field,
+ nullptr,
Primitive::kPrimLong,
MemberOffset(10),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph_->GetDexFile(),
- dex_cache,
0);
loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index a599c2a..d45fa11 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2498,6 +2498,17 @@
}
}
+// Helper for InstructionDataEquals to fetch the mirror String out
+// from a kJitTableAddress LoadString kind.
+// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing
+// mirrors, they are stored in a variable size handle scope which is always
+// visited during a pause. Also, the only caller of this helper
+// only uses the mirror for pointer comparison.
+static inline mirror::String* AsMirrorInternal(Handle<mirror::String> handle)
+ NO_THREAD_SAFETY_ANALYSIS {
+ return handle.Get();
+}
+
bool HLoadString::InstructionDataEquals(const HInstruction* other) const {
const HLoadString* other_load_string = other->AsLoadString();
// TODO: To allow GVN for HLoadString from different dex files, we should compare the strings
@@ -2506,16 +2517,16 @@
GetPackedFields() != other_load_string->GetPackedFields()) {
return false;
}
- LoadKind load_kind = GetLoadKind();
- if (HasAddress(load_kind)) {
- return GetAddress() == other_load_string->GetAddress();
- } else {
- DCHECK(HasStringReference(load_kind)) << load_kind;
- return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
+ switch (GetLoadKind()) {
+ case LoadKind::kBootImageAddress:
+ case LoadKind::kJitTableAddress:
+ return AsMirrorInternal(GetString()) == AsMirrorInternal(other_load_string->GetString());
+ default:
+ return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
}
}
-void HLoadString::SetLoadKindInternal(LoadKind load_kind) {
+void HLoadString::SetLoadKind(LoadKind load_kind) {
// Once sharpened, the load kind should not be changed again.
DCHECK_EQ(GetLoadKind(), LoadKind::kDexCacheViaMethod);
SetPackedField<LoadKindField>(load_kind);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8c64d25..7d6f616 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -171,6 +171,7 @@
friend class HGraph;
friend class HInstruction;
friend class HInstructionIterator;
+ friend class HInstructionIteratorHandleChanges;
friend class HBackwardInstructionIterator;
DISALLOW_COPY_AND_ASSIGN(HInstructionList);
@@ -2312,6 +2313,9 @@
};
std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
+// Iterates over the instructions, while preserving the next instruction
+// in case the current instruction gets removed from the list by the user
+// of this iterator.
class HInstructionIterator : public ValueObject {
public:
explicit HInstructionIterator(const HInstructionList& instructions)
@@ -2333,6 +2337,28 @@
DISALLOW_COPY_AND_ASSIGN(HInstructionIterator);
};
+// Iterates over the instructions without saving the next instruction,
+// therefore handling changes in the graph potentially made by the user
+// of this iterator.
+class HInstructionIteratorHandleChanges : public ValueObject {
+ public:
+ explicit HInstructionIteratorHandleChanges(const HInstructionList& instructions)
+ : instruction_(instructions.first_instruction_) {
+ }
+
+ bool Done() const { return instruction_ == nullptr; }
+ HInstruction* Current() const { return instruction_; }
+ void Advance() {
+ instruction_ = instruction_->GetNext();
+ }
+
+ private:
+ HInstruction* instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInstructionIteratorHandleChanges);
+};
+
+
class HBackwardInstructionIterator : public ValueObject {
public:
explicit HBackwardInstructionIterator(const HInstructionList& instructions)
@@ -5056,60 +5082,62 @@
DISALLOW_COPY_AND_ASSIGN(HNullCheck);
};
+// Embeds an ArtField and all the information required by the compiler. We cache
+// that information to avoid requiring the mutator lock every time we need it.
class FieldInfo : public ValueObject {
public:
- FieldInfo(MemberOffset field_offset,
+ FieldInfo(ArtField* field,
+ MemberOffset field_offset,
Primitive::Type field_type,
bool is_volatile,
uint32_t index,
uint16_t declaring_class_def_index,
- const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
- : field_offset_(field_offset),
+ const DexFile& dex_file)
+ : field_(field),
+ field_offset_(field_offset),
field_type_(field_type),
is_volatile_(is_volatile),
index_(index),
declaring_class_def_index_(declaring_class_def_index),
- dex_file_(dex_file),
- dex_cache_(dex_cache) {}
+ dex_file_(dex_file) {}
+ ArtField* GetField() const { return field_; }
MemberOffset GetFieldOffset() const { return field_offset_; }
Primitive::Type GetFieldType() const { return field_type_; }
uint32_t GetFieldIndex() const { return index_; }
uint16_t GetDeclaringClassDefIndex() const { return declaring_class_def_index_;}
const DexFile& GetDexFile() const { return dex_file_; }
bool IsVolatile() const { return is_volatile_; }
- Handle<mirror::DexCache> GetDexCache() const { return dex_cache_; }
private:
+ ArtField* const field_;
const MemberOffset field_offset_;
const Primitive::Type field_type_;
const bool is_volatile_;
const uint32_t index_;
const uint16_t declaring_class_def_index_;
const DexFile& dex_file_;
- const Handle<mirror::DexCache> dex_cache_;
};
class HInstanceFieldGet FINAL : public HExpression<1> {
public:
HInstanceFieldGet(HInstruction* value,
+ ArtField* field,
Primitive::Type field_type,
MemberOffset field_offset,
bool is_volatile,
uint32_t field_idx,
uint16_t declaring_class_def_index,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache,
uint32_t dex_pc)
: HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
- field_info_(field_offset,
+ field_info_(field,
+ field_offset,
field_type,
is_volatile,
field_idx,
declaring_class_def_index,
- dex_file,
- dex_cache) {
+ dex_file) {
SetRawInputAt(0, value);
}
@@ -5145,22 +5173,22 @@
public:
HInstanceFieldSet(HInstruction* object,
HInstruction* value,
+ ArtField* field,
Primitive::Type field_type,
MemberOffset field_offset,
bool is_volatile,
uint32_t field_idx,
uint16_t declaring_class_def_index,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
- field_info_(field_offset,
+ field_info_(field,
+ field_offset,
field_type,
is_volatile,
field_idx,
declaring_class_def_index,
- dex_file,
- dex_cache) {
+ dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, value);
@@ -5761,39 +5789,31 @@
uint32_t dex_pc)
: HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
special_input_(HUserRecord<HInstruction*>(current_method)),
- string_index_(string_index) {
+ string_index_(string_index),
+ dex_file_(dex_file) {
SetPackedField<LoadKindField>(LoadKind::kDexCacheViaMethod);
- load_data_.dex_file_ = &dex_file;
}
- void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
- DCHECK(HasAddress(load_kind));
- load_data_.address = address;
- SetLoadKindInternal(load_kind);
- }
-
- void SetLoadKindWithStringReference(LoadKind load_kind,
- const DexFile& dex_file,
- dex::StringIndex string_index) {
- DCHECK(HasStringReference(load_kind));
- load_data_.dex_file_ = &dex_file;
- string_index_ = string_index;
- SetLoadKindInternal(load_kind);
- }
+ void SetLoadKind(LoadKind load_kind);
LoadKind GetLoadKind() const {
return GetPackedField<LoadKindField>();
}
- const DexFile& GetDexFile() const;
+ const DexFile& GetDexFile() const {
+ return dex_file_;
+ }
dex::StringIndex GetStringIndex() const {
return string_index_;
}
- uint64_t GetAddress() const {
- DCHECK(HasAddress(GetLoadKind()));
- return load_data_.address;
+ Handle<mirror::String> GetString() const {
+ return string_;
+ }
+
+ void SetString(Handle<mirror::String> str) {
+ string_ = str;
}
bool CanBeMoved() const OVERRIDE { return true; }
@@ -5848,18 +5868,6 @@
static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
- static bool HasStringReference(LoadKind load_kind) {
- return load_kind == LoadKind::kBootImageLinkTimeAddress ||
- load_kind == LoadKind::kBootImageLinkTimePcRelative ||
- load_kind == LoadKind::kBssEntry ||
- load_kind == LoadKind::kDexCacheViaMethod ||
- load_kind == LoadKind::kJitTableAddress;
- }
-
- static bool HasAddress(LoadKind load_kind) {
- return load_kind == LoadKind::kBootImageAddress;
- }
-
void SetLoadKindInternal(LoadKind load_kind);
// The special input is the HCurrentMethod for kDexCacheViaMethod.
@@ -5867,26 +5875,16 @@
// for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
HUserRecord<HInstruction*> special_input_;
- // String index serves also as the hash code and it's also needed for slow-paths,
- // so it must not be overwritten with other load data.
dex::StringIndex string_index_;
+ const DexFile& dex_file_;
- union {
- const DexFile* dex_file_; // For string reference.
- uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets.
- } load_data_;
+ Handle<mirror::String> string_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs);
// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
-inline const DexFile& HLoadString::GetDexFile() const {
- DCHECK(HasStringReference(GetLoadKind())) << GetLoadKind();
- return *load_data_.dex_file_;
-}
-
-// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
inline void HLoadString::AddSpecialInput(HInstruction* special_input) {
// The special input is used for PC-relative loads on some architectures,
// including literal pool loads, which are PC-relative too.
@@ -5937,22 +5935,22 @@
class HStaticFieldGet FINAL : public HExpression<1> {
public:
HStaticFieldGet(HInstruction* cls,
+ ArtField* field,
Primitive::Type field_type,
MemberOffset field_offset,
bool is_volatile,
uint32_t field_idx,
uint16_t declaring_class_def_index,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache,
uint32_t dex_pc)
: HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
- field_info_(field_offset,
+ field_info_(field,
+ field_offset,
field_type,
is_volatile,
field_idx,
declaring_class_def_index,
- dex_file,
- dex_cache) {
+ dex_file) {
SetRawInputAt(0, cls);
}
@@ -5985,22 +5983,22 @@
public:
HStaticFieldSet(HInstruction* cls,
HInstruction* value,
+ ArtField* field,
Primitive::Type field_type,
MemberOffset field_offset,
bool is_volatile,
uint32_t field_idx,
uint16_t declaring_class_def_index,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
- field_info_(field_offset,
+ field_info_(field,
+ field_offset,
field_type,
is_volatile,
field_idx,
declaring_class_def_index,
- dex_file,
- dex_cache) {
+ dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4bf5b08..297500b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1205,7 +1205,7 @@
}
MaybeRecordStat(MethodCompilationStat::kCompiled);
codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), *code_item);
- codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data, dex_cache);
+ codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data);
const void* code = code_cache->CommitCode(
self,
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 33b3875..f8a4469 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -76,6 +76,7 @@
worklist_(worklist),
is_first_run_(is_first_run) {}
+ void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
@@ -151,38 +152,6 @@
instruction->Accept(&visitor);
}
-void ReferenceTypePropagation::Run() {
- worklist_.reserve(kDefaultWorklistSize);
-
- // To properly propagate type info we need to visit in the dominator-based order.
- // Reverse post order guarantees a node's dominators are visited first.
- // We take advantage of this order in `VisitBasicBlock`.
- for (HBasicBlock* block : graph_->GetReversePostOrder()) {
- VisitBasicBlock(block);
- }
-
- ProcessWorklist();
- ValidateTypes();
-}
-
-void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
- RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
- // Handle Phis first as there might be instructions in the same block who depend on them.
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- VisitPhi(it.Current()->AsPhi());
- }
-
- // Handle instructions.
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* instr = it.Current();
- instr->Accept(&visitor);
- }
-
- // Add extra nodes to bound types.
- BoundTypeForIfNotNull(block);
- BoundTypeForIfInstanceOf(block);
-}
-
// Check if we should create a bound type for the given object at the specified
// position. Because of inlining and the fact we run RTP more than once and we
// might have a HBoundType already. If we do, we should not create a new one.
@@ -225,6 +194,153 @@
return false;
}
+// Helper method to bound the type of `receiver` for all instructions dominated
+// by `start_block`, or `start_instruction` if `start_block` is null. The new
+// bound type will have its upper bound be `class_rti`.
+static void BoundTypeIn(HInstruction* receiver,
+ HBasicBlock* start_block,
+ HInstruction* start_instruction,
+ const ReferenceTypeInfo& class_rti) {
+ // We only need to bound the type if we have uses in the relevant block.
+ // So start with null and create the HBoundType lazily, only if it's needed.
+ HBoundType* bound_type = nullptr;
+ DCHECK(!receiver->IsLoadClass()) << "We should not replace HLoadClass instructions";
+ const HUseList<HInstruction*>& uses = receiver->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
+ size_t index = it->GetIndex();
+ // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+ ++it;
+ bool dominates = (start_instruction != nullptr)
+ ? start_instruction->StrictlyDominates(user)
+ : start_block->Dominates(user->GetBlock());
+ if (!dominates) {
+ continue;
+ }
+ if (bound_type == nullptr) {
+ ScopedObjectAccess soa(Thread::Current());
+ HInstruction* insert_point = (start_instruction != nullptr)
+ ? start_instruction->GetNext()
+ : start_block->GetFirstInstruction();
+ if (ShouldCreateBoundType(
+ insert_point, receiver, class_rti, start_instruction, start_block)) {
+ bound_type = new (receiver->GetBlock()->GetGraph()->GetArena()) HBoundType(receiver);
+ bound_type->SetUpperBound(class_rti, /* bound_can_be_null */ false);
+ start_block->InsertInstructionBefore(bound_type, insert_point);
+ // To comply with the RTP algorithm, don't type the bound type just yet, it will
+ // be handled in RTPVisitor::VisitBoundType.
+ } else {
+ // We already have a bound type on the position we would need to insert
+ // the new one. The existing bound type should dominate all the users
+ // (dchecked) so there's no need to continue.
+ break;
+ }
+ }
+ user->ReplaceInput(bound_type, index);
+ }
+ // If the receiver is a null check, also bound the type of the actual
+ // receiver.
+ if (receiver->IsNullCheck()) {
+ BoundTypeIn(receiver->InputAt(0), start_block, start_instruction, class_rti);
+ }
+}
+
+// Recognize the patterns:
+// if (obj.shadow$_klass_ == Foo.class) ...
+// deoptimize if (obj.shadow$_klass_ == Foo.class)
+static void BoundTypeForClassCheck(HInstruction* check) {
+ if (!check->IsIf() && !check->IsDeoptimize()) {
+ return;
+ }
+ HInstruction* compare = check->InputAt(0);
+ if (!compare->IsEqual() && !compare->IsNotEqual()) {
+ return;
+ }
+ HInstruction* input_one = compare->InputAt(0);
+ HInstruction* input_two = compare->InputAt(1);
+ HLoadClass* load_class = input_one->IsLoadClass()
+ ? input_one->AsLoadClass()
+ : input_two->AsLoadClass();
+ if (load_class == nullptr) {
+ return;
+ }
+
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ if (!class_rti.IsValid()) {
+ // We have loaded an unresolved class. Don't bother bounding the type.
+ return;
+ }
+
+ HInstanceFieldGet* field_get = (load_class == input_one)
+ ? input_two->AsInstanceFieldGet()
+ : input_one->AsInstanceFieldGet();
+ if (field_get == nullptr) {
+ return;
+ }
+ HInstruction* receiver = field_get->InputAt(0);
+ ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo();
+ if (receiver_type.IsExact()) {
+ // If we already know the receiver type, don't bother updating its users.
+ return;
+ }
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+ DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+ if (field_get->GetFieldInfo().GetField() != field) {
+ return;
+ }
+ }
+
+ if (check->IsIf()) {
+ HBasicBlock* trueBlock = check->IsEqual()
+ ? check->AsIf()->IfTrueSuccessor()
+ : check->AsIf()->IfFalseSuccessor();
+ BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti);
+ } else {
+ DCHECK(check->IsDeoptimize());
+ if (check->IsEqual()) {
+ BoundTypeIn(receiver, check->GetBlock(), check, class_rti);
+ }
+ }
+}
+
+void ReferenceTypePropagation::Run() {
+ worklist_.reserve(kDefaultWorklistSize);
+
+ // To properly propagate type info we need to visit in the dominator-based order.
+ // Reverse post order guarantees a node's dominators are visited first.
+ // We take advantage of this order in `VisitBasicBlock`.
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ VisitBasicBlock(block);
+ }
+
+ ProcessWorklist();
+ ValidateTypes();
+}
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+ RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+ // Handle Phis first as there might be instructions in the same block who depend on them.
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ VisitPhi(it.Current()->AsPhi());
+ }
+
+ // Handle instructions. Since RTP may add HBoundType instructions just after the
+ // last visited instruction, use `HInstructionIteratorHandleChanges` iterator.
+ for (HInstructionIteratorHandleChanges it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ instr->Accept(&visitor);
+ }
+
+ // Add extra nodes to bound types.
+ BoundTypeForIfNotNull(block);
+ BoundTypeForIfInstanceOf(block);
+ BoundTypeForClassCheck(block->GetLastInstruction());
+}
+
void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
HIf* ifInstruction = block->GetLastInstruction()->AsIf();
if (ifInstruction == nullptr) {
@@ -254,40 +370,14 @@
// We only need to bound the type if we have uses in the relevant block.
// So start with null and create the HBoundType lazily, only if it's needed.
- HBoundType* bound_type = nullptr;
HBasicBlock* notNullBlock = ifInput->IsNotEqual()
? ifInstruction->IfTrueSuccessor()
: ifInstruction->IfFalseSuccessor();
- const HUseList<HInstruction*>& uses = obj->GetUses();
- for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
- HInstruction* user = it->GetUser();
- size_t index = it->GetIndex();
- // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
- ++it;
- if (notNullBlock->Dominates(user->GetBlock())) {
- if (bound_type == nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- HInstruction* insert_point = notNullBlock->GetFirstInstruction();
- ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
- handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
- if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
- bound_type = new (graph_->GetArena()) HBoundType(obj);
- bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
- if (obj->GetReferenceTypeInfo().IsValid()) {
- bound_type->SetReferenceTypeInfo(obj->GetReferenceTypeInfo());
- }
- notNullBlock->InsertInstructionBefore(bound_type, insert_point);
- } else {
- // We already have a bound type on the position we would need to insert
- // the new one. The existing bound type should dominate all the users
- // (dchecked) so there's no need to continue.
- break;
- }
- }
- user->ReplaceInput(bound_type, index);
- }
- }
+ ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
+ handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
+
+ BoundTypeIn(obj, notNullBlock, /* start_instruction */ nullptr, object_rti);
}
// Returns true if one of the patterns below has been recognized. If so, the
@@ -378,15 +468,10 @@
HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
- {
- if (!class_rti.IsValid()) {
- // He have loaded an unresolved class. Don't bother bounding the type.
- return;
- }
+ if (!class_rti.IsValid()) {
+ // He have loaded an unresolved class. Don't bother bounding the type.
+ return;
}
- // We only need to bound the type if we have uses in the relevant block.
- // So start with null and create the HBoundType lazily, only if it's needed.
- HBoundType* bound_type = nullptr;
HInstruction* obj = instanceOf->InputAt(0);
if (obj->GetReferenceTypeInfo().IsExact() && !obj->IsPhi()) {
@@ -398,33 +483,14 @@
// input.
return;
}
- DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
- const HUseList<HInstruction*>& uses = obj->GetUses();
- for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
- HInstruction* user = it->GetUser();
- size_t index = it->GetIndex();
- // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
- ++it;
- if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
- if (bound_type == nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
- if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
- bound_type = new (graph_->GetArena()) HBoundType(obj);
- bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
- bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
- /* InstanceOf fails for null. */ false);
- instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
- } else {
- // We already have a bound type on the position we would need to insert
- // the new one. The existing bound type should dominate all the users
- // (dchecked) so there's no need to continue.
- break;
- }
- }
- user->ReplaceInput(bound_type, index);
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ if (!class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
+ class_rti = ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false);
}
}
+ BoundTypeIn(obj, instanceOfTrueBlock, /* start_instruction */ nullptr, class_rti);
}
void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
@@ -464,6 +530,10 @@
}
}
+void ReferenceTypePropagation::RTPVisitor::VisitDeoptimize(HDeoptimize* instr) {
+ BoundTypeForClassCheck(instr);
+}
+
void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
dex::TypeIndex type_idx,
const DexFile& dex_file,
@@ -515,16 +585,9 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::Class> klass;
- // The field index is unknown only during tests.
- if (info.GetFieldIndex() != kUnknownFieldIndex) {
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ArtField* field = cl->GetResolvedField(info.GetFieldIndex(),
- MakeObjPtr(info.GetDexCache().Get()));
- // TODO: There are certain cases where we can't resolve the field.
- // b/21914925 is open to keep track of a repro case for this issue.
- if (field != nullptr) {
- klass = field->GetType<false>();
- }
+ // The field is unknown only during tests.
+ if (info.GetField() != nullptr) {
+ klass = info.GetField()->GetType<false>();
}
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 559f409..2227872 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -492,7 +492,6 @@
HInstruction** input2) {
HGraph* graph = CreateGraph(allocator);
HBasicBlock* entry = new (allocator) HBasicBlock(graph);
- ScopedNullHandle<mirror::DexCache> dex_cache;
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (allocator) HParameterValue(
@@ -504,13 +503,13 @@
entry->AddSuccessor(block);
HInstruction* test = new (allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimBoolean,
MemberOffset(22),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0);
block->AddInstruction(test);
block->AddInstruction(new (allocator) HIf(test));
@@ -531,22 +530,22 @@
*phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
join->AddPhi(*phi);
*input1 = new (allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimInt,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0);
*input2 = new (allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimInt,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0);
then->AddInstruction(*input1);
else_->AddInstruction(*input2);
@@ -654,7 +653,6 @@
HInstruction** field,
HInstruction** ret) {
HGraph* graph = CreateGraph(allocator);
- ScopedNullHandle<mirror::DexCache> dex_cache;
HBasicBlock* entry = new (allocator) HBasicBlock(graph);
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
@@ -667,13 +665,13 @@
entry->AddSuccessor(block);
*field = new (allocator) HInstanceFieldGet(parameter,
+ nullptr,
Primitive::kPrimInt,
MemberOffset(42),
false,
kUnknownFieldIndex,
kUnknownClassDefIndex,
graph->GetDexFile(),
- dex_cache,
0);
block->AddInstruction(*field);
*ret = new (allocator) HReturn(*field);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index ca26c30..dc8ee23 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -275,7 +275,6 @@
dex::StringIndex string_index = load_string->GetStringIndex();
HLoadString::LoadKind desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
- uint64_t address = 0u; // String or dex cache element address.
{
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
@@ -284,12 +283,13 @@
Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
? compilation_unit_.GetDexCache()
: hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+ mirror::String* string = nullptr;
if (codegen_->GetCompilerOptions().IsBootImage()) {
// Compiling boot image. Resolve the string and allocate it if needed, to ensure
// the string will be added to the boot image.
DCHECK(!runtime->UseJitCompilation());
- mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
+ string = class_linker->ResolveString(dex_file, string_index, dex_cache);
CHECK(string != nullptr);
if (compiler_driver_->GetSupportBootImageFixup()) {
DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
@@ -303,43 +303,32 @@
} else if (runtime->UseJitCompilation()) {
// TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
// DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
- mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
+ string = class_linker->LookupString(dex_file, string_index, dex_cache);
if (string != nullptr) {
if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
- address = reinterpret_cast64<uint64_t>(string);
} else {
desired_load_kind = HLoadString::LoadKind::kJitTableAddress;
}
}
} else {
// AOT app compilation. Try to lookup the string without allocating if not found.
- mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
+ string = class_linker->LookupString(dex_file, string_index, dex_cache);
if (string != nullptr &&
runtime->GetHeap()->ObjectIsInBootImageSpace(string) &&
!codegen_->GetCompilerOptions().GetCompilePic()) {
desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
- address = reinterpret_cast64<uint64_t>(string);
} else {
desired_load_kind = HLoadString::LoadKind::kBssEntry;
}
}
+ if (string != nullptr) {
+ load_string->SetString(handles_->NewHandle(string));
+ }
}
HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind);
- switch (load_kind) {
- case HLoadString::LoadKind::kBootImageLinkTimeAddress:
- case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
- case HLoadString::LoadKind::kBssEntry:
- case HLoadString::LoadKind::kDexCacheViaMethod:
- case HLoadString::LoadKind::kJitTableAddress:
- load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index);
- break;
- case HLoadString::LoadKind::kBootImageAddress:
- DCHECK_NE(address, 0u);
- load_string->SetLoadKindWithAddress(load_kind, address);
- break;
- }
+ load_string->SetLoadKind(load_kind);
}
} // namespace art
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e716cdb..4f06a91 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -524,7 +524,7 @@
/* src */ "LMyThreadSet;",
/* is_strict */ true,
/* is_assignable */ true));
- ASSERT_TRUE(HasAssignable("Ljava/util/Collection;", "LMyThreadSet;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/util/Collection;", "Ljava/util/Set;", true));
}
TEST_F(VerifierDepsTest, Assignable_BothArrays_Resolved) {
@@ -539,26 +539,6 @@
ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
}
-// We test that VerifierDeps does not try to optimize by storing assignability
-// of the component types. This is due to the fact that the component type may
-// be an erroneous class, even though the array type has resolved status.
-
-TEST_F(VerifierDepsTest, Assignable_ArrayToInterface1) {
- ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
- /* src */ "[Ljava/util/TimeZone;",
- /* is_strict */ true,
- /* is_assignable */ true));
- ASSERT_TRUE(HasAssignable("Ljava/io/Serializable;", "[Ljava/util/TimeZone;", true));
-}
-
-TEST_F(VerifierDepsTest, Assignable_ArrayToInterface2) {
- ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
- /* src */ "[LMyThreadSet;",
- /* is_strict */ true,
- /* is_assignable */ true));
- ASSERT_TRUE(HasAssignable("Ljava/io/Serializable;", "[LMyThreadSet;", true));
-}
-
TEST_F(VerifierDepsTest, NotAssignable_BothInBoot) {
ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/lang/Exception;",
/* src */ "Ljava/util/SimpleTimeZone;",
@@ -1083,7 +1063,7 @@
TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) {
ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable"));
ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public abstract interface"));
- ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "LMain;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true));
ASSERT_TRUE(HasMethod("interface",
"Ljava/lang/Runnable;",
"run",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ece81e3..21b03eb 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1095,9 +1095,6 @@
compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
key_value_store_->Put(OatHeader::kCompilerFilter,
CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
- key_value_store_->Put(OatHeader::kHasPatchInfoKey,
- compiler_options_->GetIncludePatchInformation() ? OatHeader::kTrueValue
- : OatHeader::kFalseValue);
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index 181b2ed..6c2c815 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -39,80 +39,69 @@
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
- // Assume all ARM processors are SMP.
- // TODO: set the SMP support based on variant.
- const bool smp = true;
-
// Look for variants that have divide support.
static const char* arm_variants_with_div[] = {
- "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57",
- "cortex-a53.a57", "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5",
- "cyclone", "denver", "krait", "swift" };
+ "cortex-a7",
+ "cortex-a12",
+ "cortex-a15",
+ "cortex-a17",
+ "cortex-a53",
+ "cortex-a53.a57",
+ "cortex-a57",
+ "denver",
+ "krait",
+ };
- bool has_div = FindVariantInArray(arm_variants_with_div, arraysize(arm_variants_with_div),
+ bool has_div = FindVariantInArray(arm_variants_with_div,
+ arraysize(arm_variants_with_div),
variant);
// Look for variants that have LPAE support.
static const char* arm_variants_with_lpae[] = {
- "cortex-a7", "cortex-a15", "krait", "denver", "cortex-a53", "cortex-a57", "cortex-a53.a57"
+ "cortex-a7",
+ "cortex-a12",
+ "cortex-a15",
+ "cortex-a17",
+ "cortex-a53",
+ "cortex-a53.a57",
+ "cortex-a57",
+ "denver",
+ "krait",
};
- bool has_lpae = FindVariantInArray(arm_variants_with_lpae, arraysize(arm_variants_with_lpae),
+ bool has_lpae = FindVariantInArray(arm_variants_with_lpae,
+ arraysize(arm_variants_with_lpae),
variant);
if (has_div == false && has_lpae == false) {
- // Avoid unsupported variants.
- static const char* unsupported_arm_variants[] = {
- // ARM processors that aren't ARMv7 compatible aren't supported.
- "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620",
- "cortex-m0", "cortex-m0plus", "cortex-m1",
- "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te",
- "iwmmxt", "iwmmxt2",
- "strongarm", "strongarm110", "strongarm1100", "strongarm1110",
- "xscale"
- };
- if (FindVariantInArray(unsupported_arm_variants, arraysize(unsupported_arm_variants),
- variant)) {
- *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", variant.c_str());
- return ArmFeaturesUniquePtr();
- }
- // Warn if the variant is unknown.
- // TODO: some of the variants below may have feature support, but that support is currently
- // unknown so we'll choose conservative (sub-optimal) defaults without warning.
- // TODO: some of the architectures may not support all features required by ART and should be
- // moved to unsupported_arm_variants[] above.
- static const char* arm_variants_without_known_features[] = {
+ static const char* arm_variants_with_default_features[] = {
+ "cortex-a5",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a9-mp",
"default",
- "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i",
- "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s",
- "arm710t", "arm720t", "arm740t",
- "arm8", "arm810",
- "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s",
- "arm926ej-s", "arm940t", "arm9tdmi",
- "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e",
- "arm1136j-s", "arm1136jf-s",
- "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s",
- "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f",
- "marvell-pj4", "mpcore", "mpcorenovfp"
+ "generic"
};
- if (!FindVariantInArray(arm_variants_without_known_features,
- arraysize(arm_variants_without_known_features),
+ if (!FindVariantInArray(arm_variants_with_default_features,
+ arraysize(arm_variants_with_default_features),
variant)) {
- LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant
+ *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", variant.c_str());
+ return nullptr;
+ } else {
+ // Warn if we use the default features.
+ LOG(WARNING) << "Using default instruction set features for ARM CPU variant (" << variant
<< ") using conservative defaults";
}
}
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_lpae));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
}
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
- bool smp = (bitmap & kSmpBitfield) != 0;
bool has_div = (bitmap & kDivBitfield) != 0;
bool has_atomic_ldrd_strd = (bitmap & kAtomicLdrdStrdBitfield) != 0;
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_atomic_ldrd_strd));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd));
}
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCppDefines() {
- const bool smp = true;
#if defined(__ARM_ARCH_EXT_IDIV__)
const bool has_div = true;
#else
@@ -123,13 +112,12 @@
#else
const bool has_lpae = false;
#endif
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_lpae));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
}
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCpuInfo() {
// Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
// the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
- bool smp = false;
bool has_lpae = false;
bool has_div = false;
@@ -151,9 +139,6 @@
if (line.find("lpae") != std::string::npos) {
has_lpae = true;
}
- } else if (line.find("processor") != std::string::npos &&
- line.find(": 1") != std::string::npos) {
- smp = true;
}
}
}
@@ -161,12 +146,10 @@
} else {
LOG(ERROR) << "Failed to open /proc/cpuinfo";
}
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_lpae));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
}
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromHwcap() {
- bool smp = sysconf(_SC_NPROCESSORS_CONF) > 1;
-
bool has_div = false;
bool has_lpae = false;
@@ -184,7 +167,7 @@
}
#endif
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_lpae));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
}
// A signal handler called by a fault for an illegal instruction. We record the fact in r0
@@ -203,8 +186,6 @@
}
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromAssembly() {
- const bool smp = true;
-
// See if have a sdiv instruction. Register a signal handler and try to execute an sdiv
// instruction. If we get a SIGILL then it's not supported.
struct sigaction sa, osa;
@@ -230,7 +211,7 @@
#else
const bool has_lpae = false;
#endif
- return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(smp, has_div, has_lpae));
+ return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
}
bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
@@ -238,28 +219,21 @@
return false;
}
const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
- return IsSmp() == other_as_arm->IsSmp() &&
- has_div_ == other_as_arm->has_div_ &&
+ return has_div_ == other_as_arm->has_div_ &&
has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_;
}
uint32_t ArmInstructionSetFeatures::AsBitmap() const {
- return (IsSmp() ? kSmpBitfield : 0) |
- (has_div_ ? kDivBitfield : 0) |
+ return (has_div_ ? kDivBitfield : 0) |
(has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0);
}
std::string ArmInstructionSetFeatures::GetFeatureString() const {
std::string result;
- if (IsSmp()) {
- result += "smp";
- } else {
- result += "-smp";
- }
if (has_div_) {
- result += ",div";
+ result += "div";
} else {
- result += ",-div";
+ result += "-div";
}
if (has_atomic_ldrd_strd_) {
result += ",atomic_ldrd_strd";
@@ -271,7 +245,7 @@
std::unique_ptr<const InstructionSetFeatures>
ArmInstructionSetFeatures::AddFeaturesFromSplitString(
- const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+ const std::vector<std::string>& features, std::string* error_msg) const {
bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
bool has_div = has_div_;
for (auto i = features.begin(); i != features.end(); i++) {
@@ -290,7 +264,7 @@
}
}
return std::unique_ptr<const InstructionSetFeatures>(
- new ArmInstructionSetFeatures(smp, has_div, has_atomic_ldrd_strd));
+ new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd));
}
} // namespace art
diff --git a/runtime/arch/arm/instruction_set_features_arm.h b/runtime/arch/arm/instruction_set_features_arm.h
index 204d1d7..11f8bf0 100644
--- a/runtime/arch/arm/instruction_set_features_arm.h
+++ b/runtime/arch/arm/instruction_set_features_arm.h
@@ -74,20 +74,19 @@
protected:
// Parse a vector of the form "div", "lpae" adding these to a new ArmInstructionSetFeatures.
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE;
private:
- ArmInstructionSetFeatures(bool smp, bool has_div, bool has_atomic_ldrd_strd)
- : InstructionSetFeatures(smp),
+ ArmInstructionSetFeatures(bool has_div, bool has_atomic_ldrd_strd)
+ : InstructionSetFeatures(),
has_div_(has_div), has_atomic_ldrd_strd_(has_atomic_ldrd_strd) {
}
// Bitmap positions for encoding features as a bitmap.
enum {
- kSmpBitfield = 1,
- kDivBitfield = 2,
- kAtomicLdrdStrdBitfield = 4,
+ kDivBitfield = 1 << 0,
+ kAtomicLdrdStrdBitfield = 1 << 1,
};
const bool has_div_;
diff --git a/runtime/arch/arm/instruction_set_features_arm_test.cc b/runtime/arch/arm/instruction_set_features_arm_test.cc
index 44b1640..697ca90 100644
--- a/runtime/arch/arm/instruction_set_features_arm_test.cc
+++ b/runtime/arch/arm/instruction_set_features_arm_test.cc
@@ -31,8 +31,8 @@
EXPECT_TRUE(krait_features->Equals(krait_features.get()));
EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
- EXPECT_EQ(krait_features->AsBitmap(), 7U);
+ EXPECT_STREQ("div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
+ EXPECT_EQ(krait_features->AsBitmap(), 3U);
// Build features for a 32-bit ARM denver processor.
std::unique_ptr<const InstructionSetFeatures> denver_features(
@@ -44,21 +44,21 @@
EXPECT_TRUE(krait_features->Equals(denver_features.get()));
EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
- EXPECT_EQ(denver_features->AsBitmap(), 7U);
+ EXPECT_STREQ("div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
+ EXPECT_EQ(denver_features->AsBitmap(), 3U);
// Build features for a 32-bit ARMv7 processor.
- std::unique_ptr<const InstructionSetFeatures> arm7_features(
- InstructionSetFeatures::FromVariant(kArm, "arm7", &error_msg));
- ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg;
+ std::unique_ptr<const InstructionSetFeatures> generic_features(
+ InstructionSetFeatures::FromVariant(kArm, "generic", &error_msg));
+ ASSERT_TRUE(generic_features.get() != nullptr) << error_msg;
- EXPECT_TRUE(arm7_features->Equals(arm7_features.get()));
- EXPECT_FALSE(arm7_features->Equals(krait_features.get()));
- EXPECT_FALSE(krait_features->Equals(arm7_features.get()));
- EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
- EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,-div,-atomic_ldrd_strd", arm7_features->GetFeatureString().c_str());
- EXPECT_EQ(arm7_features->AsBitmap(), 1U);
+ EXPECT_TRUE(generic_features->Equals(generic_features.get()));
+ EXPECT_FALSE(generic_features->Equals(krait_features.get()));
+ EXPECT_FALSE(krait_features->Equals(generic_features.get()));
+ EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+ EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+ EXPECT_STREQ("-div,-atomic_ldrd_strd", generic_features->GetFeatureString().c_str());
+ EXPECT_EQ(generic_features->AsBitmap(), 0U);
// ARM6 is not a supported architecture variant.
std::unique_ptr<const InstructionSetFeatures> arm6_features(
@@ -70,7 +70,7 @@
TEST(ArmInstructionSetFeaturesTest, ArmAddFeaturesFromString) {
std::string error_msg;
std::unique_ptr<const InstructionSetFeatures> base_features(
- InstructionSetFeatures::FromVariant(kArm, "arm7", &error_msg));
+ InstructionSetFeatures::FromVariant(kArm, "generic", &error_msg));
ASSERT_TRUE(base_features.get() != nullptr) << error_msg;
// Build features for a 32-bit ARM with LPAE and div processor.
@@ -82,8 +82,8 @@
EXPECT_TRUE(krait_features->Equals(krait_features.get()));
EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
- EXPECT_EQ(krait_features->AsBitmap(), 7U);
+ EXPECT_STREQ("div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
+ EXPECT_EQ(krait_features->AsBitmap(), 3U);
// Build features for a 32-bit ARM processor with LPAE and div flipped.
std::unique_ptr<const InstructionSetFeatures> denver_features(
@@ -95,21 +95,21 @@
EXPECT_TRUE(krait_features->Equals(denver_features.get()));
EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
- EXPECT_EQ(denver_features->AsBitmap(), 7U);
+ EXPECT_STREQ("div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
+ EXPECT_EQ(denver_features->AsBitmap(), 3U);
// Build features for a 32-bit default ARM processor.
- std::unique_ptr<const InstructionSetFeatures> arm7_features(
+ std::unique_ptr<const InstructionSetFeatures> generic_features(
base_features->AddFeaturesFromString("default", &error_msg));
- ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg;
+ ASSERT_TRUE(generic_features.get() != nullptr) << error_msg;
- EXPECT_TRUE(arm7_features->Equals(arm7_features.get()));
- EXPECT_FALSE(arm7_features->Equals(krait_features.get()));
- EXPECT_FALSE(krait_features->Equals(arm7_features.get()));
- EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
- EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
- EXPECT_STREQ("smp,-div,-atomic_ldrd_strd", arm7_features->GetFeatureString().c_str());
- EXPECT_EQ(arm7_features->AsBitmap(), 1U);
+ EXPECT_TRUE(generic_features->Equals(generic_features.get()));
+ EXPECT_FALSE(generic_features->Equals(krait_features.get()));
+ EXPECT_FALSE(krait_features->Equals(generic_features.get()));
+ EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+ EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+ EXPECT_STREQ("-div,-atomic_ldrd_strd", generic_features->GetFeatureString().c_str());
+ EXPECT_EQ(generic_features->AsBitmap(), 0U);
}
} // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 52d8b3e..c598743 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -31,8 +31,6 @@
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
- const bool smp = true; // Conservative default.
-
// Look for variants that need a fix for a53 erratum 835769.
static const char* arm64_variants_with_a53_835769_bug[] = {
"default", "generic", "cortex-a53" // Pessimistically assume all generic ARM64s are A53s.
@@ -58,50 +56,27 @@
bool needs_a53_843419_fix = needs_a53_835769_fix;
return Arm64FeaturesUniquePtr(
- new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix, needs_a53_843419_fix));
+ new Arm64InstructionSetFeatures(needs_a53_835769_fix, needs_a53_843419_fix));
}
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
- bool smp = (bitmap & kSmpBitfield) != 0;
bool is_a53 = (bitmap & kA53Bitfield) != 0;
- return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(smp, is_a53, is_a53));
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
}
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCppDefines() {
- const bool smp = true;
const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
- return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(smp, is_a53, is_a53));
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
}
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCpuInfo() {
- // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
- // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
- bool smp = false;
const bool is_a53 = true; // Conservative default.
-
- std::ifstream in("/proc/cpuinfo");
- if (!in.fail()) {
- while (!in.eof()) {
- std::string line;
- std::getline(in, line);
- if (!in.eof()) {
- LOG(INFO) << "cpuinfo line: " << line;
- if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) {
- smp = true;
- }
- }
- }
- in.close();
- } else {
- LOG(ERROR) << "Failed to open /proc/cpuinfo";
- }
- return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(smp, is_a53, is_a53));
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
}
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromHwcap() {
- bool smp = sysconf(_SC_NPROCESSORS_CONF) > 1;
const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
- return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(smp, is_a53, is_a53));
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
}
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromAssembly() {
@@ -113,32 +88,28 @@
if (kArm64 != other->GetInstructionSet()) {
return false;
}
- const Arm64InstructionSetFeatures* other_as_arm = other->AsArm64InstructionSetFeatures();
- return fix_cortex_a53_835769_ == other_as_arm->fix_cortex_a53_835769_;
+ const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures();
+ return fix_cortex_a53_835769_ == other_as_arm64->fix_cortex_a53_835769_ &&
+ fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_;
}
uint32_t Arm64InstructionSetFeatures::AsBitmap() const {
- return (IsSmp() ? kSmpBitfield : 0) | (fix_cortex_a53_835769_ ? kA53Bitfield : 0);
+ return (fix_cortex_a53_835769_ ? kA53Bitfield : 0);
}
std::string Arm64InstructionSetFeatures::GetFeatureString() const {
std::string result;
- if (IsSmp()) {
- result += "smp";
- } else {
- result += "-smp";
- }
if (fix_cortex_a53_835769_) {
- result += ",a53";
+ result += "a53";
} else {
- result += ",-a53";
+ result += "-a53";
}
return result;
}
std::unique_ptr<const InstructionSetFeatures>
Arm64InstructionSetFeatures::AddFeaturesFromSplitString(
- const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+ const std::vector<std::string>& features, std::string* error_msg) const {
bool is_a53 = fix_cortex_a53_835769_;
for (auto i = features.begin(); i != features.end(); i++) {
std::string feature = android::base::Trim(*i);
@@ -152,7 +123,7 @@
}
}
return std::unique_ptr<const InstructionSetFeatures>(
- new Arm64InstructionSetFeatures(smp, is_a53, is_a53));
+ new Arm64InstructionSetFeatures(is_a53, is_a53));
}
} // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index e51aa1c..4243d32 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -73,20 +73,19 @@
protected:
// Parse a vector of the form "a53" adding these to a new ArmInstructionSetFeatures.
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE;
private:
- Arm64InstructionSetFeatures(bool smp, bool needs_a53_835769_fix, bool needs_a53_843419_fix)
- : InstructionSetFeatures(smp),
+ Arm64InstructionSetFeatures(bool needs_a53_835769_fix, bool needs_a53_843419_fix)
+ : InstructionSetFeatures(),
fix_cortex_a53_835769_(needs_a53_835769_fix),
fix_cortex_a53_843419_(needs_a53_843419_fix) {
}
// Bitmap positions for encoding features as a bitmap.
enum {
- kSmpBitfield = 1,
- kA53Bitfield = 2,
+ kA53Bitfield = 1 << 0,
};
const bool fix_cortex_a53_835769_;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index 027e59c..cefa499 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -28,8 +28,8 @@
ASSERT_TRUE(arm64_features.get() != nullptr) << error_msg;
EXPECT_EQ(arm64_features->GetInstructionSet(), kArm64);
EXPECT_TRUE(arm64_features->Equals(arm64_features.get()));
- EXPECT_STREQ("smp,a53", arm64_features->GetFeatureString().c_str());
- EXPECT_EQ(arm64_features->AsBitmap(), 3U);
+ EXPECT_STREQ("a53", arm64_features->GetFeatureString().c_str());
+ EXPECT_EQ(arm64_features->AsBitmap(), 1U);
}
} // namespace art
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index db004e7..00d22c4 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -218,7 +218,6 @@
}
std::vector<std::string> features;
Split(feature_list, ',', &features);
- bool smp = smp_;
bool use_default = false; // Have we seen the 'default' feature?
bool first = false; // Is this first feature?
for (auto it = features.begin(); it != features.end();) {
@@ -236,14 +235,7 @@
*error_msg = "Unexpected instruction set features before 'default'";
return std::unique_ptr<const InstructionSetFeatures>();
}
- } else if (feature == "smp") {
- smp = true;
- erase = true;
- } else if (feature == "-smp") {
- smp = false;
- erase = true;
}
- // Erase the smp feature once processed.
if (!erase) {
++it;
} else {
@@ -252,11 +244,11 @@
first = true;
}
// Expectation: "default" is standalone, no other flags. But an empty features vector after
- // processing can also come along if the handled flags (at the moment only smp) are the only
- // ones in the list. So logically, we check "default -> features.empty."
+ // processing can also come along if the handled flags are the only ones in the list. So
+ // logically, we check "default -> features.empty."
DCHECK(!use_default || features.empty());
- return AddFeaturesFromSplitString(smp, features, error_msg);
+ return AddFeaturesFromSplitString(features, error_msg);
}
const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index d84bc02..b6c5c71 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -76,11 +76,6 @@
// Return a string of the form "div,lpae" or "none".
virtual std::string GetFeatureString() const = 0;
- // Does the instruction set variant require instructions for correctness with SMP?
- bool IsSmp() const {
- return smp_;
- }
-
// Down cast this ArmInstructionFeatures.
const ArmInstructionSetFeatures* AsArmInstructionSetFeatures() const;
@@ -102,7 +97,7 @@
virtual ~InstructionSetFeatures() {}
protected:
- explicit InstructionSetFeatures(bool smp) : smp_(smp) {}
+ InstructionSetFeatures() {}
// Returns true if variant appears in the array variants.
static bool FindVariantInArray(const char* const variants[], size_t num_variants,
@@ -110,12 +105,10 @@
// Add architecture specific features in sub-classes.
virtual std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const = 0;
private:
- const bool smp_;
-
DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures);
};
std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs);
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 5b50573..3c5afc2 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -71,8 +71,6 @@
MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
- bool smp = true; // Conservative default.
-
// Override defaults based on compiler flags.
// This is needed when running ART test where the variant is not defined.
bool fpu_32bit;
@@ -90,7 +88,7 @@
fpu_32bit = (variant[kPrefixLength] < '5');
mips_isa_gte2 = (variant[kPrefixLength] >= '2');
} else if (variant == "default") {
- // Default variant is: smp = true, has FPU, is gte2. This is the traditional setting.
+ // Default variant has FPU, is gte2. This is the traditional setting.
//
// Note, we get FPU bitness and R6-ness from the build (using cpp defines, see above)
// and don't override them because many things depend on the "default" variant being
@@ -102,58 +100,32 @@
LOG(WARNING) << "Unexpected CPU variant for Mips32 using defaults: " << variant;
}
- return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6));
+ return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(fpu_32bit, mips_isa_gte2, r6));
}
-MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromBitmap(
- uint32_t bitmap) {
- bool smp = (bitmap & kSmpBitfield) != 0;
+MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
bool fpu_32bit = (bitmap & kFpu32Bitfield) != 0;
bool mips_isa_gte2 = (bitmap & kIsaRevGte2Bitfield) != 0;
bool r6 = (bitmap & kR6) != 0;
- return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6));
+ return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(fpu_32bit, mips_isa_gte2, r6));
}
MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromCppDefines() {
- // Assume conservative defaults.
- const bool smp = true;
-
bool fpu_32bit;
bool mips_isa_gte2;
bool r6;
GetFlagsFromCppDefined(&mips_isa_gte2, &r6, &fpu_32bit);
- return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6));
+ return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(fpu_32bit, mips_isa_gte2, r6));
}
MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromCpuInfo() {
- // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
- // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
- // Assume conservative defaults.
- bool smp = false;
-
bool fpu_32bit;
bool mips_isa_gte2;
bool r6;
GetFlagsFromCppDefined(&mips_isa_gte2, &r6, &fpu_32bit);
- std::ifstream in("/proc/cpuinfo");
- if (!in.fail()) {
- while (!in.eof()) {
- std::string line;
- std::getline(in, line);
- if (!in.eof()) {
- LOG(INFO) << "cpuinfo line: " << line;
- if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) {
- smp = true;
- }
- }
- }
- in.close();
- } else {
- LOG(ERROR) << "Failed to open /proc/cpuinfo";
- }
- return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6));
+ return MipsFeaturesUniquePtr(new MipsInstructionSetFeatures(fpu_32bit, mips_isa_gte2, r6));
}
MipsFeaturesUniquePtr MipsInstructionSetFeatures::FromHwcap() {
@@ -171,30 +143,23 @@
return false;
}
const MipsInstructionSetFeatures* other_as_mips = other->AsMipsInstructionSetFeatures();
- return (IsSmp() == other->IsSmp()) &&
- (fpu_32bit_ == other_as_mips->fpu_32bit_) &&
+ return (fpu_32bit_ == other_as_mips->fpu_32bit_) &&
(mips_isa_gte2_ == other_as_mips->mips_isa_gte2_) &&
(r6_ == other_as_mips->r6_);
}
uint32_t MipsInstructionSetFeatures::AsBitmap() const {
- return (IsSmp() ? kSmpBitfield : 0) |
- (fpu_32bit_ ? kFpu32Bitfield : 0) |
+ return (fpu_32bit_ ? kFpu32Bitfield : 0) |
(mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0) |
(r6_ ? kR6 : 0);
}
std::string MipsInstructionSetFeatures::GetFeatureString() const {
std::string result;
- if (IsSmp()) {
- result += "smp";
- } else {
- result += "-smp";
- }
if (fpu_32bit_) {
- result += ",fpu32";
+ result += "fpu32";
} else {
- result += ",-fpu32";
+ result += "-fpu32";
}
if (mips_isa_gte2_) {
result += ",mips2";
@@ -209,7 +174,7 @@
std::unique_ptr<const InstructionSetFeatures>
MipsInstructionSetFeatures::AddFeaturesFromSplitString(
- const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+ const std::vector<std::string>& features, std::string* error_msg) const {
bool fpu_32bit = fpu_32bit_;
bool mips_isa_gte2 = mips_isa_gte2_;
bool r6 = r6_;
@@ -233,7 +198,7 @@
}
}
return std::unique_ptr<const InstructionSetFeatures>(
- new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6));
+ new MipsInstructionSetFeatures(fpu_32bit, mips_isa_gte2, r6));
}
} // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h
index c2a28dc..1aec99f 100644
--- a/runtime/arch/mips/instruction_set_features_mips.h
+++ b/runtime/arch/mips/instruction_set_features_mips.h
@@ -80,12 +80,12 @@
protected:
// Parse a vector of the form "fpu32", "mips2" adding these to a new MipsInstructionSetFeatures.
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE;
private:
- MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2, bool r6)
- : InstructionSetFeatures(smp),
+ MipsInstructionSetFeatures(bool fpu_32bit, bool mips_isa_gte2, bool r6)
+ : InstructionSetFeatures(),
fpu_32bit_(fpu_32bit),
mips_isa_gte2_(mips_isa_gte2),
r6_(r6) {
@@ -101,10 +101,9 @@
// Bitmap positions for encoding features as a bitmap.
enum {
- kSmpBitfield = 1,
- kFpu32Bitfield = 2,
- kIsaRevGte2Bitfield = 4,
- kR6 = 8,
+ kFpu32Bitfield = 1 << 0,
+ kIsaRevGte2Bitfield = 1 << 1,
+ kR6 = 1 << 2,
};
const bool fpu_32bit_;
diff --git a/runtime/arch/mips/instruction_set_features_mips_test.cc b/runtime/arch/mips/instruction_set_features_mips_test.cc
index 9b81ce2..6613b84 100644
--- a/runtime/arch/mips/instruction_set_features_mips_test.cc
+++ b/runtime/arch/mips/instruction_set_features_mips_test.cc
@@ -27,8 +27,8 @@
ASSERT_TRUE(mips_features.get() != nullptr) << error_msg;
EXPECT_EQ(mips_features->GetInstructionSet(), kMips);
EXPECT_TRUE(mips_features->Equals(mips_features.get()));
- EXPECT_STREQ("smp,fpu32,mips2", mips_features->GetFeatureString().c_str());
- EXPECT_EQ(mips_features->AsBitmap(), 7U);
+ EXPECT_STREQ("fpu32,mips2", mips_features->GetFeatureString().c_str());
+ EXPECT_EQ(mips_features->AsBitmap(), 3U);
}
} // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index 92c44e8..5606c1d 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -33,43 +33,19 @@
if (variant != "default" && variant != "mips64r6") {
LOG(WARNING) << "Unexpected CPU variant for Mips64 using defaults: " << variant;
}
- bool smp = true; // Conservative default.
- return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures(smp));
+ return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures());
}
-Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
- bool smp = (bitmap & kSmpBitfield) != 0;
- return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures(smp));
+Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromBitmap(uint32_t bitmap ATTRIBUTE_UNUSED) {
+ return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures());
}
Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromCppDefines() {
- const bool smp = true;
-
- return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures(smp));
+ return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures());
}
Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromCpuInfo() {
- // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
- // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
- bool smp = false;
-
- std::ifstream in("/proc/cpuinfo");
- if (!in.fail()) {
- while (!in.eof()) {
- std::string line;
- std::getline(in, line);
- if (!in.eof()) {
- LOG(INFO) << "cpuinfo line: " << line;
- if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) {
- smp = true;
- }
- }
- }
- in.close();
- } else {
- LOG(ERROR) << "Failed to open /proc/cpuinfo";
- }
- return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures(smp));
+ return Mips64FeaturesUniquePtr(new Mips64InstructionSetFeatures());
}
Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromHwcap() {
@@ -86,26 +62,20 @@
if (kMips64 != other->GetInstructionSet()) {
return false;
}
- return (IsSmp() == other->IsSmp());
+ return true;
}
uint32_t Mips64InstructionSetFeatures::AsBitmap() const {
- return (IsSmp() ? kSmpBitfield : 0);
+ return 0;
}
std::string Mips64InstructionSetFeatures::GetFeatureString() const {
- std::string result;
- if (IsSmp()) {
- result += "smp";
- } else {
- result += "-smp";
- }
- return result;
+ return "";
}
std::unique_ptr<const InstructionSetFeatures>
Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
- const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+ const std::vector<std::string>& features, std::string* error_msg) const {
auto i = features.begin();
if (i != features.end()) {
// We don't have any features.
@@ -113,7 +83,7 @@
*error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
return nullptr;
}
- return std::unique_ptr<const InstructionSetFeatures>(new Mips64InstructionSetFeatures(smp));
+ return std::unique_ptr<const InstructionSetFeatures>(new Mips64InstructionSetFeatures());
}
} // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.h b/runtime/arch/mips64/instruction_set_features_mips64.h
index 2e66235..c80c466 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.h
+++ b/runtime/arch/mips64/instruction_set_features_mips64.h
@@ -29,7 +29,7 @@
public:
// Process a CPU variant string like "r4000" and create InstructionSetFeatures.
static Mips64FeaturesUniquePtr FromVariant(const std::string& variant,
- std::string* error_msg);
+ std::string* error_msg);
// Parse a bitmap and create an InstructionSetFeatures.
static Mips64FeaturesUniquePtr FromBitmap(uint32_t bitmap);
@@ -63,19 +63,13 @@
protected:
// Parse a vector of the form "fpu32", "mips2" adding these to a new Mips64InstructionSetFeatures.
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp,
- const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE;
private:
- explicit Mips64InstructionSetFeatures(bool smp) : InstructionSetFeatures(smp) {
+ Mips64InstructionSetFeatures() : InstructionSetFeatures() {
}
- // Bitmap positions for encoding features as a bitmap.
- enum {
- kSmpBitfield = 1,
- };
-
DISALLOW_COPY_AND_ASSIGN(Mips64InstructionSetFeatures);
};
diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
index dc34506..1d03794 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64_test.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
@@ -27,8 +27,8 @@
ASSERT_TRUE(mips64_features.get() != nullptr) << error_msg;
EXPECT_EQ(mips64_features->GetInstructionSet(), kMips64);
EXPECT_TRUE(mips64_features->Equals(mips64_features.get()));
- EXPECT_STREQ("smp", mips64_features->GetFeatureString().c_str());
- EXPECT_EQ(mips64_features->AsBitmap(), 1U);
+ EXPECT_STREQ("", mips64_features->GetFeatureString().c_str());
+ EXPECT_EQ(mips64_features->AsBitmap(), 0U);
}
} // namespace art
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index c520d63..5788122 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -54,7 +54,6 @@
};
X86FeaturesUniquePtr X86InstructionSetFeatures::Create(bool x86_64,
- bool smp,
bool has_SSSE3,
bool has_SSE4_1,
bool has_SSE4_2,
@@ -62,16 +61,14 @@
bool has_AVX2,
bool has_POPCNT) {
if (x86_64) {
- return X86FeaturesUniquePtr(new X86_64InstructionSetFeatures(smp,
- has_SSSE3,
+ return X86FeaturesUniquePtr(new X86_64InstructionSetFeatures(has_SSSE3,
has_SSE4_1,
has_SSE4_2,
has_AVX,
has_AVX2,
has_POPCNT));
} else {
- return X86FeaturesUniquePtr(new X86InstructionSetFeatures(smp,
- has_SSSE3,
+ return X86FeaturesUniquePtr(new X86InstructionSetFeatures(has_SSSE3,
has_SSE4_1,
has_SSE4_2,
has_AVX,
@@ -83,7 +80,6 @@
X86FeaturesUniquePtr X86InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED,
bool x86_64) {
- bool smp = true; // Conservative default.
bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
variant);
bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
@@ -106,23 +102,20 @@
LOG(WARNING) << "Unexpected CPU variant for X86 using defaults: " << variant;
}
- return Create(x86_64, smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
+ return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
}
X86FeaturesUniquePtr X86InstructionSetFeatures::FromBitmap(uint32_t bitmap, bool x86_64) {
- bool smp = (bitmap & kSmpBitfield) != 0;
bool has_SSSE3 = (bitmap & kSsse3Bitfield) != 0;
bool has_SSE4_1 = (bitmap & kSse4_1Bitfield) != 0;
bool has_SSE4_2 = (bitmap & kSse4_2Bitfield) != 0;
bool has_AVX = (bitmap & kAvxBitfield) != 0;
bool has_AVX2 = (bitmap & kAvxBitfield) != 0;
bool has_POPCNT = (bitmap & kPopCntBitfield) != 0;
- return Create(x86_64, smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
+ return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
}
X86FeaturesUniquePtr X86InstructionSetFeatures::FromCppDefines(bool x86_64) {
- const bool smp = true;
-
#ifndef __SSSE3__
const bool has_SSSE3 = false;
#else
@@ -159,13 +152,12 @@
const bool has_POPCNT = true;
#endif
- return Create(x86_64, smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
+ return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
}
X86FeaturesUniquePtr X86InstructionSetFeatures::FromCpuInfo(bool x86_64) {
// Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
// the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
- bool smp = false;
bool has_SSSE3 = false;
bool has_SSE4_1 = false;
bool has_SSE4_2 = false;
@@ -200,9 +192,6 @@
if (line.find("popcnt") != std::string::npos) {
has_POPCNT = true;
}
- } else if (line.find("processor") != std::string::npos &&
- line.find(": 1") != std::string::npos) {
- smp = true;
}
}
}
@@ -210,7 +199,7 @@
} else {
LOG(ERROR) << "Failed to open /proc/cpuinfo";
}
- return Create(x86_64, smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
+ return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
}
X86FeaturesUniquePtr X86InstructionSetFeatures::FromHwcap(bool x86_64) {
@@ -228,8 +217,7 @@
return false;
}
const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
- return (IsSmp() == other->IsSmp()) &&
- (has_SSSE3_ == other_as_x86->has_SSSE3_) &&
+ return (has_SSSE3_ == other_as_x86->has_SSSE3_) &&
(has_SSE4_1_ == other_as_x86->has_SSE4_1_) &&
(has_SSE4_2_ == other_as_x86->has_SSE4_2_) &&
(has_AVX_ == other_as_x86->has_AVX_) &&
@@ -238,8 +226,7 @@
}
uint32_t X86InstructionSetFeatures::AsBitmap() const {
- return (IsSmp() ? kSmpBitfield : 0) |
- (has_SSSE3_ ? kSsse3Bitfield : 0) |
+ return (has_SSSE3_ ? kSsse3Bitfield : 0) |
(has_SSE4_1_ ? kSse4_1Bitfield : 0) |
(has_SSE4_2_ ? kSse4_2Bitfield : 0) |
(has_AVX_ ? kAvxBitfield : 0) |
@@ -249,15 +236,10 @@
std::string X86InstructionSetFeatures::GetFeatureString() const {
std::string result;
- if (IsSmp()) {
- result += "smp";
- } else {
- result += "-smp";
- }
if (has_SSSE3_) {
- result += ",ssse3";
+ result += "ssse3";
} else {
- result += ",-ssse3";
+ result += "-ssse3";
}
if (has_SSE4_1_) {
result += ",sse4.1";
@@ -288,7 +270,7 @@
}
std::unique_ptr<const InstructionSetFeatures> X86InstructionSetFeatures::AddFeaturesFromSplitString(
- const bool smp, const std::vector<std::string>& features, bool x86_64,
+ const std::vector<std::string>& features, bool x86_64,
std::string* error_msg) const {
bool has_SSSE3 = has_SSSE3_;
bool has_SSE4_1 = has_SSE4_1_;
@@ -327,7 +309,7 @@
return nullptr;
}
}
- return Create(x86_64, smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
+ return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
}
} // namespace art
diff --git a/runtime/arch/x86/instruction_set_features_x86.h b/runtime/arch/x86/instruction_set_features_x86.h
index 672892e..eb8a710 100644
--- a/runtime/arch/x86/instruction_set_features_x86.h
+++ b/runtime/arch/x86/instruction_set_features_x86.h
@@ -69,18 +69,23 @@
protected:
// Parse a string of the form "ssse3" adding these to a new InstructionSetFeatures.
virtual std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE {
- return AddFeaturesFromSplitString(smp, features, false, error_msg);
+ return AddFeaturesFromSplitString(features, false, error_msg);
}
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
- bool x86_64, std::string* error_msg) const;
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
+ bool x86_64,
+ std::string* error_msg) const;
- X86InstructionSetFeatures(bool smp, bool has_SSSE3, bool has_SSE4_1, bool has_SSE4_2,
- bool has_AVX, bool has_AVX2, bool has_POPCNT)
- : InstructionSetFeatures(smp),
+ X86InstructionSetFeatures(bool has_SSSE3,
+ bool has_SSE4_1,
+ bool has_SSE4_2,
+ bool has_AVX,
+ bool has_AVX2,
+ bool has_POPCNT)
+ : InstructionSetFeatures(),
has_SSSE3_(has_SSSE3),
has_SSE4_1_(has_SSE4_1),
has_SSE4_2_(has_SSE4_2),
@@ -90,7 +95,6 @@
}
static X86FeaturesUniquePtr Create(bool x86_64,
- bool smp,
bool has_SSSE3,
bool has_SSE4_1,
bool has_SSE4_2,
@@ -101,13 +105,12 @@
private:
// Bitmap positions for encoding features as a bitmap.
enum {
- kSmpBitfield = 1,
- kSsse3Bitfield = 2,
- kSse4_1Bitfield = 4,
- kSse4_2Bitfield = 8,
- kAvxBitfield = 16,
- kAvx2Bitfield = 32,
- kPopCntBitfield = 64,
+ kSsse3Bitfield = 1 << 0,
+ kSse4_1Bitfield = 1 << 1,
+ kSse4_2Bitfield = 1 << 2,
+ kAvxBitfield = 1 << 3,
+ kAvx2Bitfield = 1 << 4,
+ kPopCntBitfield = 1 << 5,
};
const bool has_SSSE3_; // x86 128bit SIMD - Supplemental SSE.
diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc
index 9e154c6..7e6ad3e 100644
--- a/runtime/arch/x86/instruction_set_features_x86_test.cc
+++ b/runtime/arch/x86/instruction_set_features_x86_test.cc
@@ -27,9 +27,9 @@
ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_features->GetInstructionSet(), kX86);
EXPECT_TRUE(x86_features->Equals(x86_features.get()));
- EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_features->AsBitmap(), 1U);
+ EXPECT_EQ(x86_features->AsBitmap(), 0U);
}
TEST(X86InstructionSetFeaturesTest, X86FeaturesFromAtomVariant) {
@@ -40,9 +40,9 @@
ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_features->GetInstructionSet(), kX86);
EXPECT_TRUE(x86_features->Equals(x86_features.get()));
- EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_features->AsBitmap(), 3U);
+ EXPECT_EQ(x86_features->AsBitmap(), 1U);
// Build features for a 32-bit x86 default processor.
std::unique_ptr<const InstructionSetFeatures> x86_default_features(
@@ -50,9 +50,9 @@
ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86);
EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
- EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_default_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_default_features->AsBitmap(), 1U);
+ EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
// Build features for a 64-bit x86-64 atom processor.
std::unique_ptr<const InstructionSetFeatures> x86_64_features(
@@ -60,9 +60,9 @@
ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64);
EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
- EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_64_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_64_features->AsBitmap(), 3U);
+ EXPECT_EQ(x86_64_features->AsBitmap(), 1U);
EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
@@ -77,9 +77,9 @@
ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_features->GetInstructionSet(), kX86);
EXPECT_TRUE(x86_features->Equals(x86_features.get()));
- EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
x86_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_features->AsBitmap(), 79U);
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
// Build features for a 32-bit x86 default processor.
std::unique_ptr<const InstructionSetFeatures> x86_default_features(
@@ -87,9 +87,9 @@
ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86);
EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
- EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_default_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_default_features->AsBitmap(), 1U);
+ EXPECT_EQ(x86_default_features->AsBitmap(), 0U);
// Build features for a 64-bit x86-64 silvermont processor.
std::unique_ptr<const InstructionSetFeatures> x86_64_features(
@@ -97,9 +97,9 @@
ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64);
EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
- EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
x86_64_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_64_features->AsBitmap(), 79U);
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
diff --git a/runtime/arch/x86_64/instruction_set_features_x86_64.h b/runtime/arch/x86_64/instruction_set_features_x86_64.h
index bc0f708..83f4093 100644
--- a/runtime/arch/x86_64/instruction_set_features_x86_64.h
+++ b/runtime/arch/x86_64/instruction_set_features_x86_64.h
@@ -68,15 +68,19 @@
protected:
// Parse a string of the form "ssse3" adding these to a new InstructionSetFeatures.
std::unique_ptr<const InstructionSetFeatures>
- AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const OVERRIDE {
- return X86InstructionSetFeatures::AddFeaturesFromSplitString(smp, features, true, error_msg);
+ return X86InstructionSetFeatures::AddFeaturesFromSplitString(features, true, error_msg);
}
private:
- X86_64InstructionSetFeatures(bool smp, bool has_SSSE3, bool has_SSE4_1, bool has_SSE4_2,
- bool has_AVX, bool has_AVX2, bool has_POPCNT)
- : X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
+ X86_64InstructionSetFeatures(bool has_SSSE3,
+ bool has_SSE4_1,
+ bool has_SSE4_2,
+ bool has_AVX,
+ bool has_AVX2,
+ bool has_POPCNT)
+ : X86InstructionSetFeatures(has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
has_AVX2, has_POPCNT) {
}
diff --git a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
index f2b2cd8..3c2ceac 100644
--- a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
+++ b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc
@@ -27,9 +27,9 @@
ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64);
EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
- EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
+ EXPECT_STREQ("-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-popcnt",
x86_64_features->GetFeatureString().c_str());
- EXPECT_EQ(x86_64_features->AsBitmap(), 1U);
+ EXPECT_EQ(x86_64_features->AsBitmap(), 0U);
}
} // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b38508b..11dcc35 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -95,18 +95,20 @@
// This setter guarantees atomicity.
void AddAccessFlags(uint32_t flag) {
- uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed);
+ uint32_t old_access_flags;
uint32_t new_access_flags;
do {
+ old_access_flags = access_flags_.load(std::memory_order_relaxed);
new_access_flags = old_access_flags | flag;
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
// This setter guarantees atomicity.
void ClearAccessFlags(uint32_t flag) {
- uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed);
+ uint32_t old_access_flags;
uint32_t new_access_flags;
do {
+ old_access_flags = access_flags_.load(std::memory_order_relaxed);
new_access_flags = old_access_flags & ~flag;
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 1dca428..55b4306 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -26,7 +26,7 @@
// Headers for LogMessage::LogLine.
#ifdef ART_TARGET_ANDROID
-#include <android/log.h>
+#include <log/log.h>
#else
#include <sys/types.h>
#include <unistd.h>
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5b8d4e4..8586b78 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4280,9 +4280,10 @@
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 23u);
+
ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
- 2, image_pointer_size_);
+ 8, image_pointer_size_);
DCHECK_EQ(std::string(proxy_constructor->GetName()), "<init>");
// Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
// constructor method.
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index bf1d4ea..a3e5b55 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -705,7 +705,7 @@
QuickExceptionHandler::DumpFramesWithType(self, true);
}
- mirror::Throwable* pending_exception = nullptr;
+ ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index dccec9f..7b86339 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -58,7 +58,7 @@
bool measure_read_barrier_slow_path)
: GarbageCollector(heap,
name_prefix + (name_prefix.empty() ? "" : " ") +
- "concurrent copying + mark sweep"),
+ "concurrent copying"),
region_space_(nullptr), gc_barrier_(new Barrier(0)),
gc_mark_stack_(accounting::ObjectStack::Create("concurrent copying gc mark stack",
kDefaultGcMarkStackSize,
@@ -832,7 +832,6 @@
static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000; // 10 minutes.
bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
if (timed_out) {
- Runtime* runtime = Runtime::Current();
std::ostringstream ss;
ss << "Empty checkpoint timeout\n";
ss << "Barrier count " << barrier->GetCount(self) << "\n";
@@ -845,11 +844,11 @@
ss << "\n";
LOG(FATAL_WITHOUT_ABORT) << ss.str();
// Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
- // Avoid using ThreadList::Dump() because it is likely to get stuck as well.
+ // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
{
ReaderMutexLock mu0(self, *Locks::mutator_lock_);
MutexLock mu1(self, *Locks::thread_list_lock_);
- for (Thread* thread : runtime->GetThreadList()->GetList()) {
+ for (Thread* thread : thread_list->GetList()) {
uint32_t tid = thread->GetThreadId();
bool is_in_runnable_thread_ids =
std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) !=
@@ -862,7 +861,11 @@
}
}
}
- LOG(FATAL) << "Dumped runnable threads that haven't responded to empty checkpoint.";
+ LOG(FATAL_WITHOUT_ABORT)
+ << "Dumped runnable threads that haven't responded to empty checkpoint.";
+ // Now use ThreadList::Dump() to dump more threads, noting it may get stuck.
+ thread_list->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ LOG(FATAL) << "Dumped all threads.";
}
} else {
barrier->Increment(self, barrier_count);
@@ -2403,16 +2406,29 @@
}
}
-bool ConcurrentCopying::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) {
+bool ConcurrentCopying::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
+ bool do_atomic_update) {
mirror::Object* from_ref = field->AsMirrorPtr();
+ if (from_ref == nullptr) {
+ return true;
+ }
mirror::Object* to_ref = IsMarked(from_ref);
if (to_ref == nullptr) {
return false;
}
if (from_ref != to_ref) {
- QuasiAtomic::ThreadFenceRelease();
- field->Assign(to_ref);
- QuasiAtomic::ThreadFenceSequentiallyConsistent();
+ if (do_atomic_update) {
+ do {
+ if (field->AsMirrorPtr() != from_ref) {
+ // Concurrently overwritten by a mutator.
+ break;
+ }
+ } while (!field->CasWeakRelaxed(from_ref, to_ref));
+ } else {
+ QuasiAtomic::ThreadFenceRelease();
+ field->Assign(to_ref);
+ QuasiAtomic::ThreadFenceSequentiallyConsistent();
+ }
}
return true;
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 5b8a557..844bb45 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -183,7 +183,8 @@
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsMarkedInUnevacFromSpace(mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_);
- virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) OVERRIDE
+ virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
+ bool do_atomic_update) OVERRIDE
REQUIRES_SHARED(Locks::mutator_lock_);
void SweepSystemWeaks(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index 5b51399..0177e2a 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -187,7 +187,10 @@
// and will be used for reading system weaks while the GC is running.
virtual mirror::Object* IsMarked(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
- virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj)
+ // Returns true if the given heap reference is null or is already marked. If it's already marked,
+ // update the reference (uses a CAS if do_atomic_update is true. Otherwise, returns false.
+ virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Used by reference processor.
virtual void ProcessMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index ddcb6c0..85e6783 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -472,9 +472,15 @@
return mark_bitmap_->Test(object) ? object : nullptr;
}
-bool MarkCompact::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr) {
+bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr,
+ // MarkCompact does the GC in a pause. No CAS needed.
+ bool do_atomic_update ATTRIBUTE_UNUSED) {
// Side effect free since we call this before ever moving objects.
- return IsMarked(ref_ptr->AsMirrorPtr()) != nullptr;
+ mirror::Object* obj = ref_ptr->AsMirrorPtr();
+ if (obj == nullptr) {
+ return true;
+ }
+ return IsMarked(obj) != nullptr;
}
void MarkCompact::SweepSystemWeaks() {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 564f85b..6d52d5d 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -175,7 +175,8 @@
virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE
REQUIRES_SHARED(Locks::heap_bitmap_lock_)
REQUIRES(Locks::mutator_lock_);
- virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj) OVERRIDE
+ virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update) OVERRIDE
REQUIRES_SHARED(Locks::heap_bitmap_lock_)
REQUIRES(Locks::mutator_lock_);
void ForwardObject(mirror::Object* obj) REQUIRES(Locks::heap_bitmap_lock_,
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 06ed029..f00da73 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -390,8 +390,13 @@
}
}
-bool MarkSweep::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) {
- return IsMarked(ref->AsMirrorPtr());
+bool MarkSweep::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref,
+ bool do_atomic_update ATTRIBUTE_UNUSED) {
+ mirror::Object* obj = ref->AsMirrorPtr();
+ if (obj == nullptr) {
+ return true;
+ }
+ return IsMarked(obj);
}
class MarkSweep::MarkObjectSlowPath {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 02cf462..a6e2d61 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -188,7 +188,8 @@
void VerifyIsLive(const mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
- virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) OVERRIDE
+ virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref,
+ bool do_atomic_update) OVERRIDE
REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index a815b83..cb9e7e2 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -89,7 +89,7 @@
SemiSpace::SemiSpace(Heap* heap, bool generational, const std::string& name_prefix)
: GarbageCollector(heap,
- name_prefix + (name_prefix.empty() ? "" : " ") + "marksweep + semispace"),
+ name_prefix + (name_prefix.empty() ? "" : " ") + "semispace"),
mark_stack_(nullptr),
is_large_object_space_immune_(false),
to_space_(nullptr),
@@ -765,8 +765,13 @@
return mark_bitmap_->Test(obj) ? obj : nullptr;
}
-bool SemiSpace::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) {
+bool SemiSpace::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object,
+ // SemiSpace does the GC in a pause. No CAS needed.
+ bool do_atomic_update ATTRIBUTE_UNUSED) {
mirror::Object* obj = object->AsMirrorPtr();
+ if (obj == nullptr) {
+ return true;
+ }
mirror::Object* new_obj = IsMarked(obj);
if (new_obj == nullptr) {
return false;
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 4cebcc3..52b5e5f 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -166,7 +166,8 @@
REQUIRES(Locks::mutator_lock_)
REQUIRES_SHARED(Locks::heap_bitmap_lock_);
- virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) OVERRIDE
+ virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object,
+ bool do_atomic_update) OVERRIDE
REQUIRES(Locks::mutator_lock_)
REQUIRES_SHARED(Locks::heap_bitmap_lock_);
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 081be96..c154836 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -203,7 +203,9 @@
DCHECK(klass != nullptr);
DCHECK(klass->IsTypeOfReferenceClass());
mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr();
- if (referent->AsMirrorPtr() != nullptr && !collector->IsMarkedHeapReference(referent)) {
+ // do_atomic_update needs to be true because this happens outside of the reference processing
+ // phase.
+ if (!collector->IsNullOrMarkedHeapReference(referent, /*do_atomic_update*/true)) {
Thread* self = Thread::Current();
// TODO: Remove these locks, and use atomic stacks for storing references?
// We need to check that the references haven't already been enqueued since we can end up
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index a0eb197..734caea 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -129,8 +129,9 @@
while (!IsEmpty()) {
ObjPtr<mirror::Reference> ref = DequeuePendingReference();
mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
- if (referent_addr->AsMirrorPtr() != nullptr &&
- !collector->IsMarkedHeapReference(referent_addr)) {
+ // do_atomic_update is false because this happens during the reference processing phase where
+ // Reference.clear() would block.
+ if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) {
// Referent is white, clear it.
if (Runtime::Current()->IsActiveTransaction()) {
ref->ClearReferent<true>();
@@ -147,8 +148,9 @@
while (!IsEmpty()) {
ObjPtr<mirror::FinalizerReference> ref = DequeuePendingReference()->AsFinalizerReference();
mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
- if (referent_addr->AsMirrorPtr() != nullptr &&
- !collector->IsMarkedHeapReference(referent_addr)) {
+ // do_atomic_update is false because this happens during the reference processing phase where
+ // Reference.clear() would block.
+ if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) {
ObjPtr<mirror::Object> forward_address = collector->MarkObject(referent_addr->AsMirrorPtr());
// Move the updated referent to the zombie field.
if (Runtime::Current()->IsActiveTransaction()) {
diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S
index 6ffbd3f..388fc8d 100644
--- a/runtime/interpreter/mterp/arm64/footer.S
+++ b/runtime/interpreter/mterp/arm64/footer.S
@@ -267,13 +267,7 @@
b MterpDone
MterpReturn:
ldr x2, [xFP, #OFF_FP_RESULT_REGISTER]
- ldr lr, [xSELF, #THREAD_FLAGS_OFFSET]
str x0, [x2]
- mov x0, xSELF
- ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
- b.eq check2
- bl MterpSuspendCheck // (self)
-check2:
mov x0, #1 // signal return to caller.
MterpDone:
/*
diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S
index 64772c8..312fa9c 100644
--- a/runtime/interpreter/mterp/mips64/footer.S
+++ b/runtime/interpreter/mterp/mips64/footer.S
@@ -222,13 +222,7 @@
*/
MterpReturn:
ld a2, OFF_FP_RESULT_REGISTER(rFP)
- lw ra, THREAD_FLAGS_OFFSET(rSELF)
sd a0, 0(a2)
- move a0, rSELF
- and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
- beqzc ra, check2
- jal MterpSuspendCheck # (self)
-check2:
li v0, 1 # signal return to caller.
MterpDone:
/*
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 34d99a8..681790d 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -7246,13 +7246,7 @@
b MterpDone
MterpReturn:
ldr x2, [xFP, #OFF_FP_RESULT_REGISTER]
- ldr lr, [xSELF, #THREAD_FLAGS_OFFSET]
str x0, [x2]
- mov x0, xSELF
- ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
- b.eq check2
- bl MterpSuspendCheck // (self)
-check2:
mov x0, #1 // signal return to caller.
MterpDone:
/*
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 037787f..bf09666 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -12293,13 +12293,7 @@
*/
MterpReturn:
ld a2, OFF_FP_RESULT_REGISTER(rFP)
- lw ra, THREAD_FLAGS_OFFSET(rSELF)
sd a0, 0(a2)
- move a0, rSELF
- and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST
- beqzc ra, check2
- jal MterpSuspendCheck # (self)
-check2:
li v0, 1 # signal return to caller.
MterpDone:
/*
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index d8869ad..b13d565 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -47,8 +47,16 @@
* JDWP-handshake, etc...
*/
-#define kJdwpControlName "\0jdwp-control"
-#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
+static constexpr char kJdwpControlName[] = "\0jdwp-control";
+static constexpr size_t kJdwpControlNameLen = sizeof(kJdwpControlName) - 1;
+/* This timeout is for connect/send with control socket. In practice, the
+ * connect should never timeout since it's just connect to a local unix domain
+ * socket. But in case adb is buggy and doesn't respond to any connection, the
+ * connect will block. For send, actually it would never block since we only send
+ * several bytes and the kernel buffer is big enough to accept it. 10 seconds
+ * should be far enough.
+ */
+static constexpr int kControlSockSendTimeout = 10;
namespace art {
@@ -224,6 +232,10 @@
PLOG(ERROR) << "Could not create ADB control socket";
return false;
}
+ struct timeval timeout;
+ timeout.tv_sec = kControlSockSendTimeout;
+ timeout.tv_usec = 0;
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
{
MutexLock mu(Thread::Current(), state_lock_);
control_sock_ = sock;
diff --git a/runtime/mirror/object_reference-inl.h b/runtime/mirror/object_reference-inl.h
index e70b936..22fb83c 100644
--- a/runtime/mirror/object_reference-inl.h
+++ b/runtime/mirror/object_reference-inl.h
@@ -34,6 +34,15 @@
return HeapReference<MirrorType>(ptr.Ptr());
}
+template<class MirrorType>
+bool HeapReference<MirrorType>::CasWeakRelaxed(MirrorType* expected_ptr, MirrorType* new_ptr) {
+ HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_ptr));
+ HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_ptr));
+ Atomic<uint32_t>* atomic_reference = reinterpret_cast<Atomic<uint32_t>*>(&this->reference_);
+ return atomic_reference->CompareExchangeWeakRelaxed(expected_ref.reference_,
+ new_ref.reference_);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 71f34c6..a96a120 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -94,6 +94,9 @@
static HeapReference<MirrorType> FromObjPtr(ObjPtr<MirrorType> ptr)
REQUIRES_SHARED(Locks::mutator_lock_);
+ bool CasWeakRelaxed(MirrorType* old_ptr, MirrorType* new_ptr)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
explicit HeapReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_)
: ObjectReference<kPoisonHeapReferences, MirrorType>(mirror_ptr) {}
diff --git a/runtime/oat.cc b/runtime/oat.cc
index cebe765..1a07cdc 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -469,10 +469,6 @@
return IsKeyEnabled(OatHeader::kPicKey);
}
-bool OatHeader::HasPatchInfo() const {
- return IsKeyEnabled(OatHeader::kHasPatchInfoKey);
-}
-
bool OatHeader::IsDebuggable() const {
return IsKeyEnabled(OatHeader::kDebuggableKey);
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 0f4cbbb..1fd906d 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,13 +32,12 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '9', '3', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '9', '4', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
- static constexpr const char* kHasPatchInfoKey = "has-patch-info";
static constexpr const char* kDebuggableKey = "debuggable";
static constexpr const char* kNativeDebuggableKey = "native-debuggable";
static constexpr const char* kCompilerFilter = "compiler-filter";
@@ -110,7 +109,6 @@
size_t GetHeaderSize() const;
bool IsPic() const;
- bool HasPatchInfo() const;
bool IsDebuggable() const;
bool IsNativeDebuggable() const;
CompilerFilter::Filter GetCompilerFilter() const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 0bf7136..38df427 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -710,7 +710,7 @@
return false;
}
#ifdef ART_TARGET_ANDROID
- android_dlextinfo extinfo;
+ android_dlextinfo extinfo = {};
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | // Force-load, don't reuse handle
// (open oat files multiple
// times).
@@ -1438,10 +1438,6 @@
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}
-bool OatFile::HasPatchInfo() const {
- return GetOatHeader().HasPatchInfo();
-}
-
bool OatFile::IsPic() const {
return GetOatHeader().IsPic();
// TODO: Check against oat_patches. b/18144996
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 29add5b..62d99fb 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -104,8 +104,6 @@
return is_executable_;
}
- bool HasPatchInfo() const;
-
bool IsPic() const;
// Indicates whether the oat file was compiled with full debugging capability.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index ee7cf9d..f12a5e7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -203,10 +203,6 @@
case kDex2OatForRelocation:
case kDex2OatForFilter:
return GenerateOatFile(error_msg);
-
- case kPatchoatForRelocation: {
- return RelocateOatFile(info.Filename(), error_msg);
- }
}
UNREACHABLE();
}
@@ -420,58 +416,6 @@
}
OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* error_msg) {
- CHECK(error_msg != nullptr);
-
- if (input_file == nullptr) {
- *error_msg = "Patching of oat file for dex location " + dex_location_
- + " not attempted because the input file name could not be determined.";
- return kUpdateNotAttempted;
- }
- const std::string& input_file_name = *input_file;
-
- if (oat_.Filename() == nullptr) {
- *error_msg = "Patching of oat file for dex location " + dex_location_
- + " not attempted because the oat file name could not be determined.";
- return kUpdateNotAttempted;
- }
- const std::string& oat_file_name = *oat_.Filename();
-
- const ImageInfo* image_info = GetImageInfo();
- Runtime* runtime = Runtime::Current();
- if (image_info == nullptr) {
- *error_msg = "Patching of oat file " + oat_file_name
- + " not attempted because no image location was found.";
- return kUpdateNotAttempted;
- }
-
- if (!runtime->IsDex2OatEnabled()) {
- *error_msg = "Patching of oat file " + oat_file_name
- + " not attempted because dex2oat is disabled";
- return kUpdateNotAttempted;
- }
-
- std::vector<std::string> argv;
- argv.push_back(runtime->GetPatchoatExecutable());
- argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(isa_)));
- argv.push_back("--input-oat-file=" + input_file_name);
- argv.push_back("--output-oat-file=" + oat_file_name);
- argv.push_back("--patched-image-location=" + image_info->location);
-
- std::string command_line(android::base::Join(argv, ' '));
- if (!Exec(argv, error_msg)) {
- // Manually delete the file. This ensures there is no garbage left over if
- // the process unexpectedly died.
- unlink(oat_file_name.c_str());
- return kUpdateFailed;
- }
-
- // Mark that the oat file has changed and we should try to reload.
- oat_.Reset();
- return kUpdateSucceeded;
-}
-
-OatFileAssistant::ResultOfAttemptToUpdate
OatFileAssistant::GenerateOatFile(std::string* error_msg) {
CHECK(error_msg != nullptr);
@@ -852,13 +796,7 @@
return kNoDexOptNeeded;
}
- if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) {
- return kPatchoatForRelocation;
- }
-
if (oat_file_assistant_->HasOriginalDexFiles()) {
- // Run dex2oat for relocation if we didn't have the patch info necessary
- // to use patchoat.
if (filter_okay && Status() == kOatRelocationOutOfDate) {
return kDex2OatForRelocation;
}
@@ -921,11 +859,6 @@
return (file != nullptr && file->IsExecutable());
}
-bool OatFileAssistant::OatFileInfo::HasPatchInfo() {
- const OatFile* file = GetFile();
- return (file != nullptr && file->HasPatchInfo());
-}
-
void OatFileAssistant::OatFileInfo::Reset() {
load_attempted_ = false;
file_.reset();
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index bed1edc..588a698 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -67,14 +67,9 @@
kDex2OatForFilter = 3,
// dex2oat should be run to update the apk/jar because the existing code
- // is not relocated to match the boot image and does not have the
- // necessary patch information to use patchoat.
+ // is not relocated to match the boot image.
// Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION
kDex2OatForRelocation = 4,
-
- // patchoat should be run to update the apk/jar.
- // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION
- kPatchoatForRelocation = 5,
};
enum OatStatus {
@@ -237,15 +232,6 @@
// Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
- // Generates the oat file by relocation from the named input file.
- // This does not check the current status before attempting to relocate the
- // oat file.
- //
- // If the result is not kUpdateSucceeded, the value of error_msg will be set
- // to a string describing why there was a failure or the update was not
- // attempted. error_msg must not be null.
- ResultOfAttemptToUpdate RelocateOatFile(const std::string* input_file, std::string* error_msg);
-
// Generate the oat file from the dex file using the current runtime
// compiler options.
// This does not check the current status before attempting to generate the
@@ -328,8 +314,6 @@
// given target_compilation_filter.
// profile_changed should be true to indicate the profile has recently
// changed for this dex location.
- // If patchoat is needed, this function will return the kPatchOatNeeded
- // status, not the kSelfPatchOatNeeded status.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed);
@@ -341,9 +325,6 @@
// Returns true if the file is opened executable.
bool IsExecutable();
- // Returns true if the file has patch info required to run patchoat.
- bool HasPatchInfo();
-
// Clear any cached information about the file that depends on the
// contents of the file. This does not reset the provided filename.
void Reset();
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 26dbaab..afa804c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -85,7 +85,6 @@
CompilerFilter::Filter filter,
bool relocate,
bool pic,
- bool with_patch_info,
bool with_alternate_image) {
std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
@@ -111,10 +110,6 @@
args.push_back("--compile-pic");
}
- if (with_patch_info) {
- args.push_back("--include-patch-information");
- }
-
std::string image_location = GetImageLocation();
if (with_alternate_image) {
args.push_back("--boot-image=" + GetImageLocation2());
@@ -139,7 +134,6 @@
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_EQ(pic, odex_file->IsPic());
- EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
std::unique_ptr<ImageHeader> image_header(
@@ -181,7 +175,6 @@
filter,
/*relocate*/false,
/*pic*/false,
- /*with_patch_info*/true,
/*with_alternate_image*/false);
}
@@ -193,21 +186,6 @@
filter,
/*relocate*/false,
/*pic*/true,
- /*with_patch_info*/false,
- /*with_alternate_image*/false);
- }
-
- // Generate a non-PIC odex file without patch information for the purposes
- // of test. The generated odex file will be un-relocated.
- void GenerateNoPatchOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- odex_location,
- filter,
- /*relocate*/false,
- /*pic*/false,
- /*with_patch_info*/false,
/*with_alternate_image*/false);
}
@@ -216,7 +194,6 @@
CompilerFilter::Filter filter,
bool relocate,
bool pic,
- bool with_patch_info,
bool with_alternate_image) {
std::string oat_location;
std::string error_msg;
@@ -227,7 +204,6 @@
filter,
relocate,
pic,
- with_patch_info,
with_alternate_image);
}
@@ -237,7 +213,6 @@
filter,
/*relocate*/true,
/*pic*/false,
- /*with_patch_info*/false,
/*with_alternate_image*/false);
}
@@ -519,7 +494,6 @@
CompilerFilter::kSpeed,
/*relocate*/true,
/*pic*/false,
- /*with_patch_info*/false,
/*with_alternate_image*/true);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
@@ -548,7 +522,6 @@
CompilerFilter::kVerifyAtRuntime,
/*relocate*/true,
/*pic*/false,
- /*with_patch_info*/false,
/*with_alternate_image*/true);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
@@ -564,7 +537,6 @@
}
// Case: We have a DEX file and an ODEX file, but no OAT file.
-// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
@@ -578,7 +550,7 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -591,15 +563,14 @@
ASSERT_TRUE(oat_file.get() != nullptr);
}
-// Case: We have a stripped DEX file and an ODEX file, but no OAT file.
-// Expect: The status is kPatchOatNeeded
+// Case: We have a stripped DEX file and a PIC ODEX file, but no OAT file.
TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
// Strip the dex file
Copy(GetStrippedDexSrc1(), dex_location);
@@ -607,28 +578,14 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
- // Make the oat file up to date.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
- EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
// Verify we can load the dex files from it.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -638,8 +595,7 @@
EXPECT_EQ(1u, dex_files.size());
}
-// Case: We have a stripped DEX file, an ODEX file, and an out-of-date OAT file.
-// Expect: The status is kPatchOatNeeded.
+// Case: We have a stripped DEX file, a PIC ODEX file, and an out-of-date OAT file.
TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
@@ -650,7 +606,7 @@
// Create the odex file
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
// Strip the dex file.
Copy(GetStrippedDexSrc1(), dex_location);
@@ -660,30 +616,14 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped.
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
- EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
- // Make the oat file up to date.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped.
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
- EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Verify we can load the dex files from it.
@@ -732,90 +672,9 @@
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
}
-// Case: We have a DEX file, no ODEX file and an OAT file that needs
-// relocation.
-// Expect: The status is kSelfPatchOatNeeded.
-TEST_F(OatFileAssistantTest, SelfRelocation) {
- std::string dex_location = GetScratchDir() + "/SelfRelocation.jar";
- std::string oat_location = GetOdexDir() + "/SelfRelocation.oat";
-
- // Create the dex and odex files
- Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
-
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
- EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
- EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-
- // Make the oat file up to date.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
-
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
- EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
- EXPECT_EQ(1u, dex_files.size());
-}
-
-// Case: We have a DEX file, no ODEX file and an OAT file that needs
-// relocation but doesn't have patch info.
-// Expect: The status is kDex2OatNeeded, because we can't run patchoat.
-TEST_F(OatFileAssistantTest, NoSelfRelocation) {
- std::string dex_location = GetScratchDir() + "/NoSelfRelocation.jar";
- std::string oat_location = GetOdexDir() + "/NoSelfRelocation.oat";
-
- // Create the dex and odex files
- Copy(GetDexSrc1(), dex_location);
- GenerateNoPatchOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
-
- EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- // Make the oat file up to date.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
- EXPECT_EQ(1u, dex_files.size());
-}
-
// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
// OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
+// Expect: It shouldn't crash.
TEST_F(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -833,10 +692,10 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(), kRuntimeISA, true);
- // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation
+ // kDex2OatForRelocation is expected rather than -kDex2OatForRelocation
// based on the assumption that the oat location is more up-to-date than the odex
// location, even if they both need relocation.
- EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -1285,7 +1144,6 @@
{OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"},
{OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"},
{OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"},
- {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"}
};
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 0f9fbb2..b757b21 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -21,8 +21,12 @@
"object_tagging.cc",
"OpenjdkJvmTi.cc",
"ti_class.cc",
+ "ti_field.cc",
"ti_heap.cc",
"ti_method.cc",
+ "ti_monitor.cc",
+ "ti_object.cc",
+ "ti_properties.cc",
"ti_stack.cc",
"ti_redefine.cc",
"transform.cc"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5f97b60..c52dd76 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -47,8 +47,12 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_class.h"
+#include "ti_field.h"
#include "ti_heap.h"
#include "ti_method.h"
+#include "ti_monitor.h"
+#include "ti_object.h"
+#include "ti_properties.h"
#include "ti_redefine.h"
#include "ti_stack.h"
#include "transform.h"
@@ -537,7 +541,7 @@
}
static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassStatus(env, klass, status_ptr);
}
static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) {
@@ -545,28 +549,28 @@
}
static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr);
}
static jvmtiError GetClassMethods(jvmtiEnv* env,
jclass klass,
jint* method_count_ptr,
jmethodID** methods_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr);
}
static jvmtiError GetClassFields(jvmtiEnv* env,
jclass klass,
jint* field_count_ptr,
jfieldID** fields_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr);
}
static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
jclass klass,
jint* interface_count_ptr,
jclass** interfaces_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr);
}
static jvmtiError GetClassVersionNumbers(jvmtiEnv* env,
@@ -585,23 +589,23 @@
}
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::IsInterface(env, klass, is_interface_ptr);
}
static jvmtiError IsArrayClass(jvmtiEnv* env,
jclass klass,
jboolean* is_array_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr);
}
static jvmtiError IsModifiableClass(jvmtiEnv* env,
jclass klass,
jboolean* is_modifiable_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return Redefiner::IsModifiableClass(env, klass, is_modifiable_class_ptr);
}
static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassLoader(env, klass, classloader_ptr);
}
static jvmtiError GetSourceDebugExtension(jvmtiEnv* env,
@@ -621,11 +625,11 @@
}
static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ObjectUtil::GetObjectSize(env, object, size_ptr);
}
static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ObjectUtil::GetObjectHashCode(env, object, hash_code_ptr);
}
static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
@@ -640,28 +644,28 @@
char** name_ptr,
char** signature_ptr,
char** generic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldName(env, klass, field, name_ptr, signature_ptr, generic_ptr);
}
static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
jclass klass,
jfieldID field,
jclass* declaring_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr);
}
static jvmtiError GetFieldModifiers(jvmtiEnv* env,
jclass klass,
jfieldID field,
jint* modifiers_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr);
}
static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
jclass klass,
jfieldID field,
jboolean* is_synthetic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr);
}
static jvmtiError GetMethodName(jvmtiEnv* env,
@@ -687,13 +691,13 @@
static jvmtiError GetMaxLocals(jvmtiEnv* env,
jmethodID method,
jint* max_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMaxLocals(env, method, max_ptr);
}
static jvmtiError GetArgumentsSize(jvmtiEnv* env,
jmethodID method,
jint* size_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetArgumentsSize(env, method, size_ptr);
}
static jvmtiError GetLineNumberTable(jvmtiEnv* env,
@@ -707,7 +711,7 @@
jmethodID method,
jlocation* start_location_ptr,
jlocation* end_location_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodLocation(env, method, start_location_ptr, end_location_ptr);
}
static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
@@ -725,15 +729,15 @@
}
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::IsMethodNative(env, method, is_native_ptr);
}
static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr);
}
static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::IsMethodObsolete(env, method, is_obsolete_ptr);
}
static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix) {
@@ -745,31 +749,31 @@
}
static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::CreateRawMonitor(env, name, monitor_ptr);
}
static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::DestroyRawMonitor(env, monitor);
}
static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::RawMonitorEnter(env, monitor);
}
static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::RawMonitorExit(env, monitor);
}
static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::RawMonitorWait(env, monitor, millis);
}
static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::RawMonitorNotify(env, monitor);
}
static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor) {
- return ERR(NOT_IMPLEMENTED);
+ return MonitorUtil::RawMonitorNotifyAll(env, monitor);
}
static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
@@ -838,19 +842,28 @@
static jvmtiError GetExtensionFunctions(jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionFunctionInfo** extensions) {
- return ERR(NOT_IMPLEMENTED);
+ // We do not have any extension functions.
+ *extension_count_ptr = 0;
+ *extensions = nullptr;
+
+ return ERR(NONE);
}
static jvmtiError GetExtensionEvents(jvmtiEnv* env,
jint* extension_count_ptr,
jvmtiExtensionEventInfo** extensions) {
- return ERR(NOT_IMPLEMENTED);
+ // We do not have any extension events.
+ *extension_count_ptr = 0;
+ *extensions = nullptr;
+
+ return ERR(NONE);
}
static jvmtiError SetExtensionEventCallback(jvmtiEnv* env,
jint extension_event_index,
jvmtiExtensionEvent callback) {
- return ERR(NOT_IMPLEMENTED);
+ // We do not have any extension events, so any call is illegal.
+ return ERR(ILLEGAL_ARGUMENT);
}
static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) {
@@ -1023,15 +1036,15 @@
}
static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return PropertiesUtil::GetSystemProperties(env, count_ptr, property_ptr);
}
static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return PropertiesUtil::GetSystemProperty(env, property, value_ptr);
}
static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value) {
- return ERR(NOT_IMPLEMENTED);
+ return PropertiesUtil::SetSystemProperty(env, property, value);
}
static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
@@ -1066,8 +1079,15 @@
ENSURE_NON_NULL(name_ptr);
switch (error) {
#define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : do { \
- *name_ptr = const_cast<char*>("JVMTI_ERROR_"#e); \
- return OK; \
+ jvmtiError res = CopyString(env, \
+ "JVMTI_ERROR_"#e, \
+ reinterpret_cast<unsigned char**>(name_ptr)); \
+ if (res != OK) { \
+ *name_ptr = nullptr; \
+ return res; \
+ } else { \
+ return OK; \
+ } \
} while (false)
ERROR_CASE(NONE);
ERROR_CASE(INVALID_THREAD);
@@ -1119,8 +1139,15 @@
ERROR_CASE(INVALID_ENVIRONMENT);
#undef ERROR_CASE
default: {
- *name_ptr = const_cast<char*>("JVMTI_ERROR_UNKNOWN");
- return ERR(ILLEGAL_ARGUMENT);
+ jvmtiError res = CopyString(env,
+ "JVMTI_ERROR_UNKNOWN",
+ reinterpret_cast<unsigned char**>(name_ptr));
+ if (res != OK) {
+ *name_ptr = nullptr;
+ return res;
+ } else {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
}
}
}
@@ -1165,7 +1192,7 @@
reinterpret_cast<uint8_t*>(dex_file),
&error);
if (ret != OK) {
- LOG(ERROR) << "FAILURE TO REDEFINE " << error;
+ LOG(WARNING) << "FAILURE TO REDEFINE " << error;
}
return ret;
}
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 48b29a3..5eadc5a 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -105,9 +105,10 @@
using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+template <typename T>
ALWAYS_INLINE
-static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* mem) {
- return JvmtiUniquePtr(mem, JvmtiDeleter(env));
+static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
+ return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env));
}
ALWAYS_INLINE
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index de2076a..0d1704c 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -32,11 +32,146 @@
#include "ti_class.h"
#include "art_jvmti.h"
+#include "jni_internal.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
namespace openjdkjvmti {
+jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
+ jclass jklass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (field_count_ptr == nullptr || fields_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields();
+ art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields();
+ size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
+
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jfieldID), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jfieldID* field_array = reinterpret_cast<jfieldID*>(out_ptr);
+
+ size_t array_idx = 0;
+ for (art::ArtField& field : sfields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+ for (art::ArtField& field : ifields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+
+ *field_count_ptr = static_cast<jint>(array_size);
+ *fields_ptr = field_array;
+
+ return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env,
+ jclass jklass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (method_count_ptr == nullptr || methods_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ size_t array_size = klass->NumDeclaredVirtualMethods() + klass->NumDirectMethods();
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jmethodID), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jmethodID* method_array = reinterpret_cast<jmethodID*>(out_ptr);
+
+ if (art::kIsDebugBuild) {
+ size_t count = 0;
+ for (auto& m ATTRIBUTE_UNUSED : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ count++;
+ }
+ CHECK_EQ(count, klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods());
+ }
+
+ size_t array_idx = 0;
+ for (auto& m : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ method_array[array_idx] = art::jni::EncodeArtMethod(&m);
+ ++array_idx;
+ }
+
+ *method_count_ptr = static_cast<jint>(array_size);
+ *methods_ptr = method_array;
+
+ return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetImplementedInterfaces(jvmtiEnv* env,
+ jclass jklass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (interface_count_ptr == nullptr || interfaces_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ // Need to handle array specifically. Arrays implement Serializable and Cloneable, but the
+ // spec says these should not be reported.
+ if (klass->IsArrayClass()) {
+ *interface_count_ptr = 0;
+ *interfaces_ptr = nullptr; // TODO: Should we allocate a dummy here?
+ return ERR(NONE);
+ }
+
+ size_t array_size = klass->NumDirectInterfaces();
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jclass), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jclass* interface_array = reinterpret_cast<jclass*>(out_ptr);
+
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
+
+ for (uint32_t idx = 0; idx != array_size; ++idx) {
+ art::ObjPtr<art::mirror::Class> inf_klass =
+ art::mirror::Class::ResolveDirectInterface(soa.Self(), h_klass, idx);
+ if (inf_klass == nullptr) {
+ soa.Self()->ClearException();
+ env->Deallocate(out_ptr);
+ // TODO: What is the right error code here?
+ return ERR(INTERNAL);
+ }
+ interface_array[idx] = soa.AddLocalReference<jclass>(inf_klass);
+ }
+
+ *interface_count_ptr = static_cast<jint>(array_size);
+ *interfaces_ptr = interface_array;
+
+ return ERR(NONE);
+}
+
jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
jclass jklass,
char** signature_ptr,
@@ -70,4 +205,127 @@
return ERR(NONE);
}
+jvmtiError ClassUtil::GetClassStatus(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jint* status_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (status_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ if (klass->IsArrayClass()) {
+ *status_ptr = JVMTI_CLASS_STATUS_ARRAY;
+ } else if (klass->IsPrimitive()) {
+ *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
+ } else {
+ *status_ptr = JVMTI_CLASS_STATUS_VERIFIED; // All loaded classes are structurally verified.
+ // This is finicky. If there's an error, we'll say it wasn't prepared.
+ if (klass->IsResolved()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_PREPARED;
+ }
+ if (klass->IsInitialized()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_INITIALIZED;
+ }
+ // Technically the class may be erroneous for other reasons, but we do not have enough info.
+ if (klass->IsErroneous()) {
+ *status_ptr |= JVMTI_CLASS_STATUS_ERROR;
+ }
+ }
+
+ return ERR(NONE);
+}
+
+template <typename T>
+static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (is_t_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ *is_t_ptr = test(klass) ? JNI_TRUE : JNI_FALSE;
+ return ERR(NONE);
+}
+
+jvmtiError ClassUtil::IsInterface(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jboolean* is_interface_ptr) {
+ auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return klass->IsInterface();
+ };
+ return ClassIsT(jklass, test, is_interface_ptr);
+}
+
+jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jboolean* is_array_class_ptr) {
+ auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return klass->IsArrayClass();
+ };
+ return ClassIsT(jklass, test, is_array_class_ptr);
+}
+
+// Keep this in sync with Class.getModifiers().
+static uint32_t ClassGetModifiers(art::Thread* self, art::ObjPtr<art::mirror::Class> klass)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (klass->IsArrayClass()) {
+ uint32_t component_modifiers = ClassGetModifiers(self, klass->GetComponentType());
+ if ((component_modifiers & art::kAccInterface) != 0) {
+ component_modifiers &= ~(art::kAccInterface | art::kAccStatic);
+ }
+ return art::kAccAbstract | art::kAccFinal | component_modifiers;
+ }
+
+ uint32_t modifiers = klass->GetAccessFlags() & art::kAccJavaFlagsMask;
+
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
+ return art::mirror::Class::GetInnerClassFlags(h_klass, modifiers);
+}
+
+jvmtiError ClassUtil::GetClassModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jint* modifiers_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ *modifiers_ptr = ClassGetModifiers(soa.Self(), klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jobject* classloader_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (classloader_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ *classloader_ptr = soa.AddLocalReference<jobject>(klass->GetClassLoader());
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index caa77d4..577fc8e 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -39,10 +39,34 @@
class ClassUtil {
public:
+ static jvmtiError GetClassFields(jvmtiEnv* env,
+ jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr);
+
+ static jvmtiError GetClassMethods(jvmtiEnv* env,
+ jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr);
+
+ static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
+ jclass klass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr);
+
+ static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr);
+
static jvmtiError GetClassSignature(jvmtiEnv* env,
jclass klass,
char** signature_ptr,
char** generic_ptr);
+
+ static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr);
+
+ static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr);
+
+ static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
+ static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc
new file mode 100644
index 0000000..a762830
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -0,0 +1,171 @@
+/* Copyright (C) 2016 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_field.h"
+
+#include "art_jvmti.h"
+#include "art_field-inl.h"
+#include "base/enums.h"
+#include "jni_internal.h"
+#include "modifiers.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+// Note: For all these functions, we could do a check that the field actually belongs to the given
+// class. But the spec seems to assume a certain encoding of the field ID, and so doesn't
+// specify any errors.
+
+jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+
+ JvmtiUniquePtr name_copy;
+ if (name_ptr != nullptr) {
+ const char* field_name = art_field->GetName();
+ if (field_name == nullptr) {
+ field_name = "<error>";
+ }
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, field_name, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ name_copy = MakeJvmtiUniquePtr(env, tmp);
+ *name_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ JvmtiUniquePtr signature_copy;
+ if (signature_ptr != nullptr) {
+ const char* sig = art_field->GetTypeDescriptor();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, sig, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ signature_copy = MakeJvmtiUniquePtr(env, tmp);
+ *signature_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ // TODO: Support generic signature.
+ if (generic_ptr != nullptr) {
+ *generic_ptr = nullptr;
+ }
+
+ // Everything is fine, release the buffers.
+ name_copy.release();
+ signature_copy.release();
+
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (declaring_class_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ art::ObjPtr<art::mirror::Class> field_klass = art_field->GetDeclaringClass();
+
+ *declaring_class_ptr = soa.AddLocalReference<jclass>(field_klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ // Note: Keep this code in sync with Field.getModifiers.
+ uint32_t modifiers = art_field->GetAccessFlags() & 0xFFFF;
+
+ *modifiers_ptr = modifiers;
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::IsFieldSynthetic(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (is_synthetic_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ uint32_t modifiers = art_field->GetAccessFlags();
+
+ *is_synthetic_ptr = ((modifiers & art::kAccSynthetic) != 0) ? JNI_TRUE : JNI_FALSE;
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.h b/runtime/openjdkjvmti/ti_field.h
new file mode 100644
index 0000000..9a29f81
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2016 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_FIELD_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class FieldUtil {
+ public:
+ static jvmtiError GetFieldName(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr);
+
+ static jvmtiError GetFieldModifiers(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr);
+
+ static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index a0a0923..2ddd64a 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -37,9 +37,68 @@
#include "jni_internal.h"
#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
namespace openjdkjvmti {
+jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* size_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // This isn't specified as an error case, so return 0.
+ *size_ptr = 0;
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *size_ptr = art_method->GetCodeItem()->ins_size_;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* max_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (max_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // This isn't specified as an error case, so return 0.
+ *max_ptr = 0;
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *max_ptr = art_method->GetCodeItem()->registers_size_;
+
+ return ERR(NONE);
+}
+
jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
jmethodID method,
char** name_ptr,
@@ -106,6 +165,38 @@
return ERR(NONE);
}
+jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // This isn't specified as an error case, so return 0/0.
+ *start_location_ptr = 0;
+ *end_location_ptr = 0;
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *start_location_ptr = 0;
+ *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
+
+ return ERR(NONE);
+}
+
jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* modifiers_ptr) {
@@ -189,4 +280,43 @@
return ERR(NONE);
}
+template <typename T>
+static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ T test,
+ jboolean* is_t_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ if (is_t_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsNative();
+ };
+ return IsMethodT(env, m, test, is_native_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsObsolete();
+ };
+ return IsMethodT(env, m, test, is_obsolete_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsSynthetic();
+ };
+ return IsMethodT(env, m, test, is_synthetic_ptr);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index fb2fbb2..e5c1705 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -39,6 +39,10 @@
class MethodUtil {
public:
+ static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
+
+ static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
+
static jvmtiError GetMethodName(jvmtiEnv* env,
jmethodID method,
char** name_ptr,
@@ -49,6 +53,11 @@
jmethodID method,
jclass* declaring_class_ptr);
+ static jvmtiError GetMethodLocation(jvmtiEnv* env,
+ jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr);
+
static jvmtiError GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr);
@@ -57,6 +66,10 @@
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr);
+
+ static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr);
+ static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr);
+ static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_monitor.cc b/runtime/openjdkjvmti/ti_monitor.cc
new file mode 100644
index 0000000..b827683
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_monitor.cc
@@ -0,0 +1,302 @@
+/* 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_monitor.h"
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include "art_jvmti.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+// We cannot use ART monitors, as they require the mutator lock for contention locking. We
+// also cannot use pthread mutexes and condition variables (or C++11 abstractions) directly,
+// as the do not have the right semantics for recursive mutexes and waiting (wait only unlocks
+// the mutex once).
+// So go ahead and use a wrapper that does the counting explicitly.
+
+class JvmtiMonitor {
+ public:
+ JvmtiMonitor() : owner_(nullptr), count_(0) {
+ }
+
+ static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) {
+ // Check whether this thread holds the monitor, or nobody does.
+ art::Thread* owner_thread = monitor->owner_.load(std::memory_order_relaxed);
+ if (owner_thread != nullptr && self != owner_thread) {
+ return false;
+ }
+
+ if (monitor->count_ > 0) {
+ monitor->count_ = 0;
+ monitor->owner_.store(nullptr, std::memory_order_relaxed);
+ monitor->mutex_.unlock();
+ }
+
+ delete monitor;
+ return true;
+ }
+
+ void MonitorEnter(art::Thread* self) {
+ // Check for recursive enter.
+ if (IsOwner(self)) {
+ count_++;
+ return;
+ }
+
+ mutex_.lock();
+
+ DCHECK(owner_.load(std::memory_order_relaxed) == nullptr);
+ owner_.store(self, std::memory_order_relaxed);
+ DCHECK_EQ(0u, count_);
+ count_ = 1;
+ }
+
+ bool MonitorExit(art::Thread* self) {
+ if (!IsOwner(self)) {
+ return false;
+ }
+
+ --count_;
+ if (count_ == 0u) {
+ owner_.store(nullptr, std::memory_order_relaxed);
+ mutex_.unlock();
+ }
+
+ return true;
+ }
+
+ bool Wait(art::Thread* self) {
+ auto wait_without_timeout = [&](std::unique_lock<std::mutex>& lk) {
+ cond_.wait(lk);
+ };
+ return Wait(self, wait_without_timeout);
+ }
+
+ bool Wait(art::Thread* self, uint64_t timeout_in_ms) {
+ auto wait_with_timeout = [&](std::unique_lock<std::mutex>& lk) {
+ cond_.wait_for(lk, std::chrono::milliseconds(timeout_in_ms));
+ };
+ return Wait(self, wait_with_timeout);
+ }
+
+ bool Notify(art::Thread* self) {
+ return Notify(self, [&]() { cond_.notify_one(); });
+ }
+
+ bool NotifyAll(art::Thread* self) {
+ return Notify(self, [&]() { cond_.notify_all(); });
+ }
+
+ private:
+ bool IsOwner(art::Thread* self) {
+ // There's a subtle correctness argument here for a relaxed load outside the critical section.
+ // A thread is guaranteed to see either its own latest store or another thread's store. If a
+ // thread sees another thread's store than it cannot be holding the lock.
+ art::Thread* owner_thread = owner_.load(std::memory_order_relaxed);
+ return self == owner_thread;
+ }
+
+ template <typename T>
+ bool Wait(art::Thread* self, T how_to_wait) {
+ if (!IsOwner(self)) {
+ return false;
+ }
+
+ size_t old_count = count_;
+
+ count_ = 0;
+ owner_.store(nullptr, std::memory_order_relaxed);
+
+ {
+ std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock);
+ how_to_wait(lk);
+ lk.release(); // Do not unlock the mutex.
+ }
+
+ DCHECK(owner_.load(std::memory_order_relaxed) == nullptr);
+ owner_.store(self, std::memory_order_relaxed);
+ DCHECK_EQ(0u, count_);
+ count_ = old_count;
+
+ return true;
+ }
+
+ template <typename T>
+ bool Notify(art::Thread* self, T how_to_notify) {
+ if (!IsOwner(self)) {
+ return false;
+ }
+
+ how_to_notify();
+
+ return true;
+ }
+
+ std::mutex mutex_;
+ std::condition_variable cond_;
+ std::atomic<art::Thread*> owner_;
+ size_t count_;
+};
+
+static jrawMonitorID EncodeMonitor(JvmtiMonitor* monitor) {
+ return reinterpret_cast<jrawMonitorID>(monitor);
+}
+
+static JvmtiMonitor* DecodeMonitor(jrawMonitorID id) {
+ return reinterpret_cast<JvmtiMonitor*>(id);
+}
+
+jvmtiError MonitorUtil::CreateRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const char* name,
+ jrawMonitorID* monitor_ptr) {
+ if (name == nullptr || monitor_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ JvmtiMonitor* monitor = new JvmtiMonitor();
+ *monitor_ptr = EncodeMonitor(monitor);
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::DestroyRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ if (!JvmtiMonitor::Destroy(self, monitor)) {
+ return ERR(NOT_MONITOR_OWNER);
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ monitor->MonitorEnter(self);
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::RawMonitorExit(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ if (!monitor->MonitorExit(self)) {
+ return ERR(NOT_MONITOR_OWNER);
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::RawMonitorWait(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jrawMonitorID id,
+ jlong millis) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ // This is not in the spec, but it's the only thing that makes sense (and agrees with
+ // Object.wait).
+ if (millis < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ bool result = (millis > 0)
+ ? monitor->Wait(self, static_cast<uint64_t>(millis))
+ : monitor->Wait(self);
+
+ if (!result) {
+ return ERR(NOT_MONITOR_OWNER);
+ }
+
+ // TODO: Make sure that is really what we should be checking here.
+ if (self->IsInterrupted()) {
+ return ERR(INTERRUPT);
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::RawMonitorNotify(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ if (!monitor->Notify(self)) {
+ return ERR(NOT_MONITOR_OWNER);
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError MonitorUtil::RawMonitorNotifyAll(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
+ if (id == nullptr) {
+ return ERR(INVALID_MONITOR);
+ }
+
+ JvmtiMonitor* monitor = DecodeMonitor(id);
+ art::Thread* self = art::Thread::Current();
+
+ if (!monitor->NotifyAll(self)) {
+ return ERR(NOT_MONITOR_OWNER);
+ }
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_monitor.h b/runtime/openjdkjvmti/ti_monitor.h
new file mode 100644
index 0000000..96ccb0d
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_monitor.h
@@ -0,0 +1,59 @@
+/* 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_MONITOR_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class MonitorUtil {
+ public:
+ static jvmtiError CreateRawMonitor(jvmtiEnv* env, const char* name, jrawMonitorID* monitor_ptr);
+
+ static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor);
+
+ static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor);
+
+ static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor);
+
+ static jvmtiError RawMonitorWait(jvmtiEnv* env, jrawMonitorID monitor, jlong millis);
+
+ static jvmtiError RawMonitorNotify(jvmtiEnv* env, jrawMonitorID monitor);
+
+ static jvmtiError RawMonitorNotifyAll(jvmtiEnv* env, jrawMonitorID monitor);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_
diff --git a/runtime/openjdkjvmti/ti_object.cc b/runtime/openjdkjvmti/ti_object.cc
new file mode 100644
index 0000000..bf84499
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_object.cc
@@ -0,0 +1,76 @@
+/* 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_object.h"
+
+#include "art_jvmti.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError ObjectUtil::GetObjectSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jobject jobject,
+ jlong* size_ptr) {
+ if (jobject == nullptr) {
+ return ERR(INVALID_OBJECT);
+ }
+ if (size_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);
+
+ *size_ptr = object->SizeOf();
+ return ERR(NONE);
+}
+
+jvmtiError ObjectUtil::GetObjectHashCode(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jobject jobject,
+ jint* hash_code_ptr) {
+ if (jobject == nullptr) {
+ return ERR(INVALID_OBJECT);
+ }
+ if (hash_code_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);
+
+ *hash_code_ptr = object->IdentityHashCode();
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_object.h b/runtime/openjdkjvmti/ti_object.h
new file mode 100644
index 0000000..09eee61
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_object.h
@@ -0,0 +1,49 @@
+/* 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_OBJECT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ObjectUtil {
+ public:
+ static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
+
+ static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc
new file mode 100644
index 0000000..46b9e71
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_properties.cc
@@ -0,0 +1,192 @@
+/* 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_properties.h"
+
+#include <string.h>
+#include <vector>
+
+#include "art_jvmti.h"
+#include "runtime.h"
+
+namespace openjdkjvmti {
+
+// Hardcoded properties. Tests ensure that these are consistent with libcore's view, as seen
+// in System.java and AndroidHardcodedSystemProperties.java.
+static constexpr const char* kProperties[][2] = {
+ // Recommended by the spec.
+ { "java.vm.vendor", "The Android Project" },
+ { "java.vm.version", "2.1.0" }, // This is Runtime::GetVersion().
+ { "java.vm.name", "Dalvik" },
+ // Android does not provide java.vm.info.
+ //
+ // These are other values provided by AndroidHardcodedSystemProperties.
+ { "java.class.version", "50.0" },
+ { "java.version", "0" },
+ { "java.compiler", "" },
+ { "java.ext.dirs", "" },
+
+ { "java.specification.name", "Dalvik Core Library" },
+ { "java.specification.vendor", "The Android Project" },
+ { "java.specification.version", "0.9" },
+
+ { "java.vendor", "The Android Project" },
+ { "java.vendor.url", "http://www.android.com/" },
+ { "java.vm.name", "Dalvik" },
+ { "java.vm.specification.name", "Dalvik Virtual Machine Specification" },
+ { "java.vm.specification.vendor", "The Android Project" },
+ { "java.vm.specification.version", "0.9" },
+ { "java.vm.vendor", "The Android Project" },
+
+ { "java.vm.vendor.url", "http://www.android.com/" },
+
+ { "java.net.preferIPv6Addresses", "false" },
+
+ { "file.encoding", "UTF-8" },
+
+ { "file.separator", "/" },
+ { "line.separator", "\n" },
+ { "path.separator", ":" },
+
+ { "os.name", "Linux" },
+};
+static constexpr size_t kPropertiesSize = arraysize(kProperties);
+static constexpr const char* kPropertyLibraryPath = "java.library.path";
+static constexpr const char* kPropertyClassPath = "java.class.path";
+
+static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) {
+ unsigned char* data = nullptr;
+ jvmtiError result = CopyString(env, in, &data);
+ *out = reinterpret_cast<char*>(data);
+ return result;
+}
+
+jvmtiError PropertiesUtil::GetSystemProperties(jvmtiEnv* env,
+ jint* count_ptr,
+ char*** property_ptr) {
+ if (count_ptr == nullptr || property_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ unsigned char* array_data;
+ jvmtiError array_alloc_result = env->Allocate((kPropertiesSize + 2) * sizeof(char*), &array_data);
+ if (array_alloc_result != ERR(NONE)) {
+ return array_alloc_result;
+ }
+ JvmtiUniquePtr array_data_ptr = MakeJvmtiUniquePtr(env, array_data);
+ char** array = reinterpret_cast<char**>(array_data);
+
+ std::vector<JvmtiUniquePtr> property_copies;
+
+ {
+ char* libpath_data;
+ jvmtiError libpath_result = Copy(env, kPropertyLibraryPath, &libpath_data);
+ if (libpath_result != ERR(NONE)) {
+ return libpath_result;
+ }
+ array[0] = libpath_data;
+ property_copies.push_back(MakeJvmtiUniquePtr(env, libpath_data));
+ }
+
+ {
+ char* classpath_data;
+ jvmtiError classpath_result = Copy(env, kPropertyClassPath, &classpath_data);
+ if (classpath_result != ERR(NONE)) {
+ return classpath_result;
+ }
+ array[1] = classpath_data;
+ property_copies.push_back(MakeJvmtiUniquePtr(env, classpath_data));
+ }
+
+ for (size_t i = 0; i != kPropertiesSize; ++i) {
+ char* data;
+ jvmtiError data_result = Copy(env, kProperties[i][0], &data);
+ if (data_result != ERR(NONE)) {
+ return data_result;
+ }
+ array[i + 2] = data;
+ property_copies.push_back(MakeJvmtiUniquePtr(env, data));
+ }
+
+ // Everything is OK, release the data.
+ array_data_ptr.release();
+ for (auto& uptr : property_copies) {
+ uptr.release();
+ }
+
+ *count_ptr = kPropertiesSize + 2;
+ *property_ptr = array;
+
+ return ERR(NONE);
+}
+
+jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env,
+ const char* property,
+ char** value_ptr) {
+ if (property == nullptr || value_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ if (strcmp(property, kPropertyLibraryPath) == 0) {
+ // TODO: In the live phase, we should probably compare to System.getProperty. java.library.path
+ // may not be set initially, and is then freely modifiable.
+ const std::vector<std::string>& runtime_props = art::Runtime::Current()->GetProperties();
+ for (const std::string& prop_assignment : runtime_props) {
+ size_t assign_pos = prop_assignment.find('=');
+ if (assign_pos != std::string::npos && assign_pos > 0) {
+ if (prop_assignment.substr(0, assign_pos) == kPropertyLibraryPath) {
+ return Copy(env, prop_assignment.substr(assign_pos + 1).c_str(), value_ptr);
+ }
+ }
+ }
+ return ERR(NOT_AVAILABLE);
+ }
+
+ if (strcmp(property, kPropertyClassPath) == 0) {
+ return Copy(env, art::Runtime::Current()->GetClassPathString().c_str(), value_ptr);
+ }
+
+ for (size_t i = 0; i != kPropertiesSize; ++i) {
+ if (strcmp(property, kProperties[i][0]) == 0) {
+ return Copy(env, kProperties[i][1], value_ptr);
+ }
+ }
+
+ return ERR(NOT_AVAILABLE);
+}
+
+jvmtiError PropertiesUtil::SetSystemProperty(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const char* property ATTRIBUTE_UNUSED,
+ const char* value ATTRIBUTE_UNUSED) {
+ // We do not allow manipulation of any property here.
+ return ERR(NOT_AVAILABLE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_properties.h b/runtime/openjdkjvmti/ti_properties.h
new file mode 100644
index 0000000..7073481
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_properties.h
@@ -0,0 +1,51 @@
+/* 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_PROPERTIES_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class PropertiesUtil {
+ public:
+ static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr);
+
+ static jvmtiError GetSystemProperty(jvmtiEnv* env, const char* property, char** value_ptr);
+
+ static jvmtiError SetSystemProperty(jvmtiEnv* env, const char* property, const char* value);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 926819d..5bf8445 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -37,6 +37,8 @@
#include "art_jvmti.h"
#include "base/logging.h"
+#include "dex_file.h"
+#include "dex_file_types.h"
#include "events-inl.h"
#include "gc/allocation_listener.h"
#include "gc/heap.h"
@@ -64,19 +66,14 @@
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
- /*out*/bool* success,
- /*out*/std::string* error_msg)
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
: StackVisitor(thread,
/*context*/nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames),
allocator_(allocator),
obsoleted_methods_(obsoleted_methods),
obsolete_maps_(obsolete_maps),
- success_(success),
- is_runtime_frame_(false),
- error_msg_(error_msg) {
- *success_ = true;
+ is_runtime_frame_(false) {
}
~ObsoleteMethodStackVisitor() OVERRIDE {}
@@ -85,34 +82,17 @@
// Returns true if we successfully installed obsolete methods on this thread, filling
// obsolete_maps_ with the translations if needed. Returns false and fills error_msg if we fail.
// The stack is cleaned up when we fail.
- static bool UpdateObsoleteFrames(
+ static void UpdateObsoleteFrames(
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
- /*out*/std::string* error_msg) REQUIRES(art::Locks::mutator_lock_) {
- bool success = true;
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+ REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
obsoleted_methods,
- obsolete_maps,
- &success,
- error_msg);
+ obsolete_maps);
visitor.WalkStack();
- if (!success) {
- RestoreFrames(thread, *obsolete_maps, error_msg);
- return false;
- } else {
- return true;
- }
- }
-
- static void RestoreFrames(
- art::Thread* thread ATTRIBUTE_UNUSED,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsolete_maps ATTRIBUTE_UNUSED,
- std::string* error_msg)
- REQUIRES(art::Locks::mutator_lock_) {
- LOG(FATAL) << "Restoring stack frames is not yet supported. Error was: " << *error_msg;
}
bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
@@ -130,9 +110,7 @@
// works through runtime methods.
// TODO b/33616143
if (!IsShadowFrame() && prev_was_runtime_frame_) {
- *error_msg_ = StringPrintf("Deoptimization failed due to runtime method in stack.");
- *success_ = false;
- return false;
+ LOG(FATAL) << "Deoptimization failed due to runtime method in stack. See b/33616143";
}
// We cannot ensure that the right dex file is used in inlined frames so we don't support
// redefining them.
@@ -150,12 +128,8 @@
auto ptr_size = cl->GetImagePointerSize();
const size_t method_size = art::ArtMethod::Size(ptr_size);
auto* method_storage = allocator_->Alloc(GetThread(), method_size);
- if (method_storage == nullptr) {
- *success_ = false;
- *error_msg_ = StringPrintf("Unable to allocate storage for obsolete version of '%s'",
- old_method->PrettyMethod().c_str());
- return false;
- }
+ CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '"
+ << old_method->PrettyMethod() << "'";
new_obsolete_method = new (method_storage) art::ArtMethod();
new_obsolete_method->CopyFrom(old_method, ptr_size);
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
@@ -186,13 +160,50 @@
// values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
// the redefined classes ClassExt by the caller.
std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
- bool* success_;
// TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
// works through runtime methods.
bool is_runtime_frame_;
- std::string* error_msg_;
};
+jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jboolean* is_redefinable) {
+ // TODO Check for the appropriate feature flags once we have enabled them.
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<1> hs(self);
+ art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
+ if (obj.IsNull()) {
+ return ERR(INVALID_CLASS);
+ }
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
+ std::string err_unused;
+ *is_redefinable =
+ Redefiner::GetClassRedefinitionError(h_klass, &err_unused) == OK ? JNI_TRUE : JNI_FALSE;
+ return OK;
+}
+
+jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
+ /*out*/std::string* error_msg) {
+ if (klass->IsPrimitive()) {
+ *error_msg = "Modification of primitive classes is not supported";
+ return ERR(UNMODIFIABLE_CLASS);
+ } else if (klass->IsInterface()) {
+ *error_msg = "Modification of Interface classes is currently not supported";
+ return ERR(UNMODIFIABLE_CLASS);
+ } else if (klass->IsArrayClass()) {
+ *error_msg = "Modification of Array classes is not supported";
+ return ERR(UNMODIFIABLE_CLASS);
+ } else if (klass->IsProxyClass()) {
+ *error_msg = "Modification of proxy classes is not supported";
+ return ERR(UNMODIFIABLE_CLASS);
+ }
+
+ // TODO We should check if the class has non-obsoletable methods on the stack
+ LOG(WARNING) << "presence of non-obsoletable methods on stacks is not currently checked";
+ return OK;
+}
+
// Moves dex data to an anonymous, read-only mmap'd region.
std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
jint data_len,
@@ -446,59 +457,38 @@
art::LinearAlloc* allocator;
std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
std::unordered_set<art::ArtMethod*> obsolete_methods;
- bool success;
- std::string* error_msg;
- CallbackCtx(Redefiner* self, art::LinearAlloc* alloc, std::string* error)
- : r(self), allocator(alloc), success(true), error_msg(error) {}
+ CallbackCtx(Redefiner* self, art::LinearAlloc* alloc)
+ : r(self), allocator(alloc) {}
};
-void DoRestoreObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
- CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
- ObsoleteMethodStackVisitor::RestoreFrames(t, data->obsolete_map, data->error_msg);
-}
-
void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
- if (data->success) {
- // Don't do anything if we already failed once.
- data->success = ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
- data->allocator,
- data->obsolete_methods,
- &data->obsolete_map,
- data->error_msg);
- }
+ ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
+ data->allocator,
+ data->obsolete_methods,
+ &data->obsolete_map);
}
// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
// updated so they will be run.
-bool Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
+void Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking");
art::mirror::ClassExt* ext = art_klass->GetExtData();
CHECK(ext->GetObsoleteMethods() != nullptr);
- CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator(), error_msg_);
+ CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator());
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
ctx.obsolete_methods.insert(&m);
- }
- for (art::ArtMethod* old_method : ctx.obsolete_methods) {
- if (old_method->IsIntrinsic()) {
- *error_msg_ = StringPrintf("Method '%s' is intrinsic and cannot be made obsolete!",
- old_method->PrettyMethod().c_str());
- return false;
- }
+ // TODO Allow this or check in IsModifiableClass.
+ DCHECK(!m.IsIntrinsic());
}
{
art::MutexLock mu(self_, *art::Locks::thread_list_lock_);
art::ThreadList* list = art::Runtime::Current()->GetThreadList();
list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
- if (!ctx.success) {
- list->ForEach(DoRestoreObsoleteMethodsCallback, static_cast<void*>(&ctx));
- return false;
- }
}
FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
- return true;
}
// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
@@ -542,6 +532,107 @@
i->ReJitEverything("libOpenJkdJvmti - Class Redefinition");
}
+bool Redefiner::CheckClass() {
+ // TODO Might just want to put it in a ObjPtr and NoSuspend assert.
+ art::StackHandleScope<1> hs(self_);
+ // Easy check that only 1 class def is present.
+ if (dex_file_->NumClassDefs() != 1) {
+ RecordFailure(ERR(ILLEGAL_ARGUMENT),
+ StringPrintf("Expected 1 class def in dex file but found %d",
+ dex_file_->NumClassDefs()));
+ return false;
+ }
+ // Get the ClassDef from the new DexFile.
+ // Since the dex file has only a single class def the index is always 0.
+ const art::DexFile::ClassDef& def = dex_file_->GetClassDef(0);
+ // Get the class as it is now.
+ art::Handle<art::mirror::Class> current_class(hs.NewHandle(GetMirrorClass()));
+
+ // Check the access flags didn't change.
+ if (def.GetJavaAccessFlags() != (current_class->GetAccessFlags() & art::kAccValidClassFlags)) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED),
+ "Cannot change modifiers of class by redefinition");
+ return false;
+ }
+
+ // Check class name.
+ // These should have been checked by the dexfile verifier on load.
+ DCHECK_NE(def.class_idx_, art::dex::TypeIndex::Invalid()) << "Invalid type index";
+ const char* descriptor = dex_file_->StringByTypeIdx(def.class_idx_);
+ DCHECK(descriptor != nullptr) << "Invalid dex file structure!";
+ if (!current_class->DescriptorEquals(descriptor)) {
+ std::string storage;
+ RecordFailure(ERR(NAMES_DONT_MATCH),
+ StringPrintf("expected file to contain class called '%s' but found '%s'!",
+ current_class->GetDescriptor(&storage),
+ descriptor));
+ return false;
+ }
+ if (current_class->IsObjectClass()) {
+ if (def.superclass_idx_ != art::dex::TypeIndex::Invalid()) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Superclass added!");
+ return false;
+ }
+ } else {
+ const char* super_descriptor = dex_file_->StringByTypeIdx(def.superclass_idx_);
+ DCHECK(descriptor != nullptr) << "Invalid dex file structure!";
+ if (!current_class->GetSuperClass()->DescriptorEquals(super_descriptor)) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Superclass changed");
+ return false;
+ }
+ }
+ const art::DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(def);
+ if (interfaces == nullptr) {
+ if (current_class->NumDirectInterfaces() != 0) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Interfaces added");
+ return false;
+ }
+ } else {
+ DCHECK(!current_class->IsProxyClass());
+ const art::DexFile::TypeList* current_interfaces = current_class->GetInterfaceTypeList();
+ if (current_interfaces == nullptr || current_interfaces->Size() != interfaces->Size()) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED), "Interfaces added or removed");
+ return false;
+ }
+ // The order of interfaces is (barely) meaningful so we error if it changes.
+ const art::DexFile& orig_dex_file = current_class->GetDexFile();
+ for (uint32_t i = 0; i < interfaces->Size(); i++) {
+ if (strcmp(
+ dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_),
+ orig_dex_file.StringByTypeIdx(current_interfaces->GetTypeItem(i).type_idx_)) != 0) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED),
+ "Interfaces changed or re-ordered");
+ return false;
+ }
+ }
+ }
+ LOG(WARNING) << "No verification is done on annotations of redefined classes.";
+
+ return true;
+}
+
+// TODO Move this to use IsRedefinable when that function is made.
+bool Redefiner::CheckRedefinable() {
+ std::string err;
+ art::StackHandleScope<1> hs(self_);
+
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
+ jvmtiError res = Redefiner::GetClassRedefinitionError(h_klass, &err);
+ if (res != OK) {
+ RecordFailure(res, err);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Redefiner::CheckRedefinitionIsValid() {
+ return CheckRedefinable() &&
+ CheckClass() &&
+ CheckSameFields() &&
+ CheckSameMethods();
+}
+
jvmtiError Redefiner::Run() {
art::StackHandleScope<5> hs(self_);
// TODO We might want to have a global lock (or one based on the class being redefined at least)
@@ -552,7 +643,7 @@
// doing a try loop. The other allocations we need to ensure that nothing has changed in the time
// between allocating them and pausing all threads before we can update them so we need to do a
// try loop.
- if (!EnsureRedefinitionIsValid() || !EnsureClassAllocationsFinished()) {
+ if (!CheckRedefinitionIsValid() || !EnsureClassAllocationsFinished()) {
return result_;
}
art::MutableHandle<art::mirror::ClassLoader> source_class_loader(
@@ -601,31 +692,9 @@
// TODO We should really Retry if this fails instead of simply aborting.
// Set the new DexFileCookie returns the original so we can fix it back up if redefinition fails
art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr);
- if (!UpdateJavaDexFile(java_dex_file.Get(),
- new_dex_file_cookie.Get(),
- &original_dex_file_cookie) ||
- !FindAndAllocateObsoleteMethods(art_class.Get())) {
- // Release suspendAll
- runtime_->GetThreadList()->ResumeAll();
- // Get back shared mutator lock as expected for return.
- self_->TransitionFromSuspendedToRunnable();
- if (heap->IsGcConcurrentAndMoving()) {
- heap->DecrementDisableMovingGC(self_);
- }
- return result_;
- }
- if (!UpdateClass(art_class.Get(), new_dex_cache.Get())) {
- // TODO Should have some form of scope to do this.
- RestoreJavaDexFile(java_dex_file.Get(), original_dex_file_cookie);
- // Release suspendAll
- runtime_->GetThreadList()->ResumeAll();
- // Get back shared mutator lock as expected for return.
- self_->TransitionFromSuspendedToRunnable();
- if (heap->IsGcConcurrentAndMoving()) {
- heap->DecrementDisableMovingGC(self_);
- }
- return result_;
- }
+ UpdateJavaDexFile(java_dex_file.Get(), new_dex_file_cookie.Get(), &original_dex_file_cookie);
+ FindAndAllocateObsoleteMethods(art_class.Get());
+ UpdateClass(art_class.Get(), new_dex_cache.Get());
// Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
// pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the
// DexCache.
@@ -652,21 +721,7 @@
return OK;
}
-void Redefiner::RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
- art::ObjPtr<art::mirror::LongArray> orig_cookie) {
- art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
- "mInternalCookie", "Ljava/lang/Object;");
- art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
- "mCookie", "Ljava/lang/Object;");
- art::ObjPtr<art::mirror::LongArray> new_cookie(
- cookie_field->GetObject(java_dex_file)->AsLongArray());
- internal_cookie_field->SetObject<false>(java_dex_file, orig_cookie);
- if (!new_cookie.IsNull()) {
- cookie_field->SetObject<false>(java_dex_file, orig_cookie);
- }
-}
-
-bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+void Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache,
const art::DexFile::ClassDef& class_def) {
art::ClassLinker* linker = runtime_->GetClassLinker();
@@ -709,10 +764,9 @@
jit->GetCodeCache()->NotifyMethodRedefined(&method);
}
}
- return true;
}
-bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+void Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
// TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
for (art::ArtField& field : fields_iter) {
@@ -730,28 +784,16 @@
field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
}
}
- return true;
}
// Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+void Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
*dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
- if (class_def == nullptr) {
- RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
- return false;
- }
- if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
- // TODO Investigate appropriate error types.
- RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
- return false;
- }
- if (!UpdateFields(mclass)) {
- // TODO Investigate appropriate error types.
- RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
- return false;
- }
+ DCHECK(class_def != nullptr);
+ UpdateMethods(mclass, new_dex_cache, *class_def);
+ UpdateFields(mclass);
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
@@ -759,10 +801,9 @@
mclass->SetDexCache(new_dex_cache.Ptr());
mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_)));
- return true;
}
-bool Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+void Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
art::ObjPtr<art::mirror::LongArray> new_cookie,
/*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) {
art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
@@ -779,7 +820,6 @@
if (!orig_cookie.IsNull()) {
cookie_field->SetObject<false>(java_dex_file, new_cookie);
}
- return true;
}
// This function does all (java) allocations we need to do for the Class being redefined.
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 9d23ce4..5852309 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -80,6 +80,8 @@
unsigned char* dex_data,
std::string* error_msg);
+ static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
+
private:
jvmtiError result_;
art::Runtime* runtime_;
@@ -106,6 +108,10 @@
error_msg_(error_msg),
class_sig_(class_sig) { }
+ static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
+ /*out*/std::string* error_msg)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
jint data_len,
unsigned char* dex_data,
@@ -152,36 +158,47 @@
void RecordFailure(jvmtiError result, const std::string& error_msg);
- // TODO Actually write this.
// This will check that no constraints are violated (more than 1 class in dex file, any changes in
// number/declaration of methods & fields, changes in access flags, etc.)
- bool EnsureRedefinitionIsValid() {
- LOG(WARNING) << "Redefinition is not checked for validity currently";
+ bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Checks that the class can even be redefined.
+ bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Checks that the dex file does not add/remove methods.
+ bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ LOG(WARNING) << "methods are not checked for modification currently";
return true;
}
- bool UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+ // Checks that the dex file does not modify fields
+ bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ LOG(WARNING) << "Fields are not checked for modification currently";
+ return true;
+ }
+
+ // Checks that the dex file contains only the single expected class and that the top-level class
+ // data has not been modified in an incompatible manner.
+ bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
art::ObjPtr<art::mirror::LongArray> new_cookie,
/*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie)
REQUIRES(art::Locks::mutator_lock_);
- void RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
- art::ObjPtr<art::mirror::LongArray> original_cookie)
+ void UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
REQUIRES(art::Locks::mutator_lock_);
- bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
- REQUIRES(art::Locks::mutator_lock_);
-
- bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache,
const art::DexFile::ClassDef& class_def)
REQUIRES(art::Locks::mutator_lock_);
- bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+ void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache)
REQUIRES(art::Locks::mutator_lock_);
- bool FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
+ void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
REQUIRES(art::Locks::mutator_lock_);
void FillObsoleteMethodMap(art::mirror::Class* art_klass,
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a81458f..b809c3e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -140,7 +140,7 @@
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
-void QuickExceptionHandler::FindCatch(mirror::Throwable* exception) {
+void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
DCHECK(!is_deoptimization_);
if (kDebugExceptionDelivery) {
mirror::String* msg = exception->GetDetailMessage();
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 5592126..3ead7db 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -46,7 +46,7 @@
}
// Find the catch handler for the given exception.
- void FindCatch(mirror::Throwable* exception) REQUIRES_SHARED(Locks::mutator_lock_);
+ void FindCatch(ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize the stack to the upcall/some code that's not deoptimizeable. For
// every compiled frame, we create a "copy" shadow frame that will be executed
diff --git a/runtime/thread.cc b/runtime/thread.cc
index aff12ff..a4f0631 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -154,18 +154,18 @@
DeoptimizationContextRecord(const JValue& ret_val,
bool is_reference,
bool from_code,
- mirror::Throwable* pending_exception,
+ ObjPtr<mirror::Throwable> pending_exception,
DeoptimizationContextRecord* link)
: ret_val_(ret_val),
is_reference_(is_reference),
from_code_(from_code),
- pending_exception_(pending_exception),
+ pending_exception_(pending_exception.Ptr()),
link_(link) {}
JValue GetReturnValue() const { return ret_val_; }
bool IsReference() const { return is_reference_; }
bool GetFromCode() const { return from_code_; }
- mirror::Throwable* GetPendingException() const { return pending_exception_; }
+ ObjPtr<mirror::Throwable> GetPendingException() const { return pending_exception_; }
DeoptimizationContextRecord* GetLink() const { return link_; }
mirror::Object** GetReturnValueAsGCRoot() {
DCHECK(is_reference_);
@@ -219,7 +219,7 @@
void Thread::PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
bool from_code,
- mirror::Throwable* exception) {
+ ObjPtr<mirror::Throwable> exception) {
DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
return_value,
is_reference,
@@ -230,7 +230,7 @@
}
void Thread::PopDeoptimizationContext(JValue* result,
- mirror::Throwable** exception,
+ ObjPtr<mirror::Throwable>* exception,
bool* from_code) {
AssertHasDeoptimizationContext();
DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack;
@@ -434,7 +434,7 @@
Dbg::PostThreadStart(self);
// Invoke the 'run' method of our java.lang.Thread.
- mirror::Object* receiver = self->tlsPtr_.opeer;
+ ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
@@ -446,7 +446,7 @@
}
Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* thread_peer) {
+ ObjPtr<mirror::Object> thread_peer) {
ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer);
Thread* result = reinterpret_cast<Thread*>(static_cast<uintptr_t>(f->GetLong(thread_peer)));
// Sanity check that if we have a result it is either suspended or we hold the thread_list_lock_
@@ -1573,8 +1573,8 @@
}
m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize);
const int kMaxRepetition = 3;
- mirror::Class* c = m->GetDeclaringClass();
- mirror::DexCache* dex_cache = c->GetDexCache();
+ ObjPtr<mirror::Class> c = m->GetDeclaringClass();
+ ObjPtr<mirror::DexCache> dex_cache = c->GetDexCache();
int line_number = -1;
if (dex_cache != nullptr) { // be tolerant of bad input
const DexFile* dex_file = dex_cache->GetDexFile();
@@ -1860,17 +1860,15 @@
void Thread::AssertNoPendingException() const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Throwable* exception = GetException();
- LOG(FATAL) << "No pending exception expected: " << exception->Dump();
+ LOG(FATAL) << "No pending exception expected: " << GetException()->Dump();
}
}
void Thread::AssertNoPendingExceptionForNewException(const char* msg) const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Throwable* exception = GetException();
LOG(FATAL) << "Throwing new exception '" << msg << "' with unexpected pending exception: "
- << exception->Dump();
+ << GetException()->Dump();
}
}
@@ -2213,7 +2211,7 @@
// class of the ArtMethod pointers.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self_);
- mirror::Class* array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass);
+ ObjPtr<mirror::Class> array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass);
// The first element is the methods and dex pc array, the other elements are declaring classes
// for the methods to ensure classes in the stack trace don't get unloaded.
Handle<mirror::ObjectArray<mirror::Object>> trace(
@@ -2225,7 +2223,8 @@
self_->AssertPendingOOMException();
return false;
}
- mirror::PointerArray* methods_and_pcs = class_linker->AllocPointerArray(self_, depth * 2);
+ ObjPtr<mirror::PointerArray> methods_and_pcs =
+ class_linker->AllocPointerArray(self_, depth * 2);
const char* last_no_suspend_cause =
self_->StartAssertNoThreadSuspension("Building internal stack trace");
if (methods_and_pcs == nullptr) {
@@ -2255,7 +2254,7 @@
if (m->IsRuntimeMethod()) {
return true; // Ignore runtime frames (in particular callee save).
}
- mirror::PointerArray* trace_methods_and_pcs = GetTraceMethodsAndPCs();
+ ObjPtr<mirror::PointerArray> trace_methods_and_pcs = GetTraceMethodsAndPCs();
trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(count_, m, pointer_size_);
trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(
trace_methods_and_pcs->GetLength() / 2 + count_,
@@ -2268,8 +2267,8 @@
return true;
}
- mirror::PointerArray* GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) {
- return down_cast<mirror::PointerArray*>(trace_->Get(0));
+ ObjPtr<mirror::PointerArray> GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(trace_->Get(0)));
}
mirror::ObjectArray<mirror::Object>* GetInternalStackTrace() const {
@@ -2311,7 +2310,7 @@
build_trace_visitor.WalkStack();
mirror::ObjectArray<mirror::Object>* trace = build_trace_visitor.GetInternalStackTrace();
if (kIsDebugBuild) {
- mirror::PointerArray* trace_methods = build_trace_visitor.GetTraceMethodsAndPCs();
+ ObjPtr<mirror::PointerArray> trace_methods = build_trace_visitor.GetTraceMethodsAndPCs();
// Second half of trace_methods is dex PCs.
for (uint32_t i = 0; i < static_cast<uint32_t>(trace_methods->GetLength() / 2); ++i) {
auto* method = trace_methods->GetElementPtrSize<ArtMethod*>(
@@ -2326,7 +2325,7 @@
template jobject Thread::CreateInternalStackTrace<true>(
const ScopedObjectAccessAlreadyRunnable& soa) const;
-bool Thread::IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const {
+bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const {
CountStackDepthVisitor count_visitor(const_cast<Thread*>(this));
count_visitor.WalkStack();
return count_visitor.GetDepth() == exception->GetStackDepth();
@@ -2368,12 +2367,12 @@
}
for (int32_t i = 0; i < depth; ++i) {
- mirror::ObjectArray<mirror::Object>* decoded_traces =
+ ObjPtr<mirror::ObjectArray<mirror::Object>> decoded_traces =
soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>();
// Methods and dex PC trace is element 0.
DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
- mirror::PointerArray* const method_trace =
- down_cast<mirror::PointerArray*>(decoded_traces->Get(0));
+ ObjPtr<mirror::PointerArray> const method_trace =
+ ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(decoded_traces->Get(0)));
// Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
@@ -2415,8 +2414,11 @@
if (method_name_object.Get() == nullptr) {
return nullptr;
}
- mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(
- soa.Self(), class_name_object, method_name_object, source_name_object, line_number);
+ ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(),
+ class_name_object,
+ method_name_object,
+ source_name_object,
+ line_number);
if (obj == nullptr) {
return nullptr;
}
@@ -2447,7 +2449,7 @@
ThrowNewWrappedException(exception_class_descriptor, msg);
}
-static mirror::ClassLoader* GetCurrentClassLoader(Thread* self)
+static ObjPtr<mirror::ClassLoader> GetCurrentClassLoader(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = self->GetCurrentMethod(nullptr);
return method != nullptr
@@ -2794,7 +2796,7 @@
void Thread::QuickDeliverException() {
// Get exception from thread.
- mirror::Throwable* exception = GetException();
+ ObjPtr<mirror::Throwable> exception = GetException();
CHECK(exception != nullptr);
if (exception == GetDeoptimizationException()) {
artDeoptimize(this);
@@ -2807,8 +2809,8 @@
IsExceptionThrownByCurrentMethod(exception)) {
// Instrumentation may cause GC so keep the exception object safe.
StackHandleScope<1> hs(this);
- HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
- instrumentation->ExceptionCaughtEvent(this, exception);
+ HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
+ instrumentation->ExceptionCaughtEvent(this, exception.Ptr());
}
// Does instrumentation need to deoptimize the stack?
// Note: we do this *after* reporting the exception to instrumentation in case it
@@ -2870,7 +2872,7 @@
dex_pc_ = GetDexPc(abort_on_error_);
return false;
}
- mirror::Object* this_object_;
+ ObjPtr<mirror::Object> this_object_;
ArtMethod* method_;
uint32_t dex_pc_;
const bool abort_on_error_;
@@ -2885,11 +2887,8 @@
return visitor.method_;
}
-bool Thread::HoldsLock(mirror::Object* object) const {
- if (object == nullptr) {
- return false;
- }
- return object->GetLockOwnerThreadId() == GetThreadId();
+bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const {
+ return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId();
}
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
@@ -2945,7 +2944,7 @@
void VisitDeclaringClass(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS {
- mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
+ ObjPtr<mirror::Class> klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
// klass can be null for runtime methods.
if (klass != nullptr) {
if (kVerifyImageObjectsMarked) {
@@ -2954,10 +2953,10 @@
/*fail_ok*/true);
if (space != nullptr && space->IsImageSpace()) {
bool failed = false;
- if (!space->GetLiveBitmap()->Test(klass)) {
+ if (!space->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image " << *space;
- } else if (!heap->GetLiveBitmap()->Test(klass)) {
+ } else if (!heap->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image through live bitmap " << *space;
}
@@ -2965,17 +2964,17 @@
GetThread()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
space->AsImageSpace()->DumpSections(LOG_STREAM(FATAL_WITHOUT_ABORT));
LOG(FATAL_WITHOUT_ABORT) << "Method@" << method->GetDexMethodIndex() << ":" << method
- << " klass@" << klass;
+ << " klass@" << klass.Ptr();
// Pretty info last in case it crashes.
LOG(FATAL) << "Method " << method->PrettyMethod() << " klass "
<< klass->PrettyClass();
}
}
}
- mirror::Object* new_ref = klass;
+ mirror::Object* new_ref = klass.Ptr();
visitor_(&new_ref, -1, this);
if (new_ref != klass) {
- method->CASDeclaringClass(klass, new_ref->AsClass());
+ method->CASDeclaringClass(klass.Ptr(), new_ref->AsClass());
}
}
}
@@ -3367,7 +3366,7 @@
ClearException();
ShadowFrame* shadow_frame =
PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
- mirror::Throwable* pending_exception = nullptr;
+ ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
PopDeoptimizationContext(result, &pending_exception, &from_code);
SetTopOfStack(nullptr);
diff --git a/runtime/thread.h b/runtime/thread.h
index 411d85f..c7acfc4 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -177,7 +177,7 @@
void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts,
- mirror::Object* thread_peer)
+ ObjPtr<mirror::Object> thread_peer)
REQUIRES(Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, jobject thread)
@@ -312,7 +312,7 @@
size_t NumberOfHeldMutexes() const;
- bool HoldsLock(mirror::Object*) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool HoldsLock(ObjPtr<mirror::Object> object) const REQUIRES_SHARED(Locks::mutator_lock_);
/*
* Changes the priority of this thread to match that of the java.lang.Thread object.
@@ -413,7 +413,7 @@
// Returns whether the given exception was thrown by the current Java method being executed
// (Note that this includes native Java methods).
- bool IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const
+ bool IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const
REQUIRES_SHARED(Locks::mutator_lock_);
void SetTopOfStack(ArtMethod** top_method) {
@@ -925,9 +925,11 @@
void PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
bool from_code,
- mirror::Throwable* exception)
+ ObjPtr<mirror::Throwable> exception)
REQUIRES_SHARED(Locks::mutator_lock_);
- void PopDeoptimizationContext(JValue* result, mirror::Throwable** exception, bool* from_code)
+ void PopDeoptimizationContext(JValue* result,
+ ObjPtr<mirror::Throwable>* exception,
+ bool* from_code)
REQUIRES_SHARED(Locks::mutator_lock_);
void AssertHasDeoptimizationContext()
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index bf9eef8..c5c7e2c 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -653,8 +653,9 @@
// is done with a timeout so that we can detect problems.
#if ART_USE_FUTEXES
timespec wait_timeout;
- InitTimeSpec(true, CLOCK_MONOTONIC, 10000, 0, &wait_timeout);
+ InitTimeSpec(false, CLOCK_MONOTONIC, kIsDebugBuild ? 50000 : 10000, 0, &wait_timeout);
#endif
+ const uint64_t start_time = NanoTime();
while (true) {
int32_t cur_val = pending_threads.LoadRelaxed();
if (LIKELY(cur_val > 0)) {
@@ -664,7 +665,8 @@
if ((errno != EAGAIN) && (errno != EINTR)) {
if (errno == ETIMEDOUT) {
LOG(kIsDebugBuild ? ::android::base::FATAL : ::android::base::ERROR)
- << "Unexpected time out during suspend all.";
+ << "Timed out waiting for threads to suspend, waited for "
+ << PrettyDuration(NanoTime() - start_time);
} else {
PLOG(FATAL) << "futex wait failed for SuspendAllInternal()";
}
@@ -672,6 +674,7 @@
} // else re-check pending_threads in the next iteration (this may be a spurious wake-up).
#else
// Spin wait. This is likely to be slow, but on most architecture ART_USE_FUTEXES is set.
+ UNUSED(start_time);
#endif
} else {
CHECK_EQ(cur_val, 0);
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index c4058d6..15cc566 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -335,6 +335,60 @@
}
}
+mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination,
+ mirror::Class* source) const {
+ DCHECK(destination->IsInterface());
+ DCHECK(IsInClassPath(destination));
+ Thread* thread = Thread::Current();
+ mirror::Class* current = source;
+ // Record the classes that are at the boundary between the compiled DEX files and
+ // the classpath. We will check those classes later to find one class that inherits
+ // `destination`.
+ std::vector<ObjPtr<mirror::Class>> boundaries;
+ // If the destination is a direct interface of a class defined in the DEX files being
+ // compiled, no need to record it.
+ while (!IsInClassPath(current)) {
+ for (size_t i = 0; i < current->NumDirectInterfaces(); ++i) {
+ ObjPtr<mirror::Class> direct = mirror::Class::GetDirectInterface(thread, current, i);
+ if (direct == destination) {
+ return nullptr;
+ } else if (IsInClassPath(direct)) {
+ boundaries.push_back(direct);
+ }
+ }
+ current = current->GetSuperClass();
+ }
+ DCHECK(current != nullptr);
+ boundaries.push_back(current);
+
+ // Check if we have an interface defined in the DEX files being compiled, direclty
+ // inheriting `destination`.
+ int32_t iftable_count = source->GetIfTableCount();
+ ObjPtr<mirror::IfTable> iftable = source->GetIfTable();
+ for (int32_t i = 0; i < iftable_count; ++i) {
+ mirror::Class* itf = iftable->GetInterface(i);
+ if (!IsInClassPath(itf)) {
+ for (size_t j = 0; j < itf->NumDirectInterfaces(); ++j) {
+ ObjPtr<mirror::Class> direct = mirror::Class::GetDirectInterface(thread, itf, j);
+ if (direct == destination) {
+ return nullptr;
+ } else if (IsInClassPath(direct)) {
+ boundaries.push_back(direct);
+ }
+ }
+ }
+ }
+
+ // Find a boundary making `source` inherit from `destination`. We must find one.
+ for (const ObjPtr<mirror::Class>& boundary : boundaries) {
+ if (destination->IsAssignableFrom(boundary)) {
+ return boundary.Ptr();
+ }
+ }
+ LOG(FATAL) << "Should have found a classpath boundary";
+ UNREACHABLE();
+}
+
void VerifierDeps::AddAssignability(const DexFile& dex_file,
mirror::Class* destination,
mirror::Class* source,
@@ -403,17 +457,26 @@
return;
}
- if (!IsInClassPath(source) && !source->IsInterface() && !destination->IsInterface()) {
- // Find the super class at the classpath boundary. Only that class
- // can change the assignability.
- // TODO: also chase the boundary for interfaces.
- do {
- source = source->GetSuperClass();
- } while (!IsInClassPath(source));
+ if (!IsInClassPath(source)) {
+ if (!destination->IsInterface()) {
+ DCHECK(!source->IsInterface());
+ // Find the super class at the classpath boundary. Only that class
+ // can change the assignability.
+ do {
+ source = source->GetSuperClass();
+ } while (!IsInClassPath(source));
- // If that class is the actual destination, no need to record it.
- if (source == destination) {
- return;
+ // If that class is the actual destination, no need to record it.
+ if (source == destination) {
+ return;
+ }
+ } else if (is_assignable) {
+ source = FindOneClassPathBoundaryForInterface(destination, source);
+ if (source == nullptr) {
+ // There was no classpath boundary, no need to record.
+ return;
+ }
+ DCHECK(IsInClassPath(source));
}
}
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 4b8206f..11750fd 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -204,6 +204,13 @@
bool IsInClassPath(ObjPtr<mirror::Class> klass) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Finds the class in the classpath that makes `source` inherit` from `destination`.
+ // Returns null if a class defined in the compiled DEX files, and assignable to
+ // `source`, direclty inherits from `destination`.
+ mirror::Class* FindOneClassPathBoundaryForInterface(mirror::Class* destination,
+ mirror::Class* source) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns the index of `str`. If it is defined in `dex_file_`, this is the dex
// string ID. If not, an ID is assigned to the string and cached in `strings_`
// of the corresponding DexFileDeps structure (either provided or inferred from
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 7b5ced1..a5b275c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -337,7 +337,6 @@
java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
- java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
@@ -371,7 +370,6 @@
java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J");
- java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;");
java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;");
@@ -398,10 +396,20 @@
void WellKnownClasses::LateInit(JNIEnv* env) {
ScopedLocalRef<jclass> java_lang_Runtime(env, env->FindClass("java/lang/Runtime"));
+ // CacheField and CacheMethod will initialize their classes. Classes below
+ // have clinit sections that call JNI methods. Late init is required
+ // to make sure these JNI methods are available.
java_lang_Runtime_nativeLoad =
CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
"Ljava/lang/String;");
+ java_lang_reflect_Proxy_invoke =
+ CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
+ "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
+ "[Ljava/lang/Object;)Ljava/lang/Object;");
+ java_lang_reflect_Proxy_h =
+ CacheField(env, java_lang_reflect_Proxy, false, "h",
+ "Ljava/lang/reflect/InvocationHandler;");
}
ObjPtr<mirror::Class> WellKnownClasses::ToClass(jclass global_jclass) {
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 26475ae..ed7524c 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -153,8 +153,8 @@
/// CHECK-DAG: <<Pos:i\d+>> ParameterValue
/// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
- /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
- /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
/// CHECK-DAG: Return [<<Char>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
@@ -174,8 +174,8 @@
/// CHECK-DAG: <<Pos:i\d+>> ParameterValue
/// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
- /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
- /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
/// CHECK-DAG: Return [<<Char>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
diff --git a/test/631-checker-get-class/expected.txt b/test/631-checker-get-class/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/631-checker-get-class/expected.txt
diff --git a/test/631-checker-get-class/info.txt b/test/631-checker-get-class/info.txt
new file mode 100644
index 0000000..f236a22
--- /dev/null
+++ b/test/631-checker-get-class/info.txt
@@ -0,0 +1,4 @@
+Checker test to make sure we recognize the pattern:
+if (foo.getClass() == Foo.class)
+
+For doing better type propagation.
diff --git a/test/631-checker-get-class/src/Main.java b/test/631-checker-get-class/src/Main.java
new file mode 100644
index 0000000..61c0adf
--- /dev/null
+++ b/test/631-checker-get-class/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * 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 {
+
+ /// CHECK-START: int Main.bar(Main) inliner (before)
+ /// CHECK: InvokeVirtual
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: int Main.bar(Main) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+ public static int bar(Main m) {
+ if (m.getClass() == Main.class) {
+ return m.foo();
+ }
+ return 4;
+ }
+
+ public int foo() {
+ return 42;
+ }
+
+ /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Eq:z\d+>> {{Equal|NotEqual}}
+ /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Constant>>]
+ public static boolean classEquality1() {
+ return new Main().getClass() == Main.class;
+ }
+
+ /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Eq:z\d+>> {{Equal|NotEqual}}
+ /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Constant>>]
+ public static boolean classEquality2() {
+ Object o = new SubMain();
+ return o.getClass() == Main.class;
+ }
+
+ /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Eq:z\d+>> {{Equal|NotEqual}}
+ /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Constant:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Constant>>]
+ public static boolean classEquality3() {
+ return new Main().getClass() != Main.class;
+ }
+
+ /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Eq:z\d+>> {{Equal|NotEqual}}
+ /// CHECK-DAG: <<Select:i\d+>> Select [{{i\d+}},{{i\d+}},<<Eq>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Constant:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Constant>>]
+ public static boolean classEquality4() {
+ Object o = new SubMain();
+ return o.getClass() != Main.class;
+ }
+
+ public static void main(String[] args) {
+ int actual = bar(new Main());
+ if (actual != 42) {
+ throw new Error("Expected 42, got " + actual);
+ }
+ actual = bar(new SubMain());
+ if (actual != 4) {
+ throw new Error("Expected 4, got " + actual);
+ }
+ }
+}
+
+class SubMain extends Main {
+}
diff --git a/test/632-checker-char-at-bounds/expected.txt b/test/632-checker-char-at-bounds/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/632-checker-char-at-bounds/expected.txt
diff --git a/test/632-checker-char-at-bounds/info.txt b/test/632-checker-char-at-bounds/info.txt
new file mode 100644
index 0000000..10b9a44
--- /dev/null
+++ b/test/632-checker-char-at-bounds/info.txt
@@ -0,0 +1,2 @@
+Regression test for the optimization of String.charAt, which
+had its SSA dependency incorrect with its corresponding bound check.
diff --git a/test/632-checker-char-at-bounds/src/Main.java b/test/632-checker-char-at-bounds/src/Main.java
new file mode 100644
index 0000000..65022d0
--- /dev/null
+++ b/test/632-checker-char-at-bounds/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 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.
+ */
+
+public class Main {
+
+ /// CHECK-START: void Main.main(java.lang.String[]) licm (after)
+ /// CHECK-DAG: <<NullCheck:l\d+>> NullCheck
+ /// CHECK-DAG: <<BoundsCheck:i\d+>> BoundsCheck
+ /// CHECK-DAG: ArrayGet [<<NullCheck>>,<<BoundsCheck>>]
+ public static void main(String[] args) {
+ try {
+ String foo = myString;
+ foo.getClass(); // Make sure the null check is not in the loop.
+ char c = 0;
+ for (int i = 0; i < 10; i++) {
+ // The charAt may be licm'ed, but it has to be licm'ed with its
+ // bounds check.
+ c = foo.charAt(10000000);
+ }
+ System.out.println(c);
+ } catch (StringIndexOutOfBoundsException e) {
+ // Expected
+ }
+ }
+
+ static String myString = "foo";
+}
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 1557d45..f74c1fc 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "tagging.h"
-
#include <iostream>
#include <pthread.h>
#include <stdio.h>
@@ -44,6 +42,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error setting tag: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -56,6 +55,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error getting tag: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
return tag;
}
@@ -90,6 +90,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Failure running GetLoadedClasses: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -138,18 +139,6 @@
return resultArray;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test903HelloTagging
} // namespace art
diff --git a/test/903-hello-tagging/tagging.h b/test/903-hello-tagging/tagging.h
deleted file mode 100644
index f062d44..0000000
--- a/test/903-hello-tagging/tagging.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_903_HELLO_TAGGING_TAGGING_H_
-#define ART_TEST_903_HELLO_TAGGING_TAGGING_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test903HelloTagging {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test903HelloTagging
-} // namespace art
-
-#endif // ART_TEST_903_HELLO_TAGGING_TAGGING_H_
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 9261a9f..95eab0c 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "tracking.h"
-
#include <iostream>
#include <pthread.h>
#include <stdio.h>
@@ -69,6 +67,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error setting callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -84,22 +83,10 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error enabling/disabling allocation tracking: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr);
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test904ObjectAllocation
} // namespace art
diff --git a/test/904-object-allocation/tracking.h b/test/904-object-allocation/tracking.h
deleted file mode 100644
index 21c1837..0000000
--- a/test/904-object-allocation/tracking.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_904_OBJECT_ALLOCATION_TRACKING_H_
-#define ART_TEST_904_OBJECT_ALLOCATION_TRACKING_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test904ObjectAllocation {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test904ObjectAllocation
-} // namespace art
-
-#endif // ART_TEST_904_OBJECT_ALLOCATION_TRACKING_H_
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index fc43acc..7b26d79 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "tracking_free.h"
-
#include <iostream>
#include <pthread.h>
#include <stdio.h>
@@ -50,6 +48,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error setting callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -64,6 +63,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error enabling/disabling object-free callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -80,17 +80,5 @@
return ret;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test905ObjectFree
} // namespace art
diff --git a/test/905-object-free/tracking_free.h b/test/905-object-free/tracking_free.h
deleted file mode 100644
index ba4aa43..0000000
--- a/test/905-object-free/tracking_free.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
-#define ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test905ObjectFree {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test905ObjectFree
-} // namespace art
-
-#endif // ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 8dac89d..1362d47 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "iterate_heap.h"
-
#include <iostream>
#include <pthread.h>
#include <stdio.h>
@@ -61,6 +59,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Failure running IterateThroughHeap: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return false;
}
return true;
@@ -173,17 +172,5 @@
Run(heap_filter, klass_filter, &config);
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test906IterateHeap
} // namespace art
diff --git a/test/906-iterate-heap/iterate_heap.h b/test/906-iterate-heap/iterate_heap.h
deleted file mode 100644
index f25cdba..0000000
--- a/test/906-iterate-heap/iterate_heap.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
-#define ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test906IterateHeap {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test906IterateHeap
-} // namespace art
-
-#endif // ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index afbb774..5bda7eb 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "get_loaded_classes.h"
-
#include <iostream>
#include <pthread.h>
#include <stdio.h>
@@ -48,6 +46,7 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetLoadedClasses: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -64,17 +63,5 @@
return ret;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test907GetLoadedClasses
} // namespace art
diff --git a/test/907-get-loaded-classes/get_loaded_classes.h b/test/907-get-loaded-classes/get_loaded_classes.h
deleted file mode 100644
index 4d27f89..0000000
--- a/test/907-get-loaded-classes/get_loaded_classes.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_
-#define ART_TEST_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test907GetLoadedClasses {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test907GetLoadedClasses
-} // namespace art
-
-#endif // ART_TEST_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_
diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc
index 771d1ad..59801ff 100644
--- a/test/908-gc-start-finish/gc_callbacks.cc
+++ b/test/908-gc-start-finish/gc_callbacks.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "gc_callbacks.h"
-
#include <stdio.h>
#include <string.h>
@@ -51,6 +49,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error setting callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -65,6 +64,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error enabling/disabling gc callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
ret = jvmti_env->SetEventNotificationMode(
enable ? JVMTI_ENABLE : JVMTI_DISABLE,
@@ -74,6 +74,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error enabling/disabling gc callbacks: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -91,17 +92,5 @@
return result;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test908GcStartFinish
} // namespace art
diff --git a/test/908-gc-start-finish/gc_callbacks.h b/test/908-gc-start-finish/gc_callbacks.h
deleted file mode 100644
index 177a4eb..0000000
--- a/test/908-gc-start-finish/gc_callbacks.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_908_GC_START_FINISH_GC_CALLBACKS_H_
-#define ART_TEST_908_GC_START_FINISH_GC_CALLBACKS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test908GcStartFinish {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test908GcStartFinish
-} // namespace art
-
-#endif // ART_TEST_908_GC_START_FINISH_GC_CALLBACKS_H_
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index 9a74799..c913b3f 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -1,15 +1,59 @@
[toString, ()Ljava/lang/String;, null]
class java.lang.Object
1
+Max locals: 3
+Argument size: 1
+Location start: 0
+Location end: 40
+Is native: false
+Is obsolete: false
+Is synthetic: false
[charAt, (I)C, null]
class java.lang.String
257
+Max locals: JVMTI_ERROR_NATIVE_METHOD
+Argument size: JVMTI_ERROR_NATIVE_METHOD
+Location start: JVMTI_ERROR_NATIVE_METHOD
+Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
[sqrt, (D)D, null]
class java.lang.Math
265
+Max locals: JVMTI_ERROR_NATIVE_METHOD
+Argument size: JVMTI_ERROR_NATIVE_METHOD
+Location start: JVMTI_ERROR_NATIVE_METHOD
+Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
[add, (Ljava/lang/Object;)Z, null]
interface java.util.List
1025
+Max locals: 0
+Argument size: 0
+Location start: 0
+Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
[run, ()V, null]
class $Proxy0
17
+Max locals: 0
+Argument size: 0
+Location start: 0
+Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
+class Main$NestedSynthetic
+4104
+Max locals: 1
+Argument size: 0
+Location start: 0
+Location end: 2
+Is native: false
+Is obsolete: false
+Is synthetic: true
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index 3ed91d7..f60fabb 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "methods.h"
-
#include <stdio.h>
#include "base/macros.h"
@@ -41,6 +39,7 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetMethodName: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -72,6 +71,7 @@
char* err;
jvmti_env->GetErrorName(result2, &err);
printf("Failure running GetMethodName(null, null, null): %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -88,6 +88,7 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetMethodDeclaringClass: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -104,22 +105,104 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetMethodModifiers: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return 0;
}
return modifiers;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
+extern "C" JNIEXPORT jint JNICALL Java_Main_getMaxLocals(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jint max_locals;
+ jvmtiError result = jvmti_env->GetMaxLocals(id, &max_locals);
+ if (JvmtiErrorToException(env, result)) {
+ return -1;
}
- SetAllCapabilities(jvmti_env);
- return 0;
+
+ return max_locals;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getArgumentsSize(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jint arguments;
+ jvmtiError result = jvmti_env->GetArgumentsSize(id, &arguments);
+ if (JvmtiErrorToException(env, result)) {
+ return -1;
+ }
+
+ return arguments;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationStart(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jlong start;
+ jlong end;
+ jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
+ if (JvmtiErrorToException(env, result)) {
+ return -1;
+ }
+
+ return start;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationEnd(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jlong start;
+ jlong end;
+ jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
+ if (JvmtiErrorToException(env, result)) {
+ return -1;
+ }
+
+ return end;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodNative(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jboolean is_native;
+ jvmtiError result = jvmti_env->IsMethodNative(id, &is_native);
+ if (JvmtiErrorToException(env, result)) {
+ return JNI_FALSE;
+ }
+
+ return is_native;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodObsolete(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jboolean is_obsolete;
+ jvmtiError result = jvmti_env->IsMethodObsolete(id, &is_obsolete);
+ if (JvmtiErrorToException(env, result)) {
+ return JNI_FALSE;
+ }
+
+ return is_obsolete;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodSynthetic(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jboolean is_synthetic;
+ jvmtiError result = jvmti_env->IsMethodSynthetic(id, &is_synthetic);
+ if (JvmtiErrorToException(env, result)) {
+ return JNI_FALSE;
+ }
+
+ return is_synthetic;
}
} // namespace Test910Methods
diff --git a/test/910-methods/methods.h b/test/910-methods/methods.h
deleted file mode 100644
index 93d1874..0000000
--- a/test/910-methods/methods.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_910_METHODS_METHODS_H_
-#define ART_TEST_910_METHODS_METHODS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test910Methods {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test910Methods
-} // namespace art
-
-#endif // ART_TEST_910_METHODS_METHODS_H_
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
index 3459134..bf25a0d 100644
--- a/test/910-methods/src/Main.java
+++ b/test/910-methods/src/Main.java
@@ -32,6 +32,10 @@
testMethod("java.util.List", "add", Object.class);
testMethod(getProxyClass(), "run");
+
+ // Find a synthetic method in the dummy inner class. Do not print the name. Javac and Jack
+ // disagree on the naming of synthetic accessors.
+ testMethod(findSyntheticMethod(), NestedSynthetic.class, false);
}
private static Class<?> proxyClass = null;
@@ -54,8 +58,17 @@
private static void testMethod(Class<?> base, String methodName, Class<?>... types)
throws Exception {
Method m = base.getDeclaredMethod(methodName, types);
+ testMethod(m, base, true);
+ }
+
+ private static void testMethod(Method m, Class<?> base, boolean printName) {
String[] result = getMethodName(m);
- System.out.println(Arrays.toString(result));
+ if (!result[0].equals(m.getName())) {
+ throw new RuntimeException("Name not equal: " + m.getName() + " vs " + result[0]);
+ }
+ if (printName) {
+ System.out.println(Arrays.toString(result));
+ }
Class<?> declClass = getMethodDeclaringClass(m);
if (base != declClass) {
@@ -68,9 +81,67 @@
throw new RuntimeException("Modifiers not equal: " + m.getModifiers() + " vs " + modifiers);
}
System.out.println(modifiers);
+
+ System.out.print("Max locals: ");
+ try {
+ System.out.println(getMaxLocals(m));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ System.out.print("Argument size: ");
+ try {
+ System.out.println(getArgumentsSize(m));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ System.out.print("Location start: ");
+ try {
+ System.out.println(getMethodLocationStart(m));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ System.out.print("Location end: ");
+ try {
+ System.out.println(getMethodLocationEnd(m));
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ System.out.println("Is native: " + isMethodNative(m));
+ System.out.println("Is obsolete: " + isMethodObsolete(m));
+ System.out.println("Is synthetic: " + isMethodSynthetic(m));
+ }
+
+ private static class NestedSynthetic {
+ // Accessing this private field will create a synthetic accessor method;
+ private static String dummy;
+ }
+
+ private static void dummyAccess() {
+ System.out.println(NestedSynthetic.dummy);
+ }
+
+ private static Method findSyntheticMethod() throws Exception {
+ Method methods[] = NestedSynthetic.class.getDeclaredMethods();
+ for (Method m : methods) {
+ if (m.isSynthetic()) {
+ return m;
+ }
+ }
+ throw new RuntimeException("Could not find synthetic method");
}
private static native String[] getMethodName(Method m);
private static native Class<?> getMethodDeclaringClass(Method m);
private static native int getMethodModifiers(Method m);
+ private static native int getMaxLocals(Method m);
+ private static native int getArgumentsSize(Method m);
+ private static native long getMethodLocationStart(Method m);
+ private static native long getMethodLocationEnd(Method m);
+ private static native boolean isMethodNative(Method m);
+ private static native boolean isMethodObsolete(Method m);
+ private static native boolean isMethodSynthetic(Method m);
}
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 57f6a92..cca163b 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "stack_trace.h"
-
#include <inttypes.h>
#include <memory>
#include <stdio.h>
@@ -63,6 +61,7 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetStackTrace: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
}
@@ -77,6 +76,7 @@
char* err;
jvmti_env->GetErrorName(result2, &err);
printf("Failure running GetMethodName: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
}
@@ -94,6 +94,7 @@
char* err;
jvmti_env->GetErrorName(line_result, &err);
printf("Failure running GetLineNumberTable: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
line_number_table = nullptr;
@@ -139,17 +140,5 @@
return CreateObjectArray(env, count, "[Ljava/lang/String;", callback);
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test911GetStackTrace
} // namespace art
diff --git a/test/911-get-stack-trace/stack_trace.h b/test/911-get-stack-trace/stack_trace.h
deleted file mode 100644
index eba2a91..0000000
--- a/test/911-get-stack-trace/stack_trace.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
-#define ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test911GetStackTrace {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test911GetStackTrace
-} // namespace art
-
-#endif // ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 838a92a..69301c7 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "classes.h"
-
#include <stdio.h>
#include "base/macros.h"
@@ -29,6 +27,20 @@
namespace art {
namespace Test912Classes {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isModifiableClass(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jboolean res = JNI_FALSE;
+ jvmtiError result = jvmti_env->IsModifiableClass(klass, &res);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsModifiableClass: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return JNI_FALSE;
+ }
+ return res;
+}
+
extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
char* sig;
@@ -38,6 +50,7 @@
char* err;
jvmti_env->GetErrorName(result, &err);
printf("Failure running GetClassSignature: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return nullptr;
}
@@ -61,16 +74,152 @@
return ret;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jboolean is_interface = JNI_FALSE;
+ jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsInterface: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return JNI_FALSE;
}
- SetAllCapabilities(jvmti_env);
- return 0;
+ return is_interface;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jboolean is_array_class = JNI_FALSE;
+ jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsArrayClass: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return JNI_FALSE;
+ }
+ return is_array_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getClassModifiers(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint mod;
+ jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassModifiers: %s\n", err);
+ return JNI_FALSE;
+ }
+ return mod;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jfieldID* fields = nullptr;
+ jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassFields: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ jint modifiers;
+ // Ignore any errors for simplicity.
+ jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedField(klass,
+ fields[i],
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ };
+ jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+ if (fields != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jmethodID* methods = nullptr;
+ jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassMethods: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ jint modifiers;
+ // Ignore any errors for simplicity.
+ jvmti_env->GetMethodModifiers(methods[i], &modifiers);
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedMethod(klass,
+ methods[i],
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ };
+ jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
+ if (methods != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods));
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jclass* classes = nullptr;
+ jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetImplementedInterfaces: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ return classes[i];
+ };
+ jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
+ if (classes != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint status;
+ jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassStatus: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return JNI_FALSE;
+ }
+ return status;
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jobject classloader;
+ jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassLoader: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return nullptr;
+ }
+ return classloader;
}
} // namespace Test912Classes
diff --git a/test/912-classes/classes.h b/test/912-classes/classes.h
deleted file mode 100644
index 62fb203..0000000
--- a/test/912-classes/classes.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef ART_TEST_912_CLASSES_CLASSES_H_
-#define ART_TEST_912_CLASSES_CLASSES_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test912Classes {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test912Classes
-} // namespace art
-
-#endif // ART_TEST_912_CLASSES_CLASSES_H_
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 71b22f4..44c861a 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -1,7 +1,45 @@
[Ljava/lang/Object;, null]
+1
[Ljava/lang/String;, null]
+11
[Ljava/lang/Math;, null]
+11
[Ljava/util/List;, null]
+601
[L$Proxy0;, null]
+11
[I, null]
+411
[[D, null]
+411
+int interface=false array=false modifiable=false
+$Proxy0 interface=false array=false modifiable=false
+java.lang.Runnable interface=true array=false modifiable=false
+java.lang.String interface=false array=false modifiable=true
+[I interface=false array=true modifiable=false
+[Ljava.lang.Runnable; interface=false array=true modifiable=false
+[Ljava.lang.String; interface=false array=true modifiable=false
+[public static final int java.lang.Integer.BYTES, static final char[] java.lang.Integer.DigitOnes, static final char[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
+[]
+[]
+[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static int java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static void java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
+[]
+[]
+int 100000
+class [Ljava.lang.String; 10000
+class java.lang.Object 111
+class Main$TestForNonInit 11
+class Main$TestForInitFail 1001
+int []
+class [Ljava.lang.String; []
+class java.lang.Object []
+interface Main$InfA []
+interface Main$InfB [interface Main$InfA]
+interface Main$InfC [interface Main$InfB]
+class Main$ClassA [interface Main$InfA]
+class Main$ClassB [interface Main$InfB]
+class Main$ClassC [interface Main$InfA, interface Main$InfC]
+class java.lang.String null
+class [Ljava.lang.String; null
+interface Main$InfA dalvik.system.PathClassLoader
+class $Proxy0 dalvik.system.PathClassLoader
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 025584e..e627d42 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -34,6 +34,48 @@
testClass(int.class);
testClass(double[].class);
+
+ testClassType(int.class);
+ testClassType(getProxyClass());
+ testClassType(Runnable.class);
+ testClassType(String.class);
+
+ testClassType(int[].class);
+ testClassType(Runnable[].class);
+ testClassType(String[].class);
+
+ testClassFields(Integer.class);
+ testClassFields(int.class);
+ testClassFields(String[].class);
+
+ testClassMethods(Integer.class);
+ testClassMethods(int.class);
+ testClassMethods(String[].class);
+
+ testClassStatus(int.class);
+ testClassStatus(String[].class);
+ testClassStatus(Object.class);
+ testClassStatus(TestForNonInit.class);
+ try {
+ System.out.println(TestForInitFail.dummy);
+ } catch (ExceptionInInitializerError e) {
+ }
+ testClassStatus(TestForInitFail.class);
+
+ testInterfaces(int.class);
+ testInterfaces(String[].class);
+ testInterfaces(Object.class);
+ testInterfaces(InfA.class);
+ testInterfaces(InfB.class);
+ testInterfaces(InfC.class);
+ testInterfaces(ClassA.class);
+ testInterfaces(ClassB.class);
+ testInterfaces(ClassC.class);
+
+ testClassLoader(String.class);
+ testClassLoader(String[].class);
+ testClassLoader(InfA.class);
+ testClassLoader(getProxyClass());
}
private static Class<?> proxyClass = null;
@@ -55,7 +97,95 @@
private static void testClass(Class<?> base) throws Exception {
String[] result = getClassSignature(base);
System.out.println(Arrays.toString(result));
+ int mod = getClassModifiers(base);
+ if (mod != base.getModifiers()) {
+ throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
+ }
+ System.out.println(Integer.toHexString(mod));
}
+ private static void testClassType(Class<?> c) throws Exception {
+ boolean isInterface = isInterface(c);
+ boolean isArray = isArrayClass(c);
+ boolean isModifiable = isModifiableClass(c);
+ System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
+ " modifiable=" + isModifiable);
+ }
+
+ private static void testClassFields(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassFields(c)));
+ }
+
+ private static void testClassMethods(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassMethods(c)));
+ }
+
+ private static void testClassStatus(Class<?> c) {
+ System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
+ }
+
+ private static void testInterfaces(Class<?> c) {
+ System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
+ }
+
+ private static boolean IsBootClassLoader(ClassLoader l) {
+ // Hacky check for Android's fake boot classloader.
+ return l.getClass().getName().equals("java.lang.BootClassLoader");
+ }
+
+ private static void testClassLoader(Class<?> c) {
+ Object cl = getClassLoader(c);
+ System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
+ if (cl == null) {
+ if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
+ throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
+ }
+ } else {
+ if (!(cl instanceof ClassLoader)) {
+ throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
+ ")");
+ }
+ if (cl != c.getClassLoader()) {
+ throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
+ }
+ }
+ }
+
+ private static native boolean isModifiableClass(Class<?> c);
private static native String[] getClassSignature(Class<?> c);
+
+ private static native boolean isInterface(Class<?> c);
+ private static native boolean isArrayClass(Class<?> c);
+
+ private static native int getClassModifiers(Class<?> c);
+
+ private static native Object[] getClassFields(Class<?> c);
+ private static native Object[] getClassMethods(Class<?> c);
+ private static native Class[] getImplementedInterfaces(Class<?> c);
+
+ private static native int getClassStatus(Class<?> c);
+
+ private static native Object getClassLoader(Class<?> c);
+
+ private static class TestForNonInit {
+ public static double dummy = Math.random(); // So it can't be compile-time initialized.
+ }
+
+ private static class TestForInitFail {
+ public static int dummy = ((int)Math.random())/0; // So it throws when initializing.
+ }
+
+ public static interface InfA {
+ }
+ public static interface InfB extends InfA {
+ }
+ public static interface InfC extends InfB {
+ }
+
+ public abstract static class ClassA implements InfA {
+ }
+ public abstract static class ClassB extends ClassA implements InfB {
+ }
+ public abstract static class ClassC implements InfA, InfC {
+ }
}
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 49ab7dd..6759919 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "heaps.h"
-
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
@@ -49,6 +47,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Error forcing a garbage collection: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
}
}
@@ -106,6 +105,7 @@
char* err;
jvmti_env->GetErrorName(ret, &err);
printf("Failure running FollowReferences: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return false;
}
return true;
@@ -493,17 +493,5 @@
return ret;
}
-// Don't do anything
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetAllCapabilities(jvmti_env);
- return 0;
-}
-
} // namespace Test913Heaps
} // namespace art
diff --git a/test/918-fields/build b/test/918-fields/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/918-fields/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/918-fields/expected.txt b/test/918-fields/expected.txt
new file mode 100644
index 0000000..39d3e70
--- /dev/null
+++ b/test/918-fields/expected.txt
@@ -0,0 +1,16 @@
+[PI, D, null]
+class java.lang.Math
+25
+false
+[value, I, null]
+class java.lang.Integer
+18
+false
+[this$0, LMain;, null]
+class Main$Foo
+4112
+true
+[VAL, I, null]
+interface Main$Bar
+25
+false
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
new file mode 100644
index 0000000..7d29912
--- /dev/null
+++ b/test/918-fields/fields.cc
@@ -0,0 +1,134 @@
+/*
+ * 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 "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test918Fields {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getFieldName(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ char* name;
+ char* sig;
+ char* gen;
+ // Note: technically putting the caller class here is wrong, but we don't need it, anyways.
+ jvmtiError result = jvmti_env->GetFieldName(klass, id, &name, &sig, &gen);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldName: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ if (i == 0) {
+ return name == nullptr ? nullptr : env->NewStringUTF(name);
+ } else if (i == 1) {
+ return sig == nullptr ? nullptr : env->NewStringUTF(sig);
+ } else {
+ return gen == nullptr ? nullptr : env->NewStringUTF(gen);
+ }
+ };
+ jobjectArray ret = CreateObjectArray(env, 3, "java/lang/String", callback);
+
+ // Need to deallocate the strings.
+ if (name != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+ }
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+
+ // Also run GetMethodName with all parameter pointers null to check for segfaults.
+ jvmtiError result2 = jvmti_env->GetFieldName(klass, id, nullptr, nullptr, nullptr);
+ if (result2 != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result2, &err);
+ printf("Failure running GetFieldName(null, null, null): %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return nullptr;
+ }
+
+ return ret;
+}
+
+extern "C" JNIEXPORT jclass JNICALL Java_Main_getFieldDeclaringClass(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jclass declaring_class;
+ jvmtiError result = jvmti_env->GetFieldDeclaringClass(klass, id, &declaring_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldDeclaringClass: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return nullptr;
+ }
+
+ return declaring_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldModifiers(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jint modifiers;
+ jvmtiError result = jvmti_env->GetFieldModifiers(klass, id, &modifiers);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldModifiers: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return 0;
+ }
+
+ return modifiers;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isFieldSynthetic(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jboolean synth;
+ jvmtiError result = jvmti_env->IsFieldSynthetic(klass, id, &synth);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsFieldSynthetic: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return 0;
+ }
+
+ return synth;
+}
+
+} // namespace Test918Fields
+} // namespace art
diff --git a/test/918-fields/info.txt b/test/918-fields/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/918-fields/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/918-fields/run b/test/918-fields/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/918-fields/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/918-fields/src/Main.java b/test/918-fields/src/Main.java
new file mode 100644
index 0000000..8af6e7b
--- /dev/null
+++ b/test/918-fields/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testField(Math.class, "PI");
+ testField(Integer.class, "value");
+ testField(Foo.class, "this$0");
+ testField(Bar.class, "VAL");
+ }
+
+ private static void testField(Class<?> base, String fieldName)
+ throws Exception {
+ Field f = base.getDeclaredField(fieldName);
+ String[] result = getFieldName(f);
+ System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getFieldDeclaringClass(f);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getFieldModifiers(f);
+ if (modifiers != f.getModifiers()) {
+ throw new RuntimeException("Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
+
+ boolean synth = isFieldSynthetic(f);
+ if (synth != f.isSynthetic()) {
+ throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ }
+ System.out.println(synth);
+ }
+
+ private static native String[] getFieldName(Field f);
+ private static native Class<?> getFieldDeclaringClass(Field f);
+ private static native int getFieldModifiers(Field f);
+ private static native boolean isFieldSynthetic(Field f);
+
+ private class Foo {
+ }
+
+ private static interface Bar {
+ public static int VAL = 1;
+ }
+}
diff --git a/test/919-obsolete-fields/build b/test/919-obsolete-fields/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/919-obsolete-fields/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/919-obsolete-fields/expected.txt b/test/919-obsolete-fields/expected.txt
new file mode 100644
index 0000000..4caefc6
--- /dev/null
+++ b/test/919-obsolete-fields/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+pre Start private method call - Transformed
+Hello - private - Transformed
+post Start private method call - Transformed
+Not doing anything here
+pre Finish private method call - Transformed
+Goodbye - private - Transformed
+post Finish private method call - Transformed
diff --git a/test/919-obsolete-fields/info.txt b/test/919-obsolete-fields/info.txt
new file mode 100644
index 0000000..97739ca
--- /dev/null
+++ b/test/919-obsolete-fields/info.txt
@@ -0,0 +1 @@
+Tests obsolete method field access support
diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/919-obsolete-fields/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/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
new file mode 100644
index 0000000..895c7a3
--- /dev/null
+++ b/test/919-obsolete-fields/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.util.function.Consumer;
+import java.util.Base64;
+
+public class Main {
+
+ // What follows is the base64 encoded representation of the following class:
+ //
+ // import java.util.function.Consumer;
+ //
+ // class Transform {
+ // private Consumer<String> reporter;
+ // public Transform(Consumer<String> reporter) {
+ // this.reporter = reporter;
+ // }
+ //
+ // private void Start() {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start();
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish();
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQANAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
+ "LAcALQcALgEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
+ "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
+ "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
+ "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
+ "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
+ "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwAEwAZDAAPABABAB1IZWxs" +
+ "byAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAcALwwAMAAxAQAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAEAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQM" +
+ "ABgAGQEALHBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkBwAyDAAz" +
+ "ABkBACxwcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAGgAZAQAt" +
+ "cG9zdCBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAQAJVHJhbnNmb3Jt" +
+ "AQAQamF2YS9sYW5nL09iamVjdAEAG2phdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcgEABmFjY2Vw" +
+ "dAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgAgAA0A" +
+ "DgAAAAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAA" +
+ "AAEAFgAAAA4AAwAAABUABAAWAAkAFwARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAIS" +
+ "A7kABAIAsQAAAAEAFgAAAAoAAgAAABoACwAbAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkA" +
+ "BAIAsQAAAAEAFgAAAAoAAgAAAB4ACwAfAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIA" +
+ "KrcAByq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAA" +
+ "IgAIAAAAIgALACMADwAkABoAJQAgACYAKwAnAC8AKAA6ACkAAQAdAAAAAgAe");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAw/b59wCwTlSVDmuhPEezuK3oe0rtT4ujMBQAAcAAAAHhWNBIAAAAAAAAAAAgFAAAd" +
+ "AAAAcAAAAAYAAADkAAAABAAAAPwAAAABAAAALAEAAAcAAAA0AQAAAQAAAGwBAABABAAAjAEAAJoC" +
+ "AACdAgAAoAIAAKgCAACsAgAAsgIAALoCAADbAgAA+gIAAAcDAAAmAwAAOgMAAFADAABkAwAAggMA" +
+ "AKEDAACoAwAAuAMAALsDAAC/AwAAxwMAANsDAAAKBAAAOAQAAGYEAACTBAAAnQQAAKIEAACpBAAA" +
+ "CAAAAAkAAAAKAAAACwAAAA4AAAARAAAAEQAAAAUAAAAAAAAAEgAAAAUAAACEAgAAEgAAAAUAAACM" +
+ "AgAAEgAAAAUAAACUAgAAAAAEABkAAAAAAAMAAgAAAAAAAAAFAAAAAAAAAA8AAAAAAAIAGwAAAAIA" +
+ "AAACAAAAAwAAABoAAAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAQAAAAZAIAAO8EAAAAAAAAAQAA" +
+ "ANEEAAABAAAA3wQAAAIAAgABAAAAsAQAAAYAAABwEAQAAABbAQAADgADAAEAAgAAALgEAAAJAAAA" +
+ "VCAAABsBBgAAAHIgBgAQAA4AAAADAAEAAgAAAL4EAAAJAAAAVCAAABsBBwAAAHIgBgAQAA4AAAAE" +
+ "AAIAAgAAAMQEAAAqAAAAVCAAABsBGAAAAHIgBgAQAHAQAgACAFQgAAAbARYAAAByIAYAEAByEAUA" +
+ "AwBUIAAAGwEXAAAAciAGABAAcBABAAIAVCAAABsBFQAAAHIgBgAQAA4AAAAAAAEAAAABAAAAAAAA" +
+ "AAAAAACMAQAAAAAAAJQBAAABAAAAAgAAAAEAAAADAAAAAQAAAAQAASgAATwABjxpbml0PgACPjsA" +
+ "BD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8gLSBw" +
+ "cml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1MZGFsdmlrL2Fubm90YXRpb24vU2ln" +
+ "bmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEv" +
+ "bGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAB1MamF2YS91dGlsL2Z1" +
+ "bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAGYWNjZXB0ABJl" +
+ "bWl0dGVyOiBqYWNrLTQuMTkALXBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFu" +
+ "c2Zvcm1lZAAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHBy" +
+ "ZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJp" +
+ "dmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFs" +
+ "dWUAFQEABw48LQAeAAcOhwAaAAcOhwAiAQAHDoc8hzyHPIcAAgEBHBwEFw0XARcMFwMCAQEcHAUX" +
+ "ABcNFwEXDBcEAAEDAQACAIGABJwDAQK4AwEC3AMDAYAEABAAAAAAAAAAAQAAAAAAAAABAAAAHQAA" +
+ "AHAAAAACAAAABgAAAOQAAAADAAAABAAAAPwAAAAEAAAAAQAAACwBAAAFAAAABwAAADQBAAAGAAAA" +
+ "AQAAAGwBAAADEAAAAgAAAIwBAAABIAAABAAAAJwBAAAGIAAAAQAAAGQCAAABEAAAAwAAAIQCAAAC" +
+ "IAAAHQAAAJoCAAADIAAABAAAALAEAAAEIAAAAgAAANEEAAAAIAAAAQAAAO8EAAAAEAAAAQAAAAgF" +
+ "AAA=");
+
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ TestWatcher w = new TestWatcher();
+ doTest(new Transform(w), w);
+ }
+
+ // TODO Workaround to (1) inability to ensure that current_method is not put into a register by
+ // the JIT and/or (2) inability to deoptimize frames near runtime functions.
+ // TODO Fix one/both of these issues.
+ public static void doCall(Runnable r) {
+ r.run();
+ }
+
+ private static boolean interpreting = true;
+ private static boolean retry = false;
+
+ public static void doTest(Transform t, TestWatcher w) {
+ Runnable do_redefinition = () -> {
+ w.accept("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ };
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+
+ // Try and redefine.
+ t.sayHi(say_nothing);
+ t.sayHi(do_redefinition);
+ t.sayHi(say_nothing);
+
+ // Print output of last run.
+ System.out.print(w.getOutput());
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+}
diff --git a/test/919-obsolete-fields/src/Transform.java b/test/919-obsolete-fields/src/Transform.java
new file mode 100644
index 0000000..abd1d19
--- /dev/null
+++ b/test/919-obsolete-fields/src/Transform.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.util.function.Consumer;
+
+class Transform {
+ private Consumer<String> reporter;
+ public Transform(Consumer<String> reporter) {
+ this.reporter = reporter;
+ }
+
+ private void Start() {
+ reporter.accept("hello - private");
+ }
+
+ private void Finish() {
+ reporter.accept("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ reporter.accept("Pre Start private method call");
+ Start();
+ reporter.accept("Post Start private method call");
+ // TODO Revist with b/33616143
+ // TODO Uncomment this
+ // r.run();
+ // TODO This is a very temporary fix until we get either deoptimization near runtime frames
+ // working, forcing current method to be always read from the stack or both working.
+ Main.doCall(r);
+ reporter.accept("Pre Finish private method call");
+ Finish();
+ reporter.accept("Post Finish private method call");
+ }
+}
diff --git a/test/920-objects/build b/test/920-objects/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/920-objects/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/920-objects/expected.txt b/test/920-objects/expected.txt
new file mode 100644
index 0000000..80feeb9
--- /dev/null
+++ b/test/920-objects/expected.txt
@@ -0,0 +1,10 @@
+class java.lang.Object 8
+class java.lang.Object 8
+class [I 12
+class [I 16
+class [I 20
+class [D 16
+class [D 24
+class [D 32
+class java.lang.String 24
+class java.lang.String 24
diff --git a/test/920-objects/info.txt b/test/920-objects/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/920-objects/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc
new file mode 100644
index 0000000..0553a9d
--- /dev/null
+++ b/test/920-objects/objects.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test920Objects {
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getObjectSize(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jobject object) {
+ jlong size;
+
+ jvmtiError result = jvmti_env->GetObjectSize(object, &size);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetObjectSize: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return -1;
+ }
+
+ return size;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getObjectHashCode(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jobject object) {
+ jint hash;
+
+ jvmtiError result = jvmti_env->GetObjectHashCode(object, &hash);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetObjectHashCode: %s\n", err);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return -1;
+ }
+
+ return hash;
+}
+
+} // namespace Test920Objects
+} // namespace art
diff --git a/test/920-objects/run b/test/920-objects/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/920-objects/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/920-objects/src/Main.java b/test/920-objects/src/Main.java
new file mode 100644
index 0000000..5dbe1a7
--- /dev/null
+++ b/test/920-objects/src/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testObjectSize(new Object());
+ testObjectSize(new Object());
+
+ testObjectSize(new int[0]);
+ testObjectSize(new int[1]);
+ testObjectSize(new int[2]);
+
+ testObjectSize(new double[0]);
+ testObjectSize(new double[1]);
+ testObjectSize(new double[2]);
+
+ testObjectSize(new String("abc"));
+ testObjectSize(new String("wxyz"));
+
+ testObjectHash();
+ }
+
+ private static void testObjectSize(Object o) {
+ System.out.println(o.getClass() + " " + getObjectSize(o));
+ }
+
+ private static void testObjectHash() {
+ Object[] objects = new Object[] {
+ new Object(),
+ new Object(),
+
+ new MyHash(1),
+ new MyHash(1),
+ new MyHash(2)
+ };
+
+ int hashes[] = new int[objects.length];
+
+ for (int i = 0; i < objects.length; i++) {
+ hashes[i] = getObjectHashCode(objects[i]);
+ }
+
+ // Implementation detail: we use the identity hashcode, for simplicity.
+ for (int i = 0; i < objects.length; i++) {
+ int ihash = System.identityHashCode(objects[i]);
+ if (hashes[i] != ihash) {
+ throw new RuntimeException(objects[i] + ": " + hashes[i] + " vs " + ihash);
+ }
+ }
+
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().gc();
+
+ for (int i = 0; i < objects.length; i++) {
+ int newhash = getObjectHashCode(objects[i]);
+ if (hashes[i] != newhash) {
+ throw new RuntimeException(objects[i] + ": " + hashes[i] + " vs " + newhash);
+ }
+ }
+ }
+
+ private static native long getObjectSize(Object o);
+ private static native int getObjectHashCode(Object o);
+
+ private static class MyHash {
+ private int hash;
+
+ public MyHash(int h) {
+ hash = h;
+ }
+
+ public int hashCode() {
+ return hash;
+ }
+ }
+}
diff --git a/test/921-hello-failure/build b/test/921-hello-failure/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/921-hello-failure/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/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt
new file mode 100644
index 0000000..e2665ef
--- /dev/null
+++ b/test/921-hello-failure/expected.txt
@@ -0,0 +1,15 @@
+hello - NewName
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
+hello - NewName
+hello - DifferentAccess
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED)
+hello - DifferentAccess
+hello2 - NewInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - NewInterface
+hello2 - MissingInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - MissingInterface
+hello2 - ReorderInterface
+Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED)
+hello2 - ReorderInterface
diff --git a/test/921-hello-failure/info.txt b/test/921-hello-failure/info.txt
new file mode 100644
index 0000000..9d241c7
--- /dev/null
+++ b/test/921-hello-failure/info.txt
@@ -0,0 +1,7 @@
+Tests for redefinition failure modes.
+
+Tests
+----
+
+- NewName: The name of the class is changed
+- DifferentAccess: Class access is changed from <default> to 'public'
diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run
new file mode 100755
index 0000000..3ef4832
--- /dev/null
+++ b/test/921-hello-failure/run
@@ -0,0 +1,20 @@
+#!/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/921-hello-failure/src/DifferentAccess.java b/test/921-hello-failure/src/DifferentAccess.java
new file mode 100644
index 0000000..d4b16e0
--- /dev/null
+++ b/test/921-hello-failure/src/DifferentAccess.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class DifferentAccess {
+ // The following is a base64 encoding of the following class.
+ // public class NotTransform {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" +
+ "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" +
+ "aWxlAQAOVHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBub3Qg" +
+ "YmUgY2FsbGVkIQwABwAMAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAhAAUABgAAAAAA" +
+ "AgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwAAQAJAAAA" +
+ "IgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQANVRT7zleRLG4E5DhtK7OtoDxZlUQMI5eQAgAAcAAAAHhWNBIAAAAAAAAAAPwBAAAL" +
+ "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACIAQAACAEAAEoB" +
+ "AABSAQAAXwEAAHIBAACGAQAAmgEAALEBAADBAQAAxAEAAMgBAADcAQAAAQAAAAIAAAADAAAABAAA" +
+ "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" +
+ "AAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAAO4BAAAAAAAAAQABAAEAAADjAQAABAAAAHAQAwAA" +
+ "AA4ABAACAAIAAADoAQAACQAAACIAAQAbAQUAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgALTFRy" +
+ "YW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xh" +
+ "bmcvU3RyaW5nOwAVU2hvdWxkIG5vdCBiZSBjYWxsZWQhAA5UcmFuc2Zvcm0uamF2YQABVgACVkwA" +
+ "EmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAIGABIgCAQGgAgwAAAAA" +
+ "AAAAAQAAAAAAAAABAAAACwAAAHAAAAACAAAABQAAAJwAAAADAAAAAgAAALAAAAAFAAAABAAAAMgA" +
+ "AAAGAAAAAQAAAOgAAAABIAAAAgAAAAgBAAABEAAAAQAAAEQBAAACIAAACwAAAEoBAAADIAAAAgAA" +
+ "AOMBAAAAIAAAAQAAAO4BAAAAEAAAAQAAAPwBAAA=");
+
+ public static void doTest(Transform t) {
+ t.sayHi("DifferentAccess");
+ try {
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("DifferentAccess");
+ }
+}
diff --git a/test/913-heaps/heaps.h b/test/921-hello-failure/src/Iface1.java
similarity index 62%
copy from test/913-heaps/heaps.h
copy to test/921-hello-failure/src/Iface1.java
index bd828ac..f53275a 100644
--- a/test/913-heaps/heaps.h
+++ b/test/921-hello-failure/src/Iface1.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-#ifndef ART_TEST_913_HEAPS_HEAPS_H_
-#define ART_TEST_913_HEAPS_HEAPS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test913Heaps {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test913Heaps
-} // namespace art
-
-#endif // ART_TEST_913_HEAPS_HEAPS_H_
+interface Iface1 {
+ void sayHi(String s);
+}
diff --git a/test/913-heaps/heaps.h b/test/921-hello-failure/src/Iface2.java
similarity index 62%
copy from test/913-heaps/heaps.h
copy to test/921-hello-failure/src/Iface2.java
index bd828ac..54cdd90 100644
--- a/test/913-heaps/heaps.h
+++ b/test/921-hello-failure/src/Iface2.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-#ifndef ART_TEST_913_HEAPS_HEAPS_H_
-#define ART_TEST_913_HEAPS_HEAPS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test913Heaps {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test913Heaps
-} // namespace art
-
-#endif // ART_TEST_913_HEAPS_HEAPS_H_
+interface Iface2 {
+ void sayHi(String s);
+}
diff --git a/test/913-heaps/heaps.h b/test/921-hello-failure/src/Iface3.java
similarity index 62%
rename from test/913-heaps/heaps.h
rename to test/921-hello-failure/src/Iface3.java
index bd828ac..819134d 100644
--- a/test/913-heaps/heaps.h
+++ b/test/921-hello-failure/src/Iface3.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,17 +14,6 @@
* limitations under the License.
*/
-#ifndef ART_TEST_913_HEAPS_HEAPS_H_
-#define ART_TEST_913_HEAPS_HEAPS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test913Heaps {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test913Heaps
-} // namespace art
-
-#endif // ART_TEST_913_HEAPS_HEAPS_H_
+interface Iface3 {
+ void sayHi(String s);
+}
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
new file mode 100644
index 0000000..69c48e2
--- /dev/null
+++ b/test/921-hello-failure/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * 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) {
+ System.loadLibrary(args[1]);
+ NewName.doTest(new Transform());
+ DifferentAccess.doTest(new Transform());
+ NewInterface.doTest(new Transform2());
+ MissingInterface.doTest(new Transform2());
+ ReorderInterface.doTest(new Transform2());
+ }
+
+ // Transforms the class. This throws an exception if something goes wrong.
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile) throws Exception;
+}
diff --git a/test/921-hello-failure/src/MissingInterface.java b/test/921-hello-failure/src/MissingInterface.java
new file mode 100644
index 0000000..d17a6de
--- /dev/null
+++ b/test/921-hello-failure/src/MissingInterface.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class MissingInterface {
+ // The following is a base64 encoding of the following class.
+ // class Transform2 implements Iface1 {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAFwoABgAQBwARCAASCgACABMHABQHABUHABYBAAY8aW5pdD4BAAMoKVYBAARDb2Rl" +
+ "AQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3Vy" +
+ "Y2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAIAAkBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91bGQg" +
+ "bm90IGJlIGNhbGxlZCEMAAgADQEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAGSWZh" +
+ "Y2UxACAABQAGAAEABwAAAAIAAAAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEA" +
+ "AAABAAEADAANAAEACgAAACIAAwACAAAACrsAAlkSA7cABL8AAAABAAsAAAAGAAEAAAADAAEADgAA" +
+ "AAIADw==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDiWVay8/Z0/tXQaTTI+QtwTM65gRJVMOusAgAAcAAAAHhWNBIAAAAAAAAAABgCAAAM" +
+ "AAAAcAAAAAYAAACgAAAAAgAAALgAAAAAAAAAAAAAAAQAAADQAAAAAQAAAPAAAACcAQAAEAEAAFoB" +
+ "AABiAQAAbAEAAHoBAACNAQAAoQEAALUBAADMAQAA3QEAAOABAADkAQAA+AEAAAEAAAACAAAAAwAA" +
+ "AAQAAAAFAAAACAAAAAgAAAAFAAAAAAAAAAkAAAAFAAAAVAEAAAEAAAAAAAAAAQABAAsAAAACAAEA" +
+ "AAAAAAMAAAAAAAAAAQAAAAAAAAADAAAATAEAAAcAAAAAAAAACgIAAAAAAAABAAEAAQAAAP8BAAAE" +
+ "AAAAcBADAAAADgAEAAIAAgAAAAQCAAAJAAAAIgACABsBBgAAAHAgAgAQACcAAAABAAAAAAAAAAEA" +
+ "AAAEAAY8aW5pdD4ACExJZmFjZTE7AAxMVHJhbnNmb3JtMjsAEUxqYXZhL2xhbmcvRXJyb3I7ABJM" +
+ "amF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAVU2hvdWxkIG5vdCBiZSBjYWxs" +
+ "ZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjAABXNheUhpAAEA" +
+ "Bw4AAwEABw4AAAABAQCAgASQAgEBqAIMAAAAAAAAAAEAAAAAAAAAAQAAAAwAAABwAAAAAgAAAAYA" +
+ "AACgAAAAAwAAAAIAAAC4AAAABQAAAAQAAADQAAAABgAAAAEAAADwAAAAASAAAAIAAAAQAQAAARAA" +
+ "AAIAAABMAQAAAiAAAAwAAABaAQAAAyAAAAIAAAD/AQAAACAAAAEAAAAKAgAAABAAAAEAAAAYAgAA");
+
+ public static void doTest(Transform2 t) {
+ t.sayHi("MissingInterface");
+ try {
+ Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("MissingInterface");
+ }
+}
diff --git a/test/921-hello-failure/src/NewInterface.java b/test/921-hello-failure/src/NewInterface.java
new file mode 100644
index 0000000..fe77222
--- /dev/null
+++ b/test/921-hello-failure/src/NewInterface.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class NewInterface {
+ // The following is a base64 encoding of the following class.
+ // class Transform2 implements Iface1, Iface2, Iface3 {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAGwoABgASBwATCAAUCgACABUHABYHABcHABgHABkHABoBAAY8aW5pdD4BAAMoKVYB" +
+ "AARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYB" +
+ "AApTb3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAKAAsBAA9qYXZhL2xhbmcvRXJyb3IBABVT" +
+ "aG91bGQgbm90IGJlIGNhbGxlZCEMAAoADwEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0" +
+ "AQAGSWZhY2UxAQAGSWZhY2UyAQAGSWZhY2UzACAABQAGAAMABwAIAAkAAAACAAAACgALAAEADAAA" +
+ "AB0AAQABAAAABSq3AAGxAAAAAQANAAAABgABAAAAAQABAA4ADwABAAwAAAAiAAMAAgAAAAq7AAJZ" +
+ "EgO3AAS/AAAAAQANAAAABgABAAAAAwABABAAAAACABE=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCBWnko4SMXeuXSO3fGJBp0WSlc0HLRr63UAgAAcAAAAHhWNBIAAAAAAAAAAEACAAAO" +
+ "AAAAcAAAAAgAAACoAAAAAgAAAMgAAAAAAAAAAAAAAAQAAADgAAAAAQAAAAABAAC0AQAAIAEAAG4B" +
+ "AAB2AQAAgAEAAIoBAACUAQAAogEAALUBAADJAQAA3QEAAPQBAAAFAgAACAIAAAwCAAAgAgAAAQAA" +
+ "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAAKAAAABwAAAAAAAAALAAAABwAAAGgBAAADAAAA" +
+ "AAAAAAMAAQANAAAABAABAAAAAAAFAAAAAAAAAAMAAAAAAAAABQAAAFwBAAAJAAAAAAAAADICAAAA" +
+ "AAAAAQABAAEAAAAnAgAABAAAAHAQAwAAAA4ABAACAAIAAAAsAgAACQAAACIABAAbAQgAAABwIAIA" +
+ "EAAnAAAAAwAAAAAAAQACAAAAAQAAAAYABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAhMSWZh" +
+ "Y2UzOwAMTFRyYW5zZm9ybTI7ABFMamF2YS9sYW5nL0Vycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7" +
+ "ABJMamF2YS9sYW5nL1N0cmluZzsAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAPVHJhbnNmb3JtMi5q" +
+ "YXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00LjIwAAVzYXlIaQABAAcOAAMBAAcOAAAAAQEAgIAE" +
+ "oAIBAbgCDAAAAAAAAAABAAAAAAAAAAEAAAAOAAAAcAAAAAIAAAAIAAAAqAAAAAMAAAACAAAAyAAA" +
+ "AAUAAAAEAAAA4AAAAAYAAAABAAAAAAEAAAEgAAACAAAAIAEAAAEQAAACAAAAXAEAAAIgAAAOAAAA" +
+ "bgEAAAMgAAACAAAAJwIAAAAgAAABAAAAMgIAAAAQAAABAAAAQAIAAA==");
+
+ public static void doTest(Transform2 t) {
+ t.sayHi("NewInterface");
+ try {
+ Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("NewInterface");
+ }
+}
diff --git a/test/921-hello-failure/src/NewName.java b/test/921-hello-failure/src/NewName.java
new file mode 100644
index 0000000..a6f249a
--- /dev/null
+++ b/test/921-hello-failure/src/NewName.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class NewName {
+ // class NotTransform {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" +
+ "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" +
+ "aWxlAQARTm90VHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBu" +
+ "b3QgYmUgY2FsbGVkIQwABwAMAQAMTm90VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAgAAUA" +
+ "BgAAAAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwA" +
+ "AQAJAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDLV95i5xnv6iUi6uIeDoY5jP5Xe9NP1AiYAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAL" +
+ "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACQAQAACAEAAEoB" +
+ "AABSAQAAYgEAAHUBAACJAQAAnQEAALABAADHAQAAygEAAM4BAADiAQAAAQAAAAIAAAADAAAABAAA" +
+ "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" +
+ "AAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAPQBAAAAAAAAAQABAAEAAADpAQAABAAAAHAQAwAA" +
+ "AA4ABAACAAIAAADuAQAACQAAACIAAQAbAQYAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgAOTE5v" +
+ "dFRyYW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" +
+ "L2xhbmcvU3RyaW5nOwARTm90VHJhbnNmb3JtLmphdmEAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAB" +
+ "VgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAICABIgCAQGg" +
+ "AgAADAAAAAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAFAAAAnAAAAAMAAAACAAAAsAAAAAUA" +
+ "AAAEAAAAyAAAAAYAAAABAAAA6AAAAAEgAAACAAAACAEAAAEQAAABAAAARAEAAAIgAAALAAAASgEA" +
+ "AAMgAAACAAAA6QEAAAAgAAABAAAA9AEAAAAQAAABAAAABAIAAA==");
+
+ public static void doTest(Transform t) {
+ t.sayHi("NewName");
+ try {
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("NewName");
+ }
+}
diff --git a/test/921-hello-failure/src/ReorderInterface.java b/test/921-hello-failure/src/ReorderInterface.java
new file mode 100644
index 0000000..ce78dbc
--- /dev/null
+++ b/test/921-hello-failure/src/ReorderInterface.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+class ReorderInterface {
+ // The following is a base64 encoding of the following class.
+ // class Transform2 implements Iface2, Iface1 {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAGQoABgARBwASCAATCgACABQHABUHABYHABcHABgBAAY8aW5pdD4BAAMoKVYBAARD" +
+ "b2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApT" +
+ "b3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91" +
+ "bGQgbm90IGJlIGNhbGxlZCEMAAkADgEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAG" +
+ "SWZhY2UyAQAGSWZhY2UxACAABQAGAAIABwAIAAAAAgAAAAkACgABAAsAAAAdAAEAAQAAAAUqtwAB" +
+ "sQAAAAEADAAAAAYAAQAAAAEAAQANAA4AAQALAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEADAAA" +
+ "AAYAAQAAAAMAAQAPAAAAAgAQ");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQChWfUC02YEHJZLC4V4pHrGMdqwD8NnzXvAAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAN" +
+ "AAAAcAAAAAcAAACkAAAAAgAAAMAAAAAAAAAAAAAAAAQAAADYAAAAAQAAAPgAAACoAQAAGAEAAGIB" +
+ "AABqAQAAdAEAAH4BAACMAQAAnwEAALMBAADHAQAA3gEAAO8BAADyAQAA9gEAAAoCAAABAAAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAABcAQAAAgAAAAAAAAACAAEA" +
+ "DAAAAAMAAQAAAAAABAAAAAAAAAACAAAAAAAAAAQAAABUAQAACAAAAAAAAAAcAgAAAAAAAAEAAQAB" +
+ "AAAAEQIAAAQAAABwEAMAAAAOAAQAAgACAAAAFgIAAAkAAAAiAAMAGwEHAAAAcCACABAAJwAAAAIA" +
+ "AAABAAAAAQAAAAUABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAxMVHJhbnNmb3JtMjsAEUxq" +
+ "YXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAV" +
+ "U2hvdWxkIG5vdCBiZSBjYWxsZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBq" +
+ "YWNrLTQuMjAABXNheUhpAAEABw4AAwEABw4AAAABAQCAgASYAgEBsAIAAAwAAAAAAAAAAQAAAAAA" +
+ "AAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAgAAAMAAAAAFAAAABAAAANgAAAAGAAAAAQAA" +
+ "APgAAAABIAAAAgAAABgBAAABEAAAAgAAAFQBAAACIAAADQAAAGIBAAADIAAAAgAAABECAAAAIAAA" +
+ "AQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+ public static void doTest(Transform2 t) {
+ t.sayHi("ReorderInterface");
+ try {
+ Main.doCommonClassRedefinition(Transform2.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ }
+ t.sayHi("ReorderInterface");
+ }
+}
diff --git a/test/913-heaps/heaps.h b/test/921-hello-failure/src/Transform.java
similarity index 62%
copy from test/913-heaps/heaps.h
copy to test/921-hello-failure/src/Transform.java
index bd828ac..ee444f0 100644
--- a/test/913-heaps/heaps.h
+++ b/test/921-hello-failure/src/Transform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,17 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_TEST_913_HEAPS_HEAPS_H_
-#define ART_TEST_913_HEAPS_HEAPS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test913Heaps {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test913Heaps
-} // namespace art
-
-#endif // ART_TEST_913_HEAPS_HEAPS_H_
+class Transform {
+ public void sayHi(String name) {
+ System.out.println("hello - " + name);
+ }
+}
diff --git a/test/913-heaps/heaps.h b/test/921-hello-failure/src/Transform2.java
similarity index 62%
copy from test/913-heaps/heaps.h
copy to test/921-hello-failure/src/Transform2.java
index bd828ac..9d949f3 100644
--- a/test/913-heaps/heaps.h
+++ b/test/921-hello-failure/src/Transform2.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,17 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_TEST_913_HEAPS_HEAPS_H_
-#define ART_TEST_913_HEAPS_HEAPS_H_
-
-#include <jni.h>
-
-namespace art {
-namespace Test913Heaps {
-
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
-} // namespace Test913Heaps
-} // namespace art
-
-#endif // ART_TEST_913_HEAPS_HEAPS_H_
+class Transform2 implements Iface1, Iface2 {
+ public void sayHi(String name) {
+ System.out.println("hello2 - " + name);
+ }
+}
diff --git a/test/922-properties/build b/test/922-properties/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/922-properties/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/922-properties/expected.txt b/test/922-properties/expected.txt
new file mode 100644
index 0000000..0be939b
--- /dev/null
+++ b/test/922-properties/expected.txt
@@ -0,0 +1,59 @@
+Recommended properties:
+ "java.class.path": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.library.path": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.info": OK !!!JVMTI_ERROR_NOT_AVAILABLE
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.name": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.vendor": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.version": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Missing recommended properties: [java.vm.info]
+Other properties:
+ "file.encoding": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "file.separator": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.class.version": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.compiler": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.ext.dirs": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.net.preferIPv6Addresses": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.name": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.vendor": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.specification.version": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vendor": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vendor.url": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.version": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.name": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.vendor": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.specification.version": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "java.vm.vendor.url": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "line.separator": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "os.name": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+ "path.separator": OK
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Non-specified property:
+ "java.boot.class.path": ERROR !!!JVMTI_ERROR_NOT_AVAILABLE
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
+Non-specified property (2):
+ "a": OK !!!JVMTI_ERROR_NOT_AVAILABLE
+ Setting value to "abc": !!!JVMTI_ERROR_NOT_AVAILABLE
diff --git a/test/922-properties/info.txt b/test/922-properties/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/922-properties/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc
new file mode 100644
index 0000000..cb732c7
--- /dev/null
+++ b/test/922-properties/properties.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 <stdio.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 Test922Properties {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getSystemProperties(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jint count;
+ char** properties;
+ jvmtiError result = jvmti_env->GetSystemProperties(&count, &properties);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) -> jstring {
+ char* data = properties[i];
+ if (data == nullptr) {
+ return nullptr;
+ }
+ jstring ret = env->NewStringUTF(data);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(data));
+ return ret;
+ };
+ jobjectArray ret = CreateObjectArray(env, count, "java/lang/String", callback);
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(properties));
+
+ return ret;
+}
+
+extern "C" JNIEXPORT jstring JNICALL Java_Main_getSystemProperty(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring key) {
+ ScopedUtfChars string(env, key);
+ if (string.c_str() == nullptr) {
+ return nullptr;
+ }
+
+ char* value = nullptr;
+ jvmtiError result = jvmti_env->GetSystemProperty(string.c_str(), &value);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ jstring ret = (value == nullptr) ? nullptr : env->NewStringUTF(value);
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(value));
+
+ return ret;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setSystemProperty(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring key, jstring value) {
+ ScopedUtfChars key_string(env, key);
+ if (key_string.c_str() == nullptr) {
+ return;
+ }
+ ScopedUtfChars value_string(env, value);
+ if (value_string.c_str() == nullptr) {
+ return;
+ }
+
+ jvmtiError result = jvmti_env->SetSystemProperty(key_string.c_str(), value_string.c_str());
+ if (JvmtiErrorToException(env, result)) {
+ return;
+ }
+}
+
+} // namespace Test922Properties
+} // namespace art
diff --git a/test/922-properties/run b/test/922-properties/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/922-properties/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/922-properties/src/Main.java b/test/922-properties/src/Main.java
new file mode 100644
index 0000000..6cec6e9
--- /dev/null
+++ b/test/922-properties/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * 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.Set;
+import java.util.TreeSet;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ Set<String> recommendedProperties = getRecommendedProperties();
+
+ System.out.println("Recommended properties:");
+ for (String key : recommendedProperties) {
+ checkProperty(key);
+ }
+
+ Set<String> allProperties = getAllProperties();
+
+ Set<String> retained = new TreeSet<String>(recommendedProperties);
+ retained.retainAll(allProperties);
+ if (!retained.equals(recommendedProperties)) {
+ Set<String> missing = new TreeSet<String>(recommendedProperties);
+ missing.removeAll(retained);
+ System.out.println("Missing recommended properties: " + missing);
+ }
+
+ Set<String> nonRecommended = new TreeSet<String>(allProperties);
+ nonRecommended.removeAll(recommendedProperties);
+
+ System.out.println("Other properties:");
+ for (String key : nonRecommended) {
+ checkProperty(key);
+ }
+
+ System.out.println("Non-specified property:");
+ String key = generate(allProperties);
+ checkProperty(key);
+
+ System.out.println("Non-specified property (2):");
+ String key2 = generateUnique(allProperties);
+ checkProperty(key2);
+ }
+
+ private static Set<String> getRecommendedProperties() {
+ Set<String> keys = new TreeSet<String>();
+ keys.add("java.vm.vendor");
+ keys.add("java.vm.version");
+ keys.add("java.vm.name");
+ keys.add("java.vm.info");
+ keys.add("java.library.path");
+ keys.add("java.class.path");
+ return keys;
+ }
+
+ private static Set<String> getAllProperties() {
+ Set<String> keys = new TreeSet<String>();
+ String[] props = getSystemProperties();
+ for (String p : props) {
+ keys.add(p);
+ }
+ return keys;
+ }
+
+ private static boolean equals(String s1, String s2) {
+ if (s1 == null && s2 == null) {
+ return true;
+ } else if (s1 != null) {
+ return s1.equals(s2);
+ } else {
+ return false;
+ }
+ }
+
+ private static void checkProperty(String key) {
+ System.out.print(" \"" + key + "\": ");
+ String err = null;
+ String value = null;
+ try {
+ value = getSystemProperty(key);
+ } catch (RuntimeException e) {
+ err = e.getMessage();
+ }
+ String sysValue = System.getProperty(key);
+ if (equals(value, sysValue)) {
+ System.out.print("OK");
+ if (err != null) {
+ System.out.println(" !!!" + err);
+ } else {
+ System.out.println();
+ }
+ } else {
+ System.out.println("ERROR !!!" + err);
+ }
+
+ System.out.print(" Setting value to \"abc\": ");
+ try {
+ setSystemProperty(key, "abc");
+ System.out.println("SUCCEEDED");
+ } catch (RuntimeException e) {
+ System.out.println("!!!" + e.getMessage());
+ }
+ }
+
+ private static String generateUnique(Set<String> others) {
+ // Construct something. To be deterministic, just use "a+".
+ StringBuilder sb = new StringBuilder("a");
+ for (;;) {
+ String key = sb.toString();
+ if (!others.contains(key)) {
+ return key;
+ }
+ sb.append('a');
+ }
+ }
+
+ private static String generate(Set<String> others) {
+ // First check for something in the overall System properties.
+ TreeSet<String> sysProps = new TreeSet<String>(System.getProperties().stringPropertyNames());
+ sysProps.removeAll(others);
+ if (!sysProps.isEmpty()) {
+ // Find something that starts with "java" or "os," trying to be platform-independent.
+ for (String s: sysProps) {
+ if (s.startsWith("java.") || s.startsWith("os.")) {
+ return s;
+ }
+ }
+ // Just return the first thing.
+ return sysProps.iterator().next();
+ }
+
+ return generateUnique(others);
+ }
+
+ private static native String[] getSystemProperties();
+ private static native String getSystemProperty(String key);
+ private static native void setSystemProperty(String key, String value);
+}
diff --git a/test/923-monitors/build b/test/923-monitors/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/923-monitors/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/923-monitors/expected.txt b/test/923-monitors/expected.txt
new file mode 100644
index 0000000..5fbfb98
--- /dev/null
+++ b/test/923-monitors/expected.txt
@@ -0,0 +1,38 @@
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Lock
+Unlock
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Lock
+Lock
+Unlock
+Unlock
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Wait
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Wait
+JVMTI_ERROR_ILLEGAL_ARGUMENT
+Wait
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Lock
+Wait
+Unlock
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Notify
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Lock
+Notify
+Unlock
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+NotifyAll
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Lock
+NotifyAll
+Unlock
+Unlock
+JVMTI_ERROR_NOT_MONITOR_OWNER
+Done
diff --git a/test/923-monitors/info.txt b/test/923-monitors/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/923-monitors/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc
new file mode 100644
index 0000000..4baa530
--- /dev/null
+++ b/test/923-monitors/monitors.cc
@@ -0,0 +1,86 @@
+/*
+ * 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 <stdio.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 Test923Monitors {
+
+
+static jlong MonitorToLong(jrawMonitorID id) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(id));
+}
+
+static jrawMonitorID LongToMonitor(jlong l) {
+ return reinterpret_cast<jrawMonitorID>(static_cast<uintptr_t>(l));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_createRawMonitor(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jrawMonitorID id;
+ jvmtiError result = jvmti_env->CreateRawMonitor("dummy", &id);
+ if (JvmtiErrorToException(env, result)) {
+ return 0;
+ }
+ return MonitorToLong(id);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_destroyRawMonitor(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
+ jvmtiError result = jvmti_env->DestroyRawMonitor(LongToMonitor(l));
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorEnter(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
+ jvmtiError result = jvmti_env->RawMonitorEnter(LongToMonitor(l));
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorExit(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
+ jvmtiError result = jvmti_env->RawMonitorExit(LongToMonitor(l));
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorWait(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l, jlong millis) {
+ jvmtiError result = jvmti_env->RawMonitorWait(LongToMonitor(l), millis);
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotify(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
+ jvmtiError result = jvmti_env->RawMonitorNotify(LongToMonitor(l));
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotifyAll(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
+ jvmtiError result = jvmti_env->RawMonitorNotifyAll(LongToMonitor(l));
+ JvmtiErrorToException(env, result);
+}
+
+} // namespace Test923Monitors
+} // namespace art
diff --git a/test/923-monitors/run b/test/923-monitors/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/923-monitors/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/923-monitors/src/Main.java b/test/923-monitors/src/Main.java
new file mode 100644
index 0000000..e35ce12
--- /dev/null
+++ b/test/923-monitors/src/Main.java
@@ -0,0 +1,298 @@
+/*
+ * 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.concurrent.CountDownLatch;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ private static void doTest() throws Exception {
+ // Start a watchdog, to make sure on deadlocks etc the test dies.
+ startWatchdog();
+
+ sharedId = createRawMonitor();
+
+ output = new ArrayList<String>(100);
+
+ simpleTests(sharedId);
+
+ for (String s : output) {
+ System.out.println(s);
+ }
+ output.clear();
+
+ threadTests(sharedId);
+
+ destroyRawMonitor(sharedId);
+ }
+
+ private static void simpleTests(long id) {
+ unlock(id); // Should fail.
+
+ lock(id);
+ unlock(id);
+ unlock(id); // Should fail.
+
+ lock(id);
+ lock(id);
+ unlock(id);
+ unlock(id);
+ unlock(id); // Should fail.
+
+ rawWait(id, 0); // Should fail.
+ rawWait(id, -1); // Should fail.
+ rawWait(id, 1); // Should fail.
+
+ lock(id);
+ rawWait(id, 50);
+ unlock(id);
+ unlock(id); // Should fail.
+
+ rawNotify(id); // Should fail.
+ lock(id);
+ rawNotify(id);
+ unlock(id);
+ unlock(id); // Should fail.
+
+ rawNotifyAll(id); // Should fail.
+ lock(id);
+ rawNotifyAll(id);
+ unlock(id);
+ unlock(id); // Should fail.
+ }
+
+ private static void threadTests(final long id) throws Exception {
+ final int N = 10;
+
+ final CountDownLatch waitLatch = new CountDownLatch(N);
+ final CountDownLatch wait2Latch = new CountDownLatch(1);
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ lock(id);
+ waitLatch.countDown();
+ rawWait(id, 0);
+ firstAwakened = Thread.currentThread();
+ appendToLog("Awakened");
+ unlock(id);
+ wait2Latch.countDown();
+ }
+ };
+
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = 0; i < N; i++) {
+ Thread t = new Thread(r);
+ threads.add(t);
+ t.start();
+ }
+
+ // Wait till all threads have been started.
+ waitLatch.await();
+
+ // Hopefully enough time for all the threads to progress into wait.
+ Thread.yield();
+ Thread.sleep(500);
+
+ // Wake up one.
+ lock(id);
+ rawNotify(id);
+ unlock(id);
+
+ wait2Latch.await();
+
+ // Wait a little bit more to see stragglers. This is flaky - spurious wakeups could
+ // make the test fail.
+ Thread.yield();
+ Thread.sleep(500);
+ if (firstAwakened != null) {
+ firstAwakened.join();
+ }
+
+ // Wake up everyone else.
+ lock(id);
+ rawNotifyAll(id);
+ unlock(id);
+
+ // Wait for everyone to die.
+ for (Thread t : threads) {
+ t.join();
+ }
+
+ // Check threaded output.
+ Iterator<String> it = output.iterator();
+ // 1) Start with N locks and Waits.
+ {
+ int locks = 0;
+ int waits = 0;
+ for (int i = 0; i < 2*N; i++) {
+ String s = it.next();
+ if (s.equals("Lock")) {
+ locks++;
+ } else if (s.equals("Wait")) {
+ if (locks <= waits) {
+ System.out.println(output);
+ throw new RuntimeException("Wait before Lock");
+ }
+ waits++;
+ } else {
+ System.out.println(output);
+ throw new RuntimeException("Unexpected operation: " + s);
+ }
+ }
+ }
+
+ // 2) Expect Lock + Notify + Unlock.
+ expect("Lock", it, output);
+ expect("Notify", it, output);
+ expect("Unlock", it, output);
+
+ // 3) A single thread wakes up, runs, and dies.
+ expect("Awakened", it, output);
+ expect("Unlock", it, output);
+
+ // 4) Expect Lock + NotifyAll + Unlock.
+ expect("Lock", it, output);
+ expect("NotifyAll", it, output);
+ expect("Unlock", it, output);
+
+ // 5) N-1 threads wake up, run, and die.
+ {
+ int expectedUnlocks = 0;
+ int ops = 2 * (N-1);
+ for (int i = 0; i < ops; i++) {
+ String s = it.next();
+ if (s.equals("Awakened")) {
+ expectedUnlocks++;
+ } else if (s.equals("Unlock")) {
+ expectedUnlocks--;
+ if (expectedUnlocks < 0) {
+ System.out.println(output);
+ throw new RuntimeException("Unexpected unlock");
+ }
+ }
+ }
+ }
+
+ // 6) That should be it.
+ if (it.hasNext()) {
+ System.out.println(output);
+ throw new RuntimeException("Unexpected trailing output, starting with " + it.next());
+ }
+
+ output.clear();
+ System.out.println("Done");
+ }
+
+ private static void expect(String s, Iterator<String> it, List<String> output) {
+ String t = it.next();
+ if (!s.equals(t)) {
+ System.out.println(output);
+ throw new RuntimeException("Expected " + s + " but got " + t);
+ }
+ }
+
+ private static void lock(long id) {
+ appendToLog("Lock");
+ rawMonitorEnter(id);
+ }
+
+ private static void unlock(long id) {
+ appendToLog("Unlock");
+ try {
+ rawMonitorExit(id);
+ } catch (RuntimeException e) {
+ appendToLog(e.getMessage());
+ }
+ }
+
+ private static void rawWait(long id, long millis) {
+ appendToLog("Wait");
+ try {
+ rawMonitorWait(id, millis);
+ } catch (RuntimeException e) {
+ appendToLog(e.getMessage());
+ }
+ }
+
+ private static void rawNotify(long id) {
+ appendToLog("Notify");
+ try {
+ rawMonitorNotify(id);
+ } catch (RuntimeException e) {
+ appendToLog(e.getMessage());
+ }
+ }
+
+ private static void rawNotifyAll(long id) {
+ appendToLog("NotifyAll");
+ try {
+ rawMonitorNotifyAll(id);
+ } catch (RuntimeException e) {
+ appendToLog(e.getMessage());
+ }
+ }
+
+ private static synchronized void appendToLog(String s) {
+ output.add(s);
+ }
+
+ private static void startWatchdog() {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ // Give it a minute.
+ long end = 60 * 1000 + start;
+ for (;;) {
+ long delta = end - System.currentTimeMillis();
+ if (delta <= 0) {
+ break;
+ }
+
+ try {
+ Thread.currentThread().sleep(delta);
+ } catch (Exception e) {
+ }
+ }
+ System.out.println("TIMEOUT!");
+ System.exit(1);
+ }
+ };
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ t.start();
+ }
+
+ static volatile long sharedId;
+ static List<String> output;
+ static Thread firstAwakened;
+
+ private static native long createRawMonitor();
+ private static native void destroyRawMonitor(long id);
+ private static native void rawMonitorEnter(long id);
+ private static native void rawMonitorExit(long id);
+ private static native void rawMonitorWait(long id, long millis);
+ private static native void rawMonitorNotify(long id);
+ private static native void rawMonitorNotifyAll(long id);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 5a2c902..a223c3a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -260,6 +260,10 @@
"911-get-stack-trace/stack_trace.cc",
"912-classes/classes.cc",
"913-heaps/heaps.cc",
+ "918-fields/fields.cc",
+ "920-objects/objects.cc",
+ "922-properties/properties.cc",
+ "923-monitors/monitors.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ec1f6ba..fd3a897 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -290,6 +290,12 @@
915-obsolete-2 \
916-obsolete-jit \
917-fields-transformation \
+ 918-fields \
+ 919-obsolete-fields \
+ 920-objects \
+ 921-hello-failure \
+ 922-properties \
+ 923-monitors \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -543,7 +549,7 @@
# between those runs to be able to have precise checks.
# Test 902 hits races with the JIT compiler. b/32821077
# Test 629 requires compilation.
-# Test 914, 915, 917, & 918 are very sensitive to the exact state of the stack,
+# Test 914, 915, 917, & 919 are very sensitive to the exact state of the stack,
# including the jit-inserted runtime frames. This causes them to be somewhat
# flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are
# resolved but until then just disable them. Test 916 already checks this
@@ -557,7 +563,7 @@
914-hello-obsolescence \
915-obsolete-2 \
917-fields-transformation \
- 918-obsolete-fields \
+ 919-obsolete-fields \
ifneq (,$(filter jit,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -1021,10 +1027,11 @@
test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES
run_test_options += --no-image
# Add the core dependency. This is required for pre-building.
+ # Use the PIC image, as it is the default in run-test, to match dependencies.
ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13))
else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13))
endif
else
ifeq ($(9),npicimage)
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index ebf1e46..6f98f10 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -17,6 +17,7 @@
#include "ti-agent/common_helper.h"
#include <stdio.h>
+#include <sstream>
#include "art_method.h"
#include "jni.h"
@@ -39,10 +40,46 @@
env->AddCapabilities(&caps);
}
+bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
+ if (error == JVMTI_ERROR_NONE) {
+ return false;
+ }
+
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ if (rt_exception.get() == nullptr) {
+ // CNFE should be pending.
+ return true;
+ }
+
+ char* err;
+ jvmti_env->GetErrorName(error, &err);
+
+ env->ThrowNew(rt_exception.get(), err);
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ return true;
+}
+
namespace common_redefine {
+static void throwRedefinitionError(jvmtiEnv* jvmti, JNIEnv* env, jclass target, jvmtiError res) {
+ std::stringstream err;
+ char* signature = nullptr;
+ char* generic = nullptr;
+ jvmti->GetClassSignature(target, &signature, &generic);
+ char* error = nullptr;
+ jvmti->GetErrorName(res, &error);
+ err << "Failed to redefine class <" << signature << "> due to " << error;
+ std::string message = err.str();
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
+ env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
+}
+
using RedefineDirectFunction = jvmtiError (*)(jvmtiEnv*, jclass, jint, const unsigned char*);
-static void DoClassTransformation(jvmtiEnv* jvmti_env, JNIEnv* env,
+static void DoClassTransformation(jvmtiEnv* jvmti_env,
+ JNIEnv* env,
jclass target,
jbyteArray class_file_bytes,
jbyteArray dex_file_bytes) {
@@ -63,7 +100,7 @@
res = f(jvmti_env, target, len, redef_bytes);
}
if (res != JVMTI_ERROR_NONE) {
- printf("Redefinition failed!");
+ throwRedefinitionError(jvmti_env, env, target, res);
}
}
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index 76543fe..642ca03 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -65,6 +65,8 @@
void SetAllCapabilities(jvmtiEnv* env);
+bool JvmtiErrorToException(JNIEnv* env, jvmtiError error);
+
} // namespace art
#endif // ART_TEST_TI_AGENT_COMMON_HELPER_H_
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 79c17d7..8abd063 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -26,17 +26,7 @@
#include "common_helper.h"
#include "901-hello-ti-agent/basics.h"
-#include "903-hello-tagging/tagging.h"
-#include "904-object-allocation/tracking.h"
-#include "905-object-free/tracking_free.h"
-#include "906-iterate-heap/iterate_heap.h"
-#include "907-get-loaded-classes/get_loaded_classes.h"
-#include "908-gc-start-finish/gc_callbacks.h"
#include "909-attach-agent/attach.h"
-#include "910-methods/methods.h"
-#include "911-get-stack-trace/stack_trace.h"
-#include "912-classes/classes.h"
-#include "913-heaps/heaps.h"
namespace art {
@@ -51,25 +41,30 @@
OnAttach attach;
};
-// A list of all the agents we have for testing.
+// A trivial OnLoad implementation that only initializes the global jvmti_env.
+static jint MinimalOnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ SetAllCapabilities(jvmti_env);
+ return 0;
+}
+
+// A list of all non-standard the agents we have for testing. All other agents will use
+// MinimalOnLoad.
AgentLib agents[] = {
{ "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
{ "902-hello-transformation", common_redefine::OnLoad, nullptr },
- { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr },
- { "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr },
- { "905-object-free", Test905ObjectFree::OnLoad, nullptr },
- { "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr },
- { "907-get-loaded-classes", Test907GetLoadedClasses::OnLoad, nullptr },
- { "908-gc-start-finish", Test908GcStartFinish::OnLoad, nullptr },
{ "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
- { "910-methods", Test910Methods::OnLoad, nullptr },
- { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
- { "912-classes", Test912Classes::OnLoad, nullptr },
- { "913-heaps", Test913Heaps::OnLoad, nullptr },
{ "914-hello-obsolescence", common_redefine::OnLoad, nullptr },
{ "915-obsolete-2", common_redefine::OnLoad, nullptr },
{ "916-obsolete-jit", common_redefine::OnLoad, nullptr },
{ "917-fields-transformation", common_redefine::OnLoad, nullptr },
+ { "919-obsolete-fields", common_redefine::OnLoad, nullptr },
+ { "921-hello-failure", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
@@ -110,18 +105,21 @@
printf("Unable to find agent name in options: %s\n", options);
return -1;
}
- AgentLib* lib = FindAgent(name_option);
- if (lib == nullptr) {
- printf("Unable to find agent named: %s, add it to the list in test/ti-agent/common_load.cc\n",
- name_option);
- return -2;
- }
- if (lib->load == nullptr) {
- printf("agent: %s does not include an OnLoad method.\n", name_option);
- return -3;
- }
+
SetIsJVM(remaining_options);
- return lib->load(vm, remaining_options, reserved);
+
+ AgentLib* lib = FindAgent(name_option);
+ OnLoad fn = nullptr;
+ if (lib == nullptr) {
+ fn = &MinimalOnLoad;
+ } else {
+ if (lib->load == nullptr) {
+ printf("agent: %s does not include an OnLoad method.\n", name_option);
+ return -3;
+ }
+ fn = lib->load;
+ }
+ return fn(vm, remaining_options, reserved);
}
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
index fbc99b1..452a174 100644
--- a/test/valgrind-target-suppressions.txt
+++ b/test/valgrind-target-suppressions.txt
@@ -36,8 +36,7 @@
MemCpySelfAssign
Memcheck:Overlap
fun:memcpy
- fun:je_tsd_set
- fun:je_tsd_fetch
+ ...
fun:je_malloc_tsd_boot0
}
@@ -59,3 +58,12 @@
...
fun:_ZN3art7Runtime17InitNativeMethodsEv
}
+
+# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings.
+{
+ art::MemMap::MapInternal()
+ Memcheck:Param
+ msync(start)
+ fun:msync
+ fun:_ZN3art6MemMap11MapInternalEPvmiiilb
+}