Merge "ART: Use C++17 extract/modify/insert pattern."
diff --git a/Android.mk b/Android.mk
index 526cd59..1a7ed43 100644
--- a/Android.mk
+++ b/Android.mk
@@ -352,13 +352,18 @@
# Module with both release and debug variants, as well as
# additional tools.
TARGET_RUNTIME_APEX := com.android.runtime.debug
+ APEX_TEST_MODULE := art-check-debug-apex-gen-fakebin
else
# Release module (without debug variants nor tools).
TARGET_RUNTIME_APEX := com.android.runtime.release
+ APEX_TEST_MODULE := art-check-release-apex-gen-fakebin
endif
LOCAL_MODULE := com.android.runtime
LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX)
+ifneq ($(HOST_OS),darwin)
+ LOCAL_REQUIRED_MODULES += $(APEX_TEST_MODULE)
+endif
# Clear locally used variable.
art_target_include_debug_build :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 4a6637b..e3e5b6e 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -230,3 +230,72 @@
},
},
}
+
+python_binary_host {
+ name: "art-apex-tester",
+ srcs: ["art_apex_test.py"],
+ main: "art_apex_test.py",
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+}
+
+// Genrules so we can run the checker, and empty Java library so that it gets executed.
+
+genrule {
+ name: "art-check-release-apex-gen",
+ srcs: [":com.android.runtime.release"],
+ tools: [
+ "art-apex-tester",
+ "debugfs",
+ ],
+ cmd: "$(location art-apex-tester)"
+ + " --debugfs $(location debugfs)"
+ + " --tmpdir $(genDir)"
+ + " $(in)"
+ + " && touch $(out)",
+ out: ["art-check-release-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+ name: "art-check-release-apex-gen-fakebin",
+ srcs: [":art-check-release-apex-gen"],
+ host_supported: true,
+ device_supported: false,
+ target: {
+ darwin: {
+ enabled: false, // No python3.
+ },
+ },
+}
+
+genrule {
+ name: "art-check-debug-apex-gen",
+ srcs: [":com.android.runtime.debug"],
+ tools: [
+ "art-apex-tester",
+ "debugfs",
+ ],
+ cmd: "$(location art-apex-tester)"
+ + " --debugfs $(location debugfs)"
+ + " --tmpdir $(genDir)"
+ + " --debug"
+ + " $(in)"
+ + " && touch $(out)",
+ out: ["art-check-debug-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+ name: "art-check-debug-apex-gen-fakebin",
+ srcs: [":art-check-debug-apex-gen"],
+ host_supported: true,
+ device_supported: false,
+ target: {
+ darwin: {
+ enabled: false, // No python3.
+ },
+ },
+}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2721cb5..2de0f0c 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,6 +20,7 @@
#include "art_method-inl.h"
#include "base/utils.h"
#include "class_linker.h"
+#include "class_root.h"
#include "dex/invoke_type.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
@@ -362,4 +363,13 @@
return info;
}
+void IntrinsicVisitor::AssertNonMovableStringClass() {
+ if (kIsDebugBuild) {
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
+ CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 50b13c8..ab68cce 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -142,6 +142,8 @@
protected:
IntrinsicVisitor() {}
+ static void AssertNonMovableStringClass();
+
private:
DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
};
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index ca790f6..ec5d17a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1464,8 +1464,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str.X(), class_offset));
+ // /* HeapReference<Class> */ temp1 = arg->klass_
__ Ldr(temp1, MemOperand(arg.X(), class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, temp1);
__ B(&return_false, ne);
}
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 396ff62..f0aa92e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1529,8 +1529,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str, class_offset));
+ // /* HeapReference<Class> */ out = arg->klass_
__ Ldr(out, MemOperand(arg, class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, out);
__ B(ne, &return_false, /* is_far_target= */ false);
}
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5b35974..3da0e57 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1575,8 +1575,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bne(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index afaa4ca..3e68765 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bnec(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 8747f06..de697f0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1072,7 +1072,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ ecx = str->klass_
__ movl(ecx, Address(str, class_offset));
+ // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(ecx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 167c1d8..e79c0c9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1275,7 +1275,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ rcx = str->klass_
__ movl(rcx, Address(str, class_offset));
+ // if (rcx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(rcx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d29a6b7..46ae545 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -25,6 +25,7 @@
#include <memory>
#include <queue>
#include <string>
+#include <string_view>
#include <tuple>
#include <unordered_map>
#include <utility>
@@ -7040,11 +7041,13 @@
return FindSameNameAndSignature(cmp, rest...);
}
+namespace {
+
// Check that all vtable entries are present in this class's virtuals or are the same as a
// superclasses vtable entry.
-static void CheckClassOwnsVTableEntries(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+void CheckClassOwnsVTableEntries(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<2> hs(self);
Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
@@ -7074,47 +7077,185 @@
// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
// method is overridden in a subclass.
-static void CheckVTableHasNoDuplicates(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+template <PointerSize kPointerSize>
+void CheckVTableHasNoDuplicates(Thread* self, Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
int32_t num_entries = vtable->GetLength();
- for (int32_t i = 0; i < num_entries; i++) {
- ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
- // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+
+ // Observations:
+ // * The older implementation was O(n^2) and got too expensive for apps with larger classes.
+ // * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus,
+ // for many classes outside of libcore a cross-dexfile check has to be run anyways.
+ // * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have
+ // to be done. It is thus OK in a single-pass algorithm to read all data, anyways.
+ // * The single-pass algorithm will trade memory for speed, but that is OK.
+
+ CHECK_GT(num_entries, 0);
+
+ auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m1 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i);
+ ArtMethod* m2 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+ LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+ << klass->PrettyClass() << " in method " << m1->PrettyMethod()
+ << " (0x" << std::hex << reinterpret_cast<uintptr_t>(m2) << ") and "
+ << m2->PrettyMethod() << " (0x" << std::hex
+ << reinterpret_cast<uintptr_t>(m2) << ")";
+ };
+ struct BaseHashType {
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ };
+
+ // Check assuming all entries come from the same dex file.
+ {
+ // Find the first interesting method and its dex file.
+ int32_t start = 0;
+ for (; start < num_entries; ++start) {
+ ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ break;
+ }
+ if (start == num_entries) {
+ return;
+ }
+ const DexFile* dex_file =
+ vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start)->
+ GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile();
+
+ // Helper function to avoid logging if we have to run the cross-file checks.
+ auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Use a map to store seen entries, as the storage space is too large for a bitvector.
+ using PairType = std::pair<uint32_t, uint16_t>;
+ struct PairHash : BaseHashType {
+ size_t operator()(const PairType& key) const {
+ return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second);
+ }
+ };
+ std::unordered_map<PairType, int32_t, PairHash> seen;
+ seen.reserve(2 * num_entries);
+ bool need_slow_path = false;
+ bool found_dup = false;
+ for (int i = start; i < num_entries; ++i) {
+ // Can use Unchecked here as the start loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ if (dex_file != m->GetDexFile()) {
+ need_slow_path = true;
+ break;
+ }
+ const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex());
+ PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_);
+ auto it = seen.find(pair);
+ if (it != seen.end()) {
+ found_dup = true;
+ if (log_warn) {
+ log_fn(it->second, i);
+ }
+ } else {
+ seen.emplace(pair, i);
+ }
+ }
+ return std::make_pair(need_slow_path, found_dup);
+ };
+ std::pair<bool, bool> result = check_fn(/* log_warn= */ false);
+ if (!result.first) {
+ if (result.second) {
+ check_fn(/* log_warn= */ true);
+ }
+ return;
+ }
+ }
+
+ // Need to check across dex files.
+ struct Entry {
+ size_t cached_hash = 0;
+ const char* name = nullptr;
+ Signature signature = Signature::NoSignature();
+ uint32_t name_len = 0;
+
+ Entry(const DexFile* dex_file, const dex::MethodId& mid)
+ : name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)),
+ signature(dex_file->GetMethodSignature(mid)) {
+ }
+
+ bool operator==(const Entry& other) const {
+ if (name_len != other.name_len || strcmp(name, other.name) != 0) {
+ return false;
+ }
+ return signature == other.signature;
+ }
+ };
+ struct EntryHash {
+ size_t operator()(const Entry& key) const {
+ return key.cached_hash;
+ }
+ };
+ std::unordered_map<Entry, int32_t, EntryHash> map;
+ for (int32_t i = 0; i < num_entries; ++i) {
+ // Can use Unchecked here as the first loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
vtable_entry->GetAccessFlags())) {
continue;
}
- MethodNameAndSignatureComparator name_comparator(
- vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
- for (int32_t j = i + 1; j < num_entries; j++) {
- ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
- if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
- other_entry->GetAccessFlags())) {
- continue;
- }
- if (vtable_entry == other_entry ||
- name_comparator.HasSameNameAndSignature(
- other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
- LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
- << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
- << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
- << other_entry->PrettyMethod() << " (0x" << std::hex
- << reinterpret_cast<uintptr_t>(other_entry) << ")";
- }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ const DexFile* dex_file = m->GetDexFile();
+ const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex());
+
+ Entry e(dex_file, mid);
+
+ size_t string_hash = std::hash<std::string_view>()(std::string_view(e.name, e.name_len));
+ size_t sig_hash = std::hash<std::string>()(e.signature.ToString());
+ e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash),
+ sig_hash);
+
+ auto it = map.find(e);
+ if (it != map.end()) {
+ log_fn(it->second, i);
+ } else {
+ map.emplace(e, i);
}
}
}
+void CheckVTableHasNoDuplicates(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (pointer_size) {
+ case PointerSize::k64:
+ CheckVTableHasNoDuplicates<PointerSize::k64>(self, klass);
+ break;
+ case PointerSize::k32:
+ CheckVTableHasNoDuplicates<PointerSize::k32>(self, klass);
+ break;
+ }
+}
+
static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
CheckClassOwnsVTableEntries(self, klass, pointer_size);
CheckVTableHasNoDuplicates(self, klass, pointer_size);
}
+} // namespace
+
void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
ArtMethod* unimplemented_method,
ArtMethod* imt_conflict_method,
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..d9d88e1 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,15 +224,31 @@
}
}
-template<typename T, VerifyObjectFlags kVerifyFlags>
-inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx) {
// C style casts here since we sometimes have T be a pointer, or sometimes an integer
// (for stack traces).
- if (ptr_size == PointerSize::k64) {
+ if (kPointerSize == PointerSize::k64) {
return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
+ // C style casts here since we sometimes have T be a pointer, or sometimes an integer
+ // (for stack traces).
+ if (kPointerSize == PointerSize::k64) {
+ return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+ }
+ return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+}
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+ if (ptr_size == PointerSize::k64) {
+ return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
+ }
+ return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
+}
template<bool kTransactionActive, bool kUnchecked>
inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..2e894d5 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -32,6 +32,8 @@
class MANAGED Array : public Object {
public:
+ static constexpr size_t kFirstElementOffset = 12u;
+
// The size of a java.lang.Class representing an array.
static uint32_t ClassSize(PointerSize pointer_size);
@@ -79,6 +81,17 @@
<< "Array data offset isn't aligned with component size";
return MemberOffset(data_offset);
}
+ template <size_t kComponentSize>
+ static constexpr MemberOffset DataOffset() {
+ static_assert(IsPowerOfTwo(kComponentSize), "Invalid component size");
+ constexpr size_t data_offset = RoundUp(kFirstElementOffset, kComponentSize);
+ static_assert(RoundUp(data_offset, kComponentSize) == data_offset, "RoundUp fail");
+ return MemberOffset(data_offset);
+ }
+
+ static constexpr size_t FirstElementOffset() {
+ return OFFSETOF_MEMBER(Array, first_element_);
+ }
void* GetRawData(size_t component_size, int32_t index)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -86,12 +99,24 @@
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ void* GetRawData(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
const void* GetRawData(size_t component_size, int32_t index) const {
intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset(component_size).Int32Value() +
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ const void* GetRawData(int32_t index) const {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
// Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
// returns false.
@@ -132,11 +157,11 @@
const T* GetData() const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<const T*>(GetRawData<sizeof(T)>(0));
}
T* GetData() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<T*>(GetRawData<sizeof(T)>(0));
}
T Get(int32_t i) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -196,6 +221,15 @@
template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSize(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Same as GetElementPtrSize, but uses unchecked version of array conversion. It is thus not
+ // checked whether kPtrSize matches the underlying array. Only use after at least one invocation
+ // of GetElementPtrSize!
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSizeUnchecked(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2c2ad9b..005e272 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -253,9 +253,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline IntArray* Object::AsIntArrayUnchecked() {
+ return down_cast<IntArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline IntArray* Object::AsIntArray() {
DCHECK((IsIntArray<kVerifyFlags>()));
- return down_cast<IntArray*>(this);
+ return AsIntArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
@@ -264,9 +268,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline LongArray* Object::AsLongArrayUnchecked() {
+ return down_cast<LongArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline LongArray* Object::AsLongArray() {
DCHECK((IsLongArray<kVerifyFlags>()));
- return down_cast<LongArray*>(this);
+ return AsLongArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ba222f6..ca8867d 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -223,11 +223,15 @@
bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ IntArray* AsIntArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ LongArray* AsLongArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsFloatArray() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 26f21b0..8f89f52 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -199,6 +199,7 @@
};
namespace {
+
#ifdef __APPLE__
inline char** GetEnviron() {
// When Google Test is built as a framework on MacOS X, the environ variable
@@ -212,6 +213,11 @@
extern "C" char** environ;
inline char** GetEnviron() { return environ; }
#endif
+
+void CheckConstants() {
+ CHECK_EQ(mirror::Array::kFirstElementOffset, mirror::Array::FirstElementOffset());
+}
+
} // namespace
Runtime::Runtime()
@@ -284,6 +290,7 @@
verifier_logging_threshold_ms_(100) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
+ CheckConstants();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();