| 1 | /* |
| 2 | * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "precompiled.hpp" |
| 26 | #include "jvm.h" |
| 27 | #include "classfile/javaClasses.inline.hpp" |
| 28 | #include "classfile/resolutionErrors.hpp" |
| 29 | #include "interpreter/bootstrapInfo.hpp" |
| 30 | #include "interpreter/linkResolver.hpp" |
| 31 | #include "logging/log.hpp" |
| 32 | #include "logging/logStream.hpp" |
| 33 | #include "memory/oopFactory.hpp" |
| 34 | #include "oops/cpCache.inline.hpp" |
| 35 | #include "oops/objArrayOop.inline.hpp" |
| 36 | #include "oops/typeArrayOop.inline.hpp" |
| 37 | #include "runtime/handles.inline.hpp" |
| 38 | #include "runtime/thread.inline.hpp" |
| 39 | #include "runtime/vmThread.hpp" |
| 40 | |
| 41 | //------------------------------------------------------------------------------------------------------------------------ |
| 42 | // Implementation of BootstrapInfo |
| 43 | |
| 44 | BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index) |
| 45 | : _pool(pool), |
| 46 | _bss_index(bss_index), |
| 47 | _indy_index(indy_index), |
| 48 | // derived and eagerly cached: |
| 49 | _argc( pool->bootstrap_argument_count_at(bss_index) ), |
| 50 | _name( pool->uncached_name_ref_at(bss_index) ), |
| 51 | _signature( pool->uncached_signature_ref_at(bss_index) ) |
| 52 | { |
| 53 | _is_resolved = false; |
| 54 | assert(pool->tag_at(bss_index).has_bootstrap(), "" ); |
| 55 | assert(indy_index == -1 || pool->invokedynamic_bootstrap_ref_index_at(indy_index) == bss_index, "invalid bootstrap specifier index" ); |
| 56 | } |
| 57 | |
| 58 | // If there is evidence this call site was already linked, set the |
| 59 | // existing linkage data into result, or throw previous exception. |
| 60 | // Return true if either action is taken, else false. |
| 61 | bool BootstrapInfo::resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS) { |
| 62 | assert(_indy_index != -1, "" ); |
| 63 | ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); |
| 64 | if (!cpce->is_f1_null()) { |
| 65 | methodHandle method( THREAD, cpce->f1_as_method()); |
| 66 | Handle appendix( THREAD, cpce->appendix_if_resolved(_pool)); |
| 67 | result.set_handle(method, appendix, THREAD); |
| 68 | Exceptions::wrap_dynamic_exception(CHECK_false); |
| 69 | return true; |
| 70 | } else if (cpce->indy_resolution_failed()) { |
| 71 | int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); |
| 72 | ConstantPool::throw_resolution_error(_pool, encoded_index, CHECK_false); |
| 73 | return true; |
| 74 | } else { |
| 75 | return false; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | // Resolve the bootstrap specifier in 3 steps: |
| 80 | // - unpack the BSM by resolving the MH constant |
| 81 | // - obtain the NameAndType description for the condy/indy |
| 82 | // - prepare the BSM's static arguments |
| 83 | Handle BootstrapInfo::resolve_bsm(TRAPS) { |
| 84 | if (_bsm.not_null()) return _bsm; |
| 85 | // The tag at the bootstrap method index must be a valid method handle or a method handle in error. |
| 86 | // If it is a MethodHandleInError, a resolution error will be thrown which will be wrapped if necessary |
| 87 | // with a BootstrapMethodError. |
| 88 | assert(_pool->tag_at(bsm_index()).is_method_handle() || |
| 89 | _pool->tag_at(bsm_index()).is_method_handle_in_error(), "MH not present, classfile structural constraint" ); |
| 90 | oop bsm_oop = _pool->resolve_possibly_cached_constant_at(bsm_index(), THREAD); |
| 91 | Exceptions::wrap_dynamic_exception(CHECK_NH); |
| 92 | guarantee(java_lang_invoke_MethodHandle::is_instance(bsm_oop), "classfile must supply a valid BSM" ); |
| 93 | _bsm = Handle(THREAD, bsm_oop); |
| 94 | |
| 95 | // Obtain NameAndType information |
| 96 | resolve_bss_name_and_type(THREAD); |
| 97 | Exceptions::wrap_dynamic_exception(CHECK_NH); |
| 98 | |
| 99 | // Prepare static arguments |
| 100 | resolve_args(THREAD); |
| 101 | Exceptions::wrap_dynamic_exception(CHECK_NH); |
| 102 | |
| 103 | return _bsm; |
| 104 | } |
| 105 | |
| 106 | // Resolve metadata from the JVM_Dynamic_info or JVM_InvokeDynamic_info's name and type information. |
| 107 | void BootstrapInfo::resolve_bss_name_and_type(TRAPS) { |
| 108 | assert(_bsm.not_null(), "resolve_bsm first" ); |
| 109 | Symbol* name = this->name(); |
| 110 | Symbol* type = this->signature(); |
| 111 | _name_arg = java_lang_String::create_from_symbol(name, CHECK); |
| 112 | if (type->char_at(0) == '(') { |
| 113 | _type_arg = SystemDictionary::find_method_handle_type(type, caller(), CHECK); |
| 114 | } else { |
| 115 | _type_arg = SystemDictionary::find_java_mirror_for_type(type, caller(), SignatureStream::NCDFError, CHECK); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Resolve the bootstrap method's static arguments and store the result in _arg_values. |
| 120 | void BootstrapInfo::resolve_args(TRAPS) { |
| 121 | assert(_bsm.not_null(), "resolve_bsm first" ); |
| 122 | |
| 123 | // if there are no static arguments, return leaving _arg_values as null |
| 124 | if (_argc == 0 && UseBootstrapCallInfo < 2) return; |
| 125 | |
| 126 | bool use_BSCI; |
| 127 | switch (UseBootstrapCallInfo) { |
| 128 | default: use_BSCI = true; break; // stress mode |
| 129 | case 0: use_BSCI = false; break; // stress mode |
| 130 | case 1: // normal mode |
| 131 | // If we were to support an alternative mode of BSM invocation, |
| 132 | // we'd convert to pull mode here if the BSM could be a candidate |
| 133 | // for that alternative mode. We can't easily test for things |
| 134 | // like varargs here, but we can get away with approximate testing, |
| 135 | // since the JDK runtime will make up the difference either way. |
| 136 | // For now, exercise the pull-mode path if the BSM is of arity 2, |
| 137 | // or if there is a potential condy loop (see below). |
| 138 | oop mt_oop = java_lang_invoke_MethodHandle::type(_bsm()); |
| 139 | use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2); |
| 140 | break; |
| 141 | } |
| 142 | |
| 143 | // Here's a reason to use BSCI even if it wasn't requested: |
| 144 | // If a condy uses a condy argument, we want to avoid infinite |
| 145 | // recursion (condy loops) in the C code. It's OK in Java, |
| 146 | // because Java has stack overflow checking, so we punt |
| 147 | // potentially cyclic cases from C to Java. |
| 148 | if (!use_BSCI && _pool->tag_at(_bss_index).is_dynamic_constant()) { |
| 149 | bool found_unresolved_condy = false; |
| 150 | for (int i = 0; i < _argc; i++) { |
| 151 | int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i); |
| 152 | if (_pool->tag_at(arg_index).is_dynamic_constant()) { |
| 153 | // potential recursion point condy -> condy |
| 154 | bool found_it = false; |
| 155 | _pool->find_cached_constant_at(arg_index, found_it, CHECK); |
| 156 | if (!found_it) { found_unresolved_condy = true; break; } |
| 157 | } |
| 158 | } |
| 159 | if (found_unresolved_condy) |
| 160 | use_BSCI = true; |
| 161 | } |
| 162 | |
| 163 | const int SMALL_ARITY = 5; |
| 164 | if (use_BSCI && _argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) { |
| 165 | // If there are only a few arguments, and none of them need linking, |
| 166 | // push them, instead of asking the JDK runtime to turn around and |
| 167 | // pull them, saving a JVM/JDK transition in some simple cases. |
| 168 | bool all_resolved = true; |
| 169 | for (int i = 0; i < _argc; i++) { |
| 170 | bool found_it = false; |
| 171 | int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i); |
| 172 | _pool->find_cached_constant_at(arg_index, found_it, CHECK); |
| 173 | if (!found_it) { all_resolved = false; break; } |
| 174 | } |
| 175 | if (all_resolved) |
| 176 | use_BSCI = false; |
| 177 | } |
| 178 | |
| 179 | if (!use_BSCI) { |
| 180 | // return {arg...}; resolution of arguments is done immediately, before JDK code is called |
| 181 | objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), _argc, CHECK); |
| 182 | objArrayHandle args(THREAD, args_oop); |
| 183 | _pool->copy_bootstrap_arguments_at(_bss_index, 0, _argc, args, 0, true, Handle(), CHECK); |
| 184 | oop arg_oop = ((_argc == 1) ? args->obj_at(0) : (oop)NULL); |
| 185 | // try to discard the singleton array |
| 186 | if (arg_oop != NULL && !arg_oop->is_array()) { |
| 187 | // JVM treats arrays and nulls specially in this position, |
| 188 | // but other things are just single arguments |
| 189 | _arg_values = Handle(THREAD, arg_oop); |
| 190 | } else { |
| 191 | _arg_values = args; |
| 192 | } |
| 193 | } else { |
| 194 | // return {arg_count, pool_index}; JDK code must pull the arguments as needed |
| 195 | typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK); |
| 196 | ints_oop->int_at_put(0, _argc); |
| 197 | ints_oop->int_at_put(1, _bss_index); |
| 198 | _arg_values = Handle(THREAD, ints_oop); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // there must be a LinkageError pending; try to save it and then throw |
| 203 | bool BootstrapInfo::save_and_throw_indy_exc(TRAPS) { |
| 204 | assert(HAS_PENDING_EXCEPTION, "" ); |
| 205 | assert(_indy_index != -1, "" ); |
| 206 | ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); |
| 207 | int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); |
| 208 | bool recorded_res_status = cpce->save_and_throw_indy_exc(_pool, _bss_index, |
| 209 | encoded_index, |
| 210 | pool()->tag_at(_bss_index), |
| 211 | CHECK_false); |
| 212 | return recorded_res_status; |
| 213 | } |
| 214 | |
| 215 | void BootstrapInfo::resolve_newly_linked_invokedynamic(CallInfo& result, TRAPS) { |
| 216 | assert(is_resolved(), "" ); |
| 217 | result.set_handle(resolved_method(), resolved_appendix(), CHECK); |
| 218 | } |
| 219 | |
| 220 | void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { |
| 221 | ResourceMark rm; |
| 222 | char what[20]; |
| 223 | st = st ? st : tty; |
| 224 | |
| 225 | if (_indy_index != -1) |
| 226 | sprintf(what, "indy#%d" , decode_indy_index()); |
| 227 | else |
| 228 | sprintf(what, "condy" ); |
| 229 | bool have_msg = (msg != NULL && strlen(msg) > 0); |
| 230 | st->print_cr("%s%sBootstrap in %s %s@CP[%d] %s:%s%s BSMS[%d] BSM@CP[%d]%s argc=%d%s" , |
| 231 | (have_msg ? msg : "" ), (have_msg ? " " : "" ), |
| 232 | caller()->name()->as_C_string(), |
| 233 | what, // "indy#42" or "condy" |
| 234 | _bss_index, |
| 235 | _name->as_C_string(), |
| 236 | _signature->as_C_string(), |
| 237 | (_type_arg.is_null() ? "" : "(resolved)" ), |
| 238 | bsms_attr_index(), |
| 239 | bsm_index(), (_bsm.is_null() ? "" : "(resolved)" ), |
| 240 | _argc, (_arg_values.is_null() ? "" : "(resolved)" )); |
| 241 | if (_argc > 0) { |
| 242 | char argbuf[80]; |
| 243 | argbuf[0] = 0; |
| 244 | for (int i = 0; i < _argc; i++) { |
| 245 | int pos = (int) strlen(argbuf); |
| 246 | if (pos + 20 > (int)sizeof(argbuf)) { |
| 247 | sprintf(argbuf + pos, "..." ); |
| 248 | break; |
| 249 | } |
| 250 | if (i > 0) argbuf[pos++] = ','; |
| 251 | sprintf(argbuf+pos, "%d" , arg_index(i)); |
| 252 | } |
| 253 | st->print_cr(" argument indexes: {%s}" , argbuf); |
| 254 | } |
| 255 | if (_bsm.not_null()) { |
| 256 | st->print(" resolved BSM: " ); _bsm->print(); |
| 257 | } |
| 258 | |
| 259 | // How the array of resolved arguments is printed depends highly |
| 260 | // on how BootstrapInfo::resolve_args structures the array based on |
| 261 | // the use_BSCI setting. |
| 262 | if (_arg_values.not_null()) { |
| 263 | // Find the static arguments within the first element of _arg_values. |
| 264 | objArrayOop static_args = (objArrayOop)_arg_values(); |
| 265 | if (!static_args->is_array()) { |
| 266 | assert(_argc == 1, "Invalid BSM _arg_values for non-array" ); |
| 267 | st->print(" resolved arg[0]: " ); static_args->print(); |
| 268 | } else if (static_args->is_objArray()) { |
| 269 | int lines = 0; |
| 270 | for (int i = 0; i < _argc; i++) { |
| 271 | oop x = static_args->obj_at(i); |
| 272 | if (x != NULL) { |
| 273 | if (++lines > 6) { |
| 274 | st->print_cr(" resolved arg[%d]: ..." , i); |
| 275 | break; |
| 276 | } |
| 277 | st->print(" resolved arg[%d]: " , i); x->print(); |
| 278 | } |
| 279 | } |
| 280 | } else if (static_args->is_typeArray()) { |
| 281 | typeArrayOop tmp_array = (typeArrayOop) static_args; |
| 282 | assert(tmp_array->length() == 2, "Invalid BSM _arg_values type array" ); |
| 283 | st->print_cr(" resolved arg[0]: %d" , tmp_array->int_at(0)); |
| 284 | st->print_cr(" resolved arg[1]: %d" , tmp_array->int_at(1)); |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | |