changeset 2180:3b2dea75431e

6984311: JSR 292 needs optional bootstrap method parameters Summary: Allow CONSTANT_InvokeDynamic nodes to have any number of extra operands. Reviewed-by: twisti
author jrose
date Sat, 30 Oct 2010 13:08:23 -0700
parents 8213b0f5c92d
children ae065c367d93
files agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java src/cpu/sparc/vm/templateTable_sparc.cpp src/cpu/x86/vm/templateTable_x86_32.cpp src/cpu/x86/vm/templateTable_x86_64.cpp src/share/vm/classfile/classFileParser.cpp src/share/vm/classfile/classFileParser.hpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/classfile/systemDictionary.hpp src/share/vm/classfile/verifier.cpp src/share/vm/includeDB_core src/share/vm/interpreter/interpreterRuntime.cpp src/share/vm/memory/universe.hpp src/share/vm/oops/constantPoolKlass.cpp src/share/vm/oops/constantPoolOop.cpp src/share/vm/oops/constantPoolOop.hpp src/share/vm/oops/cpCacheOop.hpp src/share/vm/prims/jvm.h src/share/vm/prims/methodComparator.cpp src/share/vm/prims/methodComparator.hpp src/share/vm/runtime/vmStructs.cpp src/share/vm/utilities/constantTag.hpp
diffstat 26 files changed, 686 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2010, 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
@@ -188,7 +188,7 @@
        } else {
           throw new RuntimeException("should not reach here");
        }
-    } else if (ctag.isMethodHandle() || ctag.isMethodType()) {
+    } else if (ctag.isMethodHandle()) {
        Oop x = getCachedConstant();
        int refidx = cpool.getMethodHandleIndexAt(cpIndex);
        int refkind = cpool.getMethodHandleRefKindAt(cpIndex);
--- a/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java	Sat Oct 30 13:08:23 2010 -0700
@@ -53,11 +53,19 @@
   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
     Type type   = db.lookupType("constantPoolOopDesc");
     tags        = new OopField(type.getOopField("_tags"), 0);
+    operands    = new OopField(type.getOopField("_operands"), 0);
     cache       = new OopField(type.getOopField("_cache"), 0);
     poolHolder  = new OopField(type.getOopField("_pool_holder"), 0);
     length      = new CIntField(type.getCIntegerField("_length"), 0);
     headerSize  = type.getSize();
     elementSize = 0;
+    // fetch constants:
+    MULTI_OPERAND_COUNT_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_multi_operand_count_offset").intValue();
+    MULTI_OPERAND_BASE_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_multi_operand_base_offset").intValue();
+    INDY_BSM_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_bsm_offset").intValue();
+    INDY_NT_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_nt_offset").intValue();
+    INDY_ARGC_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argc_offset").intValue();
+    INDY_ARGV_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argv_offset").intValue();
   }
 
   ConstantPool(OopHandle handle, ObjectHeap heap) {
@@ -67,6 +75,7 @@
   public boolean isConstantPool()      { return true; }
 
   private static OopField tags;
+  private static OopField operands;
   private static OopField cache;
   private static OopField poolHolder;
   private static CIntField length; // number of elements in oop
@@ -74,7 +83,15 @@
   private static long headerSize;
   private static long elementSize;
 
+  private static int MULTI_OPERAND_COUNT_OFFSET;
+  private static int MULTI_OPERAND_BASE_OFFSET;
+  private static int INDY_BSM_OFFSET;
+  private static int INDY_NT_OFFSET;
+  private static int INDY_ARGC_OFFSET;
+  private static int INDY_ARGV_OFFSET;
+
   public TypeArray         getTags()       { return (TypeArray)         tags.getValue(this); }
+  public TypeArray         getOperands()   { return (TypeArray)         operands.getValue(this); }
   public ConstantPoolCache getCache()      { return (ConstantPoolCache) cache.getValue(this); }
   public Klass             getPoolHolder() { return (Klass)             poolHolder.getValue(this); }
   public int               getLength()     { return (int)length.getValue(this); }
@@ -278,6 +295,25 @@
     return res;
   }
 
+  /** Lookup for multi-operand (InvokeDynamic) entries. */
+  public int[] getMultiOperandsAt(int i) {
+    if (Assert.ASSERTS_ENABLED) {
+      Assert.that(getTagAt(i).isInvokeDynamic(), "Corrupted constant pool");
+    }
+    int pos = this.getIntAt(i);
+    int countPos = pos + MULTI_OPERAND_COUNT_OFFSET;  // == pos-1
+    int basePos  = pos + MULTI_OPERAND_BASE_OFFSET;   // == pos
+    if (countPos < 0)  return null;  // safety first
+    TypeArray operands = getOperands();
+    if (operands == null)  return null;  // safety first
+    int length = operands.getIntAt(countPos);
+    int[] values = new int[length];
+    for (int j = 0; j < length; j++) {
+        values[j] = operands.getIntAt(basePos+j);
+    }
+    return values;
+  }
+
   final private static String[] nameForTag = new String[] {
   };
 
@@ -522,15 +558,20 @@
 
               case JVM_CONSTANT_InvokeDynamic: {
                   dos.writeByte(cpConstType);
-                  int value = getIntAt(ci);
-                  short bootstrapMethodIndex = (short) extractLowShortFromInt(value);
-                  short nameAndTypeIndex = (short) extractHighShortFromInt(value);
-                  dos.writeShort(bootstrapMethodIndex);
-                  dos.writeShort(nameAndTypeIndex);
+                  int[] values = getMultiOperandsAt(ci);
+                  for (int vn = 0; vn < values.length; vn++) {
+                      dos.writeShort(values[vn]);
+                  }
+                  int bootstrapMethodIndex = values[INDY_BSM_OFFSET];
+                  int nameAndTypeIndex = values[INDY_NT_OFFSET];
+                  int argumentCount = values[INDY_ARGC_OFFSET];
+                  assert(INDY_ARGV_OFFSET + argumentCount == values.length);
                   if (DEBUG) debugMessage("CP[" + ci + "] = indy BSM = " + bootstrapMethodIndex
-                                          + ", N&T = " + nameAndTypeIndex);
+                                          + ", N&T = " + nameAndTypeIndex
+                                          + ", argc = " + argumentCount);
                   break;
               }
+
               default:
                   throw new InternalError("unknown tag: " + cpConstType);
           } // switch
--- a/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java	Sat Oct 30 13:08:23 2010 -0700
@@ -42,7 +42,8 @@
     public static final int JVM_CONSTANT_NameAndType        = 12;
     public static final int JVM_CONSTANT_MethodHandle       = 15;
     public static final int JVM_CONSTANT_MethodType         = 16;
-    public static final int JVM_CONSTANT_InvokeDynamic      = 17;
+    public static final int JVM_CONSTANT_InvokeDynamicTrans = 17;  // only occurs in old class files
+    public static final int JVM_CONSTANT_InvokeDynamic      = 18;
 
     // JVM_CONSTANT_MethodHandle subtypes
     public static final int JVM_REF_getField                = 1;
--- a/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java	Sat Oct 30 13:08:23 2010 -0700
@@ -323,10 +323,11 @@
 
                 case JVM_CONSTANT_InvokeDynamic: {
                      dos.writeByte(cpConstType);
-                     int value = cpool.getIntAt(ci);
-                     short refIndex = (short) value;
-                     dos.writeShort(refIndex);
-                     if (DEBUG) debugMessage("CP[" + ci + "] = MT index = " + refIndex);
+                     int[] values = cpool.getMultiOperandsAt(ci);
+                     for (int vn = 0; vn < values.length; vn++) {
+                         dos.writeShort(values[vn]);
+                     }
+                     if (DEBUG) debugMessage("CP[" + ci + "] = INDY indexes = " + Arrays.toString(values));
                      break;
                 }
 
--- a/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java	Sat Oct 30 13:08:23 2010 -0700
@@ -460,6 +460,18 @@
       return buf.toString();
    }
 
