| /* | |
| * Copyright (c) 2020, 2025, 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 "classfile/javaClasses.inline.hpp" | |
| #include "classfile/symbolTable.hpp" | |
| #include "classfile/systemDictionary.hpp" | |
| #include "compiler/compilationPolicy.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "prims/upcallLinker.hpp" | |
| #include "runtime/interfaceSupport.inline.hpp" | |
| #include "runtime/javaCalls.hpp" | |
| #include "runtime/jniHandles.inline.hpp" | |
| #include "utilities/checkedCast.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #define FOREIGN_ABI "jdk/internal/foreign/abi/" | |
| extern struct JavaVM_ main_vm; | |
| // When an upcall is invoked from a thread that is not attached to the VM, we need to attach it, | |
| // and then to detach it at some point later. Detaching a thread as soon as the upcall completes | |
| // is suboptimal, as the same thread could later upcall to Java again, at which point the VM would | |
| // create multiple Java views of the same native thread. For this reason, we use thread local storage | |
| // to keep track of the fact that we have attached a native thread to the VM. When the thread local | |
| // storage is destroyed (which happens when the native threads is terminated), we check if the | |
| // storage has an attached thread and, if so, we detach it from the VM. | |
| struct UpcallContext { | |
| Thread* attachedThread; | |
| ~UpcallContext() { | |
| if (attachedThread != nullptr) { | |
| JavaVM_ *vm = (JavaVM *)(&main_vm); | |
| vm->functions->DetachCurrentThread(vm); | |
| } | |
| } | |
| }; | |
| APPROVED_CPP_THREAD_LOCAL UpcallContext threadContext; | |
| JavaThread* UpcallLinker::maybe_attach_and_get_thread() { | |
| JavaThread* thread = JavaThread::current_or_null(); | |
| if (thread == nullptr) { | |
| JavaVM_ *vm = (JavaVM *)(&main_vm); | |
| JNIEnv* p_env = nullptr; // unused | |
| jint result = vm->functions->AttachCurrentThreadAsDaemon(vm, (void**) &p_env, nullptr); | |
| guarantee(result == JNI_OK, "Could not attach thread for upcall. JNI error code: %d", result); | |
| thread = JavaThread::current(); | |
| threadContext.attachedThread = thread; | |
| assert(!thread->has_last_Java_frame(), "newly-attached thread not expected to have last Java frame"); | |
| } | |
| return thread; | |
| } | |
| // modelled after JavaCallWrapper::JavaCallWrapper | |
| JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { | |
| JavaThread* thread = maybe_attach_and_get_thread(); | |
| guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); | |
| context->thread = thread; | |
| guarantee(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); | |
| // The call to transition_from_native below contains a safepoint check | |
| // which needs the code cache to be writable. | |
| MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, 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, false /* check_asyncs */); | |
| // clear any pending exception in thread, in case someone forgot to check it after a JNI API call. | |
| thread->clear_pending_exception(); | |
| 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 == nullptr 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 | |
| return thread; | |
| } | |
| // modelled after JavaCallWrapper::~JavaCallWrapper | |
| void UpcallLinker::on_exit(UpcallStub::FrameData* context) { | |
| JavaThread* thread = context->thread; | |
| assert(thread == JavaThread::current(), "must still be the same thread"); | |
| // restore previous handle block | |
| thread->set_active_handles(context->old_handles); | |
| DEBUG_ONLY(thread->dec_java_call_counter()); | |
| thread->frame_anchor()->copy(&context->jfa); | |
| // Old thread-local info. has been restored. We are now back in native code. | |
| ThreadStateTransition::transition_from_java(thread, _thread_in_native); | |
| // Release handles after we are marked as being in native code again, since this | |
| // operation might block | |
| JNIHandleBlock::release_block(context->new_handles, thread); | |
| } | |
| void UpcallLinker::handle_uncaught_exception(oop exception) { | |
| tty->print_cr("Uncaught exception:"); | |
| Handle exception_h(Thread::current(), exception); | |
| java_lang_Throwable::print_stack_trace(exception_h, tty); | |
| fatal("Unrecoverable uncaught exception encountered"); | |
| } | |
| JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobject abi, jobject conv, | |
| jboolean needs_return_buffer, jlong ret_buf_size)) | |
| ResourceMark rm(THREAD); | |
| Handle mh_h(THREAD, JNIHandles::resolve(mh)); | |
| jobject mh_j = JNIHandles::make_global(mh_h); | |
| oop type = java_lang_invoke_MethodHandle::type(mh_h()); | |
| // Fill in the signature array, for the calling-convention call. | |
| const int total_out_args = java_lang_invoke_MethodType::ptype_slot_count(type) + 1; // +1 for receiver | |
| bool create_new = true; | |
| TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(type, create_new); | |
| BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_out_args); | |
| BasicType ret_type; | |
| { | |
| int i = 0; | |
| out_sig_bt[i++] = T_OBJECT; // receiver MH | |
| SignatureStream ss(signature); | |
| for (; !ss.at_return_type(); ss.next()) { | |
| out_sig_bt[i++] = ss.type(); // Collect remaining bits of signature | |
| if (ss.type() == T_LONG || ss.type() == T_DOUBLE) | |
| out_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots | |
| } | |
| assert(i == total_out_args, "%d != %d", i, total_out_args); | |
| ret_type = ss.type(); | |
| } | |
| return (jlong) UpcallLinker::make_upcall_stub( | |
| mh_j, signature, out_sig_bt, total_out_args, ret_type, | |
| abi, conv, needs_return_buffer, checked_cast<int>(ret_buf_size)); | |
| JVM_END | |
| #define CC (char*) /*cast a literal from (const char*)*/ | |
| #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) | |
| static JNINativeMethod UL_methods[] = { | |
| {CC "makeUpcallStub", CC "(" "Ljava/lang/invoke/MethodHandle;" "L" FOREIGN_ABI "ABIDescriptor;" "L" FOREIGN_ABI "UpcallLinker$CallRegs;" "ZJ)J", FN_PTR(UL_MakeUpcallStub)}, | |
| }; | |
| /** | |
| * This one function is exported, used by NativeLookup. | |
| */ | |
| JNI_ENTRY(void, JVM_RegisterUpcallLinkerMethods(JNIEnv *env, jclass UL_class)) | |
| ThreadToNativeFromVM ttnfv(thread); | |
| int status = env->RegisterNatives(UL_class, UL_methods, sizeof(UL_methods)/sizeof(JNINativeMethod)); | |
| guarantee(status == JNI_OK && !env->ExceptionCheck(), | |
| "register jdk.internal.foreign.abi.UpcallLinker natives"); | |
| JNI_END |