You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1913 lines
59 KiB
1913 lines
59 KiB
/*
|
|
* 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 CheckTransactionAbort() {
|
|
if (transaction_active && Runtime::Current()->IsTransactionAborted()) {
|
|
// Transaction abort cannot be caught by catch handlers.
|
|
// Preserve the abort exception while doing non-standard return.
|
|
StackHandleScope<1u> hs(Self());
|
|
Handle<mirror::Throwable> abort_exception = hs.NewHandle(Self()->GetException());
|
|
DCHECK(abort_exception != nullptr);
|
|
DCHECK(abort_exception->GetClass()->DescriptorEquals(Transaction::kAbortExceptionDescriptor));
|
|
Self()->ClearException();
|
|
PerformNonStandardReturn<kMonitorState>(Self(),
|
|
shadow_frame_,
|
|
ctx_->result,
|
|
Instrumentation(),
|
|
Accessor().InsSize(),
|
|
inst_->GetDexPc(Insns()));
|
|
Self()->SetException(abort_exception.Get());
|
|
ExitInterpreterLoop();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool CheckForceReturn() {
|
|
if (shadow_frame_.GetForcePopFrame()) {
|
|
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
|
|
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 (!CheckTransactionAbort()) {
|
|
return false;
|
|
}
|
|
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);
|
|
}
|
|
if (!transaction_active) {
|
|
// 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<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<InvokeType type, bool is_range>
|
|
HANDLER_ATTRIBUTES bool HandleInvoke() {
|
|
bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false>(
|
|
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() {
|
|
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 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 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_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_73() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_79() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_7A() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E3() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E4() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E5() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E6() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E7() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E8() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_E9() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_EA() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_EB() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_EC() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_ED() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_EE() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_EF() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_F0() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_F1() {
|
|
return HandleUnused();
|
|
}
|
|
|
|
HANDLER_ATTRIBUTES bool UNUSED_F2() {
|
|
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_
|