+   private String genListOfShort(int[] values) {
+      Formatter buf = new Formatter(genHTML);
+      buf.append('[');
+      for (int i = 0; i < values.length; i++) {
+          if (i > 0)  buf.append(' ');
+          buf.append('#');
+          buf.append(Integer.toString(values[i]));
+      }
+      buf.append(']');
+      return buf.toString();
+   }
+
    protected String genHTMLTableForConstantPool(ConstantPool cpool) {
       Formatter buf = new Formatter(genHTML);
       buf.beginTable(1);
@@ -584,7 +596,7 @@
 
             case JVM_CONSTANT_InvokeDynamic:
                buf.cell("JVM_CONSTANT_InvokeDynamic");
-               buf.cell(genLowHighShort(cpool.getIntAt(index)));
+               buf.cell(genListOfShort(cpool.getMultiOperandsAt(index)));
                break;
 
             default:
--- a/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java	Sat Oct 30 12:19:07 2010 -0700
+++ b/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2010, 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
@@ -40,7 +40,8 @@
   private static int JVM_CONSTANT_NameAndType             = 12;
   private static int JVM_CONSTANT_MethodHandle            = 15;  // JSR 292
   private static int JVM_CONSTANT_MethodType              = 16;  // JSR 292
-  private static int JVM_CONSTANT_InvokeDynamic           = 17;  // JSR 292
+  //      static int JVM_CONSTANT_InvokeDynamicTrans      = 17;  // JSR 292, only occurs in old class files
+  private static int JVM_CONSTANT_InvokeDynamic           = 18;  // JSR 292
   private static int JVM_CONSTANT_Invalid                 = 0;   // For bad value initialization
   private static int JVM_CONSTANT_UnresolvedClass         = 100; // Temporary tag until actual use
   private static int JVM_CONSTANT_ClassIndex              = 101; // Temporary tag while constructing constant pool
--- a/src/cpu/sparc/vm/templateTable_sparc.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/cpu/sparc/vm/templateTable_sparc.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -341,6 +341,26 @@
   resolve_cache_and_index(f1_oop, Otos_i, Rcache, Rscratch, wide ? sizeof(u2) : sizeof(u1));
 
   __ verify_oop(Otos_i);
+
+  Label L_done;
+  const Register Rcon_klass = G3_scratch;  // same as Rcache
+  const Register Rarray_klass = G4_scratch;  // same as Rscratch
+  __ load_klass(Otos_i, Rcon_klass);
+  AddressLiteral array_klass_addr((address)Universe::systemObjArrayKlassObj_addr());
+  __ load_contents(array_klass_addr, Rarray_klass);
+  __ cmp(Rarray_klass, Rcon_klass);
+  __ brx(Assembler::notEqual, false, Assembler::pt, L_done);
+  __ delayed()->nop();
+  __ ld(Address(Otos_i, arrayOopDesc::length_offset_in_bytes()), Rcon_klass);
+  __ tst(Rcon_klass);
+  __ brx(Assembler::zero, true, Assembler::pt, L_done);
+  __ delayed()->clr(Otos_i);    // executed only if branch is taken
+
+  // Load the exception from the system-array which wraps it:
+  __ load_heap_oop(Otos_i, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Otos_i);
+  __ throw_if_not_x(Assembler::never, Interpreter::throw_exception_entry(), G3_scratch);
+
+  __ bind(L_done);
 }
 
 void TemplateTable::ldc2_w() {
--- a/src/cpu/x86/vm/templateTable_x86_32.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/cpu/x86/vm/templateTable_x86_32.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -399,6 +399,23 @@
   if (VerifyOops) {
     __ verify_oop(rax);
   }
+
+  Label L_done, L_throw_exception;
+  const Register con_klass_temp = rcx;  // same as Rcache
+  __ movptr(con_klass_temp, Address(rax, oopDesc::klass_offset_in_bytes()));
+  __ cmpptr(con_klass_temp, ExternalAddress((address)Universe::systemObjArrayKlassObj_addr()));
+  __ jcc(Assembler::notEqual, L_done);
+  __ cmpl(Address(rax, arrayOopDesc::length_offset_in_bytes()), 0);
+  __ jcc(Assembler::notEqual, L_throw_exception);
+  __ xorptr(rax, rax);
+  __ jmp(L_done);
+
+  // Load the exception from the system-array which wraps it:
+  __ bind(L_throw_exception);
+  __ movptr(rax, Address(rax, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+  __ jump(ExternalAddress(Interpreter::throw_exception_entry()));
+
+  __ bind(L_done);
 }
 
 void TemplateTable::ldc2_w() {
--- a/src/cpu/x86/vm/templateTable_x86_64.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/cpu/x86/vm/templateTable_x86_64.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -413,6 +413,25 @@
   if (VerifyOops) {
     __ verify_oop(rax);
   }
+
+  Label L_done, L_throw_exception;
+  const Register con_klass_temp = rcx;  // same as cache
+  const Register array_klass_temp = rdx;  // same as index
+  __ movptr(con_klass_temp, Address(rax, oopDesc::klass_offset_in_bytes()));
+  __ lea(array_klass_temp, ExternalAddress((address)Universe::systemObjArrayKlassObj_addr()));
+  __ cmpptr(con_klass_temp, Address(array_klass_temp, 0));
+  __ jcc(Assembler::notEqual, L_done);
+  __ cmpl(Address(rax, arrayOopDesc::length_offset_in_bytes()), 0);
+  __ jcc(Assembler::notEqual, L_throw_exception);
+  __ xorptr(rax, rax);
+  __ jmp(L_done);
+
+  // Load the exception from the system-array which wraps it:
+  __ bind(L_throw_exception);
+  __ movptr(rax, Address(rax, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+  __ jump(ExternalAddress(Interpreter::throw_exception_entry()));
+
+  __ bind(L_done);
 }
 
 void TemplateTable::ldc2_w() {
--- a/src/share/vm/classfile/classFileParser.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/classfile/classFileParser.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -73,6 +73,12 @@
   unsigned int hashValues[SymbolTable::symbol_alloc_batch_size];
   int names_count = 0;
 
+  // Side buffer for operands of variable-sized (InvokeDynamic) entries.
+  GrowableArray<int>* operands = NULL;
+#ifdef ASSERT
+  GrowableArray<int>* indy_instructions = new GrowableArray<int>(THREAD, 10);
+#endif
+
   // parsing  Index 0 is unused
   for (int index = 1; index < length; index++) {
     // Each of the following case guarantees one more byte in the stream
@@ -141,6 +147,7 @@
           ShouldNotReachHere();
         }
         break;
+      case JVM_CONSTANT_InvokeDynamicTrans :  // this tag appears only in old classfiles
       case JVM_CONSTANT_InvokeDynamic :
         {
           if (!EnableInvokeDynamic ||
@@ -151,10 +158,36 @@
                "Class file version does not support constant tag %u in class file %s"),
               tag, CHECK);
           }
-          cfs->guarantee_more(5, CHECK);  // bsm_index, name_and_type_index, tag/access_flags
+          if (!AllowTransitionalJSR292 && tag == JVM_CONSTANT_InvokeDynamicTrans) {
+            classfile_parse_error(
+                "This JVM does not support transitional InvokeDynamic tag %u in class file %s",
+                tag, CHECK);
+          }
+          bool trans_no_argc = AllowTransitionalJSR292 && (tag == JVM_CONSTANT_InvokeDynamicTrans);
+          cfs->guarantee_more(7, CHECK);  // bsm_index, nt, argc, ..., tag/access_flags
           u2 bootstrap_method_index = cfs->get_u2_fast();
           u2 name_and_type_index = cfs->get_u2_fast();
-          cp->invoke_dynamic_at_put(index, bootstrap_method_index, name_and_type_index);
+          int argument_count = trans_no_argc ? 0 : cfs->get_u2_fast();
+          cfs->guarantee_more(2*argument_count + 1, CHECK);  // argv[argc]..., tag/access_flags
+          int argv_offset = constantPoolOopDesc::_indy_argv_offset;
+          int op_count = argv_offset + argument_count;  // bsm, nt, argc, argv[]...
+          int op_base = start_operand_group(operands, op_count, CHECK);
+          assert(argv_offset == 3, "else adjust next 3 assignments");
+          operands->at_put(op_base + constantPoolOopDesc::_indy_bsm_offset, bootstrap_method_index);
+          operands->at_put(op_base + constantPoolOopDesc::_indy_nt_offset, name_and_type_index);
+          operands->at_put(op_base + constantPoolOopDesc::_indy_argc_offset, argument_count);
+          for (int arg_i = 0; arg_i < argument_count; arg_i++) {
+            int arg = cfs->get_u2_fast();
+            operands->at_put(op_base + constantPoolOopDesc::_indy_argv_offset + arg_i, arg);
+          }
+          cp->invoke_dynamic_at_put(index, op_base, op_count);
+#ifdef ASSERT
+          // Record the steps just taken for later checking.
+          indy_instructions->append(index);
+          indy_instructions->append(bootstrap_method_index);
+          indy_instructions->append(name_and_type_index);
+          indy_instructions->append(argument_count);
+#endif //ASSERT
         }
         break;
       case JVM_CONSTANT_Integer :
@@ -257,6 +290,23 @@
     oopFactory::new_symbols(cp, names_count, names, lengths, indices, hashValues, CHECK);
   }
 
+  if (operands != NULL && operands->length() > 0) {
+    store_operand_array(operands, cp, CHECK);
+  }
+#ifdef ASSERT
+  // Re-assert the indy structures, now that assertion checking can work.
+  for (int indy_i = 0; indy_i < indy_instructions->length(); ) {
+    int index                  = indy_instructions->at(indy_i++);
+    int bootstrap_method_index = indy_instructions->at(indy_i++);
+    int name_and_type_index    = indy_instructions->at(indy_i++);
+    int argument_count         = indy_instructions->at(indy_i++);
+    assert(cp->check_invoke_dynamic_at(index,
+                                       bootstrap_method_index, name_and_type_index,
+                                       argument_count),
+           "indy structure is OK");
+  }
+#endif //ASSERT
+
   // Copy _current pointer of local copy back to stream().
 #ifdef ASSERT
   assert(cfs0->current() == old_current, "non-exclusive use of stream()");
@@ -264,6 +314,41 @@
   cfs0->set_current(cfs1.current());
 }
 
+int ClassFileParser::start_operand_group(GrowableArray<int>* &operands, int op_count, TRAPS) {
+  if (operands == NULL) {
+    operands = new GrowableArray<int>(THREAD, 100);
+    int fillp_offset = constantPoolOopDesc::_multi_operand_buffer_fill_pointer_offset;
+    while (operands->length() <= fillp_offset)
+      operands->append(0);  // force op_base > 0, for an error check
+    DEBUG_ONLY(operands->at_put(fillp_offset, (int)badHeapWordVal));
+  }
+  int cnt_pos = operands->append(op_count);
+  int arg_pos = operands->length();
+  operands->at_grow(arg_pos + op_count - 1);  // grow to include the operands
+  assert(operands->length() == arg_pos + op_count, "");
+  int op_base = cnt_pos - constantPoolOopDesc::_multi_operand_count_offset;
+  return op_base;
+}
+
+void ClassFileParser::store_operand_array(GrowableArray<int>* operands, constantPoolHandle cp, TRAPS) {
+  // Collect the buffer of operands from variable-sized entries into a permanent array.
+  int arraylen = operands->length();
+  int fillp_offset = constantPoolOopDesc::_multi_operand_buffer_fill_pointer_offset;
+  assert(operands->at(fillp_offset) == (int)badHeapWordVal, "value unused so far");
+  operands->at_put(fillp_offset, arraylen);
+  cp->multi_operand_buffer_grow(arraylen, CHECK);
+  typeArrayOop operands_oop = cp->operands();
+  assert(operands_oop->length() == arraylen, "");
+  for (int i = 0; i < arraylen; i++) {
+    operands_oop->int_at_put(i, operands->at(i));
+  }
+  cp->set_operands(operands_oop);
+  // The fill_pointer is used only by constantPoolOop::copy_entry_to and friends,
+  // when constant pools need to be merged.  Make sure it is sane now.
+  assert(cp->multi_operand_buffer_fill_pointer() == arraylen, "");
+}
+
+
 bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); }
 
 constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) {
@@ -431,6 +516,8 @@
               ref_index, CHECK_(nullHandle));
         }
         break;
