| /* |
| * 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 "runtime_support_llvm.h" |
| |
| #include "ScopedLocalRef.h" |
| #include "asm_support.h" |
| #include "class_linker.h" |
| #include "compiler_runtime_func_list.h" |
| #include "dex_file.h" |
| #include "dex_instruction.h" |
| #include "nth_caller_visitor.h" |
| #include "object.h" |
| #include "object_utils.h" |
| #include "reflection.h" |
| #include "runtime_support.h" |
| #include "runtime_support_func_list.h" |
| #include "scoped_thread_state_change.h" |
| #include "thread.h" |
| #include "thread_list.h" |
| #include "utils_llvm.h" |
| #include "verifier/method_verifier.h" |
| #include "well_known_classes.h" |
| |
| #include <algorithm> |
| #include <math.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| |
| namespace art { |
| |
| //---------------------------------------------------------------------------- |
| // Thread |
| //---------------------------------------------------------------------------- |
| |
| // This is used by other runtime support functions, NOT FROM CODE. The REAL GetCurrentThread is |
| // implemented by IRBuilder. (So, ARM can't return R9 in this function.) |
| // TODO: Maybe remove these which are implemented by IRBuilder after refactor runtime support. |
| Thread* art_get_current_thread_from_code() { |
| #if defined(__i386__) |
| Thread* ptr; |
| __asm__ __volatile__("movl %%fs:(%1), %0" |
| : "=r"(ptr) // output |
| : "r"(THREAD_SELF_OFFSET) // input |
| :); // clobber |
| return ptr; |
| #else |
| return Thread::Current(); |
| #endif |
| } |
| |
| void* art_set_current_thread_from_code(void* thread_object_addr) { |
| // Nothing to be done. |
| return NULL; |
| } |
| |
| void art_lock_object_from_code(Object* obj, Thread* thread) |
| EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) { |
| DCHECK(obj != NULL); // Assumed to have been checked before entry |
| obj->MonitorEnter(thread); // May block |
| DCHECK(thread->HoldsLock(obj)); |
| // Only possible exception is NPE and is handled before entry |
| DCHECK(!thread->IsExceptionPending()); |
| } |
| |
| void art_unlock_object_from_code(Object* obj, Thread* thread) |
| UNLOCK_FUNCTION(monitor_lock_) { |
| DCHECK(obj != NULL); // Assumed to have been checked before entry |
| // MonitorExit may throw exception |
| obj->MonitorExit(thread); |
| } |
| |
| void art_test_suspend_from_code(Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| thread->FullSuspendCheck(); |
| } |
| |
| ShadowFrame* art_push_shadow_frame_from_code(Thread* thread, ShadowFrame* new_shadow_frame, |
| AbstractMethod* method, uint16_t num_refs, |
| uint16_t num_vregs) { |
| ShadowFrame* old_frame = thread->PushShadowFrame(new_shadow_frame); |
| new_shadow_frame->SetMethod(method); |
| new_shadow_frame->SetNumberOfReferences(num_refs); |
| new_shadow_frame->SetNumberOfVRegs(num_vregs); |
| return old_frame; |
| } |
| |
| void art_pop_shadow_frame_from_code(void*) { |
| LOG(FATAL) << "Implemented by IRBuilder."; |
| } |
| |
| void art_mark_gc_card_from_code(void *, void*) { |
| LOG(FATAL) << "Implemented by IRBuilder."; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Exception |
| //---------------------------------------------------------------------------- |
| |
| bool art_is_exception_pending_from_code() { |
| LOG(FATAL) << "Implemented by IRBuilder."; |
| return false; |
| } |
| |
| void art_throw_div_zero_from_code() |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| thread->ThrowNewException("Ljava/lang/ArithmeticException;", |
| "divide by zero"); |
| } |
| |
| void art_throw_array_bounds_from_code(int32_t index, int32_t length) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", |
| "length=%d; index=%d", length, index); |
| } |
| |
| void art_throw_no_such_method_from_code(int32_t method_idx) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| // We need the calling method as context for the method_idx |
| AbstractMethod* method = thread->GetCurrentMethod(); |
| ThrowNoSuchMethodError(method_idx, method); |
| } |
| |
| void art_throw_null_pointer_exception_from_code(uint32_t dex_pc) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| NthCallerVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack(), 0); |
| visitor.WalkStack(); |
| AbstractMethod* throw_method = visitor.caller; |
| ThrowNullPointerExceptionFromDexPC(throw_method, dex_pc); |
| } |
| |
| void art_throw_stack_overflow_from_code() |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| if (Runtime::Current()->IsMethodTracingActive()) { |
| TraceMethodUnwindFromCode(thread); |
| } |
| thread->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute. |
| thread->ThrowNewExceptionF("Ljava/lang/StackOverflowError;", "stack size %s", |
| PrettySize(thread->GetStackSize()).c_str()); |
| thread->ResetDefaultStackEnd(); // Return to default stack size. |
| } |
| |
| void art_throw_exception_from_code(Object* exception) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| if (exception == NULL) { |
| thread->ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception"); |
| } else { |
| thread->SetException(static_cast<Throwable*>(exception)); |
| } |
| } |
| |
| void* art_get_and_clear_exception(Thread* self) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| DCHECK(self->IsExceptionPending()); |
| Throwable* exception = self->GetException(); |
| self->ClearException(); |
| return exception; |
| } |
| |
| int32_t art_find_catch_block_from_code(AbstractMethod* current_method, |
| uint32_t ti_offset) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Thread* thread = art_get_current_thread_from_code(); |
| Class* exception_type = thread->GetException()->GetClass(); |
| MethodHelper mh(current_method); |
| const DexFile::CodeItem* code_item = mh.GetCodeItem(); |
| DCHECK_LT(ti_offset, code_item->tries_size_); |
| const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset); |
| |
| int iter_index = 0; |
| // Iterate over the catch handlers associated with dex_pc |
| for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) { |
| uint16_t iter_type_idx = it.GetHandlerTypeIndex(); |
| // Catch all case |
| if (iter_type_idx == DexFile::kDexNoIndex16) { |
| return iter_index; |
| } |
| // Does this catch exception type apply? |
| Class* iter_exception_type = mh.GetDexCacheResolvedType(iter_type_idx); |
| if (iter_exception_type == NULL) { |
| // The verifier should take care of resolving all exception classes early |
| LOG(WARNING) << "Unresolved exception class when finding catch block: " |
| << mh.GetTypeDescriptorFromTypeIdx(iter_type_idx); |
| } else if (iter_exception_type->IsAssignableFrom(exception_type)) { |
| return iter_index; |
| } |
| ++iter_index; |
| } |
| // Handler not found |
| return -1; |
| } |
| |
| |
| //---------------------------------------------------------------------------- |
| // Object Space |
| //---------------------------------------------------------------------------- |
| |
| Object* art_alloc_object_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return AllocObjectFromCode(type_idx, referrer, thread, false); |
| } |
| |
| Object* art_alloc_object_from_code_with_access_check(uint32_t type_idx, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return AllocObjectFromCode(type_idx, referrer, thread, true); |
| } |
| |
| Object* art_alloc_array_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| uint32_t length, |
| Thread* self) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return AllocArrayFromCode(type_idx, referrer, length, self, false); |
| } |
| |
| Object* art_alloc_array_from_code_with_access_check(uint32_t type_idx, |
| AbstractMethod* referrer, |
| uint32_t length, |
| Thread* self) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return AllocArrayFromCode(type_idx, referrer, length, self, true); |
| } |
| |
| Object* art_check_and_alloc_array_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| uint32_t length, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, false); |
| } |
| |
| Object* art_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx, |
| AbstractMethod* referrer, |
| uint32_t length, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, true); |
| } |
| |
| static AbstractMethod* FindMethodHelper(uint32_t method_idx, Object* this_object, |
| AbstractMethod* caller_method, bool access_check, |
| InvokeType type, Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| AbstractMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); |
| if (UNLIKELY(method == NULL)) { |
| method = FindMethodFromCode(method_idx, this_object, caller_method, |
| thread, access_check, type); |
| if (UNLIKELY(method == NULL)) { |
| CHECK(thread->IsExceptionPending()); |
| return 0; // failure |
| } |
| } |
| DCHECK(!thread->IsExceptionPending()); |
| const void* code = method->GetCode(); |
| |
| // When we return, the caller will branch to this address, so it had better not be 0! |
| if (UNLIKELY(code == NULL)) { |
| MethodHelper mh(method); |
| LOG(FATAL) << "Code was NULL in method: " << PrettyMethod(method) |
| << " location: " << mh.GetDexFile().GetLocation(); |
| } |
| return method; |
| } |
| |
| Object* art_find_static_method_from_code_with_access_check(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, true, kStatic, thread); |
| } |
| |
| Object* art_find_direct_method_from_code_with_access_check(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, true, kDirect, thread); |
| } |
| |
| Object* art_find_virtual_method_from_code_with_access_check(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, true, kVirtual, thread); |
| } |
| |
| Object* art_find_super_method_from_code_with_access_check(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, true, kSuper, thread); |
| } |
| |
| Object* |
| art_find_interface_method_from_code_with_access_check(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, true, kInterface, thread); |
| } |
| |
| Object* art_find_interface_method_from_code(uint32_t method_idx, |
| Object* this_object, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return FindMethodHelper(method_idx, this_object, referrer, false, kInterface, thread); |
| } |
| |
| Object* art_initialize_static_storage_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return ResolveVerifyAndClinit(type_idx, referrer, thread, true, false); |
| } |
| |
| Object* art_initialize_type_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return ResolveVerifyAndClinit(type_idx, referrer, thread, false, false); |
| } |
| |
| Object* art_initialize_type_and_verify_access_from_code(uint32_t type_idx, |
| AbstractMethod* referrer, |
| Thread* thread) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| // Called when caller isn't guaranteed to have access to a type and the dex cache may be |
| // unpopulated |
| return ResolveVerifyAndClinit(type_idx, referrer, thread, false, true); |
| } |
| |
| Object* art_resolve_string_from_code(AbstractMethod* referrer, uint32_t string_idx) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return ResolveStringFromCode(referrer, string_idx); |
| } |
| |
| int32_t art_set32_static_from_code(uint32_t field_idx, AbstractMethod* referrer, int32_t new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set32(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticPrimitiveWrite, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set32(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_set64_static_from_code(uint32_t field_idx, AbstractMethod* referrer, int64_t new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set64(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticPrimitiveWrite, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set64(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_set_obj_static_from_code(uint32_t field_idx, AbstractMethod* referrer, Object* new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticObjectWrite, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| field->SetObj(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticObjectWrite, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| field->SetObj(field->GetDeclaringClass(), new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_get32_static_from_code(uint32_t field_idx, AbstractMethod* referrer) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get32(field->GetDeclaringClass()); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticPrimitiveRead, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get32(field->GetDeclaringClass()); |
| } |
| return 0; |
| } |
| |
| int64_t art_get64_static_from_code(uint32_t field_idx, AbstractMethod* referrer) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get64(field->GetDeclaringClass()); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticPrimitiveRead, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get64(field->GetDeclaringClass()); |
| } |
| return 0; |
| } |
| |
| Object* art_get_obj_static_from_code(uint32_t field_idx, AbstractMethod* referrer) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, StaticObjectRead, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| return field->GetObj(field->GetDeclaringClass()); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| StaticObjectRead, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| return field->GetObj(field->GetDeclaringClass()); |
| } |
| return 0; |
| } |
| |
| int32_t art_set32_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, |
| Object* obj, uint32_t new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set32(obj, new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstancePrimitiveWrite, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set32(obj, new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_set64_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, |
| Object* obj, int64_t new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set64(obj, new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstancePrimitiveWrite, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| field->Set64(obj, new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_set_obj_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, |
| Object* obj, Object* new_value) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| field->SetObj(obj, new_value); |
| return 0; |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstanceObjectWrite, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| field->SetObj(obj, new_value); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t art_get32_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, Object* obj) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get32(obj); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstancePrimitiveRead, sizeof(uint32_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get32(obj); |
| } |
| return 0; |
| } |
| |
| int64_t art_get64_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, Object* obj) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get64(obj); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstancePrimitiveRead, sizeof(uint64_t)); |
| if (LIKELY(field != NULL)) { |
| return field->Get64(obj); |
| } |
| return 0; |
| } |
| |
| Object* art_get_obj_instance_from_code(uint32_t field_idx, AbstractMethod* referrer, Object* obj) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Field* field = FindFieldFast(field_idx, referrer, InstanceObjectRead, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| return field->GetObj(obj); |
| } |
| field = FindFieldFromCode(field_idx, referrer, art_get_current_thread_from_code(), |
| InstanceObjectRead, sizeof(Object*)); |
| if (LIKELY(field != NULL)) { |
| return field->GetObj(obj); |
| } |
| return 0; |
| } |
| |
| void art_fill_array_data_from_code(AbstractMethod* method, uint32_t dex_pc, |
| Array* array, uint32_t payload_offset) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| // Test: Is array equal to null? (Guard NullPointerException) |
| if (UNLIKELY(array == NULL)) { |
| art_throw_null_pointer_exception_from_code(dex_pc); |
| return; |
| } |
| |
| // Find the payload from the CodeItem |
| MethodHelper mh(method); |
| const DexFile::CodeItem* code_item = mh.GetCodeItem(); |
| |
| DCHECK_GT(code_item->insns_size_in_code_units_, payload_offset); |
| |
| const Instruction::ArrayDataPayload* payload = |
| reinterpret_cast<const Instruction::ArrayDataPayload*>( |
| code_item->insns_ + payload_offset); |
| |
| DCHECK_EQ(payload->ident, |
| static_cast<uint16_t>(Instruction::kArrayDataSignature)); |
| |
| // Test: Is array big enough? |
| uint32_t array_len = static_cast<uint32_t>(array->GetLength()); |
| if (UNLIKELY(array_len < payload->element_count)) { |
| int32_t last_index = payload->element_count - 1; |
| art_throw_array_bounds_from_code(array_len, last_index); |
| return; |
| } |
| |
| // Copy the data |
| size_t size = payload->element_width * payload->element_count; |
| memcpy(array->GetRawData(payload->element_width), payload->data, size); |
| } |
| |
| |
| |
| //---------------------------------------------------------------------------- |
| // Type checking, in the nature of casting |
| //---------------------------------------------------------------------------- |
| |
| int32_t art_is_assignable_from_code(const Class* dest_type, const Class* src_type) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| DCHECK(dest_type != NULL); |
| DCHECK(src_type != NULL); |
| return dest_type->IsAssignableFrom(src_type) ? 1 : 0; |
| } |
| |
| void art_check_cast_from_code(const Class* dest_type, const Class* src_type) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| DCHECK(dest_type->IsClass()) << PrettyClass(dest_type); |
| DCHECK(src_type->IsClass()) << PrettyClass(src_type); |
| if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) { |
| Thread* thread = art_get_current_thread_from_code(); |
| thread->ThrowNewExceptionF("Ljava/lang/ClassCastException;", |
| "%s cannot be cast to %s", |
| PrettyDescriptor(src_type).c_str(), |
| PrettyDescriptor(dest_type).c_str()); |
| } |
| } |
| |
| void art_check_put_array_element_from_code(const Object* element, const Object* array) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (element == NULL) { |
| return; |
| } |
| DCHECK(array != NULL); |
| Class* array_class = array->GetClass(); |
| DCHECK(array_class != NULL); |
| Class* component_type = array_class->GetComponentType(); |
| Class* element_class = element->GetClass(); |
| if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) { |
| Thread* thread = art_get_current_thread_from_code(); |
| thread->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", |
| "%s cannot be stored in an array of type %s", |
| PrettyDescriptor(element_class).c_str(), |
| PrettyDescriptor(array_class).c_str()); |
| } |
| return; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // JNI |
| //---------------------------------------------------------------------------- |
| |
| // Called on entry to JNI, transition out of Runnable and release share of mutator_lock_. |
| uint32_t art_jni_method_start(Thread* self) |
| UNLOCK_FUNCTION(GlobalSynchronizatio::mutator_lock_) { |
| JNIEnvExt* env = self->GetJniEnv(); |
| uint32_t saved_local_ref_cookie = env->local_ref_cookie; |
| env->local_ref_cookie = env->locals.GetSegmentState(); |
| self->TransitionFromRunnableToSuspended(kNative); |
| return saved_local_ref_cookie; |
| } |
| |
| uint32_t art_jni_method_start_synchronized(jobject to_lock, Thread* self) |
| UNLOCK_FUNCTION(Locks::mutator_lock_) { |
| self->DecodeJObject(to_lock)->MonitorEnter(self); |
| return art_jni_method_start(self); |
| } |
| |
| static inline void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) { |
| JNIEnvExt* env = self->GetJniEnv(); |
| env->locals.SetSegmentState(env->local_ref_cookie); |
| env->local_ref_cookie = saved_local_ref_cookie; |
| } |
| |
| void art_jni_method_end(uint32_t saved_local_ref_cookie, Thread* self) |
| SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { |
| self->TransitionFromSuspendedToRunnable(); |
| PopLocalReferences(saved_local_ref_cookie, self); |
| } |
| |
| |
| void art_jni_method_end_synchronized(uint32_t saved_local_ref_cookie, jobject locked, |
| Thread* self) |
| SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { |
| self->TransitionFromSuspendedToRunnable(); |
| UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. |
| PopLocalReferences(saved_local_ref_cookie, self); |
| } |
| |
| Object* art_jni_method_end_with_reference(jobject result, uint32_t saved_local_ref_cookie, |
| Thread* self) |
| SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { |
| self->TransitionFromSuspendedToRunnable(); |
| Object* o = self->DecodeJObject(result); // Must decode before pop. |
| PopLocalReferences(saved_local_ref_cookie, self); |
| // Process result. |
| if (UNLIKELY(self->GetJniEnv()->check_jni)) { |
| if (self->IsExceptionPending()) { |
| return NULL; |
| } |
| CheckReferenceResult(o, self); |
| } |
| return o; |
| } |
| |
| Object* art_jni_method_end_with_reference_synchronized(jobject result, |
| uint32_t saved_local_ref_cookie, |
| jobject locked, Thread* self) |
| SHARED_LOCK_FUNCTION(Locks::mutator_lock_) { |
| self->TransitionFromSuspendedToRunnable(); |
| UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. |
| Object* o = self->DecodeJObject(result); |
| PopLocalReferences(saved_local_ref_cookie, self); |
| // Process result. |
| if (UNLIKELY(self->GetJniEnv()->check_jni)) { |
| if (self->IsExceptionPending()) { |
| return NULL; |
| } |
| CheckReferenceResult(o, self); |
| } |
| return o; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Runtime Support Function Lookup Callback |
| //---------------------------------------------------------------------------- |
| |
| #define EXTERNAL_LINKAGE(NAME, RETURN_TYPE, ...) \ |
| extern "C" RETURN_TYPE NAME(__VA_ARGS__); |
| COMPILER_RUNTIME_FUNC_LIST_NATIVE(EXTERNAL_LINKAGE) |
| #undef EXTERNAL_LINKAGE |
| |
| static void* art_find_compiler_runtime_func(const char* name) { |
| // TODO: If target support some math func, use the target's version. (e.g. art_d2i -> __aeabi_d2iz) |
| static const char* const names[] = { |
| #define DEFINE_ENTRY(NAME, RETURN_TYPE, ...) #NAME , |
| COMPILER_RUNTIME_FUNC_LIST_NATIVE(DEFINE_ENTRY) |
| #undef DEFINE_ENTRY |
| }; |
| |
| static void* const funcs[] = { |
| #define DEFINE_ENTRY(NAME, RETURN_TYPE, ...) \ |
| reinterpret_cast<void*>(static_cast<RETURN_TYPE (*)(__VA_ARGS__)>(NAME)) , |
| COMPILER_RUNTIME_FUNC_LIST_NATIVE(DEFINE_ENTRY) |
| #undef DEFINE_ENTRY |
| }; |
| |
| static const size_t num_entries = sizeof(names) / sizeof(const char* const); |
| |
| const char* const* const names_begin = names; |
| const char* const* const names_end = names + num_entries; |
| |
| const char* const* name_lbound_ptr = |
| std::lower_bound(names_begin, names_end, name, |
| CStringLessThanComparator()); |
| |
| if (name_lbound_ptr < names_end && strcmp(*name_lbound_ptr, name) == 0) { |
| return funcs[name_lbound_ptr - names_begin]; |
| } else { |
| return NULL; |
| } |
| } |
| |
| // Handler for invocation on proxy methods. We create a boxed argument array. And we invoke |
| // the invocation handler which is a field within the proxy object receiver. |
| void art_proxy_invoke_handler_from_code(AbstractMethod* proxy_method, ...) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| va_list ap; |
| va_start(ap, proxy_method); |
| |
| Object* receiver = va_arg(ap, Object*); |
| Thread* self = va_arg(ap, Thread*); |
| MethodHelper proxy_mh(proxy_method); |
| const size_t num_params = proxy_mh.NumArgs(); |
| |
| // Start new JNI local reference state |
| JNIEnvExt* env = self->GetJniEnv(); |
| ScopedObjectAccessUnchecked soa(env); |
| ScopedJniEnvLocalRefState env_state(env); |
| |
| // Create local ref. copies of the receiver |
| jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver); |
| |
| // Convert proxy method into expected interface method |
| AbstractMethod* interface_method = proxy_method->FindOverriddenMethod(); |
| DCHECK(interface_method != NULL); |
| DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); |
| |
| // Set up arguments array and place in local IRT during boxing (which may allocate/GC) |
| jvalue args_jobj[3]; |
| args_jobj[0].l = rcvr_jobj; |
| args_jobj[1].l = soa.AddLocalReference<jobject>(interface_method); |
| // Args array, if no arguments then NULL (don't include receiver in argument count) |
| args_jobj[2].l = NULL; |
| ObjectArray<Object>* args = NULL; |
| if ((num_params - 1) > 0) { |
| args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(self, num_params - 1); |
| if (args == NULL) { |
| CHECK(self->IsExceptionPending()); |
| return; |
| } |
| args_jobj[2].l = soa.AddLocalReference<jobjectArray>(args); |
| } |
| |
| // Get parameter types. |
| const char* shorty = proxy_mh.GetShorty(); |
| ObjectArray<Class>* param_types = proxy_mh.GetParameterTypes(self); |
| if (param_types == NULL) { |
| CHECK(self->IsExceptionPending()); |
| return; |
| } |
| |
| // Box arguments. |
| for (size_t i = 0; i < (num_params - 1);++i) { |
| JValue val; |
| switch (shorty[i+1]) { |
| case 'Z': |
| val.SetZ(va_arg(ap, jint)); |
| break; |
| case 'B': |
| val.SetB(va_arg(ap, jint)); |
| break; |
| case 'C': |
| val.SetC(va_arg(ap, jint)); |
| break; |
| case 'S': |
| val.SetS(va_arg(ap, jint)); |
| break; |
| case 'I': |
| val.SetI(va_arg(ap, jint)); |
| break; |
| case 'F': |
| val.SetI(va_arg(ap, jint)); // TODO: is this right? |
| break; |
| case 'L': |
| val.SetL(va_arg(ap, Object*)); |
| break; |
| case 'D': |
| case 'J': |
| val.SetJ(va_arg(ap, jlong)); // TODO: is this right for double? |
| break; |
| } |
| Class* param_type = param_types->Get(i); |
| if (param_type->IsPrimitive()) { |
| BoxPrimitive(param_type->GetPrimitiveType(), val); |
| if (self->IsExceptionPending()) { |
| return; |
| } |
| } |
| args->Set(i, val.GetL()); |
| } |
| |
| DCHECK(env->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy)); |
| |
| jobject inv_hand = env->GetObjectField(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy_h); |
| // Call InvocationHandler.invoke |
| jobject result = env->CallObjectMethodA(inv_hand, WellKnownClasses::java_lang_reflect_InvocationHandler_invoke, args_jobj); |
| |
| // Place result in stack args |
| if (!self->IsExceptionPending()) { |
| if (shorty[0] == 'V') { |
| return; |
| } |
| Object* result_ref = self->DecodeJObject(result); |
| JValue* result_unboxed = va_arg(ap, JValue*); |
| if (result_ref == NULL) { |
| result_unboxed->SetL(NULL); |
| } else { |
| bool unboxed_okay = UnboxPrimitiveForResult(result_ref, proxy_mh.GetReturnType(), *result_unboxed); |
| if (!unboxed_okay) { |
| self->ClearException(); |
| self->ThrowNewExceptionF("Ljava/lang/ClassCastException;", |
| "Couldn't convert result of type %s to %s", |
| PrettyTypeOf(result_ref).c_str(), |
| PrettyDescriptor(proxy_mh.GetReturnType()).c_str()); |
| return; |
| } |
| } |
| } else { |
| // In the case of checked exceptions that aren't declared, the exception must be wrapped by |
| // a UndeclaredThrowableException. |
| Throwable* exception = self->GetException(); |
| if (exception->IsCheckedException()) { |
| SynthesizedProxyClass* proxy_class = |
| down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass()); |
| int throws_index = -1; |
| size_t num_virt_methods = proxy_class->NumVirtualMethods(); |
| for (size_t i = 0; i < num_virt_methods; i++) { |
| if (proxy_class->GetVirtualMethod(i) == proxy_method) { |
| throws_index = i; |
| break; |
| } |
| } |
| CHECK_NE(throws_index, -1); |
| ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index); |
| Class* exception_class = exception->GetClass(); |
| bool declares_exception = false; |
| for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) { |
| Class* declared_exception = declared_exceptions->Get(i); |
| declares_exception = declared_exception->IsAssignableFrom(exception_class); |
| } |
| if (!declares_exception) { |
| self->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL); |
| } |
| } |
| } |
| |
| va_end(ap); |
| } |
| |
| void* art_find_runtime_support_func(void* context, const char* name) { |
| struct func_entry_t { |
| const char* name; |
| size_t name_len; |
| void* addr; |
| }; |
| |
| static struct func_entry_t const tab[] = { |
| #define DEFINE_ENTRY(ID, NAME) \ |
| { #NAME, sizeof(#NAME) - 1, reinterpret_cast<void*>(NAME) }, |
| RUNTIME_SUPPORT_FUNC_LIST(DEFINE_ENTRY) |
| #undef DEFINE_ENTRY |
| }; |
| |
| static size_t const tab_size = sizeof(tab) / sizeof(struct func_entry_t); |
| |
| // Search the compiler runtime (such as __divdi3) |
| void* result = art_find_compiler_runtime_func(name); |
| if (result != NULL) { |
| return result; |
| } |
| |
| // Note: Since our table is small, we are using trivial O(n) searching |
| // function. For bigger table, it will be better to use a binary |
| // search or hash function. |
| size_t i; |
| size_t name_len = strlen(name); |
| for (i = 0; i < tab_size; ++i) { |
| if (name_len == tab[i].name_len && strcmp(name, tab[i].name) == 0) { |
| return tab[i].addr; |
| } |
| } |
| |
| LOG(FATAL) << "Error: Can't find symbol " << name; |
| return 0; |
| } |
| |
| } // namespace art |