| /* |
| * Copyright (C) 2017 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 "var_handle.h" |
| |
| #include "array-inl.h" |
| #include "art_field-inl.h" |
| #include "class-inl.h" |
| #include "class_linker.h" |
| #include "class_root.h" |
| #include "intrinsics_enum.h" |
| #include "jni/jni_internal.h" |
| #include "jvalue-inl.h" |
| #include "method_handles-inl.h" |
| #include "method_type.h" |
| #include "obj_ptr-inl.h" |
| #include "well_known_classes.h" |
| |
| namespace art { |
| namespace mirror { |
| |
| static constexpr bool kTransactionActive = true; |
| static constexpr bool kTransactionInactive = !kTransactionActive; |
| |
| namespace { |
| |
| struct VarHandleAccessorToAccessModeEntry { |
| const char* method_name; |
| VarHandle::AccessMode access_mode; |
| |
| // Binary predicate function for finding access_mode by |
| // method_name. The access_mode field is ignored. |
| static bool CompareName(const VarHandleAccessorToAccessModeEntry& lhs, |
| const VarHandleAccessorToAccessModeEntry& rhs) { |
| return strcmp(lhs.method_name, rhs.method_name) < 0; |
| } |
| }; |
| |
| // Map of VarHandle accessor method names to access mode values. The list is alpha-sorted to support |
| // binary search. For the usage scenario - lookups in the verifier - a linear scan would likely |
| // suffice since we expect VarHandles to be a lesser encountered class. We could use a std::hashmap |
| // here and this would be easier to maintain if new values are added here. However, this entails |
| // CPU cycles initializing the structure on every execution and uses O(N) more memory for |
| // intermediate nodes and makes that memory dirty. Compile-time magic using constexpr is possible |
| // here, but that's a tax when this code is recompiled. |
| const VarHandleAccessorToAccessModeEntry kAccessorToAccessMode[VarHandle::kNumberOfAccessModes] = { |
| { "compareAndExchange", VarHandle::AccessMode::kCompareAndExchange }, |
| { "compareAndExchangeAcquire", VarHandle::AccessMode::kCompareAndExchangeAcquire }, |
| { "compareAndExchangeRelease", VarHandle::AccessMode::kCompareAndExchangeRelease }, |
| { "compareAndSet", VarHandle::AccessMode::kCompareAndSet }, |
| { "get", VarHandle::AccessMode::kGet }, |
| { "getAcquire", VarHandle::AccessMode::kGetAcquire }, |
| { "getAndAdd", VarHandle::AccessMode::kGetAndAdd }, |
| { "getAndAddAcquire", VarHandle::AccessMode::kGetAndAddAcquire }, |
| { "getAndAddRelease", VarHandle::AccessMode::kGetAndAddRelease }, |
| { "getAndBitwiseAnd", VarHandle::AccessMode::kGetAndBitwiseAnd }, |
| { "getAndBitwiseAndAcquire", VarHandle::AccessMode::kGetAndBitwiseAndAcquire }, |
| { "getAndBitwiseAndRelease", VarHandle::AccessMode::kGetAndBitwiseAndRelease }, |
| { "getAndBitwiseOr", VarHandle::AccessMode::kGetAndBitwiseOr }, |
| { "getAndBitwiseOrAcquire", VarHandle::AccessMode::kGetAndBitwiseOrAcquire }, |
| { "getAndBitwiseOrRelease", VarHandle::AccessMode::kGetAndBitwiseOrRelease }, |
| { "getAndBitwiseXor", VarHandle::AccessMode::kGetAndBitwiseXor }, |
| { "getAndBitwiseXorAcquire", VarHandle::AccessMode::kGetAndBitwiseXorAcquire }, |
| { "getAndBitwiseXorRelease", VarHandle::AccessMode::kGetAndBitwiseXorRelease }, |
| { "getAndSet", VarHandle::AccessMode::kGetAndSet }, |
| { "getAndSetAcquire", VarHandle::AccessMode::kGetAndSetAcquire }, |
| { "getAndSetRelease", VarHandle::AccessMode::kGetAndSetRelease }, |
| { "getOpaque", VarHandle::AccessMode::kGetOpaque }, |
| { "getVolatile", VarHandle::AccessMode::kGetVolatile }, |
| { "set", VarHandle::AccessMode::kSet }, |
| { "setOpaque", VarHandle::AccessMode::kSetOpaque }, |
| { "setRelease", VarHandle::AccessMode::kSetRelease }, |
| { "setVolatile", VarHandle::AccessMode::kSetVolatile }, |
| { "weakCompareAndSet", VarHandle::AccessMode::kWeakCompareAndSet }, |
| { "weakCompareAndSetAcquire", VarHandle::AccessMode::kWeakCompareAndSetAcquire }, |
| { "weakCompareAndSetPlain", VarHandle::AccessMode::kWeakCompareAndSetPlain }, |
| { "weakCompareAndSetRelease", VarHandle::AccessMode::kWeakCompareAndSetRelease }, |
| }; |
| |
| // Enumeration for describing the parameter and return types of an AccessMode. |
| enum class AccessModeTemplate : uint32_t { |
| kGet, // T Op(C0..CN) |
| kSet, // void Op(C0..CN, T) |
| kCompareAndSet, // boolean Op(C0..CN, T, T) |
| kCompareAndExchange, // T Op(C0..CN, T, T) |
| kGetAndUpdate, // T Op(C0..CN, T) |
| }; |
| |
| // Look up the AccessModeTemplate for a given VarHandle |
| // AccessMode. This simplifies finding the correct signature for a |
| // VarHandle accessor method. |
| AccessModeTemplate GetAccessModeTemplate(VarHandle::AccessMode access_mode) { |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: |
| return AccessModeTemplate::kGet; |
| case VarHandle::AccessMode::kSet: |
| return AccessModeTemplate::kSet; |
| case VarHandle::AccessMode::kGetVolatile: |
| return AccessModeTemplate::kGet; |
| case VarHandle::AccessMode::kSetVolatile: |
| return AccessModeTemplate::kSet; |
| case VarHandle::AccessMode::kGetAcquire: |
| return AccessModeTemplate::kGet; |
| case VarHandle::AccessMode::kSetRelease: |
| return AccessModeTemplate::kSet; |
| case VarHandle::AccessMode::kGetOpaque: |
| return AccessModeTemplate::kGet; |
| case VarHandle::AccessMode::kSetOpaque: |
| return AccessModeTemplate::kSet; |
| case VarHandle::AccessMode::kCompareAndSet: |
| return AccessModeTemplate::kCompareAndSet; |
| case VarHandle::AccessMode::kCompareAndExchange: |
| return AccessModeTemplate::kCompareAndExchange; |
| case VarHandle::AccessMode::kCompareAndExchangeAcquire: |
| return AccessModeTemplate::kCompareAndExchange; |
| case VarHandle::AccessMode::kCompareAndExchangeRelease: |
| return AccessModeTemplate::kCompareAndExchange; |
| case VarHandle::AccessMode::kWeakCompareAndSetPlain: |
| return AccessModeTemplate::kCompareAndSet; |
| case VarHandle::AccessMode::kWeakCompareAndSet: |
| return AccessModeTemplate::kCompareAndSet; |
| case VarHandle::AccessMode::kWeakCompareAndSetAcquire: |
| return AccessModeTemplate::kCompareAndSet; |
| case VarHandle::AccessMode::kWeakCompareAndSetRelease: |
| return AccessModeTemplate::kCompareAndSet; |
| case VarHandle::AccessMode::kGetAndSet: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndSetAcquire: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndSetRelease: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndAdd: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndAddAcquire: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndAddRelease: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseOr: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseOrRelease: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseAnd: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseAndRelease: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseXor: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseXorRelease: |
| return AccessModeTemplate::kGetAndUpdate; |
| case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: |
| return AccessModeTemplate::kGetAndUpdate; |
| } |
| } |
| |
| int32_t GetNumberOfVarTypeParameters(AccessModeTemplate access_mode_template) { |
| switch (access_mode_template) { |
| case AccessModeTemplate::kGet: |
| return 0; |
| case AccessModeTemplate::kSet: |
| case AccessModeTemplate::kGetAndUpdate: |
| return 1; |
| case AccessModeTemplate::kCompareAndSet: |
| case AccessModeTemplate::kCompareAndExchange: |
| return 2; |
| } |
| UNREACHABLE(); |
| } |
| |
| // Returns the number of parameters associated with an |
| // AccessModeTemplate and the supplied coordinate types. |
| int32_t GetNumberOfParameters(AccessModeTemplate access_mode_template, |
| ObjPtr<Class> coordinateType0, |
| ObjPtr<Class> coordinateType1) { |
| int32_t count = 0; |
| if (!coordinateType0.IsNull()) { |
| count++; |
| if (!coordinateType1.IsNull()) { |
| count++; |
| } |
| } |
| return count + GetNumberOfVarTypeParameters(access_mode_template); |
| } |
| |
| void ThrowNullPointerExceptionForCoordinate() REQUIRES_SHARED(Locks::mutator_lock_) { |
| ThrowNullPointerException("Attempt to access memory on a null object"); |
| } |
| |
| bool CheckElementIndex(Primitive::Type type, |
| int32_t relative_index, |
| int32_t start, |
| int32_t limit) REQUIRES_SHARED(Locks::mutator_lock_) { |
| int64_t index = start + relative_index; |
| int64_t max_index = limit - Primitive::ComponentSize(type); |
| if (index < start || index > max_index) { |
| ThrowIndexOutOfBoundsException(index, limit - start); |
| return false; |
| } |
| return true; |
| } |
| |
| bool CheckElementIndex(Primitive::Type type, int32_t index, int32_t range_limit) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| return CheckElementIndex(type, index, 0, range_limit); |
| } |
| |
| // Returns true if access_mode only entails a memory read. False if |
| // access_mode may write to memory. |
| bool IsReadOnlyAccessMode(VarHandle::AccessMode access_mode) { |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| return access_mode_template == AccessModeTemplate::kGet; |
| } |
| |
| // Writes the parameter types associated with the AccessModeTemplate |
| // into an array. The parameter types are derived from the specified |
| // variable type and coordinate types. Returns the number of |
| // parameters written. |
| int32_t BuildParameterArray(ObjPtr<Class> (¶meters)[VarHandle::kMaxAccessorParameters], |
| AccessModeTemplate access_mode_template, |
| ObjPtr<Class> varType, |
| ObjPtr<Class> coordinateType0, |
| ObjPtr<Class> coordinateType1) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| DCHECK(varType != nullptr); |
| int32_t index = 0; |
| if (!coordinateType0.IsNull()) { |
| parameters[index++] = coordinateType0; |
| if (!coordinateType1.IsNull()) { |
| parameters[index++] = coordinateType1; |
| } |
| } else { |
| DCHECK(coordinateType1.IsNull()); |
| } |
| |
| switch (access_mode_template) { |
| case AccessModeTemplate::kCompareAndExchange: |
| case AccessModeTemplate::kCompareAndSet: |
| parameters[index++] = varType; |
| parameters[index++] = varType; |
| return index; |
| case AccessModeTemplate::kGet: |
| return index; |
| case AccessModeTemplate::kGetAndUpdate: |
| case AccessModeTemplate::kSet: |
| parameters[index++] = varType; |
| return index; |
| } |
| return -1; |
| } |
| |
| // Returns the return type associated with an AccessModeTemplate based |
| // on the template and the variable type specified. |
| static ObjPtr<Class> GetReturnType(AccessModeTemplate access_mode_template, ObjPtr<Class> varType) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| DCHECK(varType != nullptr); |
| switch (access_mode_template) { |
| case AccessModeTemplate::kCompareAndSet: |
| return GetClassRoot(ClassRoot::kPrimitiveBoolean); |
| case AccessModeTemplate::kCompareAndExchange: |
| case AccessModeTemplate::kGet: |
| case AccessModeTemplate::kGetAndUpdate: |
| return varType; |
| case AccessModeTemplate::kSet: |
| return GetClassRoot(ClassRoot::kPrimitiveVoid); |
| } |
| return nullptr; |
| } |
| |
| // Method to insert a read barrier for accessors to reference fields. |
| inline void ReadBarrierForVarHandleAccess(ObjPtr<Object> obj, MemberOffset field_offset) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (kUseReadBarrier) { |
| // We need to ensure that the reference stored in the field is a to-space one before attempting |
| // the CompareAndSet/CompareAndExchange/Exchange operation otherwise it will fail incorrectly |
| // if obj is in the process of being moved. |
| uint8_t* raw_field_addr = reinterpret_cast<uint8_t*>(obj.Ptr()) + field_offset.SizeValue(); |
| auto field_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr); |
| // Note that the read barrier load does NOT need to be volatile. |
| static constexpr bool kIsVolatile = false; |
| static constexpr bool kAlwaysUpdateField = true; |
| ReadBarrier::Barrier<mirror::Object, kIsVolatile, kWithReadBarrier, kAlwaysUpdateField>( |
| obj.Ptr(), |
| MemberOffset(field_offset), |
| field_addr); |
| } |
| } |
| |
| inline MemberOffset GetMemberOffset(jfieldID field_id) REQUIRES_SHARED(Locks::mutator_lock_) { |
| ArtField* const field = jni::DecodeArtField(field_id); |
| return field->GetOffset(); |
| } |
| |
| // |
| // Helper methods for storing results from atomic operations into |
| // JValue instances. |
| // |
| |
| inline void StoreResult(uint8_t value, JValue* result) { |
| result->SetZ(value); |
| } |
| |
| inline void StoreResult(int8_t value, JValue* result) { |
| result->SetB(value); |
| } |
| |
| inline void StoreResult(uint16_t value, JValue* result) { |
| result->SetC(value); |
| } |
| |
| inline void StoreResult(int16_t value, JValue* result) { |
| result->SetS(value); |
| } |
| |
| inline void StoreResult(int32_t value, JValue* result) { |
| result->SetI(value); |
| } |
| |
| inline void StoreResult(int64_t value, JValue* result) { |
| result->SetJ(value); |
| } |
| |
| inline void StoreResult(float value, JValue* result) { |
| result->SetF(value); |
| } |
| |
| inline void StoreResult(double value, JValue* result) { |
| result->SetD(value); |
| } |
| |
| inline void StoreResult(ObjPtr<Object> value, JValue* result) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| result->SetL(value); |
| } |
| |
| // |
| // Helper class for byte-swapping value that has been stored in a JValue. |
| // |
| |
| template <typename T> |
| class JValueByteSwapper final { |
| public: |
| static void ByteSwap(JValue* value); |
| static void MaybeByteSwap(bool byte_swap, JValue* value) { |
| if (byte_swap) { |
| ByteSwap(value); |
| } |
| } |
| }; |
| |
| template <> |
| void JValueByteSwapper<uint16_t>::ByteSwap(JValue* value) { |
| value->SetC(BSWAP(value->GetC())); |
| } |
| |
| template <> |
| void JValueByteSwapper<int16_t>::ByteSwap(JValue* value) { |
| value->SetS(BSWAP(value->GetS())); |
| } |
| |
| template <> |
| void JValueByteSwapper<int32_t>::ByteSwap(JValue* value) { |
| value->SetI(BSWAP(value->GetI())); |
| } |
| |
| template <> |
| void JValueByteSwapper<int64_t>::ByteSwap(JValue* value) { |
| value->SetJ(BSWAP(value->GetJ())); |
| } |
| |
| // |
| // Accessor implementations, shared across all VarHandle types. |
| // |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAccessor : public Object::Accessor<T> { |
| public: |
| explicit AtomicGetAccessor(JValue* result) : result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| StoreResult(atom->load(MO), result_); |
| } |
| |
| private: |
| JValue* result_; |
| }; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicSetAccessor : public Object::Accessor<T> { |
| public: |
| explicit AtomicSetAccessor(T new_value) : new_value_(new_value) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| atom->store(new_value_, MO); |
| } |
| |
| private: |
| T new_value_; |
| }; |
| |
| template <typename T> using GetAccessor = AtomicGetAccessor<T, std::memory_order_relaxed>; |
| |
| template <typename T> using SetAccessor = AtomicSetAccessor<T, std::memory_order_relaxed>; |
| |
| template <typename T> |
| using GetVolatileAccessor = AtomicGetAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T> |
| using SetVolatileAccessor = AtomicSetAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MOS, std::memory_order MOF> |
| class AtomicStrongCompareAndSetAccessor : public Object::Accessor<T> { |
| public: |
| AtomicStrongCompareAndSetAccessor(T expected_value, T desired_value, JValue* result) |
| : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| bool success = atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF); |
| StoreResult(success ? JNI_TRUE : JNI_FALSE, result_); |
| } |
| |
| private: |
| T expected_value_; |
| T desired_value_; |
| JValue* result_; |
| }; |
| |
| template<typename T> |
| using CompareAndSetAccessor = |
| AtomicStrongCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MOS, std::memory_order MOF> |
| class AtomicStrongCompareAndExchangeAccessor : public Object::Accessor<T> { |
| public: |
| AtomicStrongCompareAndExchangeAccessor(T expected_value, T desired_value, JValue* result) |
| : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF); |
| StoreResult(expected_value_, result_); |
| } |
| |
| private: |
| T expected_value_; |
| T desired_value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using CompareAndExchangeAccessor = |
| AtomicStrongCompareAndExchangeAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MOS, std::memory_order MOF> |
| class AtomicWeakCompareAndSetAccessor : public Object::Accessor<T> { |
| public: |
| AtomicWeakCompareAndSetAccessor(T expected_value, T desired_value, JValue* result) |
| : expected_value_(expected_value), desired_value_(desired_value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| bool success = atom->compare_exchange_weak(expected_value_, desired_value_, MOS, MOF); |
| StoreResult(success ? JNI_TRUE : JNI_FALSE, result_); |
| } |
| |
| private: |
| T expected_value_; |
| T desired_value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using WeakCompareAndSetAccessor = |
| AtomicWeakCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndSetAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndSetAccessor(T new_value, JValue* result) : new_value_(new_value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T old_value = atom->exchange(new_value_, MO); |
| StoreResult(old_value, result_); |
| } |
| |
| private: |
| T new_value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndSetAccessor = AtomicGetAndSetAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T, bool kIsFloat, std::memory_order MO> |
| class AtomicGetAndAddOperator { |
| public: |
| static T Apply(T* addr, T addend) { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| return atom->fetch_add(addend, MO); |
| } |
| }; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndAddOperator<T, /* kIsFloat */ true, MO> { |
| public: |
| static T Apply(T* addr, T addend) { |
| // c++11 does not have std::atomic<T>::fetch_and_add for floating |
| // point types, so we effect one with a compare and swap. |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T old_value = atom->load(std::memory_order_relaxed); |
| T new_value; |
| do { |
| new_value = old_value + addend; |
| } while (!atom->compare_exchange_weak(old_value, new_value, MO, std::memory_order_relaxed)); |
| return old_value; |
| } |
| }; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndAddAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndAddAccessor(T addend, JValue* result) : addend_(addend), result_(result) {} |
| |
| void Access(T* addr) override { |
| constexpr bool kIsFloatingPoint = std::is_floating_point<T>::value; |
| T old_value = AtomicGetAndAddOperator<T, kIsFloatingPoint, MO>::Apply(addr, addend_); |
| StoreResult(old_value, result_); |
| } |
| |
| private: |
| T addend_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndAddAccessor = AtomicGetAndAddAccessor<T, std::memory_order_seq_cst>; |
| |
| // Accessor specifically for memory views where the caller can specify |
| // the byte-ordering. Addition only works outside of the byte-swapped |
| // memory view because of the direction of carries. |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndAddWithByteSwapAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndAddWithByteSwapAccessor(T value, JValue* result) : value_(value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* const atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T current_value = atom->load(std::memory_order_relaxed); |
| T sum; |
| do { |
| sum = BSWAP(current_value) + value_; |
| // NB current_value is a pass-by-reference argument in the call to |
| // atomic<T>::compare_exchange_weak(). |
| } while (!atom->compare_exchange_weak(current_value, |
| BSWAP(sum), |
| MO, |
| std::memory_order_relaxed)); |
| StoreResult(BSWAP(current_value), result_); |
| } |
| |
| private: |
| T value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndAddWithByteSwapAccessor = |
| AtomicGetAndAddWithByteSwapAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndBitwiseOrAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndBitwiseOrAccessor(T value, JValue* result) : value_(value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T old_value = atom->fetch_or(value_, MO); |
| StoreResult(old_value, result_); |
| } |
| |
| private: |
| T value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndBitwiseOrAccessor = AtomicGetAndBitwiseOrAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndBitwiseAndAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndBitwiseAndAccessor(T value, JValue* result) : value_(value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T old_value = atom->fetch_and(value_, MO); |
| StoreResult(old_value, result_); |
| } |
| |
| private: |
| T value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndBitwiseAndAccessor = |
| AtomicGetAndBitwiseAndAccessor<T, std::memory_order_seq_cst>; |
| |
| template <typename T, std::memory_order MO> |
| class AtomicGetAndBitwiseXorAccessor : public Object::Accessor<T> { |
| public: |
| AtomicGetAndBitwiseXorAccessor(T value, JValue* result) : value_(value), result_(result) {} |
| |
| void Access(T* addr) override { |
| std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr); |
| T old_value = atom->fetch_xor(value_, MO); |
| StoreResult(old_value, result_); |
| } |
| |
| private: |
| T value_; |
| JValue* result_; |
| }; |
| |
| template <typename T> |
| using GetAndBitwiseXorAccessor = AtomicGetAndBitwiseXorAccessor<T, std::memory_order_seq_cst>; |
| |
| // |
| // Unreachable access modes. |
| // |
| |
| NO_RETURN void UnreachableAccessMode(const char* access_mode, const char* type_name) { |
| LOG(FATAL) << "Unreachable access mode :" << access_mode << " for type " << type_name; |
| UNREACHABLE(); |
| } |
| |
| #define UNREACHABLE_ACCESS_MODE(ACCESS_MODE, TYPE) \ |
| template<> void ACCESS_MODE ## Accessor<TYPE>::Access(TYPE*) { \ |
| UnreachableAccessMode(#ACCESS_MODE, #TYPE); \ |
| } |
| |
| // The boolean primitive type is not numeric (boolean == std::uint8_t). |
| UNREACHABLE_ACCESS_MODE(GetAndAdd, uint8_t) |
| |
| // The floating point types do not support bitwise operations. |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, float) |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, float) |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, float) |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, double) |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, double) |
| UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, double) |
| |
| // A helper class for object field accesses for floats and |
| // doubles. The object interface deals with Field32 and Field64. The |
| // former is used for both integers and floats, the latter for longs |
| // and doubles. This class provides the necessary coercion. |
| template <typename T, typename U> |
| class TypeAdaptorAccessor : public Object::Accessor<T> { |
| public: |
| explicit TypeAdaptorAccessor(Object::Accessor<U>* inner_accessor) |
| : inner_accessor_(inner_accessor) {} |
| |
| void Access(T* addr) override { |
| static_assert(sizeof(T) == sizeof(U), "bad conversion"); |
| inner_accessor_->Access(reinterpret_cast<U*>(addr)); |
| } |
| |
| private: |
| Object::Accessor<U>* inner_accessor_; |
| }; |
| |
| template <typename T> |
| class FieldAccessViaAccessor { |
| public: |
| typedef Object::Accessor<T> Accessor; |
| |
| // Apply an Accessor to get a field in an object. |
| static void Get(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| obj->GetPrimitiveFieldViaAccessor(field_offset, accessor); |
| } |
| |
| // Apply an Accessor to update a field in an object. |
| static void Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| }; |
| |
| template <> |
| inline void FieldAccessViaAccessor<float>::Get(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor); |
| obj->GetPrimitiveFieldViaAccessor(field_offset, &float_to_int_accessor); |
| } |
| |
| template <> |
| inline void FieldAccessViaAccessor<double>::Get(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor); |
| obj->GetPrimitiveFieldViaAccessor(field_offset, &double_to_int_accessor); |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<uint8_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateFieldBooleanViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateFieldBooleanViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<int8_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateFieldByteViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateFieldByteViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<uint16_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateFieldCharViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateFieldCharViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<int16_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateFieldShortViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateFieldShortViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<int32_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<int64_t>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, accessor); |
| } else { |
| obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<float>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor); |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, &float_to_int_accessor); |
| } else { |
| obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, &float_to_int_accessor); |
| } |
| } |
| |
| template <> |
| void FieldAccessViaAccessor<double>::Update(ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| Accessor* accessor) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor); |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, &double_to_int_accessor); |
| } else { |
| obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, &double_to_int_accessor); |
| } |
| } |
| |
| // Helper class that gets values from a shadow frame with appropriate type coercion. |
| template <typename T> |
| class ValueGetter { |
| public: |
| static T Get(ShadowFrameGetter* getter) REQUIRES_SHARED(Locks::mutator_lock_) { |
| static_assert(sizeof(T) <= sizeof(uint32_t), "Bad size"); |
| uint32_t raw_value = getter->Get(); |
| return static_cast<T>(raw_value); |
| } |
| }; |
| |
| template <> |
| int64_t ValueGetter<int64_t>::Get(ShadowFrameGetter* getter) { |
| return getter->GetLong(); |
| } |
| |
| template <> |
| float ValueGetter<float>::Get(ShadowFrameGetter* getter) { |
| uint32_t raw_value = getter->Get(); |
| return *reinterpret_cast<float*>(&raw_value); |
| } |
| |
| template <> |
| double ValueGetter<double>::Get(ShadowFrameGetter* getter) { |
| int64_t raw_value = getter->GetLong(); |
| return *reinterpret_cast<double*>(&raw_value); |
| } |
| |
| template <> |
| ObjPtr<Object> ValueGetter<ObjPtr<Object>>::Get(ShadowFrameGetter* getter) { |
| return getter->GetReference(); |
| } |
| |
| // Class for accessing fields of Object instances |
| template <typename T> |
| class FieldAccessor { |
| public: |
| static bool Dispatch(VarHandle::AccessMode access_mode, |
| ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| ShadowFrameGetter* getter, |
| JValue* result) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| }; |
| |
| // Dispatch implementation for primitive fields. |
| template <typename T> |
| bool FieldAccessor<T>::Dispatch(VarHandle::AccessMode access_mode, |
| ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| ShadowFrameGetter* getter, |
| JValue* result) { |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: { |
| GetAccessor<T> accessor(result); |
| FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kSet: { |
| T new_value = ValueGetter<T>::Get(getter); |
| SetAccessor<T> accessor(new_value); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAcquire: |
| case VarHandle::AccessMode::kGetOpaque: |
| case VarHandle::AccessMode::kGetVolatile: { |
| GetVolatileAccessor<T> accessor(result); |
| FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kSetOpaque: |
| case VarHandle::AccessMode::kSetRelease: |
| case VarHandle::AccessMode::kSetVolatile: { |
| T new_value = ValueGetter<T>::Get(getter); |
| SetVolatileAccessor<T> accessor(new_value); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndSet: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndExchange: |
| case VarHandle::AccessMode::kCompareAndExchangeAcquire: |
| case VarHandle::AccessMode::kCompareAndExchangeRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kWeakCompareAndSet: |
| case VarHandle::AccessMode::kWeakCompareAndSetAcquire: |
| case VarHandle::AccessMode::kWeakCompareAndSetPlain: |
| case VarHandle::AccessMode::kWeakCompareAndSetRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndSet: |
| case VarHandle::AccessMode::kGetAndSetAcquire: |
| case VarHandle::AccessMode::kGetAndSetRelease: { |
| T new_value = ValueGetter<T>::Get(getter); |
| GetAndSetAccessor<T> accessor(new_value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndAdd: |
| case VarHandle::AccessMode::kGetAndAddAcquire: |
| case VarHandle::AccessMode::kGetAndAddRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndAddAccessor<T> accessor(value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseOr: |
| case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseOrAccessor<T> accessor(value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseAnd: |
| case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseAndAccessor<T> accessor(value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseXor: |
| case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseXorAccessor<T> accessor(value, result); |
| FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor); |
| break; |
| } |
| } |
| return true; |
| } |
| |
| // Dispatch implementation for reference fields. |
| template <> |
| bool FieldAccessor<ObjPtr<Object>>::Dispatch(VarHandle::AccessMode access_mode, |
| ObjPtr<Object> obj, |
| MemberOffset field_offset, |
| ShadowFrameGetter* getter, |
| JValue* result) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| // To keep things simple, use the minimum strongest existing |
| // field accessor for Object fields. This may be the most |
| // straightforward strategy in general for the interpreter. |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: { |
| StoreResult(obj->GetFieldObject<Object>(field_offset), result); |
| break; |
| } |
| case VarHandle::AccessMode::kSet: { |
| ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->SetFieldObject<kTransactionActive>(field_offset, new_value); |
| } else { |
| obj->SetFieldObject<kTransactionInactive>(field_offset, new_value); |
| } |
| break; |
| } |
| case VarHandle::AccessMode::kGetAcquire: |
| case VarHandle::AccessMode::kGetOpaque: |
| case VarHandle::AccessMode::kGetVolatile: { |
| StoreResult(obj->GetFieldObjectVolatile<Object>(field_offset), result); |
| break; |
| } |
| case VarHandle::AccessMode::kSetOpaque: |
| case VarHandle::AccessMode::kSetRelease: |
| case VarHandle::AccessMode::kSetVolatile: { |
| ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| if (Runtime::Current()->IsActiveTransaction()) { |
| obj->SetFieldObjectVolatile<kTransactionActive>(field_offset, new_value); |
| } else { |
| obj->SetFieldObjectVolatile<kTransactionInactive>(field_offset, new_value); |
| } |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndSet: { |
| ReadBarrierForVarHandleAccess(obj, field_offset); |
| ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| bool cas_result; |
| if (Runtime::Current()->IsActiveTransaction()) { |
| cas_result = obj->CasFieldObject<kTransactionActive>(field_offset, |
| expected_value, |
| desired_value, |
| CASMode::kStrong, |
| std::memory_order_seq_cst); |
| } else { |
| cas_result = obj->CasFieldObject<kTransactionInactive>(field_offset, |
| expected_value, |
| desired_value, |
| CASMode::kStrong, |
| std::memory_order_seq_cst); |
| } |
| StoreResult(cas_result, result); |
| break; |
| } |
| case VarHandle::AccessMode::kWeakCompareAndSet: |
| case VarHandle::AccessMode::kWeakCompareAndSetAcquire: |
| case VarHandle::AccessMode::kWeakCompareAndSetPlain: |
| case VarHandle::AccessMode::kWeakCompareAndSetRelease: { |
| ReadBarrierForVarHandleAccess(obj, field_offset); |
| ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| bool cas_result; |
| if (Runtime::Current()->IsActiveTransaction()) { |
| cas_result = obj->CasFieldObject<kTransactionActive>(field_offset, |
| expected_value, |
| desired_value, |
| CASMode::kWeak, |
| std::memory_order_seq_cst); |
| } else { |
| cas_result = obj->CasFieldObject<kTransactionInactive>( |
| field_offset, |
| expected_value, |
| desired_value, |
| CASMode::kWeak, |
| std::memory_order_seq_cst); |
| } |
| StoreResult(cas_result, result); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndExchange: |
| case VarHandle::AccessMode::kCompareAndExchangeAcquire: |
| case VarHandle::AccessMode::kCompareAndExchangeRelease: { |
| ReadBarrierForVarHandleAccess(obj, field_offset); |
| ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| ObjPtr<Object> witness_value; |
| if (Runtime::Current()->IsActiveTransaction()) { |
| witness_value = obj->CompareAndExchangeFieldObject<kTransactionActive>(field_offset, |
| expected_value, |
| desired_value); |
| } else { |
| witness_value = obj->CompareAndExchangeFieldObject<kTransactionInactive>(field_offset, |
| expected_value, |
| desired_value); |
| } |
| StoreResult(witness_value, result); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndSet: |
| case VarHandle::AccessMode::kGetAndSetAcquire: |
| case VarHandle::AccessMode::kGetAndSetRelease: { |
| ReadBarrierForVarHandleAccess(obj, field_offset); |
| ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter); |
| ObjPtr<Object> old_value; |
| if (Runtime::Current()->IsActiveTransaction()) { |
| old_value = obj->ExchangeFieldObject<kTransactionActive>(field_offset, new_value); |
| } else { |
| old_value = obj->ExchangeFieldObject<kTransactionInactive>(field_offset, new_value); |
| } |
| StoreResult(old_value, result); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndAdd: |
| case VarHandle::AccessMode::kGetAndAddAcquire: |
| case VarHandle::AccessMode::kGetAndAddRelease: |
| case VarHandle::AccessMode::kGetAndBitwiseOr: |
| case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseOrRelease: |
| case VarHandle::AccessMode::kGetAndBitwiseAnd: |
| case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseAndRelease: |
| case VarHandle::AccessMode::kGetAndBitwiseXor: |
| case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { |
| size_t index = static_cast<size_t>(access_mode); |
| const char* access_mode_name = kAccessorToAccessMode[index].method_name; |
| UnreachableAccessMode(access_mode_name, "Object"); |
| } |
| } |
| return true; |
| } |
| |
| // Class for accessing primitive array elements. |
| template <typename T> |
| class PrimitiveArrayElementAccessor { |
| public: |
| static T* GetElementAddress(ObjPtr<Array> target_array, int target_element) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| auto primitive_array = ObjPtr<PrimitiveArray<T>>::DownCast(target_array); |
| DCHECK(primitive_array->CheckIsValidIndex(target_element)); |
| return &primitive_array->GetData()[target_element]; |
| } |
| |
| static bool Dispatch(VarHandle::AccessMode access_mode, |
| ObjPtr<Array> target_array, |
| int target_element, |
| ShadowFrameGetter* getter, |
| JValue* result) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| T* element_address = GetElementAddress(target_array, target_element); |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: { |
| GetAccessor<T> accessor(result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kSet: { |
| T new_value = ValueGetter<T>::Get(getter); |
| SetAccessor<T> accessor(new_value); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAcquire: |
| case VarHandle::AccessMode::kGetOpaque: |
| case VarHandle::AccessMode::kGetVolatile: { |
| GetVolatileAccessor<T> accessor(result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kSetOpaque: |
| case VarHandle::AccessMode::kSetRelease: |
| case VarHandle::AccessMode::kSetVolatile: { |
| T new_value = ValueGetter<T>::Get(getter); |
| SetVolatileAccessor<T> accessor(new_value); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndSet: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndExchange: |
| case VarHandle::AccessMode::kCompareAndExchangeAcquire: |
| case VarHandle::AccessMode::kCompareAndExchangeRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kWeakCompareAndSet: |
| case VarHandle::AccessMode::kWeakCompareAndSetAcquire: |
| case VarHandle::AccessMode::kWeakCompareAndSetPlain: |
| case VarHandle::AccessMode::kWeakCompareAndSetRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndSet: |
| case VarHandle::AccessMode::kGetAndSetAcquire: |
| case VarHandle::AccessMode::kGetAndSetRelease: { |
| T new_value = ValueGetter<T>::Get(getter); |
| GetAndSetAccessor<T> accessor(new_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndAdd: |
| case VarHandle::AccessMode::kGetAndAddAcquire: |
| case VarHandle::AccessMode::kGetAndAddRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndAddAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseOr: |
| case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseOrAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseAnd: |
| case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseAndAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseXor: |
| case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| GetAndBitwiseXorAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| } |
| return true; |
| } |
| }; |
| |
| // Class for accessing primitive array elements. |
| template <typename T> |
| class ByteArrayViewAccessor { |
| public: |
| static inline bool IsAccessAligned(int8_t* data, int data_index) { |
| static_assert(IsPowerOfTwo(sizeof(T)), "unexpected size"); |
| static_assert(std::is_arithmetic<T>::value, "unexpected type"); |
| uintptr_t alignment_mask = sizeof(T) - 1; |
| uintptr_t address = reinterpret_cast<uintptr_t>(data + data_index); |
| return (address & alignment_mask) == 0; |
| } |
| |
| static inline void MaybeByteSwap(bool byte_swap, T* const value) { |
| if (byte_swap) { |
| *value = BSWAP(*value); |
| } |
| } |
| |
| static bool Dispatch(const VarHandle::AccessMode access_mode, |
| int8_t* const data, |
| const int data_index, |
| const bool byte_swap, |
| ShadowFrameGetter* const getter, |
| JValue* const result) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| const bool is_aligned = IsAccessAligned(data, data_index); |
| if (!is_aligned) { |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: { |
| T value; |
| memcpy(&value, data + data_index, sizeof(T)); |
| MaybeByteSwap(byte_swap, &value); |
| StoreResult(value, result); |
| return true; |
| } |
| case VarHandle::AccessMode::kSet: { |
| T new_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &new_value); |
| memcpy(data + data_index, &new_value, sizeof(T)); |
| return true; |
| } |
| default: |
| // No other access modes support unaligned access. |
| ThrowIllegalStateException("Unaligned access not supported"); |
| return false; |
| } |
| } |
| |
| T* const element_address = reinterpret_cast<T*>(data + data_index); |
| CHECK(IsAccessAligned(reinterpret_cast<int8_t*>(element_address), 0)); |
| switch (access_mode) { |
| case VarHandle::AccessMode::kGet: { |
| GetAccessor<T> accessor(result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kSet: { |
| T new_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &new_value); |
| SetAccessor<T> accessor(new_value); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAcquire: |
| case VarHandle::AccessMode::kGetOpaque: |
| case VarHandle::AccessMode::kGetVolatile: { |
| GetVolatileAccessor<T> accessor(result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kSetOpaque: |
| case VarHandle::AccessMode::kSetRelease: |
| case VarHandle::AccessMode::kSetVolatile: { |
| T new_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &new_value); |
| SetVolatileAccessor<T> accessor(new_value); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndSet: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &expected_value); |
| MaybeByteSwap(byte_swap, &desired_value); |
| CompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kCompareAndExchange: |
| case VarHandle::AccessMode::kCompareAndExchangeAcquire: |
| case VarHandle::AccessMode::kCompareAndExchangeRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &expected_value); |
| MaybeByteSwap(byte_swap, &desired_value); |
| CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kWeakCompareAndSet: |
| case VarHandle::AccessMode::kWeakCompareAndSetAcquire: |
| case VarHandle::AccessMode::kWeakCompareAndSetPlain: |
| case VarHandle::AccessMode::kWeakCompareAndSetRelease: { |
| T expected_value = ValueGetter<T>::Get(getter); |
| T desired_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &expected_value); |
| MaybeByteSwap(byte_swap, &desired_value); |
| WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result); |
| accessor.Access(element_address); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndSet: |
| case VarHandle::AccessMode::kGetAndSetAcquire: |
| case VarHandle::AccessMode::kGetAndSetRelease: { |
| T new_value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &new_value); |
| GetAndSetAccessor<T> accessor(new_value, result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndAdd: |
| case VarHandle::AccessMode::kGetAndAddAcquire: |
| case VarHandle::AccessMode::kGetAndAddRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| if (byte_swap) { |
| GetAndAddWithByteSwapAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| } else { |
| GetAndAddAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| } |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseOr: |
| case VarHandle::AccessMode::kGetAndBitwiseOrAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseOrRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &value); |
| GetAndBitwiseOrAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseAnd: |
| case VarHandle::AccessMode::kGetAndBitwiseAndAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseAndRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &value); |
| GetAndBitwiseAndAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| case VarHandle::AccessMode::kGetAndBitwiseXor: |
| case VarHandle::AccessMode::kGetAndBitwiseXorAcquire: |
| case VarHandle::AccessMode::kGetAndBitwiseXorRelease: { |
| T value = ValueGetter<T>::Get(getter); |
| MaybeByteSwap(byte_swap, &value); |
| GetAndBitwiseXorAccessor<T> accessor(value, result); |
| accessor.Access(element_address); |
| JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result); |
| break; |
| } |
| } |
| return true; |
| } |
| }; |
| |
| } // namespace |
| |
| ObjPtr<Class> VarHandle::GetVarType() { |
| return GetFieldObject<Class>(VarTypeOffset()); |
| } |
| |
| ObjPtr<Class> VarHandle::GetCoordinateType0() { |
| return GetFieldObject<Class>(CoordinateType0Offset()); |
| } |
| |
| ObjPtr<Class> VarHandle::GetCoordinateType1() { |
| return GetFieldObject<Class>(CoordinateType1Offset()); |
| } |
| |
| int32_t VarHandle::GetAccessModesBitMask() { |
| return GetField32(AccessModesBitMaskOffset()); |
| } |
| |
| VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode, |
| MethodType* method_type) { |
| MatchKind match = MatchKind::kExact; |
| |
| ObjPtr<VarHandle> vh = this; |
| ObjPtr<Class> var_type = vh->GetVarType(); |
| ObjPtr<Class> mt_rtype = method_type->GetRType(); |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| |
| // Check return type first. If the return type of the method |
| // of the VarHandle is immaterial. |
| if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) { |
| ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type); |
| if (vh_rtype != mt_rtype) { |
| if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) { |
| return MatchKind::kNone; |
| } |
| match = MatchKind::kWithConversions; |
| } |
| } |
| |
| // Check the number of parameters matches. |
| ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters]; |
| const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, |
| access_mode_template, |
| var_type, |
| GetCoordinateType0(), |
| GetCoordinateType1()); |
| if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { |
| return MatchKind::kNone; |
| } |
| |
| // Check the parameter types are compatible. |
| ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes(); |
| for (int32_t i = 0; i < vh_ptypes_count; ++i) { |
| if (mt_ptypes->Get(i) == vh_ptypes[i]) { |
| continue; |
| } |
| if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { |
| return MatchKind::kNone; |
| } |
| match = MatchKind::kWithConversions; |
| } |
| return match; |
| } |
| |
| bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, |
| MethodType* method_type) { |
| StackHandleScope<3> hs(Thread::Current()); |
| Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType())); |
| Handle<VarHandle> vh(hs.NewHandle(this)); |
| Handle<Class> var_type(hs.NewHandle(vh->GetVarType())); |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| |
| // Check return type first. |
| if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) { |
| // The result of the operation will be discarded. The return type |
| // of the VarHandle is immaterial. |
| } else { |
| ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get())); |
| if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) { |
| return false; |
| } |
| } |
| |
| // Check the number of parameters matches (ignoring the VarHandle parameter). |
| static const int32_t kVarHandleParameters = 1; |
| ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters]; |
| const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, |
| access_mode_template, |
| var_type.Get(), |
| GetCoordinateType0(), |
| GetCoordinateType1()); |
| if (vh_ptypes_count != method_type->GetPTypes()->GetLength() - kVarHandleParameters) { |
| return false; |
| } |
| |
| // Check the parameter types are compatible (ignoring the VarHandle parameter). |
| ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes(); |
| for (int32_t i = 0; i < vh_ptypes_count; ++i) { |
| if (!IsParameterTypeConvertible(mt_ptypes->Get(i + kVarHandleParameters), vh_ptypes[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| ObjPtr<MethodType> VarHandle::GetMethodTypeForAccessMode(Thread* self, |
| ObjPtr<VarHandle> var_handle, |
| AccessMode access_mode) { |
| // This is a static method as the var_handle might be moved by the GC during it's execution. |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| |
| StackHandleScope<3> hs(self); |
| Handle<VarHandle> vh = hs.NewHandle(var_handle); |
| Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType())); |
| const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, |
| vh->GetCoordinateType0(), |
| vh->GetCoordinateType1()); |
| ObjPtr<Class> array_of_class = GetClassRoot<ObjectArray<Class>>(); |
| Handle<ObjectArray<Class>> ptypes = |
| hs.NewHandle(ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, ptypes_count)); |
| if (ptypes == nullptr) { |
| return nullptr; |
| } |
| |
| ObjPtr<Class> ptypes_array[VarHandle::kMaxAccessorParameters]; |
| BuildParameterArray(ptypes_array, |
| access_mode_template, |
| vh->GetVarType(), |
| vh->GetCoordinateType0(), |
| vh->GetCoordinateType1()); |
| for (int32_t i = 0; i < ptypes_count; ++i) { |
| ptypes->Set(i, ptypes_array[i]); |
| } |
| return MethodType::Create(self, rtype, ptypes); |
| } |
| |
| ObjPtr<MethodType> VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { |
| return GetMethodTypeForAccessMode(self, this, access_mode); |
| } |
| |
| std::string VarHandle::PrettyDescriptorForAccessMode(AccessMode access_mode) { |
| // Effect MethodType::PrettyDescriptor() without first creating a method type first. |
| std::ostringstream oss; |
| oss << '('; |
| |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| ObjPtr<Class> var_type = GetVarType(); |
| ObjPtr<Class> ctypes[2] = { GetCoordinateType0(), GetCoordinateType1() }; |
| const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, ctypes[0], ctypes[1]); |
| int32_t ptypes_done = 0; |
| for (ObjPtr<Class> ctype : ctypes) { |
| if (!ctype.IsNull()) { |
| if (ptypes_done != 0) { |
| oss << ", "; |
| } |
| oss << ctype->PrettyDescriptor();; |
| ptypes_done++; |
| } |
| } |
| while (ptypes_done != ptypes_count) { |
| if (ptypes_done != 0) { |
| oss << ", "; |
| } |
| oss << var_type->PrettyDescriptor(); |
| ptypes_done++; |
| } |
| ObjPtr<Class> rtype = GetReturnType(access_mode_template, var_type); |
| oss << ')' << rtype->PrettyDescriptor(); |
| return oss.str(); |
| } |
| |
| bool VarHandle::Access(AccessMode access_mode, |
| ShadowFrame* shadow_frame, |
| const InstructionOperands* const operands, |
| JValue* result) { |
| ObjPtr<ObjectArray<Class>> class_roots = Runtime::Current()->GetClassLinker()->GetClassRoots(); |
| ObjPtr<Class> klass = GetClass(); |
| if (klass == GetClassRoot<FieldVarHandle>(class_roots)) { |
| auto vh = reinterpret_cast<FieldVarHandle*>(this); |
| return vh->Access(access_mode, shadow_frame, operands, result); |
| } else if (klass == GetClassRoot<ArrayElementVarHandle>(class_roots)) { |
| auto vh = reinterpret_cast<ArrayElementVarHandle*>(this); |
| return vh->Access(access_mode, shadow_frame, operands, result); |
| } else if (klass == GetClassRoot<ByteArrayViewVarHandle>(class_roots)) { |
| auto vh = reinterpret_cast<ByteArrayViewVarHandle*>(this); |
| return vh->Access(access_mode, shadow_frame, operands, result); |
| } else if (klass == GetClassRoot<ByteBufferViewVarHandle>(class_roots)) { |
| auto vh = reinterpret_cast<ByteBufferViewVarHandle*>(this); |
| return vh->Access(access_mode, shadow_frame, operands, result); |
| } else { |
| LOG(FATAL) << "Unknown varhandle kind"; |
| UNREACHABLE(); |
| } |
| } |
| |
| const char* VarHandle::GetReturnTypeDescriptor(const char* accessor_name) { |
| AccessMode access_mode; |
| if (!GetAccessModeByMethodName(accessor_name, &access_mode)) { |
| return nullptr; |
| } |
| AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); |
| switch (access_mode_template) { |
| case AccessModeTemplate::kGet: |
| case AccessModeTemplate::kCompareAndExchange: |
| case AccessModeTemplate::kGetAndUpdate: |
| return "Ljava/lang/Object;"; |
| case AccessModeTemplate::kCompareAndSet: |
| return "Z"; |
| case AccessModeTemplate::kSet: |
| return "V"; |
| } |
| } |
| |
| VarHandle::AccessMode VarHandle::GetAccessModeByIntrinsic(Intrinsics intrinsic) { |
| #define VAR_HANDLE_ACCESS_MODE(V) \ |
| V(CompareAndExchange) \ |
| V(CompareAndExchangeAcquire) \ |
| V(CompareAndExchangeRelease) \ |
| V(CompareAndSet) \ |
| V(Get) \ |
| V(GetAcquire) \ |
| V(GetAndAdd) \ |
| V(GetAndAddAcquire) \ |
| V(GetAndAddRelease) \ |
| V(GetAndBitwiseAnd) \ |
| V(GetAndBitwiseAndAcquire) \ |
| V(GetAndBitwiseAndRelease) \ |
| V(GetAndBitwiseOr) \ |
| V(GetAndBitwiseOrAcquire) \ |
| V(GetAndBitwiseOrRelease) \ |
| V(GetAndBitwiseXor) \ |
| V(GetAndBitwiseXorAcquire) \ |
| V(GetAndBitwiseXorRelease) \ |
| V(GetAndSet) \ |
| V(GetAndSetAcquire) \ |
| V(GetAndSetRelease) \ |
| V(GetOpaque) \ |
| V(GetVolatile) \ |
| V(Set) \ |
| V(SetOpaque) \ |
| V(SetRelease) \ |
| V(SetVolatile) \ |
| V(WeakCompareAndSet) \ |
| V(WeakCompareAndSetAcquire) \ |
| V(WeakCompareAndSetPlain) \ |
| V(WeakCompareAndSetRelease) |
| switch (intrinsic) { |
| #define INTRINSIC_CASE(Name) \ |
| case Intrinsics::kVarHandle ## Name: \ |
| return VarHandle::AccessMode::k ## Name; |
| VAR_HANDLE_ACCESS_MODE(INTRINSIC_CASE) |
| #undef INTRINSIC_CASE |
| #undef VAR_HANDLE_ACCESS_MODE |
| default: |
| break; |
| } |
| LOG(FATAL) << "Unknown VarHandle instrinsic: " << static_cast<int>(intrinsic); |
| UNREACHABLE(); |
| } |
| |
| bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode) { |
| if (method_name == nullptr) { |
| return false; |
| } |
| VarHandleAccessorToAccessModeEntry target = { method_name, /*dummy*/VarHandle::AccessMode::kGet }; |
| auto last = std::cend(kAccessorToAccessMode); |
| auto it = std::lower_bound(std::cbegin(kAccessorToAccessMode), |
| last, |
| target, |
| VarHandleAccessorToAccessModeEntry::CompareName); |
| if (it == last || strcmp(it->method_name, method_name) != 0) { |
| return false; |
| } |
| *access_mode = it->access_mode; |
| return true; |
| } |
| |
| ArtField* FieldVarHandle::GetField() { |
| uintptr_t opaque_field = static_cast<uintptr_t>(GetField64(ArtFieldOffset())); |
| return reinterpret_cast<ArtField*>(opaque_field); |
| } |
| |
| bool FieldVarHandle::Access(AccessMode access_mode, |
| ShadowFrame* shadow_frame, |
| const InstructionOperands* const operands, |
| JValue* result) { |
| ShadowFrameGetter getter(*shadow_frame, operands); |
| ArtField* field = GetField(); |
| ObjPtr<Object> obj; |
| if (field->IsStatic()) { |
| DCHECK_LE(operands->GetNumberOfOperands(), |
| 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u)); |
| obj = field->GetDeclaringClass(); |
| } else { |
| DCHECK_GE(operands->GetNumberOfOperands(), 1u); |
| DCHECK_LE(operands->GetNumberOfOperands(), |
| 1u + 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u)); |
| obj = getter.GetReference(); |
| if (obj.IsNull()) { |
| ThrowNullPointerExceptionForCoordinate(); |
| return false; |
| } |
| } |
| DCHECK(!obj.IsNull()); |
| |
| const MemberOffset offset = field->GetOffset(); |
| const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); |
| switch (primitive_type) { |
| case Primitive::Type::kPrimNot: |
| return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimBoolean: |
| return FieldAccessor<uint8_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimByte: |
| return FieldAccessor<int8_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimChar: |
| return FieldAccessor<uint16_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimShort: |
| return FieldAccessor<int16_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimInt: |
| return FieldAccessor<int32_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimFloat: |
| return FieldAccessor<float>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimLong: |
| return FieldAccessor<int64_t>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimDouble: |
| return FieldAccessor<double>::Dispatch(access_mode, obj, offset, &getter, result); |
| case Primitive::kPrimVoid: |
| break; |
| } |
| LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; |
| UNREACHABLE(); |
| } |
| |
| bool ArrayElementVarHandle::Access(AccessMode access_mode, |
| ShadowFrame* shadow_frame, |
| const InstructionOperands* const operands, |
| JValue* result) { |
| ShadowFrameGetter getter(*shadow_frame, operands); |
| |
| // The target array is the first co-ordinate type preceeding var type arguments. |
| ObjPtr<Object> raw_array(getter.GetReference()); |
| if (raw_array == nullptr) { |
| ThrowNullPointerExceptionForCoordinate(); |
| return false; |
| } |
| |
| ObjPtr<Array> target_array(raw_array->AsArray()); |
| |
| // The target array element is the second co-ordinate type preceeding var type arguments. |
| const int target_element = getter.Get(); |
| if (!target_array->CheckIsValidIndex(target_element)) { |
| DCHECK(Thread::Current()->IsExceptionPending()); |
| return false; |
| } |
| |
| const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); |
| switch (primitive_type) { |
| case Primitive::Type::kPrimNot: { |
| MemberOffset target_element_offset = |
| target_array->AsObjectArray<Object>()->OffsetOfElement(target_element); |
| return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode, |
| target_array, |
| target_element_offset, |
| &getter, |
| result); |
| } |
| case Primitive::Type::kPrimBoolean: |
| return PrimitiveArrayElementAccessor<uint8_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimByte: |
| return PrimitiveArrayElementAccessor<int8_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimChar: |
| return PrimitiveArrayElementAccessor<uint16_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimShort: |
| return PrimitiveArrayElementAccessor<int16_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimInt: |
| return PrimitiveArrayElementAccessor<int32_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimLong: |
| return PrimitiveArrayElementAccessor<int64_t>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimFloat: |
| return PrimitiveArrayElementAccessor<float>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimDouble: |
| return PrimitiveArrayElementAccessor<double>::Dispatch(access_mode, |
| target_array, |
| target_element, |
| &getter, |
| result); |
| case Primitive::Type::kPrimVoid: |
| break; |
| } |
| LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; |
| UNREACHABLE(); |
| } |
| |
| bool ByteArrayViewVarHandle::GetNativeByteOrder() { |
| return GetFieldBoolean(NativeByteOrderOffset()); |
| } |
| |
| bool ByteArrayViewVarHandle::Access(AccessMode access_mode, |
| ShadowFrame* shadow_frame, |
| const InstructionOperands* const operands, |
| JValue* result) { |
| ShadowFrameGetter getter(*shadow_frame, operands); |
| |
| // The byte array is the first co-ordinate type preceeding var type arguments. |
| ObjPtr<Object> raw_byte_array(getter.GetReference()); |
| if (raw_byte_array == nullptr) { |
| ThrowNullPointerExceptionForCoordinate(); |
| return false; |
| } |
| |
| ObjPtr<ByteArray> byte_array(raw_byte_array->AsByteArray()); |
| |
| // The offset in the byte array element is the second co-ordinate type. |
| const int32_t data_offset = getter.Get(); |
| |
| // Bounds check requested access. |
| const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); |
| if (!CheckElementIndex(primitive_type, data_offset, byte_array->GetLength())) { |
| return false; |
| } |
| |
| int8_t* const data = byte_array->GetData(); |
| bool byte_swap = !GetNativeByteOrder(); |
| switch (primitive_type) { |
| case Primitive::Type::kPrimNot: |
| case Primitive::kPrimBoolean: |
| case Primitive::kPrimByte: |
| case Primitive::kPrimVoid: |
| // These are not supported for byte array views and not instantiable. |
| break; |
| case Primitive::kPrimChar: |
| return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimShort: |
| return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimInt: |
| return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimFloat: |
| // Treated as a bitwise representation. See javadoc comments for |
| // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). |
| return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimLong: |
| return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimDouble: |
| // Treated as a bitwise representation. See javadoc comments for |
| // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). |
| return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, |
| data, |
| data_offset, |
| byte_swap, |
| &getter, |
| result); |
| } |
| LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; |
| UNREACHABLE(); |
| } |
| |
| bool ByteBufferViewVarHandle::GetNativeByteOrder() { |
| return GetFieldBoolean(NativeByteOrderOffset()); |
| } |
| |
| bool ByteBufferViewVarHandle::Access(AccessMode access_mode, |
| ShadowFrame* shadow_frame, |
| const InstructionOperands* const operands, |
| JValue* result) { |
| ShadowFrameGetter getter(*shadow_frame, operands); |
| |
| // The byte buffer is the first co-ordinate argument preceeding var type arguments. |
| ObjPtr<Object> byte_buffer(getter.GetReference()); |
| if (byte_buffer == nullptr) { |
| ThrowNullPointerExceptionForCoordinate(); |
| return false; |
| } |
| |
| // The byte index for access is the second co-ordinate |
| // argument. This is relative to the offset field of the ByteBuffer. |
| const int32_t byte_index = getter.Get(); |
| |
| // Check access_mode is compatible with ByteBuffer's read-only property. |
| bool is_read_only = byte_buffer->GetFieldBoolean( |
| GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_isReadOnly)); |
| if (is_read_only && !IsReadOnlyAccessMode(access_mode)) { |
| ThrowReadOnlyBufferException(); |
| return false; |
| } |
| |
| // The native_address is only set for ByteBuffer instances backed by native memory. |
| const int64_t native_address = |
| byte_buffer->GetField64(GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_address)); |
| |
| // Determine offset and limit for accesses. |
| int32_t byte_buffer_offset; |
| if (native_address == 0l) { |
| // Accessing a heap allocated byte buffer. |
| byte_buffer_offset = byte_buffer->GetField32( |
| GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_offset)); |
| } else { |
| // Accessing direct memory. |
| byte_buffer_offset = 0; |
| } |
| const int32_t byte_buffer_limit = byte_buffer->GetField32( |
| GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_limit)); |
| |
| const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType(); |
| if (!CheckElementIndex(primitive_type, byte_index, byte_buffer_offset, byte_buffer_limit)) { |
| return false; |
| } |
| const int32_t checked_offset32 = byte_buffer_offset + byte_index; |
| |
| int8_t* data; |
| if (native_address == 0) { |
| ObjPtr<ByteArray> heap_byte_array = byte_buffer->GetFieldObject<ByteArray>( |
| GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_hb)); |
| data = heap_byte_array->GetData(); |
| } else { |
| data = reinterpret_cast<int8_t*>(static_cast<uint32_t>(native_address)); |
| } |
| |
| bool byte_swap = !GetNativeByteOrder(); |
| switch (primitive_type) { |
| case Primitive::kPrimChar: |
| return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimShort: |
| return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimInt: |
| return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimFloat: |
| // Treated as a bitwise representation. See javadoc comments for |
| // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). |
| return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimLong: |
| return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::kPrimDouble: |
| // Treated as a bitwise representation. See javadoc comments for |
| // java.lang.invoke.MethodHandles.byteArrayViewVarHandle(). |
| return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode, |
| data, |
| checked_offset32, |
| byte_swap, |
| &getter, |
| result); |
| case Primitive::Type::kPrimNot: |
| case Primitive::kPrimBoolean: |
| case Primitive::kPrimByte: |
| case Primitive::kPrimVoid: |
| // These are not supported for byte array views and not instantiable. |
| break; |
| } |
| LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type; |
| UNREACHABLE(); |
| } |
| |
| } // namespace mirror |
| } // namespace art |