| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_ |
| #define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_ |
| |
| #include "local_reference_table.h" |
| |
| #include "android-base/stringprintf.h" |
| |
| #include "base/casts.h" |
| #include "gc_root-inl.h" |
| #include "obj_ptr-inl.h" |
| #include "mirror/object_reference.h" |
| #include "verify_object.h" |
| |
| namespace art { |
| namespace jni { |
| |
| inline void LrtEntry::SetReference(ObjPtr<mirror::Object> ref) { |
| root_ = GcRoot<mirror::Object>( |
| mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref.Ptr())); |
| DCHECK(!IsFree()); |
| DCHECK(!IsSerialNumber()); |
| } |
| |
| inline ObjPtr<mirror::Object> LrtEntry::GetReference() { |
| DCHECK(!IsFree()); |
| DCHECK(!IsSerialNumber()); |
| DCHECK(!IsNull()); |
| // Local references do not need read barriers. They are marked during the thread root flip. |
| return root_.Read<kWithoutReadBarrier>(); |
| } |
| |
| inline void LrtEntry::SetNextFree(uint32_t next_free) { |
| SetVRegValue(NextFreeField::Update(next_free, 1u << kFlagFree)); |
| DCHECK(IsFree()); |
| DCHECK(!IsSerialNumber()); |
| } |
| |
| inline void LrtEntry::SetSerialNumber(uint32_t serial_number) { |
| SetVRegValue(SerialNumberField::Update(serial_number, 1u << kFlagSerialNumber)); |
| DCHECK(!IsFree()); |
| DCHECK(IsSerialNumber()); |
| } |
| |
| inline void LrtEntry::SetVRegValue(uint32_t value) { |
| root_ = GcRoot<mirror::Object>( |
| mirror::CompressedReference<mirror::Object>::FromVRegValue(value)); |
| } |
| |
| inline uint32_t LocalReferenceTable::GetReferenceEntryIndex(IndirectRef iref) const { |
| DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(iref), kLocal); |
| LrtEntry* entry = ToLrtEntry(iref); |
| |
| if (LIKELY(small_table_ != nullptr)) { |
| DCHECK(tables_.empty()); |
| if (!std::less<const LrtEntry*>()(entry, small_table_) && |
| std::less<const LrtEntry*>()(entry, small_table_ + kSmallLrtEntries)) { |
| return dchecked_integral_cast<uint32_t>(entry - small_table_); |
| } |
| } else { |
| for (size_t i = 0, size = tables_.size(); i != size; ++i) { |
| LrtEntry* table = tables_[i]; |
| size_t table_size = GetTableSize(i); |
| if (!std::less<const LrtEntry*>()(entry, table) && |
| std::less<const LrtEntry*>()(entry, table + table_size)) { |
| return dchecked_integral_cast<size_t>(i != 0u ? table_size : 0u) + |
| dchecked_integral_cast<size_t>(entry - table); |
| } |
| } |
| } |
| return std::numeric_limits<uint32_t>::max(); |
| } |
| |
| inline bool LocalReferenceTable::IsValidReference(IndirectRef iref, |
| /*out*/std::string* error_msg) const { |
| uint32_t entry_index = GetReferenceEntryIndex(iref); |
| if (UNLIKELY(entry_index == std::numeric_limits<uint32_t>::max())) { |
| *error_msg = android::base::StringPrintf("reference outside the table: %p", iref); |
| return false; |
| } |
| if (UNLIKELY(entry_index >= segment_state_.top_index)) { |
| *error_msg = android::base::StringPrintf("popped reference at index %u in a table of size %u", |
| entry_index, |
| segment_state_.top_index); |
| return false; |
| } |
| LrtEntry* entry = ToLrtEntry(iref); |
| LrtEntry* serial_number_entry = GetCheckJniSerialNumberEntry(entry); |
| if (serial_number_entry->IsSerialNumber()) { |
| // This reference was created with CheckJNI enabled. |
| uint32_t expected_serial_number = serial_number_entry->GetSerialNumber(); |
| uint32_t serial_number = entry - serial_number_entry; |
| DCHECK_LT(serial_number, kCheckJniEntriesPerReference); |
| if (serial_number != expected_serial_number || serial_number == 0u) { |
| *error_msg = android::base::StringPrintf( |
| "reference at index %u with bad serial number %u v. %u (valid 1 - %u)", |
| entry_index, |
| serial_number, |
| expected_serial_number, |
| dchecked_integral_cast<uint32_t>(kCheckJniEntriesPerReference - 1u)); |
| return false; |
| } |
| } |
| if (UNLIKELY(entry->IsFree())) { |
| *error_msg = android::base::StringPrintf("deleted reference at index %u", entry_index); |
| return false; |
| } |
| if (UNLIKELY(entry->IsNull())) { |
| // This should never really happen and may indicate memory coruption. |
| *error_msg = android::base::StringPrintf("null reference at index %u", entry_index); |
| return false; |
| } |
| return true; |
| } |
| |
| inline void LocalReferenceTable::DCheckValidReference(IndirectRef iref) const { |
| // If CheckJNI is performing the checks, we should not reach this point with an invalid |
| // reference with the exception of gtests that intercept the CheckJNI abort and proceed |
| // to decode the reference anyway and we do not want to abort again in this case. |
| if (kIsDebugBuild && !IsCheckJniEnabled()) { |
| std::string error_msg; |
| CHECK(IsValidReference(iref, &error_msg)) << error_msg; |
| } |
| } |
| |
| inline ObjPtr<mirror::Object> LocalReferenceTable::Get(IndirectRef iref) const { |
| DCheckValidReference(iref); |
| return ToLrtEntry(iref)->GetReference(); |
| } |
| |
| inline void LocalReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) { |
| DCheckValidReference(iref); |
| ToLrtEntry(iref)->SetReference(obj); |
| } |
| |
| } // namespace jni |
| } // namespace art |
| |
| #endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_ |