view src/share/vm/prims/methodHandleWalk.cpp @ 3917:eca1193ca245

4965777: GC changes to support use of discovered field for pending references Summary: If and when the reference handler thread is able to use the discovered field to link reference objects in its pending list, so will GC. In that case, GC will scan through this field once a reference object has been placed on the pending list, but not scan that field before that stage, as the field is used by the concurrent GC thread to link discovered objects. When ReferenceHandleR thread does not use the discovered field for the purpose of linking the elements in the pending list, as would be the case in older JDKs, the JVM will fall back to the old behaviour of using the next field for that purpose. Reviewed-by: jcoomes, mchung, stefank
author ysr
date Wed, 07 Sep 2011 13:55:42 -0700
parents ddd894528dbc
children c26de9aef2ed
line wrap: on
line source

/*
 * Copyright (c) 2008, 2011, 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 "interpreter/rewriter.hpp"
#include "memory/oopFactory.hpp"
#include "prims/methodHandleWalk.hpp"

/*
 * JSR 292 reference implementation: method handle structure analysis
 */

#ifdef PRODUCT
#define print_method_handle(mh) {}
#else //PRODUCT
extern "C" void print_method_handle(oop mh);
#endif //PRODUCT

// -----------------------------------------------------------------------------
// MethodHandleChain

void MethodHandleChain::set_method_handle(Handle mh, TRAPS) {
  if (!java_lang_invoke_MethodHandle::is_instance(mh()))  lose("bad method handle", CHECK);

  // set current method handle and unpack partially
  _method_handle = mh;
  _is_last       = false;
  _is_bound      = false;
  _arg_slot      = -1;
  _arg_type      = T_VOID;
  _conversion    = -1;
  _last_invoke   = Bytecodes::_nop;  //arbitrary non-garbage

  if (java_lang_invoke_DirectMethodHandle::is_instance(mh())) {
    set_last_method(mh(), THREAD);
    return;
  }
  if (java_lang_invoke_AdapterMethodHandle::is_instance(mh())) {
    _conversion = AdapterMethodHandle_conversion();
    assert(_conversion != -1, "bad conv value");
    assert(java_lang_invoke_BoundMethodHandle::is_instance(mh()), "also BMH");
  }
  if (java_lang_invoke_BoundMethodHandle::is_instance(mh())) {
    if (!is_adapter())          // keep AMH and BMH separate in this model
      _is_bound = true;
    _arg_slot = BoundMethodHandle_vmargslot();
    oop target = MethodHandle_vmtarget_oop();
    if (!is_bound() || java_lang_invoke_MethodHandle::is_instance(target)) {
      _arg_type = compute_bound_arg_type(target, NULL, _arg_slot, CHECK);
    } else if (target != NULL && target->is_method()) {
      methodOop m = (methodOop) target;
      _arg_type = compute_bound_arg_type(NULL, m, _arg_slot, CHECK);
      set_last_method(mh(), CHECK);
    } else {
      _is_bound = false;  // lose!
    }
  }
  if (is_bound() && _arg_type == T_VOID) {
    lose("bad vmargslot", CHECK);
  }
  if (!is_bound() && !is_adapter()) {
    lose("unrecognized MH type", CHECK);
  }
}


void MethodHandleChain::set_last_method(oop target, TRAPS) {
  _is_last = true;
  KlassHandle receiver_limit; int flags = 0;
  _last_method = MethodHandles::decode_method(target, receiver_limit, flags);
  if ((flags & MethodHandles::_dmf_has_receiver) == 0)
    _last_invoke = Bytecodes::_invokestatic;
  else if ((flags & MethodHandles::_dmf_does_dispatch) == 0)
    _last_invoke = Bytecodes::_invokespecial;
  else if ((flags & MethodHandles::_dmf_from_interface) != 0)
    _last_invoke = Bytecodes::_invokeinterface;
  else
    _last_invoke = Bytecodes::_invokevirtual;
}


BasicType MethodHandleChain::compute_bound_arg_type(oop target, methodOop m, int arg_slot, TRAPS) {
  // There is no direct indication of whether the argument is primitive or not.
  // It is implied by the _vmentry code, and by the MethodType of the target.
  BasicType arg_type = T_VOID;
  if (target != NULL) {
    oop mtype = java_lang_invoke_MethodHandle::type(target);
    int arg_num = MethodHandles::argument_slot_to_argnum(mtype, arg_slot);
    if (arg_num >= 0) {
      oop ptype = java_lang_invoke_MethodType::ptype(mtype, arg_num);
      arg_type = java_lang_Class::as_BasicType(ptype);
    }
  } else if (m != NULL) {
    // figure out the argument type from the slot
    // FIXME: make this explicit in the MH
    int cur_slot = m->size_of_parameters();
    if (arg_slot >= cur_slot)
      return T_VOID;
    if (!m->is_static()) {
      cur_slot -= type2size[T_OBJECT];
      if (cur_slot == arg_slot)
        return T_OBJECT;
    }
    ResourceMark rm(THREAD);
    for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) {
      BasicType bt = ss.type();
      cur_slot -= type2size[bt];
      if (cur_slot <= arg_slot) {
        if (cur_slot == arg_slot)
          arg_type = bt;
        break;
      }
    }
  }
  if (arg_type == T_ARRAY)
    arg_type = T_OBJECT;
  return arg_type;
}


void MethodHandleChain::lose(const char* msg, TRAPS) {
  _lose_message = msg;
#ifdef ASSERT
  if (Verbose) {
    tty->print_cr(INTPTR_FORMAT " lose: %s", _method_handle(), msg);
    print();
  }
#endif
  if (!THREAD->is_Java_thread() || ((JavaThread*)THREAD)->thread_state() != _thread_in_vm) {
    // throw a preallocated exception
    THROW_OOP(Universe::virtual_machine_error_instance());
  }
  THROW_MSG(vmSymbols::java_lang_InternalError(), msg);
}


#ifdef ASSERT
static const char* adapter_ops[] = {
  "retype_only"  ,
  "retype_raw"   ,
  "check_cast"   ,
  "prim_to_prim" ,
  "ref_to_prim"  ,
  "prim_to_ref"  ,
  "swap_args"    ,
  "rot_args"     ,
  "dup_args"     ,
  "drop_args"    ,
  "collect_args" ,
  "spread_args"  ,
  "fold_args"
};

static const char* adapter_op_to_string(int op) {
  if (op >= 0 && op < (int)ARRAY_SIZE(adapter_ops))
    return adapter_ops[op];
  return "unknown_op";
}

void MethodHandleChain::print(oopDesc* m) {
  HandleMark hm;
  ResourceMark rm;
  Handle mh(m);
  print(mh);
}

void MethodHandleChain::print(Handle mh) {
  EXCEPTION_MARK;
  MethodHandleChain mhc(mh, THREAD);
  if (HAS_PENDING_EXCEPTION) {
    oop ex = THREAD->pending_exception();
    CLEAR_PENDING_EXCEPTION;
    ex->print();
    return;
  }
  mhc.print();
}


void MethodHandleChain::print() {
  EXCEPTION_MARK;
  print_impl(THREAD);
  if (HAS_PENDING_EXCEPTION) {
    oop ex = THREAD->pending_exception();
    CLEAR_PENDING_EXCEPTION;
    ex->print();
  }
}

void MethodHandleChain::print_impl(TRAPS) {
  ResourceMark rm;

  MethodHandleChain chain(_root, CHECK);
  for (;;) {
    tty->print(INTPTR_FORMAT ": ", chain.method_handle()());
    if (chain.is_bound()) {
      tty->print("bound: arg_type %s arg_slot %d",
                 type2name(chain.bound_arg_type()),
                 chain.bound_arg_slot());
      oop o = chain.bound_arg_oop();
      if (o != NULL) {
        if (o->is_instance()) {
          tty->print(" instance %s", o->klass()->klass_part()->internal_name());
        } else {
          o->print();
        }
      }
    } else if (chain.is_adapter()) {
      tty->print("adapter: arg_slot %d conversion op %s",
                 chain.adapter_arg_slot(),
                 adapter_op_to_string(chain.adapter_conversion_op()));
      switch (chain.adapter_conversion_op()) {
        case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
        case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW:
        case java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST:
        case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM:
        case java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM:
          break;

        case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF: {
          tty->print(" src_type = %s", type2name(chain.adapter_conversion_src_type()));
          break;
        }

        case java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS:
        case java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS: {
          int dest_arg_slot = chain.adapter_conversion_vminfo();
          tty->print(" dest_arg_slot %d type %s", dest_arg_slot, type2name(chain.adapter_conversion_src_type()));
          break;
        }

        case java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS:
        case java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS: {
          int dup_slots = chain.adapter_conversion_stack_pushes();
          tty->print(" pushes %d", dup_slots);
          break;
        }

        case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS:
        case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: {
          int coll_slots = chain.MethodHandle_vmslots();
          tty->print(" coll_slots %d", coll_slots);
          break;
        }

        case java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS: {
          // Check the required length.
          int spread_slots = 1 + chain.adapter_conversion_stack_pushes();
          tty->print(" spread_slots %d", spread_slots);
          break;
        }

        default:
          tty->print_cr("bad adapter conversion");
          break;
      }
    } else {
      // DMH
      tty->print("direct: ");
      chain.last_method_oop()->print_short_name(tty);
    }

    tty->print(" (");
    objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(chain.method_type_oop());
    for (int i = ptypes->length() - 1; i >= 0; i--) {
      BasicType t = java_lang_Class::as_BasicType(ptypes->obj_at(i));
      if (t == T_ARRAY) t = T_OBJECT;
      tty->print("%c", type2char(t));
      if (t == T_LONG || t == T_DOUBLE) tty->print("_");
    }
    tty->print(")");
    BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(chain.method_type_oop()));
    if (rtype == T_ARRAY) rtype = T_OBJECT;
    tty->print("%c", type2char(rtype));
    tty->cr();
    if (!chain.is_last()) {
      chain.next(CHECK);
    } else {
      break;
    }
  }
}
#endif


// -----------------------------------------------------------------------------
// MethodHandleWalker

Bytecodes::Code MethodHandleWalker::conversion_code(BasicType src, BasicType dest) {
  if (is_subword_type(src)) {
    src = T_INT;          // all subword src types act like int
  }
  if (src == dest) {
    return Bytecodes::_nop;
  }

#define SRC_DEST(s,d) (((int)(s) << 4) + (int)(d))
  switch (SRC_DEST(src, dest)) {
  case SRC_DEST(T_INT, T_LONG):           return Bytecodes::_i2l;
  case SRC_DEST(T_INT, T_FLOAT):          return Bytecodes::_i2f;
  case SRC_DEST(T_INT, T_DOUBLE):         return Bytecodes::_i2d;
  case SRC_DEST(T_INT, T_BYTE):           return Bytecodes::_i2b;
  case SRC_DEST(T_INT, T_CHAR):           return Bytecodes::_i2c;
  case SRC_DEST(T_INT, T_SHORT):          return Bytecodes::_i2s;

  case SRC_DEST(T_LONG, T_INT):           return Bytecodes::_l2i;
  case SRC_DEST(T_LONG, T_FLOAT):         return Bytecodes::_l2f;
  case SRC_DEST(T_LONG, T_DOUBLE):        return Bytecodes::_l2d;

  case SRC_DEST(T_FLOAT, T_INT):          return Bytecodes::_f2i;
  case SRC_DEST(T_FLOAT, T_LONG):         return Bytecodes::_f2l;
  case SRC_DEST(T_FLOAT, T_DOUBLE):       return Bytecodes::_f2d;

  case SRC_DEST(T_DOUBLE, T_INT):         return Bytecodes::_d2i;
  case SRC_DEST(T_DOUBLE, T_LONG):        return Bytecodes::_d2l;
  case SRC_DEST(T_DOUBLE, T_FLOAT):       return Bytecodes::_d2f;
  }
#undef SRC_DEST

  // cannot do it in one step, or at all
  return Bytecodes::_illegal;
}


// -----------------------------------------------------------------------------
// MethodHandleWalker::walk
//
MethodHandleWalker::ArgToken
MethodHandleWalker::walk(TRAPS) {
  ArgToken empty = ArgToken();  // Empty return value.

  walk_incoming_state(CHECK_(empty));

  for (;;) {
    set_method_handle(chain().method_handle_oop());

    assert(_outgoing_argc == argument_count_slow(), "empty slots under control");

    if (chain().is_adapter()) {
      int conv_op = chain().adapter_conversion_op();
      int arg_slot = chain().adapter_arg_slot();

      // Check that the arg_slot is valid.  In most cases it must be
      // within range of the current arguments but there are some
      // exceptions.  Those are sanity checked in their implemention
      // below.
      if ((arg_slot < 0 || arg_slot >= _outgoing.length()) &&
          conv_op > java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW &&
          conv_op != java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS &&
          conv_op != java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS) {
        lose(err_msg("bad argument index %d", arg_slot), CHECK_(empty));
      }

      bool retain_original_args = false;  // used by fold/collect logic

      // perform the adapter action
      switch (conv_op) {
      case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
        // No changes to arguments; pass the bits through.
        break;

      case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW: {
        // To keep the verifier happy, emit bitwise ("raw") conversions as needed.
        // See MethodHandles::same_basic_type_for_arguments for allowed conversions.
        Handle incoming_mtype(THREAD, chain().method_type_oop());
        Handle outgoing_mtype;
        {
          oop outgoing_mh_oop = chain().vmtarget_oop();
          if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop))
            lose("outgoing target not a MethodHandle", CHECK_(empty));
          outgoing_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop));
        }

        int nptypes = java_lang_invoke_MethodType::ptype_count(outgoing_mtype());
        if (nptypes != java_lang_invoke_MethodType::ptype_count(incoming_mtype()))
          lose("incoming and outgoing parameter count do not agree", CHECK_(empty));

        // Argument types.
        for (int i = 0, slot = _outgoing.length() - 1; slot >= 0; slot--) {
          if (arg_type(slot) == T_VOID)  continue;

          klassOop  src_klass = NULL;
          klassOop  dst_klass = NULL;
          BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &src_klass);
          BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &dst_klass);
          retype_raw_argument_type(src, dst, slot, CHECK_(empty));
          i++;  // We need to skip void slots at the top of the loop.
        }

        // Return type.
        {
          BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype()));
          BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype()));
          retype_raw_return_type(src, dst, CHECK_(empty));
        }
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST: {
        // checkcast the Nth outgoing argument in place
        klassOop dest_klass = NULL;
        BasicType dest = java_lang_Class::as_BasicType(chain().adapter_arg_oop(), &dest_klass);
        assert(dest == T_OBJECT, "");
        ArgToken arg = _outgoing.at(arg_slot);
        assert(dest == arg.basic_type(), "");
        arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty));
        // replace the object by the result of the cast, to make the compiler happy:
        change_argument(T_OBJECT, arg_slot, T_OBJECT, arg);
        debug_only(dest_klass = (klassOop)badOop);
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM: {
        // i2l, etc., on the Nth outgoing argument in place
        BasicType src = chain().adapter_conversion_src_type(),
                  dest = chain().adapter_conversion_dest_type();
        ArgToken arg = _outgoing.at(arg_slot);
        Bytecodes::Code bc = conversion_code(src, dest);
        if (bc == Bytecodes::_nop) {
          break;
        } else if (bc != Bytecodes::_illegal) {
          arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
        } else if (is_subword_type(dest)) {
          bc = conversion_code(src, T_INT);
          if (bc != Bytecodes::_illegal) {
            arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
            bc = conversion_code(T_INT, dest);
            arg = make_conversion(dest, NULL, bc, arg, CHECK_(empty));
          }
        }
        if (bc == Bytecodes::_illegal) {
          lose(err_msg("bad primitive conversion for %s -> %s", type2name(src), type2name(dest)), CHECK_(empty));
        }
        change_argument(src, arg_slot, dest, arg);
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM: {
        // checkcast to wrapper type & call intValue, etc.
        BasicType dest = chain().adapter_conversion_dest_type();
        ArgToken arg = _outgoing.at(arg_slot);
        arg = make_conversion(T_OBJECT, SystemDictionary::box_klass(dest),
                              Bytecodes::_checkcast, arg, CHECK_(empty));
        vmIntrinsics::ID unboxer = vmIntrinsics::for_unboxing(dest);
        if (unboxer == vmIntrinsics::_none) {
          lose("no unboxing method", CHECK_(empty));
        }
        ArgToken arglist[2];
        arglist[0] = arg;         // outgoing 'this'
        arglist[1] = ArgToken();  // sentinel
        arg = make_invoke(methodHandle(), unboxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty));
        change_argument(T_OBJECT, arg_slot, dest, arg);
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF: {
        // call wrapper type.valueOf
        BasicType src = chain().adapter_conversion_src_type();
        vmIntrinsics::ID boxer = vmIntrinsics::for_boxing(src);
        if (boxer == vmIntrinsics::_none) {
          lose("no boxing method", CHECK_(empty));
        }
        ArgToken arg = _outgoing.at(arg_slot);
        ArgToken arglist[2];
        arglist[0] = arg;         // outgoing value
        arglist[1] = ArgToken();  // sentinel
        arg = make_invoke(methodHandle(), boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
        change_argument(src, arg_slot, T_OBJECT, arg);
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS: {
        int dest_arg_slot = chain().adapter_conversion_vminfo();
        if (!has_argument(dest_arg_slot)) {
          lose("bad swap index", CHECK_(empty));
        }
        // a simple swap between two arguments
        if (arg_slot > dest_arg_slot) {
          int tmp = arg_slot;
          arg_slot = dest_arg_slot;
          dest_arg_slot = tmp;
        }
        ArgToken a1 = _outgoing.at(arg_slot);
        ArgToken a2 = _outgoing.at(dest_arg_slot);
        change_argument(a2.basic_type(), dest_arg_slot, a1);
        change_argument(a1.basic_type(), arg_slot, a2);
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS: {
        int limit_raw  = chain().adapter_conversion_vminfo();
        bool rot_down  = (arg_slot < limit_raw);
        int limit_bias = (rot_down ? MethodHandles::OP_ROT_ARGS_DOWN_LIMIT_BIAS : 0);
        int limit_slot = limit_raw - limit_bias;
        if ((uint)limit_slot > (uint)_outgoing.length()) {
          lose("bad rotate index", CHECK_(empty));
        }
        // Rotate the source argument (plus following N slots) into the
        // position occupied by the dest argument (plus following N slots).
        int rotate_count = type2size[chain().adapter_conversion_src_type()];
        // (no other rotate counts are currently supported)
        if (rot_down) {
          for (int i = 0; i < rotate_count; i++) {
            ArgToken temp = _outgoing.at(arg_slot);
            _outgoing.remove_at(arg_slot);
            _outgoing.insert_before(limit_slot - 1, temp);
          }
        } else { // arg_slot > limit_slot => rotate_up
          for (int i = 0; i < rotate_count; i++) {
            ArgToken temp = _outgoing.at(arg_slot + rotate_count - 1);
            _outgoing.remove_at(arg_slot + rotate_count - 1);
            _outgoing.insert_before(limit_slot, temp);
          }
        }
        assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS: {
        int dup_slots = chain().adapter_conversion_stack_pushes();
        if (dup_slots <= 0) {
          lose("bad dup count", CHECK_(empty));
        }
        for (int i = 0; i < dup_slots; i++) {
          ArgToken dup = _outgoing.at(arg_slot + 2*i);
          if (dup.basic_type() != T_VOID)     _outgoing_argc += 1;
          _outgoing.insert_before(i, dup);
        }
        assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS: {
        int drop_slots = -chain().adapter_conversion_stack_pushes();
        if (drop_slots <= 0) {
          lose("bad drop count", CHECK_(empty));
        }
        for (int i = 0; i < drop_slots; i++) {
          ArgToken drop = _outgoing.at(arg_slot);
          if (drop.basic_type() != T_VOID)    _outgoing_argc -= 1;
          _outgoing.remove_at(arg_slot);
        }
        assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS:
        retain_original_args = true;   // and fall through:
      case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: {
        // call argument MH recursively
        //{static int x; if (!x++) print_method_handle(chain().method_handle_oop()); --x;}
        Handle recursive_mh(THREAD, chain().adapter_arg_oop());
        if (!java_lang_invoke_MethodHandle::is_instance(recursive_mh())) {
          lose("recursive target not a MethodHandle", CHECK_(empty));
        }
        Handle recursive_mtype(THREAD, java_lang_invoke_MethodHandle::type(recursive_mh()));
        int argc = java_lang_invoke_MethodType::ptype_count(recursive_mtype());
        int coll_slots = java_lang_invoke_MethodHandle::vmslots(recursive_mh());
        BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(recursive_mtype()));
        ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, 1 + argc + 1);  // 1+: mh, +1: sentinel
        arglist[0] = make_oop_constant(recursive_mh(), CHECK_(empty));
        if (arg_slot < 0 || coll_slots < 0 || arg_slot + coll_slots > _outgoing.length()) {
          lose("bad fold/collect arg slot", CHECK_(empty));
        }
        for (int i = 0, slot = arg_slot + coll_slots - 1; slot >= arg_slot; slot--) {
          ArgToken arg_state = _outgoing.at(slot);
          BasicType  arg_type  = arg_state.basic_type();
          if (arg_type == T_VOID)  continue;
          ArgToken arg = _outgoing.at(slot);
          if (i >= argc) { lose("bad fold/collect arg", CHECK_(empty)); }
          arglist[1+i] = arg;
          if (!retain_original_args)
            change_argument(arg_type, slot, T_VOID, ArgToken(tt_void));
          i++;
        }
        arglist[1+argc] = ArgToken();  // sentinel
        oop invoker = java_lang_invoke_MethodTypeForm::vmlayout(
                          java_lang_invoke_MethodType::form(recursive_mtype()) );
        if (invoker == NULL || !invoker->is_method()) {
          lose("bad vmlayout slot", CHECK_(empty));
        }
        // FIXME: consider inlining the invokee at the bytecode level
        ArgToken ret = make_invoke(methodHandle(THREAD, methodOop(invoker)), vmIntrinsics::_invokeGeneric,
                                   Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty));
        // The iid = _invokeGeneric really means to adjust reference types as needed.
        DEBUG_ONLY(invoker = NULL);
        if (rtype == T_OBJECT) {
          klassOop rklass = java_lang_Class::as_klassOop( java_lang_invoke_MethodType::rtype(recursive_mtype()) );
          if (rklass != SystemDictionary::Object_klass() &&
              !Klass::cast(rklass)->is_interface()) {
            // preserve type safety
            ret = make_conversion(T_OBJECT, rklass, Bytecodes::_checkcast, ret, CHECK_(empty));
          }
        }
        if (rtype != T_VOID) {
          int ret_slot = arg_slot + (retain_original_args ? coll_slots : 0);
          change_argument(T_VOID, ret_slot, rtype, ret);
        }
        break;
      }

      case java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS: {
        klassOop array_klass_oop = NULL;
        BasicType array_type = java_lang_Class::as_BasicType(chain().adapter_arg_oop(),
                                                             &array_klass_oop);
        assert(array_type == T_OBJECT, "");
        assert(Klass::cast(array_klass_oop)->oop_is_array(), "");
        arrayKlassHandle array_klass(THREAD, array_klass_oop);
        debug_only(array_klass_oop = (klassOop)badOop);

        klassOop element_klass_oop = NULL;
        BasicType element_type = java_lang_Class::as_BasicType(array_klass->component_mirror(),
                                                               &element_klass_oop);
        KlassHandle element_klass(THREAD, element_klass_oop);
        debug_only(element_klass_oop = (klassOop)badOop);

        // Fetch the argument, which we will cast to the required array type.
        ArgToken arg = _outgoing.at(arg_slot);
        assert(arg.basic_type() == T_OBJECT, "");
        ArgToken array_arg = arg;
        array_arg = make_conversion(T_OBJECT, array_klass(), Bytecodes::_checkcast, array_arg, CHECK_(empty));
        change_argument(T_OBJECT, arg_slot, T_VOID, ArgToken(tt_void));

        // Check the required length.
        int spread_slots = 1 + chain().adapter_conversion_stack_pushes();
        int spread_length = spread_slots;
        if (type2size[element_type] == 2) {
          if (spread_slots % 2 != 0)  spread_slots = -1;  // force error
          spread_length = spread_slots / 2;
        }
        if (spread_slots < 0) {
          lose("bad spread length", CHECK_(empty));
        }

        jvalue   length_jvalue;  length_jvalue.i = spread_length;
        ArgToken length_arg = make_prim_constant(T_INT, &length_jvalue, CHECK_(empty));
        // Call a built-in method known to the JVM to validate the length.
        ArgToken arglist[3];
        arglist[0] = array_arg;   // value to check
        arglist[1] = length_arg;  // length to check
        arglist[2] = ArgToken();  // sentinel
        make_invoke(methodHandle(), vmIntrinsics::_checkSpreadArgument,
                    Bytecodes::_invokestatic, false, 2, &arglist[0], CHECK_(empty));

        // Spread out the array elements.
        Bytecodes::Code aload_op = Bytecodes::_nop;
        switch (element_type) {
        case T_INT:       aload_op = Bytecodes::_iaload; break;
        case T_LONG:      aload_op = Bytecodes::_laload; break;
        case T_FLOAT:     aload_op = Bytecodes::_faload; break;
        case T_DOUBLE:    aload_op = Bytecodes::_daload; break;
        case T_OBJECT:    aload_op = Bytecodes::_aaload; break;
        case T_BOOLEAN:   // fall through:
        case T_BYTE:      aload_op = Bytecodes::_baload; break;
        case T_CHAR:      aload_op = Bytecodes::_caload; break;
        case T_SHORT:     aload_op = Bytecodes::_saload; break;
        default:          lose("primitive array NYI", CHECK_(empty));
        }
        int ap = arg_slot;
        for (int i = 0; i < spread_length; i++) {
          jvalue   offset_jvalue;  offset_jvalue.i = i;
          ArgToken offset_arg = make_prim_constant(T_INT, &offset_jvalue, CHECK_(empty));
          ArgToken element_arg = make_fetch(element_type, element_klass(), aload_op, array_arg, offset_arg, CHECK_(empty));
          change_argument(T_VOID, ap, element_type, element_arg);
          //ap += type2size[element_type];  // don't do this; insert next arg to *right* of previous
        }
        break;
      }

      default:
        lose("bad adapter conversion", CHECK_(empty));
        break;
      }
    }

    if (chain().is_bound()) {
      // push a new argument
      BasicType arg_type  = chain().bound_arg_type();
      jint      arg_slot  = chain().bound_arg_slot();
      oop       arg_oop   = chain().bound_arg_oop();
      ArgToken  arg;
      if (arg_type == T_OBJECT) {
        arg = make_oop_constant(arg_oop, CHECK_(empty));
      } else {
        jvalue arg_value;
        BasicType bt = java_lang_boxing_object::get_value(arg_oop, &arg_value);
        if (bt == arg_type || (bt == T_INT && is_subword_type(arg_type))) {
          arg = make_prim_constant(arg_type, &arg_value, CHECK_(empty));
        } else {
          lose(err_msg("bad bound value: arg_type %s boxing %s", type2name(arg_type), type2name(bt)), CHECK_(empty));
        }
      }
      DEBUG_ONLY(arg_oop = badOop);
      change_argument(T_VOID, arg_slot, arg_type, arg);
    }

    // this test must come after the body of the loop
    if (!chain().is_last()) {
      chain().next(CHECK_(empty));
    } else {
      break;
    }
  }

  // finish the sequence with a tail-call to the ultimate target
  // parameters are passed in logical order (recv 1st), not slot order
  ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, _outgoing.length() + 1);
  int ap = 0;
  for (int i = _outgoing.length() - 1; i >= 0; i--) {
    ArgToken arg_state = _outgoing.at(i);
    if (arg_state.basic_type() == T_VOID)  continue;
    arglist[ap++] = _outgoing.at(i);
  }
  assert(ap == _outgoing_argc, "");
  arglist[ap] = ArgToken();  // add a sentinel, for the sake of asserts
  return make_invoke(chain().last_method(),
                     vmIntrinsics::_none,
                     chain().last_invoke_code(), true,
                     ap, arglist, THREAD);
}


// -----------------------------------------------------------------------------
// MethodHandleWalker::walk_incoming_state
//
void MethodHandleWalker::walk_incoming_state(TRAPS) {
  Handle mtype(THREAD, chain().method_type_oop());
  int nptypes = java_lang_invoke_MethodType::ptype_count(mtype());
  _outgoing_argc = nptypes;
  int argp = nptypes - 1;
  if (argp >= 0) {
    _outgoing.at_grow(argp, ArgToken(tt_void)); // presize
  }
  for (int i = 0; i < nptypes; i++) {
    klassOop  arg_type_klass = NULL;
    BasicType arg_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass);
    int index = new_local_index(arg_type);
    ArgToken arg = make_parameter(arg_type, arg_type_klass, index, CHECK);
    DEBUG_ONLY(arg_type_klass = (klassOop) NULL);
    _outgoing.at_put(argp, arg);
    if (type2size[arg_type] == 2) {
      // add the extra slot, so we can model the JVM stack
      _outgoing.insert_before(argp+1, ArgToken(tt_void));
    }
    --argp;
  }
  // call make_parameter at the end of the list for the return type
  klassOop  ret_type_klass = NULL;
  BasicType ret_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass);
  ArgToken  ret = make_parameter(ret_type, ret_type_klass, -1, CHECK);
  // ignore ret; client can catch it if needed

  assert(_outgoing_argc == argument_count_slow(), "empty slots under control");

  verify_args_and_signature(CHECK);
}


#ifdef ASSERT
void MethodHandleWalker::verify_args_and_signature(TRAPS) {
  int index = _outgoing.length() - 1;
  objArrayOop ptypes = java_lang_invoke_MethodType::ptypes(chain().method_type_oop());
  for (int i = 0, limit = ptypes->length(); i < limit; i++) {
    BasicType t = java_lang_Class::as_BasicType(ptypes->obj_at(i));
    if (t == T_ARRAY) t = T_OBJECT;
    if (t == T_LONG || t == T_DOUBLE) {
      assert(T_VOID == _outgoing.at(index).basic_type(), "types must match");
      index--;
    }
    assert(t == _outgoing.at(index).basic_type(), "types must match");
    index--;
  }
}
#endif


// -----------------------------------------------------------------------------
// MethodHandleWalker::change_argument
//
// This is messy because some kinds of arguments are paired with
// companion slots containing an empty value.
void MethodHandleWalker::change_argument(BasicType old_type, int slot, const ArgToken& new_arg) {
  BasicType new_type = new_arg.basic_type();
  int old_size = type2size[old_type];
  int new_size = type2size[new_type];
  if (old_size == new_size) {
    // simple case first
    _outgoing.at_put(slot, new_arg);
  } else if (old_size > new_size) {
    for (int i = old_size - 1; i >= new_size; i--) {
      assert((i != 0) == (_outgoing.at(slot + i).basic_type() == T_VOID), "");
      _outgoing.remove_at(slot + i);
    }
    if (new_size > 0)
      _outgoing.at_put(slot, new_arg);
    else
      _outgoing_argc -= 1;      // deleted a real argument
  } else {
    for (int i = old_size; i < new_size; i++) {
      _outgoing.insert_before(slot + i, ArgToken(tt_void));
    }
    _outgoing.at_put(slot, new_arg);
    if (old_size == 0)
      _outgoing_argc += 1;      // inserted a real argument
  }
  assert(_outgoing_argc == argument_count_slow(), "empty slots under control");
}


#ifdef ASSERT
int MethodHandleWalker::argument_count_slow() {
  int args_seen = 0;
  for (int i = _outgoing.length() - 1; i >= 0; i--) {
    if (_outgoing.at(i).basic_type() != T_VOID) {
      ++args_seen;
      if (_outgoing.at(i).basic_type() == T_LONG ||
          _outgoing.at(i).basic_type() == T_DOUBLE) {
        assert(_outgoing.at(i + 1).basic_type() == T_VOID, "should only follow two word");
      }
    } else {
      assert(_outgoing.at(i - 1).basic_type() == T_LONG ||
             _outgoing.at(i - 1).basic_type() == T_DOUBLE, "should only follow two word");
    }
  }
  return args_seen;
}
#endif


// -----------------------------------------------------------------------------
// MethodHandleWalker::retype_raw_conversion
//
// Do the raw retype conversions for OP_RETYPE_RAW.
void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS) {
  if (src != dst) {
    if (MethodHandles::same_basic_type_for_returns(src, dst, /*raw*/ true)) {
      if (MethodHandles::is_float_fixed_reinterpretation_cast(src, dst)) {
        vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(src, dst);
        if (iid == vmIntrinsics::_none) {
          lose("no raw conversion method", CHECK);
        }
        ArgToken arglist[2];
        if (!for_return) {
          // argument type conversion
          ArgToken arg = _outgoing.at(slot);
          assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity");
          arglist[0] = arg;         // outgoing 'this'
          arglist[1] = ArgToken();  // sentinel
          arg = make_invoke(methodHandle(), iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
          change_argument(src, slot, dst, arg);
        } else {
          // return type conversion
          if (_return_conv == vmIntrinsics::_none) {
            _return_conv = iid;
          } else if (_return_conv == vmIntrinsics::for_raw_conversion(dst, src)) {
            _return_conv = vmIntrinsics::_none;
          } else if (_return_conv != zero_return_conv()) {
            lose(err_msg("requested raw return conversion not allowed: %s -> %s (before %s)", type2name(src), type2name(dst), vmIntrinsics::name_at(_return_conv)), CHECK);
          }
        }
      } else {
        // Nothing to do.
      }
    } else if (for_return && (!is_subword_type(src) || !is_subword_type(dst))) {
      // This can occur in exception-throwing MHs, which have a fictitious return value encoded as Void or Empty.
      _return_conv = zero_return_conv();
    } else if (src == T_OBJECT && is_java_primitive(dst)) {
      // ref-to-prim: discard ref, push zero
      lose("requested ref-to-prim conversion not expected", CHECK);
    } else {
      lose(err_msg("requested raw conversion not allowed: %s -> %s", type2name(src), type2name(dst)), CHECK);
    }
  }
}


// -----------------------------------------------------------------------------
// MethodHandleCompiler

MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* signature, int invoke_count, bool is_invokedynamic, TRAPS)
  : MethodHandleWalker(root, is_invokedynamic, THREAD),
    _invoke_count(invoke_count),
    _thread(THREAD),
    _bytecode(THREAD, 50),
    _constants(THREAD, 10),
    _non_bcp_klasses(THREAD, 5),
    _cur_stack(0),
    _max_stack(0),
    _rtype(T_ILLEGAL)
{

  // Element zero is always the null constant.
  (void) _constants.append(NULL);

  // Set name and signature index.
  _name_index      = cpool_symbol_put(name);
  _signature_index = cpool_symbol_put(signature);

  // To make the resulting methods more recognizable by
  // stack walkers and compiler heuristics,
  // we put them in holder class MethodHandle.
  // See klass_is_method_handle_adapter_holder
  // and methodOopDesc::is_method_handle_adapter.
  _target_klass = SystemDictionaryHandles::MethodHandle_klass();

  check_non_bcp_klasses(java_lang_invoke_MethodHandle::type(root()), CHECK);

  // Get return type klass.
  Handle first_mtype(THREAD, chain().method_type_oop());
  // _rklass is NULL for primitives.
  _rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(first_mtype()), &_rklass);
  if (_rtype == T_ARRAY)  _rtype = T_OBJECT;

  ArgumentSizeComputer args(signature);
  int params = args.size() + 1;  // Incoming arguments plus receiver.
  _num_params = for_invokedynamic() ? params - 1 : params;  // XXX Check if callee is static?
}


// -----------------------------------------------------------------------------
// MethodHandleCompiler::compile
//
// Compile this MethodHandle into a bytecode adapter and return a
// methodOop.
methodHandle MethodHandleCompiler::compile(TRAPS) {
  assert(_thread == THREAD, "must be same thread");
  methodHandle nullHandle;
  (void) walk(CHECK_(nullHandle));
  record_non_bcp_klasses();
  return get_method_oop(CHECK_(nullHandle));
}


void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index, int args_size) {
  Bytecodes::check(op);  // Are we legal?

  switch (op) {
  // b
  case Bytecodes::_aconst_null:
  case Bytecodes::_iconst_m1:
  case Bytecodes::_iconst_0:
  case Bytecodes::_iconst_1:
  case Bytecodes::_iconst_2:
  case Bytecodes::_iconst_3:
  case Bytecodes::_iconst_4:
  case Bytecodes::_iconst_5:
  case Bytecodes::_lconst_0:
  case Bytecodes::_lconst_1:
  case Bytecodes::_fconst_0:
  case Bytecodes::_fconst_1:
  case Bytecodes::_fconst_2:
  case Bytecodes::_dconst_0:
  case Bytecodes::_dconst_1:
  case Bytecodes::_iload_0:
  case Bytecodes::_iload_1:
  case Bytecodes::_iload_2:
  case Bytecodes::_iload_3:
  case Bytecodes::_lload_0:
  case Bytecodes::_lload_1:
  case Bytecodes::_lload_2:
  case Bytecodes::_lload_3:
  case Bytecodes::_fload_0:
  case Bytecodes::_fload_1:
  case Bytecodes::_fload_2:
  case Bytecodes::_fload_3:
  case Bytecodes::_dload_0:
  case Bytecodes::_dload_1:
  case Bytecodes::_dload_2:
  case Bytecodes::_dload_3:
  case Bytecodes::_aload_0:
  case Bytecodes::_aload_1:
  case Bytecodes::_aload_2:
  case Bytecodes::_aload_3:
  case Bytecodes::_istore_0:
  case Bytecodes::_istore_1:
  case Bytecodes::_istore_2:
  case Bytecodes::_istore_3:
  case Bytecodes::_lstore_0:
  case Bytecodes::_lstore_1:
  case Bytecodes::_lstore_2:
  case Bytecodes::_lstore_3:
  case Bytecodes::_fstore_0:
  case Bytecodes::_fstore_1:
  case Bytecodes::_fstore_2:
  case Bytecodes::_fstore_3:
  case Bytecodes::_dstore_0:
  case Bytecodes::_dstore_1:
  case Bytecodes::_dstore_2:
  case Bytecodes::_dstore_3:
  case Bytecodes::_astore_0:
  case Bytecodes::_astore_1:
  case Bytecodes::_astore_2:
  case Bytecodes::_astore_3:
  case Bytecodes::_iand:
  case Bytecodes::_i2l:
  case Bytecodes::_i2f:
  case Bytecodes::_i2d:
  case Bytecodes::_i2b:
  case Bytecodes::_i2c:
  case Bytecodes::_i2s:
  case Bytecodes::_l2i:
  case Bytecodes::_l2f:
  case Bytecodes::_l2d:
  case Bytecodes::_f2i:
  case Bytecodes::_f2l:
  case Bytecodes::_f2d:
  case Bytecodes::_d2i:
  case Bytecodes::_d2l:
  case Bytecodes::_d2f:
  case Bytecodes::_iaload:
  case Bytecodes::_laload:
  case Bytecodes::_faload:
  case Bytecodes::_daload:
  case Bytecodes::_aaload:
  case Bytecodes::_baload:
  case Bytecodes::_caload:
  case Bytecodes::_saload:
  case Bytecodes::_ireturn:
  case Bytecodes::_lreturn:
  case Bytecodes::_freturn:
  case Bytecodes::_dreturn:
  case Bytecodes::_areturn:
  case Bytecodes::_return:
    assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_b, "wrong bytecode format");
    _bytecode.push(op);
    break;

  // bi
  case Bytecodes::_ldc:
    assert(Bytecodes::format_bits(op, false) == (Bytecodes::_fmt_b|Bytecodes::_fmt_has_k), "wrong bytecode format");
    if (index == (index & 0xff)) {
      _bytecode.push(op);
      _bytecode.push(index);
    } else {
      _bytecode.push(Bytecodes::_ldc_w);
      _bytecode.push(index >> 8);
      _bytecode.push(index);
    }
    break;

  case Bytecodes::_iload:
  case Bytecodes::_lload:
  case Bytecodes::_fload:
  case Bytecodes::_dload:
  case Bytecodes::_aload:
  case Bytecodes::_istore:
  case Bytecodes::_lstore:
  case Bytecodes::_fstore:
  case Bytecodes::_dstore:
  case Bytecodes::_astore:
    assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bi, "wrong bytecode format");
    if (index == (index & 0xff)) {
      _bytecode.push(op);
      _bytecode.push(index);
    } else {
      // doesn't fit in a u2
      _bytecode.push(Bytecodes::_wide);
      _bytecode.push(op);
      _bytecode.push(index >> 8);
      _bytecode.push(index);
    }
    break;

  // bkk
  case Bytecodes::_ldc_w:
  case Bytecodes::_ldc2_w:
  case Bytecodes::_checkcast:
    assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bkk, "wrong bytecode format");
    assert((unsigned short) index == index, "index does not fit in 16-bit");
    _bytecode.push(op);
    _bytecode.push(index >> 8);
    _bytecode.push(index);
    break;

  // bJJ
  case Bytecodes::_invokestatic:
  case Bytecodes::_invokespecial:
  case Bytecodes::_invokevirtual:
    assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bJJ, "wrong bytecode format");
    assert((unsigned short) index == index, "index does not fit in 16-bit");
    _bytecode.push(op);
    _bytecode.push(index >> 8);
    _bytecode.push(index);
    break;

  case Bytecodes::_invokeinterface:
    assert(Bytecodes::format_bits(op, false) == Bytecodes::_fmt_bJJ, "wrong bytecode format");
    assert((unsigned short) index == index, "index does not fit in 16-bit");
    assert(args_size > 0, "valid args_size");
    _bytecode.push(op);
    _bytecode.push(index >> 8);
    _bytecode.push(index);
    _bytecode.push(args_size);
    _bytecode.push(0);
    break;

  default:
    ShouldNotReachHere();
  }
}


void MethodHandleCompiler::emit_load(BasicType bt, int index) {
  if (index <= 3) {
    switch (bt) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
    case T_INT:    emit_bc(Bytecodes::cast(Bytecodes::_iload_0 + index)); break;
    case T_LONG:   emit_bc(Bytecodes::cast(Bytecodes::_lload_0 + index)); break;
    case T_FLOAT:  emit_bc(Bytecodes::cast(Bytecodes::_fload_0 + index)); break;
    case T_DOUBLE: emit_bc(Bytecodes::cast(Bytecodes::_dload_0 + index)); break;
    case T_OBJECT: emit_bc(Bytecodes::cast(Bytecodes::_aload_0 + index)); break;
    default:
      ShouldNotReachHere();
    }
  }
  else {
    switch (bt) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
    case T_INT:    emit_bc(Bytecodes::_iload, index); break;
    case T_LONG:   emit_bc(Bytecodes::_lload, index); break;
    case T_FLOAT:  emit_bc(Bytecodes::_fload, index); break;
    case T_DOUBLE: emit_bc(Bytecodes::_dload, index); break;
    case T_OBJECT: emit_bc(Bytecodes::_aload, index); break;
    default:
      ShouldNotReachHere();
    }
  }
  stack_push(bt);
}

void MethodHandleCompiler::emit_store(BasicType bt, int index) {
  if (index <= 3) {
    switch (bt) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
    case T_INT:    emit_bc(Bytecodes::cast(Bytecodes::_istore_0 + index)); break;
    case T_LONG:   emit_bc(Bytecodes::cast(Bytecodes::_lstore_0 + index)); break;
    case T_FLOAT:  emit_bc(Bytecodes::cast(Bytecodes::_fstore_0 + index)); break;
    case T_DOUBLE: emit_bc(Bytecodes::cast(Bytecodes::_dstore_0 + index)); break;
    case T_OBJECT: emit_bc(Bytecodes::cast(Bytecodes::_astore_0 + index)); break;
    default:
      ShouldNotReachHere();
    }
  }
  else {
    switch (bt) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
    case T_INT:    emit_bc(Bytecodes::_istore, index); break;
    case T_LONG:   emit_bc(Bytecodes::_lstore, index); break;
    case T_FLOAT:  emit_bc(Bytecodes::_fstore, index); break;
    case T_DOUBLE: emit_bc(Bytecodes::_dstore, index); break;
    case T_OBJECT: emit_bc(Bytecodes::_astore, index); break;
    default:
      ShouldNotReachHere();
    }
  }
  stack_pop(bt);
}


void MethodHandleCompiler::emit_load_constant(ArgToken arg) {
  BasicType bt = arg.basic_type();
  if (is_subword_type(bt)) bt = T_INT;
  switch (bt) {
  case T_INT: {
    jint value = arg.get_jint();
    if (-1 <= value && value <= 5)
      emit_bc(Bytecodes::cast(Bytecodes::_iconst_0 + value));
    else
      emit_bc(Bytecodes::_ldc, cpool_int_put(value));
    break;
  }
  case T_LONG: {
    jlong value = arg.get_jlong();
    if (0 <= value && value <= 1)
      emit_bc(Bytecodes::cast(Bytecodes::_lconst_0 + (int) value));
    else
      emit_bc(Bytecodes::_ldc2_w, cpool_long_put(value));
    break;
  }
  case T_FLOAT: {
    jfloat value  = arg.get_jfloat();
    if (value == 0.0 || value == 1.0 || value == 2.0)
      emit_bc(Bytecodes::cast(Bytecodes::_fconst_0 + (int) value));
    else
      emit_bc(Bytecodes::_ldc, cpool_float_put(value));
    break;
  }
  case T_DOUBLE: {
    jdouble value = arg.get_jdouble();
    if (value == 0.0 || value == 1.0)
      emit_bc(Bytecodes::cast(Bytecodes::_dconst_0 + (int) value));
    else
      emit_bc(Bytecodes::_ldc2_w, cpool_double_put(value));
    break;
  }
  case T_OBJECT: {
    Handle value = arg.object();
    if (value.is_null()) {
      emit_bc(Bytecodes::_aconst_null);
      break;
    }
    if (java_lang_Class::is_instance(value())) {
      klassOop k = java_lang_Class::as_klassOop(value());
      if (k != NULL) {
        emit_bc(Bytecodes::_ldc, cpool_klass_put(k));
        break;
      }
    }
    emit_bc(Bytecodes::_ldc, cpool_object_put(value));
    break;
  }
  default:
    ShouldNotReachHere();
  }
  stack_push(bt);
}


MethodHandleWalker::ArgToken
MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Code op,
                                      const ArgToken& src, TRAPS) {

  BasicType srctype = src.basic_type();
  TokenType tt = src.token_type();
  int index = -1;

  switch (op) {
  case Bytecodes::_i2l:
  case Bytecodes::_i2f:
  case Bytecodes::_i2d:
  case Bytecodes::_i2b:
  case Bytecodes::_i2c:
  case Bytecodes::_i2s:

  case Bytecodes::_l2i:
  case Bytecodes::_l2f:
  case Bytecodes::_l2d:

  case Bytecodes::_f2i:
  case Bytecodes::_f2l:
  case Bytecodes::_f2d:

  case Bytecodes::_d2i:
  case Bytecodes::_d2l:
  case Bytecodes::_d2f:
    if (tt == tt_constant) {
      emit_load_constant(src);
    } else {
      emit_load(srctype, src.index());
    }
    stack_pop(srctype);  // pop the src type
    emit_bc(op);
    stack_push(type);    // push the dest value
    if (tt != tt_constant)
      index = src.index();
    if (srctype != type || index == -1)
      index = new_local_index(type);
    emit_store(type, index);
    break;

  case Bytecodes::_checkcast:
    if (tt == tt_constant) {
      emit_load_constant(src);
    } else {
      emit_load(srctype, src.index());
      index = src.index();
    }
    emit_bc(op, cpool_klass_put(tk));
    check_non_bcp_klass(tk, CHECK_(src));
    // Allocate a new local for the type so that we don't hide the
    // previous type from the verifier.
    index = new_local_index(type);
    emit_store(srctype, index);
    break;

  case Bytecodes::_nop:
    // nothing to do
    return src;

  default:
    if (op == Bytecodes::_illegal)
      lose(err_msg("no such primitive conversion: %s -> %s", type2name(src.basic_type()), type2name(type)), THREAD);
    else
      lose(err_msg("bad primitive conversion op: %s", Bytecodes::name(op)), THREAD);
    return make_prim_constant(type, &zero_jvalue, THREAD);
  }

  return make_parameter(type, tk, index, THREAD);
}


// -----------------------------------------------------------------------------
// MethodHandleCompiler
//

// Values used by the compiler.
jvalue MethodHandleCompiler::zero_jvalue = { 0 };
jvalue MethodHandleCompiler::one_jvalue  = { 1 };

// Emit bytecodes for the given invoke instruction.
MethodHandleWalker::ArgToken
MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
                                  Bytecodes::Code op, bool tailcall,
                                  int argc, MethodHandleWalker::ArgToken* argv,
                                  TRAPS) {
  ArgToken zero;
  if (m.is_null()) {
    // Get the intrinsic methodOop.
    m = methodHandle(THREAD, vmIntrinsics::method_for(iid));
    if (m.is_null()) {
      lose(vmIntrinsics::name_at(iid), CHECK_(zero));
    }
  }

  klassOop klass     = m->method_holder();
  Symbol*  name      = m->name();
  Symbol*  signature = m->signature();

  if (iid == vmIntrinsics::_invokeGeneric &&
      argc >= 1 && argv[0].token_type() == tt_constant) {
    assert(m->intrinsic_id() == vmIntrinsics::_invokeExact, "");
    Handle receiver = argv[0].object();
    Handle rtype(THREAD, java_lang_invoke_MethodHandle::type(receiver()));
    Handle mtype(THREAD, m->method_handle_type());
    if (rtype() != mtype()) {
      assert(java_lang_invoke_MethodType::form(rtype()) ==
             java_lang_invoke_MethodType::form(mtype()),
             "must be the same shape");
      // customize m to the exact required rtype
      bool has_non_bcp_klass = check_non_bcp_klasses(rtype(), CHECK_(zero));
      TempNewSymbol sig2 = java_lang_invoke_MethodType::as_signature(rtype(), true, CHECK_(zero));
      methodHandle m2;
      if (!has_non_bcp_klass) {
        methodOop m2_oop = SystemDictionary::find_method_handle_invoke(m->name(), sig2,
                                                                       KlassHandle(), CHECK_(zero));
        m2 = methodHandle(THREAD, m2_oop);
      }
      if (m2.is_null()) {
        // just build it fresh
        m2 = methodOopDesc::make_invoke_method(klass, m->name(), sig2, rtype, CHECK_(zero));
        if (m2.is_null())
          lose(err_msg("no customized invoker %s", sig2->as_utf8()), CHECK_(zero));
      }
      m = m2;
      signature = m->signature();
    }
  }

  check_non_bcp_klass(klass, CHECK_(zero));
  if (m->is_method_handle_invoke()) {
    check_non_bcp_klasses(m->method_handle_type(), CHECK_(zero));
  }

  // Count the number of arguments, not the size
  ArgumentCount asc(signature);
  assert(argc == asc.size() + ((op == Bytecodes::_invokestatic || op == Bytecodes::_invokedynamic) ? 0 : 1),
         "argc mismatch");

  // Inline the method.
  InvocationCounter* ic = m->invocation_counter();
  ic->set_carry_flag();

  for (int i = 0; i < argc; i++) {
    ArgToken arg = argv[i];
    TokenType tt = arg.token_type();
    BasicType bt = arg.basic_type();

    switch (tt) {
    case tt_parameter:
    case tt_temporary:
      emit_load(bt, arg.index());
      break;
    case tt_constant:
      emit_load_constant(arg);
      break;
    case tt_illegal:
      // Sentinel.
      assert(i == (argc - 1), "sentinel must be last entry");
      break;
    case tt_void:
    default:
      ShouldNotReachHere();
    }
  }

  // Populate constant pool.
  int name_index          = cpool_symbol_put(name);
  int signature_index     = cpool_symbol_put(signature);
  int name_and_type_index = cpool_name_and_type_put(name_index, signature_index);
  int klass_index         = cpool_klass_put(klass);
  int methodref_index     = cpool_methodref_put(op, klass_index, name_and_type_index, m);

  // Generate invoke.
  switch (op) {
  case Bytecodes::_invokestatic:
  case Bytecodes::_invokespecial:
  case Bytecodes::_invokevirtual:
    emit_bc(op, methodref_index);
    break;

  case Bytecodes::_invokeinterface: {
    ArgumentSizeComputer asc(signature);
    emit_bc(op, methodref_index, asc.size() + 1);
    break;
  }

  default:
    ShouldNotReachHere();
  }

  // If tailcall, we have walked all the way to a direct method handle.
  // Otherwise, make a recursive call to some helper routine.
  BasicType rbt = m->result_type();
  if (rbt == T_ARRAY)  rbt = T_OBJECT;
  stack_push(rbt);  // The return value is already pushed onto the stack.
  ArgToken ret;
  if (tailcall) {
    if (return_conv() == zero_return_conv()) {
      rbt = T_VOID;  // discard value
    } else if (return_conv() != vmIntrinsics::_none) {
      // return value conversion
      int index = new_local_index(rbt);
      emit_store(rbt, index);
      ArgToken arglist[2];
      arglist[0] = ArgToken(tt_temporary, rbt, index);
      arglist[1] = ArgToken();  // sentinel
      ret = make_invoke(methodHandle(), return_conv(), Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(zero));
      set_return_conv(vmIntrinsics::_none);
      rbt = ret.basic_type();
      emit_load(rbt, ret.index());
    }
    if (rbt != _rtype) {
      if (rbt == T_VOID) {
        // push a zero of the right sort
        if (_rtype == T_OBJECT) {
          zero = make_oop_constant(NULL, CHECK_(zero));
        } else {
          zero = make_prim_constant(_rtype, &zero_jvalue, CHECK_(zero));
        }
        emit_load_constant(zero);
      } else if (_rtype == T_VOID) {
        // We'll emit a _return with something on the stack.
        // It's OK to ignore what's on the stack.
      } else if (rbt == T_INT && is_subword_type(_rtype)) {
        // Convert value to match return type.
        switch (_rtype) {
        case T_BOOLEAN: {
          // boolean is treated as a one-bit unsigned integer.
          // Cf. API documentation: java/lang/invoke/MethodHandles.html#explicitCastArguments
          ArgToken one = make_prim_constant(T_INT, &one_jvalue, CHECK_(zero));
          emit_load_constant(one);
          emit_bc(Bytecodes::_iand);
          break;
        }
        case T_BYTE:    emit_bc(Bytecodes::_i2b); break;
        case T_CHAR:    emit_bc(Bytecodes::_i2c); break;
        case T_SHORT:   emit_bc(Bytecodes::_i2s); break;
        default: ShouldNotReachHere();
        }
      } else if (is_subword_type(rbt) && (is_subword_type(_rtype) || (_rtype == T_INT))) {
        // The subword type was returned as an int and will be passed
        // on as an int.
      } else {
        lose("unknown conversion", CHECK_(zero));
      }
    }
    switch (_rtype) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT:
    case T_INT:    emit_bc(Bytecodes::_ireturn); break;
    case T_LONG:   emit_bc(Bytecodes::_lreturn); break;
    case T_FLOAT:  emit_bc(Bytecodes::_freturn); break;
    case T_DOUBLE: emit_bc(Bytecodes::_dreturn); break;
    case T_VOID:   emit_bc(Bytecodes::_return);  break;
    case T_OBJECT:
      if (_rklass.not_null() && _rklass() != SystemDictionary::Object_klass() && !Klass::cast(_rklass())->is_interface()) {
        emit_bc(Bytecodes::_checkcast, cpool_klass_put(_rklass()));
        check_non_bcp_klass(_rklass(), CHECK_(zero));
      }
      emit_bc(Bytecodes::_areturn);
      break;
    default: ShouldNotReachHere();
    }
    ret = ArgToken();  // Dummy return value.
  }
  else {
    int index = new_local_index(rbt);
    switch (rbt) {
    case T_BOOLEAN: case T_BYTE: case T_CHAR:  case T_SHORT:
    case T_INT:     case T_LONG: case T_FLOAT: case T_DOUBLE:
    case T_OBJECT:
      emit_store(rbt, index);
      ret = ArgToken(tt_temporary, rbt, index);
      break;
    case T_VOID:
      ret = ArgToken(tt_void);
      break;
    default:
      ShouldNotReachHere();
    }
  }

  return ret;
}

MethodHandleWalker::ArgToken
MethodHandleCompiler::make_fetch(BasicType type, klassOop tk, Bytecodes::Code op,
                                 const MethodHandleWalker::ArgToken& base,
                                 const MethodHandleWalker::ArgToken& offset,
                                 TRAPS) {
  switch (base.token_type()) {
    case tt_parameter:
    case tt_temporary:
      emit_load(base.basic_type(), base.index());
      break;
    case tt_constant:
      emit_load_constant(base);
      break;
    default:
      ShouldNotReachHere();
  }
  switch (offset.token_type()) {
    case tt_parameter:
    case tt_temporary:
      emit_load(offset.basic_type(), offset.index());
      break;
    case tt_constant:
      emit_load_constant(offset);
      break;
    default:
      ShouldNotReachHere();
  }
  emit_bc(op);
  int index = new_local_index(type);
  emit_store(type, index);
  return ArgToken(tt_temporary, type, index);
}


int MethodHandleCompiler::cpool_primitive_put(BasicType bt, jvalue* con) {
  jvalue con_copy;
  assert(bt < T_OBJECT, "");
  if (type2aelembytes(bt) < jintSize) {
    // widen to int
    con_copy = (*con);
    con = &con_copy;
    switch (bt) {
    case T_BOOLEAN: con->i = (con->z ? 1 : 0); break;
    case T_BYTE:    con->i = con->b;           break;
    case T_CHAR:    con->i = con->c;           break;
    case T_SHORT:   con->i = con->s;           break;
    default: ShouldNotReachHere();
    }
    bt = T_INT;
  }

//   for (int i = 1, imax = _constants.length(); i < imax; i++) {
//     ConstantValue* con = _constants.at(i);
//     if (con != NULL && con->is_primitive() && con.basic_type() == bt) {
//       bool match = false;
//       switch (type2size[bt]) {
//       case 1:  if (pcon->_value.i == con->i)  match = true;  break;
//       case 2:  if (pcon->_value.j == con->j)  match = true;  break;
//       }
//       if (match)
//         return i;
//     }
//   }
  ConstantValue* cv = new ConstantValue(bt, *con);
  int index = _constants.append(cv);

  // long and double entries take 2 slots, we add another empty entry.
  if (type2size[bt] == 2)
    (void) _constants.append(NULL);

  return index;
}

bool MethodHandleCompiler::check_non_bcp_klasses(Handle method_type, TRAPS) {
  bool res = false;
  for (int i = -1, len = java_lang_invoke_MethodType::ptype_count(method_type()); i < len; i++) {
    oop ptype = (i == -1
                 ? java_lang_invoke_MethodType::rtype(method_type())
                 : java_lang_invoke_MethodType::ptype(method_type(), i));
    res |= check_non_bcp_klass(java_lang_Class::as_klassOop(ptype), CHECK_(false));
  }
  return res;
}

bool MethodHandleCompiler::check_non_bcp_klass(klassOop klass, TRAPS) {
  klass = methodOopDesc::check_non_bcp_klass(klass);
  if (klass != NULL) {
    Symbol* name = Klass::cast(klass)->name();
    for (int i = _non_bcp_klasses.length() - 1; i >= 0; i--) {
      klassOop k2 = _non_bcp_klasses.at(i)();
      if (Klass::cast(k2)->name() == name) {
        if (k2 != klass) {
          lose(err_msg("unsupported klass name alias %s", name->as_utf8()), THREAD);
        }
        return true;
      }
    }
    _non_bcp_klasses.append(KlassHandle(THREAD, klass));
    return true;
  }
  return false;
}

void MethodHandleCompiler::record_non_bcp_klasses() {
  // Append extra klasses to constant pool, to guide klass lookup.
  for (int k = 0; k < _non_bcp_klasses.length(); k++) {
    klassOop non_bcp_klass = _non_bcp_klasses.at(k)();
    bool add_to_cp = true;
    for (int j = 1; j < _constants.length(); j++) {
      ConstantValue* cv = _constants.at(j);
      if (cv != NULL && cv->tag() == JVM_CONSTANT_Class
          && cv->klass_oop() == non_bcp_klass) {
        add_to_cp = false;
        break;
      }
    }
    if (add_to_cp)  cpool_klass_put(non_bcp_klass);
  }
}

constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const {
  constantPoolHandle nullHandle;
  constantPoolOop cpool_oop = oopFactory::new_constantPool(_constants.length(),
                                                           oopDesc::IsSafeConc,
                                                           CHECK_(nullHandle));
  constantPoolHandle cpool(THREAD, cpool_oop);

  // Fill the real constant pool skipping the zero element.
  for (int i = 1; i < _constants.length(); i++) {
    ConstantValue* cv = _constants.at(i);
    switch (cv->tag()) {
    case JVM_CONSTANT_Utf8:        cpool->symbol_at_put(       i, cv->symbol()                         ); break;
    case JVM_CONSTANT_Integer:     cpool->int_at_put(          i, cv->get_jint()                       ); break;
    case JVM_CONSTANT_Float:       cpool->float_at_put(        i, cv->get_jfloat()                     ); break;
    case JVM_CONSTANT_Long:        cpool->long_at_put(         i, cv->get_jlong()                      ); break;
    case JVM_CONSTANT_Double:      cpool->double_at_put(       i, cv->get_jdouble()                    ); break;
    case JVM_CONSTANT_Class:       cpool->klass_at_put(        i, cv->klass_oop()                      ); break;
    case JVM_CONSTANT_Methodref:   cpool->method_at_put(       i, cv->first_index(), cv->second_index()); break;
    case JVM_CONSTANT_InterfaceMethodref:
                                cpool->interface_method_at_put(i, cv->first_index(), cv->second_index()); break;
    case JVM_CONSTANT_NameAndType: cpool->name_and_type_at_put(i, cv->first_index(), cv->second_index()); break;
    case JVM_CONSTANT_Object:      cpool->object_at_put(       i, cv->object_oop()                     ); break;
    default: ShouldNotReachHere();
    }

    switch (cv->tag()) {
    case JVM_CONSTANT_Long:
    case JVM_CONSTANT_Double:
      i++;  // Skip empty entry.
      assert(_constants.at(i) == NULL, "empty entry");
      break;
    }
  }

  cpool->set_preresolution();

  // Set the constant pool holder to the target method's class.
  cpool->set_pool_holder(_target_klass());

  return cpool;
}


methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
  methodHandle empty;
  // Create a method that holds the generated bytecode.  invokedynamic
  // has no receiver, normal MH calls do.
  int flags_bits;
  if (for_invokedynamic())
    flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC | JVM_ACC_STATIC);
  else
    flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC);

  // Create a new method
  methodHandle m;
  {
    methodOop m_oop = oopFactory::new_method(bytecode_length(),
                                             accessFlags_from(flags_bits),
                                             0, 0, 0, oopDesc::IsSafeConc, CHECK_(empty));
    m = methodHandle(THREAD, m_oop);
  }

  constantPoolHandle cpool = get_constant_pool(CHECK_(empty));
  m->set_constants(cpool());

  m->set_name_index(_name_index);
  m->set_signature_index(_signature_index);

  m->set_code((address) bytecode());

  m->set_max_stack(_max_stack);
  m->set_max_locals(max_locals());
  m->set_size_of_parameters(_num_params);

  typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array());
  m->set_exception_table(exception_handlers());

  // Rewrite the method and set up the constant pool cache.
  objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(empty));
  objArrayHandle methods(THREAD, m_array);
  methods->obj_at_put(0, m());
  Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty));  // Use fake class.
  Rewriter::relocate_and_link(_target_klass(), methods, CHECK_(empty));  // Use fake class.

  // Pre-resolve selected CP cache entries, to avoid problems with class loader scoping.
  constantPoolCacheHandle cpc(THREAD, cpool->cache());
  for (int i = 0; i < cpc->length(); i++) {
    ConstantPoolCacheEntry* e = cpc->entry_at(i);
    assert(!e->is_secondary_entry(), "no indy instructions in here, yet");
    int constant_pool_index = e->constant_pool_index();
    ConstantValue* cv = _constants.at(constant_pool_index);
    if (!cv->has_linkage())  continue;
    methodHandle m = cv->linkage();
    int index;
    switch (cv->tag()) {
    case JVM_CONSTANT_Methodref:
      index = m->vtable_index();
      if (m->is_static()) {
        e->set_method(Bytecodes::_invokestatic, m, index);
      } else {
        e->set_method(Bytecodes::_invokespecial, m, index);
        e->set_method(Bytecodes::_invokevirtual, m, index);
      }
      break;
    case JVM_CONSTANT_InterfaceMethodref:
      index = klassItable::compute_itable_index(m());
      e->set_interface_call(m, index);
      break;
    }
  }

  // Set the invocation counter's count to the invoke count of the
  // original call site.
  InvocationCounter* ic = m->invocation_counter();
  ic->set(InvocationCounter::wait_for_compile, _invoke_count);

  // Create a new MDO
  {
    methodDataOop mdo = oopFactory::new_methodData(m, CHECK_(empty));
    assert(m->method_data() == NULL, "there should not be an MDO yet");
    m->set_method_data(mdo);

    // Iterate over all profile data and set the count of the counter
    // data entries to the original call site counter.
    for (ProfileData* profile_data = mdo->first_data();
         mdo->is_valid(profile_data);
         profile_data = mdo->next_data(profile_data)) {
      if (profile_data->is_CounterData()) {
        CounterData* counter_data = profile_data->as_CounterData();
        counter_data->set_count(_invoke_count);
      }
    }
  }

#ifndef PRODUCT
  if (TraceMethodHandles) {
    m->print();
    m->print_codes();
  }
#endif //PRODUCT

  assert(m->is_method_handle_adapter(), "must be recognized as an adapter");
  return m;
}


#ifndef PRODUCT

// MH printer for debugging.

class MethodHandlePrinter : public MethodHandleWalker {
private:
  outputStream* _out;
  bool          _verbose;
  int           _temp_num;
  int           _param_state;
  stringStream  _strbuf;
  const char* strbuf() {
    const char* s = _strbuf.as_string();
    _strbuf.reset();
    return s;
  }
  ArgToken token(const char* str, BasicType type) {
    return ArgToken(str, type);
  }
  const char* string(ArgToken token) {
    return token.str();
  }
  void start_params() {
    _param_state <<= 1;
    _out->print("(");
  }
  void end_params() {
    if (_verbose)  _out->print("\n");
    _out->print(") => {");
    _param_state >>= 1;
  }
  void put_type_name(BasicType type, klassOop tk, outputStream* s) {
    const char* kname = NULL;
    if (tk != NULL)
      kname = Klass::cast(tk)->external_name();
    s->print("%s", (kname != NULL) ? kname : type2name(type));
  }
  ArgToken maybe_make_temp(const char* statement_op, BasicType type, const char* temp_name) {
    const char* value = strbuf();
    if (!_verbose)  return token(value, type);
    // make an explicit binding for each separate value
    _strbuf.print("%s%d", temp_name, ++_temp_num);
    const char* temp = strbuf();
    _out->print("\n  %s %s %s = %s;", statement_op, type2name(type), temp, value);
    return token(temp, type);
  }

public:
  MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS)
    : MethodHandleWalker(root, false, THREAD),
      _out(out),
      _verbose(verbose),
      _param_state(0),
      _temp_num(0)
  {
    out->print("MethodHandle:");
    java_lang_invoke_MethodType::print_signature(java_lang_invoke_MethodHandle::type(root()), out);
    out->print(" : #");
    start_params();
  }
  virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
    if (argnum < 0) {
      end_params();
      return token("return", type);
    }
    if ((_param_state & 1) == 0) {
      _param_state |= 1;
      _out->print(_verbose ? "\n  " : "");
    } else {
      _out->print(_verbose ? ",\n  " : ", ");
    }
    if (argnum >= _temp_num)
      _temp_num = argnum;
    // generate an argument name
    _strbuf.print("a%d", argnum);
    const char* arg = strbuf();
    put_type_name(type, tk, _out);
    _out->print(" %s", arg);
    return token(arg, type);
  }
  virtual ArgToken make_oop_constant(oop con, TRAPS) {
    if (con == NULL)
      _strbuf.print("null");
    else
      con->print_value_on(&_strbuf);
    if (_strbuf.size() == 0) {  // yuck
      _strbuf.print("(a ");
      put_type_name(T_OBJECT, con->klass(), &_strbuf);
      _strbuf.print(")");
    }
    return maybe_make_temp("constant", T_OBJECT, "k");
  }
  virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) {
    java_lang_boxing_object::print(type, con, &_strbuf);
    return maybe_make_temp("constant", type, "k");
  }
  void print_bytecode_name(Bytecodes::Code op) {
    if (Bytecodes::is_defined(op))
      _strbuf.print("%s", Bytecodes::name(op));
    else
      _strbuf.print("bytecode_%d", (int) op);
  }
  virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) {
    print_bytecode_name(op);
    _strbuf.print("(%s", string(src));
    if (tk != NULL) {
      _strbuf.print(", ");
      put_type_name(type, tk, &_strbuf);
    }
    _strbuf.print(")");
    return maybe_make_temp("convert", type, "v");
  }
  virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) {
    _strbuf.print("%s(%s, %s", Bytecodes::name(op), string(base), string(offset));
    if (tk != NULL) {
      _strbuf.print(", ");
      put_type_name(type, tk, &_strbuf);
    }
    _strbuf.print(")");
    return maybe_make_temp("fetch", type, "x");
  }
  virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid,
                               Bytecodes::Code op, bool tailcall,
                               int argc, ArgToken* argv, TRAPS) {
    Symbol* name;
    Symbol* sig;
    if (m.not_null()) {
      name = m->name();
      sig  = m->signature();
    } else {
      name = vmSymbols::symbol_at(vmIntrinsics::name_for(iid));
      sig  = vmSymbols::symbol_at(vmIntrinsics::signature_for(iid));
    }
    _strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string());
    for (int i = 0; i < argc; i++) {
      _strbuf.print("%s%s", (i > 0 ? ", " : ""), string(argv[i]));
    }
    _strbuf.print(")");
    if (!tailcall) {
      BasicType rt = char2type(sig->byte_at(sig->utf8_length()-1));
      if (rt == T_ILLEGAL)  rt = T_OBJECT;  // ';' at the end of '(...)L...;'
      return maybe_make_temp("invoke", rt, "x");
    } else {
      const char* ret = strbuf();
      _out->print(_verbose ? "\n  return " : " ");
      _out->print("%s", ret);
      _out->print(_verbose ? "\n}\n" : " }");
    }
    return ArgToken();
  }

  virtual void set_method_handle(oop mh) {
    if (WizardMode && Verbose) {
      tty->print("\n--- next target: ");
      mh->print();
    }
  }

  static void print(Handle root, bool verbose, outputStream* out, TRAPS) {
    ResourceMark rm;
    MethodHandlePrinter printer(root, verbose, out, CHECK);
    printer.walk(CHECK);
    out->print("\n");
  }
  static void print(Handle root, bool verbose = Verbose, outputStream* out = tty) {
    Thread* THREAD = Thread::current();
    ResourceMark rm;
    MethodHandlePrinter printer(root, verbose, out, THREAD);
    if (!HAS_PENDING_EXCEPTION)
      printer.walk(THREAD);
    if (HAS_PENDING_EXCEPTION) {
      oop ex = PENDING_EXCEPTION;
      CLEAR_PENDING_EXCEPTION;
      out->print(" *** ");
      if (printer.lose_message() != NULL)  out->print("%s ", printer.lose_message());
      out->print("}");
    }
    out->print("\n");
  }
};

extern "C"
void print_method_handle(oop mh) {
  if (!mh->is_oop()) {
    tty->print_cr("*** not a method handle: "PTR_FORMAT, (intptr_t)mh);
  } else if (java_lang_invoke_MethodHandle::is_instance(mh)) {
    MethodHandlePrinter::print(mh);
  } else {
    tty->print("*** not a method handle: ");
    mh->print();
  }
}

#endif // PRODUCT