/* * Copyright (C) 2012 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 #include "art_field-inl.h" #include "art_method-inl.h" #include "base/callee_save_type.h" #include "callee_save_frame.h" #include "dex_file-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc_root-inl.h" #include "mirror/class-inl.h" #include "mirror/object_reference.h" namespace art { inline constexpr bool FindFieldTypeIsRead(FindFieldType type) { return type == InstanceObjectRead || type == InstancePrimitiveRead || type == StaticObjectRead || type == StaticPrimitiveRead; } // Helper function to do a null check after trying to resolve the field. Not for statics since obj // does not exist there. There is a suspend check, object is a double pointer to update the value // in the caller in case it moves. template ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx, ArtMethod* referrer, Thread* self, size_t size, mirror::Object** obj) REQUIRES(!Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); HandleWrapper h(hs.NewHandleWrapper(obj)); ArtField* field = FindFieldFromCode(field_idx, referrer, self, size); if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) { ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/FindFieldTypeIsRead(type)); return nullptr; } return field; } static ArtMethod* GetReferrer(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { if (kIsDebugBuild) { // stub_test doesn't call this code with a proper frame, so get the outer, and if // it does not have compiled code return it. ArtMethod* outer = GetCalleeSaveOuterMethod(self, CalleeSaveType::kSaveRefsOnly); if (outer->GetEntryPointFromQuickCompiledCode() == nullptr) { return outer; } } return GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveRefsOnly).caller; } #define ART_GET_FIELD_FROM_CODE(Kind, PrimitiveType, RetType, SetType, \ PrimitiveOrObject, IsObject, Ptr) \ extern "C" RetType artGet ## Kind ## StaticFromCode(uint32_t field_idx, \ ArtMethod* referrer, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ ArtField* field = FindFieldFast( \ field_idx, referrer, Static ## PrimitiveOrObject ## Read, \ sizeof(PrimitiveType)); \ if (LIKELY(field != nullptr)) { \ return field->Get ## Kind (field->GetDeclaringClass())Ptr; \ } \ field = FindFieldFromCode( \ field_idx, referrer, self, sizeof(PrimitiveType)); \ if (LIKELY(field != nullptr)) { \ return field->Get ## Kind (field->GetDeclaringClass())Ptr; \ } \ /* Will throw exception by checking with Thread::Current. */ \ return 0; \ } \ \ extern "C" RetType artGet ## Kind ## InstanceFromCode(uint32_t field_idx, \ mirror::Object* obj, \ ArtMethod* referrer, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ ArtField* field = FindFieldFast( \ field_idx, referrer, Instance ## PrimitiveOrObject ## Read, \ sizeof(PrimitiveType)); \ if (LIKELY(field != nullptr) && obj != nullptr) { \ return field->Get ## Kind (obj)Ptr; \ } \ field = FindInstanceField( \ field_idx, referrer, self, sizeof(PrimitiveType), &obj); \ if (LIKELY(field != nullptr)) { \ return field->Get ## Kind (obj)Ptr; \ } \ /* Will throw exception by checking with Thread::Current. */ \ return 0; \ } \ \ extern "C" int artSet ## Kind ## StaticFromCode(uint32_t field_idx, \ SetType new_value, \ ArtMethod* referrer, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ ArtField* field = FindFieldFast( \ field_idx, referrer, Static ## PrimitiveOrObject ## Write, \ sizeof(PrimitiveType)); \ if (LIKELY(field != nullptr)) { \ field->Set ## Kind (field->GetDeclaringClass(), new_value); \ return 0; \ } \ if (IsObject) { \ StackHandleScope<1> hs(self); \ HandleWrapper h_obj(hs.NewHandleWrapper( \ reinterpret_cast(&new_value))); \ field = FindFieldFromCode( \ field_idx, referrer, self, sizeof(PrimitiveType)); \ } else { \ field = FindFieldFromCode( \ field_idx, referrer, self, sizeof(PrimitiveType)); \ } \ if (LIKELY(field != nullptr)) { \ field->Set ## Kind (field->GetDeclaringClass(), new_value); \ return 0; \ } \ return -1; \ } \ \ extern "C" int artSet ## Kind ## InstanceFromCode(uint32_t field_idx, \ mirror::Object* obj, \ SetType new_value, \ ArtMethod* referrer, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ ArtField* field = FindFieldFast( \ field_idx, referrer, Instance ## PrimitiveOrObject ## Write, \ sizeof(PrimitiveType)); \ if (LIKELY(field != nullptr && obj != nullptr)) { \ field->Set ## Kind (obj, new_value); \ return 0; \ } \ if (IsObject) { \ StackHandleScope<1> hs(self); \ HandleWrapper h_obj(hs.NewHandleWrapper( \ reinterpret_cast(&new_value))); \ field = FindInstanceField( \ field_idx, \ referrer, \ self, \ sizeof(PrimitiveType), \ &obj); \ } else { \ field = FindInstanceField( \ field_idx, \ referrer, \ self, \ sizeof(PrimitiveType), \ &obj); \ } \ if (LIKELY(field != nullptr)) { \ field->Set ## Kind(obj, new_value); \ return 0; \ } \ return -1; \ } \ \ extern "C" RetType artGet ## Kind ## StaticFromCompiledCode( \ uint32_t field_idx, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artGet ## Kind ## StaticFromCode( \ field_idx, GetReferrer(self), self); \ } \ \ extern "C" RetType artGet ## Kind ## InstanceFromCompiledCode( \ uint32_t field_idx, \ mirror::Object* obj, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artGet ## Kind ## InstanceFromCode( \ field_idx, obj, GetReferrer(self), self); \ } \ \ extern "C" int artSet ## Kind ## StaticFromCompiledCode( \ uint32_t field_idx, \ SetType new_value, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artSet ## Kind ## StaticFromCode( \ field_idx, new_value, GetReferrer(self), self); \ } \ \ extern "C" int artSet ## Kind ## InstanceFromCompiledCode( \ uint32_t field_idx, \ mirror::Object* obj, \ SetType new_value, \ Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artSet ## Kind ## InstanceFromCode( \ field_idx, obj, new_value, GetReferrer(self), self); \ } ART_GET_FIELD_FROM_CODE(Byte, int8_t, ssize_t, uint32_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(Boolean, int8_t, size_t, uint32_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(Short, int16_t, ssize_t, uint16_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(Char, int16_t, size_t, uint16_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(32, int32_t, size_t, uint32_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(64, int64_t, uint64_t, uint64_t, Primitive, false, ) ART_GET_FIELD_FROM_CODE(Obj, mirror::HeapReference, mirror::Object*, mirror::Object*, Object, true, .Ptr()) // To cut on the number of entrypoints, we have shared entries for // byte/boolean and char/short for setting an instance or static field. We just // forward those to the unsigned variant. extern "C" int artSet8StaticFromCompiledCode(uint32_t field_idx, uint32_t new_value, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetBooleanStaticFromCode(field_idx, new_value, GetReferrer(self), self); } extern "C" int artSet16StaticFromCompiledCode(uint32_t field_idx, uint16_t new_value, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetCharStaticFromCode(field_idx, new_value, GetReferrer(self), self); } extern "C" int artSet8InstanceFromCompiledCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetBooleanInstanceFromCode(field_idx, obj, new_value, GetReferrer(self), self); } extern "C" int artSet16InstanceFromCompiledCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetCharInstanceFromCode(field_idx, obj, new_value, GetReferrer(self), self); } extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value, ArtMethod* referrer, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetBooleanStaticFromCode(field_idx, new_value, referrer, self); } extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value, ArtMethod* referrer, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetCharStaticFromCode(field_idx, new_value, referrer, self); } extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value, ArtMethod* referrer, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetBooleanInstanceFromCode(field_idx, obj, new_value, referrer, self); } extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value, ArtMethod* referrer, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return artSetCharInstanceFromCode(field_idx, obj, new_value, referrer, self); } extern "C" mirror::Object* artReadBarrierMark(mirror::Object* obj) { DCHECK(kEmitCompilerReadBarrier); return ReadBarrier::Mark(obj); } extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref ATTRIBUTE_UNUSED, mirror::Object* obj, uint32_t offset) { DCHECK(kEmitCompilerReadBarrier); uint8_t* raw_addr = reinterpret_cast(obj) + offset; mirror::HeapReference* ref_addr = reinterpret_cast*>(raw_addr); constexpr ReadBarrierOption kReadBarrierOption = kUseReadBarrier ? kWithReadBarrier : kWithoutReadBarrier; mirror::Object* result = ReadBarrier::Barrier(obj, MemberOffset(offset), ref_addr); return result; } extern "C" mirror::Object* artReadBarrierForRootSlow(GcRoot* root) { DCHECK(kEmitCompilerReadBarrier); return root->Read(); } } // namespace art