+      case JVM_CONSTANT_InvokeDynamicTrans :
+        ShouldNotReachHere();  // this tag does not appear in the heap
       case JVM_CONSTANT_InvokeDynamic :
         {
           int bootstrap_method_ref_index = cp->invoke_dynamic_bootstrap_method_ref_index_at(index);
@@ -438,7 +525,7 @@
           check_property((bootstrap_method_ref_index == 0 && AllowTransitionalJSR292)
                          ||
                          (valid_cp_range(bootstrap_method_ref_index, length) &&
-                          cp->tag_at(bootstrap_method_ref_index).is_method_handle()),
+                          (cp->tag_at(bootstrap_method_ref_index).is_method_handle())),
                          "Invalid constant pool index %u in class file %s",
                          bootstrap_method_ref_index,
                          CHECK_(nullHandle));
@@ -447,6 +534,18 @@
                          "Invalid constant pool index %u in class file %s",
                          name_and_type_ref_index,
                          CHECK_(nullHandle));
+          int argc = cp->invoke_dynamic_argument_count_at(index);
+          for (int arg_i = 0; arg_i < argc; arg_i++) {
+            int arg = cp->invoke_dynamic_argument_index_at(index, arg_i);
+            check_property(valid_cp_range(arg, length) &&
+                           cp->tag_at(arg).is_loadable_constant() ||
+                           // temporary early forms of string and class:
+                           cp->tag_at(arg).is_klass_index() ||
+                           cp->tag_at(arg).is_string_index(),
+                           "Invalid constant pool index %u in class file %s",
+                           arg,
+                           CHECK_(nullHandle));
+          }
           break;
         }
       default:
--- a/src/share/vm/classfile/classFileParser.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/classfile/classFileParser.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -56,6 +56,9 @@
 
   constantPoolHandle parse_constant_pool(TRAPS);
 
