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 | |