Merge "ART: Use _exit in dex2oat"
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 4297979..41df56b 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2940,6 +2940,199 @@
GenFPToFPCall(invoke, codegen_, kQuickTanh);
}
+// static void java.lang.System.arraycopy(Object src, int srcPos,
+// Object dest, int destPos,
+// int length)
+void IntrinsicLocationsBuilderMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
+ HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
+ HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
+ HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
+
+ // As long as we are checking, we might as well check to see if the src and dest
+ // positions are >= 0.
+ if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
+ (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
+ // We will have to fail anyways.
+ return;
+ }
+
+ // And since we are already checking, check the length too.
+ if (length != nullptr) {
+ int32_t len = length->GetValue();
+ if (len < 0) {
+ // Just call as normal.
+ return;
+ }
+ }
+
+ // Okay, it is safe to generate inline code.
+ LocationSummary* locations =
+ new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
+ // arraycopy(Object src, int srcPos, Object dest, int destPos, int length).
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
+ locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+// Utility routine to verify that "length(input) - pos >= length"
+static void EnoughItems(MipsAssembler* assembler,
+ Register length_input_minus_pos,
+ Location length,
+ SlowPathCodeMIPS* slow_path) {
+ if (length.IsConstant()) {
+ int32_t length_constant = length.GetConstant()->AsIntConstant()->GetValue();
+
+ if (IsInt<16>(length_constant)) {
+ __ Slti(TMP, length_input_minus_pos, length_constant);
+ __ Bnez(TMP, slow_path->GetEntryLabel());
+ } else {
+ __ LoadConst32(TMP, length_constant);
+ __ Blt(length_input_minus_pos, TMP, slow_path->GetEntryLabel());
+ }
+ } else {
+ __ Blt(length_input_minus_pos, length.AsRegister<Register>(), slow_path->GetEntryLabel());
+ }
+}
+
+static void CheckPosition(MipsAssembler* assembler,
+ Location pos,
+ Register input,
+ Location length,
+ SlowPathCodeMIPS* slow_path,
+ bool length_is_input_length = false) {
+ // Where is the length in the Array?
+ const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
+
+ // Calculate length(input) - pos.
+ if (pos.IsConstant()) {
+ int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
+ if (pos_const == 0) {
+ if (!length_is_input_length) {
+ // Check that length(input) >= length.
+ __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+ EnoughItems(assembler, AT, length, slow_path);
+ }
+ } else {
+ // Check that (length(input) - pos) >= zero.
+ __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+ DCHECK_GT(pos_const, 0);
+ __ Addiu32(AT, AT, -pos_const, TMP);
+ __ Bltz(AT, slow_path->GetEntryLabel());
+
+ // Verify that (length(input) - pos) >= length.
+ EnoughItems(assembler, AT, length, slow_path);
+ }
+ } else if (length_is_input_length) {
+ // The only way the copy can succeed is if pos is zero.
+ Register pos_reg = pos.AsRegister<Register>();
+ __ Bnez(pos_reg, slow_path->GetEntryLabel());
+ } else {
+ // Verify that pos >= 0.
+ Register pos_reg = pos.AsRegister<Register>();
+ __ Bltz(pos_reg, slow_path->GetEntryLabel());
+
+ // Check that (length(input) - pos) >= zero.
+ __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+ __ Subu(AT, AT, pos_reg);
+ __ Bltz(AT, slow_path->GetEntryLabel());
+
+ // Verify that (length(input) - pos) >= length.
+ EnoughItems(assembler, AT, length, slow_path);
+ }
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register src = locations->InAt(0).AsRegister<Register>();
+ Location src_pos = locations->InAt(1);
+ Register dest = locations->InAt(2).AsRegister<Register>();
+ Location dest_pos = locations->InAt(3);
+ Location length = locations->InAt(4);
+
+ MipsLabel loop;
+
+ Register dest_base = locations->GetTemp(0).AsRegister<Register>();
+ Register src_base = locations->GetTemp(1).AsRegister<Register>();
+ Register count = locations->GetTemp(2).AsRegister<Register>();
+
+ SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Bail out if the source and destination are the same (to handle overlap).
+ __ Beq(src, dest, slow_path->GetEntryLabel());
+
+ // Bail out if the source is null.
+ __ Beqz(src, slow_path->GetEntryLabel());
+
+ // Bail out if the destination is null.
+ __ Beqz(dest, slow_path->GetEntryLabel());
+
+ // Load length into register for count.
+ if (length.IsConstant()) {
+ __ LoadConst32(count, length.GetConstant()->AsIntConstant()->GetValue());
+ } else {
+ // If the length is negative, bail out.
+ // We have already checked in the LocationsBuilder for the constant case.
+ __ Bltz(length.AsRegister<Register>(), slow_path->GetEntryLabel());
+
+ __ Move(count, length.AsRegister<Register>());
+ }
+
+ // Validity checks: source.
+ CheckPosition(assembler, src_pos, src, Location::RegisterLocation(count), slow_path);
+
+ // Validity checks: dest.
+ CheckPosition(assembler, dest_pos, dest, Location::RegisterLocation(count), slow_path);
+
+ // If count is zero, we're done.
+ __ Beqz(count, slow_path->GetExitLabel());
+
+ // Okay, everything checks out. Finally time to do the copy.
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Calculate source and destination addresses.
+ if (src_pos.IsConstant()) {
+ int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue();
+
+ __ Addiu32(src_base, src, data_offset + char_size * src_pos_const, TMP);
+ } else {
+ __ Addiu32(src_base, src, data_offset, TMP);
+ __ ShiftAndAdd(src_base, src_pos.AsRegister<Register>(), src_base, char_shift);
+ }
+ if (dest_pos.IsConstant()) {
+ int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue();
+
+ __ Addiu32(dest_base, dest, data_offset + char_size * dest_pos_const, TMP);
+ } else {
+ __ Addiu32(dest_base, dest, data_offset, TMP);
+ __ ShiftAndAdd(dest_base, dest_pos.AsRegister<Register>(), dest_base, char_shift);
+ }
+
+ __ Bind(&loop);
+ __ Lh(TMP, src_base, 0);
+ __ Addiu(src_base, src_base, char_size);
+ __ Addiu(count, count, -1);
+ __ Sh(TMP, dest_base, 0);
+ __ Addiu(dest_base, dest_base, char_size);
+ __ Bnez(count, &loop);
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
// Unimplemented intrinsics.
UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
@@ -2951,7 +3144,6 @@
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 0536f322..0e0d66b 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1816,6 +1816,10 @@
output_location += "/" + dex_file_location + ".new";
}
new_file.reset(OS::CreateEmptyFile(output_location.c_str()));
+ if (new_file == nullptr) {
+ LOG(ERROR) << "Could not create dex writer output file: " << output_location;
+ return;
+ }
ftruncate(new_file->Fd(), header_->FileSize());
mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED,
new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg));
@@ -1825,7 +1829,7 @@
}
if (mem_map_ == nullptr) {
LOG(ERROR) << "Could not create mem map for dex writer output: " << error_msg;
- if (new_file.get() != nullptr) {
+ if (new_file != nullptr) {
new_file->Erase();
}
return;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 85100ae..688f95b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -590,6 +590,10 @@
void DexFile::InitializeSectionsFromMapList() {
const MapList* map_list = reinterpret_cast<const MapList*>(begin_ + header_->map_off_);
+ if (header_->map_off_ == 0 || header_->map_off_ > size_) {
+ // Bad offset. The dex file verifier runs after this method and will reject the file.
+ return;
+ }
const size_t count = map_list->size_;
size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9131715..3f7461c 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -161,6 +161,16 @@
"ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA"
"AFBLBQYAAAAAAwADAPUAAABkBAAAAAA=";
+static const char kRawDexBadMapOffset[] =
+ "ZGV4CjAzNQAZKGSz85r+tXJ1I24FYi+FpQtWbXtelAmoAQAAcAAAAHhWNBIAAAAAAAAAAEAwIBAF"
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA"
+ "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA"
+ "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A"
+ "Bjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABAAcOAAAAAQAAgYAE"
+ "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA"
+ "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA"
+ "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA==";
+
static void DecodeAndWriteDexFile(const char* base64, const char* location) {
// decode base64
CHECK(base64 != nullptr);
@@ -212,7 +222,8 @@
static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
const char* location,
- uint32_t location_checksum) {
+ uint32_t location_checksum,
+ bool expect_success) {
CHECK(base64 != nullptr);
std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
CHECK_NE(dex_bytes.size(), 0u);
@@ -232,7 +243,11 @@
/* verify */ true,
/* verify_checksum */ true,
&error_message));
- CHECK(dex_file != nullptr) << error_message;
+ if (expect_success) {
+ CHECK(dex_file != nullptr) << error_message;
+ } else {
+ CHECK(dex_file == nullptr) << "Expected dex file open to fail.";
+ }
return dex_file;
}
@@ -282,7 +297,7 @@
TEST_F(DexFileTest, HeaderInMemory) {
ScratchFile tmp;
std::unique_ptr<const DexFile> raw =
- OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U);
+ OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U, true);
ValidateDexFileHeader(std::move(raw));
}
@@ -569,4 +584,11 @@
EXPECT_EQ(dex_files.size(), 3u);
}
+TEST_F(DexFileTest, OpenDexBadMapOffset) {
+ ScratchFile tmp;
+ std::unique_ptr<const DexFile> raw =
+ OpenDexFileInMemoryBase64(kRawDexBadMapOffset, tmp.GetFilename().c_str(), 0xb3642819U, false);
+ EXPECT_EQ(raw, nullptr);
+}
+
} // namespace art
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 24ba52f..bcf5008 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1108,7 +1108,9 @@
space::RegionSpace* region_space = collector->RegionSpace();
CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
VerifyNoFromSpaceRefsFieldVisitor visitor(collector);
- obj->VisitReferences(visitor, visitor);
+ obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+ visitor,
+ visitor);
if (kUseBakerReadBarrier) {
CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
<< "obj=" << obj << " non-white rb_state " << obj->GetReadBarrierState();
@@ -1232,7 +1234,9 @@
CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
AssertToSpaceInvariantFieldVisitor visitor(collector);
- obj->VisitReferences(visitor, visitor);
+ obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+ visitor,
+ visitor);
}
private:
@@ -1471,8 +1475,7 @@
// Add to the live bytes per unevacuated from space. Note this code is always run by the
// GC-running thread (no synchronization required).
DCHECK(region_space_bitmap_->Test(to_ref));
- // Disable the read barrier in SizeOf for performance, which is safe.
- size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags>();
size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
region_space_->AddLiveBytes(to_ref, alloc_size);
}
@@ -1714,16 +1717,19 @@
}
// Assert the to-space invariant.
-void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj,
+ MemberOffset offset,
mirror::Object* ref) {
- CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
+ CHECK_EQ(heap_->collector_type_, kCollectorTypeCC);
if (is_asserting_to_space_invariant_) {
- if (region_space_->IsInToSpace(ref)) {
+ using RegionType = space::RegionSpace::RegionType;
+ space::RegionSpace::RegionType type = region_space_->GetRegionType(ref);
+ if (type == RegionType::kRegionTypeToSpace) {
// OK.
return;
- } else if (region_space_->IsInUnevacFromSpace(ref)) {
+ } else if (type == RegionType::kRegionTypeUnevacFromSpace) {
CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
- } else if (region_space_->IsInFromSpace(ref)) {
+ } else if (UNLIKELY(type == RegionType::kRegionTypeFromSpace)) {
// Not OK. Do extra logging.
if (obj != nullptr) {
LogFromSpaceRefHolder(obj, offset);
@@ -1888,10 +1894,10 @@
explicit RefFieldsVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
- void operator()(ObjPtr<mirror::Object> obj, MemberOffset offset, bool /* is_static */)
+ void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
- collector_->Process(obj.Ptr(), offset);
+ collector_->Process(obj, offset);
}
void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
@@ -2064,7 +2070,7 @@
AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object.Ptr());
CHECK_EQ(byte_size, (java_lang_Object->GetObjectSize<kVerifyNone, kWithoutReadBarrier>()));
dummy_obj->SetClass(java_lang_Object.Ptr());
- CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()));
+ CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>()));
} else {
// Use an int array.
dummy_obj->SetClass(int_array_class);
@@ -2075,7 +2081,7 @@
CHECK_EQ(dummy_arr->GetLength(), length)
<< "byte_size=" << byte_size << " length=" << length
<< " component_size=" << component_size << " data_offset=" << data_offset;
- CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()))
+ CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>()))
<< "byte_size=" << byte_size << " length=" << length
<< " component_size=" << component_size << " data_offset=" << data_offset;
}
@@ -2142,10 +2148,10 @@
mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) {
DCHECK(region_space_->IsInFromSpace(from_ref));
- // No read barrier to avoid nested RB that might violate the to-space
- // invariant. Note that from_ref is a from space ref so the SizeOf()
- // call will access the from-space meta objects, but it's ok and necessary.
- size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ // There must not be a read barrier to avoid nested RB that might violate the to-space invariant.
+ // Note that from_ref is a from space ref so the SizeOf() call will access the from-space meta
+ // objects, but it's ok and necessary.
+ size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags>();
size_t region_space_alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
size_t region_space_bytes_allocated = 0U;
size_t non_moving_space_bytes_allocated = 0U;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 357541f..a853b98 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2337,9 +2337,7 @@
size_t bin_size = object_addr - context->prev_;
// Add the bin consisting of the end of the previous object to the start of the current object.
collector->AddBin(bin_size, context->prev_);
- // Turn off read barrier. ZygoteCompactingCollector doesn't use it (even in the CC build.)
- context->prev_ = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>(),
- kObjectAlignment);
+ context->prev_ = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kObjectAlignment);
}
void AddBin(size_t size, uintptr_t position) {
@@ -2359,8 +2357,7 @@
virtual mirror::Object* MarkNonForwardedObject(mirror::Object* obj)
REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
- // Turn off read barrier. ZygoteCompactingCollector doesn't use it (even in the CC build.)
- size_t obj_size = obj->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ size_t obj_size = obj->SizeOf<kDefaultVerifyFlags>();
size_t alloc_size = RoundUp(obj_size, kObjectAlignment);
mirror::Object* forward_address;
// Find the smallest bin which we can move obj in.
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 811f1ea..f83645e 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -513,8 +513,11 @@
return GetClass<kVerifyFlags>()->IsPhantomReferenceClass();
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline size_t Object::SizeOf() {
+ // Read barrier is never required for SizeOf since objects sizes are constant. Reading from-space
+ // values is OK because of that.
+ static constexpr ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier;
size_t result;
constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
if (IsArrayInstance<kVerifyFlags, kReadBarrierOption>()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index f7ab26d..417a22d 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -128,8 +128,7 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ALWAYS_INLINE bool InstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_);
Object* Clone(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index bc73029..01bf21d 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -61,8 +61,13 @@
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;
+ // Use the shorty.
+ art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+ size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
+ if (!base_method->IsStatic()) {
+ arg_count++;
+ }
+ *size_ptr = static_cast<jint>(arg_count);
return ERR(NONE);
}
@@ -203,9 +208,9 @@
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;
+ // This isn't specified as an error case, so return -1/-1 as the RI does.
+ *start_location_ptr = -1;
+ *end_location_ptr = -1;
return ERR(NONE);
}
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 2b38b2e..c102fb0 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -28,12 +28,15 @@
namespace art {
+// Disabled for performance reasons.
+static constexpr bool kCheckDebugDisallowReadBarrierCount = false;
+
template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kAlwaysUpdateField>
inline MirrorType* ReadBarrier::Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
if (kUseReadBarrier && with_read_barrier) {
- if (kIsDebugBuild) {
+ if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
if (self != nullptr) {
CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index 8e6b6e7..c14c6c4 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -32,19 +32,19 @@
interface java.util.List
1025
Max locals: 0
-Argument size: 0
-Location start: 0
-Location end: 0
+Argument size: 2
+Location start: -1
+Location end: -1
Is native: false
Is obsolete: false
Is synthetic: false
[run, ()V, null]
-class $Proxy0
+class $Proxy20
17
Max locals: 0
-Argument size: 0
-Location start: 0
-Location end: 0
+Argument size: 1
+Location start: -1
+Location end: -1
Is native: false
Is obsolete: false
Is synthetic: false
diff --git a/test/910-methods/src/art/Test910.java b/test/910-methods/src/art/Test910.java
index b3490e9..aa6d13a 100644
--- a/test/910-methods/src/art/Test910.java
+++ b/test/910-methods/src/art/Test910.java
@@ -39,17 +39,6 @@
testMethod(findSyntheticMethod(), NestedSynthetic.class, false);
}
- private static Class<?> proxyClass = null;
-
- private static Class<?> getProxyClass() throws Exception {
- if (proxyClass != null) {
- return proxyClass;
- }
-
- proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
- return proxyClass;
- }
-
private static void testMethod(String className, String methodName, Class<?>... types)
throws Exception {
Class<?> base = Class.forName(className);
@@ -145,4 +134,62 @@
private static native boolean isMethodNative(Method m);
private static native boolean isMethodObsolete(Method m);
private static native boolean isMethodSynthetic(Method m);
+
+ // We need this machinery for a consistent proxy name. Names of proxy classes include a
+ // unique number (derived by counting). This means that a simple call to getProxyClass
+ // depends on the test environment.
+ //
+ // To work around this, we assume that at most twenty proxies have been created before
+ // the test is run, and canonicalize on "$Proxy20". We add infrastructure to create
+ // as many proxy classes but cycling through subsets of the test-provided interfaces
+ // I0...I4.
+ //
+ //
+ // (This is made under the judgment that we do not want to have proxy-specific behavior
+ // for testMethod.)
+
+ private static Class<?> proxyClass = null;
+
+ private static Class<?> getProxyClass() throws Exception {
+ if (proxyClass != null) {
+ return proxyClass;
+ }
+
+ for (int i = 1; i <= 21; i++) {
+ proxyClass = createProxyClass(i);
+ String name = proxyClass.getName();
+ if (name.equals("$Proxy20")) {
+ return proxyClass;
+ }
+ }
+ return proxyClass;
+ }
+
+ private static Class<?> createProxyClass(int i) throws Exception {
+ int count = Integer.bitCount(i);
+ Class<?>[] input = new Class<?>[count + 1];
+ input[0] = Runnable.class;
+ int inputIndex = 1;
+ int bitIndex = 0;
+ while (i != 0) {
+ if ((i & 1) != 0) {
+ input[inputIndex++] = Class.forName("art.Test910$I" + bitIndex);
+ }
+ i >>>= 1;
+ bitIndex++;
+ }
+ return Proxy.getProxyClass(Test910.class.getClassLoader(), input);
+ }
+
+ // Need this for the proxy naming.
+ public static interface I0 {
+ }
+ public static interface I1 {
+ }
+ public static interface I2 {
+ }
+ public static interface I3 {
+ }
+ public static interface I4 {
+ }
}
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
index 987066f..07b21b3 100755
--- a/test/980-redefine-object/check
+++ b/test/980-redefine-object/check
@@ -17,4 +17,4 @@
# The number of paused background threads (and therefore InterruptedExceptions)
# can change so we will just delete their lines from the log.
-sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
+sed "/Object allocated of type 'java\.lang\.InterruptedException'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt
index 6e9bce0..4c294bc 100644
--- a/test/980-redefine-object/expected.txt
+++ b/test/980-redefine-object/expected.txt
@@ -2,51 +2,31 @@
Allocating an j.l.Object before redefining Object class
Allocating a Transform before redefining Object class
Redefining the Object class to add a hook into the <init> method
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Allocating an j.l.Object after redefining Object class
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
Allocating a Transform after redefining Object class
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'Transform'
Allocating an int[] after redefining Object class
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Allocating an array list
-Object allocated of type 'Ljava/util/ArrayList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.ArrayList'
Adding a bunch of stuff to the array list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'Transform'
Allocating a linked list
-Object allocated of type 'Ljava/util/LinkedList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.LinkedList'
Adding a bunch of stuff to the linked list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'Transform'
+Object allocated of type 'java.util.LinkedList$Node'
Throwing from down 4 stack frames
-Object allocated of type 'Ljava/lang/Exception;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Exception'
Exception caught.
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
Finishing test!
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
deleted file mode 100644
index 1faf1a1..0000000
--- a/test/980-redefine-object/redefine_object.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <iostream>
-
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "jni.h"
-#include "jvmti.h"
-#include "scoped_utf_chars.h"
-
-// Test infrastructure
-#include "jni_binder.h"
-#include "jvmti_helper.h"
-#include "test_env.h"
-
-namespace art {
-namespace Test980RedefineObjects {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) {
- BindFunctionsOnClass(jvmti_env, env, target);
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed(
- JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
- char* sig = nullptr;
- char* generic_sig = nullptr;
- if (JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
- &sig,
- &generic_sig))) {
- // Exception.
- return;
- }
- std::cout << "Object allocated of type '" << sig << "'" << std::endl;
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig));
-}
-
-} // namespace Test980RedefineObjects
-} // namespace art
diff --git a/test/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java
index d15e688..c38e07b 100644
--- a/test/980-redefine-object/src-ex/TestWatcher.java
+++ b/test/980-redefine-object/src-ex/TestWatcher.java
@@ -16,10 +16,60 @@
package art.test;
+import java.util.concurrent.locks.ReentrantLock;
+
public class TestWatcher {
- // NB This function is native since it is called in the Object.<init> method and so cannot cause
- // any java allocations at all. The normal System.out.print* functions will cause allocations to
- // occur so we cannot use them. This means the easiest way to report the object as being created
- // is to go into native code and do it there.
- public static native void NotifyConstructed(Object o);
+ // Lock to synchronize access to the static state of this class.
+ private static final ReentrantLock lock = new ReentrantLock();
+ private static volatile boolean criticalFailure = false;
+ private static boolean reportingEnabled = true;
+ private static boolean doingReport = false;
+
+ private static void MonitorEnter() {
+ lock.lock();
+ }
+
+ private static void MonitorExit() {
+ // Need to do this manually since we need to notify critical failure but would deadlock if
+ // waited for the unlock.
+ if (!lock.isHeldByCurrentThread()) {
+ NotifyCriticalFailure();
+ throw new IllegalMonitorStateException("Locking error!");
+ } else {
+ lock.unlock();
+ }
+ }
+
+ // Stops reporting. Must be paired with an EnableReporting call.
+ public static void DisableReporting() {
+ MonitorEnter();
+ reportingEnabled = false;
+ }
+
+ // Stops reporting. Must be paired with a DisableReporting call.
+ public static void EnableReporting() {
+ reportingEnabled = true;
+ MonitorExit();
+ }
+
+ public static void NotifyCriticalFailure() {
+ criticalFailure = true;
+ }
+
+ public static void NotifyConstructed(Object o) {
+ if (criticalFailure) {
+ // Something went very wrong. We are probably trying to report it so don't get in the way.
+ return;
+ }
+ MonitorEnter();
+ // We could enter an infinite loop if println allocates (which it does) so we disable
+ // reporting while we are doing a report. Since we are synchronized we won't miss any
+ // allocations.
+ if (reportingEnabled && !doingReport) {
+ doingReport = true;
+ System.out.println("Object allocated of type '" + o.getClass().getName() + "'");
+ doingReport = false;
+ }
+ MonitorExit();
+ }
}
diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java
index a50215e..7a82fde 100644
--- a/test/980-redefine-object/src/Main.java
+++ b/test/980-redefine-object/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedList;
@@ -287,6 +288,31 @@
private static final String LISTENER_LOCATION =
System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
+ private static Method doEnableReporting;
+ private static Method doDisableReporting;
+
+ private static void DisableReporting() {
+ if (doDisableReporting == null) {
+ return;
+ }
+ try {
+ doDisableReporting.invoke(null);
+ } catch (Exception e) {
+ throw new Error("Unable to disable reporting!");
+ }
+ }
+
+ private static void EnableReporting() {
+ if (doEnableReporting == null) {
+ return;
+ }
+ try {
+ doEnableReporting.invoke(null);
+ } catch (Exception e) {
+ throw new Error("Unable to enable reporting!");
+ }
+ }
+
public static void main(String[] args) {
art.Main.bindAgentJNIForClass(Main.class);
doTest();
@@ -298,8 +324,8 @@
addToBootClassLoader(LISTENER_LOCATION);
// Load TestWatcher from the bootclassloader and make sure it is initialized.
Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
- // Bind the native functions of testwatcher_class.
- bindFunctionsForClass(testwatcher_class);
+ doEnableReporting = testwatcher_class.getDeclaredMethod("EnableReporting");
+ doDisableReporting = testwatcher_class.getDeclaredMethod("DisableReporting");
} catch (Exception e) {
throw new Error("Exception while making testwatcher", e);
}
@@ -308,9 +334,9 @@
// NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
// "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
private static void safePrintln(Object o) {
- System.out.flush();
- System.out.print("\t" + o + "\n");
- System.out.flush();
+ DisableReporting();
+ System.out.println("\t" + o);
+ EnableReporting();
}
private static void throwFrom(int depth) throws Exception {
@@ -382,8 +408,6 @@
private static native void addToBootClassLoader(String s);
- private static native void bindFunctionsForClass(Class<?> target);
-
// Transforms the class
private static native void doCommonClassRedefinition(Class<?> target,
byte[] class_file,
diff --git a/test/Android.bp b/test/Android.bp
index 8059a2f..538fac4 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -297,7 +297,6 @@
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
"945-obsolete-native/obsolete_native.cc",
- "980-redefine-object/redefine_object.cc",
"983-source-transform-verify/source_transform.cc",
"984-obsolete-invoke/obsolete_invoke.cc",
],