blob: 44b64504d1c75731ea000b8740b397ba13f2b2b3 [file] [log] [blame]
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/compilationPolicy.hpp"
#include "memory/resourceArea.hpp"
#include "prims/universalUpcallHandler.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.inline.hpp"
#define FOREIGN_ABI "jdk/internal/foreign/abi/"
extern struct JavaVM_ main_vm;
void ProgrammableUpcallHandler::upcall_helper(JavaThread* thread, jobject rec, address buff) {
JavaThread* THREAD = thread; // For exception macros.
ThreadInVMfromNative tiv(THREAD);
const UpcallMethod& upcall_method = instance().upcall_method;
ResourceMark rm(THREAD);
JavaValue result(T_VOID);
JavaCallArguments args(2); // long = 2 slots
args.push_jobject(rec);
args.push_long((jlong) buff);
JavaCalls::call_static(&result, upcall_method.klass, upcall_method.name, upcall_method.sig, &args, CATCH);
}
JavaThread* ProgrammableUpcallHandler::maybe_attach_and_get_thread(bool* should_detach) {
JavaThread* thread = JavaThread::current_or_null();
if (thread == nullptr) {
JavaVM_ *vm = (JavaVM *)(&main_vm);
JNIEnv* p_env = nullptr; // unused
jint result = vm->functions->AttachCurrentThread(vm, (void**) &p_env, nullptr);
guarantee(result == JNI_OK, "Could not attach thread for upcall. JNI error code: %d", result);
*should_detach = true;
thread = JavaThread::current();
assert(!thread->has_last_Java_frame(), "newly-attached thread not expected to have last Java frame");
} else {
*should_detach = false;
}
return thread;
}
void ProgrammableUpcallHandler::detach_current_thread() {
JavaVM_ *vm = (JavaVM *)(&main_vm);
vm->functions->DetachCurrentThread(vm);
}
// modelled after JavaCallWrapper::JavaCallWrapper
JavaThread* ProgrammableUpcallHandler::on_entry(OptimizedEntryBlob::FrameData* context) {
JavaThread* thread = maybe_attach_and_get_thread(&context->should_detach);
context->thread = thread;
assert(thread->can_call_java(), "must be able to call Java");
// Allocate handle block for Java code. This must be done before we change thread_state to _thread_in_Java,
// since it can potentially block.
context->new_handles = JNIHandleBlock::allocate_block(thread);
// After this, we are officially in Java Code. This needs to be done before we change any of the thread local
// info, since we cannot find oops before the new information is set up completely.
ThreadStateTransition::transition_from_native(thread, _thread_in_Java);
// Make sure that we handle asynchronous stops and suspends _before_ we clear all thread state
// in OptimizedEntryBlob::FrameData. This way, we can decide if we need to do any pd actions
// to prepare for stop/suspend (cache sp, or other state).
bool clear_pending_exception = true;
if (thread->has_special_runtime_exit_condition()) {
thread->handle_special_runtime_exit_condition();
if (thread->has_pending_exception()) {
clear_pending_exception = false;
}
}
context->old_handles = thread->active_handles();
// For the profiler, the last_Java_frame information in thread must always be in
// legal state. We have no last Java frame if last_Java_sp == NULL so
// the valid transition is to clear _last_Java_sp and then reset the rest of
// the (platform specific) state.
context->jfa.copy(thread->frame_anchor());
thread->frame_anchor()->clear();
debug_only(thread->inc_java_call_counter());
thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage
// clear any pending exception in thread (native calls start with no exception pending)
if (clear_pending_exception) {
thread->clear_pending_exception();
}
MACOS_AARCH64_ONLY(thread->enable_wx(WXExec));
return thread;
}
// modelled after JavaCallWrapper::~JavaCallWrapper
void ProgrammableUpcallHandler::on_exit(OptimizedEntryBlob::FrameData* context) {
JavaThread* thread = context->thread;
assert(thread == JavaThread::current(), "must still be the same thread");
MACOS_AARCH64_ONLY(thread->enable_wx(WXWrite));
// restore previous handle block
thread->set_active_handles(context->old_handles);
thread->frame_anchor()->zap();
debug_only(thread->dec_java_call_counter());
// Old thread-local info. has been restored. We are now back in native code.
ThreadStateTransition::transition_from_java(thread, _thread_in_native);
thread->frame_anchor()->copy(&context->jfa);
// Release handles after we are marked as being in native code again, since this
// operation might block
JNIHandleBlock::release_block(context->new_handles, thread);
assert(!thread->has_pending_exception(), "Upcall can not throw an exception");
if (context->should_detach) {
detach_current_thread();
}
}
void ProgrammableUpcallHandler::attach_thread_and_do_upcall(jobject rec, address buff) {
bool should_detach = false;
JavaThread* thread = maybe_attach_and_get_thread(&should_detach);
{
MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, thread));
upcall_helper(thread, rec, buff);
}
if (should_detach) {
detach_current_thread();
}
}
const ProgrammableUpcallHandler& ProgrammableUpcallHandler::instance() {
static ProgrammableUpcallHandler handler;
return handler;
}
ProgrammableUpcallHandler::ProgrammableUpcallHandler() {
JavaThread* THREAD = JavaThread::current(); // For exception macros.
ResourceMark rm(THREAD);
Symbol* sym = SymbolTable::new_symbol(FOREIGN_ABI "ProgrammableUpcallHandler");
Klass* k = SystemDictionary::resolve_or_null(sym, Handle(), Handle(), CATCH);
k->initialize(CATCH);
upcall_method.klass = k;
upcall_method.name = SymbolTable::new_symbol("invoke");
upcall_method.sig = SymbolTable::new_symbol("(Ljava/lang/invoke/MethodHandle;J)V");
assert(upcall_method.klass->lookup_method(upcall_method.name, upcall_method.sig) != nullptr,
"Could not find upcall method: %s.%s%s", upcall_method.klass->external_name(),
upcall_method.name->as_C_string(), upcall_method.sig->as_C_string());
}
void ProgrammableUpcallHandler::handle_uncaught_exception(oop exception) {
// Based on CATCH macro
tty->print_cr("Uncaught exception:");
exception->print();
ShouldNotReachHere();
}
JVM_ENTRY(jlong, PUH_AllocateUpcallStub(JNIEnv *env, jclass unused, jobject rec, jobject abi, jobject buffer_layout))
Handle receiver(THREAD, JNIHandles::resolve(rec));
jobject global_rec = JNIHandles::make_global(receiver);
return (jlong) ProgrammableUpcallHandler::generate_upcall_stub(global_rec, abi, buffer_layout);
JNI_END
JVM_ENTRY(jlong, PUH_AllocateOptimizedUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobject abi, jobject conv))
Handle mh_h(THREAD, JNIHandles::resolve(mh));
jobject mh_j = JNIHandles::make_global(mh_h);
oop lform = java_lang_invoke_MethodHandle::form(mh_h());
oop vmentry = java_lang_invoke_LambdaForm::vmentry(lform);
Method* entry = java_lang_invoke_MemberName::vmtarget(vmentry);
const methodHandle mh_entry(THREAD, entry);
assert(entry->method_holder()->is_initialized(), "no clinit barrier");
CompilationPolicy::compile_if_required(mh_entry, CHECK_0);
return (jlong) ProgrammableUpcallHandler::generate_optimized_upcall_stub(mh_j, entry, abi, conv);
JVM_END
JVM_ENTRY(jboolean, PUH_SupportsOptimizedUpcalls(JNIEnv *env, jclass unused))
return (jboolean) ProgrammableUpcallHandler::supports_optimized_upcalls();
JVM_END
#define CC (char*) /*cast a literal from (const char*)*/
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
static JNINativeMethod PUH_methods[] = {
{CC "allocateUpcallStub", CC "(" "Ljava/lang/invoke/MethodHandle;" "L" FOREIGN_ABI "ABIDescriptor;" "L" FOREIGN_ABI "BufferLayout;" ")J", FN_PTR(PUH_AllocateUpcallStub)},
{CC "allocateOptimizedUpcallStub", CC "(" "Ljava/lang/invoke/MethodHandle;" "L" FOREIGN_ABI "ABIDescriptor;" "L" FOREIGN_ABI "ProgrammableUpcallHandler$CallRegs;" ")J", FN_PTR(PUH_AllocateOptimizedUpcallStub)},
{CC "supportsOptimizedUpcalls", CC "()Z", FN_PTR(PUH_SupportsOptimizedUpcalls)},
};
/**
* This one function is exported, used by NativeLookup.
*/
JNI_ENTRY(void, JVM_RegisterProgrammableUpcallHandlerMethods(JNIEnv *env, jclass PUH_class))
ThreadToNativeFromVM ttnfv(thread);
int status = env->RegisterNatives(PUH_class, PUH_methods, sizeof(PUH_methods)/sizeof(JNINativeMethod));
guarantee(status == JNI_OK && !env->ExceptionOccurred(),
"register jdk.internal.foreign.abi.ProgrammableUpcallHandler natives");
JNI_END