You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
692 lines
26 KiB
692 lines
26 KiB
/*
|
|
* Copyright (C) 2019 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 "jni_id_manager.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
|
|
#include "android-base/macros.h"
|
|
#include "art_field-inl.h"
|
|
#include "art_method-inl.h"
|
|
#include "base/enums.h"
|
|
#include "base/globals.h"
|
|
#include "base/locks.h"
|
|
#include "base/mutex.h"
|
|
#include "class_root-inl.h"
|
|
#include "gc/allocation_listener.h"
|
|
#include "gc/heap.h"
|
|
#include "jni/jni_internal.h"
|
|
#include "jni_id_type.h"
|
|
#include "mirror/array-inl.h"
|
|
#include "mirror/array.h"
|
|
#include "mirror/class-alloc-inl.h"
|
|
#include "mirror/class-inl.h"
|
|
#include "mirror/class.h"
|
|
#include "mirror/class_ext-inl.h"
|
|
#include "mirror/object-inl.h"
|
|
#include "obj_ptr-inl.h"
|
|
#include "reflective_handle_scope-inl.h"
|
|
#include "reflective_handle_scope.h"
|
|
#include "reflective_value_visitor.h"
|
|
#include "thread-inl.h"
|
|
#include "thread.h"
|
|
|
|
namespace art {
|
|
namespace jni {
|
|
|
|
constexpr bool kTraceIds = false;
|
|
|
|
// TODO This whole thing could be done lock & wait free (since we never remove anything from the
|
|
// ids list). It's not clear this would be worthwile though.
|
|
|
|
namespace {
|
|
|
|
static constexpr size_t IdToIndex(uintptr_t id) {
|
|
return id >> 1;
|
|
}
|
|
|
|
static constexpr uintptr_t IndexToId(size_t index) {
|
|
return (index << 1) + 1;
|
|
}
|
|
|
|
template <typename ArtType>
|
|
ObjPtr<mirror::PointerArray> GetIds(ObjPtr<mirror::Class> k, ArtType* t)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
ObjPtr<mirror::Object> ret;
|
|
if constexpr (std::is_same_v<ArtType, ArtField>) {
|
|
ret = t->IsStatic() ? k->GetStaticFieldIds() : k->GetInstanceFieldIds();
|
|
} else {
|
|
ret = t->IsObsolete() ? nullptr : k->GetMethodIds();
|
|
}
|
|
DCHECK(ret.IsNull() || ret->IsArrayInstance()) << "Should have bailed out early!";
|
|
if (kIsDebugBuild && !ret.IsNull()) {
|
|
if (kRuntimePointerSize == PointerSize::k32) {
|
|
CHECK(ret->IsIntArray());
|
|
} else {
|
|
CHECK(ret->IsLongArray());
|
|
}
|
|
}
|
|
return down_cast<mirror::PointerArray*>(ret.Ptr());
|
|
}
|
|
|
|
template <typename ArtType>
|
|
bool ShouldReturnPointer(ObjPtr<mirror::Class> klass, ArtType* t)
|
|
REQUIRES_SHARED(Locks::mutator_lock_);
|
|
|
|
template <>
|
|
bool ShouldReturnPointer(ObjPtr<mirror::Class> klass, ArtMethod* t ATTRIBUTE_UNUSED) {
|
|
ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
|
|
if (ext.IsNull()) {
|
|
return true;
|
|
}
|
|
ObjPtr<mirror::Object> arr = ext->GetJMethodIDs();
|
|
return arr.IsNull() || !arr->IsArrayInstance();
|
|
}
|
|
|
|
template<>
|
|
bool ShouldReturnPointer(ObjPtr<mirror::Class> klass, ArtField* t) {
|
|
ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
|
|
if (ext.IsNull()) {
|
|
return true;
|
|
}
|
|
ObjPtr<mirror::Object> arr = t->IsStatic() ? ext->GetStaticJFieldIDs()
|
|
: ext->GetInstanceJFieldIDs();
|
|
return arr.IsNull() || !arr->IsArrayInstance();
|
|
}
|
|
|
|
|
|
// Forces the appropriate id array to be present if possible. Returns true if allocation was
|
|
// attempted but failed.
|
|
template <typename ArtType>
|
|
bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtType* t)
|
|
REQUIRES_SHARED(Locks::mutator_lock_);
|
|
|
|
template <>
|
|
bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtField* field) {
|
|
ScopedExceptionStorage ses(self);
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::Class> h_k(hs.NewHandle(k));
|
|
if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
|
|
return false;
|
|
} else {
|
|
// NB This modifies the class to allocate the ClassExt and the ids array.
|
|
field->IsStatic() ? mirror::Class::EnsureStaticFieldIds(h_k)
|
|
: mirror::Class::EnsureInstanceFieldIds(h_k);
|
|
}
|
|
if (self->IsExceptionPending()) {
|
|
self->AssertPendingOOMException();
|
|
ses.SuppressOldException("Failed to allocate maps for jmethodIDs. ");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <>
|
|
bool EnsureIdsArray(Thread* self, ObjPtr<mirror::Class> k, ArtMethod* method) {
|
|
if (method->IsObsolete()) {
|
|
if (kTraceIds) {
|
|
LOG(INFO) << "jmethodID for Obsolete method " << method->PrettyMethod() << " requested!";
|
|
}
|
|
// No ids array for obsolete methods. Just do a linear scan.
|
|
return false;
|
|
}
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::Class> h_k(hs.NewHandle(k));
|
|
if (Locks::mutator_lock_->IsExclusiveHeld(self) || !Locks::mutator_lock_->IsSharedHeld(self)) {
|
|
return false;
|
|
} else {
|
|
// NB This modifies the class to allocate the ClassExt and the ids array.
|
|
mirror::Class::EnsureMethodIds(h_k);
|
|
}
|
|
if (self->IsExceptionPending()) {
|
|
self->AssertPendingOOMException();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename ArtType>
|
|
size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtType* t, PointerSize pointer_size)
|
|
REQUIRES_SHARED(Locks::mutator_lock_);
|
|
template <>
|
|
size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtField* f, PointerSize ptr_size ATTRIBUTE_UNUSED) {
|
|
return f->IsStatic() ? k->GetStaticFieldIdOffset(f) : k->GetInstanceFieldIdOffset(f);
|
|
}
|
|
template <>
|
|
size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtMethod* method, PointerSize pointer_size) {
|
|
return method->IsObsolete() ? -1 : k->GetMethodIdOffset(method, pointer_size);
|
|
}
|
|
|
|
// Calls the relevant PrettyMethod/PrettyField on the input.
|
|
template <typename ArtType>
|
|
std::string PrettyGeneric(ArtType t) REQUIRES_SHARED(Locks::mutator_lock_);
|
|
template <>
|
|
std::string PrettyGeneric(ArtMethod* f) {
|
|
return f->PrettyMethod();
|
|
}
|
|
template <>
|
|
std::string PrettyGeneric(ReflectiveHandle<ArtMethod> f) {
|
|
return f->PrettyMethod();
|
|
}
|
|
template <>
|
|
std::string PrettyGeneric(ArtField* f) {
|
|
return f->PrettyField();
|
|
}
|
|
template <>
|
|
std::string PrettyGeneric(ReflectiveHandle<ArtField> f) {
|
|
return f->PrettyField();
|
|
}
|
|
|
|
// Checks if the field or method is obsolete.
|
|
template <typename ArtType>
|
|
bool IsObsolete(ReflectiveHandle<ArtType> t) REQUIRES_SHARED(Locks::mutator_lock_);
|
|
template <>
|
|
bool IsObsolete(ReflectiveHandle<ArtField> t ATTRIBUTE_UNUSED) {
|
|
return false;
|
|
}
|
|
template <>
|
|
bool IsObsolete(ReflectiveHandle<ArtMethod> t) {
|
|
return t->IsObsolete();
|
|
}
|
|
|
|
// Get the canonical (non-copied) version of the field or method. Only relevant for methods.
|
|
template <typename ArtType>
|
|
ArtType* Canonicalize(ReflectiveHandle<ArtType> t) REQUIRES_SHARED(Locks::mutator_lock_);
|
|
template <>
|
|
ArtField* Canonicalize(ReflectiveHandle<ArtField> t) {
|
|
return t.Get();
|
|
}
|
|
template <>
|
|
ArtMethod* Canonicalize(ReflectiveHandle<ArtMethod> t) {
|
|
if (UNLIKELY(t->IsCopied())) {
|
|
return t->GetCanonicalMethod();
|
|
}
|
|
return t.Get();
|
|
}
|
|
|
|
}; // namespace
|
|
|
|
// We increment the id by 2 each time to allow us to use the LSB as a flag that the ID is an index
|
|
// and not a pointer. This gives us 2**31 unique methods that can be addressed on 32-bit art, which
|
|
// should be more than enough.
|
|
template <>
|
|
uintptr_t JniIdManager::GetNextId<ArtField>(JniIdType type) {
|
|
DCHECK_EQ(type, JniIdType::kIndices);
|
|
uintptr_t res = next_field_id_;
|
|
next_field_id_ += 2;
|
|
CHECK_GT(next_field_id_, res) << "jfieldID Overflow";
|
|
return res;
|
|
}
|
|
|
|
template <>
|
|
uintptr_t JniIdManager::GetNextId<ArtMethod>(JniIdType type) {
|
|
DCHECK_EQ(type, JniIdType::kIndices);
|
|
uintptr_t res = next_method_id_;
|
|
next_method_id_ += 2;
|
|
CHECK_GT(next_method_id_, res) << "jmethodID Overflow";
|
|
return res;
|
|
}
|
|
template <>
|
|
std::vector<ArtField*>& JniIdManager::GetGenericMap<ArtField>() {
|
|
return field_id_map_;
|
|
}
|
|
|
|
template <>
|
|
std::vector<ArtMethod*>& JniIdManager::GetGenericMap<ArtMethod>() {
|
|
return method_id_map_;
|
|
}
|
|
template <>
|
|
size_t JniIdManager::GetLinearSearchStartId<ArtField>(
|
|
ReflectiveHandle<ArtField> t ATTRIBUTE_UNUSED) {
|
|
return deferred_allocation_field_id_start_;
|
|
}
|
|
|
|
template <>
|
|
size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ReflectiveHandle<ArtMethod> m) {
|
|
if (m->IsObsolete()) {
|
|
return 1;
|
|
} else {
|
|
return deferred_allocation_method_id_start_;
|
|
}
|
|
}
|
|
|
|
// TODO need to fix races in here with visitors
|
|
template <typename ArtType>
|
|
uintptr_t JniIdManager::EncodeGenericId(ReflectiveHandle<ArtType> t) {
|
|
static_assert(std::is_same_v<ArtType, ArtField> || std::is_same_v<ArtType, ArtMethod>,
|
|
"Expected ArtField or ArtMethod");
|
|
Runtime* runtime = Runtime::Current();
|
|
JniIdType id_type = runtime->GetJniIdType();
|
|
if (id_type == JniIdType::kPointer || t == nullptr) {
|
|
return reinterpret_cast<uintptr_t>(t.Get());
|
|
}
|
|
Thread* self = Thread::Current();
|
|
ScopedExceptionStorage ses(self);
|
|
DCHECK(!t->GetDeclaringClass().IsNull()) << "Null declaring class " << PrettyGeneric(t);
|
|
size_t off = GetIdOffset(t->GetDeclaringClass(), Canonicalize(t), kRuntimePointerSize);
|
|
// Here is the earliest point we can suspend.
|
|
bool allocation_failure = EnsureIdsArray(self, t->GetDeclaringClass(), t.Get());
|
|
if (allocation_failure) {
|
|
self->AssertPendingOOMException();
|
|
ses.SuppressOldException("OOM exception while trying to allocate JNI ids.");
|
|
return 0u;
|
|
} else if (ShouldReturnPointer(t->GetDeclaringClass(), t.Get())) {
|
|
return reinterpret_cast<uintptr_t>(t.Get());
|
|
}
|
|
ObjPtr<mirror::Class> klass = t->GetDeclaringClass();
|
|
ObjPtr<mirror::PointerArray> ids(GetIds(klass, t.Get()));
|
|
uintptr_t cur_id = 0;
|
|
if (!ids.IsNull()) {
|
|
DCHECK_GT(ids->GetLength(), static_cast<int32_t>(off)) << " is " << PrettyGeneric(t);
|
|
DCHECK_LE(0, static_cast<int32_t>(off)) << " is " << PrettyGeneric(t);
|
|
cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize);
|
|
}
|
|
if (cur_id != 0) {
|
|
return cur_id;
|
|
}
|
|
WriterMutexLock mu(self, *Locks::jni_id_lock_);
|
|
ScopedAssertNoThreadSuspension sants("EncodeJniId critical section.");
|
|
// Check the ids array for a racing id.
|
|
constexpr std::pair<size_t, size_t> counts {
|
|
std::is_same_v<ArtType, ArtField> ? 1 : 0,
|
|
std::is_same_v<ArtType, ArtField> ? 0 : 1,
|
|
};
|
|
StackReflectiveHandleScope<counts.first, counts.second> hs(self);
|
|
t = hs.NewHandle(Canonicalize(t));
|
|
if (!ids.IsNull()) {
|
|
// It's possible we got suspended and structurally redefined during the EnsureIdsArray. We need
|
|
// to get the information again.
|
|
ids = GetIds(klass, t.Get());
|
|
off = GetIdOffset(klass, Canonicalize(t), kRuntimePointerSize);
|
|
CHECK(!ids.IsNull());
|
|
cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize);
|
|
if (cur_id != 0) {
|
|
// We were racing some other thread and lost.
|
|
return cur_id;
|
|
}
|
|
} else {
|
|
// We cannot allocate anything here or don't have an ids array (we might be an obsolete method).
|
|
DCHECK(IsObsolete(t) || deferred_allocation_refcount_ > 0u)
|
|
<< "deferred_allocation_refcount_: " << deferred_allocation_refcount_
|
|
<< " t: " << PrettyGeneric(t);
|
|
// Check to see if we raced and lost to another thread.
|
|
const std::vector<ArtType*>& vec = GetGenericMap<ArtType>();
|
|
bool found = false;
|
|
// simple count-while.
|
|
size_t search_start_index = IdToIndex(GetLinearSearchStartId(t));
|
|
size_t index = std::count_if(vec.cbegin() + search_start_index,
|
|
vec.cend(),
|
|
[&found, &self, t](const ArtType* candidate) {
|
|
Locks::mutator_lock_->AssertSharedHeld(self);
|
|
found = found || candidate == t.Get();
|
|
return !found;
|
|
}) +
|
|
search_start_index;
|
|
if (found) {
|
|
// We were either racing some other thread and lost or this thread was asked to encode the
|
|
// same method multiple times while holding the mutator lock.
|
|
DCHECK_EQ(vec[index], t.Get())
|
|
<< "Expected: " << PrettyGeneric(vec[index]) << " got " << PrettyGeneric(t)
|
|
<< " at index " << index << " (id: " << IndexToId(index) << ").";
|
|
return IndexToId(index);
|
|
}
|
|
}
|
|
cur_id = GetNextId<ArtType>(id_type);
|
|
DCHECK_EQ(cur_id % 2, 1u);
|
|
size_t cur_index = IdToIndex(cur_id);
|
|
std::vector<ArtType*>& vec = GetGenericMap<ArtType>();
|
|
vec.reserve(cur_index + 1);
|
|
vec.resize(std::max(vec.size(), cur_index + 1), nullptr);
|
|
vec[cur_index] = t.Get();
|
|
if (ids.IsNull()) {
|
|
if (kIsDebugBuild && !IsObsolete(t)) {
|
|
CHECK_NE(deferred_allocation_refcount_, 0u)
|
|
<< "Failed to allocate ids array despite not being forbidden from doing so!";
|
|
Locks::mutator_lock_->AssertExclusiveHeld(self);
|
|
}
|
|
} else {
|
|
ids->SetElementPtrSize(off, reinterpret_cast<void*>(cur_id), kRuntimePointerSize);
|
|
}
|
|
return cur_id;
|
|
}
|
|
|
|
jfieldID JniIdManager::EncodeFieldId(ArtField* field) {
|
|
StackArtFieldHandleScope<1> rhs(Thread::Current());
|
|
return EncodeFieldId(rhs.NewHandle(field));
|
|
}
|
|
|
|
jfieldID JniIdManager::EncodeFieldId(ReflectiveHandle<ArtField> field) {
|
|
auto* res = reinterpret_cast<jfieldID>(EncodeGenericId(field));
|
|
if (kTraceIds && field != nullptr) {
|
|
LOG(INFO) << "Returning " << res << " for field " << field->PrettyField();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
jmethodID JniIdManager::EncodeMethodId(ArtMethod* method) {
|
|
StackArtMethodHandleScope<1> rhs(Thread::Current());
|
|
return EncodeMethodId(rhs.NewHandle(method));
|
|
}
|
|
|
|
jmethodID JniIdManager::EncodeMethodId(ReflectiveHandle<ArtMethod> method) {
|
|
auto* res = reinterpret_cast<jmethodID>(EncodeGenericId(method));
|
|
if (kTraceIds && method != nullptr) {
|
|
LOG(INFO) << "Returning " << res << " for method " << method->PrettyMethod();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void JniIdManager::VisitRoots(RootVisitor *visitor) {
|
|
pointer_marker_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
|
|
}
|
|
|
|
void JniIdManager::Init(Thread* self) {
|
|
// When compiling we don't want to have anything to do with any of this, which is fine since JNI
|
|
// ids won't be created during AOT compilation. This also means we don't need to do any
|
|
// complicated stuff with the image-writer.
|
|
if (!Runtime::Current()->IsAotCompiler()) {
|
|
// Allocate the marker
|
|
StackHandleScope<3> hs(self);
|
|
Handle<mirror::Object> marker_obj(
|
|
hs.NewHandle(GetClassRoot<mirror::Object>()->AllocObject(self)));
|
|
CHECK(!marker_obj.IsNull());
|
|
pointer_marker_ = GcRoot<mirror::Object>(marker_obj.Get());
|
|
// Manually mark class-ext as having all pointer-ids to avoid any annoying loops.
|
|
Handle<mirror::Class> class_ext_class(hs.NewHandle(GetClassRoot<mirror::ClassExt>()));
|
|
mirror::Class::EnsureExtDataPresent(class_ext_class, self);
|
|
Handle<mirror::ClassExt> class_ext_ext(hs.NewHandle(class_ext_class->GetExtData()));
|
|
class_ext_ext->SetIdsArraysForClassExtExtData(marker_obj.Get());
|
|
}
|
|
}
|
|
|
|
void JniIdManager::VisitReflectiveTargets(ReflectiveValueVisitor* rvv) {
|
|
art::WriterMutexLock mu(Thread::Current(), *Locks::jni_id_lock_);
|
|
for (auto it = field_id_map_.begin(); it != field_id_map_.end(); ++it) {
|
|
ArtField* old_field = *it;
|
|
uintptr_t id = IndexToId(std::distance(field_id_map_.begin(), it));
|
|
ArtField* new_field =
|
|
rvv->VisitField(old_field, JniIdReflectiveSourceInfo(reinterpret_cast<jfieldID>(id)));
|
|
if (old_field != new_field) {
|
|
*it = new_field;
|
|
ObjPtr<mirror::Class> old_class(old_field->GetDeclaringClass());
|
|
ObjPtr<mirror::Class> new_class(new_field->GetDeclaringClass());
|
|
ObjPtr<mirror::ClassExt> old_ext_data(old_class->GetExtData());
|
|
ObjPtr<mirror::ClassExt> new_ext_data(new_class->GetExtData());
|
|
if (!old_ext_data.IsNull()) {
|
|
CHECK(!old_ext_data->HasInstanceFieldPointerIdMarker() &&
|
|
!old_ext_data->HasStaticFieldPointerIdMarker())
|
|
<< old_class->PrettyClass();
|
|
// Clear the old field mapping.
|
|
if (old_field->IsStatic()) {
|
|
size_t old_off = ArraySlice<ArtField>(old_class->GetSFieldsPtr()).OffsetOf(old_field);
|
|
ObjPtr<mirror::PointerArray> old_statics(old_ext_data->GetStaticJFieldIDsPointerArray());
|
|
if (!old_statics.IsNull()) {
|
|
old_statics->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
|
|
}
|
|
} else {
|
|
size_t old_off = ArraySlice<ArtField>(old_class->GetIFieldsPtr()).OffsetOf(old_field);
|
|
ObjPtr<mirror::PointerArray> old_instances(
|
|
old_ext_data->GetInstanceJFieldIDsPointerArray());
|
|
if (!old_instances.IsNull()) {
|
|
old_instances->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
|
|
}
|
|
}
|
|
}
|
|
if (!new_ext_data.IsNull()) {
|
|
CHECK(!new_ext_data->HasInstanceFieldPointerIdMarker() &&
|
|
!new_ext_data->HasStaticFieldPointerIdMarker())
|
|
<< new_class->PrettyClass();
|
|
// Set the new field mapping.
|
|
if (new_field->IsStatic()) {
|
|
size_t new_off = ArraySlice<ArtField>(new_class->GetSFieldsPtr()).OffsetOf(new_field);
|
|
ObjPtr<mirror::PointerArray> new_statics(new_ext_data->GetStaticJFieldIDsPointerArray());
|
|
if (!new_statics.IsNull()) {
|
|
new_statics->SetElementPtrSize(new_off, id, kRuntimePointerSize);
|
|
}
|
|
} else {
|
|
size_t new_off = ArraySlice<ArtField>(new_class->GetIFieldsPtr()).OffsetOf(new_field);
|
|
ObjPtr<mirror::PointerArray> new_instances(
|
|
new_ext_data->GetInstanceJFieldIDsPointerArray());
|
|
if (!new_instances.IsNull()) {
|
|
new_instances->SetElementPtrSize(new_off, id, kRuntimePointerSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (auto it = method_id_map_.begin(); it != method_id_map_.end(); ++it) {
|
|
ArtMethod* old_method = *it;
|
|
uintptr_t id = IndexToId(std::distance(method_id_map_.begin(), it));
|
|
ArtMethod* new_method =
|
|
rvv->VisitMethod(old_method, JniIdReflectiveSourceInfo(reinterpret_cast<jmethodID>(id)));
|
|
if (old_method != new_method) {
|
|
*it = new_method;
|
|
ObjPtr<mirror::Class> old_class(old_method->GetDeclaringClass());
|
|
ObjPtr<mirror::Class> new_class(new_method->GetDeclaringClass());
|
|
ObjPtr<mirror::ClassExt> old_ext_data(old_class->GetExtData());
|
|
ObjPtr<mirror::ClassExt> new_ext_data(new_class->GetExtData());
|
|
if (!old_ext_data.IsNull()) {
|
|
CHECK(!old_ext_data->HasMethodPointerIdMarker()) << old_class->PrettyClass();
|
|
// Clear the old method mapping.
|
|
size_t old_off = ArraySlice<ArtMethod>(old_class->GetMethodsPtr()).OffsetOf(old_method);
|
|
ObjPtr<mirror::PointerArray> old_methods(old_ext_data->GetJMethodIDsPointerArray());
|
|
if (!old_methods.IsNull()) {
|
|
old_methods->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
|
|
}
|
|
}
|
|
if (!new_ext_data.IsNull()) {
|
|
CHECK(!new_ext_data->HasMethodPointerIdMarker()) << new_class->PrettyClass();
|
|
// Set the new method mapping.
|
|
size_t new_off = ArraySlice<ArtMethod>(new_class->GetMethodsPtr()).OffsetOf(new_method);
|
|
ObjPtr<mirror::PointerArray> new_methods(new_ext_data->GetJMethodIDsPointerArray());
|
|
if (!new_methods.IsNull()) {
|
|
new_methods->SetElementPtrSize(new_off, id, kRuntimePointerSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename ArtType> ArtType* JniIdManager::DecodeGenericId(uintptr_t t) {
|
|
if (Runtime::Current()->GetJniIdType() == JniIdType::kIndices && (t % 2) == 1) {
|
|
ReaderMutexLock mu(Thread::Current(), *Locks::jni_id_lock_);
|
|
size_t index = IdToIndex(t);
|
|
DCHECK_GT(GetGenericMap<ArtType>().size(), index);
|
|
return GetGenericMap<ArtType>().at(index);
|
|
} else {
|
|
DCHECK_EQ((t % 2), 0u) << "id: " << t;
|
|
return reinterpret_cast<ArtType*>(t);
|
|
}
|
|
}
|
|
|
|
ArtMethod* JniIdManager::DecodeMethodId(jmethodID method) {
|
|
return DecodeGenericId<ArtMethod>(reinterpret_cast<uintptr_t>(method));
|
|
}
|
|
|
|
ArtField* JniIdManager::DecodeFieldId(jfieldID field) {
|
|
return DecodeGenericId<ArtField>(reinterpret_cast<uintptr_t>(field));
|
|
}
|
|
|
|
ObjPtr<mirror::Object> JniIdManager::GetPointerMarker() {
|
|
return pointer_marker_.Read();
|
|
}
|
|
|
|
// This whole defer system is an annoying requirement to allow us to generate IDs during heap-walks
|
|
// such as those required for instrumentation tooling.
|
|
//
|
|
// The defer system works with the normal id-assignment routine to ensure that all the class-ext
|
|
// data structures are eventually created and filled in. Basically how it works is the id-assignment
|
|
// function will check to see if it has a strong mutator-lock. If it does not then it will try to
|
|
// allocate the class-ext data structures normally and fail if it is unable to do so. In the case
|
|
// where mutator-lock is being held exclusive no attempt to allocate will be made and the thread
|
|
// will CHECK that allocations are being deferred (or that the method is obsolete, in which case
|
|
// there is no class-ext to store the method->id map in).
|
|
//
|
|
// Once the thread is done holding the exclusive mutator-lock it will go back and fill-in the
|
|
// class-ext data of all the methods that were added. We do this without the exclusive mutator-lock
|
|
// on a copy of the maps before we decrement the deferred refcount. This ensures that any other
|
|
// threads running at the same time know they need to perform a linear scan of the id-map. Since we
|
|
// don't have the mutator-lock anymore other threads can allocate the class-ext data, meaning our
|
|
// copy is fine. The only way additional methods could end up on the id-maps after our copy without
|
|
// having class-ext data is if another thread picked up the exclusive mutator-lock and added another
|
|
// defer, in which case that thread would fix-up the remaining ids. In this way we maintain eventual
|
|
// consistency between the class-ext method/field->id maps and the JniIdManager id->method/field
|
|
// maps.
|
|
//
|
|
// TODO It is possible that another thread to gain the mutator-lock and allocate new ids without
|
|
// calling StartDefer. This is basically a race that we should try to catch but doing so is
|
|
// rather difficult and since this defer system is only used in very rare circumstances unlikely to
|
|
// be worth the trouble.
|
|
void JniIdManager::StartDefer() {
|
|
Thread* self = Thread::Current();
|
|
WriterMutexLock mu(self, *Locks::jni_id_lock_);
|
|
if (deferred_allocation_refcount_++ == 0) {
|
|
deferred_allocation_field_id_start_ = next_field_id_;
|
|
deferred_allocation_method_id_start_ = next_method_id_;
|
|
}
|
|
}
|
|
|
|
class JniIdDeferStackReflectiveScope : public BaseReflectiveHandleScope {
|
|
public:
|
|
JniIdDeferStackReflectiveScope() REQUIRES_SHARED(art::Locks::mutator_lock_)
|
|
: BaseReflectiveHandleScope(), methods_(), fields_() {
|
|
PushScope(Thread::Current());
|
|
}
|
|
|
|
void Initialize(const std::vector<ArtMethod*>& methods, const std::vector<ArtField*>& fields)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Roles::uninterruptible_) {
|
|
methods_ = methods;
|
|
fields_ = fields;
|
|
}
|
|
|
|
~JniIdDeferStackReflectiveScope() REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
PopScope();
|
|
}
|
|
|
|
void VisitTargets(ReflectiveValueVisitor* visitor) override
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
for (auto it = methods_.begin(); it != methods_.end(); ++it) {
|
|
if (*it == nullptr) {
|
|
continue;
|
|
}
|
|
*it = visitor->VisitMethod(*it, ReflectiveHandleScopeSourceInfo(this));
|
|
}
|
|
for (auto it = fields_.begin(); it != fields_.end(); ++it) {
|
|
if (*it == nullptr) {
|
|
continue;
|
|
}
|
|
*it = visitor->VisitField(*it, ReflectiveHandleScopeSourceInfo(this));
|
|
}
|
|
}
|
|
|
|
ArtField** GetFieldPtr(size_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
return &fields_[idx];
|
|
}
|
|
|
|
ArtMethod** GetMethodPtr(size_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
return &methods_[idx];
|
|
}
|
|
|
|
size_t NumFields() const {
|
|
return fields_.size();
|
|
}
|
|
size_t NumMethods() const {
|
|
return methods_.size();
|
|
}
|
|
|
|
private:
|
|
std::vector<ArtMethod*> methods_;
|
|
std::vector<ArtField*> fields_;
|
|
};
|
|
|
|
void JniIdManager::EndDefer() {
|
|
// Fixup the method->id map.
|
|
Thread* self = Thread::Current();
|
|
auto set_id = [&](auto** t, uintptr_t id) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
if (t == nullptr) {
|
|
return;
|
|
}
|
|
bool alloc_failure = EnsureIdsArray(self, (*t)->GetDeclaringClass(), *t);
|
|
ObjPtr<mirror::Class> klass((*t)->GetDeclaringClass());
|
|
size_t off = GetIdOffset(klass, (*t), kRuntimePointerSize);
|
|
ObjPtr<mirror::PointerArray> ids = GetIds(klass, (*t));
|
|
CHECK(!alloc_failure) << "Could not allocate jni ids array!";
|
|
if (ids.IsNull()) {
|
|
return;
|
|
}
|
|
if (kIsDebugBuild) {
|
|
uintptr_t old_id = ids->GetElementPtrSize<uintptr_t, kRuntimePointerSize>(off);
|
|
if (old_id != 0) {
|
|
DCHECK_EQ(old_id, id);
|
|
}
|
|
}
|
|
ids->SetElementPtrSize(off, reinterpret_cast<void*>(id), kRuntimePointerSize);
|
|
};
|
|
// To ensure eventual consistency this depends on the fact that the method_id_map_ and
|
|
// field_id_map_ are the ultimate source of truth and no id is ever reused to be valid. It also
|
|
// relies on all threads always getting calling StartDefer if they are going to be allocating jni
|
|
// ids while suspended. If a thread tries to do so while it doesn't have a scope we could miss
|
|
// ids.
|
|
// TODO We should use roles or something to verify that this requirement is not broken.
|
|
//
|
|
// If another thread comes along and adds more methods to the list after
|
|
// copying either (1) the id-maps are already present for the method and everything is fine, (2)
|
|
// the thread is not suspended and so can create the ext-data and id lists or, (3) the thread also
|
|
// suspended everything and incremented the deferred_allocation_refcount_ so it will fix up new
|
|
// ids when it finishes.
|
|
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
|
|
Locks::mutator_lock_->AssertSharedHeld(self);
|
|
JniIdDeferStackReflectiveScope jidsrs;
|
|
uintptr_t method_start_id;
|
|
uintptr_t field_start_id;
|
|
{
|
|
ReaderMutexLock mu(self, *Locks::jni_id_lock_);
|
|
ScopedAssertNoThreadSuspension sants(__FUNCTION__);
|
|
jidsrs.Initialize(method_id_map_, field_id_map_);
|
|
method_start_id = deferred_allocation_method_id_start_;
|
|
field_start_id = deferred_allocation_field_id_start_;
|
|
}
|
|
|
|
for (size_t index = kIsDebugBuild ? 0 : IdToIndex(method_start_id); index < jidsrs.NumMethods();
|
|
++index) {
|
|
set_id(jidsrs.GetMethodPtr(index), IndexToId(index));
|
|
}
|
|
for (size_t index = kIsDebugBuild ? 0 : IdToIndex(field_start_id); index < jidsrs.NumFields();
|
|
++index) {
|
|
set_id(jidsrs.GetFieldPtr(index), IndexToId(index));
|
|
}
|
|
WriterMutexLock mu(self, *Locks::jni_id_lock_);
|
|
DCHECK_GE(deferred_allocation_refcount_, 1u);
|
|
if (--deferred_allocation_refcount_ == 0) {
|
|
deferred_allocation_field_id_start_ = 0;
|
|
deferred_allocation_method_id_start_ = 0;
|
|
}
|
|
}
|
|
|
|
ScopedEnableSuspendAllJniIdQueries::ScopedEnableSuspendAllJniIdQueries()
|
|
: manager_(Runtime::Current()->GetJniIdManager()) {
|
|
manager_->StartDefer();
|
|
}
|
|
|
|
ScopedEnableSuspendAllJniIdQueries::~ScopedEnableSuspendAllJniIdQueries() {
|
|
manager_->EndDefer();
|
|
}
|
|
|
|
}; // namespace jni
|
|
}; // namespace art
|