Merge "ART: Add array-alloc-inl.h"
diff --git a/build/art.go b/build/art.go
index f3cd3ca..0b5ee44 100644
--- a/build/art.go
+++ b/build/art.go
@@ -355,7 +355,8 @@
// TODO: express this in .bp instead b/79671158
if !envTrue(ctx, "ART_TARGET_LINUX") {
p.Target.Android.Static_libs = []string{
- "libmetricslogger_static",
+ "libmetricslogger",
+ "libstatssocket",
}
}
ctx.AppendProperties(p)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index fbad1af..898940a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2074,9 +2074,6 @@
}
TEST_F(Dex2oatTest, AppImageResolveStrings) {
- if (!ClassLinker::kAppImageMayContainStrings) {
- TEST_DISABLED();
- }
using Hotness = ProfileCompilationInfo::MethodHotness;
// Create a profile with the startup method marked.
ScratchFile profile_file;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index ddc9f43..60a4a32 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -224,7 +224,7 @@
// Used to store information that will later be used to calculate image
// offsets to string references in the AppImage.
- std::vector<RefInfoPair> string_ref_info;
+ std::vector<HeapReferencePointerInfo> string_ref_info;
if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
// Count the number of string fields so we can allocate the appropriate
// amount of space in the image section.
@@ -273,36 +273,31 @@
TimingLogger::ScopedTiming t("AppImage:CalculateImageOffsets", timings);
ScopedObjectAccess soa(self);
- size_t managed_string_refs = 0,
- native_string_refs = 0;
+ size_t managed_string_refs = 0;
+ size_t native_string_refs = 0;
/*
* Iterate over the string reference info and calculate image offsets.
- * The first element of the pair is the object the reference belongs to
- * and the second element is the offset to the field. If the offset has
- * a native ref tag 1) the object is a DexCache and 2) the offset needs
- * to be calculated using the relocation information for the DexCache's
- * strings array.
+ * The first element of the pair is either the object the reference belongs
+ * to or the beginning of the native reference array it is located in. In
+ * the first case the second element is the offset of the field relative to
+ * the object's base address. In the second case, it is the index of the
+ * StringDexCacheType object in the array.
*/
- for (const RefInfoPair& ref_info : string_ref_info) {
- uint32_t image_offset;
+ for (const HeapReferencePointerInfo& ref_info : string_ref_info) {
+ uint32_t base_offset;
- if (HasNativeRefTag(ref_info.second)) {
+ if (HasDexCacheNativeRefTag(ref_info.first)) {
++native_string_refs;
-
- // Only DexCaches can contain native references to Java strings.
- ObjPtr<mirror::DexCache> dex_cache(ref_info.first->AsDexCache());
-
- // No need to set or clear native ref tags. The existing tag will be
- // carried forward.
- image_offset = native_object_relocations_[dex_cache->GetStrings()].offset +
- ref_info.second;
+ auto* obj_ptr = reinterpret_cast<mirror::Object*>(ClearDexCacheNativeRefTag(
+ ref_info.first));
+ base_offset = SetDexCacheNativeRefTag(GetImageOffset(obj_ptr));
} else {
++managed_string_refs;
- image_offset = GetImageOffset(ref_info.first) + ref_info.second;
+ base_offset = GetImageOffset(reinterpret_cast<mirror::Object*>(ref_info.first));
}
- string_reference_offsets_.push_back(image_offset);
+ string_reference_offsets_.emplace_back(base_offset, ref_info.second);
}
CHECK_EQ(image_infos_.back().num_string_references_,
@@ -316,18 +311,16 @@
// This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and
// bin size sums being calculated.
TimingLogger::ScopedTiming t("AllocMemory", timings);
- if (!AllocMemory()) {
- return false;
- }
-
- return true;
+ return AllocMemory();
}
class ImageWriter::CollectStringReferenceVisitor {
public:
explicit CollectStringReferenceVisitor(const ImageWriter& image_writer)
- : dex_cache_string_ref_counter_(0),
- image_writer_(image_writer) {}
+ : image_writer_(image_writer),
+ curr_obj_(nullptr),
+ string_ref_info_(0),
+ dex_cache_string_ref_counter_(0) {}
// Used to prevent repeated null checks in the code that calls the visitor.
ALWAYS_INLINE
@@ -353,7 +346,7 @@
}
}
- // Collects info for Java fields that reference Java Strings.
+ // Collects info for managed fields that reference managed Strings.
ALWAYS_INLINE
void operator() (ObjPtr<mirror::Object> obj,
MemberOffset member_offset,
@@ -364,7 +357,8 @@
member_offset);
if (image_writer_.IsValidAppImageStringReference(referred_obj)) {
- string_ref_info_.emplace_back(obj.Ptr(), member_offset.Uint32Value());
+ string_ref_info_.emplace_back(reinterpret_cast<uintptr_t>(obj.Ptr()),
+ member_offset.Uint32Value());
}
}
@@ -375,84 +369,104 @@
operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
}
+ void AddStringRefInfo(uint32_t first, uint32_t second) {
+ string_ref_info_.emplace_back(first, second);
+ }
+
+ std::vector<HeapReferencePointerInfo>&& MoveRefInfo() {
+ return std::move(string_ref_info_);
+ }
+
// Used by the wrapper function to obtain a native reference count.
- size_t GetDexCacheStringRefCounter() const {
+ size_t GetDexCacheStringRefCount() const {
return dex_cache_string_ref_counter_;
}
- // Resets the native reference count.
- void ResetDexCacheStringRefCounter() {
+ void SetObject(ObjPtr<mirror::Object> obj) {
+ curr_obj_ = obj;
dex_cache_string_ref_counter_ = 0;
}
- ObjPtr<mirror::Object> curr_obj_;
- mutable std::vector<RefInfoPair> string_ref_info_;
-
private:
- mutable size_t dex_cache_string_ref_counter_;
const ImageWriter& image_writer_;
+ ObjPtr<mirror::Object> curr_obj_;
+ mutable std::vector<HeapReferencePointerInfo> string_ref_info_;
+ mutable size_t dex_cache_string_ref_counter_;
};
-std::vector<ImageWriter::RefInfoPair> ImageWriter::CollectStringReferenceInfo() const
+std::vector<ImageWriter::HeapReferencePointerInfo> ImageWriter::CollectStringReferenceInfo() const
REQUIRES_SHARED(Locks::mutator_lock_) {
gc::Heap* const heap = Runtime::Current()->GetHeap();
CollectStringReferenceVisitor visitor(*this);
+ /*
+ * References to managed strings can occur either in the managed heap or in
+ * native memory regions. Information about managed references is collected
+ * by the CollectStringReferenceVisitor and directly added to the internal
+ * info vector.
+ *
+ * Native references to managed strings can only occur through DexCache
+ * objects. This is verified by VerifyNativeGCRootInvariants(). Due to the
+ * fact that these native references are encapsulated in std::atomic objects
+ * the VisitReferences() function can't pass the visiting object the address
+ * of the reference. Instead, the VisitReferences() function loads the
+ * reference into a temporary variable and passes that address to the
+ * visitor. As a consequence of this we can't uniquely identify the location
+ * of the string reference in the visitor.
+ *
+ * Due to these limitations, the visitor will only count the number of
+ * managed strings reachable through the native references of a DexCache
+ * object. If there are any such strings, this function will then iterate
+ * over the native references, test the string for membership in the
+ * AppImage, and add the tagged DexCache pointer and string array offset to
+ * the info vector if necessary.
+ */
heap->VisitObjects([this, &visitor](ObjPtr<mirror::Object> object)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (!IsInBootImage(object.Ptr())) {
- // Many native GC roots are wrapped in std::atomics. Due to the
- // semantics of atomic objects we can't actually visit the addresses of
- // the native GC roots. Instead the visiting functions will pass the
- // visitor the address of a temporary copy of the native GC root and, if
- // it is changed, copy it back into the original location.
- //
- // This analysis requires the actual address of the native GC root so
- // we will only count them in the visitor and then collect them manually
- // afterwards. This count will then be used to verify that we collected
- // all native GC roots.
- visitor.curr_obj_ = object;
+ visitor.SetObject(object);
+
if (object->IsDexCache()) {
- object->VisitReferences</* kVisitNativeRoots */ true,
- kVerifyNone,
- kWithoutReadBarrier>(visitor, visitor);
+ object->VisitReferences</* kVisitNativeRoots= */ true,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
- ObjPtr<mirror::DexCache> dex_cache = object->AsDexCache();
- size_t new_native_ref_counter = 0;
+ if (visitor.GetDexCacheStringRefCount() > 0) {
+ size_t string_info_collected = 0;
- for (size_t string_index = 0; string_index < dex_cache->NumStrings(); ++string_index) {
- mirror::StringDexCachePair dc_pair = dex_cache->GetStrings()[string_index].load();
- mirror::Object* referred_obj = dc_pair.object.AddressWithoutBarrier()->AsMirrorPtr();
+ ObjPtr<mirror::DexCache> dex_cache = object->AsDexCache();
+ DCHECK_LE(visitor.GetDexCacheStringRefCount(), dex_cache->NumStrings());
- if (IsValidAppImageStringReference(referred_obj)) {
- ++new_native_ref_counter;
+ for (uint32_t index = 0; index < dex_cache->NumStrings(); ++index) {
+ // GetResolvedString() can't be used here due to the circular
+ // nature of the cache and the collision detection this requires.
+ ObjPtr<mirror::String> referred_string =
+ dex_cache->GetStrings()[index].load().object.Read();
- uint32_t string_vector_offset =
- (string_index * sizeof(mirror::StringDexCachePair)) +
- offsetof(mirror::StringDexCachePair, object);
-
- visitor.string_ref_info_.emplace_back(object.Ptr(),
- SetNativeRefTag(string_vector_offset));
+ if (IsValidAppImageStringReference(referred_string)) {
+ ++string_info_collected;
+ visitor.AddStringRefInfo(
+ SetDexCacheNativeRefTag(reinterpret_cast<uintptr_t>(object.Ptr())), index);
+ }
}
+
+ DCHECK_EQ(string_info_collected, visitor.GetDexCacheStringRefCount());
}
-
- CHECK_EQ(visitor.GetDexCacheStringRefCounter(), new_native_ref_counter);
} else {
- object->VisitReferences</* kVisitNativeRoots */ false,
- kVerifyNone,
- kWithoutReadBarrier>(visitor, visitor);
+ object->VisitReferences</* kVisitNativeRoots= */ false,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
}
-
- visitor.ResetDexCacheStringRefCounter();
}
});
- return std::move(visitor.string_ref_info_);
+ return visitor.MoveRefInfo();
}
class ImageWriter::NativeGCRootInvariantVisitor {
public:
explicit NativeGCRootInvariantVisitor(const ImageWriter& image_writer) :
+ curr_obj_(nullptr), class_violation_(false), class_loader_violation_(false),
image_writer_(image_writer) {}
ALWAYS_INLINE
@@ -469,12 +483,12 @@
ObjPtr<mirror::Object> referred_obj = root->AsMirrorPtr();
if (curr_obj_->IsClass()) {
- class_violation = class_violation ||
- image_writer_.IsValidAppImageStringReference(referred_obj);
+ class_violation_ = class_violation_ ||
+ image_writer_.IsValidAppImageStringReference(referred_obj);
} else if (curr_obj_->IsClassLoader()) {
- class_loader_violation = class_loader_violation ||
- image_writer_.IsValidAppImageStringReference(referred_obj);
+ class_loader_violation_ = class_loader_violation_ ||
+ image_writer_.IsValidAppImageStringReference(referred_obj);
} else if (!curr_obj_->IsDexCache()) {
LOG(FATAL) << "Dex2Oat:AppImage | " <<
@@ -495,12 +509,12 @@
// Returns true iff the only reachable native string references are through DexCache objects.
bool InvariantsHold() const {
- return !(class_violation || class_loader_violation);
+ return !(class_violation_ || class_loader_violation_);
}
ObjPtr<mirror::Object> curr_obj_;
- mutable bool class_violation = false,
- class_loader_violation = false;
+ mutable bool class_violation_;
+ mutable bool class_loader_violation_;
private:
const ImageWriter& image_writer_;
@@ -516,9 +530,9 @@
visitor.curr_obj_ = object;
if (!IsInBootImage(object.Ptr())) {
- object->VisitReferences</* kVisitNativeRefernces */ true,
- kVerifyNone,
- kWithoutReadBarrier>(visitor, visitor);
+ object->VisitReferences</* kVisitNativeReferences= */ true,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
}
});
@@ -529,12 +543,12 @@
* Build the error string
*/
- if (UNLIKELY(visitor.class_violation)) {
+ if (UNLIKELY(visitor.class_violation_)) {
error_str << "Class";
error = true;
}
- if (UNLIKELY(visitor.class_loader_violation)) {
+ if (UNLIKELY(visitor.class_loader_violation_)) {
if (error) {
error_str << ", ";
}
@@ -543,8 +557,8 @@
}
CHECK(visitor.InvariantsHold()) <<
- "Native GC root invariant failure. String refs reachable through the following objects: " <<
- error_str.str();
+ "Native GC root invariant failure. String ref invariants don't hold for the following " <<
+ "object types: " << error_str.str();
}
void ImageWriter::CopyMetadata() {
@@ -554,7 +568,7 @@
const ImageInfo& image_info = image_infos_.back();
std::vector<ImageSection> image_sections = image_info.CreateImageSections().second;
- uint32_t* sfo_section_base = reinterpret_cast<uint32_t*>(
+ auto* sfo_section_base = reinterpret_cast<AppImageReferenceOffsetInfo*>(
image_info.image_.Begin() +
image_sections[ImageHeader::kSectionStringReferenceOffsets].Offset());
@@ -2315,9 +2329,15 @@
// Round up to the alignment of the offsets we are going to store.
cur_pos = RoundUp(class_table_section.End(), sizeof(uint32_t));
+ // The size of string_reference_offsets_ can't be used here because it hasn't
+ // been filled with AppImageReferenceOffsetInfo objects yet. The
+ // num_string_references_ value is calculated separately, before we can
+ // compute the actual offsets.
const ImageSection& string_reference_offsets =
sections[ImageHeader::kSectionStringReferenceOffsets] =
- ImageSection(cur_pos, sizeof(uint32_t) * num_string_references_);
+ ImageSection(cur_pos,
+ sizeof(typename decltype(string_reference_offsets_)::value_type) *
+ num_string_references_);
// Return the number of bytes described by these sections, and the sections
// themselves.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index c35dbaa..e019a50 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -582,19 +582,27 @@
REQUIRES_SHARED(Locks::mutator_lock_);
/*
- * A pair containing the information necessary to calculate the position of a
- * managed object's field or native reference inside an AppImage.
+ * This type holds the information necessary for calculating
+ * AppImageReferenceOffsetInfo values after the object relocations have been
+ * computed.
*
- * The first element of this pair is a raw mirror::Object pointer because its
- * usage will cross a suspend point and ObjPtr would produce a false positive.
+ * The first element will always be a pointer to a managed object. If the
+ * pointer has been tagged (testable with HasDexCacheNativeRefTag) it
+ * indicates that the referenced object is a DexCache object that requires
+ * special handling during loading and the second element has no meaningful
+ * value. If the pointer isn't tagged then the second element is an
+ * object-relative offset to a field containing a string reference.
*
- * The second element is an offset either into the object or into the string
- * array of a DexCache object.
+ * Note that it is possible for an untagged DexCache pointer to occur in the
+ * first position if it has a managed reference that needs to be updated.
*
* TODO (chriswailes): Add a note indicating the source line where we ensure
* that no moving garbage collection will occur.
+ *
+ * TODO (chriswailes): Replace with std::variant once ART is building with
+ * C++17
*/
- typedef std::pair<mirror::Object*, uint32_t> RefInfoPair;
+ typedef std::pair<uintptr_t, uint32_t> HeapReferencePointerInfo;
/*
* Collects the info necessary for calculating image offsets to string field
@@ -614,23 +622,18 @@
* references that will be included in the AppImage. This allows use to both
* allocate enough memory for soring the offsets and correctly calculate the
* offsets of various objects into the image. Once the image offset
- * calculations are done for Java objects the reference object/offset pairs
+ * calculations are done for managed objects the reference object/offset pairs
* are translated to image offsets. The CopyMetadata function then copies
* these offsets into the image.
- *
- * A vector containing pairs of object pointers and offsets. The offsets are
- * tagged to indicate if the offset is for a field of a mirror object or a
- * native reference. If the offset is tagged as a native reference it must
- * have come from a DexCache's string array.
*/
- std::vector<RefInfoPair> CollectStringReferenceInfo() const
+ std::vector<HeapReferencePointerInfo> CollectStringReferenceInfo() const
REQUIRES_SHARED(Locks::mutator_lock_);
/*
* Ensures that assumptions about native GC roots and AppImages hold.
*
* This function verifies the following condition(s):
- * - Native references to Java strings are only reachable through DexCache
+ * - Native references to managed strings are only reachable through DexCache
* objects
*/
void VerifyNativeGCRootInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
@@ -773,7 +776,7 @@
mirror::ObjectArray<mirror::Object>* boot_image_live_objects_;
// Offsets into the image that indicate where string references are recorded.
- std::vector<uint32_t> string_reference_offsets_;
+ std::vector<AppImageReferenceOffsetInfo> string_reference_offsets_;
// Which mode the image is stored as, see image.h
const ImageHeader::StorageMode image_storage_mode_;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index afd2b6c..28a1924 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1180,28 +1180,33 @@
/*
* A class used to ensure that all strings in an AppImage have been properly
- * interned.
+ * interned, and is only ever run in debug mode.
*/
class VerifyStringInterningVisitor {
public:
explicit VerifyStringInterningVisitor(const gc::space::ImageSpace& space) :
- uninterned_string_found_(false),
space_(space),
intern_table_(*Runtime::Current()->GetInternTable()) {}
- ALWAYS_INLINE
void TestObject(ObjPtr<mirror::Object> referred_obj) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (referred_obj != nullptr &&
space_.HasAddress(referred_obj.Ptr()) &&
referred_obj->IsString()) {
ObjPtr<mirror::String> referred_str = referred_obj->AsString();
- uninterned_string_found_ = uninterned_string_found_ ||
- (intern_table_.LookupStrong(Thread::Current(), referred_str) != referred_str);
+
+ if (kIsDebugBuild) {
+ // Saved to temporary variables to aid in debugging.
+ ObjPtr<mirror::String> strong_lookup_result =
+ intern_table_.LookupStrong(Thread::Current(), referred_str);
+ ObjPtr<mirror::String> weak_lookup_result =
+ intern_table_.LookupWeak(Thread::Current(), referred_str);
+
+ DCHECK((strong_lookup_result == referred_str) || (weak_lookup_result == referred_str));
+ }
}
}
- ALWAYS_INLINE
void VisitRootIfNonNull(
mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1210,14 +1215,12 @@
}
}
- ALWAYS_INLINE
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
TestObject(root->AsMirrorPtr());
}
// Visit Class Fields
- ALWAYS_INLINE
void operator()(ObjPtr<mirror::Object> obj,
MemberOffset offset,
bool is_static ATTRIBUTE_UNUSED) const
@@ -1239,7 +1242,6 @@
operator()(ref, mirror::Reference::ReferentOffset(), false);
}
- mutable bool uninterned_string_found_;
const gc::space::ImageSpace& space_;
InternTable& intern_table_;
};
@@ -1249,13 +1251,14 @@
* properly interned. To be considered properly interned a reference must
* point to the same version of the string that the intern table does.
*/
-bool VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks::mutator_lock_) {
+void VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks::mutator_lock_) {
const gc::accounting::ContinuousSpaceBitmap* bitmap = space.GetMarkBitmap();
const ImageHeader& image_header = space.GetImageHeader();
const uint8_t* target_base = space.GetMemMap()->Begin();
const ImageSection& objects_section = image_header.GetObjectsSection();
- uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
- uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+ auto objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ auto objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
VerifyStringInterningVisitor visitor(space);
bitmap->VisitMarkedRange(objects_begin,
@@ -1264,21 +1267,19 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
if (space.HasAddress(obj)) {
if (obj->IsDexCache()) {
- obj->VisitReferences</*kVisitNativeRoots=*/ true,
- kVerifyNone,
- kWithoutReadBarrier>(visitor, visitor);
+ obj->VisitReferences</* kVisitNativeRoots= */ true,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
} else {
// Don't visit native roots for non-dex-cache as they can't contain
// native references to strings. This is verified during compilation
// by ImageWriter::VerifyNativeGCRootInvariants.
- obj->VisitReferences</*kVisitNativeRoots=*/ false,
- kVerifyNone,
- kWithoutReadBarrier>(visitor, visitor);
+ obj->VisitReferences</* kVisitNativeRoots= */ false,
+ kVerifyNone,
+ kWithoutReadBarrier>(visitor, visitor);
}
}
});
-
- return !visitor.uninterned_string_found_;
}
// new_class_set is the set of classes that were read from the class table section in the image.
@@ -1295,7 +1296,7 @@
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- static void AddImageInternTable(gc::space::ImageSpace* space)
+ static void HandleAppImageStrings(gc::space::ImageSpace* space)
REQUIRES_SHARED(Locks::mutator_lock_);
static void UpdateInternStrings(
@@ -1379,8 +1380,11 @@
}
if (ClassLinker::kAppImageMayContainStrings) {
- AddImageInternTable(space);
- DCHECK(VerifyStringInterning(*space));
+ HandleAppImageStrings(space);
+
+ if (kIsDebugBuild) {
+ VerifyStringInterning(*space);
+ }
}
if (kVerifyArtMethodDeclaringClasses) {
@@ -1395,51 +1399,76 @@
gc::space::ImageSpace* space,
const SafeMap<mirror::String*, mirror::String*>& intern_remap) {
const uint8_t* target_base = space->Begin();
- const ImageSection& sro_section = space->GetImageHeader().GetImageStringReferenceOffsetsSection();
- const size_t num_string_offsets = sro_section.Size() / sizeof(uint32_t);
+ const ImageSection& sro_section =
+ space->GetImageHeader().GetImageStringReferenceOffsetsSection();
+ const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo);
VLOG(image)
<< "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
<< num_string_offsets;
- const uint32_t* sro_base =
- reinterpret_cast<const uint32_t*>(target_base + sro_section.Offset());
+ const auto* sro_base =
+ reinterpret_cast<const AppImageReferenceOffsetInfo*>(target_base + sro_section.Offset());
for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) {
- if (HasNativeRefTag(sro_base[offset_index])) {
- void* raw_field_addr = space->Begin() + ClearNativeRefTag(sro_base[offset_index]);
- mirror::CompressedReference<mirror::Object>* objref_addr =
- reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(raw_field_addr);
- mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString();
+ uint32_t base_offset = sro_base[offset_index].first;
+
+ if (HasDexCacheNativeRefTag(base_offset)) {
+ base_offset = ClearDexCacheNativeRefTag(base_offset);
+ DCHECK_ALIGNED(base_offset, 2);
+
+ ObjPtr<mirror::DexCache> dex_cache =
+ reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
+ uint32_t string_index = sro_base[offset_index].second;
+
+ mirror::StringDexCachePair source = dex_cache->GetStrings()[string_index].load();
+ ObjPtr<mirror::String> referred_string = source.object.Read();
DCHECK(referred_string != nullptr);
- auto it = intern_remap.find(referred_string);
+ auto it = intern_remap.find(referred_string.Ptr());
if (it != intern_remap.end()) {
- objref_addr->Assign(it->second);
+ // This doesn't use SetResolvedString to maintain consistency with how
+ // we load the string. The index from the source string must be
+ // re-used due to the circular nature of the cache. Because we are not
+ // using a helper function we need to mark the GC card manually.
+ WriteBarrier::ForEveryFieldWrite(dex_cache);
+ dex_cache->GetStrings()[string_index].store(
+ mirror::StringDexCachePair(it->second, source.index));
}
- } else {
- void* raw_field_addr = space->Begin() + sro_base[offset_index];
- mirror::HeapReference<mirror::Object>* objref_addr =
- reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr);
- mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString();
- DCHECK(referred_string != nullptr);
- auto it = intern_remap.find(referred_string);
+ } else {
+ uint32_t raw_member_offset = sro_base[offset_index].second;
+ DCHECK_ALIGNED(base_offset, 2);
+ DCHECK_ALIGNED(raw_member_offset, 2);
+
+ ObjPtr<mirror::Object> obj_ptr =
+ reinterpret_cast<mirror::Object*>(space->Begin() + base_offset);
+ MemberOffset member_offset(raw_member_offset);
+ ObjPtr<mirror::String> referred_string =
+ obj_ptr->GetFieldObject<mirror::String,
+ kVerifyNone,
+ kWithoutReadBarrier,
+ /* kIsVolatile= */ false>(member_offset);
+ DCHECK(referred_string != nullptr);
+
+ auto it = intern_remap.find(referred_string.Ptr());
if (it != intern_remap.end()) {
- objref_addr->Assign<false>(it->second);
+ obj_ptr->SetFieldObject</* kTransactionActive= */ false,
+ /* kCheckTransaction= */ false,
+ kVerifyNone,
+ /* kIsVolatile= */ false>(member_offset, it->second);
}
}
}
}
-void AppImageLoadingHelper::AddImageInternTable(gc::space::ImageSpace* space) {
+void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space) {
// Iterate over the string reference offsets stored in the image and intern
// the strings they point to.
ScopedTrace timing("AppImage:InternString");
Thread* const self = Thread::Current();
- Runtime* const runtime = Runtime::Current();
- InternTable* const intern_table = runtime->GetInternTable();
+ InternTable* const intern_table = Runtime::Current()->GetInternTable();
// Add the intern table, removing any conflicts. For conflicts, store the new address in a map
// for faster lookup.
@@ -1447,7 +1476,7 @@
SafeMap<mirror::String*, mirror::String*> intern_remap;
intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
REQUIRES_SHARED(Locks::mutator_lock_) {
- VLOG(image) << "AppImage:StringsInInternTable = " << interns.size();
+ VLOG(image) << "AppImage:stringsInInternTableSize = " << interns.size();
for (auto it = interns.begin(); it != interns.end(); ) {
ObjPtr<mirror::String> string = it->Read();
ObjPtr<mirror::String> existing = intern_table->LookupWeak(self, string);
@@ -1463,7 +1492,7 @@
}
});
- VLOG(image) << "AppImage:ConflictingInternStrings = " << intern_remap.size();
+ VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
// For debug builds, always run the code below to get coverage.
if (kIsDebugBuild || !intern_remap.empty()) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0ae7a9b..7afd575 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -111,9 +111,7 @@
class ClassLinker {
public:
- // Disabled until AppImageLoadingHelper::UpdateInternStrings does the missing GC card marks.
- // Bug: 117846779
- static constexpr bool kAppImageMayContainStrings = false;
+ static constexpr bool kAppImageMayContainStrings = true;
explicit ClassLinker(InternTable* intern_table);
virtual ~ClassLinker();
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 774f19e..c48ab36 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -191,12 +191,6 @@
DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
};
-#define TEST_DISABLED() \
- if ((true)) { \
- printf("WARNING: TEST DISABLED\n"); \
- return; \
- }
-
#define TEST_DISABLED_FOR_ARM() \
if (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kThumb2) { \
printf("WARNING: TEST DISABLED FOR ARM\n"); \
diff --git a/runtime/image.h b/runtime/image.h
index bd903da..0dec5f7 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -442,11 +442,27 @@
};
/*
- * Tags the last bit. Used by AppImage logic to differentiate between managed
- * and native references.
+ * This type holds the information necessary to fix up AppImage string
+ * references.
+ *
+ * The first element of the pair is an offset into the image space. If the
+ * offset is tagged (testable using HasDexCacheNativeRefTag) it indicates the location
+ * of a DexCache object that has one or more native references to managed
+ * strings that need to be fixed up. In this case the second element has no
+ * meaningful value.
+ *
+ * If the first element isn't tagged then it indicates the location of a
+ * managed object with a field that needs fixing up. In this case the second
+ * element of the pair is an object-relative offset to the field in question.
+ */
+typedef std::pair<uint32_t, uint32_t> AppImageReferenceOffsetInfo;
+
+/*
+ * Tags the last bit. Used by AppImage logic to differentiate between pointers
+ * to managed objects and pointers to native reference arrays.
*/
template<typename T>
-T SetNativeRefTag(T val) {
+T SetDexCacheNativeRefTag(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
return val | 1u;
@@ -454,10 +470,11 @@
/*
* Retrieves the value of the last bit. Used by AppImage logic to
- * differentiate between managed and native references.
+ * differentiate between pointers to managed objects and pointers to native
+ * reference arrays.
*/
template<typename T>
-bool HasNativeRefTag(T val) {
+bool HasDexCacheNativeRefTag(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
return (val & 1u) == 1u;
@@ -465,10 +482,11 @@
/*
* Sets the last bit of the value to 0. Used by AppImage logic to
- * differentiate between managed and native references.
+ * differentiate between pointers to managed objects and pointers to native
+ * reference arrays.
*/
template<typename T>
-T ClearNativeRefTag(T val) {
+T ClearDexCacheNativeRefTag(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
return val & ~1u;