blob: ac5d6f94c690907e7a8fed3dc14b313bba4a78d6 [file] [log] [blame]
/*
* 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.
*/
#if !defined(ART_USE_LLVM_COMPILER)
#include "callee_save_frame.h"
#endif
#include "dex_instruction.h"
#include "object.h"
#include "object_utils.h"
#if defined(ART_USE_LLVM_COMPILER)
#include "nth_caller_visitor.h"
#endif
// Architecture specific assembler helper to deliver exception.
extern "C" void art_deliver_exception_from_code(void*);
namespace art {
#if !defined(ART_USE_LLVM_COMPILER)
// Lazily resolve a method. Called by stub code.
const void* UnresolvedDirectMethodTrampolineFromCode(Method* called, Method** sp, Thread* thread,
Runtime::TrampolineType type) {
#if defined(__arm__)
// On entry the stack pointed by sp is:
// | argN | |
// | ... | |
// | arg4 | |
// | arg3 spill | | Caller's frame
// | arg2 spill | |
// | arg1 spill | |
// | Method* | ---
// | LR |
// | ... | callee saves
// | R3 | arg3
// | R2 | arg2
// | R1 | arg1
// | R0 |
// | Method* | <- sp
DCHECK_EQ(48U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
Method** caller_sp = reinterpret_cast<Method**>(reinterpret_cast<byte*>(sp) + 48);
uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + kPointerSize);
uintptr_t caller_pc = regs[10];
#elif defined(__i386__)
// On entry the stack pointed by sp is:
// | argN | |
// | ... | |
// | arg4 | |
// | arg3 spill | | Caller's frame
// | arg2 spill | |
// | arg1 spill | |
// | Method* | ---
// | Return |
// | EBP,ESI,EDI | callee saves
// | EBX | arg3
// | EDX | arg2
// | ECX | arg1
// | EAX/Method* | <- sp
DCHECK_EQ(32U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
Method** caller_sp = reinterpret_cast<Method**>(reinterpret_cast<byte*>(sp) + 32);
uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp));
uintptr_t caller_pc = regs[7];
#else
UNIMPLEMENTED(FATAL);
Method** caller_sp = NULL;
uintptr_t* regs = NULL;
uintptr_t caller_pc = 0;
#endif
FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
// Start new JNI local reference state
JNIEnvExt* env = thread->GetJniEnv();
ScopedJniEnvLocalRefState env_state(env);
// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
Method* caller = *caller_sp;
bool is_static;
bool is_virtual;
uint32_t dex_method_idx;
const char* shorty;
uint32_t shorty_len;
if (type == Runtime::kUnknownMethod) {
DCHECK(called->IsRuntimeMethod());
// less two as return address may span into next dex instruction
uint32_t dex_pc = caller->ToDexPC(caller_pc - 2);
const DexFile::CodeItem* code = MethodHelper(caller).GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
is_static = (instr_code == Instruction::INVOKE_STATIC) ||
(instr_code == Instruction::INVOKE_STATIC_RANGE);
is_virtual = (instr_code == Instruction::INVOKE_VIRTUAL) ||
(instr_code == Instruction::INVOKE_VIRTUAL_RANGE) ||
(instr_code == Instruction::INVOKE_SUPER) ||
(instr_code == Instruction::INVOKE_SUPER_RANGE);
DCHECK(is_static || is_virtual || (instr_code == Instruction::INVOKE_DIRECT) ||
(instr_code == Instruction::INVOKE_DIRECT_RANGE));
DecodedInstruction dec_insn(instr);
dex_method_idx = dec_insn.vB;
shorty = linker->MethodShorty(dex_method_idx, caller, &shorty_len);
} else {
DCHECK(!called->IsRuntimeMethod());
is_static = type == Runtime::kStaticMethod;
is_virtual = false;
dex_method_idx = called->GetDexMethodIndex();
MethodHelper mh(called);
shorty = mh.GetShorty();
shorty_len = mh.GetShortyLength();
}
#if !defined(__i386__)
// Discover shorty (avoid GCs)
size_t args_in_regs = 0;
for (size_t i = 1; i < shorty_len; i++) {
char c = shorty[i];
args_in_regs = args_in_regs + (c == 'J' || c == 'D' ? 2 : 1);
if (args_in_regs > 3) {
args_in_regs = 3;
break;
}
}
// Place into local references incoming arguments from the caller's register arguments
size_t cur_arg = 1; // skip method_idx in R0, first arg is in R1
if (!is_static) {
Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
cur_arg++;
if (args_in_regs < 3) {
// If we thought we had fewer than 3 arguments in registers, account for the receiver
args_in_regs++;
}
AddLocalReference<jobject>(env, obj);
}
size_t shorty_index = 1; // skip return value
// Iterate while arguments and arguments in registers (less 1 from cur_arg which is offset to skip
// R0)
while ((cur_arg - 1) < args_in_regs && shorty_index < shorty_len) {
char c = shorty[shorty_index];
shorty_index++;
if (c == 'L') {
Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
AddLocalReference<jobject>(env, obj);
}
cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
}
// Place into local references incoming arguments from the caller's stack arguments
cur_arg += 11; // skip LR, Method* and spills for R1 to R3 and callee saves
while (shorty_index < shorty_len) {
char c = shorty[shorty_index];
shorty_index++;
if (c == 'L') {
Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
AddLocalReference<jobject>(env, obj);
}
cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
}
#endif
// Resolve method filling in dex cache
if (type == Runtime::kUnknownMethod) {
called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
}
const void* code = NULL;
if (LIKELY(!thread->IsExceptionPending())) {
if (LIKELY(called->IsDirect() == !is_virtual)) {
// Ensure that the called method's class is initialized.
Class* called_class = called->GetDeclaringClass();
linker->EnsureInitialized(called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
code = called->GetCode();
} else if (called_class->IsInitializing()) {
if (is_static) {
// Class is still initializing, go to oat and grab code (trampoline must be left in place
// until class is initialized to stop races between threads).
code = linker->GetOatCodeFor(called);
} else {
// No trampoline for non-static methods.
code = called->GetCode();
}
} else {
DCHECK(called_class->IsErroneous());
}
} else {
// Direct method has been made virtual
thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
"Expected direct method but found virtual: %s",
PrettyMethod(called, true).c_str());
}
}
if (UNLIKELY(code == NULL)) {
// Something went wrong in ResolveMethod or EnsureInitialized,
// go into deliver exception with the pending exception in r0
code = reinterpret_cast<void*>(art_deliver_exception_from_code);
regs[0] = reinterpret_cast<uintptr_t>(thread->GetException());
thread->ClearException();
} else {
// Expect class to at least be initializing.
DCHECK(called->GetDeclaringClass()->IsInitializing());
// Don't want infinite recursion.
DCHECK(code != Runtime::Current()->GetResolutionStubArray(Runtime::kUnknownMethod)->GetData());
// Set up entry into main method
regs[0] = reinterpret_cast<uintptr_t>(called);
}
return code;
}
#else // ART_USE_LLVM_COMPILER
const void* UnresolvedDirectMethodTrampolineFromCode(Method* called, Method** called_addr,
Thread* thread, Runtime::TrampolineType type) {
NthCallerVisitor visitor(0);
thread->WalkStack(&visitor);
Method* caller = visitor.caller;
ClassLinker* linker = Runtime::Current()->GetClassLinker();
bool is_static;
bool is_virtual;
uint32_t dex_method_idx;
if (type == Runtime::kUnknownMethod) {
DCHECK(called->IsRuntimeMethod());
// less two as return address may span into next dex instruction
uint32_t dex_pc = static_cast<uint32_t>(visitor.pc);
const DexFile::CodeItem* code = MethodHelper(caller).GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
is_static = (instr_code == Instruction::INVOKE_STATIC) ||
(instr_code == Instruction::INVOKE_STATIC_RANGE);
is_virtual = (instr_code == Instruction::INVOKE_VIRTUAL) ||
(instr_code == Instruction::INVOKE_VIRTUAL_RANGE) ||
(instr_code == Instruction::INVOKE_SUPER) ||
(instr_code == Instruction::INVOKE_SUPER_RANGE);
DCHECK(is_static || is_virtual || (instr_code == Instruction::INVOKE_DIRECT) ||
(instr_code == Instruction::INVOKE_DIRECT_RANGE));
DecodedInstruction dec_insn(instr);
dex_method_idx = dec_insn.vB;
} else {
DCHECK(!called->IsRuntimeMethod());
is_static = type == Runtime::kStaticMethod;
is_virtual = false;
dex_method_idx = called->GetDexMethodIndex();
}
if (type == Runtime::kUnknownMethod) {
called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
}
const void* code = NULL;
if (LIKELY(!thread->IsExceptionPending())) {
if (LIKELY(called->IsDirect() == !is_virtual)) {
// Ensure that the called method's class is initialized.
Class* called_class = called->GetDeclaringClass();
linker->EnsureInitialized(called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
code = called->GetCode();
// TODO: remove this after we solve the link issue.
{ // for lazy link.
if (code == NULL) {
code = linker->GetOatCodeFor(called);
}
}
} else if (called_class->IsInitializing()) {
if (is_static) {
// Class is still initializing, go to oat and grab code (trampoline must be left in place
// until class is initialized to stop races between threads).
code = linker->GetOatCodeFor(called);
} else {
// No trampoline for non-static methods.
code = called->GetCode();
// TODO: remove this after we solve the link issue.
{ // for lazy link.
if (code == NULL) {
code = linker->GetOatCodeFor(called);
}
}
}
} else {
DCHECK(called_class->IsErroneous());
}
} else {
// Direct method has been made virtual
thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
"Expected direct method but found virtual: %s",
PrettyMethod(called, true).c_str());
}
}
if (LIKELY(code != NULL)) {
// Expect class to at least be initializing.
DCHECK(called->GetDeclaringClass()->IsInitializing());
// Don't want infinite recursion.
DCHECK(code != Runtime::Current()->GetResolutionStubArray(Runtime::kUnknownMethod)->GetData());
// Set up entry into main method
*called_addr = called;
}
return code;
}
#endif // ART_USE_LLVM_COMPILER
#if !defined(ART_USE_LLVM_COMPILER)
// Called by the AbstractMethodError. Called by stub code.
extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp) {
FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
"abstract method \"%s\"", PrettyMethod(method).c_str());
thread->DeliverException();
}
#else // ART_USE_LLVM_COMPILER
extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method**) {
thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
"abstract method \"%s\"", PrettyMethod(method).c_str());
}
#endif // ART_USE_LLVM_COMPILER
} // namespace art