+  static int start_operand_group(GrowableArray<int>* &operands, int op_count, TRAPS);
+  static void store_operand_array(GrowableArray<int>* operands, constantPoolHandle cp, TRAPS);
+
   // Interface parsing
   objArrayHandle parse_interfaces(constantPoolHandle cp,
                                   int length,
--- a/src/share/vm/classfile/systemDictionary.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/classfile/systemDictionary.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -2555,7 +2555,9 @@
 }
 
 Handle SystemDictionary::find_bootstrap_method(methodHandle caller_method, int caller_bci,
-                                               int cache_index, TRAPS) {
+                                               int cache_index,
+                                               Handle& argument_info_result,
+                                               TRAPS) {
   Handle empty;
 
   constantPoolHandle pool;
@@ -2569,7 +2571,7 @@
   constantTag tag = pool->tag_at(constant_pool_index);
 
   if (tag.is_invoke_dynamic()) {
-    // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type]
+    // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type], plus optional arguments
     // The bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
     int bsm_index = pool->invoke_dynamic_bootstrap_method_ref_index_at(constant_pool_index);
     if (bsm_index != 0) {
@@ -2585,9 +2587,38 @@
         tty->print_cr("bootstrap method for "PTR_FORMAT" at %d retrieved as "PTR_FORMAT":",
                       (intptr_t) caller_method(), caller_bci, (intptr_t) bsm_oop);
       }
-      assert(bsm_oop->is_oop()
-             && java_dyn_MethodHandle::is_instance(bsm_oop), "must be sane");
-      return Handle(THREAD, bsm_oop);
+      assert(bsm_oop->is_oop(), "must be sane");
+      // caller must verify that it is of type MethodHandle
+      Handle bsm(THREAD, bsm_oop);
+      bsm_oop = NULL;  // safety
+
+      // Extract the optional static arguments.
+      Handle argument_info;  // either null, or one arg, or Object[]{arg...}
+      int argc = pool->invoke_dynamic_argument_count_at(constant_pool_index);
+      if (TraceInvokeDynamic) {
+        tty->print_cr("find_bootstrap_method: [%d/%d] CONSTANT_InvokeDynamic: %d[%d]",
+                      constant_pool_index, cache_index, bsm_index, argc);
+      }
+      if (argc > 0) {
+        objArrayHandle arg_array;
+        if (argc > 1) {
+          objArrayOop arg_array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_(empty));
+          arg_array = objArrayHandle(THREAD, arg_array_oop);
+          argument_info = arg_array;
+        }
+        for (int arg_i = 0; arg_i < argc; arg_i++) {
+          int arg_index = pool->invoke_dynamic_argument_index_at(constant_pool_index, arg_i);
+          oop arg_oop = pool->resolve_possibly_cached_constant_at(arg_index, CHECK_(empty));
+          if (arg_array.is_null()) {
+            argument_info = Handle(THREAD, arg_oop);
+          } else {
+            arg_array->obj_at_put(arg_i, arg_oop);
+          }
+        }
+      }
+
+      argument_info_result = argument_info;  // return argument_info to caller
+      return bsm;
     }
     // else null BSM; fall through
   } else if (tag.is_name_and_type()) {
@@ -2600,14 +2631,14 @@
   // Fall through to pick up the per-class bootstrap method.
   // This mechanism may go away in the PFD.
   assert(AllowTransitionalJSR292, "else the verifier should have stopped us already");
+  argument_info_result = empty;  // return no argument_info to caller
   oop bsm_oop = instanceKlass::cast(caller_method->method_holder())->bootstrap_method();
   if (bsm_oop != NULL) {
     if (TraceMethodHandles) {
       tty->print_cr("bootstrap method for "PTR_FORMAT" registered as "PTR_FORMAT":",
                     (intptr_t) caller_method(), (intptr_t) bsm_oop);
     }
-    assert(bsm_oop->is_oop()
-           && java_dyn_MethodHandle::is_instance(bsm_oop), "must be sane");
+    assert(bsm_oop->is_oop(), "must be sane");
     return Handle(THREAD, bsm_oop);
   }
 
--- a/src/share/vm/classfile/systemDictionary.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/classfile/systemDictionary.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -496,6 +496,7 @@
   static Handle    find_bootstrap_method(methodHandle caller_method,
                                          int caller_bci,  // N.B. must be an invokedynamic
                                          int cache_index, // must be corresponding main_entry
+                                         Handle &argument_info_result, // static BSM arguments, if any
                                          TRAPS);
 
   // Utility for printing loader "name" as part of tracing constraints
--- a/src/share/vm/classfile/verifier.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/classfile/verifier.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1909,7 +1909,7 @@
   unsigned int types = (opcode == Bytecodes::_invokeinterface
                                 ? 1 << JVM_CONSTANT_InterfaceMethodref
                       : opcode == Bytecodes::_invokedynamic
-                                ? (1 << JVM_CONSTANT_NameAndType
+                                ? ((AllowTransitionalJSR292 ? 1 << JVM_CONSTANT_NameAndType : 0)
                                   |1 << JVM_CONSTANT_InvokeDynamic)
                                 : 1 << JVM_CONSTANT_Methodref);
   verify_cp_type(index, cp, types, CHECK_VERIFY(this));
--- a/src/share/vm/includeDB_core	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/includeDB_core	Sat Oct 30 13:08:23 2010 -0700
@@ -1265,6 +1265,7 @@
 constantPoolOop.cpp                     linkResolver.hpp
 constantPoolOop.cpp                     objArrayKlass.hpp
 constantPoolOop.cpp                     oop.inline.hpp
+constantPoolOop.cpp                     oopFactory.hpp
 constantPoolOop.cpp                     signature.hpp
 constantPoolOop.cpp                     symbolTable.hpp
 constantPoolOop.cpp                     systemDictionary.hpp
--- a/src/share/vm/interpreter/interpreterRuntime.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/interpreter/interpreterRuntime.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -716,6 +716,7 @@
   assert(constantPoolCacheOopDesc::is_secondary_index(site_index), "proper format");
   // there is a second CPC entries that is of interest; it caches signature info:
   int main_index = pool->cache()->secondary_entry_at(site_index)->main_entry_index();
+  int pool_index = pool->cache()->entry_at(main_index)->constant_pool_index();
 
   // first resolve the signature to a MH.invoke methodOop
   if (!pool->cache()->entry_at(main_index)->is_resolved(bytecode)) {
@@ -740,9 +741,10 @@
   assert(signature_invoker.not_null() && signature_invoker->is_method() && signature_invoker->is_method_handle_invoke(),
          "correct result from LinkResolver::resolve_invokedynamic");
 
+  Handle info;  // optional argument(s) in JVM_CONSTANT_InvokeDynamic
   Handle bootm = SystemDictionary::find_bootstrap_method(caller_method, caller_bci,
-                                                         main_index, CHECK);
-  if (bootm.is_null()) {
+                                                         main_index, info, CHECK);
+  if (!java_dyn_MethodHandle::is_instance(bootm())) {
     THROW_MSG(vmSymbols::java_lang_IllegalStateException(),
               "no bootstrap method found for invokedynamic");
   }
@@ -753,8 +755,6 @@
 
   symbolHandle call_site_name(THREAD, pool->name_ref_at(site_index));
 
-  Handle info;  // NYI: Other metadata from a new kind of CP entry.  (Annotations?)
-
   Handle call_site
     = SystemDictionary::make_dynamic_call_site(bootm,
                                                // Callee information:
--- a/src/share/vm/memory/universe.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/memory/universe.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2010, 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
@@ -340,6 +340,7 @@
   static klassOop* longArrayKlassObj_addr()           { return &_longArrayKlassObj;   }
   static klassOop* singleArrayKlassObj_addr()         { return &_singleArrayKlassObj; }
   static klassOop* doubleArrayKlassObj_addr()         { return &_doubleArrayKlassObj; }
+  static klassOop* systemObjArrayKlassObj_addr()      { return &_systemObjArrayKlassObj; }
 
   // The particular choice of collected heap.
   static CollectedHeap* heap() { return _collectedHeap; }
--- a/src/share/vm/oops/constantPoolKlass.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/oops/constantPoolKlass.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2010, 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
@@ -34,6 +34,7 @@
   c->set_length(length);
   c->set_tags(NULL);
   c->set_cache(NULL);
+  c->set_operands(NULL);
   c->set_pool_holder(NULL);
   c->set_flags(0);
   // only set to non-zero if constant pool is merged by RedefineClasses
@@ -92,6 +93,7 @@
     // gc of constant pool instance variables
     MarkSweep::mark_and_push(cp->tags_addr());
     MarkSweep::mark_and_push(cp->cache_addr());
+    MarkSweep::mark_and_push(cp->operands_addr());
     MarkSweep::mark_and_push(cp->pool_holder_addr());
   }
 }
@@ -118,6 +120,7 @@
     // gc of constant pool instance variables
     PSParallelCompact::mark_and_push(cm, cp->tags_addr());
     PSParallelCompact::mark_and_push(cm, cp->cache_addr());
+    PSParallelCompact::mark_and_push(cm, cp->operands_addr());
     PSParallelCompact::mark_and_push(cm, cp->pool_holder_addr());
   }
 }
@@ -146,6 +149,7 @@
   }
   MarkSweep::adjust_pointer(cp->tags_addr());
   MarkSweep::adjust_pointer(cp->cache_addr());
+  MarkSweep::adjust_pointer(cp->operands_addr());
   MarkSweep::adjust_pointer(cp->pool_holder_addr());
   return size;
 }
@@ -173,6 +177,7 @@
   }
   blk->do_oop(cp->tags_addr());
   blk->do_oop(cp->cache_addr());
+  blk->do_oop(cp->operands_addr());
   blk->do_oop(cp->pool_holder_addr());
   return size;
 }
@@ -205,6 +210,8 @@
   blk->do_oop(addr);
   addr = cp->cache_addr();
   blk->do_oop(addr);
+  addr = cp->operands_addr();
+  blk->do_oop(addr);
   addr = cp->pool_holder_addr();
   blk->do_oop(addr);
   return size;
@@ -232,6 +239,7 @@
   }
   PSParallelCompact::adjust_pointer(cp->tags_addr());
   PSParallelCompact::adjust_pointer(cp->cache_addr());
+  PSParallelCompact::adjust_pointer(cp->operands_addr());
   PSParallelCompact::adjust_pointer(cp->pool_holder_addr());
   return cp->object_size();
 }
@@ -262,6 +270,8 @@
   PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
   p = cp->cache_addr();
   PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
+  p = cp->operands_addr();
+  PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
   p = cp->pool_holder_addr();
   PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
 
@@ -363,8 +373,18 @@
         st->print("signature_index=%d", cp->method_type_index_at(index));
         break;
       case JVM_CONSTANT_InvokeDynamic :
-        st->print("bootstrap_method_index=%d", cp->invoke_dynamic_bootstrap_method_ref_index_at(index));
-        st->print(" name_and_type_index=%d", cp->invoke_dynamic_name_and_type_ref_index_at(index));
+        {
+          st->print("bootstrap_method_index=%d", cp->invoke_dynamic_bootstrap_method_ref_index_at(index));
+          st->print(" name_and_type_index=%d", cp->invoke_dynamic_name_and_type_ref_index_at(index));
+          int argc = cp->invoke_dynamic_argument_count_at(index);
+          if (argc > 0) {
+            for (int arg_i = 0; arg_i < argc; arg_i++) {
+              int arg = cp->invoke_dynamic_argument_index_at(index, arg_i);
+              st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg);
+            }
+            st->print("}");
+          }
+        }
         break;
       default:
         ShouldNotReachHere();
@@ -381,6 +401,7 @@
   st->print("constant pool [%d]", cp->length());
   if (cp->has_pseudo_string()) st->print("/pseudo_string");
   if (cp->has_invokedynamic()) st->print("/invokedynamic");
+  if (cp->operands() != NULL)  st->print("/operands[%d]", cp->operands()->length());
   cp->print_address_on(st);
   st->print(" for ");
   cp->pool_holder()->print_value_on(st);
@@ -440,6 +461,10 @@
       guarantee(cp->cache()->is_perm(),              "should be in permspace");
       guarantee(cp->cache()->is_constantPoolCache(), "should be constant pool cache");
     }
