Mon, 04 Jan 2010 15:52:40 +0100
6894206: JVM needs a way to traverse method handle structures
Summary: We need a way to walk chained method handles in the JVM to call the right methods and to generate required bytecode adapters for the compilers.
Reviewed-by: kvn
twisti@1568 | 1 | /* |
twisti@1568 | 2 | * Copyright 2008-2010 Sun Microsystems, Inc. All Rights Reserved. |
twisti@1568 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
twisti@1568 | 4 | * |
twisti@1568 | 5 | * This code is free software; you can redistribute it and/or modify it |
twisti@1568 | 6 | * under the terms of the GNU General Public License version 2 only, as |
twisti@1568 | 7 | * published by the Free Software Foundation. |
twisti@1568 | 8 | * |
twisti@1568 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
twisti@1568 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
twisti@1568 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
twisti@1568 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
twisti@1568 | 13 | * accompanied this code). |
twisti@1568 | 14 | * |
twisti@1568 | 15 | * You should have received a copy of the GNU General Public License version |
twisti@1568 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
twisti@1568 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
twisti@1568 | 18 | * |
twisti@1568 | 19 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
twisti@1568 | 20 | * CA 95054 USA or visit www.sun.com if you need additional information or |
twisti@1568 | 21 | * have any questions. |
twisti@1568 | 22 | * |
twisti@1568 | 23 | */ |
twisti@1568 | 24 | |
twisti@1568 | 25 | /* |
twisti@1568 | 26 | * JSR 292 reference implementation: method handle structure analysis |
twisti@1568 | 27 | */ |
twisti@1568 | 28 | |
twisti@1568 | 29 | #include "incls/_precompiled.incl" |
twisti@1568 | 30 | #include "incls/_methodHandleWalk.cpp.incl" |
twisti@1568 | 31 | |
twisti@1568 | 32 | void MethodHandleChain::set_method_handle(Handle mh, TRAPS) { |
twisti@1568 | 33 | if (!java_dyn_MethodHandle::is_instance(mh())) lose("bad method handle", CHECK); |
twisti@1568 | 34 | |
twisti@1568 | 35 | // set current method handle and unpack partially |
twisti@1568 | 36 | _method_handle = mh; |
twisti@1568 | 37 | _is_last = false; |
twisti@1568 | 38 | _is_bound = false; |
twisti@1568 | 39 | _arg_slot = -1; |
twisti@1568 | 40 | _arg_type = T_VOID; |
twisti@1568 | 41 | _conversion = -1; |
twisti@1568 | 42 | _last_invoke = Bytecodes::_nop; //arbitrary non-garbage |
twisti@1568 | 43 | |
twisti@1568 | 44 | if (sun_dyn_DirectMethodHandle::is_instance(mh())) { |
twisti@1568 | 45 | set_last_method(mh(), THREAD); |
twisti@1568 | 46 | return; |
twisti@1568 | 47 | } |
twisti@1568 | 48 | if (sun_dyn_AdapterMethodHandle::is_instance(mh())) { |
twisti@1568 | 49 | _conversion = AdapterMethodHandle_conversion(); |
twisti@1568 | 50 | assert(_conversion != -1, "bad conv value"); |
twisti@1568 | 51 | assert(sun_dyn_BoundMethodHandle::is_instance(mh()), "also BMH"); |
twisti@1568 | 52 | } |
twisti@1568 | 53 | if (sun_dyn_BoundMethodHandle::is_instance(mh())) { |
twisti@1568 | 54 | if (!is_adapter()) // keep AMH and BMH separate in this model |
twisti@1568 | 55 | _is_bound = true; |
twisti@1568 | 56 | _arg_slot = BoundMethodHandle_vmargslot(); |
twisti@1568 | 57 | oop target = MethodHandle_vmtarget_oop(); |
twisti@1568 | 58 | if (!is_bound() || java_dyn_MethodHandle::is_instance(target)) { |
twisti@1568 | 59 | _arg_type = compute_bound_arg_type(target, NULL, _arg_slot, CHECK); |
twisti@1568 | 60 | } else if (target != NULL && target->is_method()) { |
twisti@1568 | 61 | _arg_type = compute_bound_arg_type(NULL, (methodOop)target, _arg_slot, CHECK); |
twisti@1568 | 62 | set_last_method(mh(), CHECK); |
twisti@1568 | 63 | } else { |
twisti@1568 | 64 | _is_bound = false; // lose! |
twisti@1568 | 65 | } |
twisti@1568 | 66 | } |
twisti@1568 | 67 | if (is_bound() && _arg_type == T_VOID) { |
twisti@1568 | 68 | lose("bad vmargslot", CHECK); |
twisti@1568 | 69 | } |
twisti@1568 | 70 | if (!is_bound() && !is_adapter()) { |
twisti@1568 | 71 | lose("unrecognized MH type", CHECK); |
twisti@1568 | 72 | } |
twisti@1568 | 73 | } |
twisti@1568 | 74 | |
twisti@1568 | 75 | void MethodHandleChain::set_last_method(oop target, TRAPS) { |
twisti@1568 | 76 | _is_last = true; |
twisti@1568 | 77 | klassOop receiver_limit_oop = NULL; |
twisti@1568 | 78 | int flags = 0; |
twisti@1568 | 79 | methodOop m = MethodHandles::decode_method(target, receiver_limit_oop, flags); |
twisti@1568 | 80 | _last_method = methodHandle(THREAD, m); |
twisti@1568 | 81 | if ((flags & MethodHandles::_dmf_has_receiver) == 0) |
twisti@1568 | 82 | _last_invoke = Bytecodes::_invokestatic; |
twisti@1568 | 83 | else if ((flags & MethodHandles::_dmf_does_dispatch) == 0) |
twisti@1568 | 84 | _last_invoke = Bytecodes::_invokespecial; |
twisti@1568 | 85 | else if ((flags & MethodHandles::_dmf_from_interface) != 0) |
twisti@1568 | 86 | _last_invoke = Bytecodes::_invokeinterface; |
twisti@1568 | 87 | else |
twisti@1568 | 88 | _last_invoke = Bytecodes::_invokevirtual; |
twisti@1568 | 89 | } |
twisti@1568 | 90 | |
twisti@1568 | 91 | BasicType MethodHandleChain::compute_bound_arg_type(oop target, methodOop m, int arg_slot, TRAPS) { |
twisti@1568 | 92 | // There is no direct indication of whether the argument is primitive or not. |
twisti@1568 | 93 | // It is implied by the _vmentry code, and by the MethodType of the target. |
twisti@1568 | 94 | // FIXME: Make it explicit MethodHandleImpl refactors out from MethodHandle |
twisti@1568 | 95 | BasicType arg_type = T_VOID; |
twisti@1568 | 96 | if (target != NULL) { |
twisti@1568 | 97 | oop mtype = java_dyn_MethodHandle::type(target); |
twisti@1568 | 98 | int arg_num = MethodHandles::argument_slot_to_argnum(mtype, arg_slot); |
twisti@1568 | 99 | if (arg_num >= 0) { |
twisti@1568 | 100 | oop ptype = java_dyn_MethodType::ptype(mtype, arg_num); |
twisti@1568 | 101 | arg_type = java_lang_Class::as_BasicType(ptype); |
twisti@1568 | 102 | } |
twisti@1568 | 103 | } else if (m != NULL) { |
twisti@1568 | 104 | // figure out the argument type from the slot |
twisti@1568 | 105 | // FIXME: make this explicit in the MH |
twisti@1568 | 106 | int cur_slot = m->size_of_parameters(); |
twisti@1568 | 107 | if (arg_slot >= cur_slot) |
twisti@1568 | 108 | return T_VOID; |
twisti@1568 | 109 | if (!m->is_static()) { |
twisti@1568 | 110 | cur_slot -= type2size[T_OBJECT]; |
twisti@1568 | 111 | if (cur_slot == arg_slot) |
twisti@1568 | 112 | return T_OBJECT; |
twisti@1568 | 113 | } |
twisti@1568 | 114 | for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) { |
twisti@1568 | 115 | BasicType bt = ss.type(); |
twisti@1568 | 116 | cur_slot -= type2size[bt]; |
twisti@1568 | 117 | if (cur_slot <= arg_slot) { |
twisti@1568 | 118 | if (cur_slot == arg_slot) |
twisti@1568 | 119 | arg_type = bt; |
twisti@1568 | 120 | break; |
twisti@1568 | 121 | } |
twisti@1568 | 122 | } |
twisti@1568 | 123 | } |
twisti@1568 | 124 | if (arg_type == T_ARRAY) |
twisti@1568 | 125 | arg_type = T_OBJECT; |
twisti@1568 | 126 | return arg_type; |
twisti@1568 | 127 | } |
twisti@1568 | 128 | |
twisti@1568 | 129 | void MethodHandleChain::lose(const char* msg, TRAPS) { |
twisti@1568 | 130 | _lose_message = msg; |
twisti@1568 | 131 | if (!THREAD->is_Java_thread() || ((JavaThread*)THREAD)->thread_state() != _thread_in_vm) { |
twisti@1568 | 132 | // throw a preallocated exception |
twisti@1568 | 133 | THROW_OOP(Universe::virtual_machine_error_instance()); |
twisti@1568 | 134 | } |
twisti@1568 | 135 | THROW_MSG(vmSymbols::java_lang_InternalError(), msg); |
twisti@1568 | 136 | } |
twisti@1568 | 137 | |
twisti@1568 | 138 | Bytecodes::Code MethodHandleWalker::conversion_code(BasicType src, BasicType dest) { |
twisti@1568 | 139 | if (is_subword_type(src)) { |
twisti@1568 | 140 | src = T_INT; // all subword src types act like int |
twisti@1568 | 141 | } |
twisti@1568 | 142 | if (src == dest) { |
twisti@1568 | 143 | return Bytecodes::_nop; |
twisti@1568 | 144 | } |
twisti@1568 | 145 | |
twisti@1568 | 146 | #define SRC_DEST(s,d) (((int)(s) << 4) + (int)(d)) |
twisti@1568 | 147 | switch (SRC_DEST(src, dest)) { |
twisti@1568 | 148 | case SRC_DEST(T_INT, T_LONG): return Bytecodes::_i2l; |
twisti@1568 | 149 | case SRC_DEST(T_INT, T_FLOAT): return Bytecodes::_i2f; |
twisti@1568 | 150 | case SRC_DEST(T_INT, T_DOUBLE): return Bytecodes::_i2d; |
twisti@1568 | 151 | case SRC_DEST(T_INT, T_BYTE): return Bytecodes::_i2b; |
twisti@1568 | 152 | case SRC_DEST(T_INT, T_CHAR): return Bytecodes::_i2c; |
twisti@1568 | 153 | case SRC_DEST(T_INT, T_SHORT): return Bytecodes::_i2s; |
twisti@1568 | 154 | |
twisti@1568 | 155 | case SRC_DEST(T_LONG, T_INT): return Bytecodes::_l2i; |
twisti@1568 | 156 | case SRC_DEST(T_LONG, T_FLOAT): return Bytecodes::_l2f; |
twisti@1568 | 157 | case SRC_DEST(T_LONG, T_DOUBLE): return Bytecodes::_l2d; |
twisti@1568 | 158 | |
twisti@1568 | 159 | case SRC_DEST(T_FLOAT, T_INT): return Bytecodes::_f2i; |
twisti@1568 | 160 | case SRC_DEST(T_FLOAT, T_LONG): return Bytecodes::_f2l; |
twisti@1568 | 161 | case SRC_DEST(T_FLOAT, T_DOUBLE): return Bytecodes::_f2d; |
twisti@1568 | 162 | |
twisti@1568 | 163 | case SRC_DEST(T_DOUBLE, T_INT): return Bytecodes::_d2i; |
twisti@1568 | 164 | case SRC_DEST(T_DOUBLE, T_LONG): return Bytecodes::_d2l; |
twisti@1568 | 165 | case SRC_DEST(T_DOUBLE, T_FLOAT): return Bytecodes::_d2f; |
twisti@1568 | 166 | } |
twisti@1568 | 167 | #undef SRC_DEST |
twisti@1568 | 168 | |
twisti@1568 | 169 | // cannot do it in one step, or at all |
twisti@1568 | 170 | return Bytecodes::_illegal; |
twisti@1568 | 171 | } |
twisti@1568 | 172 | |
twisti@1568 | 173 | MethodHandleWalker::ArgToken |
twisti@1568 | 174 | MethodHandleWalker::walk(TRAPS) { |
twisti@1568 | 175 | walk_incoming_state(CHECK_NULL); |
twisti@1568 | 176 | |
twisti@1568 | 177 | for (;;) { |
twisti@1568 | 178 | set_method_handle(chain().method_handle_oop()); |
twisti@1568 | 179 | |
twisti@1568 | 180 | assert(_outgoing_argc == argument_count_slow(), "empty slots under control"); |
twisti@1568 | 181 | |
twisti@1568 | 182 | if (chain().is_adapter()) { |
twisti@1568 | 183 | int conv_op = chain().adapter_conversion_op(); |
twisti@1568 | 184 | int arg_slot = chain().adapter_arg_slot(); |
twisti@1568 | 185 | SlotState* arg_state = slot_state(arg_slot); |
twisti@1568 | 186 | if (arg_state == NULL |
twisti@1568 | 187 | && conv_op > sun_dyn_AdapterMethodHandle::OP_RETYPE_RAW) { |
twisti@1568 | 188 | lose("bad argument index", CHECK_NULL); |
twisti@1568 | 189 | } |
twisti@1568 | 190 | |
twisti@1568 | 191 | // perform the adapter action |
twisti@1568 | 192 | switch (chain().adapter_conversion_op()) { |
twisti@1568 | 193 | case sun_dyn_AdapterMethodHandle::OP_RETYPE_ONLY: |
twisti@1568 | 194 | case sun_dyn_AdapterMethodHandle::OP_RETYPE_RAW: |
twisti@1568 | 195 | // No changes to arguments; pass the bits through. |
twisti@1568 | 196 | // The only difference between the two ops is that the "only" version |
twisti@1568 | 197 | // is fully compatible with the verifier, while the "raw" version |
twisti@1568 | 198 | // performs a few extra bitwise conversions (like long <-> double). |
twisti@1568 | 199 | break; |
twisti@1568 | 200 | |
twisti@1568 | 201 | case sun_dyn_AdapterMethodHandle::OP_CHECK_CAST: { |
twisti@1568 | 202 | // checkcast the Nth outgoing argument in place |
twisti@1568 | 203 | klassOop dest_klass = NULL; |
twisti@1568 | 204 | BasicType dest = java_lang_Class::as_BasicType(chain().adapter_arg_oop(), &dest_klass); |
twisti@1568 | 205 | assert(dest == T_OBJECT, ""); |
twisti@1568 | 206 | assert(dest == arg_state->_type, ""); |
twisti@1568 | 207 | arg_state->_arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg_state->_arg, CHECK_NULL); |
twisti@1568 | 208 | debug_only(dest_klass = (klassOop)badOop); |
twisti@1568 | 209 | break; |
twisti@1568 | 210 | } |
twisti@1568 | 211 | |
twisti@1568 | 212 | case sun_dyn_AdapterMethodHandle::OP_PRIM_TO_PRIM: { |
twisti@1568 | 213 | // i2l, etc., on the Nth outgoing argument in place |
twisti@1568 | 214 | BasicType src = chain().adapter_conversion_src_type(), |
twisti@1568 | 215 | dest = chain().adapter_conversion_dest_type(); |
twisti@1568 | 216 | Bytecodes::Code bc = conversion_code(src, dest); |
twisti@1568 | 217 | ArgToken arg = arg_state->_arg; |
twisti@1568 | 218 | if (bc == Bytecodes::_nop) { |
twisti@1568 | 219 | break; |
twisti@1568 | 220 | } else if (bc != Bytecodes::_illegal) { |
twisti@1568 | 221 | arg = make_conversion(dest, NULL, bc, arg, CHECK_NULL); |
twisti@1568 | 222 | } else if (is_subword_type(dest)) { |
twisti@1568 | 223 | bc = conversion_code(src, T_INT); |
twisti@1568 | 224 | if (bc != Bytecodes::_illegal) { |
twisti@1568 | 225 | arg = make_conversion(dest, NULL, bc, arg, CHECK_NULL); |
twisti@1568 | 226 | bc = conversion_code(T_INT, dest); |
twisti@1568 | 227 | arg = make_conversion(dest, NULL, bc, arg, CHECK_NULL); |
twisti@1568 | 228 | } |
twisti@1568 | 229 | } |
twisti@1568 | 230 | if (bc == Bytecodes::_illegal) { |
twisti@1568 | 231 | lose("bad primitive conversion", CHECK_NULL); |
twisti@1568 | 232 | } |
twisti@1568 | 233 | change_argument(src, arg_slot, dest, arg); |
twisti@1568 | 234 | break; |
twisti@1568 | 235 | } |
twisti@1568 | 236 | |
twisti@1568 | 237 | case sun_dyn_AdapterMethodHandle::OP_REF_TO_PRIM: { |
twisti@1568 | 238 | // checkcast to wrapper type & call intValue, etc. |
twisti@1568 | 239 | BasicType dest = chain().adapter_conversion_dest_type(); |
twisti@1568 | 240 | ArgToken arg = arg_state->_arg; |
twisti@1568 | 241 | arg = make_conversion(T_OBJECT, SystemDictionary::box_klass(dest), |
twisti@1568 | 242 | Bytecodes::_checkcast, arg, CHECK_NULL); |
twisti@1568 | 243 | vmIntrinsics::ID unboxer = vmIntrinsics::for_unboxing(dest); |
twisti@1568 | 244 | if (unboxer == vmIntrinsics::_none) { |
twisti@1568 | 245 | lose("no unboxing method", CHECK_NULL); |
twisti@1568 | 246 | } |
twisti@1568 | 247 | ArgToken arglist[2]; |
twisti@1568 | 248 | arglist[0] = arg; // outgoing 'this' |
twisti@1568 | 249 | arglist[1] = NULL; // sentinel |
twisti@1568 | 250 | arg = make_invoke(NULL, unboxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_NULL); |
twisti@1568 | 251 | change_argument(T_OBJECT, arg_slot, dest, arg); |
twisti@1568 | 252 | break; |
twisti@1568 | 253 | } |
twisti@1568 | 254 | |
twisti@1568 | 255 | case sun_dyn_AdapterMethodHandle::OP_PRIM_TO_REF: { |
twisti@1568 | 256 | // call wrapper type.valueOf |
twisti@1568 | 257 | BasicType src = chain().adapter_conversion_src_type(); |
twisti@1568 | 258 | ArgToken arg = arg_state->_arg; |
twisti@1568 | 259 | vmIntrinsics::ID boxer = vmIntrinsics::for_boxing(src); |
twisti@1568 | 260 | if (boxer == vmIntrinsics::_none) { |
twisti@1568 | 261 | lose("no boxing method", CHECK_NULL); |
twisti@1568 | 262 | } |
twisti@1568 | 263 | ArgToken arglist[2]; |
twisti@1568 | 264 | arglist[0] = arg; // outgoing value |
twisti@1568 | 265 | arglist[1] = NULL; // sentinel |
twisti@1568 | 266 | arg = make_invoke(NULL, boxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_NULL); |
twisti@1568 | 267 | change_argument(src, arg_slot, T_OBJECT, arg); |
twisti@1568 | 268 | break; |
twisti@1568 | 269 | } |
twisti@1568 | 270 | |
twisti@1568 | 271 | case sun_dyn_AdapterMethodHandle::OP_SWAP_ARGS: { |
twisti@1568 | 272 | int dest_arg_slot = chain().adapter_conversion_vminfo(); |
twisti@1568 | 273 | if (!slot_has_argument(dest_arg_slot)) { |
twisti@1568 | 274 | lose("bad swap index", CHECK_NULL); |
twisti@1568 | 275 | } |
twisti@1568 | 276 | // a simple swap between two arguments |
twisti@1568 | 277 | SlotState* dest_arg_state = slot_state(dest_arg_slot); |
twisti@1568 | 278 | SlotState temp = (*dest_arg_state); |
twisti@1568 | 279 | (*dest_arg_state) = (*arg_state); |
twisti@1568 | 280 | (*arg_state) = temp; |
twisti@1568 | 281 | break; |
twisti@1568 | 282 | } |
twisti@1568 | 283 | |
twisti@1568 | 284 | case sun_dyn_AdapterMethodHandle::OP_ROT_ARGS: { |
twisti@1568 | 285 | int dest_arg_slot = chain().adapter_conversion_vminfo(); |
twisti@1568 | 286 | if (!slot_has_argument(dest_arg_slot) || arg_slot == dest_arg_slot) { |
twisti@1568 | 287 | lose("bad rotate index", CHECK_NULL); |
twisti@1568 | 288 | } |
twisti@1568 | 289 | SlotState* dest_arg_state = slot_state(dest_arg_slot); |
twisti@1568 | 290 | // Rotate the source argument (plus following N slots) into the |
twisti@1568 | 291 | // position occupied by the dest argument (plus following N slots). |
twisti@1568 | 292 | int rotate_count = type2size[dest_arg_state->_type]; |
twisti@1568 | 293 | // (no other rotate counts are currently supported) |
twisti@1568 | 294 | if (arg_slot < dest_arg_slot) { |
twisti@1568 | 295 | for (int i = 0; i < rotate_count; i++) { |
twisti@1568 | 296 | SlotState temp = _outgoing.at(arg_slot); |
twisti@1568 | 297 | _outgoing.remove_at(arg_slot); |
twisti@1568 | 298 | _outgoing.insert_before(dest_arg_slot + rotate_count - 1, temp); |
twisti@1568 | 299 | } |
twisti@1568 | 300 | } else { // arg_slot > dest_arg_slot |
twisti@1568 | 301 | for (int i = 0; i < rotate_count; i++) { |
twisti@1568 | 302 | SlotState temp = _outgoing.at(arg_slot + rotate_count - 1); |
twisti@1568 | 303 | _outgoing.remove_at(arg_slot + rotate_count - 1); |
twisti@1568 | 304 | _outgoing.insert_before(dest_arg_slot, temp); |
twisti@1568 | 305 | } |
twisti@1568 | 306 | } |
twisti@1568 | 307 | break; |
twisti@1568 | 308 | } |
twisti@1568 | 309 | |
twisti@1568 | 310 | case sun_dyn_AdapterMethodHandle::OP_DUP_ARGS: { |
twisti@1568 | 311 | int dup_slots = chain().adapter_conversion_stack_pushes(); |
twisti@1568 | 312 | if (dup_slots <= 0) { |
twisti@1568 | 313 | lose("bad dup count", CHECK_NULL); |
twisti@1568 | 314 | } |
twisti@1568 | 315 | for (int i = 0; i < dup_slots; i++) { |
twisti@1568 | 316 | SlotState* dup = slot_state(arg_slot + 2*i); |
twisti@1568 | 317 | if (dup == NULL) break; // safety net |
twisti@1568 | 318 | if (dup->_type != T_VOID) _outgoing_argc += 1; |
twisti@1568 | 319 | _outgoing.insert_before(i, (*dup)); |
twisti@1568 | 320 | } |
twisti@1568 | 321 | break; |
twisti@1568 | 322 | } |
twisti@1568 | 323 | |
twisti@1568 | 324 | case sun_dyn_AdapterMethodHandle::OP_DROP_ARGS: { |
twisti@1568 | 325 | int drop_slots = -chain().adapter_conversion_stack_pushes(); |
twisti@1568 | 326 | if (drop_slots <= 0) { |
twisti@1568 | 327 | lose("bad drop count", CHECK_NULL); |
twisti@1568 | 328 | } |
twisti@1568 | 329 | for (int i = 0; i < drop_slots; i++) { |
twisti@1568 | 330 | SlotState* drop = slot_state(arg_slot); |
twisti@1568 | 331 | if (drop == NULL) break; // safety net |
twisti@1568 | 332 | if (drop->_type != T_VOID) _outgoing_argc -= 1; |
twisti@1568 | 333 | _outgoing.remove_at(arg_slot); |
twisti@1568 | 334 | } |
twisti@1568 | 335 | break; |
twisti@1568 | 336 | } |
twisti@1568 | 337 | |
twisti@1568 | 338 | case sun_dyn_AdapterMethodHandle::OP_COLLECT_ARGS: { //NYI, may GC |
twisti@1568 | 339 | lose("unimplemented", CHECK_NULL); |
twisti@1568 | 340 | break; |
twisti@1568 | 341 | } |
twisti@1568 | 342 | |
twisti@1568 | 343 | case sun_dyn_AdapterMethodHandle::OP_SPREAD_ARGS: { |
twisti@1568 | 344 | klassOop array_klass_oop = NULL; |
twisti@1568 | 345 | BasicType array_type = java_lang_Class::as_BasicType(chain().adapter_arg_oop(), |
twisti@1568 | 346 | &array_klass_oop); |
twisti@1568 | 347 | assert(array_type == T_OBJECT, ""); |
twisti@1568 | 348 | assert(Klass::cast(array_klass_oop)->oop_is_array(), ""); |
twisti@1568 | 349 | arrayKlassHandle array_klass(THREAD, array_klass_oop); |
twisti@1568 | 350 | debug_only(array_klass_oop = (klassOop)badOop); |
twisti@1568 | 351 | |
twisti@1568 | 352 | klassOop element_klass_oop = NULL; |
twisti@1568 | 353 | BasicType element_type = java_lang_Class::as_BasicType(array_klass->component_mirror(), |
twisti@1568 | 354 | &element_klass_oop); |
twisti@1568 | 355 | KlassHandle element_klass(THREAD, element_klass_oop); |
twisti@1568 | 356 | debug_only(element_klass_oop = (klassOop)badOop); |
twisti@1568 | 357 | |
twisti@1568 | 358 | // Fetch the argument, which we will cast to the required array type. |
twisti@1568 | 359 | assert(arg_state->_type == T_OBJECT, ""); |
twisti@1568 | 360 | ArgToken array_arg = arg_state->_arg; |
twisti@1568 | 361 | array_arg = make_conversion(T_OBJECT, array_klass(), Bytecodes::_checkcast, array_arg, CHECK_NULL); |
twisti@1568 | 362 | change_argument(T_OBJECT, arg_slot, T_VOID, NULL); |
twisti@1568 | 363 | |
twisti@1568 | 364 | // Check the required length. |
twisti@1568 | 365 | int spread_slots = 1 + chain().adapter_conversion_stack_pushes(); |
twisti@1568 | 366 | int spread_length = spread_slots; |
twisti@1568 | 367 | if (type2size[element_type] == 2) { |
twisti@1568 | 368 | if (spread_slots % 2 != 0) spread_slots = -1; // force error |
twisti@1568 | 369 | spread_length = spread_slots / 2; |
twisti@1568 | 370 | } |
twisti@1568 | 371 | if (spread_slots < 0) { |
twisti@1568 | 372 | lose("bad spread length", CHECK_NULL); |
twisti@1568 | 373 | } |
twisti@1568 | 374 | |
twisti@1568 | 375 | jvalue length_jvalue; length_jvalue.i = spread_length; |
twisti@1568 | 376 | ArgToken length_arg = make_prim_constant(T_INT, &length_jvalue, CHECK_NULL); |
twisti@1568 | 377 | // Call a built-in method known to the JVM to validate the length. |
twisti@1568 | 378 | ArgToken arglist[3]; |
twisti@1568 | 379 | arglist[0] = array_arg; // value to check |
twisti@1568 | 380 | arglist[1] = length_arg; // length to check |
twisti@1568 | 381 | arglist[2] = NULL; // sentinel |
twisti@1568 | 382 | make_invoke(NULL, vmIntrinsics::_checkSpreadArgument, |
twisti@1568 | 383 | Bytecodes::_invokestatic, false, 3, &arglist[0], CHECK_NULL); |
twisti@1568 | 384 | |
twisti@1568 | 385 | // Spread out the array elements. |
twisti@1568 | 386 | Bytecodes::Code aload_op = Bytecodes::_aaload; |
twisti@1568 | 387 | if (element_type != T_OBJECT) { |
twisti@1568 | 388 | lose("primitive array NYI", CHECK_NULL); |
twisti@1568 | 389 | } |
twisti@1568 | 390 | int ap = arg_slot; |
twisti@1568 | 391 | for (int i = 0; i < spread_length; i++) { |
twisti@1568 | 392 | jvalue offset_jvalue; offset_jvalue.i = i; |
twisti@1568 | 393 | ArgToken offset_arg = make_prim_constant(T_INT, &offset_jvalue, CHECK_NULL); |
twisti@1568 | 394 | ArgToken element_arg = make_fetch(element_type, element_klass(), aload_op, array_arg, offset_arg, CHECK_NULL); |
twisti@1568 | 395 | change_argument(T_VOID, ap, element_type, element_arg); |
twisti@1568 | 396 | ap += type2size[element_type]; |
twisti@1568 | 397 | } |
twisti@1568 | 398 | break; |
twisti@1568 | 399 | } |
twisti@1568 | 400 | |
twisti@1568 | 401 | case sun_dyn_AdapterMethodHandle::OP_FLYBY: //NYI, runs Java code |
twisti@1568 | 402 | case sun_dyn_AdapterMethodHandle::OP_RICOCHET: //NYI, runs Java code |
twisti@1568 | 403 | lose("unimplemented", CHECK_NULL); |
twisti@1568 | 404 | break; |
twisti@1568 | 405 | |
twisti@1568 | 406 | default: |
twisti@1568 | 407 | lose("bad adapter conversion", CHECK_NULL); |
twisti@1568 | 408 | break; |
twisti@1568 | 409 | } |
twisti@1568 | 410 | } |
twisti@1568 | 411 | |
twisti@1568 | 412 | if (chain().is_bound()) { |
twisti@1568 | 413 | // push a new argument |
twisti@1568 | 414 | BasicType arg_type = chain().bound_arg_type(); |
twisti@1568 | 415 | jint arg_slot = chain().bound_arg_slot(); |
twisti@1568 | 416 | oop arg_oop = chain().bound_arg_oop(); |
twisti@1568 | 417 | ArgToken arg = NULL; |
twisti@1568 | 418 | if (arg_type == T_OBJECT) { |
twisti@1568 | 419 | arg = make_oop_constant(arg_oop, CHECK_NULL); |
twisti@1568 | 420 | } else { |
twisti@1568 | 421 | jvalue arg_value; |
twisti@1568 | 422 | BasicType bt = java_lang_boxing_object::get_value(arg_oop, &arg_value); |
twisti@1568 | 423 | if (bt == arg_type) { |
twisti@1568 | 424 | arg = make_prim_constant(arg_type, &arg_value, CHECK_NULL); |
twisti@1568 | 425 | } else { |
twisti@1568 | 426 | lose("bad bound value", CHECK_NULL); |
twisti@1568 | 427 | } |
twisti@1568 | 428 | } |
twisti@1568 | 429 | debug_only(arg_oop = badOop); |
twisti@1568 | 430 | change_argument(T_VOID, arg_slot, arg_type, arg); |
twisti@1568 | 431 | } |
twisti@1568 | 432 | |
twisti@1568 | 433 | // this test must come after the body of the loop |
twisti@1568 | 434 | if (!chain().is_last()) { |
twisti@1568 | 435 | chain().next(CHECK_NULL); |
twisti@1568 | 436 | } else { |
twisti@1568 | 437 | break; |
twisti@1568 | 438 | } |
twisti@1568 | 439 | } |
twisti@1568 | 440 | |
twisti@1568 | 441 | // finish the sequence with a tail-call to the ultimate target |
twisti@1568 | 442 | // parameters are passed in logical order (recv 1st), not slot order |
twisti@1568 | 443 | ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, _outgoing.length() + 1); |
twisti@1568 | 444 | int ap = 0; |
twisti@1568 | 445 | for (int i = _outgoing.length() - 1; i >= 0; i--) { |
twisti@1568 | 446 | SlotState* arg_state = slot_state(i); |
twisti@1568 | 447 | if (arg_state->_type == T_VOID) continue; |
twisti@1568 | 448 | arglist[ap++] = _outgoing.at(i)._arg; |
twisti@1568 | 449 | } |
twisti@1568 | 450 | assert(ap == _outgoing_argc, ""); |
twisti@1568 | 451 | arglist[ap] = NULL; // add a sentinel, for the sake of asserts |
twisti@1568 | 452 | return make_invoke(chain().last_method_oop(), |
twisti@1568 | 453 | vmIntrinsics::_none, |
twisti@1568 | 454 | chain().last_invoke_code(), true, |
twisti@1568 | 455 | ap, arglist, THREAD); |
twisti@1568 | 456 | } |
twisti@1568 | 457 | |
twisti@1568 | 458 | void MethodHandleWalker::walk_incoming_state(TRAPS) { |
twisti@1568 | 459 | Handle mtype(THREAD, chain().method_type_oop()); |
twisti@1568 | 460 | int nptypes = java_dyn_MethodType::ptype_count(mtype()); |
twisti@1568 | 461 | _outgoing_argc = nptypes; |
twisti@1568 | 462 | int argp = nptypes - 1; |
twisti@1568 | 463 | if (argp >= 0) { |
twisti@1568 | 464 | _outgoing.at_grow(argp, make_state(T_VOID, NULL)); // presize |
twisti@1568 | 465 | } |
twisti@1568 | 466 | for (int i = 0; i < nptypes; i++) { |
twisti@1568 | 467 | klassOop arg_type_klass = NULL; |
twisti@1568 | 468 | BasicType arg_type = java_lang_Class::as_BasicType( |
twisti@1568 | 469 | java_dyn_MethodType::ptype(mtype(), i), &arg_type_klass); |
twisti@1568 | 470 | ArgToken arg = make_parameter(arg_type, arg_type_klass, i, CHECK); |
twisti@1568 | 471 | debug_only(arg_type_klass = (klassOop)NULL); |
twisti@1568 | 472 | _outgoing.at_put(argp, make_state(arg_type, arg)); |
twisti@1568 | 473 | if (type2size[arg_type] == 2) { |
twisti@1568 | 474 | // add the extra slot, so we can model the JVM stack |
twisti@1568 | 475 | _outgoing.insert_before(argp+1, make_state(T_VOID, NULL)); |
twisti@1568 | 476 | } |
twisti@1568 | 477 | --argp; |
twisti@1568 | 478 | } |
twisti@1568 | 479 | // call make_parameter at the end of the list for the return type |
twisti@1568 | 480 | klassOop ret_type_klass = NULL; |
twisti@1568 | 481 | BasicType ret_type = java_lang_Class::as_BasicType( |
twisti@1568 | 482 | java_dyn_MethodType::rtype(mtype()), &ret_type_klass); |
twisti@1568 | 483 | ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK); |
twisti@1568 | 484 | // ignore ret; client can catch it if needed |
twisti@1568 | 485 | } |
twisti@1568 | 486 | |
twisti@1568 | 487 | // this is messy because some kinds of arguments are paired with |
twisti@1568 | 488 | // companion slots containing an empty value |
twisti@1568 | 489 | void MethodHandleWalker::change_argument(BasicType old_type, int slot, BasicType new_type, |
twisti@1568 | 490 | MethodHandleWalker::ArgToken new_arg) { |
twisti@1568 | 491 | int old_size = type2size[old_type]; |
twisti@1568 | 492 | int new_size = type2size[new_type]; |
twisti@1568 | 493 | if (old_size == new_size) { |
twisti@1568 | 494 | // simple case first |
twisti@1568 | 495 | _outgoing.at_put(slot, make_state(new_type, new_arg)); |
twisti@1568 | 496 | } else if (old_size > new_size) { |
twisti@1568 | 497 | for (int i = old_size-1; i >= new_size; i++) { |
twisti@1568 | 498 | assert((i != 0) == (_outgoing.at(slot + i)._type == T_VOID), ""); |
twisti@1568 | 499 | _outgoing.remove_at(slot + i); |
twisti@1568 | 500 | } |
twisti@1568 | 501 | if (new_size > 0) |
twisti@1568 | 502 | _outgoing.at_put(slot, make_state(new_type, new_arg)); |
twisti@1568 | 503 | else |
twisti@1568 | 504 | _outgoing_argc -= 1; // deleted a real argument |
twisti@1568 | 505 | } else { |
twisti@1568 | 506 | for (int i = old_size; i < new_size; i++) { |
twisti@1568 | 507 | _outgoing.insert_before(slot+i, make_state(T_VOID, NULL)); |
twisti@1568 | 508 | } |
twisti@1568 | 509 | _outgoing.at_put(slot, make_state(new_type, new_arg)); |
twisti@1568 | 510 | if (old_size == 0) |
twisti@1568 | 511 | _outgoing_argc += 1; // inserted a real argument |
twisti@1568 | 512 | } |
twisti@1568 | 513 | } |
twisti@1568 | 514 | |
twisti@1568 | 515 | |
twisti@1568 | 516 | #ifdef ASSERT |
twisti@1568 | 517 | int MethodHandleWalker::argument_count_slow() { |
twisti@1568 | 518 | int args_seen = 0; |
twisti@1568 | 519 | for (int i = _outgoing.length() - 1; i >= 0; i--) { |
twisti@1568 | 520 | if (_outgoing.at(i)._type != T_VOID) { |
twisti@1568 | 521 | ++args_seen; |
twisti@1568 | 522 | } |
twisti@1568 | 523 | } |
twisti@1568 | 524 | return args_seen; |
twisti@1568 | 525 | } |
twisti@1568 | 526 | #endif |
twisti@1568 | 527 | |
twisti@1568 | 528 | |
twisti@1568 | 529 | void MethodHandleCompiler::compile(TRAPS) { |
twisti@1568 | 530 | assert(_thread == THREAD, "must be same thread"); |
twisti@1568 | 531 | |
twisti@1568 | 532 | _constant_oops.append(Handle()); // element zero is always the null constant |
twisti@1568 | 533 | _constant_prims.append(NULL); |
twisti@1568 | 534 | { |
twisti@1568 | 535 | symbolOop sig |
twisti@1568 | 536 | = java_dyn_MethodType::as_signature(chain().method_type_oop(), true, CHECK); |
twisti@1568 | 537 | _signature_index = find_oop_constant(sig); |
twisti@1568 | 538 | assert(signature() == sig, ""); |
twisti@1568 | 539 | } |
twisti@1568 | 540 | |
twisti@1568 | 541 | walk(CHECK); |
twisti@1568 | 542 | } |
twisti@1568 | 543 | |
twisti@1568 | 544 | MethodHandleWalker::ArgToken |
twisti@1568 | 545 | MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, |
twisti@1568 | 546 | MethodHandleWalker::ArgToken src, TRAPS) { |
twisti@1568 | 547 | Unimplemented(); |
twisti@1568 | 548 | return NULL; |
twisti@1568 | 549 | } |
twisti@1568 | 550 | |
twisti@1568 | 551 | MethodHandleWalker::ArgToken |
twisti@1568 | 552 | MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, |
twisti@1568 | 553 | Bytecodes::Code op, bool tailcall, |
twisti@1568 | 554 | int argc, MethodHandleWalker::ArgToken* argv, |
twisti@1568 | 555 | TRAPS) { |
twisti@1568 | 556 | // If tailcall, we have walked all the way to a direct method handle. |
twisti@1568 | 557 | // Otherwise, make a recursive call to some helper routine. |
twisti@1568 | 558 | #ifdef ASSERT |
twisti@1568 | 559 | switch (op) { |
twisti@1568 | 560 | case Bytecodes::_invokevirtual: |
twisti@1568 | 561 | case Bytecodes::_invokespecial: |
twisti@1568 | 562 | case Bytecodes::_invokestatic: |
twisti@1568 | 563 | case Bytecodes::_invokeinterface: |
twisti@1568 | 564 | break; |
twisti@1568 | 565 | default: |
twisti@1568 | 566 | ShouldNotReachHere(); |
twisti@1568 | 567 | } |
twisti@1568 | 568 | #endif //ASSERT |
twisti@1568 | 569 | _bytes.put((char) op); |
twisti@1568 | 570 | |
twisti@1568 | 571 | Unimplemented(); |
twisti@1568 | 572 | return NULL; |
twisti@1568 | 573 | } |
twisti@1568 | 574 | |
twisti@1568 | 575 | MethodHandleWalker::ArgToken |
twisti@1568 | 576 | MethodHandleCompiler::make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, |
twisti@1568 | 577 | MethodHandleWalker::ArgToken base, |
twisti@1568 | 578 | MethodHandleWalker::ArgToken offset, |
twisti@1568 | 579 | TRAPS) { |
twisti@1568 | 580 | Unimplemented(); |
twisti@1568 | 581 | return NULL; |
twisti@1568 | 582 | } |
twisti@1568 | 583 | |
twisti@1568 | 584 | int MethodHandleCompiler::find_oop_constant(oop con) { |
twisti@1568 | 585 | if (con == NULL) return 0; |
twisti@1568 | 586 | for (int i = 1, imax = _constant_oops.length(); i < imax; i++) { |
twisti@1568 | 587 | if (_constant_oops.at(i) == con) |
twisti@1568 | 588 | return i; |
twisti@1568 | 589 | } |
twisti@1568 | 590 | _constant_prims.append(NULL); |
twisti@1568 | 591 | return _constant_oops.append(con); |
twisti@1568 | 592 | } |
twisti@1568 | 593 | |
twisti@1568 | 594 | int MethodHandleCompiler::find_prim_constant(BasicType bt, jvalue* con) { |
twisti@1568 | 595 | jvalue con_copy; |
twisti@1568 | 596 | assert(bt < T_OBJECT, ""); |
twisti@1568 | 597 | if (type2aelembytes(bt) < jintSize) { |
twisti@1568 | 598 | // widen to int |
twisti@1568 | 599 | con_copy = (*con); |
twisti@1568 | 600 | con = &con_copy; |
twisti@1568 | 601 | switch (bt) { |
twisti@1568 | 602 | case T_BOOLEAN: con->i = (con->z ? 1 : 0); break; |
twisti@1568 | 603 | case T_BYTE: con->i = con->b; break; |
twisti@1568 | 604 | case T_CHAR: con->i = con->c; break; |
twisti@1568 | 605 | case T_SHORT: con->i = con->s; break; |
twisti@1568 | 606 | default: ShouldNotReachHere(); |
twisti@1568 | 607 | } |
twisti@1568 | 608 | bt = T_INT; |
twisti@1568 | 609 | } |
twisti@1568 | 610 | for (int i = 1, imax = _constant_prims.length(); i < imax; i++) { |
twisti@1568 | 611 | PrimCon* pcon = _constant_prims.at(i); |
twisti@1568 | 612 | if (pcon != NULL && pcon->_type == bt) { |
twisti@1568 | 613 | bool match = false; |
twisti@1568 | 614 | switch (type2size[bt]) { |
twisti@1568 | 615 | case 1: if (pcon->_value.i == con->i) match = true; break; |
twisti@1568 | 616 | case 2: if (pcon->_value.j == con->j) match = true; break; |
twisti@1568 | 617 | } |
twisti@1568 | 618 | if (match) |
twisti@1568 | 619 | return i; |
twisti@1568 | 620 | } |
twisti@1568 | 621 | } |
twisti@1568 | 622 | PrimCon* pcon = new PrimCon(); |
twisti@1568 | 623 | pcon->_type = bt; |
twisti@1568 | 624 | pcon->_value = (*con); |
twisti@1568 | 625 | _constant_oops.append(Handle()); |
twisti@1568 | 626 | return _constant_prims.append(pcon); |
twisti@1568 | 627 | } |
twisti@1568 | 628 | |
twisti@1568 | 629 | |
twisti@1568 | 630 | #ifndef PRODUCT |
twisti@1568 | 631 | |
twisti@1568 | 632 | // MH printer for debugging. |
twisti@1568 | 633 | |
twisti@1568 | 634 | class MethodHandlePrinter : public MethodHandleWalker { |
twisti@1568 | 635 | private: |
twisti@1568 | 636 | outputStream* _out; |
twisti@1568 | 637 | bool _verbose; |
twisti@1568 | 638 | int _temp_num; |
twisti@1568 | 639 | stringStream _strbuf; |
twisti@1568 | 640 | const char* strbuf() { |
twisti@1568 | 641 | const char* s = _strbuf.as_string(); |
twisti@1568 | 642 | _strbuf.reset(); |
twisti@1568 | 643 | return s; |
twisti@1568 | 644 | } |
twisti@1568 | 645 | ArgToken token(const char* str) { |
twisti@1568 | 646 | return (ArgToken) str; |
twisti@1568 | 647 | } |
twisti@1568 | 648 | void start_params() { |
twisti@1568 | 649 | _out->print("("); |
twisti@1568 | 650 | } |
twisti@1568 | 651 | void end_params() { |
twisti@1568 | 652 | if (_verbose) _out->print("\n"); |
twisti@1568 | 653 | _out->print(") => {"); |
twisti@1568 | 654 | } |
twisti@1568 | 655 | void put_type_name(BasicType type, klassOop tk, outputStream* s) { |
twisti@1568 | 656 | const char* kname = NULL; |
twisti@1568 | 657 | if (tk != NULL) |
twisti@1568 | 658 | kname = Klass::cast(tk)->external_name(); |
twisti@1568 | 659 | s->print("%s", (kname != NULL) ? kname : type2name(type)); |
twisti@1568 | 660 | } |
twisti@1568 | 661 | ArgToken maybe_make_temp(const char* statement_op, BasicType type, const char* temp_name) { |
twisti@1568 | 662 | const char* value = strbuf(); |
twisti@1568 | 663 | if (!_verbose) return token(value); |
twisti@1568 | 664 | // make an explicit binding for each separate value |
twisti@1568 | 665 | _strbuf.print("%s%d", temp_name, ++_temp_num); |
twisti@1568 | 666 | const char* temp = strbuf(); |
twisti@1568 | 667 | _out->print("\n %s %s %s = %s;", statement_op, type2name(type), temp, value); |
twisti@1568 | 668 | return token(temp); |
twisti@1568 | 669 | } |
twisti@1568 | 670 | |
twisti@1568 | 671 | public: |
twisti@1568 | 672 | MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS) |
twisti@1568 | 673 | : MethodHandleWalker(root, THREAD), |
twisti@1568 | 674 | _out(out), |
twisti@1568 | 675 | _verbose(verbose), |
twisti@1568 | 676 | _temp_num(0) |
twisti@1568 | 677 | { |
twisti@1568 | 678 | start_params(); |
twisti@1568 | 679 | } |
twisti@1568 | 680 | virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) { |
twisti@1568 | 681 | if (argnum < 0) { |
twisti@1568 | 682 | end_params(); |
twisti@1568 | 683 | return NULL; |
twisti@1568 | 684 | } |
twisti@1568 | 685 | if (argnum == 0) { |
twisti@1568 | 686 | _out->print(_verbose ? "\n " : ""); |
twisti@1568 | 687 | } else { |
twisti@1568 | 688 | _out->print(_verbose ? ",\n " : ", "); |
twisti@1568 | 689 | } |
twisti@1568 | 690 | if (argnum >= _temp_num) |
twisti@1568 | 691 | _temp_num = argnum; |
twisti@1568 | 692 | // generate an argument name |
twisti@1568 | 693 | _strbuf.print("a%d", argnum); |
twisti@1568 | 694 | const char* arg = strbuf(); |
twisti@1568 | 695 | put_type_name(type, tk, _out); |
twisti@1568 | 696 | _out->print(" %s", arg); |
twisti@1568 | 697 | return token(arg); |
twisti@1568 | 698 | } |
twisti@1568 | 699 | virtual ArgToken make_oop_constant(oop con, TRAPS) { |
twisti@1568 | 700 | if (con == NULL) |
twisti@1568 | 701 | _strbuf.print("null"); |
twisti@1568 | 702 | else |
twisti@1568 | 703 | con->print_value_on(&_strbuf); |
twisti@1568 | 704 | if (_strbuf.size() == 0) { // yuck |
twisti@1568 | 705 | _strbuf.print("(a "); |
twisti@1568 | 706 | put_type_name(T_OBJECT, con->klass(), &_strbuf); |
twisti@1568 | 707 | _strbuf.print(")"); |
twisti@1568 | 708 | } |
twisti@1568 | 709 | return maybe_make_temp("constant", T_OBJECT, "k"); |
twisti@1568 | 710 | } |
twisti@1568 | 711 | virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) { |
twisti@1568 | 712 | java_lang_boxing_object::print(type, con, &_strbuf); |
twisti@1568 | 713 | return maybe_make_temp("constant", type, "k"); |
twisti@1568 | 714 | } |
twisti@1568 | 715 | virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS) { |
twisti@1568 | 716 | _strbuf.print("%s(%s", Bytecodes::name(op), (const char*)src); |
twisti@1568 | 717 | if (tk != NULL) { |
twisti@1568 | 718 | _strbuf.print(", "); |
twisti@1568 | 719 | put_type_name(type, tk, &_strbuf); |
twisti@1568 | 720 | } |
twisti@1568 | 721 | _strbuf.print(")"); |
twisti@1568 | 722 | return maybe_make_temp("convert", type, "v"); |
twisti@1568 | 723 | } |
twisti@1568 | 724 | virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS) { |
twisti@1568 | 725 | _strbuf.print("%s(%s, %s", Bytecodes::name(op), (const char*)base, (const char*)offset); |
twisti@1568 | 726 | if (tk != NULL) { |
twisti@1568 | 727 | _strbuf.print(", "); |
twisti@1568 | 728 | put_type_name(type, tk, &_strbuf); |
twisti@1568 | 729 | } |
twisti@1568 | 730 | _strbuf.print(")"); |
twisti@1568 | 731 | return maybe_make_temp("fetch", type, "x"); |
twisti@1568 | 732 | } |
twisti@1568 | 733 | virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, |
twisti@1568 | 734 | Bytecodes::Code op, bool tailcall, |
twisti@1568 | 735 | int argc, ArgToken* argv, TRAPS) { |
twisti@1568 | 736 | symbolOop name, sig; |
twisti@1568 | 737 | if (m != NULL) { |
twisti@1568 | 738 | name = m->name(); |
twisti@1568 | 739 | sig = m->signature(); |
twisti@1568 | 740 | } else { |
twisti@1568 | 741 | name = vmSymbols::symbol_at(vmIntrinsics::name_for(iid)); |
twisti@1568 | 742 | sig = vmSymbols::symbol_at(vmIntrinsics::signature_for(iid)); |
twisti@1568 | 743 | } |
twisti@1568 | 744 | _strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string()); |
twisti@1568 | 745 | for (int i = 0; i < argc; i++) { |
twisti@1568 | 746 | _strbuf.print("%s%s", (i > 0 ? ", " : ""), (const char*)argv[i]); |
twisti@1568 | 747 | } |
twisti@1568 | 748 | _strbuf.print(")"); |
twisti@1568 | 749 | if (!tailcall) { |
twisti@1568 | 750 | BasicType rt = char2type(sig->byte_at(sig->utf8_length()-1)); |
twisti@1568 | 751 | if (rt == T_ILLEGAL) rt = T_OBJECT; // ';' at the end of '(...)L...;' |
twisti@1568 | 752 | return maybe_make_temp("invoke", rt, "x"); |
twisti@1568 | 753 | } else { |
twisti@1568 | 754 | const char* ret = strbuf(); |
twisti@1568 | 755 | _out->print(_verbose ? "\n return " : " "); |
twisti@1568 | 756 | _out->print("%s", ret); |
twisti@1568 | 757 | _out->print(_verbose ? "\n}\n" : " }"); |
twisti@1568 | 758 | } |
twisti@1568 | 759 | return ArgToken(); |
twisti@1568 | 760 | } |
twisti@1568 | 761 | |
twisti@1568 | 762 | virtual void set_method_handle(oop mh) { |
twisti@1568 | 763 | if (WizardMode && Verbose) { |
twisti@1568 | 764 | tty->print("\n--- next target: "); |
twisti@1568 | 765 | mh->print(); |
twisti@1568 | 766 | } |
twisti@1568 | 767 | } |
twisti@1568 | 768 | |
twisti@1568 | 769 | static void print(Handle root, bool verbose, outputStream* out, TRAPS) { |
twisti@1568 | 770 | ResourceMark rm; |
twisti@1568 | 771 | MethodHandlePrinter printer(root, verbose, out, CHECK); |
twisti@1568 | 772 | printer.walk(CHECK); |
twisti@1568 | 773 | out->print("\n"); |
twisti@1568 | 774 | } |
twisti@1568 | 775 | static void print(Handle root, bool verbose = Verbose, outputStream* out = tty) { |
twisti@1568 | 776 | EXCEPTION_MARK; |
twisti@1568 | 777 | ResourceMark rm; |
twisti@1568 | 778 | MethodHandlePrinter printer(root, verbose, out, THREAD); |
twisti@1568 | 779 | if (!HAS_PENDING_EXCEPTION) |
twisti@1568 | 780 | printer.walk(THREAD); |
twisti@1568 | 781 | if (HAS_PENDING_EXCEPTION) { |
twisti@1568 | 782 | oop ex = PENDING_EXCEPTION; |
twisti@1568 | 783 | CLEAR_PENDING_EXCEPTION; |
twisti@1568 | 784 | out->print("\n*** "); |
twisti@1568 | 785 | if (ex != Universe::virtual_machine_error_instance()) |
twisti@1568 | 786 | ex->print_on(out); |
twisti@1568 | 787 | else |
twisti@1568 | 788 | out->print("lose: %s", printer.lose_message()); |
twisti@1568 | 789 | out->print("\n}\n"); |
twisti@1568 | 790 | } |
twisti@1568 | 791 | out->print("\n"); |
twisti@1568 | 792 | } |
twisti@1568 | 793 | }; |
twisti@1568 | 794 | |
twisti@1568 | 795 | extern "C" |
twisti@1568 | 796 | void print_method_handle(oop mh) { |
twisti@1568 | 797 | if (java_dyn_MethodHandle::is_instance(mh)) { |
twisti@1568 | 798 | MethodHandlePrinter::print(mh); |
twisti@1568 | 799 | } else { |
twisti@1568 | 800 | tty->print("*** not a method handle: "); |
twisti@1568 | 801 | mh->print(); |
twisti@1568 | 802 | } |
twisti@1568 | 803 | } |
twisti@1568 | 804 | |
twisti@1568 | 805 | #endif // PRODUCT |