| /* |
| * Copyright (c) 2019, 2023, 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.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/cpCache.inline.hpp" |
| #include "oops/objArrayOop.inline.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_cpcache_index(ConstantPool::encode_invokedynamic_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)); |
| } |
| } |
| } |