+    if (cp->operands() != NULL) {
+      guarantee(cp->operands()->is_perm(),  "should be in permspace");
+      guarantee(cp->operands()->is_typeArray(), "should be type array");
+    }
     if (cp->pool_holder() != NULL) {
       // Note: pool_holder() can be NULL in temporary constant pools
       // used during constant pool merging
--- a/src/share/vm/oops/constantPoolOop.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/oops/constantPoolOop.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -267,7 +267,7 @@
     if (constantPoolCacheOopDesc::is_secondary_index(which)) {
       // Invokedynamic index.
       int pool_index = cache()->main_entry_at(which)->constant_pool_index();
-      if (tag_at(pool_index).is_invoke_dynamic())
+      if (!AllowTransitionalJSR292 || tag_at(pool_index).is_invoke_dynamic())
         pool_index = invoke_dynamic_name_and_type_ref_index_at(pool_index);
       assert(tag_at(pool_index).is_name_and_type(), "");
       return pool_index;
@@ -275,11 +275,17 @@
     // change byte-ordering and go via cache
     i = remap_instruction_operand_from_cache(which);
   } else {
-    if (tag_at(which).is_name_and_type())
+    if (AllowTransitionalJSR292 && tag_at(which).is_name_and_type())
       // invokedynamic index is a simple name-and-type
       return which;
+    if (tag_at(which).is_invoke_dynamic()) {
+      int pool_index = invoke_dynamic_name_and_type_ref_index_at(which);
+      assert(tag_at(pool_index).is_name_and_type(), "");
+      return pool_index;
+    }
   }
   assert(tag_at(i).is_field_or_method(), "Corrupted constant pool");
+  assert(!tag_at(i).is_invoke_dynamic(), "Must be handled above");
   jint ref_index = *int_at_addr(i);
   return extract_high_short_from_int(ref_index);
 }
@@ -393,18 +399,61 @@
   }
 }
 
+// A resolved constant value in the CP cache is represented as a non-null
+// value.  As a special case, this value can be a 'systemObjArray'
+// which masks an exception object to throw.
+// This allows a MethodHandle constant reference to throw a consistent
+// exception every time, if it fails to resolve.
+static oop decode_exception_from_f1(oop result_oop, TRAPS) {
+  if (result_oop->klass() != Universe::systemObjArrayKlassObj())
+    return result_oop;
+
+  // Special cases here:  Masked null, saved exception.
+  objArrayOop sys_array = (objArrayOop) result_oop;
+  assert(sys_array->length() == 1, "bad system array");
+  if (sys_array->length() == 1) {
+    THROW_OOP_(sys_array->obj_at(0), NULL);
+  }
+  return NULL;
+}
+
 oop constantPoolOopDesc::resolve_constant_at_impl(constantPoolHandle this_oop, int index, int cache_index, TRAPS) {
   oop result_oop = NULL;
+  Handle throw_exception;
+
+  if (cache_index == _possible_index_sentinel) {
+    // It is possible that this constant is one which is cached in the CP cache.
+    // We'll do a linear search.  This should be OK because this usage is rare.
+    assert(index > 0, "valid index");
+    constantPoolCacheOop cache = this_oop()->cache();
+    for (int i = 0, len = cache->length(); i < len; i++) {
+      ConstantPoolCacheEntry* cpc_entry = cache->entry_at(i);
+      if (!cpc_entry->is_secondary_entry() && cpc_entry->constant_pool_index() == index) {
+        // Switch the query to use this CPC entry.
+        cache_index = i;
+        index = _no_index_sentinel;
+        break;
+      }
+    }
+    if (cache_index == _possible_index_sentinel)
+      cache_index = _no_index_sentinel;  // not found
+  }
+  assert(cache_index == _no_index_sentinel || cache_index >= 0, "");
+  assert(index == _no_index_sentinel || index >= 0, "");
+
   if (cache_index >= 0) {
-    assert(index < 0, "only one kind of index at a time");
+    assert(index == _no_index_sentinel, "only one kind of index at a time");
     ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index);
     result_oop = cpc_entry->f1();
     if (result_oop != NULL) {
-      return result_oop;  // that was easy...
+      return decode_exception_from_f1(result_oop, THREAD);
+      // That was easy...
     }
     index = cpc_entry->constant_pool_index();
   }
 
+  jvalue prim_value;  // temp used only in a few cases below
+
   int tag_value = this_oop->tag_at(index).value();
   switch (tag_value) {
 
@@ -448,9 +497,14 @@
       KlassHandle klass(THREAD, this_oop->pool_holder());
       Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,
                                                                    callee, name, signature,
-                                                                   CHECK_NULL);
+                                                                   THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        throw_exception = Handle(THREAD, PENDING_EXCEPTION);
+        CLEAR_PENDING_EXCEPTION;
+        break;
+      }
       result_oop = value();
-      // FIXME: Uniquify errors, using SystemDictionary::find_resolution_error.
+      assert(result_oop != NULL, "");
       break;
     }
 
@@ -467,20 +521,36 @@
                                                                klass,
                                                                false,
                                                                ignore_is_on_bcp,
-                                                               CHECK_NULL);
+                                                               THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        throw_exception = Handle(THREAD, PENDING_EXCEPTION);
+        CLEAR_PENDING_EXCEPTION;
+        break;
+      }
       result_oop = value();
-      // FIXME: Uniquify errors, using SystemDictionary::find_resolution_error.
+      assert(result_oop != NULL, "");
       break;
     }
 
-    /* maybe some day
   case JVM_CONSTANT_Integer:
+    prim_value.i = this_oop->int_at(index);
+    result_oop = java_lang_boxing_object::create(T_INT, &prim_value, CHECK_NULL);
+    break;
+
   case JVM_CONSTANT_Float:
+    prim_value.f = this_oop->float_at(index);
+    result_oop = java_lang_boxing_object::create(T_FLOAT, &prim_value, CHECK_NULL);
+    break;
+
   case JVM_CONSTANT_Long:
+    prim_value.j = this_oop->long_at(index);
+    result_oop = java_lang_boxing_object::create(T_LONG, &prim_value, CHECK_NULL);
+    break;
+
   case JVM_CONSTANT_Double:
-    result_oop = java_lang_boxing_object::create(...);
+    prim_value.d = this_oop->double_at(index);
+    result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL);
     break;
-    */
 
   default:
     DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d",
@@ -491,18 +561,31 @@
 
   if (cache_index >= 0) {
     // Cache the oop here also.
-    Handle result(THREAD, result_oop);
+    if (throw_exception.not_null()) {
+      objArrayOop sys_array = oopFactory::new_system_objArray(1, CHECK_NULL);
+      sys_array->obj_at_put(0, throw_exception());
+      result_oop = sys_array;
+      throw_exception = Handle();  // be tidy
+    }
+    Handle result_handle(THREAD, result_oop);
     result_oop = NULL;  // safety
     ObjectLocker ol(this_oop, THREAD);
     ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index);
-    oop result_oop2 = cpc_entry->f1();
-    if (result_oop2 != NULL) {
-      // Race condition:  May already be filled in while we were trying to lock.
-      return result_oop2;
+    result_oop = cpc_entry->f1();
+    // Benign race condition:  f1 may already be filled in while we were trying to lock.
+    // The important thing here is that all threads pick up the same result.
+    // It doesn't matter which racing thread wins, as long as only one
+    // result is used by all threads, and all future queries.
+    // That result may be either a resolved constant or a failure exception.
+    if (result_oop == NULL) {
+      result_oop = result_handle();
+      cpc_entry->set_f1(result_oop);
     }
-    cpc_entry->set_f1(result());
-    return result();
+    return decode_exception_from_f1(result_oop, THREAD);
   } else {
+    if (throw_exception.not_null()) {
+      THROW_HANDLE_(throw_exception, NULL);
+    }
     return result_oop;
   }
 }
@@ -620,6 +703,7 @@
 
 void constantPoolOopDesc::shared_tags_iterate(OopClosure* closure) {
   closure->do_oop(tags_addr());
+  closure->do_oop(operands_addr());
 }
 
 
@@ -837,13 +921,19 @@
 
   case JVM_CONSTANT_InvokeDynamic:
   {
-    int k1 = invoke_dynamic_bootstrap_method_ref_index_at(index1);
-    int k2 = cp2->invoke_dynamic_bootstrap_method_ref_index_at(index2);
-    if (k1 == k2) {
-      int i1 = invoke_dynamic_name_and_type_ref_index_at(index1);
-      int i2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2);
-      if (i1 == i2) {
-        return true;
+    int op_count = multi_operand_count_at(index1);
+    if (op_count == cp2->multi_operand_count_at(index2)) {
+      bool all_equal = true;
+      for (int op_i = 0; op_i < op_count; op_i++) {
+        int k1 = multi_operand_ref_at(index1, op_i);
+        int k2 = cp2->multi_operand_ref_at(index2, op_i);
+        if (k1 != k2) {
+          all_equal = false;
+          break;
+        }
+      }
+      if (all_equal) {
+        return true;           // got through loop; all elements equal
       }
     }
   } break;
@@ -880,6 +970,25 @@
 } // end compare_entry_to()
 
 
+// Grow this->operands() to the indicated length, unless it is already at least that long.
+void constantPoolOopDesc::multi_operand_buffer_grow(int min_length, TRAPS) {
+  int old_length = multi_operand_buffer_fill_pointer();
+  if (old_length >= min_length)  return;
+  int new_length = min_length;
+  assert(new_length > _multi_operand_buffer_fill_pointer_offset, "");
+  typeArrayHandle new_operands = oopFactory::new_permanent_intArray(new_length, CHECK);
+  if (operands() == NULL) {
+    new_operands->int_at_put(_multi_operand_buffer_fill_pointer_offset, old_length);
+  } else {
+    // copy fill pointer and everything else
+    for (int i = 0; i < old_length; i++) {
+      new_operands->int_at_put(i, operands()->int_at(i));
+    }
+  }
+  set_operands(new_operands());
+}
+
+
 // Copy this constant pool's entries at start_i to end_i (inclusive)
 // to the constant pool to_cp's entries starting at to_i. A total of
 // (end_i - start_i) + 1 entries are copied.
@@ -888,6 +997,13 @@
 
   int dest_i = to_i;  // leave original alone for debug purposes
 
+  if (operands() != NULL) {
+    // pre-grow the target CP's operand buffer
+    int nops = this->multi_operand_buffer_fill_pointer();
+    nops   += to_cp->multi_operand_buffer_fill_pointer();
+    to_cp->multi_operand_buffer_grow(nops, CHECK);
+  }
+
   for (int src_i = start_i; src_i <= end_i; /* see loop bottom */ ) {
     copy_entry_to(src_i, to_cp, dest_i, CHECK);
 
@@ -1036,9 +1152,26 @@
 
   case JVM_CONSTANT_InvokeDynamic:
   {
+    int op_count = multi_operand_count_at(from_i);
+    int fillp = to_cp->multi_operand_buffer_fill_pointer();
+    int to_op_base = fillp - _multi_operand_count_offset;  // fillp is count offset; get to base
+    to_cp->multi_operand_buffer_grow(to_op_base + op_count, CHECK);
+    to_cp->operands()->int_at_put(fillp++, op_count);
+    assert(fillp == to_op_base + _multi_operand_base_offset, "just wrote count, will now write args");
+    for (int op_i = 0; op_i < op_count; op_i++) {
+      int op = multi_operand_ref_at(from_i, op_i);
+      to_cp->operands()->int_at_put(fillp++, op);
+    }
+    assert(fillp <= to_cp->operands()->length(), "oob");
+    to_cp->set_multi_operand_buffer_fill_pointer(fillp);
+    to_cp->invoke_dynamic_at_put(to_i, to_op_base, op_count);
+#ifdef ASSERT
     int k1 = invoke_dynamic_bootstrap_method_ref_index_at(from_i);
     int k2 = invoke_dynamic_name_and_type_ref_index_at(from_i);
-    to_cp->invoke_dynamic_at_put(to_i, k1, k2);
+    int k3 = invoke_dynamic_argument_count_at(from_i);
+    assert(to_cp->check_invoke_dynamic_at(to_i, k1, k2, k3),
+           "indy structure is OK");
+#endif //ASSERT
   } break;
 
   // Invalid is used as the tag for the second constant pool entry
@@ -1256,8 +1389,11 @@
     case JVM_CONSTANT_Methodref:
     case JVM_CONSTANT_InterfaceMethodref:
     case JVM_CONSTANT_NameAndType:
+      return 5;
+
     case JVM_CONSTANT_InvokeDynamic:
-      return 5;
+      // u1 tag, u2 bsm, u2 nt, u2 argc, u2 argv[argc]
+      return 7 + 2 * invoke_dynamic_argument_count_at(idx);
 
     case JVM_CONSTANT_Long:
     case JVM_CONSTANT_Double:
@@ -1474,9 +1610,15 @@
         *bytes = JVM_CONSTANT_InvokeDynamic;
         idx1 = invoke_dynamic_bootstrap_method_ref_index_at(idx);
         idx2 = invoke_dynamic_name_and_type_ref_index_at(idx);
+        int argc = invoke_dynamic_argument_count_at(idx);
         Bytes::put_Java_u2((address) (bytes+1), idx1);
         Bytes::put_Java_u2((address) (bytes+3), idx2);
-        DBG(printf("JVM_CONSTANT_InvokeDynamic: %hd %hd", idx1, idx2));
+        Bytes::put_Java_u2((address) (bytes+5), argc);
+        for (int arg_i = 0; arg_i < argc; arg_i++) {
+          int arg = invoke_dynamic_argument_index_at(idx, arg_i);
+          Bytes::put_Java_u2((address) (bytes+7+2*arg_i), arg);
+        }
+        DBG(printf("JVM_CONSTANT_InvokeDynamic: %hd %hd [%d]", idx1, idx2, argc));
         break;
       }
     }
--- a/src/share/vm/oops/constantPoolOop.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/oops/constantPoolOop.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -41,6 +41,7 @@
   typeArrayOop         _tags; // the tag array describing the constant pool's contents
   constantPoolCacheOop _cache;         // the cache holding interpreter runtime information
   klassOop             _pool_holder;   // the corresponding class
+  typeArrayOop         _operands;      // for variable-sized (InvokeDynamic) nodes, usually empty
   int                  _flags;         // a few header bits to describe contents for GC
   int                  _length; // number of elements in the array
   volatile bool        _is_conc_safe; // if true, safe for concurrent
@@ -52,6 +53,8 @@
   void tag_at_put(int which, jbyte t)          { tags()->byte_at_put(which, t); }
   void release_tag_at_put(int which, jbyte t)  { tags()->release_byte_at_put(which, t); }
 
