Runtime flags only for fast/slow hiddenapi path
With more flags being supported in the dex file, stop copying all of
them into ArtField/ArtMethod access flags. Instead, store the
information needed to figure out whether to enter the slow path and
retrieve full access flags from dex or not.
At the moment, the only runtime flag is kAccPublicApi assigned to all
class members on the whitelist.
The CL also moves hardcoded API membership of intrinsics out of
ArtMethod and into hidden_api.h, and moves ArtMethod::SetIntrinsic
into the .cc file.
Test: m test-art
Change-Id: Ia1cc05060dbc22341768161dfd8697c6158e803a
diff --git a/runtime/art_field.h b/runtime/art_field.h
index dc7f985..1cf7afa 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -180,10 +180,6 @@
return (GetAccessFlags() & kAccVolatile) != 0;
}
- hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::DecodeFromRuntime(GetAccessFlags());
- }
-
// Returns an instance field with this offset in the given class or null if not found.
// If kExactOffset is true then we only find the matching offset, not the field containing the
// offset.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d8da912..f254116 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -367,160 +367,6 @@
return (GetAccessFlags() & kAccSingleImplementation) != 0;
}
-inline hiddenapi::ApiList ArtMethod::GetHiddenApiAccessFlags()
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (UNLIKELY(IsIntrinsic())) {
- switch (static_cast<Intrinsics>(GetIntrinsic())) {
- case Intrinsics::kSystemArrayCopyChar:
- case Intrinsics::kStringGetCharsNoCheck:
- case Intrinsics::kReferenceGetReferent:
- case Intrinsics::kMemoryPeekByte:
- case Intrinsics::kMemoryPokeByte:
- case Intrinsics::kUnsafeCASInt:
- case Intrinsics::kUnsafeCASLong:
- case Intrinsics::kUnsafeCASObject:
- case Intrinsics::kUnsafeGet:
- case Intrinsics::kUnsafeGetAndAddInt:
- case Intrinsics::kUnsafeGetAndAddLong:
- case Intrinsics::kUnsafeGetAndSetInt:
- case Intrinsics::kUnsafeGetAndSetLong:
- case Intrinsics::kUnsafeGetAndSetObject:
- case Intrinsics::kUnsafeGetLong:
- case Intrinsics::kUnsafeGetLongVolatile:
- case Intrinsics::kUnsafeGetObject:
- case Intrinsics::kUnsafeGetObjectVolatile:
- case Intrinsics::kUnsafeGetVolatile:
- case Intrinsics::kUnsafePut:
- case Intrinsics::kUnsafePutLong:
- case Intrinsics::kUnsafePutLongOrdered:
- case Intrinsics::kUnsafePutLongVolatile:
- case Intrinsics::kUnsafePutObject:
- case Intrinsics::kUnsafePutObjectOrdered:
- case Intrinsics::kUnsafePutObjectVolatile:
- case Intrinsics::kUnsafePutOrdered:
- case Intrinsics::kUnsafePutVolatile:
- case Intrinsics::kUnsafeLoadFence:
- case Intrinsics::kUnsafeStoreFence:
- case Intrinsics::kUnsafeFullFence:
- case Intrinsics::kCRC32Update:
- // These intrinsics are on the light greylist and will fail a DCHECK in
- // SetIntrinsic() if their flags change on the respective dex methods.
- // Note that the DCHECK currently won't fail if the dex methods are
- // whitelisted, e.g. in the core image (b/77733081). As a result, we
- // might print warnings but we won't change the semantics.
- return hiddenapi::ApiList::kLightGreylist;
- case Intrinsics::kStringNewStringFromBytes:
- case Intrinsics::kStringNewStringFromChars:
- case Intrinsics::kStringNewStringFromString:
- case Intrinsics::kMemoryPeekIntNative:
- case Intrinsics::kMemoryPeekLongNative:
- case Intrinsics::kMemoryPeekShortNative:
- case Intrinsics::kMemoryPokeIntNative:
- case Intrinsics::kMemoryPokeLongNative:
- case Intrinsics::kMemoryPokeShortNative:
- return hiddenapi::ApiList::kDarkGreylist;
- case Intrinsics::kVarHandleFullFence:
- case Intrinsics::kVarHandleAcquireFence:
- case Intrinsics::kVarHandleReleaseFence:
- case Intrinsics::kVarHandleLoadLoadFence:
- case Intrinsics::kVarHandleStoreStoreFence:
- case Intrinsics::kVarHandleCompareAndExchange:
- case Intrinsics::kVarHandleCompareAndExchangeAcquire:
- case Intrinsics::kVarHandleCompareAndExchangeRelease:
- case Intrinsics::kVarHandleCompareAndSet:
- case Intrinsics::kVarHandleGet:
- case Intrinsics::kVarHandleGetAcquire:
- case Intrinsics::kVarHandleGetAndAdd:
- case Intrinsics::kVarHandleGetAndAddAcquire:
- case Intrinsics::kVarHandleGetAndAddRelease:
- case Intrinsics::kVarHandleGetAndBitwiseAnd:
- case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
- case Intrinsics::kVarHandleGetAndBitwiseOr:
- case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
- case Intrinsics::kVarHandleGetAndBitwiseXor:
- case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
- case Intrinsics::kVarHandleGetAndSet:
- case Intrinsics::kVarHandleGetAndSetAcquire:
- case Intrinsics::kVarHandleGetAndSetRelease:
- case Intrinsics::kVarHandleGetOpaque:
- case Intrinsics::kVarHandleGetVolatile:
- case Intrinsics::kVarHandleSet:
- case Intrinsics::kVarHandleSetOpaque:
- case Intrinsics::kVarHandleSetRelease:
- case Intrinsics::kVarHandleSetVolatile:
- case Intrinsics::kVarHandleWeakCompareAndSet:
- case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
- case Intrinsics::kVarHandleWeakCompareAndSetPlain:
- case Intrinsics::kVarHandleWeakCompareAndSetRelease:
- // These intrinsics are on the blacklist and will fail a DCHECK in
- // SetIntrinsic() if their flags change on the respective dex methods.
- // Note that the DCHECK currently won't fail if the dex methods are
- // whitelisted, e.g. in the core image (b/77733081). Given that they are
- // exclusively VarHandle intrinsics, they should not be used outside
- // tests that do not enable hidden API checks.
- return hiddenapi::ApiList::kBlacklist;
- default:
- // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
- return hiddenapi::ApiList::kWhitelist;
- }
- } else {
- return hiddenapi::DecodeFromRuntime(GetAccessFlags());
- }
-}
-
-inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
- // Currently we only do intrinsics for static/final methods or methods of final
- // classes. We don't set kHasSingleImplementation for those methods.
- DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) <<
- "Potential conflict with kAccSingleImplementation";
- static const int kAccFlagsShift = CTZ(kAccIntrinsicBits);
- DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift);
- uint32_t intrinsic_bits = intrinsic << kAccFlagsShift;
- uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits;
- if (kIsDebugBuild) {
- uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask);
- bool is_constructor = IsConstructor();
- bool is_synchronized = IsSynchronized();
- bool skip_access_checks = SkipAccessChecks();
- bool is_fast_native = IsFastNative();
- bool is_critical_native = IsCriticalNative();
- bool is_copied = IsCopied();
- bool is_miranda = IsMiranda();
- bool is_default = IsDefault();
- bool is_default_conflict = IsDefaultConflicting();
- bool is_compilable = IsCompilable();
- bool must_count_locks = MustCountLocks();
- hiddenapi::ApiList hidden_api_flags = GetHiddenApiAccessFlags();
- SetAccessFlags(new_value);
- DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
- DCHECK_EQ(is_constructor, IsConstructor());
- DCHECK_EQ(is_synchronized, IsSynchronized());
- DCHECK_EQ(skip_access_checks, SkipAccessChecks());
- DCHECK_EQ(is_fast_native, IsFastNative());
- DCHECK_EQ(is_critical_native, IsCriticalNative());
- DCHECK_EQ(is_copied, IsCopied());
- DCHECK_EQ(is_miranda, IsMiranda());
- DCHECK_EQ(is_default, IsDefault());
- DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
- DCHECK_EQ(is_compilable, IsCompilable());
- DCHECK_EQ(must_count_locks, MustCountLocks());
- // Only DCHECK that we have preserved the hidden API access flags if the
- // original method was not on the whitelist. This is because the core image
- // does not have the access flags set (b/77733081). It is fine to hard-code
- // these because (a) warnings on greylist do not change semantics, and
- // (b) only VarHandle intrinsics are blacklisted at the moment and they
- // should not be used outside tests with disabled API checks.
- if (hidden_api_flags != hiddenapi::ApiList::kWhitelist) {
- DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags()) << PrettyMethod();
- }
- } else {
- SetAccessFlags(new_value);
- }
-}
-
template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType>
void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) {
if (LIKELY(!declaring_class_.IsNull())) {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 9bf31ed..abfdd55 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -33,6 +33,7 @@
#include "dex/dex_instruction.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
+#include "hidden_api.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
@@ -682,23 +683,72 @@
return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr;
}
+void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
+ // Currently we only do intrinsics for static/final methods or methods of final
+ // classes. We don't set kHasSingleImplementation for those methods.
+ DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) <<
+ "Potential conflict with kAccSingleImplementation";
+ static const int kAccFlagsShift = CTZ(kAccIntrinsicBits);
+ DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift);
+ uint32_t intrinsic_bits = intrinsic << kAccFlagsShift;
+ uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits;
+ if (kIsDebugBuild) {
+ uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask);
+ bool is_constructor = IsConstructor();
+ bool is_synchronized = IsSynchronized();
+ bool skip_access_checks = SkipAccessChecks();
+ bool is_fast_native = IsFastNative();
+ bool is_critical_native = IsCriticalNative();
+ bool is_copied = IsCopied();
+ bool is_miranda = IsMiranda();
+ bool is_default = IsDefault();
+ bool is_default_conflict = IsDefaultConflicting();
+ bool is_compilable = IsCompilable();
+ bool must_count_locks = MustCountLocks();
+ uint32_t hiddenapi_flags = hiddenapi::GetRuntimeFlags(this);
+ SetAccessFlags(new_value);
+ DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
+ DCHECK_EQ(is_constructor, IsConstructor());
+ DCHECK_EQ(is_synchronized, IsSynchronized());
+ DCHECK_EQ(skip_access_checks, SkipAccessChecks());
+ DCHECK_EQ(is_fast_native, IsFastNative());
+ DCHECK_EQ(is_critical_native, IsCriticalNative());
+ DCHECK_EQ(is_copied, IsCopied());
+ DCHECK_EQ(is_miranda, IsMiranda());
+ DCHECK_EQ(is_default, IsDefault());
+ DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
+ DCHECK_EQ(is_compilable, IsCompilable());
+ DCHECK_EQ(must_count_locks, MustCountLocks());
+ // Only DCHECK that we have preserved the hidden API access flags if the
+ // original method was not on the whitelist. This is because the core image
+ // does not have the access flags set (b/77733081). It is fine to hard-code
+ // these because (a) warnings on greylist do not change semantics, and
+ // (b) only VarHandle intrinsics are blacklisted at the moment and they
+ // should not be used outside tests with disabled API checks.
+ if ((hiddenapi_flags & kAccHiddenapiBits) == 0) {
+ DCHECK_EQ(hiddenapi_flags, hiddenapi::GetRuntimeFlags(this)) << PrettyMethod();
+ }
+ } else {
+ SetAccessFlags(new_value);
+ }
+}
+
void ArtMethod::SetNotIntrinsic() {
if (!IsIntrinsic()) {
return;
}
- // Query the hidden API access flags of the intrinsic.
- hiddenapi::ApiList intrinsic_api_list = GetHiddenApiAccessFlags();
+ // Read the existing hiddenapi flags.
+ uint32_t hiddenapi_runtime_flags = hiddenapi::GetRuntimeFlags(this);
// Clear intrinsic-related access flags.
ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
// Re-apply hidden API access flags now that the method is not an intrinsic.
- SetAccessFlags(hiddenapi::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list));
- DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list);
+ SetAccessFlags(GetAccessFlags() | hiddenapi_runtime_flags);
+ DCHECK_EQ(hiddenapi_runtime_flags, hiddenapi::GetRuntimeFlags(this));
}
-
void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) {
memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
Size(image_pointer_size));
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4e3ef53..5bbee92 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -343,8 +343,6 @@
AddAccessFlags(kAccMustCountLocks);
}
- hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 35379cc..ce7dfaf 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3468,14 +3468,8 @@
dst->SetDexFieldIndex(field_idx);
dst->SetDeclaringClass(klass.Get());
- // Get access flags from the DexFile. If this is a boot class path class,
- // also set its runtime hidden API access flags.
- uint32_t access_flags = field.GetAccessFlags();
- if (klass->IsBootStrapClassLoaded()) {
- access_flags = hiddenapi::EncodeForRuntime(
- access_flags, static_cast<hiddenapi::ApiList>(field.GetHiddenapiFlags()));
- }
- dst->SetAccessFlags(access_flags);
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
}
void ClassLinker::LoadMethod(const DexFile& dex_file,
@@ -3491,13 +3485,8 @@
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(method.GetCodeItemOffset());
- // Get access flags from the DexFile. If this is a boot class path class,
- // also set its runtime hidden API access flags.
- uint32_t access_flags = method.GetAccessFlags();
- if (klass->IsBootStrapClassLoaded()) {
- access_flags = hiddenapi::EncodeForRuntime(
- access_flags, static_cast<hiddenapi::ApiList>(method.GetHiddenapiFlags()));
- }
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 3b7b938..ab9e65c 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -18,8 +18,12 @@
#include <nativehelper/scoped_local_ref.h>
+#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "base/dumpable.h"
-#include "thread-current-inl.h"
+#include "dex/class_accessor-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
#include "well_known_classes.h"
#ifdef ART_TARGET_ANDROID
@@ -235,23 +239,82 @@
}
}
-static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
+static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtField*) {
return true;
}
-static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) {
+static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtMethod* method) {
return !method->IsIntrinsic();
}
template<typename T>
static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(hiddenapi::EncodeForRuntime(
- member->GetAccessFlags(), hiddenapi::ApiList::kWhitelist));
+ if (CanUpdateRuntimeFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(member->GetAccessFlags() | kAccPublicApi);
}
}
+static constexpr uint32_t kNoDexFlags = 0u;
+static constexpr uint32_t kInvalidDexFlags = static_cast<uint32_t>(-1);
+
+uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = field->GetDeclaringClass();
+ DCHECK(declaring_class != nullptr) << "Fields always have a declaring class";
+
+ const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
+ if (class_def == nullptr) {
+ return kNoDexFlags;
+ }
+
+ uint32_t flags = kInvalidDexFlags;
+ DCHECK(!AreValidFlags(flags));
+
+ ClassAccessor accessor(declaring_class->GetDexFile(),
+ *class_def,
+ /* parse_hiddenapi_class_data= */ true);
+ auto fn_visit = [&](const ClassAccessor::Field& dex_field) {
+ if (dex_field.GetIndex() == field->GetDexFieldIndex()) {
+ flags = dex_field.GetHiddenapiFlags();
+ }
+ };
+ accessor.VisitFields(fn_visit, fn_visit);
+
+ CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField();
+ DCHECK(AreValidFlags(flags));
+ return flags;
+}
+
+uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
+ if (declaring_class.IsNull()) {
+ DCHECK(method->IsRuntimeMethod());
+ return kNoDexFlags;
+ }
+
+ const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
+ if (class_def == nullptr) {
+ return kNoDexFlags;
+ }
+
+ uint32_t flags = kInvalidDexFlags;
+ DCHECK(!AreValidFlags(flags));
+
+ ClassAccessor accessor(declaring_class->GetDexFile(),
+ *class_def,
+ /* parse_hiddenapi_class_data= */ true);
+ auto fn_visit = [&](const ClassAccessor::Method& dex_method) {
+ if (dex_method.GetIndex() == method->GetDexMethodIndex()) {
+ flags = dex_method.GetHiddenapiFlags();
+ }
+ };
+ accessor.VisitMethods(fn_visit, fn_visit);
+
+ CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod();
+ DCHECK(AreValidFlags(flags));
+ return flags;
+}
+
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member,
hiddenapi::ApiList api_list,
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index ed00e2a..98414f7 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -17,10 +17,11 @@
#ifndef ART_RUNTIME_HIDDEN_API_H_
#define ART_RUNTIME_HIDDEN_API_H_
-#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_field.h"
+#include "art_method.h"
#include "base/mutex.h"
#include "dex/hidden_api_access_flags.h"
+#include "intrinsics_enum.h"
#include "mirror/class-inl.h"
#include "reflection.h"
#include "runtime.h"
@@ -164,12 +165,136 @@
void NotifyHiddenApiListener(AccessMethod access_method);
};
+// Locates hiddenapi flags for `field` in the corresponding dex file.
+// NB: This is an O(N) operation, linear with the number of members in the class def.
+uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Locates hiddenapi flags for `method` in the corresponding dex file.
+// NB: This is an O(N) operation, linear with the number of members in the class def.
+uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace detail
+// Returns access flags for the runtime representation of a class member (ArtField/ArtMember).
+ALWAYS_INLINE inline uint32_t CreateRuntimeFlags(const ClassAccessor::BaseItem& member) {
+ uint32_t runtime_flags = 0u;
+
+ uint32_t dex_flags = member.GetHiddenapiFlags();
+ DCHECK(AreValidFlags(dex_flags));
+
+ ApiList api_list = static_cast<hiddenapi::ApiList>(dex_flags);
+ if (api_list == ApiList::kWhitelist) {
+ runtime_flags |= kAccPublicApi;
+ }
+
+ DCHECK_EQ(runtime_flags & kAccHiddenapiBits, runtime_flags)
+ << "Runtime flags not in reserved access flags bits";
+ return runtime_flags;
+}
+
+// Extracts hiddenapi runtime flags from access flags of ArtField.
+ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtField* field)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return field->GetAccessFlags() & kAccHiddenapiBits;
+}
+
+// Extracts hiddenapi runtime flags from access flags of ArtMethod.
+// Uses hardcoded values for intrinsics.
+ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(method->IsIntrinsic())) {
+ switch (static_cast<Intrinsics>(method->GetIntrinsic())) {
+ case Intrinsics::kSystemArrayCopyChar:
+ case Intrinsics::kStringGetCharsNoCheck:
+ case Intrinsics::kReferenceGetReferent:
+ case Intrinsics::kMemoryPeekByte:
+ case Intrinsics::kMemoryPokeByte:
+ case Intrinsics::kUnsafeCASInt:
+ case Intrinsics::kUnsafeCASLong:
+ case Intrinsics::kUnsafeCASObject:
+ case Intrinsics::kUnsafeGet:
+ case Intrinsics::kUnsafeGetAndAddInt:
+ case Intrinsics::kUnsafeGetAndAddLong:
+ case Intrinsics::kUnsafeGetAndSetInt:
+ case Intrinsics::kUnsafeGetAndSetLong:
+ case Intrinsics::kUnsafeGetAndSetObject:
+ case Intrinsics::kUnsafeGetLong:
+ case Intrinsics::kUnsafeGetLongVolatile:
+ case Intrinsics::kUnsafeGetObject:
+ case Intrinsics::kUnsafeGetObjectVolatile:
+ case Intrinsics::kUnsafeGetVolatile:
+ case Intrinsics::kUnsafePut:
+ case Intrinsics::kUnsafePutLong:
+ case Intrinsics::kUnsafePutLongOrdered:
+ case Intrinsics::kUnsafePutLongVolatile:
+ case Intrinsics::kUnsafePutObject:
+ case Intrinsics::kUnsafePutObjectOrdered:
+ case Intrinsics::kUnsafePutObjectVolatile:
+ case Intrinsics::kUnsafePutOrdered:
+ case Intrinsics::kUnsafePutVolatile:
+ case Intrinsics::kUnsafeLoadFence:
+ case Intrinsics::kUnsafeStoreFence:
+ case Intrinsics::kUnsafeFullFence:
+ case Intrinsics::kCRC32Update:
+ case Intrinsics::kStringNewStringFromBytes:
+ case Intrinsics::kStringNewStringFromChars:
+ case Intrinsics::kStringNewStringFromString:
+ case Intrinsics::kMemoryPeekIntNative:
+ case Intrinsics::kMemoryPeekLongNative:
+ case Intrinsics::kMemoryPeekShortNative:
+ case Intrinsics::kMemoryPokeIntNative:
+ case Intrinsics::kMemoryPokeLongNative:
+ case Intrinsics::kMemoryPokeShortNative:
+ case Intrinsics::kVarHandleFullFence:
+ case Intrinsics::kVarHandleAcquireFence:
+ case Intrinsics::kVarHandleReleaseFence:
+ case Intrinsics::kVarHandleLoadLoadFence:
+ case Intrinsics::kVarHandleStoreStoreFence:
+ case Intrinsics::kVarHandleCompareAndExchange:
+ case Intrinsics::kVarHandleCompareAndExchangeAcquire:
+ case Intrinsics::kVarHandleCompareAndExchangeRelease:
+ case Intrinsics::kVarHandleCompareAndSet:
+ case Intrinsics::kVarHandleGet:
+ case Intrinsics::kVarHandleGetAcquire:
+ case Intrinsics::kVarHandleGetAndAdd:
+ case Intrinsics::kVarHandleGetAndAddAcquire:
+ case Intrinsics::kVarHandleGetAndAddRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseAnd:
+ case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseOr:
+ case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseXor:
+ case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
+ case Intrinsics::kVarHandleGetAndSet:
+ case Intrinsics::kVarHandleGetAndSetAcquire:
+ case Intrinsics::kVarHandleGetAndSetRelease:
+ case Intrinsics::kVarHandleGetOpaque:
+ case Intrinsics::kVarHandleGetVolatile:
+ case Intrinsics::kVarHandleSet:
+ case Intrinsics::kVarHandleSetOpaque:
+ case Intrinsics::kVarHandleSetRelease:
+ case Intrinsics::kVarHandleSetVolatile:
+ case Intrinsics::kVarHandleWeakCompareAndSet:
+ case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
+ case Intrinsics::kVarHandleWeakCompareAndSetPlain:
+ case Intrinsics::kVarHandleWeakCompareAndSetRelease:
+ return 0u;
+ default:
+ // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
+ return kAccPublicApi;
+ }
+ } else {
+ return method->GetAccessFlags() & kAccHiddenapiBits;
+ }
+}
+
// Returns true if access to `member` should be denied in the given context.
// The decision is based on whether the caller is in a trusted context or not.
// Because determining the access context can be expensive, a lambda function
@@ -183,16 +308,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
- // Decode hidden API access flags.
- // NB Multiple threads might try to access (and overwrite) these simultaneously,
- // causing a race. We only do that if access has not been denied, so the race
- // cannot change Java semantics. We should, however, decode the access flags
- // once and use it throughout this function, otherwise we may get inconsistent
- // results, e.g. print whitelist warnings (b/78327881).
- ApiList api_list = member->GetHiddenApiAccessFlags();
-
- // Exit early if member is on the whitelist.
- if (api_list == ApiList::kWhitelist) {
+ // Exit early if member is public API. This flag is also set for non-boot class
+ // path fields/methods.
+ if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) {
return false;
}
@@ -202,6 +320,11 @@
return false;
}
+ // Decode hidden API access flags from the dex file.
+ // This is an O(N) operation scaling with the number of fields/methods
+ // in the class. Only do this on slow path and only do it once.
+ ApiList api_list = static_cast<hiddenapi::ApiList>(detail::GetDexFlags(member));
+
// Member is hidden and caller is not exempted. Enter slow path.
return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index c38cc86..4e551ad 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -211,12 +211,12 @@
}
ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+ return (GetAccessFlags() & kAccSkipHiddenapiChecks) != 0;
}
ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t flags = GetAccessFlags();
- SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+ SetAccessFlags(flags | kAccSkipHiddenapiChecks);
}
ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {