|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | #ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_INL_H_ | 
|  | #define ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_INL_H_ | 
|  |  | 
|  | #include "interpreter_switch_impl.h" | 
|  |  | 
|  | #include "base/enums.h" | 
|  | #include "base/globals.h" | 
|  | #include "base/memory_tool.h" | 
|  | #include "base/quasi_atomic.h" | 
|  | #include "dex/dex_file_types.h" | 
|  | #include "dex/dex_instruction_list.h" | 
|  | #include "experimental_flags.h" | 
|  | #include "handle_scope.h" | 
|  | #include "interpreter_common.h" | 
|  | #include "interpreter/shadow_frame.h" | 
|  | #include "jit/jit-inl.h" | 
|  | #include "jvalue-inl.h" | 
|  | #include "mirror/string-alloc-inl.h" | 
|  | #include "mirror/throwable.h" | 
|  | #include "monitor.h" | 
|  | #include "nth_caller_visitor.h" | 
|  | #include "safe_math.h" | 
|  | #include "shadow_frame-inl.h" | 
|  | #include "thread.h" | 
|  | #include "verifier/method_verifier.h" | 
|  |  | 
|  | namespace art { | 
|  | namespace interpreter { | 
|  |  | 
|  | // Short-lived helper class which executes single DEX bytecode.  It is inlined by compiler. | 
|  | // Any relevant execution information is stored in the fields - it should be kept to minimum. | 
|  | // All instance functions must be inlined so that the fields can be stored in registers. | 
|  | // | 
|  | // The function names must match the names from dex_instruction_list.h and have no arguments. | 
|  | // Return value: The handlers must return false if the instruction throws or returns (exits). | 
|  | // | 
|  | template<bool do_access_check, bool transaction_active, Instruction::Format kFormat> | 
|  | class InstructionHandler { | 
|  | public: | 
|  | #define HANDLER_ATTRIBUTES ALWAYS_INLINE FLATTEN WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_) | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CheckForceReturn() { | 
|  | if (PerformNonStandardReturn<kMonitorState>(Self(), | 
|  | shadow_frame_, | 
|  | ctx_->result, | 
|  | Instrumentation(), | 
|  | Accessor().InsSize(), | 
|  | inst_->GetDexPc(Insns()))) { | 
|  | ExitInterpreterLoop(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandlePendingException() { | 
|  | DCHECK(Self()->IsExceptionPending()); | 
|  | Self()->AllowThreadSuspension(); | 
|  | if (!CheckForceReturn()) { | 
|  | return false; | 
|  | } | 
|  | bool skip_event = shadow_frame_.GetSkipNextExceptionEvent(); | 
|  | shadow_frame_.SetSkipNextExceptionEvent(false); | 
|  | if (!MoveToExceptionHandler(Self(), shadow_frame_, skip_event ? nullptr : Instrumentation())) { | 
|  | /* Structured locking is to be enforced for abnormal termination, too. */ | 
|  | DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_); | 
|  | ctx_->result = JValue(); /* Handled in caller. */ | 
|  | ExitInterpreterLoop(); | 
|  | return false;  // Return to caller. | 
|  | } | 
|  | if (!CheckForceReturn()) { | 
|  | return false; | 
|  | } | 
|  | int32_t displacement = | 
|  | static_cast<int32_t>(shadow_frame_.GetDexPC()) - static_cast<int32_t>(dex_pc_); | 
|  | SetNextInstruction(inst_->RelativeAt(displacement)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool PossiblyHandlePendingExceptionOnInvoke(bool is_exception_pending) { | 
|  | if (UNLIKELY(shadow_frame_.GetForceRetryInstruction())) { | 
|  | /* Don't need to do anything except clear the flag and exception. We leave the */ | 
|  | /* instruction the same so it will be re-executed on the next go-around.       */ | 
|  | DCHECK(inst_->IsInvoke()); | 
|  | shadow_frame_.SetForceRetryInstruction(false); | 
|  | if (UNLIKELY(is_exception_pending)) { | 
|  | DCHECK(Self()->IsExceptionPending()); | 
|  | if (kIsDebugBuild) { | 
|  | LOG(WARNING) << "Suppressing exception for instruction-retry: " | 
|  | << Self()->GetException()->Dump(); | 
|  | } | 
|  | Self()->ClearException(); | 
|  | } | 
|  | SetNextInstruction(inst_); | 
|  | } else if (UNLIKELY(is_exception_pending)) { | 
|  | /* Should have succeeded. */ | 
|  | DCHECK(!shadow_frame_.GetForceRetryInstruction()); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Code to run before each dex instruction. | 
|  | HANDLER_ATTRIBUTES bool Preamble() { | 
|  | /* We need to put this before & after the instrumentation to avoid having to put in a */ | 
|  | /* post-script macro.                                                                 */ | 
|  | if (!CheckForceReturn()) { | 
|  | return false; | 
|  | } | 
|  | if (UNLIKELY(Instrumentation()->HasDexPcListeners())) { | 
|  | uint8_t opcode = inst_->Opcode(inst_data_); | 
|  | bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT); | 
|  | JValue* save_ref = is_move_result_object ? &ctx_->result_register : nullptr; | 
|  | if (UNLIKELY(!DoDexPcMoveEvent(Self(), | 
|  | Accessor(), | 
|  | shadow_frame_, | 
|  | DexPC(), | 
|  | Instrumentation(), | 
|  | save_ref))) { | 
|  | DCHECK(Self()->IsExceptionPending()); | 
|  | // Do not raise exception event if it is caused by other instrumentation event. | 
|  | shadow_frame_.SetSkipNextExceptionEvent(true); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (!CheckForceReturn()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if | 
|  | // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able | 
|  | // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by | 
|  | // jvmti-agents while handling breakpoint or single step events. We had to move this into its own | 
|  | // function because it was making ExecuteSwitchImpl have too large a stack. | 
|  | NO_INLINE static bool DoDexPcMoveEvent(Thread* self, | 
|  | const CodeItemDataAccessor& accessor, | 
|  | const ShadowFrame& shadow_frame, | 
|  | uint32_t dex_pc_, | 
|  | const instrumentation::Instrumentation* instrumentation, | 
|  | JValue* save_ref) | 
|  | REQUIRES_SHARED(Locks::mutator_lock_) { | 
|  | DCHECK(instrumentation->HasDexPcListeners()); | 
|  | StackHandleScope<2> hs(self); | 
|  | Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException())); | 
|  | mirror::Object* null_obj = nullptr; | 
|  | HandleWrapper<mirror::Object> h( | 
|  | hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); | 
|  | self->ClearException(); | 
|  | instrumentation->DexPcMovedEvent(self, | 
|  | shadow_frame.GetThisObject(accessor.InsSize()), | 
|  | shadow_frame.GetMethod(), | 
|  | dex_pc_); | 
|  | if (UNLIKELY(self->IsExceptionPending())) { | 
|  | // We got a new exception in the dex-pc-moved event. | 
|  | // We just let this exception replace the old one. | 
|  | // TODO It would be good to add the old exception to the | 
|  | // suppressed exceptions of the new one if possible. | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (UNLIKELY(!thr.IsNull())) { | 
|  | self->SetException(thr.Get()); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandleReturn(JValue result) { | 
|  | Self()->AllowThreadSuspension(); | 
|  | if (!DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_)) { | 
|  | return false; | 
|  | } | 
|  | if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) && | 
|  | !SendMethodExitEvents(Self(), | 
|  | Instrumentation(), | 
|  | shadow_frame_, | 
|  | shadow_frame_.GetThisObject(Accessor().InsSize()), | 
|  | shadow_frame_.GetMethod(), | 
|  | inst_->GetDexPc(Insns()), | 
|  | result))) { | 
|  | DCHECK(Self()->IsExceptionPending()); | 
|  | // Do not raise exception event if it is caused by other instrumentation event. | 
|  | shadow_frame_.SetSkipNextExceptionEvent(true); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ctx_->result = result; | 
|  | ExitInterpreterLoop(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandleBranch(int32_t offset) { | 
|  | if (UNLIKELY(Self()->ObserveAsyncException())) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (UNLIKELY(Instrumentation()->HasBranchListeners())) { | 
|  | Instrumentation()->Branch(Self(), shadow_frame_.GetMethod(), DexPC(), offset); | 
|  | } | 
|  | // TODO: Do OSR only on back-edges and check if OSR code is ready here. | 
|  | JValue result; | 
|  | if (jit::Jit::MaybeDoOnStackReplacement(Self(), | 
|  | shadow_frame_.GetMethod(), | 
|  | DexPC(), | 
|  | offset, | 
|  | &result)) { | 
|  | ctx_->result = result; | 
|  | ExitInterpreterLoop(); | 
|  | return false; | 
|  | } | 
|  | SetNextInstruction(inst_->RelativeAt(offset)); | 
|  | if (offset <= 0) {  // Back-edge. | 
|  | // Hotness update. | 
|  | jit::Jit* jit = Runtime::Current()->GetJit(); | 
|  | if (jit != nullptr) { | 
|  | jit->AddSamples(Self(), shadow_frame_.GetMethod(), 1, /*with_backedges=*/ true); | 
|  | } | 
|  | // Record new dex pc early to have consistent suspend point at loop header. | 
|  | shadow_frame_.SetDexPC(next_->GetDexPc(Insns())); | 
|  | Self()->AllowThreadSuspension(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandleIf(bool cond, int32_t offset) { | 
|  | return HandleBranch(cond ? offset : Instruction::SizeInCodeUnits(kFormat)); | 
|  | } | 
|  |  | 
|  | #pragma clang diagnostic push | 
|  | #pragma clang diagnostic ignored "-Wfloat-equal" | 
|  |  | 
|  | template<typename T> | 
|  | HANDLER_ATTRIBUTES bool HandleCmpl(T val1, T val2) { | 
|  | int32_t result; | 
|  | if (val1 > val2) { | 
|  | result = 1; | 
|  | } else if (val1 == val2) { | 
|  | result = 0; | 
|  | } else { | 
|  | result = -1; | 
|  | } | 
|  | SetVReg(A(), result); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Returns the same result as the function above. It only differs for NaN values. | 
|  | template<typename T> | 
|  | HANDLER_ATTRIBUTES bool HandleCmpg(T val1, T val2) { | 
|  | int32_t result; | 
|  | if (val1 < val2) { | 
|  | result = -1; | 
|  | } else if (val1 == val2) { | 
|  | result = 0; | 
|  | } else { | 
|  | result = 1; | 
|  | } | 
|  | SetVReg(A(), result); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #pragma clang diagnostic pop | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandleConstString() { | 
|  | ObjPtr<mirror::String> s = ResolveString(Self(), shadow_frame_, dex::StringIndex(B())); | 
|  | if (UNLIKELY(s == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVRegReference(A(), s); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<typename ArrayType, typename SetVRegFn> | 
|  | HANDLER_ATTRIBUTES bool HandleAGet(SetVRegFn setVReg) { | 
|  | ObjPtr<mirror::Object> a = GetVRegReference(B()); | 
|  | if (UNLIKELY(a == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | int32_t index = GetVReg(C()); | 
|  | ObjPtr<ArrayType> array = ObjPtr<ArrayType>::DownCast(a); | 
|  | if (UNLIKELY(!array->CheckIsValidIndex(index))) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | (this->*setVReg)(A(), array->GetWithoutChecks(index)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<typename ArrayType, typename T> | 
|  | HANDLER_ATTRIBUTES bool HandleAPut(T value) { | 
|  | ObjPtr<mirror::Object> a = GetVRegReference(B()); | 
|  | if (UNLIKELY(a == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | int32_t index = GetVReg(C()); | 
|  | ObjPtr<ArrayType> array = ObjPtr<ArrayType>::DownCast(a); | 
|  | if (UNLIKELY(!array->CheckIsValidIndex(index))) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (transaction_active && !CheckWriteConstraint(Self(), array)) { | 
|  | return false; | 
|  | } | 
|  | array->template SetWithoutChecks<transaction_active>(index, value); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<FindFieldType find_type, Primitive::Type field_type> | 
|  | HANDLER_ATTRIBUTES bool HandleGet() { | 
|  | return DoFieldGet<find_type, field_type, do_access_check, transaction_active>( | 
|  | Self(), shadow_frame_, inst_, inst_data_); | 
|  | } | 
|  |  | 
|  | template<Primitive::Type field_type> | 
|  | HANDLER_ATTRIBUTES bool HandleGetQuick() { | 
|  | return DoIGetQuick<field_type>(shadow_frame_, inst_, inst_data_); | 
|  | } | 
|  |  | 
|  | template<FindFieldType find_type, Primitive::Type field_type> | 
|  | HANDLER_ATTRIBUTES bool HandlePut() { | 
|  | return DoFieldPut<find_type, field_type, do_access_check, transaction_active>( | 
|  | Self(), shadow_frame_, inst_, inst_data_); | 
|  | } | 
|  |  | 
|  | template<Primitive::Type field_type> | 
|  | HANDLER_ATTRIBUTES bool HandlePutQuick() { | 
|  | return DoIPutQuick<field_type, transaction_active>( | 
|  | shadow_frame_, inst_, inst_data_); | 
|  | } | 
|  |  | 
|  | template<InvokeType type, bool is_range, bool is_quick = false> | 
|  | HANDLER_ATTRIBUTES bool HandleInvoke() { | 
|  | bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false, is_quick>( | 
|  | Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); | 
|  | return PossiblyHandlePendingExceptionOnInvoke(!success); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool HandleUnused() { | 
|  | UnexpectedOpcode(inst_, shadow_frame_); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NOP() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE() { | 
|  | SetVReg(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_FROM16() { | 
|  | SetVReg(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_16() { | 
|  | SetVReg(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_WIDE() { | 
|  | SetVRegLong(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_WIDE_FROM16() { | 
|  | SetVRegLong(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_WIDE_16() { | 
|  | SetVRegLong(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_OBJECT() { | 
|  | SetVRegReference(A(), GetVRegReference(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_OBJECT_FROM16() { | 
|  | SetVRegReference(A(), GetVRegReference(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_OBJECT_16() { | 
|  | SetVRegReference(A(), GetVRegReference(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_RESULT() { | 
|  | SetVReg(A(), ResultRegister()->GetI()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_RESULT_WIDE() { | 
|  | SetVRegLong(A(), ResultRegister()->GetJ()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_RESULT_OBJECT() { | 
|  | SetVRegReference(A(), ResultRegister()->GetL()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MOVE_EXCEPTION() { | 
|  | ObjPtr<mirror::Throwable> exception = Self()->GetException(); | 
|  | DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction"; | 
|  | SetVRegReference(A(), exception); | 
|  | Self()->ClearException(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RETURN_VOID_NO_BARRIER() { | 
|  | JValue result; | 
|  | return HandleReturn(result); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RETURN_VOID() { | 
|  | QuasiAtomic::ThreadFenceForConstructor(); | 
|  | JValue result; | 
|  | return HandleReturn(result); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RETURN() { | 
|  | JValue result; | 
|  | result.SetJ(0); | 
|  | result.SetI(GetVReg(A())); | 
|  | return HandleReturn(result); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RETURN_WIDE() { | 
|  | JValue result; | 
|  | result.SetJ(GetVRegLong(A())); | 
|  | return HandleReturn(result); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RETURN_OBJECT() { | 
|  | JValue result; | 
|  | Self()->AllowThreadSuspension(); | 
|  | if (!DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_)) { | 
|  | return false; | 
|  | } | 
|  | const size_t ref_idx = A(); | 
|  | ObjPtr<mirror::Object> obj_result = GetVRegReference(ref_idx); | 
|  | if (do_assignability_check && obj_result != nullptr) { | 
|  | ObjPtr<mirror::Class> return_type = shadow_frame_.GetMethod()->ResolveReturnType(); | 
|  | // Re-load since it might have moved. | 
|  | obj_result = GetVRegReference(ref_idx); | 
|  | if (return_type == nullptr) { | 
|  | // Return the pending exception. | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (!obj_result->VerifierInstanceOf(return_type)) { | 
|  | CHECK_LE(Runtime::Current()->GetTargetSdkVersion(), 29u); | 
|  | // This should never happen. | 
|  | std::string temp1, temp2; | 
|  | Self()->ThrowNewExceptionF("Ljava/lang/InternalError;", | 
|  | "Returning '%s' that is not instance of return type '%s'", | 
|  | obj_result->GetClass()->GetDescriptor(&temp1), | 
|  | return_type->GetDescriptor(&temp2)); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | } | 
|  | StackHandleScope<1> hs(Self()); | 
|  | MutableHandle<mirror::Object> h_result(hs.NewHandle(obj_result)); | 
|  | result.SetL(obj_result); | 
|  | if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) && | 
|  | !SendMethodExitEvents(Self(), | 
|  | Instrumentation(), | 
|  | shadow_frame_, | 
|  | shadow_frame_.GetThisObject(Accessor().InsSize()), | 
|  | shadow_frame_.GetMethod(), | 
|  | inst_->GetDexPc(Insns()), | 
|  | h_result))) { | 
|  | DCHECK(Self()->IsExceptionPending()); | 
|  | // Do not raise exception event if it is caused by other instrumentation event. | 
|  | shadow_frame_.SetSkipNextExceptionEvent(true); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | // Re-load since it might have moved or been replaced during the MethodExitEvent. | 
|  | result.SetL(h_result.Get()); | 
|  | ctx_->result = result; | 
|  | ExitInterpreterLoop(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_4() { | 
|  | SetVReg(A(), B()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_16() { | 
|  | SetVReg(A(), B()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST() { | 
|  | SetVReg(A(), B()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_HIGH16() { | 
|  | SetVReg(A(), static_cast<int32_t>(B() << 16)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_WIDE_16() { | 
|  | SetVRegLong(A(), B()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_WIDE_32() { | 
|  | SetVRegLong(A(), B()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_WIDE() { | 
|  | SetVRegLong(A(), inst_->WideVRegB()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_WIDE_HIGH16() { | 
|  | SetVRegLong(A(), static_cast<uint64_t>(B()) << 48); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_STRING() { | 
|  | return HandleConstString(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_STRING_JUMBO() { | 
|  | return HandleConstString(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_CLASS() { | 
|  | ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()), | 
|  | shadow_frame_.GetMethod(), | 
|  | Self(), | 
|  | false, | 
|  | do_access_check); | 
|  | if (UNLIKELY(c == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVRegReference(A(), c); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_METHOD_HANDLE() { | 
|  | ClassLinker* cl = Runtime::Current()->GetClassLinker(); | 
|  | ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(Self(), | 
|  | B(), | 
|  | shadow_frame_.GetMethod()); | 
|  | if (UNLIKELY(mh == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVRegReference(A(), mh); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CONST_METHOD_TYPE() { | 
|  | ClassLinker* cl = Runtime::Current()->GetClassLinker(); | 
|  | ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(Self(), | 
|  | dex::ProtoIndex(B()), | 
|  | shadow_frame_.GetMethod()); | 
|  | if (UNLIKELY(mt == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVRegReference(A(), mt); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MONITOR_ENTER() { | 
|  | if (UNLIKELY(Self()->ObserveAsyncException())) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ObjPtr<mirror::Object> obj = GetVRegReference(A()); | 
|  | if (UNLIKELY(obj == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | DoMonitorEnter<do_assignability_check>(Self(), &shadow_frame_, obj); | 
|  | return !Self()->IsExceptionPending(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MONITOR_EXIT() { | 
|  | if (UNLIKELY(Self()->ObserveAsyncException())) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ObjPtr<mirror::Object> obj = GetVRegReference(A()); | 
|  | if (UNLIKELY(obj == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | DoMonitorExit<do_assignability_check>(Self(), &shadow_frame_, obj); | 
|  | return !Self()->IsExceptionPending(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CHECK_CAST() { | 
|  | ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()), | 
|  | shadow_frame_.GetMethod(), | 
|  | Self(), | 
|  | false, | 
|  | do_access_check); | 
|  | if (UNLIKELY(c == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ObjPtr<mirror::Object> obj = GetVRegReference(A()); | 
|  | if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) { | 
|  | ThrowClassCastException(c, obj->GetClass()); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INSTANCE_OF() { | 
|  | ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(C()), | 
|  | shadow_frame_.GetMethod(), | 
|  | Self(), | 
|  | false, | 
|  | do_access_check); | 
|  | if (UNLIKELY(c == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ObjPtr<mirror::Object> obj = GetVRegReference(B()); | 
|  | SetVReg(A(), (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ARRAY_LENGTH() { | 
|  | ObjPtr<mirror::Object> array = GetVRegReference(B()); | 
|  | if (UNLIKELY(array == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVReg(A(), array->AsArray()->GetLength()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEW_INSTANCE() { | 
|  | ObjPtr<mirror::Object> obj = nullptr; | 
|  | ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()), | 
|  | shadow_frame_.GetMethod(), | 
|  | Self(), | 
|  | false, | 
|  | do_access_check); | 
|  | if (LIKELY(c != nullptr)) { | 
|  | // Don't allow finalizable objects to be allocated during a transaction since these can't | 
|  | // be finalized without a started runtime. | 
|  | if (transaction_active && c->IsFinalizable()) { | 
|  | AbortTransactionF(Self(), | 
|  | "Allocating finalizable object in transaction: %s", | 
|  | c->PrettyDescriptor().c_str()); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); | 
|  | if (UNLIKELY(c->IsStringClass())) { | 
|  | obj = mirror::String::AllocEmptyString(Self(), allocator_type); | 
|  | } else { | 
|  | obj = AllocObjectFromCode(c, Self(), allocator_type); | 
|  | } | 
|  | } | 
|  | if (UNLIKELY(obj == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | obj->GetClass()->AssertInitializedOrInitializingInThread(Self()); | 
|  | SetVRegReference(A(), obj); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEW_ARRAY() { | 
|  | int32_t length = GetVReg(B()); | 
|  | ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check>( | 
|  | dex::TypeIndex(C()), | 
|  | length, | 
|  | shadow_frame_.GetMethod(), | 
|  | Self(), | 
|  | Runtime::Current()->GetHeap()->GetCurrentAllocator()); | 
|  | if (UNLIKELY(obj == nullptr)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | SetVRegReference(A(), obj); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FILLED_NEW_ARRAY() { | 
|  | return DoFilledNewArray<false, do_access_check, transaction_active>( | 
|  | inst_, shadow_frame_, Self(), ResultRegister()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FILLED_NEW_ARRAY_RANGE() { | 
|  | return DoFilledNewArray<true, do_access_check, transaction_active>( | 
|  | inst_, shadow_frame_, Self(), ResultRegister()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FILL_ARRAY_DATA() { | 
|  | const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst_) + B(); | 
|  | const Instruction::ArrayDataPayload* payload = | 
|  | reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr); | 
|  | ObjPtr<mirror::Object> obj = GetVRegReference(A()); | 
|  | if (!FillArrayData(obj, payload)) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | if (transaction_active) { | 
|  | RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool THROW() { | 
|  | if (UNLIKELY(Self()->ObserveAsyncException())) { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | ObjPtr<mirror::Object> exception = GetVRegReference(A()); | 
|  | if (UNLIKELY(exception == nullptr)) { | 
|  | ThrowNullPointerException(); | 
|  | } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { | 
|  | // This should never happen. | 
|  | std::string temp; | 
|  | Self()->ThrowNewExceptionF("Ljava/lang/InternalError;", | 
|  | "Throwing '%s' that is not instance of Throwable", | 
|  | exception->GetClass()->GetDescriptor(&temp)); | 
|  | } else { | 
|  | Self()->SetException(exception->AsThrowable()); | 
|  | } | 
|  | return false;  // Pending exception. | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool GOTO() { | 
|  | return HandleBranch(A()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool GOTO_16() { | 
|  | return HandleBranch(A()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool GOTO_32() { | 
|  | return HandleBranch(A()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool PACKED_SWITCH() { | 
|  | return HandleBranch(DoPackedSwitch(inst_, shadow_frame_, inst_data_)); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPARSE_SWITCH() { | 
|  | return HandleBranch(DoSparseSwitch(inst_, shadow_frame_, inst_data_)); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CMPL_FLOAT() { | 
|  | return HandleCmpl<float>(GetVRegFloat(B()), GetVRegFloat(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CMPG_FLOAT() { | 
|  | return HandleCmpg<float>(GetVRegFloat(B()), GetVRegFloat(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CMPL_DOUBLE() { | 
|  | return HandleCmpl<double>(GetVRegDouble(B()), GetVRegDouble(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CMPG_DOUBLE() { | 
|  | return HandleCmpg<double>(GetVRegDouble(B()), GetVRegDouble(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool CMP_LONG() { | 
|  | return HandleCmpl<int64_t>(GetVRegLong(B()), GetVRegLong(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_EQ() { | 
|  | return HandleIf(GetVReg(A()) == GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_NE() { | 
|  | return HandleIf(GetVReg(A()) != GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_LT() { | 
|  | return HandleIf(GetVReg(A()) < GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_GE() { | 
|  | return HandleIf(GetVReg(A()) >= GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_GT() { | 
|  | return HandleIf(GetVReg(A()) > GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_LE() { | 
|  | return HandleIf(GetVReg(A()) <= GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_EQZ() { | 
|  | return HandleIf(GetVReg(A()) == 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_NEZ() { | 
|  | return HandleIf(GetVReg(A()) != 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_LTZ() { | 
|  | return HandleIf(GetVReg(A()) < 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_GEZ() { | 
|  | return HandleIf(GetVReg(A()) >= 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_GTZ() { | 
|  | return HandleIf(GetVReg(A()) > 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IF_LEZ() { | 
|  | return HandleIf(GetVReg(A()) <= 0, B()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_BOOLEAN() { | 
|  | return HandleAGet<mirror::BooleanArray>(&InstructionHandler::SetVReg); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_BYTE() { | 
|  | return HandleAGet<mirror::ByteArray>(&InstructionHandler::SetVReg); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_CHAR() { | 
|  | return HandleAGet<mirror::CharArray>(&InstructionHandler::SetVReg); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_SHORT() { | 
|  | return HandleAGet<mirror::ShortArray>(&InstructionHandler::SetVReg); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET() { | 
|  | return HandleAGet<mirror::IntArray>(&InstructionHandler::SetVReg); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_WIDE() { | 
|  | return HandleAGet<mirror::LongArray>(&InstructionHandler::SetVRegLong); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AGET_OBJECT() { | 
|  | return HandleAGet<mirror::ObjectArray<mirror::Object>>(&InstructionHandler::SetVRegReference); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_BOOLEAN() { | 
|  | return HandleAPut<mirror::BooleanArray>(GetVReg(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_BYTE() { | 
|  | return HandleAPut<mirror::ByteArray>(GetVReg(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_CHAR() { | 
|  | return HandleAPut<mirror::CharArray>(GetVReg(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_SHORT() { | 
|  | return HandleAPut<mirror::ShortArray>(GetVReg(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT() { | 
|  | return HandleAPut<mirror::IntArray>(GetVReg(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_WIDE() { | 
|  | return HandleAPut<mirror::LongArray>(GetVRegLong(A())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool APUT_OBJECT() { | 
|  | ObjPtr<mirror::Object> a = GetVRegReference(B()); | 
|  | if (UNLIKELY(a == nullptr)) { | 
|  | ThrowNullPointerExceptionFromInterpreter(); | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | int32_t index = GetVReg(C()); | 
|  | ObjPtr<mirror::Object> val = GetVRegReference(A()); | 
|  | ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); | 
|  | if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) { | 
|  | if (transaction_active && | 
|  | (!CheckWriteConstraint(Self(), array) || !CheckWriteValueConstraint(Self(), val))) { | 
|  | return false; | 
|  | } | 
|  | array->SetWithoutChecks<transaction_active>(index, val); | 
|  | } else { | 
|  | return false;  // Pending exception. | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_BOOLEAN() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_BYTE() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_CHAR() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_SHORT() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_WIDE() { | 
|  | return HandleGet<InstancePrimitiveRead, Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_OBJECT() { | 
|  | return HandleGet<InstanceObjectRead, Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_WIDE_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_OBJECT_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_BOOLEAN_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_BYTE_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_CHAR_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IGET_SHORT_QUICK() { | 
|  | return HandleGetQuick<Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_BOOLEAN() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_BYTE() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_CHAR() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_SHORT() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_WIDE() { | 
|  | return HandleGet<StaticPrimitiveRead, Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SGET_OBJECT() { | 
|  | return HandleGet<StaticObjectRead, Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_BOOLEAN() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_BYTE() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_CHAR() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_SHORT() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_WIDE() { | 
|  | return HandlePut<InstancePrimitiveWrite, Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_OBJECT() { | 
|  | return HandlePut<InstanceObjectWrite, Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_BOOLEAN_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_BYTE_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_CHAR_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_SHORT_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_WIDE_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool IPUT_OBJECT_QUICK() { | 
|  | return HandlePutQuick<Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_BOOLEAN() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimBoolean>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_BYTE() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimByte>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_CHAR() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimChar>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_SHORT() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimShort>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimInt>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_WIDE() { | 
|  | return HandlePut<StaticPrimitiveWrite, Primitive::kPrimLong>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SPUT_OBJECT() { | 
|  | return HandlePut<StaticObjectWrite, Primitive::kPrimNot>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_VIRTUAL() { | 
|  | return HandleInvoke<kVirtual, /*is_range=*/ false>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_VIRTUAL_RANGE() { | 
|  | return HandleInvoke<kVirtual, /*is_range=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_SUPER() { | 
|  | return HandleInvoke<kSuper, /*is_range=*/ false>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_SUPER_RANGE() { | 
|  | return HandleInvoke<kSuper, /*is_range=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_DIRECT() { | 
|  | return HandleInvoke<kDirect, /*is_range=*/ false>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_DIRECT_RANGE() { | 
|  | return HandleInvoke<kDirect, /*is_range=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_INTERFACE() { | 
|  | return HandleInvoke<kInterface, /*is_range=*/ false>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_INTERFACE_RANGE() { | 
|  | return HandleInvoke<kInterface, /*is_range=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_STATIC() { | 
|  | return HandleInvoke<kStatic, /*is_range=*/ false>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_STATIC_RANGE() { | 
|  | return HandleInvoke<kStatic, /*is_range=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_VIRTUAL_QUICK() { | 
|  | return HandleInvoke<kVirtual, /*is_range=*/ false, /*is_quick=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_VIRTUAL_RANGE_QUICK() { | 
|  | return HandleInvoke<kVirtual, /*is_range=*/ true, /*is_quick=*/ true>(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_POLYMORPHIC() { | 
|  | DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); | 
|  | bool success = DoInvokePolymorphic</* is_range= */ false>( | 
|  | Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); | 
|  | return PossiblyHandlePendingExceptionOnInvoke(!success); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_POLYMORPHIC_RANGE() { | 
|  | DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); | 
|  | bool success = DoInvokePolymorphic</* is_range= */ true>( | 
|  | Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); | 
|  | return PossiblyHandlePendingExceptionOnInvoke(!success); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_CUSTOM() { | 
|  | DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); | 
|  | bool success = DoInvokeCustom</* is_range= */ false>( | 
|  | Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); | 
|  | return PossiblyHandlePendingExceptionOnInvoke(!success); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INVOKE_CUSTOM_RANGE() { | 
|  | DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); | 
|  | bool success = DoInvokeCustom</* is_range= */ true>( | 
|  | Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); | 
|  | return PossiblyHandlePendingExceptionOnInvoke(!success); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEG_INT() { | 
|  | SetVReg(A(), -GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NOT_INT() { | 
|  | SetVReg(A(), ~GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEG_LONG() { | 
|  | SetVRegLong(A(), -GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NOT_LONG() { | 
|  | SetVRegLong(A(), ~GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEG_FLOAT() { | 
|  | SetVRegFloat(A(), -GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool NEG_DOUBLE() { | 
|  | SetVRegDouble(A(), -GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_LONG() { | 
|  | SetVRegLong(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_FLOAT() { | 
|  | SetVRegFloat(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool LONG_TO_INT() { | 
|  | SetVReg(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool LONG_TO_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool LONG_TO_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FLOAT_TO_INT() { | 
|  | SetVReg(A(), art_float_to_integral<int32_t, float>(GetVRegFloat(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FLOAT_TO_LONG() { | 
|  | SetVRegLong(A(), art_float_to_integral<int64_t, float>(GetVRegFloat(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool FLOAT_TO_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DOUBLE_TO_INT() { | 
|  | SetVReg(A(), art_float_to_integral<int32_t, double>(GetVRegDouble(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DOUBLE_TO_LONG() { | 
|  | SetVRegLong(A(), art_float_to_integral<int64_t, double>(GetVRegDouble(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DOUBLE_TO_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_BYTE() { | 
|  | SetVReg(A(), static_cast<int8_t>(GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_CHAR() { | 
|  | SetVReg(A(), static_cast<uint16_t>(GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool INT_TO_SHORT() { | 
|  | SetVReg(A(), static_cast<int16_t>(GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_INT() { | 
|  | SetVReg(A(), SafeAdd(GetVReg(B()), GetVReg(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_INT() { | 
|  | SetVReg(A(), SafeSub(GetVReg(B()), GetVReg(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_INT() { | 
|  | SetVReg(A(), SafeMul(GetVReg(B()), GetVReg(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_INT() { | 
|  | return DoIntDivide(shadow_frame_, A(), GetVReg(B()), GetVReg(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_INT() { | 
|  | return DoIntRemainder(shadow_frame_, A(), GetVReg(B()), GetVReg(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHL_INT() { | 
|  | SetVReg(A(), GetVReg(B()) << (GetVReg(C()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHR_INT() { | 
|  | SetVReg(A(), GetVReg(B()) >> (GetVReg(C()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool USHR_INT() { | 
|  | SetVReg(A(), static_cast<uint32_t>(GetVReg(B())) >> (GetVReg(C()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_INT() { | 
|  | SetVReg(A(), GetVReg(B()) & GetVReg(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_INT() { | 
|  | SetVReg(A(), GetVReg(B()) | GetVReg(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_INT() { | 
|  | SetVReg(A(), GetVReg(B()) ^ GetVReg(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_LONG() { | 
|  | SetVRegLong(A(), SafeAdd(GetVRegLong(B()), GetVRegLong(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_LONG() { | 
|  | SetVRegLong(A(), SafeSub(GetVRegLong(B()), GetVRegLong(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_LONG() { | 
|  | SetVRegLong(A(), SafeMul(GetVRegLong(B()), GetVRegLong(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_LONG() { | 
|  | return DoLongDivide(shadow_frame_, A(), GetVRegLong(B()), GetVRegLong(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_LONG() { | 
|  | return DoLongRemainder(shadow_frame_, A(), GetVRegLong(B()), GetVRegLong(C())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_LONG() { | 
|  | SetVRegLong(A(), GetVRegLong(B()) & GetVRegLong(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_LONG() { | 
|  | SetVRegLong(A(), GetVRegLong(B()) | GetVRegLong(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_LONG() { | 
|  | SetVRegLong(A(), GetVRegLong(B()) ^ GetVRegLong(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHL_LONG() { | 
|  | SetVRegLong(A(), GetVRegLong(B()) << (GetVReg(C()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHR_LONG() { | 
|  | SetVRegLong(A(), GetVRegLong(B()) >> (GetVReg(C()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool USHR_LONG() { | 
|  | SetVRegLong(A(), static_cast<uint64_t>(GetVRegLong(B())) >> (GetVReg(C()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegFloat(B()) + GetVRegFloat(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegFloat(B()) - GetVRegFloat(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegFloat(B()) * GetVRegFloat(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_FLOAT() { | 
|  | SetVRegFloat(A(), GetVRegFloat(B()) / GetVRegFloat(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_FLOAT() { | 
|  | SetVRegFloat(A(), fmodf(GetVRegFloat(B()), GetVRegFloat(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegDouble(B()) + GetVRegDouble(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegDouble(B()) - GetVRegDouble(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegDouble(B()) * GetVRegDouble(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_DOUBLE() { | 
|  | SetVRegDouble(A(), GetVRegDouble(B()) / GetVRegDouble(C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_DOUBLE() { | 
|  | SetVRegDouble(A(), fmod(GetVRegDouble(B()), GetVRegDouble(C()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_INT_2ADDR() { | 
|  | SetVReg(A(), SafeAdd(GetVReg(A()), GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_INT_2ADDR() { | 
|  | SetVReg(A(), SafeSub(GetVReg(A()), GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_INT_2ADDR() { | 
|  | SetVReg(A(), SafeMul(GetVReg(A()), GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_INT_2ADDR() { | 
|  | return DoIntDivide(shadow_frame_, A(), GetVReg(A()), GetVReg(B())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_INT_2ADDR() { | 
|  | return DoIntRemainder(shadow_frame_, A(), GetVReg(A()), GetVReg(B())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHL_INT_2ADDR() { | 
|  | SetVReg(A(), GetVReg(A()) << (GetVReg(B()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHR_INT_2ADDR() { | 
|  | SetVReg(A(), GetVReg(A()) >> (GetVReg(B()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool USHR_INT_2ADDR() { | 
|  | SetVReg(A(), static_cast<uint32_t>(GetVReg(A())) >> (GetVReg(B()) & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_INT_2ADDR() { | 
|  | SetVReg(A(), GetVReg(A()) & GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_INT_2ADDR() { | 
|  | SetVReg(A(), GetVReg(A()) | GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_INT_2ADDR() { | 
|  | SetVReg(A(), GetVReg(A()) ^ GetVReg(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_LONG_2ADDR() { | 
|  | SetVRegLong(A(), SafeAdd(GetVRegLong(A()), GetVRegLong(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_LONG_2ADDR() { | 
|  | SetVRegLong(A(), SafeSub(GetVRegLong(A()), GetVRegLong(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_LONG_2ADDR() { | 
|  | SetVRegLong(A(), SafeMul(GetVRegLong(A()), GetVRegLong(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_LONG_2ADDR() { | 
|  | return DoLongDivide(shadow_frame_, A(), GetVRegLong(A()), GetVRegLong(B())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_LONG_2ADDR() { | 
|  | return DoLongRemainder(shadow_frame_, A(), GetVRegLong(A()), GetVRegLong(B())); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_LONG_2ADDR() { | 
|  | SetVRegLong(A(), GetVRegLong(A()) & GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_LONG_2ADDR() { | 
|  | SetVRegLong(A(), GetVRegLong(A()) | GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_LONG_2ADDR() { | 
|  | SetVRegLong(A(), GetVRegLong(A()) ^ GetVRegLong(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHL_LONG_2ADDR() { | 
|  | SetVRegLong(A(), GetVRegLong(A()) << (GetVReg(B()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHR_LONG_2ADDR() { | 
|  | SetVRegLong(A(), GetVRegLong(A()) >> (GetVReg(B()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool USHR_LONG_2ADDR() { | 
|  | SetVRegLong(A(), static_cast<uint64_t>(GetVRegLong(A())) >> (GetVReg(B()) & 0x3f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_FLOAT_2ADDR() { | 
|  | SetVRegFloat(A(), GetVRegFloat(A()) + GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_FLOAT_2ADDR() { | 
|  | SetVRegFloat(A(), GetVRegFloat(A()) - GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_FLOAT_2ADDR() { | 
|  | SetVRegFloat(A(), GetVRegFloat(A()) * GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_FLOAT_2ADDR() { | 
|  | SetVRegFloat(A(), GetVRegFloat(A()) / GetVRegFloat(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_FLOAT_2ADDR() { | 
|  | SetVRegFloat(A(), fmodf(GetVRegFloat(A()), GetVRegFloat(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_DOUBLE_2ADDR() { | 
|  | SetVRegDouble(A(), GetVRegDouble(A()) + GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SUB_DOUBLE_2ADDR() { | 
|  | SetVRegDouble(A(), GetVRegDouble(A()) - GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_DOUBLE_2ADDR() { | 
|  | SetVRegDouble(A(), GetVRegDouble(A()) * GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_DOUBLE_2ADDR() { | 
|  | SetVRegDouble(A(), GetVRegDouble(A()) / GetVRegDouble(B())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_DOUBLE_2ADDR() { | 
|  | SetVRegDouble(A(), fmod(GetVRegDouble(A()), GetVRegDouble(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_INT_LIT16() { | 
|  | SetVReg(A(), SafeAdd(GetVReg(B()), C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RSUB_INT() { | 
|  | SetVReg(A(), SafeSub(C(), GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_INT_LIT16() { | 
|  | SetVReg(A(), SafeMul(GetVReg(B()), C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_INT_LIT16() { | 
|  | return DoIntDivide(shadow_frame_, A(), GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_INT_LIT16() { | 
|  | return DoIntRemainder(shadow_frame_, A(), GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_INT_LIT16() { | 
|  | SetVReg(A(), GetVReg(B()) & C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_INT_LIT16() { | 
|  | SetVReg(A(), GetVReg(B()) | C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_INT_LIT16() { | 
|  | SetVReg(A(), GetVReg(B()) ^ C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool ADD_INT_LIT8() { | 
|  | SetVReg(A(), SafeAdd(GetVReg(B()), C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool RSUB_INT_LIT8() { | 
|  | SetVReg(A(), SafeSub(C(), GetVReg(B()))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool MUL_INT_LIT8() { | 
|  | SetVReg(A(), SafeMul(GetVReg(B()), C())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool DIV_INT_LIT8() { | 
|  | return DoIntDivide(shadow_frame_, A(), GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool REM_INT_LIT8() { | 
|  | return DoIntRemainder(shadow_frame_, A(), GetVReg(B()), C()); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool AND_INT_LIT8() { | 
|  | SetVReg(A(), GetVReg(B()) & C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool OR_INT_LIT8() { | 
|  | SetVReg(A(), GetVReg(B()) | C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool XOR_INT_LIT8() { | 
|  | SetVReg(A(), GetVReg(B()) ^ C()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHL_INT_LIT8() { | 
|  | SetVReg(A(), GetVReg(B()) << (C() & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool SHR_INT_LIT8() { | 
|  | SetVReg(A(), GetVReg(B()) >> (C() & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool USHR_INT_LIT8() { | 
|  | SetVReg(A(), static_cast<uint32_t>(GetVReg(B())) >> (C() & 0x1f)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_3E() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_3F() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_40() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_41() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_42() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_43() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_79() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_7A() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F3() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F4() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F5() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F6() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F7() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F8() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | HANDLER_ATTRIBUTES bool UNUSED_F9() { | 
|  | return HandleUnused(); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE InstructionHandler(SwitchImplContext* ctx, | 
|  | const instrumentation::Instrumentation* instrumentation, | 
|  | Thread* self, | 
|  | ShadowFrame& shadow_frame, | 
|  | uint16_t dex_pc, | 
|  | const Instruction* inst, | 
|  | uint16_t inst_data, | 
|  | const Instruction*& next, | 
|  | bool& exit_interpreter_loop) | 
|  | : ctx_(ctx), | 
|  | instrumentation_(instrumentation), | 
|  | self_(self), | 
|  | shadow_frame_(shadow_frame), | 
|  | dex_pc_(dex_pc), | 
|  | inst_(inst), | 
|  | inst_data_(inst_data), | 
|  | next_(next), | 
|  | exit_interpreter_loop_(exit_interpreter_loop) { | 
|  | } | 
|  |  | 
|  | private: | 
|  | static constexpr bool do_assignability_check = do_access_check; | 
|  | static constexpr MonitorState kMonitorState = | 
|  | do_assignability_check ? MonitorState::kCountingMonitors : MonitorState::kNormalMonitors; | 
|  |  | 
|  | ALWAYS_INLINE const CodeItemDataAccessor& Accessor() { return ctx_->accessor; } | 
|  | ALWAYS_INLINE const uint16_t* Insns() { return ctx_->accessor.Insns(); } | 
|  | ALWAYS_INLINE JValue* ResultRegister() { return &ctx_->result_register; } | 
|  |  | 
|  | ALWAYS_INLINE Thread* Self() { | 
|  | DCHECK_EQ(self_, Thread::Current()); | 
|  | return self_; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE int32_t DexPC() { | 
|  | DCHECK_EQ(dex_pc_, shadow_frame_.GetDexPC()); | 
|  | return dex_pc_; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE const instrumentation::Instrumentation* Instrumentation() { | 
|  | return instrumentation_; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE int32_t A() { return inst_->VRegA(kFormat, inst_data_); } | 
|  | ALWAYS_INLINE int32_t B() { return inst_->VRegB(kFormat, inst_data_); } | 
|  | ALWAYS_INLINE int32_t C() { return inst_->VRegC(kFormat); } | 
|  |  | 
|  | int32_t GetVReg(size_t i) const { return shadow_frame_.GetVReg(i); } | 
|  | int64_t GetVRegLong(size_t i) const { return shadow_frame_.GetVRegLong(i); } | 
|  | float GetVRegFloat(size_t i) const { return shadow_frame_.GetVRegFloat(i); } | 
|  | double GetVRegDouble(size_t i) const { return shadow_frame_.GetVRegDouble(i); } | 
|  | ObjPtr<mirror::Object> GetVRegReference(size_t i) const REQUIRES_SHARED(Locks::mutator_lock_) { | 
|  | return shadow_frame_.GetVRegReference(i); | 
|  | } | 
|  |  | 
|  | void SetVReg(size_t i, int32_t val) { shadow_frame_.SetVReg(i, val); } | 
|  | void SetVRegLong(size_t i, int64_t val) { shadow_frame_.SetVRegLong(i, val); } | 
|  | void SetVRegFloat(size_t i, float val) { shadow_frame_.SetVRegFloat(i, val); } | 
|  | void SetVRegDouble(size_t i, double val) { shadow_frame_.SetVRegDouble(i, val); } | 
|  | void SetVRegReference(size_t i, ObjPtr<mirror::Object> val) | 
|  | REQUIRES_SHARED(Locks::mutator_lock_) { | 
|  | shadow_frame_.SetVRegReference(i, val); | 
|  | } | 
|  |  | 
|  | // Set the next instruction to be executed.  It is the 'fall-through' instruction by default. | 
|  | ALWAYS_INLINE void SetNextInstruction(const Instruction* next_inst) { | 
|  | DCHECK_LT(next_inst->GetDexPc(Insns()), Accessor().InsnsSizeInCodeUnits()); | 
|  | next_ = next_inst; | 
|  | } | 
|  |  | 
|  | // Stop interpreting the current method. (return statement, debugger-forced return, OSR, ...) | 
|  | ALWAYS_INLINE void ExitInterpreterLoop() { | 
|  | exit_interpreter_loop_ = true; | 
|  | } | 
|  |  | 
|  | SwitchImplContext* const ctx_; | 
|  | const instrumentation::Instrumentation* const instrumentation_; | 
|  | Thread* const self_; | 
|  | ShadowFrame& shadow_frame_; | 
|  | uint32_t const dex_pc_; | 
|  | const Instruction* const inst_; | 
|  | uint16_t const inst_data_; | 
|  | const Instruction*& next_; | 
|  |  | 
|  | bool& exit_interpreter_loop_; | 
|  | }; | 
|  |  | 
|  | // Don't inline in ASAN. It would create massive stack frame. | 
|  | #if defined(ADDRESS_SANITIZER) || defined(HWADDRESS_SANITIZER) | 
|  | #define ASAN_NO_INLINE NO_INLINE | 
|  | #else | 
|  | #define ASAN_NO_INLINE ALWAYS_INLINE | 
|  | #endif | 
|  |  | 
|  | #define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v)                                \ | 
|  | template<bool do_access_check, bool transaction_active>                                           \ | 
|  | ASAN_NO_INLINE static bool OP_##OPCODE_NAME(                                                      \ | 
|  | SwitchImplContext* ctx,                                                                       \ | 
|  | const instrumentation::Instrumentation* instrumentation,                                      \ | 
|  | Thread* self,                                                                                 \ | 
|  | ShadowFrame& shadow_frame,                                                                    \ | 
|  | uint16_t dex_pc,                                                                              \ | 
|  | const Instruction* inst,                                                                      \ | 
|  | uint16_t inst_data,                                                                           \ | 
|  | const Instruction*& next,                                                                     \ | 
|  | bool& exit) REQUIRES_SHARED(Locks::mutator_lock_) {                                           \ | 
|  | InstructionHandler<do_access_check, transaction_active, Instruction::FORMAT> handler(           \ | 
|  | ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit);             \ | 
|  | return LIKELY(handler.OPCODE_NAME());                                                           \ | 
|  | } | 
|  | DEX_INSTRUCTION_LIST(OPCODE_CASE) | 
|  | #undef OPCODE_CASE | 
|  |  | 
|  | template<bool do_access_check, bool transaction_active> | 
|  | void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { | 
|  | Thread* self = ctx->self; | 
|  | const CodeItemDataAccessor& accessor = ctx->accessor; | 
|  | ShadowFrame& shadow_frame = ctx->shadow_frame; | 
|  | self->VerifyStack(); | 
|  |  | 
|  | uint32_t dex_pc = shadow_frame.GetDexPC(); | 
|  | const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); | 
|  | const uint16_t* const insns = accessor.Insns(); | 
|  | const Instruction* next = Instruction::At(insns + dex_pc); | 
|  |  | 
|  | DCHECK(!shadow_frame.GetForceRetryInstruction()) | 
|  | << "Entered interpreter from invoke without retry instruction being handled!"; | 
|  |  | 
|  | bool const interpret_one_instruction = ctx->interpret_one_instruction; | 
|  | while (true) { | 
|  | const Instruction* const inst = next; | 
|  | dex_pc = inst->GetDexPc(insns); | 
|  | shadow_frame.SetDexPC(dex_pc); | 
|  | TraceExecution(shadow_frame, inst, dex_pc); | 
|  | uint16_t inst_data = inst->Fetch16(0); | 
|  | bool exit = false; | 
|  | bool success;  // Moved outside to keep frames small under asan. | 
|  | if (InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>( | 
|  | ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit). | 
|  | Preamble()) { | 
|  | DCHECK_EQ(self->IsExceptionPending(), inst->Opcode(inst_data) == Instruction::MOVE_EXCEPTION); | 
|  | switch (inst->Opcode(inst_data)) { | 
|  | #define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v)                                \ | 
|  | case OPCODE: {                                                                            \ | 
|  | next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT));             \ | 
|  | success = OP_##OPCODE_NAME<do_access_check, transaction_active>(                        \ | 
|  | ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit);     \ | 
|  | if (success && LIKELY(!interpret_one_instruction)) {                                    \ | 
|  | continue;                                                                             \ | 
|  | }                                                                                       \ | 
|  | break;                                                                                  \ | 
|  | } | 
|  | DEX_INSTRUCTION_LIST(OPCODE_CASE) | 
|  | #undef OPCODE_CASE | 
|  | } | 
|  | } | 
|  | if (exit) { | 
|  | shadow_frame.SetDexPC(dex::kDexNoIndex); | 
|  | return;  // Return statement or debugger forced exit. | 
|  | } | 
|  | if (self->IsExceptionPending()) { | 
|  | if (!InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>( | 
|  | ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit). | 
|  | HandlePendingException()) { | 
|  | shadow_frame.SetDexPC(dex::kDexNoIndex); | 
|  | return;  // Locally unhandled exception - return to caller. | 
|  | } | 
|  | // Continue execution in the catch block. | 
|  | } | 
|  | if (interpret_one_instruction) { | 
|  | shadow_frame.SetDexPC(next->GetDexPc(insns));  // Record where we stopped. | 
|  | ctx->result = ctx->result_register; | 
|  | return; | 
|  | } | 
|  | } | 
|  | }  // NOLINT(readability/fn_size) | 
|  |  | 
|  | }  // namespace interpreter | 
|  | }  // namespace art | 
|  |  | 
|  | #endif  // ART_RUNTIME_INTERPRETER_INTERPRETER_SWITCH_IMPL_INL_H_ |