+  void set_operands(typeArrayOop operands)     { oop_store_without_check((oop*)&_operands, operands); }
+
   enum FlagBit {
     FB_has_invokedynamic = 1,
     FB_has_pseudo_string = 2
@@ -67,6 +70,7 @@
   intptr_t* base() const { return (intptr_t*) (((char*) this) + sizeof(constantPoolOopDesc)); }
   oop* tags_addr()       { return (oop*)&_tags; }
   oop* cache_addr()      { return (oop*)&_cache; }
+  oop* operands_addr()   { return (oop*)&_operands; }
 
   oop* obj_at_addr(int which) const {
     assert(is_within_bounds(which), "index out of bounds");
@@ -95,6 +99,7 @@
 
  public:
   typeArrayOop tags() const                 { return _tags; }
+  typeArrayOop operands() const             { return _operands; }
 
   bool has_pseudo_string() const            { return flag_at(FB_has_pseudo_string); }
   bool has_invokedynamic() const            { return flag_at(FB_has_invokedynamic); }
@@ -113,6 +118,7 @@
   // Assembly code support
   static int tags_offset_in_bytes()         { return offset_of(constantPoolOopDesc, _tags); }
   static int cache_offset_in_bytes()        { return offset_of(constantPoolOopDesc, _cache); }
+  static int operands_offset_in_bytes()     { return offset_of(constantPoolOopDesc, _operands); }
   static int pool_holder_offset_in_bytes()  { return offset_of(constantPoolOopDesc, _pool_holder); }
 
   // Storing constants
@@ -156,10 +162,28 @@
     *int_at_addr(which) = ref_index;
   }
 
-  void invoke_dynamic_at_put(int which, int bootstrap_method_index, int name_and_type_index) {
+  void invoke_dynamic_at_put(int which, int operand_base, int operand_count) {
     tag_at_put(which, JVM_CONSTANT_InvokeDynamic);
-    *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_method_index;
+    *int_at_addr(which) = operand_base;  // this is the real information
   }
+#ifdef ASSERT
+  bool check_invoke_dynamic_at(int which,
+                               int bootstrap_method_index,
+                               int name_and_type_index,
+                               int argument_count) {
+    assert(invoke_dynamic_bootstrap_method_ref_index_at(which) == bootstrap_method_index,
+           "already stored by caller");
+    assert(invoke_dynamic_name_and_type_ref_index_at(which) == name_and_type_index,
+           "already stored by caller");
+    assert(invoke_dynamic_argument_count_at(which) == argument_count,
+           "consistent argument count");
+    if (argument_count != 0) {
+      invoke_dynamic_argument_index_at(which, 0);
+      invoke_dynamic_argument_index_at(which, argument_count - 1);
+    }
+    return true;
+  }
+#endif //ASSERT
 
   // Temporary until actual use
   void unresolved_string_at_put(int which, symbolOop s) {
@@ -401,15 +425,76 @@
     int sym = method_type_index_at(which);
     return symbol_at(sym);
   }
+
+ private:
+  // some nodes (InvokeDynamic) have a variable number of operands, each a u2 value
+  enum { _multi_operand_count_offset = -1,
+         _multi_operand_base_offset  = 0,
+         _multi_operand_buffer_fill_pointer_offset = 0  // shared at front of operands array
+  };
+  int multi_operand_buffer_length() {
+    return operands() == NULL ? 0 : operands()->length();
+  }
+  int multi_operand_buffer_fill_pointer() {
+    return operands() == NULL
+      ? _multi_operand_buffer_fill_pointer_offset + 1
+      : operands()->int_at(_multi_operand_buffer_fill_pointer_offset);
+  }
+  void multi_operand_buffer_grow(int min_length, TRAPS);
+  void set_multi_operand_buffer_fill_pointer(int fillp) {
+    assert(operands() != NULL, "");
+    operands()->int_at_put(_multi_operand_buffer_fill_pointer_offset, fillp);
+  }
+  int multi_operand_base_at(int which) {
+    assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
+    int op_base = *int_at_addr(which);
+    assert(op_base > _multi_operand_buffer_fill_pointer_offset, "Corrupted operand base");
+    return op_base;
+  }
+  int multi_operand_count_at(int which) {
+    int op_base = multi_operand_base_at(which);
+    assert((uint)(op_base + _multi_operand_count_offset) < (uint)operands()->length(), "oob");
+    int count = operands()->int_at(op_base + _multi_operand_count_offset);
+    return count;
+  }
+  int multi_operand_ref_at(int which, int i) {
+    int op_base = multi_operand_base_at(which);
+    assert((uint)i < (uint)multi_operand_count_at(which), "oob");
+    assert((uint)(op_base + _multi_operand_base_offset + i) < (uint)operands()->length(), "oob");
+    return operands()->int_at(op_base + _multi_operand_base_offset + i);
+  }
+  void set_multi_operand_ref_at(int which, int i, int ref) {
+    DEBUG_ONLY(multi_operand_ref_at(which, i));  // trigger asserts
+    int op_base = multi_operand_base_at(which);
+    operands()->int_at_put(op_base + _multi_operand_base_offset + i, ref);
+  }
+
+ public:
+  // layout of InvokeDynamic:
+  enum {
+         _indy_bsm_offset  = 0,  // CONSTANT_MethodHandle bsm
+         _indy_nt_offset   = 1,  // CONSTANT_NameAndType descr
+         _indy_argc_offset = 2,  // u2 argc
+         _indy_argv_offset = 3   // u2 argv[argc]
+  };
   int invoke_dynamic_bootstrap_method_ref_index_at(int which) {
     assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
-    jint ref_index = *int_at_addr(which);
-    return extract_low_short_from_int(ref_index);
+    return multi_operand_ref_at(which, _indy_bsm_offset);
   }
   int invoke_dynamic_name_and_type_ref_index_at(int which) {
     assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
-    jint ref_index = *int_at_addr(which);
-    return extract_high_short_from_int(ref_index);
+    return multi_operand_ref_at(which, _indy_nt_offset);
+  }
+  int invoke_dynamic_argument_count_at(int which) {
+    assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
+    int argc = multi_operand_ref_at(which, _indy_argc_offset);
+    DEBUG_ONLY(int op_count = multi_operand_count_at(which));
+    assert(_indy_argv_offset + argc == op_count, "consistent inner and outer counts");
+    return argc;
+  }
+  int invoke_dynamic_argument_index_at(int which, int j) {
+    assert((uint)j < (uint)invoke_dynamic_argument_count_at(which), "oob");
+    return multi_operand_ref_at(which, _indy_argv_offset + j);
   }
 
   // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve,
@@ -448,15 +533,24 @@
     resolve_string_constants_impl(h_this, CHECK);
   }
 
+ private:
+  enum { _no_index_sentinel = -1, _possible_index_sentinel = -2 };
+ public:
+
   // Resolve late bound constants.
   oop resolve_constant_at(int index, TRAPS) {
     constantPoolHandle h_this(THREAD, this);
-    return resolve_constant_at_impl(h_this, index, -1, THREAD);
+    return resolve_constant_at_impl(h_this, index, _no_index_sentinel, THREAD);
   }
 
   oop resolve_cached_constant_at(int cache_index, TRAPS) {
     constantPoolHandle h_this(THREAD, this);
-    return resolve_constant_at_impl(h_this, -1, cache_index, THREAD);
+    return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, THREAD);
+  }
+
+  oop resolve_possibly_cached_constant_at(int pool_index, TRAPS) {
+    constantPoolHandle h_this(THREAD, this);
+    return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, THREAD);
   }
 
   // Klass name matches name at offset
--- a/src/share/vm/oops/cpCacheOop.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/oops/cpCacheOop.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -319,7 +319,9 @@
 
   // Sizing
   debug_only(friend class ClassVerifier;)
+ public:
   int length() const                             { return _length; }
+ private:
   void set_length(int length)                    { _length = length; }
 
   static int header_size()                       { return sizeof(constantPoolCacheOopDesc) / HeapWordSize; }
--- a/src/share/vm/prims/jvm.h	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/prims/jvm.h	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2010, 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
@@ -1047,7 +1047,8 @@
     JVM_CONSTANT_NameAndType,
     JVM_CONSTANT_MethodHandle           = 15,  // JSR 292
     JVM_CONSTANT_MethodType             = 16,  // JSR 292
-    JVM_CONSTANT_InvokeDynamic          = 17  // JSR 292
+    JVM_CONSTANT_InvokeDynamicTrans     = 17,  // JSR 292, only occurs in old class files
+    JVM_CONSTANT_InvokeDynamic          = 18   // JSR 292
 };
 
 /* JVM_CONSTANT_MethodHandle subtypes */
--- a/src/share/vm/prims/methodComparator.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/prims/methodComparator.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -147,10 +147,9 @@
   case Bytecodes::_invokevirtual   : // fall through
   case Bytecodes::_invokespecial   : // fall through
   case Bytecodes::_invokestatic    : // fall through
-  case Bytecodes::_invokedynamic   : // fall through
   case Bytecodes::_invokeinterface : {
-    int cpci_old = _s_old->has_index_u4() ? _s_old->get_index_u4() : _s_old->get_index_u2_cpcache();
-    int cpci_new = _s_new->has_index_u4() ? _s_new->get_index_u4() : _s_new->get_index_u2_cpcache();
+    int cpci_old = _s_old->get_index_u2_cpcache();
+    int cpci_new = _s_new->get_index_u2_cpcache();
     // Check if the names of classes, field/method names and signatures at these indexes
     // are the same. Indices which are really into constantpool cache (rather than constant
     // pool itself) are accepted by the constantpool query routines below.
@@ -160,6 +159,33 @@
       return false;
     break;
   }
