| /* | |
| * Copyright (c) 2019, 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/resolutionErrors.hpp" | |
| #include "classfile/systemDictionary.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "interpreter/bootstrapInfo.hpp" | |
| #include "interpreter/linkResolver.hpp" | |
| #include "jvm.h" | |
| #include "logging/log.hpp" | |
| #include "logging/logStream.hpp" | |
| #include "memory/oopFactory.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "oops/constantPool.inline.hpp" | |
| #include "oops/cpCache.inline.hpp" | |
| #include "oops/objArrayOop.inline.hpp" | |
| #include "oops/resolvedIndyEntry.hpp" | |
| #include "oops/typeArrayOop.inline.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/vmThread.hpp" | |
| //------------------------------------------------------------------------------------------------------------------------ | |
| // Implementation of BootstrapInfo | |
| BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index) | |
| : _pool(pool), | |
| _bss_index(bss_index), | |
| _indy_index(indy_index), | |
| // derived and eagerly cached: | |
| _argc( pool->bootstrap_argument_count_at(bss_index) ), | |
| _name( pool->uncached_name_ref_at(bss_index) ), | |
| _signature( pool->uncached_signature_ref_at(bss_index) ) | |
| { | |
| _is_resolved = false; | |
| assert(pool->tag_at(bss_index).has_bootstrap(), ""); | |
| assert(indy_index == -1 || pool->resolved_indy_entry_at(indy_index)->constant_pool_index() == bss_index, "invalid bootstrap specifier index"); | |
| } | |
| // If there is evidence this call site was already linked, set the | |
| // existing linkage data into result, or throw previous exception. | |
| // Return true if either action is taken, else false. | |
| bool BootstrapInfo::resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS) { | |
| assert(_indy_index != -1, ""); | |
| // Check if method is not null | |
| ResolvedIndyEntry* indy_entry = _pool->resolved_indy_entry_at(_indy_index); | |
| if (indy_entry->method() != nullptr) { | |
| methodHandle method(THREAD, indy_entry->method()); | |
| Handle appendix(THREAD, _pool->resolved_reference_from_indy(_indy_index)); | |
| result.set_handle(vmClasses::MethodHandle_klass(), method, appendix, THREAD); | |
| Exceptions::wrap_dynamic_exception(/* is_indy */ true, CHECK_false); | |
| return true; | |
| } else if (indy_entry->resolution_failed()) { | |
| int encoded_index = ResolutionErrorTable::encode_indy_index(_indy_index); | |
| ConstantPool::throw_resolution_error(_pool, encoded_index, CHECK_false); // Doesn't necessarily need to be resolved yet | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| // Resolve the bootstrap specifier in 3 steps: | |
| // - unpack the BSM by resolving the MH constant | |
| // - obtain the NameAndType description for the condy/indy | |
| // - prepare the BSM's static arguments | |
| Handle BootstrapInfo::resolve_bsm(TRAPS) { | |
| if (_bsm.not_null()) { | |
| return _bsm; | |
| } | |
| bool is_indy = is_method_call(); | |
| // The tag at the bootstrap method index must be a valid method handle or a method handle in error. | |
| // If it is a MethodHandleInError, a resolution error will be thrown which will be wrapped if necessary | |
| // with a BootstrapMethodError. | |
| assert(_pool->tag_at(bsm_index()).is_method_handle() || | |
| _pool->tag_at(bsm_index()).is_method_handle_in_error(), "MH not present, classfile structural constraint"); | |
| oop bsm_oop = _pool->resolve_possibly_cached_constant_at(bsm_index(), THREAD); | |
| Exceptions::wrap_dynamic_exception(is_indy, CHECK_NH); | |
| guarantee(java_lang_invoke_MethodHandle::is_instance(bsm_oop), "classfile must supply a valid BSM"); | |
| _bsm = Handle(THREAD, bsm_oop); | |
| // Obtain NameAndType information | |
| resolve_bss_name_and_type(THREAD); | |
| Exceptions::wrap_dynamic_exception(is_indy, CHECK_NH); | |
| // Prepare static arguments | |
| resolve_args(THREAD); | |
| Exceptions::wrap_dynamic_exception(is_indy, CHECK_NH); | |
| return _bsm; | |
| } | |
| // Resolve metadata from the JVM_Dynamic_info or JVM_InvokeDynamic_info's name and type information. | |
| void BootstrapInfo::resolve_bss_name_and_type(TRAPS) { | |
| assert(_bsm.not_null(), "resolve_bsm first"); | |
| Symbol* name = this->name(); | |
| Symbol* type = this->signature(); | |
| _name_arg = java_lang_String::create_from_symbol(name, CHECK); | |
| if (type->char_at(0) == '(') { | |
| _type_arg = SystemDictionary::find_method_handle_type(type, caller(), CHECK); | |
| } else { | |
| _type_arg = SystemDictionary::find_java_mirror_for_type(type, caller(), SignatureStream::NCDFError, CHECK); | |
| } | |
| } | |
| // Resolve the bootstrap method's static arguments and store the result in _arg_values. | |
| void BootstrapInfo::resolve_args(TRAPS) { | |
| assert(_bsm.not_null(), "resolve_bsm first"); | |
| // if there are no static arguments, return leaving _arg_values as null | |
| if (_argc == 0 && UseBootstrapCallInfo < 2) return; | |
| bool use_BSCI; | |
| switch (UseBootstrapCallInfo) { | |
| default: use_BSCI = true; break; // stress mode | |
| case 0: use_BSCI = false; break; // stress mode | |
| case 1: // normal mode | |
| // If we were to support an alternative mode of BSM invocation, | |
| // we'd convert to pull mode here if the BSM could be a candidate | |
| // for that alternative mode. We can't easily test for things | |
| // like varargs here, but we can get away with approximate testing, | |
| // since the JDK runtime will make up the difference either way. | |
| // For now, exercise the pull-mode path if the BSM is of arity 2, | |
| // or if there is a potential condy loop (see below). | |
| oop mt_oop = java_lang_invoke_MethodHandle::type(_bsm()); | |
| use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2); | |
| break; | |
| } | |
| // Here's a reason to use BSCI even if it wasn't requested: | |
| // If a condy uses a condy argument, we want to avoid infinite | |
| // recursion (condy loops) in the C code. It's OK in Java, | |
| // because Java has stack overflow checking, so we punt | |
| // potentially cyclic cases from C to Java. | |
| if (!use_BSCI && _pool->tag_at(_bss_index).is_dynamic_constant()) { | |
| bool found_unresolved_condy = false; | |
| for (int i = 0; i < _argc; i++) { | |
| int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i); | |
| if (_pool->tag_at(arg_index).is_dynamic_constant()) { | |
| // potential recursion point condy -> condy | |
| bool found_it = false; | |
| _pool->find_cached_constant_at(arg_index, found_it, CHECK); | |
| if (!found_it) { found_unresolved_condy = true; break; } | |
| } | |
| } | |
| if (found_unresolved_condy) | |
| use_BSCI = true; | |
| } | |
| const int SMALL_ARITY = 5; | |
| if (use_BSCI && _argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) { | |
| // If there are only a few arguments, and none of them need linking, | |
| // push them, instead of asking the JDK runtime to turn around and | |
| // pull them, saving a JVM/JDK transition in some simple cases. | |
| bool all_resolved = true; | |
| for (int i = 0; i < _argc; i++) { | |
| bool found_it = false; | |
| int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i); | |
| _pool->find_cached_constant_at(arg_index, found_it, CHECK); | |
| if (!found_it) { all_resolved = false; break; } | |
| } | |
| if (all_resolved) | |
| use_BSCI = false; | |
| } | |
| if (!use_BSCI) { | |
| // return {arg...}; resolution of arguments is done immediately, before JDK code is called | |
| objArrayOop args_oop = oopFactory::new_objArray(vmClasses::Object_klass(), _argc, CHECK); | |
| objArrayHandle args(THREAD, args_oop); | |
| _pool->copy_bootstrap_arguments_at(_bss_index, 0, _argc, args, 0, true, Handle(), CHECK); | |
| oop arg_oop = ((_argc == 1) ? args->obj_at(0) : (oop)nullptr); | |
| // try to discard the singleton array | |
| if (arg_oop != nullptr && !arg_oop->is_array()) { | |
| // JVM treats arrays and nulls specially in this position, | |
| // but other things are just single arguments | |
| _arg_values = Handle(THREAD, arg_oop); | |
| } else { | |
| _arg_values = args; | |
| } | |
| } else { | |
| // return {arg_count, pool_index}; JDK code must pull the arguments as needed | |
| typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK); | |
| ints_oop->int_at_put(0, _argc); | |
| ints_oop->int_at_put(1, _bss_index); | |
| _arg_values = Handle(THREAD, ints_oop); | |
| } | |
| } | |
| // there must be a LinkageError pending; try to save it and then throw | |
| bool BootstrapInfo::save_and_throw_indy_exc(TRAPS) { | |
| assert(HAS_PENDING_EXCEPTION, ""); | |
| assert(_indy_index != -1, ""); | |
| assert(_indy_index >= 0, "Indy index must be decoded by now"); | |
| bool recorded_res_status = _pool->cache()->save_and_throw_indy_exc(_pool, _bss_index, | |
| _indy_index, | |
| pool()->tag_at(_bss_index), | |
| CHECK_false); | |
| return recorded_res_status; | |
| } | |
| void BootstrapInfo::resolve_newly_linked_invokedynamic(CallInfo& result, TRAPS) { | |
| assert(is_resolved(), ""); | |
| result.set_handle(vmClasses::MethodHandle_klass(), resolved_method(), resolved_appendix(), CHECK); | |
| } | |
| void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { | |
| ResourceMark rm; | |
| char what[20]; | |
| st = st ? st : tty; | |
| if (_indy_index > -1) { | |
| os::snprintf_checked(what, sizeof(what), "indy#%d", _indy_index); | |
| } else { | |
| os::snprintf_checked(what, sizeof(what), "condy"); | |
| } | |
| bool have_msg = (msg != nullptr && strlen(msg) > 0); | |
| st->print_cr("%s%sBootstrap in %s %s@CP[%d] %s:%s%s BSMS[%d] BSM@CP[%d]%s argc=%d%s", | |
| (have_msg ? msg : ""), (have_msg ? " " : ""), | |
| caller()->name()->as_C_string(), | |
| what, // "indy#42" or "condy" | |
| _bss_index, | |
| _name->as_C_string(), | |
| _signature->as_C_string(), | |
| (_type_arg.is_null() ? "" : "(resolved)"), | |
| bsms_attr_index(), | |
| bsm_index(), (_bsm.is_null() ? "" : "(resolved)"), | |
| _argc, (_arg_values.is_null() ? "" : "(resolved)")); | |
| if (_argc > 0) { | |
| char argbuf[80]; | |
| argbuf[0] = 0; | |
| for (int i = 0; i < _argc; i++) { | |
| int pos = (int) strlen(argbuf); | |
| if (pos + 20 > (int)sizeof(argbuf)) { | |
| os::snprintf_checked(argbuf + pos, sizeof(argbuf) - pos, "..."); | |
| break; | |
| } | |
| if (i > 0) argbuf[pos++] = ','; | |
| os::snprintf_checked(argbuf+pos, sizeof(argbuf) - pos, "%d", arg_index(i)); | |
| } | |
| st->print_cr(" argument indexes: {%s}", argbuf); | |
| } | |
| if (_bsm.not_null()) { | |
| st->print(" resolved BSM: "); _bsm->print_on(st); | |
| } | |
| // How the array of resolved arguments is printed depends highly | |
| // on how BootstrapInfo::resolve_args structures the array based on | |
| // the use_BSCI setting. | |
| if (_arg_values.not_null()) { | |
| // Find the static arguments within the first element of _arg_values. | |
| objArrayOop static_args = (objArrayOop)_arg_values(); | |
| if (!static_args->is_array()) { | |
| assert(_argc == 1, "Invalid BSM _arg_values for non-array"); | |
| st->print(" resolved arg[0]: "); static_args->print_on(st); | |
| } else if (static_args->is_objArray()) { | |
| int lines = 0; | |
| for (int i = 0; i < _argc; i++) { | |
| oop x = static_args->obj_at(i); | |
| if (x != nullptr) { | |
| if (++lines > 6) { | |
| st->print_cr(" resolved arg[%d]: ...", i); | |
| break; | |
| } | |
| st->print(" resolved arg[%d]: ", i); x->print_on(st); | |
| } | |
| } | |
| } else if (static_args->is_typeArray()) { | |
| typeArrayOop tmp_array = (typeArrayOop) static_args; | |
| assert(tmp_array->length() == 2, "Invalid BSM _arg_values type array"); | |
| st->print_cr(" resolved arg[0]: %d", tmp_array->int_at(0)); | |
| st->print_cr(" resolved arg[1]: %d", tmp_array->int_at(1)); | |
| } | |
| } | |
| } |