+  case Bytecodes::_invokedynamic: {
+    int cpci_old = _s_old->get_index_u4();
+    int cpci_new = _s_new->get_index_u4();
+    // Check if the names of classes, field/method names and signatures at these indexes
+    // are the same. Indices which are really into constantpool cache (rather than constant
+    // pool itself) are accepted by the constantpool query routines below.
+    if ((_old_cp->name_ref_at(cpci_old) != _new_cp->name_ref_at(cpci_new)) ||
+        (_old_cp->signature_ref_at(cpci_old) != _new_cp->signature_ref_at(cpci_new)))
+      return false;
+    int cpi_old = _old_cp->cache()->main_entry_at(cpci_old)->constant_pool_index();
+    int cpi_new = _new_cp->cache()->main_entry_at(cpci_new)->constant_pool_index();
+    int bsm_old = _old_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_old);
+    int bsm_new = _new_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_new);
+    if (!pool_constants_same(bsm_old, bsm_new))
+      return false;
+    int cnt_old = _old_cp->invoke_dynamic_argument_count_at(cpi_old);
+    int cnt_new = _new_cp->invoke_dynamic_argument_count_at(cpi_new);
+    if (cnt_old != cnt_new)
+      return false;
+    for (int arg_i = 0; arg_i < cnt_old; arg_i++) {
+      int idx_old = _old_cp->invoke_dynamic_argument_index_at(cpi_old, arg_i);
+      int idx_new = _new_cp->invoke_dynamic_argument_index_at(cpi_new, arg_i);
+      if (!pool_constants_same(idx_old, idx_new))
+        return false;
+    }
+    break;
+  }
 
   case Bytecodes::_ldc   : // fall through
   case Bytecodes::_ldc_w : {
@@ -167,51 +193,8 @@
     Bytecode_loadconstant* ldc_new = Bytecode_loadconstant_at(_s_new->method(), _s_new->bci());
     int cpi_old = ldc_old->pool_index();
     int cpi_new = ldc_new->pool_index();
-    constantTag tag_old = _old_cp->tag_at(cpi_old);
-    constantTag tag_new = _new_cp->tag_at(cpi_new);
-    if (tag_old.is_int() || tag_old.is_float()) {
-      if (tag_old.value() != tag_new.value())
-        return false;
-      if (tag_old.is_int()) {
-        if (_old_cp->int_at(cpi_old) != _new_cp->int_at(cpi_new))
-          return false;
-      } else {
-        // Use jint_cast to compare the bits rather than numerical values.
-        // This makes a difference for NaN constants.
-        if (jint_cast(_old_cp->float_at(cpi_old)) != jint_cast(_new_cp->float_at(cpi_new)))
-          return false;
-      }
-    } else if (tag_old.is_string() || tag_old.is_unresolved_string()) {
-      if (! (tag_new.is_unresolved_string() || tag_new.is_string()))
-        return false;
-      if (strcmp(_old_cp->string_at_noresolve(cpi_old),
-                 _new_cp->string_at_noresolve(cpi_new)) != 0)
-        return false;
-    } else if (tag_old.is_klass() || tag_old.is_unresolved_klass()) {
-      // tag_old should be klass - 4881222
-      if (! (tag_new.is_unresolved_klass() || tag_new.is_klass()))
-        return false;
-      if (_old_cp->klass_at_noresolve(cpi_old) !=
-          _new_cp->klass_at_noresolve(cpi_new))
-        return false;
-    } else if (tag_old.is_method_type() && tag_new.is_method_type()) {
-      int mti_old = _old_cp->method_type_index_at(cpi_old);
-      int mti_new = _new_cp->method_type_index_at(cpi_new);
-      if ((_old_cp->symbol_at(mti_old) != _new_cp->symbol_at(mti_new)))
-        return false;
-    } else if (tag_old.is_method_handle() && tag_new.is_method_handle()) {
-      if (_old_cp->method_handle_ref_kind_at(cpi_old) !=
-          _new_cp->method_handle_ref_kind_at(cpi_new))
-        return false;
-      int mhi_old = _old_cp->method_handle_index_at(cpi_old);
-      int mhi_new = _new_cp->method_handle_index_at(cpi_new);
-      if ((_old_cp->uncached_klass_ref_at_noresolve(mhi_old) != _new_cp->uncached_klass_ref_at_noresolve(mhi_new)) ||
-          (_old_cp->uncached_name_ref_at(mhi_old) != _new_cp->uncached_name_ref_at(mhi_new)) ||
-          (_old_cp->uncached_signature_ref_at(mhi_old) != _new_cp->uncached_signature_ref_at(mhi_new)))
-        return false;
-    } else {
-      return false;  // unknown tag
-    }
+    if (!pool_constants_same(cpi_old, cpi_new))
+      return false;
     break;
   }
 
@@ -392,6 +375,55 @@
   return true;
 }
 
+bool MethodComparator::pool_constants_same(int cpi_old, int cpi_new) {
+  constantTag tag_old = _old_cp->tag_at(cpi_old);
+  constantTag tag_new = _new_cp->tag_at(cpi_new);
+  if (tag_old.is_int() || tag_old.is_float()) {
+    if (tag_old.value() != tag_new.value())
+      return false;
+    if (tag_old.is_int()) {
+      if (_old_cp->int_at(cpi_old) != _new_cp->int_at(cpi_new))
+        return false;
+    } else {
+      // Use jint_cast to compare the bits rather than numerical values.
+      // This makes a difference for NaN constants.
+      if (jint_cast(_old_cp->float_at(cpi_old)) != jint_cast(_new_cp->float_at(cpi_new)))
+        return false;
+    }
+  } else if (tag_old.is_string() || tag_old.is_unresolved_string()) {
+    if (! (tag_new.is_unresolved_string() || tag_new.is_string()))
+      return false;
+    if (strcmp(_old_cp->string_at_noresolve(cpi_old),
+               _new_cp->string_at_noresolve(cpi_new)) != 0)
+      return false;
+  } else if (tag_old.is_klass() || tag_old.is_unresolved_klass()) {
+    // tag_old should be klass - 4881222
+    if (! (tag_new.is_unresolved_klass() || tag_new.is_klass()))
+      return false;
+    if (_old_cp->klass_at_noresolve(cpi_old) !=
+        _new_cp->klass_at_noresolve(cpi_new))
+      return false;
+  } else if (tag_old.is_method_type() && tag_new.is_method_type()) {
+    int mti_old = _old_cp->method_type_index_at(cpi_old);
+    int mti_new = _new_cp->method_type_index_at(cpi_new);
+    if ((_old_cp->symbol_at(mti_old) != _new_cp->symbol_at(mti_new)))
+      return false;
+  } else if (tag_old.is_method_handle() && tag_new.is_method_handle()) {
+    if (_old_cp->method_handle_ref_kind_at(cpi_old) !=
+        _new_cp->method_handle_ref_kind_at(cpi_new))
+      return false;
+    int mhi_old = _old_cp->method_handle_index_at(cpi_old);
+    int mhi_new = _new_cp->method_handle_index_at(cpi_new);
+    if ((_old_cp->uncached_klass_ref_at_noresolve(mhi_old) != _new_cp->uncached_klass_ref_at_noresolve(mhi_new)) ||
+        (_old_cp->uncached_name_ref_at(mhi_old) != _new_cp->uncached_name_ref_at(mhi_new)) ||
+        (_old_cp->uncached_signature_ref_at(mhi_old) != _new_cp->uncached_signature_ref_at(mhi_new)))
+      return false;
+  } else {
+    return false;  // unknown tag
+  }
+  return true;
+}
+
 
 int MethodComparator::check_stack_and_locals_size(methodOop old_method, methodOop new_method) {
   if (old_method->max_stack() != new_method->max_stack()) {
--- a/src/share/vm/prims/methodComparator.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/prims/methodComparator.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2010, 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
@@ -36,6 +36,7 @@
   static GrowableArray<int> *_fwd_jmps;
 
   static bool args_same(Bytecodes::Code c_old, Bytecodes::Code c_new);
+  static bool pool_constants_same(int cpi_old, int cpi_new);
   static int check_stack_and_locals_size(methodOop old_method, methodOop new_method);
 
  public:
--- a/src/share/vm/runtime/vmStructs.cpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/runtime/vmStructs.cpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1527,6 +1527,17 @@
                                                                           \
   declare_constant(symbolOopDesc::max_symbol_length)                      \
                                                                           \
+  /*************************************************/                     \
+  /* constantPoolOop layout enum for InvokeDynamic */                     \
+  /*************************************************/                     \
+                                                                          \
+  declare_constant(constantPoolOopDesc::_multi_operand_count_offset)      \
+  declare_constant(constantPoolOopDesc::_multi_operand_base_offset)       \
+  declare_constant(constantPoolOopDesc::_indy_bsm_offset)                 \
+  declare_constant(constantPoolOopDesc::_indy_nt_offset)                  \
+  declare_constant(constantPoolOopDesc::_indy_argc_offset)                \
+  declare_constant(constantPoolOopDesc::_indy_argv_offset)                \
+                                                                          \
   /*********************************************/                         \
   /* ConstantPoolCacheEntry FlagBitValues enum */                         \
   /*********************************************/                         \
--- a/src/share/vm/utilities/constantTag.hpp	Sat Oct 30 12:19:07 2010 -0700
+++ b/src/share/vm/utilities/constantTag.hpp	Sat Oct 30 13:08:23 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2010, 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
@@ -82,6 +82,13 @@
   bool is_method_handle() const            { return _tag == JVM_CONSTANT_MethodHandle; }
   bool is_invoke_dynamic() const           { return _tag == JVM_CONSTANT_InvokeDynamic; }
 
+  bool is_loadable_constant() const {
+    return ((_tag >= JVM_CONSTANT_Integer && _tag <= JVM_CONSTANT_String) ||
+            is_method_type() || is_method_handle() ||
+            is_unresolved_klass() || is_unresolved_string() ||
+            is_object());
+  }
+
   constantTag() {
     _tag = JVM_CONSTANT_Invalid;
   }