8186209: Tool support for ConstantDynamic
authorpsandoz
Fri, 08 Sep 2017 10:46:46 -0700
changeset 48826 c4d9d1b08e2e
parent 48825 ef8a98bc71f8
child 48827 8772acd913e5
8186209: Tool support for ConstantDynamic 8186046: Minimal ConstantDynamic support 8190972: Ensure that AOT/Graal filters out class files containing CONSTANT_Dynamic ahead of full AOT support Reviewed-by: acorn, coleenp, kvn Contributed-by: lois.foltan@oracle.com, john.r.rose@oracle.com, paul.sandoz@oracle.com
src/hotspot/cpu/x86/interp_masm_x86.cpp
src/hotspot/cpu/x86/macroAssembler_x86.cpp
src/hotspot/cpu/x86/templateTable_x86.cpp
src/hotspot/share/c1/c1_GraphBuilder.cpp
src/hotspot/share/ci/ciEnv.cpp
src/hotspot/share/ci/ciReplay.cpp
src/hotspot/share/ci/ciStreams.cpp
src/hotspot/share/classfile/classFileParser.cpp
src/hotspot/share/classfile/systemDictionary.cpp
src/hotspot/share/classfile/systemDictionary.hpp
src/hotspot/share/classfile/verifier.cpp
src/hotspot/share/classfile/verifier.hpp
src/hotspot/share/classfile/vmSymbols.cpp
src/hotspot/share/classfile/vmSymbols.hpp
src/hotspot/share/interpreter/bytecode.cpp
src/hotspot/share/interpreter/bytecodeInterpreter.cpp
src/hotspot/share/interpreter/bytecodeTracer.cpp
src/hotspot/share/interpreter/interpreterRuntime.cpp
src/hotspot/share/interpreter/linkResolver.cpp
src/hotspot/share/interpreter/linkResolver.hpp
src/hotspot/share/interpreter/rewriter.cpp
src/hotspot/share/interpreter/templateTable.cpp
src/hotspot/share/interpreter/templateTable.hpp
src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
src/hotspot/share/jvmci/vmStructs_jvmci.cpp
src/hotspot/share/memory/universe.cpp
src/hotspot/share/memory/universe.hpp
src/hotspot/share/oops/constantPool.cpp
src/hotspot/share/oops/constantPool.hpp
src/hotspot/share/oops/generateOopMap.cpp
src/hotspot/share/opto/parse2.cpp
src/hotspot/share/prims/jvm.cpp
src/hotspot/share/prims/jvmtiRedefineClasses.cpp
src/hotspot/share/prims/methodHandles.cpp
src/hotspot/share/runtime/globals.hpp
src/hotspot/share/runtime/reflection.cpp
src/hotspot/share/runtime/signature.cpp
src/hotspot/share/runtime/signature.hpp
src/hotspot/share/runtime/vmStructs.cpp
src/hotspot/share/utilities/constantTag.cpp
src/hotspot/share/utilities/constantTag.hpp
src/hotspot/share/utilities/exceptions.cpp
src/hotspot/share/utilities/exceptions.hpp
src/hotspot/share/utilities/globalDefinitions.hpp
src/java.base/share/classes/java/lang/BootstrapMethodError.java
src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java
src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java
src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
src/java.base/share/classes/java/lang/invoke/CallSite.java
src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
src/java.base/share/classes/java/lang/invoke/DynamicConstant.java
src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
src/java.base/share/classes/java/lang/invoke/package-info.java
src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
src/java.base/share/native/include/classfile_constants.h.template
src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java
src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java
src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java
src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java
src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java
src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java
src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java
src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java
test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java
test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java
test/jdk/java/lang/invoke/condy/CondyBSMException.java
test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java
test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java
test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java
test/jdk/java/lang/invoke/condy/CondyNestedTest.java
test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java
test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java
test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java
test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java
test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java
test/jdk/java/lang/invoke/condy/CondyWrongType.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/IsolatedMethodBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java
test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java
test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
test/langtools/tools/javac/lambda/ByteCodeTest.java
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -516,6 +516,8 @@
   // Add in the index
   addptr(result, tmp);
   load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+  // The resulting oop is null if the reference is not yet resolved.
+  // It is Universe::the_null_sentinel() if the reference resolved to NULL via condy.
 }
 
 // load cpool->resolved_klass_at(index)
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -836,7 +836,8 @@
   andq(rsp, -16);     // align stack as required by push_CPU_state and call
   push_CPU_state();   // keeps alignment at 16 bytes
   lea(c_rarg0, ExternalAddress((address) msg));
-  call_VM_leaf(CAST_FROM_FN_PTR(address, warning), c_rarg0);
+  lea(rax, ExternalAddress(CAST_FROM_FN_PTR(address, warning)));
+  call(rax);
   pop_CPU_state();
   mov(rsp, rbp);
   pop(rbp);
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -419,7 +419,7 @@
 void TemplateTable::ldc(bool wide) {
   transition(vtos, vtos);
   Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
-  Label call_ldc, notFloat, notClass, Done;
+  Label call_ldc, notFloat, notClass, notInt, Done;
 
   if (wide) {
     __ get_unsigned_2_byte_index_at_bcp(rbx, 1);
@@ -465,19 +465,18 @@
   __ jmp(Done);
 
   __ bind(notFloat);
-#ifdef ASSERT
-  {
-    Label L;
-    __ cmpl(rdx, JVM_CONSTANT_Integer);
-    __ jcc(Assembler::equal, L);
-    // String and Object are rewritten to fast_aldc
-    __ stop("unexpected tag type in ldc");
-    __ bind(L);
-  }
-#endif
-  // itos JVM_CONSTANT_Integer only
+  __ cmpl(rdx, JVM_CONSTANT_Integer);
+  __ jccb(Assembler::notEqual, notInt);
+
+  // itos
   __ movl(rax, Address(rcx, rbx, Address::times_ptr, base_offset));
   __ push(itos);
+  __ jmp(Done);
+
+  // assume the tag is for condy; if not, the VM runtime will tell us
+  __ bind(notInt);
+  condy_helper(Done);
+
   __ bind(Done);
 }
 
@@ -487,6 +486,7 @@
 
   Register result = rax;
   Register tmp = rdx;
+  Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
   int index_size = wide ? sizeof(u2) : sizeof(u1);
 
   Label resolved;
@@ -496,17 +496,28 @@
   assert_different_registers(result, tmp);
   __ get_cache_index_at_bcp(tmp, 1, index_size);
   __ load_resolved_reference_at_index(result, tmp);
-  __ testl(result, result);
+  __ testptr(result, result);
   __ jcc(Assembler::notZero, resolved);
 
   address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc);
 
   // first time invocation - must resolve first
-  __ movl(tmp, (int)bytecode());
-  __ call_VM(result, entry, tmp);
-
+  __ movl(rarg, (int)bytecode());
+  __ call_VM(result, entry, rarg);
   __ bind(resolved);
 
+  { // Check for the null sentinel.
+    // If we just called the VM, that already did the mapping for us,
+    // but it's harmless to retry.
+    Label notNull;
+    ExternalAddress null_sentinel((address)Universe::the_null_sentinel_addr());
+    __ movptr(tmp, null_sentinel);
+    __ cmpptr(tmp, result);
+    __ jccb(Assembler::notEqual, notNull);
+    __ xorptr(result, result);  // NULL object reference
+    __ bind(notNull);
+  }
+
   if (VerifyOops) {
     __ verify_oop(result);
   }
@@ -514,7 +525,7 @@
 
 void TemplateTable::ldc2_w() {
   transition(vtos, vtos);
-  Label Long, Done;
+  Label notDouble, notLong, Done;
   __ get_unsigned_2_byte_index_at_bcp(rbx, 1);
 
   __ get_cpool_and_tags(rcx, rax);
@@ -522,25 +533,143 @@
   const int tags_offset = Array<u1>::base_offset_in_bytes();
 
   // get type
-  __ cmpb(Address(rax, rbx, Address::times_1, tags_offset),
-          JVM_CONSTANT_Double);
-  __ jccb(Assembler::notEqual, Long);
+  __ movzbl(rdx, Address(rax, rbx, Address::times_1, tags_offset));
+  __ cmpl(rdx, JVM_CONSTANT_Double);
+  __ jccb(Assembler::notEqual, notDouble);
 
   // dtos
   __ load_double(Address(rcx, rbx, Address::times_ptr, base_offset));
   __ push(dtos);
 
-  __ jmpb(Done);
-  __ bind(Long);
+  __ jmp(Done);
+  __ bind(notDouble);
+  __ cmpl(rdx, JVM_CONSTANT_Long);
+  __ jccb(Assembler::notEqual, notLong);
 
   // ltos
   __ movptr(rax, Address(rcx, rbx, Address::times_ptr, base_offset + 0 * wordSize));
   NOT_LP64(__ movptr(rdx, Address(rcx, rbx, Address::times_ptr, base_offset + 1 * wordSize)));
   __ push(ltos);
+  __ jmp(Done);
+
+  __ bind(notLong);
+  condy_helper(Done);
 
   __ bind(Done);
 }
 
+void TemplateTable::condy_helper(Label& Done) {
+  const Register obj = rax;
+  const Register off = rbx;
+  const Register flags = rcx;
+  const Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
+  __ movl(rarg, (int)bytecode());
+  call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc), rarg);
+#ifndef _LP64
+  // borrow rdi from locals
+  __ get_thread(rdi);
+  __ get_vm_result_2(flags, rdi);
+  __ restore_locals();
+#else
+  __ get_vm_result_2(flags, r15_thread);
+#endif
+  // VMr = obj = base address to find primitive value to push
+  // VMr2 = flags = (tos, off) using format of CPCE::_flags
+  __ movl(off, flags);
+  __ andl(off, ConstantPoolCacheEntry::field_index_mask);
+  const Address field(obj, off, Address::times_1, 0*wordSize);
+
+  // What sort of thing are we loading?
+  __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
+  __ andl(flags, ConstantPoolCacheEntry::tos_state_mask);
+
+  switch (bytecode()) {
+  case Bytecodes::_ldc:
+  case Bytecodes::_ldc_w:
+    {
+      // tos in (itos, ftos, stos, btos, ctos, ztos)
+      Label notInt, notFloat, notShort, notByte, notChar, notBool;
+      __ cmpl(flags, itos);
+      __ jcc(Assembler::notEqual, notInt);
+      // itos
+      __ movl(rax, field);
+      __ push(itos);
+      __ jmp(Done);
+
+      __ bind(notInt);
+      __ cmpl(flags, ftos);
+      __ jcc(Assembler::notEqual, notFloat);
+      // ftos
+      __ load_float(field);
+      __ push(ftos);
+      __ jmp(Done);
+
+      __ bind(notFloat);
+      __ cmpl(flags, stos);
+      __ jcc(Assembler::notEqual, notShort);
+      // stos
+      __ load_signed_short(rax, field);
+      __ push(stos);
+      __ jmp(Done);
+
+      __ bind(notShort);
+      __ cmpl(flags, btos);
+      __ jcc(Assembler::notEqual, notByte);
+      // btos
+      __ load_signed_byte(rax, field);
+      __ push(btos);
+      __ jmp(Done);
+
+      __ bind(notByte);
+      __ cmpl(flags, ctos);
+      __ jcc(Assembler::notEqual, notChar);
+      // ctos
+      __ load_unsigned_short(rax, field);
+      __ push(ctos);
+      __ jmp(Done);
+
+      __ bind(notChar);
+      __ cmpl(flags, ztos);
+      __ jcc(Assembler::notEqual, notBool);
+      // ztos
+      __ load_signed_byte(rax, field);
+      __ push(ztos);
+      __ jmp(Done);
+
+      __ bind(notBool);
+      break;
+    }
+
+  case Bytecodes::_ldc2_w:
+    {
+      Label notLong, notDouble;
+      __ cmpl(flags, ltos);
+      __ jcc(Assembler::notEqual, notLong);
+      // ltos
+      __ movptr(rax, field);
+      NOT_LP64(__ movptr(rdx, field.plus_disp(4)));
+      __ push(ltos);
+      __ jmp(Done);
+
+      __ bind(notLong);
+      __ cmpl(flags, dtos);
+      __ jcc(Assembler::notEqual, notDouble);
+      // dtos
+      __ load_double(field);
+      __ push(dtos);
+      __ jmp(Done);
+
+      __ bind(notDouble);
+      break;
+    }
+
+  default:
+    ShouldNotReachHere();
+  }
+
+  __ stop("bad ldc/condy");
+}
+
 void TemplateTable::locals_index(Register reg, int offset) {
   __ load_unsigned_byte(reg, at_bcp(offset));
   __ negptr(reg);
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -874,6 +874,8 @@
 void GraphBuilder::load_constant() {
   ciConstant con = stream()->get_constant();
   if (con.basic_type() == T_ILLEGAL) {
+    // FIXME: an unresolved Dynamic constant can get here,
+    // and that should not terminate the whole compilation.
     BAILOUT("could not resolve a constant");
   } else {
     ValueType* t = illegalType;
@@ -893,11 +895,19 @@
         ciObject* obj = con.as_object();
         if (!obj->is_loaded()
             || (PatchALot && obj->klass() != ciEnv::current()->String_klass())) {
+          // A Class, MethodType, MethodHandle, or String.
+          // Unloaded condy nodes show up as T_ILLEGAL, above.
           patch_state = copy_state_before();
           t = new ObjectConstant(obj);
         } else {
-          assert(obj->is_instance(), "must be java_mirror of klass");
-          t = new InstanceConstant(obj->as_instance());
+          // Might be a Class, MethodType, MethodHandle, or Dynamic constant
+          // result, which might turn out to be an array.
+          if (obj->is_null_object())
+            t = objectNull;
+          else if (obj->is_array())
+            t = new ArrayConstant(obj->as_array());
+          else
+            t = new InstanceConstant(obj->as_instance());
         }
         break;
        }
--- a/src/hotspot/share/ci/ciEnv.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciEnv.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -584,8 +584,34 @@
   int index = pool_index;
   if (cache_index >= 0) {
     assert(index < 0, "only one kind of index at a time");
+    index = cpool->object_to_cp_index(cache_index);
     oop obj = cpool->resolved_references()->obj_at(cache_index);
     if (obj != NULL) {
+      if (obj == Universe::the_null_sentinel()) {
+        return ciConstant(T_OBJECT, get_object(NULL));
+      }
+      BasicType bt = T_OBJECT;
+      if (cpool->tag_at(index).is_dynamic_constant())
+        bt = FieldType::basic_type(cpool->uncached_signature_ref_at(index));
+      if (is_reference_type(bt)) {
+      } else {
+        // we have to unbox the primitive value
+        if (!is_java_primitive(bt))  return ciConstant();
+        jvalue value;
+        BasicType bt2 = java_lang_boxing_object::get_value(obj, &value);
+        assert(bt2 == bt, "");
+        switch (bt2) {
+        case T_DOUBLE:  return ciConstant(value.d);
+        case T_FLOAT:   return ciConstant(value.f);
+        case T_LONG:    return ciConstant(value.j);
+        case T_INT:     return ciConstant(bt2, value.i);
+        case T_SHORT:   return ciConstant(bt2, value.s);
+        case T_BYTE:    return ciConstant(bt2, value.b);
+        case T_CHAR:    return ciConstant(bt2, value.c);
+        case T_BOOLEAN: return ciConstant(bt2, value.z);
+        default:  return ciConstant();
+        }
+      }
       ciObject* ciobj = get_object(obj);
       if (ciobj->is_array()) {
         return ciConstant(T_ARRAY, ciobj);
@@ -594,7 +620,6 @@
         return ciConstant(T_OBJECT, ciobj);
       }
     }
-    index = cpool->object_to_cp_index(cache_index);
   }
   constantTag tag = cpool->tag_at(index);
   if (tag.is_int()) {
@@ -650,6 +675,8 @@
     ciSymbol* signature = get_symbol(cpool->method_handle_signature_ref_at(index));
     ciObject* ciobj     = get_unloaded_method_handle_constant(callee, name, signature, ref_kind);
     return ciConstant(T_OBJECT, ciobj);
+  } else if (tag.is_dynamic_constant()) {
+    return ciConstant();
   } else {
     ShouldNotReachHere();
     return ciConstant();
--- a/src/hotspot/share/ci/ciReplay.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciReplay.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -721,6 +721,7 @@
         case JVM_CONSTANT_Float:
         case JVM_CONSTANT_MethodHandle:
         case JVM_CONSTANT_MethodType:
+        case JVM_CONSTANT_Dynamic:
         case JVM_CONSTANT_InvokeDynamic:
           if (tag != cp->tag_at(i).value()) {
             report_error("tag mismatch: wrong class files?");
--- a/src/hotspot/share/ci/ciStreams.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciStreams.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -254,7 +254,8 @@
 // constant.
 constantTag ciBytecodeStream::get_constant_pool_tag(int index) const {
   VM_ENTRY_MARK;
-  return _method->get_Method()->constants()->tag_at(index);
+  BasicType bt = _method->get_Method()->constants()->basic_type_for_constant_at(index);
+  return constantTag::ofBasicType(bt);
 }
 
 // ------------------------------------------------------------------
--- a/src/hotspot/share/classfile/classFileParser.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -204,6 +204,21 @@
         }
         break;
       }
+      case JVM_CONSTANT_Dynamic : {
+        if (_major_version < Verifier::DYNAMICCONSTANT_MAJOR_VERSION) {
+          classfile_parse_error(
+              "Class file version does not support constant tag %u in class file %s",
+              tag, CHECK);
+        }
+        cfs->guarantee_more(5, CHECK);  // bsm_index, nt, tag/access_flags
+        const u2 bootstrap_specifier_index = cfs->get_u2_fast();
+        const u2 name_and_type_index = cfs->get_u2_fast();
+        if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) {
+          _max_bootstrap_specifier_index = (int) bootstrap_specifier_index;  // collect for later
+        }
+        cp->dynamic_constant_at_put(index, bootstrap_specifier_index, name_and_type_index);
+        break;
+      }
       case JVM_CONSTANT_InvokeDynamic : {
         if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
           classfile_parse_error(
@@ -536,6 +551,21 @@
           ref_index, CHECK);
         break;
       }
+      case JVM_CONSTANT_Dynamic: {
+        const int name_and_type_ref_index =
+          cp->invoke_dynamic_name_and_type_ref_index_at(index);
+
+        check_property(valid_cp_range(name_and_type_ref_index, length) &&
+          cp->tag_at(name_and_type_ref_index).is_name_and_type(),
+          "Invalid constant pool index %u in class file %s",
+          name_and_type_ref_index, CHECK);
+        // bootstrap specifier index must be checked later,
+        // when BootstrapMethods attr is available
+
+        // Mark the constant pool as having a CONSTANT_Dynamic_info structure
+        cp->set_has_dynamic_constant();
+        break;
+      }
       case JVM_CONSTANT_InvokeDynamic: {
         const int name_and_type_ref_index =
           cp->invoke_dynamic_name_and_type_ref_index_at(index);
@@ -628,6 +658,27 @@
         }
         break;
       }
+      case JVM_CONSTANT_Dynamic: {
+        const int name_and_type_ref_index =
+          cp->name_and_type_ref_index_at(index);
+        // already verified to be utf8
+        const int name_ref_index =
+          cp->name_ref_index_at(name_and_type_ref_index);
+        // already verified to be utf8
+        const int signature_ref_index =
+          cp->signature_ref_index_at(name_and_type_ref_index);
+        const Symbol* const name = cp->symbol_at(name_ref_index);
+        const Symbol* const signature = cp->symbol_at(signature_ref_index);
+        if (_need_verify) {
+          // CONSTANT_Dynamic's name and signature are verified above, when iterating NameAndType_info.
+          // Need only to be sure signature is non-zero length and the right type.
+          if (signature->utf8_length() == 0 ||
+              signature->byte_at(0) == JVM_SIGNATURE_FUNC) {
+            throwIllegalSignature("CONSTANT_Dynamic", name, signature, CHECK);
+          }
+        }
+        break;
+      }
       case JVM_CONSTANT_InvokeDynamic:
       case JVM_CONSTANT_Fieldref:
       case JVM_CONSTANT_Methodref:
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -2641,6 +2641,81 @@
           InstanceKlass::cast(klass)->is_same_class_package(SystemDictionary::MethodHandle_klass()));  // java.lang.invoke
 }
 
+
+// Return the Java mirror (java.lang.Class instance) for a single-character
+// descriptor.  This result, when available, is the same as produced by the
+// heavier API point of the same name that takes a Symbol.
+oop SystemDictionary::find_java_mirror_for_type(char signature_char) {
+  return java_lang_Class::primitive_mirror(char2type(signature_char));
+}
+
+// Find or construct the Java mirror (java.lang.Class instance) for a
+// for the given field type signature, as interpreted relative to the
+// given class loader.  Handles primitives, void, references, arrays,
+// and all other reflectable types, except method types.
+// N.B.  Code in reflection should use this entry point.
+Handle SystemDictionary::find_java_mirror_for_type(Symbol* signature,
+                                                   Klass* accessing_klass,
+                                                   Handle class_loader,
+                                                   Handle protection_domain,
+                                                   SignatureStream::FailureMode failure_mode,
+                                                   TRAPS) {
+  Handle empty;
+
+  assert(accessing_klass == NULL || (class_loader.is_null() && protection_domain.is_null()),
+         "one or the other, or perhaps neither");
+
+  Symbol* type = signature;
+
+  // What we have here must be a valid field descriptor,
+  // and all valid field descriptors are supported.
+  // Produce the same java.lang.Class that reflection reports.
+  if (type->utf8_length() == 1) {
+
+    // It's a primitive.  (Void has a primitive mirror too.)
+    char ch = (char) type->byte_at(0);
+    assert(is_java_primitive(char2type(ch)) || ch == 'V', "");
+    return Handle(THREAD, find_java_mirror_for_type(ch));
+
+  } else if (FieldType::is_obj(type) || FieldType::is_array(type)) {
+
+    // It's a reference type.
+    if (accessing_klass != NULL) {
+      class_loader      = Handle(THREAD, accessing_klass->class_loader());
+      protection_domain = Handle(THREAD, accessing_klass->protection_domain());
+    }
+    Klass* constant_type_klass;
+    if (failure_mode == SignatureStream::ReturnNull) {
+      constant_type_klass = resolve_or_null(type, class_loader, protection_domain,
+                                            CHECK_(empty));
+    } else {
+      bool throw_error = (failure_mode == SignatureStream::NCDFError);
+      constant_type_klass = resolve_or_fail(type, class_loader, protection_domain,
+                                            throw_error, CHECK_(empty));
+    }
+    if (constant_type_klass == NULL) {
+      return Handle();  // report failure this way
+    }
+    Handle mirror(THREAD, constant_type_klass->java_mirror());
+
+    // Check accessibility, emulating ConstantPool::verify_constant_pool_resolve.
+    if (accessing_klass != NULL) {
+      Klass* sel_klass = constant_type_klass;
+      bool fold_type_to_class = true;
+      LinkResolver::check_klass_accessability(accessing_klass, sel_klass,
+                                              fold_type_to_class, CHECK_(empty));
+    }
+
+    return mirror;
+
+  }
+
+  // Fall through to an error.
+  assert(false, "unsupported mirror syntax");
+  THROW_MSG_(vmSymbols::java_lang_InternalError(), "unsupported mirror syntax", empty);
+}
+
+
 // Ask Java code to find or construct a java.lang.invoke.MethodType for the given
 // signature, as interpreted relative to the given class loader.
 // Because of class loader constraints, all method handle usage must be
@@ -2695,15 +2770,13 @@
       pts->obj_at_put(arg++, mirror);
 
     // Check accessibility.
-    if (ss.is_object() && accessing_klass != NULL) {
+    if (!java_lang_Class::is_primitive(mirror) && accessing_klass != NULL) {
       Klass* sel_klass = java_lang_Class::as_Klass(mirror);
       mirror = NULL;  // safety
       // Emulate ConstantPool::verify_constant_pool_resolve.
-      if (sel_klass->is_objArray_klass())
-        sel_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass();
-      if (sel_klass->is_instance_klass()) {
-        LinkResolver::check_klass_accessability(accessing_klass, sel_klass, CHECK_(empty));
-      }
+      bool fold_type_to_class = true;
+      LinkResolver::check_klass_accessability(accessing_klass, sel_klass,
+                                              fold_type_to_class, CHECK_(empty));
     }
   }
   assert(arg == npts, "");
@@ -2806,9 +2879,60 @@
   return Handle(THREAD, (oop) result.get_jobject());
 }
 
+// Ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
+Handle SystemDictionary::link_dynamic_constant(Klass* caller,
+                                               int condy_index,
+                                               Handle bootstrap_specifier,
+                                               Symbol* name,
+                                               Symbol* type,
+                                               TRAPS) {
+  Handle empty;
+  Handle bsm, info;
+  if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
+    bsm = bootstrap_specifier;
+  } else {
+    assert(bootstrap_specifier->is_objArray(), "");
+    objArrayOop args = (objArrayOop) bootstrap_specifier();
+    assert(args->length() == 2, "");
+    bsm  = Handle(THREAD, args->obj_at(0));
+    info = Handle(THREAD, args->obj_at(1));
+  }
+  guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
+            "caller must supply a valid BSM");
+
+  // This should not happen.  JDK code should take care of that.
+  if (caller == NULL) {
+    THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad dynamic constant", empty);
+  }
+
+  Handle constant_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
+
+  // Resolve the constant type in the context of the caller class
+  Handle type_mirror = find_java_mirror_for_type(type, caller, SignatureStream::NCDFError,
+                                                 CHECK_(empty));
+
+  // call java.lang.invoke.MethodHandleNatives::linkConstantDyanmic(caller, condy_index, bsm, type, info)
+  JavaCallArguments args;
+  args.push_oop(Handle(THREAD, caller->java_mirror()));
+  args.push_int(condy_index);
+  args.push_oop(bsm);
+  args.push_oop(constant_name);
+  args.push_oop(type_mirror);
+  args.push_oop(info);
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_static(&result,
+                         SystemDictionary::MethodHandleNatives_klass(),
+                         vmSymbols::linkDynamicConstant_name(),
+                         vmSymbols::linkDynamicConstant_signature(),
+                         &args, CHECK_(empty));
+
+  return Handle(THREAD, (oop) result.get_jobject());
+}
+
 // Ask Java code to find or construct a java.lang.invoke.CallSite for the given
 // name and signature, as interpreted relative to the given class loader.
 methodHandle SystemDictionary::find_dynamic_call_site_invoker(Klass* caller,
+                                                              int indy_index,
                                                               Handle bootstrap_specifier,
                                                               Symbol* name,
                                                               Symbol* type,
@@ -2820,17 +2944,10 @@
   if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
     bsm = bootstrap_specifier;
   } else {
-    assert(bootstrap_specifier->is_objArray(), "");
-    objArrayHandle args(THREAD, (objArrayOop) bootstrap_specifier());
-    int len = args->length();
-    assert(len >= 1, "");
-    bsm = Handle(THREAD, args->obj_at(0));
-    if (len > 1) {
-      objArrayOop args1 = oopFactory::new_objArray(SystemDictionary::Object_klass(), len-1, CHECK_(empty));
-      for (int i = 1; i < len; i++)
-        args1->obj_at_put(i-1, args->obj_at(i));
-      info = Handle(THREAD, args1);
-    }
+    objArrayOop args = (objArrayOop) bootstrap_specifier();
+    assert(args->length() == 2, "");
+    bsm  = Handle(THREAD, args->obj_at(0));
+    info = Handle(THREAD, args->obj_at(1));
   }
   guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
             "caller must supply a valid BSM");
@@ -2846,9 +2963,10 @@
   objArrayHandle appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK_(empty));
   assert(appendix_box->obj_at(0) == NULL, "");
 
-  // call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, bsm, name, mtype, info, &appendix)
+  // call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, indy_index, bsm, name, mtype, info, &appendix)
   JavaCallArguments args;
   args.push_oop(Handle(THREAD, caller->java_mirror()));
+  args.push_int(indy_index);
   args.push_oop(bsm);
   args.push_oop(method_name);
   args.push_oop(method_type);
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -32,6 +32,7 @@
 #include "oops/symbol.hpp"
 #include "runtime/java.hpp"
 #include "runtime/reflectionUtils.hpp"
+#include "runtime/signature.hpp"
 #include "utilities/hashtable.hpp"
 #include "utilities/hashtable.inline.hpp"
 
@@ -527,6 +528,28 @@
   static methodHandle find_method_handle_intrinsic(vmIntrinsics::ID iid,
                                                    Symbol* signature,
                                                    TRAPS);
+
+  // compute java_mirror (java.lang.Class instance) for a type ("I", "[[B", "LFoo;", etc.)
+  // Either the accessing_klass or the CL/PD can be non-null, but not both.
+  static Handle    find_java_mirror_for_type(Symbol* signature,
+                                             Klass* accessing_klass,
+                                             Handle class_loader,
+                                             Handle protection_domain,
+                                             SignatureStream::FailureMode failure_mode,
+                                             TRAPS);
+  static Handle    find_java_mirror_for_type(Symbol* signature,
+                                             Klass* accessing_klass,
+                                             SignatureStream::FailureMode failure_mode,
+                                             TRAPS) {
+    // callee will fill in CL/PD from AK, if they are needed
+    return find_java_mirror_for_type(signature, accessing_klass, Handle(), Handle(),
+                                     failure_mode, THREAD);
+  }
+
+
+  // fast short-cut for the one-character case:
+  static oop       find_java_mirror_for_type(char signature_char);
+
   // find a java.lang.invoke.MethodType object for a given signature
   // (asks Java to compute it if necessary, except in a compiler thread)
   static Handle    find_method_handle_type(Symbol* signature,
@@ -546,8 +569,17 @@
                                                Symbol* signature,
                                                TRAPS);
 
+  // ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
+  static Handle    link_dynamic_constant(Klass* caller,
+                                         int condy_index,
+                                         Handle bootstrap_specifier,
+                                         Symbol* name,
+                                         Symbol* type,
+                                         TRAPS);
+
   // ask Java to create a dynamic call site, while linking an invokedynamic op
   static methodHandle find_dynamic_call_site_invoker(Klass* caller,
+                                                     int indy_index,
                                                      Handle bootstrap_method,
                                                      Symbol* name,
                                                      Symbol* type,
--- a/src/hotspot/share/classfile/verifier.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/verifier.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -2054,19 +2054,21 @@
     const constantPoolHandle& cp, u2 bci, TRAPS) {
   verify_cp_index(bci, cp, index, CHECK_VERIFY(this));
   constantTag tag = cp->tag_at(index);
-  unsigned int types;
+  unsigned int types = 0;
   if (opcode == Bytecodes::_ldc || opcode == Bytecodes::_ldc_w) {
     if (!tag.is_unresolved_klass()) {
       types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float)
             | (1 << JVM_CONSTANT_String)  | (1 << JVM_CONSTANT_Class)
-            | (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType);
+            | (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType)
+            | (1 << JVM_CONSTANT_Dynamic);
       // Note:  The class file parser already verified the legality of
       // MethodHandle and MethodType constants.
       verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
     }
   } else {
     assert(opcode == Bytecodes::_ldc2_w, "must be ldc2_w");
-    types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long);
+    types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long)
+          | (1 << JVM_CONSTANT_Dynamic);
     verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
   }
   if (tag.is_string() && cp->is_pseudo_string_at(index)) {
@@ -2101,6 +2103,30 @@
     current_frame->push_stack(
       VerificationType::reference_type(
         vmSymbols::java_lang_invoke_MethodType()), CHECK_VERIFY(this));
+  } else if (tag.is_dynamic_constant()) {
+    Symbol* constant_type = cp->uncached_signature_ref_at(index);
+    if (!SignatureVerifier::is_valid_type_signature(constant_type)) {
+      class_format_error(
+        "Invalid type for dynamic constant in class %s referenced "
+        "from constant pool index %d", _klass->external_name(), index);
+      return;
+    }
+    assert(sizeof(VerificationType) == sizeof(uintptr_t),
+          "buffer type must match VerificationType size");
+    uintptr_t constant_type_buffer[2];
+    VerificationType* v_constant_type = (VerificationType*)constant_type_buffer;
+    SignatureStream sig_stream(constant_type, false);
+    int n = change_sig_to_verificationType(
+      &sig_stream, v_constant_type, CHECK_VERIFY(this));
+    int opcode_n = (opcode == Bytecodes::_ldc2_w ? 2 : 1);
+    if (n != opcode_n) {
+      // wrong kind of ldc; reverify against updated type mask
+      types &= ~(1 << JVM_CONSTANT_Dynamic);
+      verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
+    }
+    for (int i = 0; i < n; i++) {
+      current_frame->push_stack(v_constant_type[i], CHECK_VERIFY(this));
+    }
   } else {
     /* Unreachable? verify_cp_type has already validated the cp type. */
     verify_error(
@@ -2665,7 +2691,7 @@
   // Make sure the constant pool item is the right type
   u2 index = bcs->get_index_u2();
   Bytecodes::Code opcode = bcs->raw_code();
-  unsigned int types;
+  unsigned int types = 0;
   switch (opcode) {
     case Bytecodes::_invokeinterface:
       types = 1 << JVM_CONSTANT_InterfaceMethodref;
--- a/src/hotspot/share/classfile/verifier.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/verifier.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -40,7 +40,8 @@
     STRICTER_ACCESS_CTRL_CHECK_VERSION  = 49,
     STACKMAP_ATTRIBUTE_MAJOR_VERSION    = 50,
     INVOKEDYNAMIC_MAJOR_VERSION         = 51,
-    NO_RELAX_ACCESS_CTRL_CHECK_VERSION  = 52
+    NO_RELAX_ACCESS_CTRL_CHECK_VERSION  = 52,
+    DYNAMICCONSTANT_MAJOR_VERSION       = 55
   };
   typedef enum { ThrowException, NoException } Mode;
 
--- a/src/hotspot/share/classfile/vmSymbols.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/vmSymbols.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -98,6 +98,14 @@
     _type_signatures[T_BOOLEAN] = bool_signature();
     _type_signatures[T_VOID]    = void_signature();
     // no single signatures for T_OBJECT or T_ARRAY
+#ifdef ASSERT
+    for (int i = (int)T_BOOLEAN; i < (int)T_VOID+1; i++) {
+      Symbol* s = _type_signatures[i];
+      if (s == NULL)  continue;
+      BasicType st = signature_type(s);
+      assert(st == i, "");
+    }
+#endif
   }
 
 #ifdef ASSERT
@@ -202,9 +210,11 @@
 
 BasicType vmSymbols::signature_type(const Symbol* s) {
   assert(s != NULL, "checking");
-  for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
-    if (s == _type_signatures[i]) {
-      return (BasicType)i;
+  if (s->utf8_length() == 1) {
+    BasicType result = char2type(s->byte_at(0));
+    if (is_java_primitive(result) || result == T_VOID) {
+      assert(s == _type_signatures[result], "");
+      return result;
     }
   }
   return T_OBJECT;
--- a/src/hotspot/share/classfile/vmSymbols.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/vmSymbols.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -307,8 +307,10 @@
   template(linkMethodHandleConstant_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;") \
   template(linkMethod_name,                           "linkMethod")                               \
   template(linkMethod_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
+  template(linkDynamicConstant_name,                  "linkDynamicConstant")                      \
+  template(linkDynamicConstant_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \
   template(linkCallSite_name,                         "linkCallSite")                             \
-  template(linkCallSite_signature, "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
+  template(linkCallSite_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
   template(setTargetNormal_name,                      "setTargetNormal")                          \
   template(setTargetVolatile_name,                    "setTargetVolatile")                        \
   template(setTarget_signature,                       "(Ljava/lang/invoke/MethodHandle;)V")       \
--- a/src/hotspot/share/interpreter/bytecode.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecode.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -207,8 +207,7 @@
 
 BasicType Bytecode_loadconstant::result_type() const {
   int index = pool_index();
-  constantTag tag = _method->constants()->tag_at(index);
-  return tag.basic_type();
+  return _method->constants()->basic_type_for_constant_at(index);
 }
 
 oop Bytecode_loadconstant::resolve_constant(TRAPS) const {
@@ -217,6 +216,8 @@
   ConstantPool* constants = _method->constants();
   if (has_cache_index()) {
     return constants->resolve_cached_constant_at(index, THREAD);
+  } else if (_method->constants()->tag_at(index).is_dynamic_constant()) {
+    return constants->resolve_possibly_cached_constant_at(index, THREAD);
   } else {
     return constants->resolve_constant_at(index, THREAD);
   }
--- a/src/hotspot/share/interpreter/bytecodeInterpreter.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecodeInterpreter.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -2368,6 +2368,30 @@
             THREAD->set_vm_result(NULL);
             break;
 
+          case JVM_CONSTANT_Dynamic:
+            {
+              oop result = constants->resolved_references()->obj_at(index);
+              if (result == NULL) {
+                CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
+                result = THREAD->vm_result();
+              }
+              VERIFY_OOP(result);
+
+              jvalue value;
+              BasicType type = java_lang_boxing_object::get_value(result, &value);
+              switch (type) {
+              case T_FLOAT:   SET_STACK_FLOAT(value.f, 0); break;
+              case T_INT:     SET_STACK_INT(value.i, 0); break;
+              case T_SHORT:   SET_STACK_INT(value.s, 0); break;
+              case T_BYTE:    SET_STACK_INT(value.b, 0); break;
+              case T_CHAR:    SET_STACK_INT(value.c, 0); break;
+              case T_BOOLEAN: SET_STACK_INT(value.z, 0); break;
+              default:  ShouldNotReachHere();
+              }
+
+              break;
+            }
+
           default:  ShouldNotReachHere();
           }
           UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
@@ -2387,6 +2411,27 @@
           case JVM_CONSTANT_Double:
              SET_STACK_DOUBLE(constants->double_at(index), 1);
             break;
+
+          case JVM_CONSTANT_Dynamic:
+            {
+              oop result = constants->resolved_references()->obj_at(index);
+              if (result == NULL) {
+                CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
+                result = THREAD->vm_result();
+              }
+              VERIFY_OOP(result);
+
+              jvalue value;
+              BasicType type = java_lang_boxing_object::get_value(result, &value);
+              switch (type) {
+              case T_DOUBLE: SET_STACK_DOUBLE(value.d, 1); break;
+              case T_LONG:   SET_STACK_LONG(value.j, 1); break;
+              default:  ShouldNotReachHere();
+              }
+
+              break;
+            }
+
           default:  ShouldNotReachHere();
           }
           UPDATE_PC_AND_TOS_AND_CONTINUE(3, 2);
@@ -2404,7 +2449,7 @@
           incr = 3;
         }
 
-        // We are resolved if the f1 field contains a non-null object (CallSite, etc.)
+        // We are resolved if the resolved_references array contains a non-null object (CallSite, etc.)
         // This kind of CP cache entry does not need to match the flags byte, because
         // there is a 1-1 relation between bytecode type and CP entry type.
         ConstantPool* constants = METHOD->constants();
@@ -2414,6 +2459,8 @@
                   handle_exception);
           result = THREAD->vm_result();
         }
+        if (result == Universe::the_null_sentinel())
+          result = NULL;
 
         VERIFY_OOP(result);
         SET_STACK_OBJECT(result, 0);
@@ -2425,7 +2472,7 @@
         u4 index = Bytes::get_native_u4(pc+1);
         ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
 
-        // We are resolved if the resolved_references field contains a non-null object (CallSite, etc.)
+        // We are resolved if the resolved_references array contains a non-null object (CallSite, etc.)
         // This kind of CP cache entry does not need to match the flags byte, because
         // there is a 1-1 relation between bytecode type and CP entry type.
         if (! cache->is_resolved((Bytecodes::Code) opcode)) {
--- a/src/hotspot/share/interpreter/bytecodeTracer.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -367,6 +367,7 @@
   case JVM_CONSTANT_Fieldref:
     break;
   case JVM_CONSTANT_NameAndType:
+  case JVM_CONSTANT_Dynamic:
   case JVM_CONSTANT_InvokeDynamic:
     has_klass = false;
     break;
@@ -382,7 +383,7 @@
     Symbol* klass = constants->klass_name_at(constants->uncached_klass_ref_index_at(i));
     st->print_cr(" %d <%s.%s%s%s> ", i, klass->as_C_string(), name->as_C_string(), sep, signature->as_C_string());
   } else {
-    if (tag.is_invoke_dynamic()) {
+    if (tag.is_dynamic_constant() || tag.is_invoke_dynamic()) {
       int bsm = constants->invoke_dynamic_bootstrap_method_ref_index_at(i);
       st->print(" bsm=%d", bsm);
     }
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -118,22 +118,54 @@
 IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::resolve_ldc(JavaThread* thread, Bytecodes::Code bytecode)) {
-  assert(bytecode == Bytecodes::_fast_aldc ||
+  assert(bytecode == Bytecodes::_ldc ||
+         bytecode == Bytecodes::_ldc_w ||
+         bytecode == Bytecodes::_ldc2_w ||
+         bytecode == Bytecodes::_fast_aldc ||
          bytecode == Bytecodes::_fast_aldc_w, "wrong bc");
   ResourceMark rm(thread);
+  const bool is_fast_aldc = (bytecode == Bytecodes::_fast_aldc ||
+                             bytecode == Bytecodes::_fast_aldc_w);
   LastFrameAccessor last_frame(thread);
   methodHandle m (thread, last_frame.method());
   Bytecode_loadconstant ldc(m, last_frame.bci());
+
+  // Double-check the size.  (Condy can have any type.)
+  BasicType type = ldc.result_type();
+  switch (type2size[type]) {
+  case 2: guarantee(bytecode == Bytecodes::_ldc2_w, ""); break;
+  case 1: guarantee(bytecode != Bytecodes::_ldc2_w, ""); break;
+  default: ShouldNotReachHere();
+  }
+
+  // Resolve the constant.  This does not do unboxing.
+  // But it does replace Universe::the_null_sentinel by null.
   oop result = ldc.resolve_constant(CHECK);
+  assert(result != NULL || is_fast_aldc, "null result only valid for fast_aldc");
+
 #ifdef ASSERT
   {
     // The bytecode wrappers aren't GC-safe so construct a new one
     Bytecode_loadconstant ldc2(m, last_frame.bci());
-    oop coop = m->constants()->resolved_references()->obj_at(ldc2.cache_index());
-    assert(result == coop, "expected result for assembly code");
+    int rindex = ldc2.cache_index();
+    if (rindex < 0)
+      rindex = m->constants()->cp_to_object_index(ldc2.pool_index());
+    if (rindex >= 0) {
+      oop coop = m->constants()->resolved_references()->obj_at(rindex);
+      oop roop = (result == NULL ? Universe::the_null_sentinel() : result);
+      assert(roop == coop, "expected result for assembly code");
+    }
   }
 #endif
   thread->set_vm_result(result);
+  if (!is_fast_aldc) {
+    // Tell the interpreter how to unbox the primitive.
+    guarantee(java_lang_boxing_object::is_instance(result, type), "");
+    int offset = java_lang_boxing_object::value_offset_in_bytes(type);
+    intptr_t flags = ((as_TosState(type) << ConstantPoolCacheEntry::tos_state_shift)
+                      | (offset & ConstantPoolCacheEntry::field_index_mask));
+    thread->set_vm_result_2((Metadata*)flags);
+  }
 }
 IRT_END
 
--- a/src/hotspot/share/interpreter/linkResolver.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/linkResolver.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -41,6 +41,7 @@
 #include "memory/universe.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/method.hpp"
+#include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "prims/methodHandles.hpp"
@@ -54,7 +55,6 @@
 #include "runtime/thread.inline.hpp"
 #include "runtime/vmThread.hpp"
 
-
 //------------------------------------------------------------------------------------------------------------------------
 // Implementation of CallInfo
 
@@ -284,20 +284,32 @@
 //------------------------------------------------------------------------------------------------------------------------
 // Klass resolution
 
-void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS) {
+void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
+                                             bool fold_type_to_class, TRAPS) {
+  Klass* base_klass = sel_klass;
+  if (fold_type_to_class) {
+    if (sel_klass->is_objArray_klass()) {
+      base_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass();
+    }
+    // The element type could be a typeArray - we only need the access
+    // check if it is an reference to another class.
+    if (!base_klass->is_instance_klass()) {
+      return;  // no relevant check to do
+    }
+  }
   Reflection::VerifyClassAccessResults vca_result =
-    Reflection::verify_class_access(ref_klass, InstanceKlass::cast(sel_klass), true);
+    Reflection::verify_class_access(ref_klass, InstanceKlass::cast(base_klass), true);
   if (vca_result != Reflection::ACCESS_OK) {
     ResourceMark rm(THREAD);
     char* msg = Reflection::verify_class_access_msg(ref_klass,
-                                                    InstanceKlass::cast(sel_klass),
+                                                    InstanceKlass::cast(base_klass),
                                                     vca_result);
     if (msg == NULL) {
       Exceptions::fthrow(
         THREAD_AND_LOCATION,
         vmSymbols::java_lang_IllegalAccessError(),
         "failed to access class %s from class %s",
-        sel_klass->external_name(),
+        base_klass->external_name(),
         ref_klass->external_name());
     } else {
       // Use module specific message returned by verify_class_access_msg().
@@ -1663,31 +1675,6 @@
   result.set_handle(resolved_klass, resolved_method, resolved_appendix, resolved_method_type, CHECK);
 }
 
-static void wrap_invokedynamic_exception(TRAPS) {
-  if (HAS_PENDING_EXCEPTION) {
-    // See the "Linking Exceptions" section for the invokedynamic instruction
-    // in JVMS 6.5.
-    if (PENDING_EXCEPTION->is_a(SystemDictionary::Error_klass())) {
-      // Pass through an Error, including BootstrapMethodError, any other form
-      // of linkage error, or say ThreadDeath/OutOfMemoryError
-      if (TraceMethodHandles) {
-        tty->print_cr("invokedynamic passes through an Error for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION));
-        PENDING_EXCEPTION->print();
-      }
-      return;
-    }
-
-    // Otherwise wrap the exception in a BootstrapMethodError
-    if (TraceMethodHandles) {
-      tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION));
-      PENDING_EXCEPTION->print();
-    }
-    Handle nested_exception(THREAD, PENDING_EXCEPTION);
-    CLEAR_PENDING_EXCEPTION;
-    THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
-  }
-}
-
 void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int index, TRAPS) {
   Symbol* method_name       = pool->name_ref_at(index);
   Symbol* method_signature  = pool->signature_ref_at(index);
@@ -1714,7 +1701,7 @@
     // set the indy_rf flag since any subsequent invokedynamic instruction which shares
     // this bootstrap method will encounter the resolution of MethodHandleInError.
     oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD);
-    wrap_invokedynamic_exception(CHECK);
+    Exceptions::wrap_dynamic_exception(CHECK);
     assert(bsm_info != NULL, "");
     // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic.
     bootstrap_specifier = Handle(THREAD, bsm_info);
@@ -1724,7 +1711,7 @@
     Handle       appendix(   THREAD, cpce->appendix_if_resolved(pool));
     Handle       method_type(THREAD, cpce->method_type_if_resolved(pool));
     result.set_handle(method, appendix, method_type, THREAD);
-    wrap_invokedynamic_exception(CHECK);
+    Exceptions::wrap_dynamic_exception(CHECK);
     return;
   }
 
@@ -1737,7 +1724,7 @@
     tty->print("  BSM info: "); bootstrap_specifier->print();
   }
 
-  resolve_dynamic_call(result, bootstrap_specifier, method_name,
+  resolve_dynamic_call(result, pool_index, bootstrap_specifier, method_name,
                        method_signature, current_klass, THREAD);
   if (HAS_PENDING_EXCEPTION && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
     int encoded_index = ResolutionErrorTable::encode_cpcache_index(index);
@@ -1753,7 +1740,7 @@
         Handle       appendix(   THREAD, cpce->appendix_if_resolved(pool));
         Handle       method_type(THREAD, cpce->method_type_if_resolved(pool));
         result.set_handle(method, appendix, method_type, THREAD);
-        wrap_invokedynamic_exception(CHECK);
+        Exceptions::wrap_dynamic_exception(CHECK);
       } else {
         assert(cpce->indy_resolution_failed(), "Resolution failure flag not set");
         ConstantPool::throw_resolution_error(pool, encoded_index, CHECK);
@@ -1765,6 +1752,7 @@
 }
 
 void LinkResolver::resolve_dynamic_call(CallInfo& result,
+                                        int pool_index,
                                         Handle bootstrap_specifier,
                                         Symbol* method_name, Symbol* method_signature,
                                         Klass* current_klass,
@@ -1775,12 +1763,13 @@
   Handle       resolved_method_type;
   methodHandle resolved_method =
     SystemDictionary::find_dynamic_call_site_invoker(current_klass,
+                                                     pool_index,
                                                      bootstrap_specifier,
                                                      method_name, method_signature,
                                                      &resolved_appendix,
                                                      &resolved_method_type,
                                                      THREAD);
-  wrap_invokedynamic_exception(CHECK);
+  Exceptions::wrap_dynamic_exception(CHECK);
   result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD);
-  wrap_invokedynamic_exception(CHECK);
+  Exceptions::wrap_dynamic_exception(CHECK);
 }
--- a/src/hotspot/share/interpreter/linkResolver.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/linkResolver.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -274,7 +274,16 @@
                                       const constantPoolHandle& pool, int index, TRAPS);
  public:
   // constant pool resolving
-  static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS);
+  static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
+                                        bool fold_type_to_class, TRAPS);
+  // The optional 'fold_type_to_class' means that a derived type (array)
+  // is first converted to the class it is derived from (element type).
+  // If this element type is not a class, then the check passes quietly.
+  // This is usually what is needed, but a few existing uses might break
+  // if this flag were always turned on.  FIXME: See if it can be, always.
+  static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS) {
+    return check_klass_accessability(ref_klass, sel_klass, false, THREAD);
+  }
 
   // static resolving calls (will not run any Java code);
   // used only from Bytecode_invoke::static_target
@@ -306,7 +315,7 @@
                                      bool check_null_and_abstract, TRAPS);
   static void resolve_handle_call   (CallInfo& result,
                                      const LinkInfo& link_info, TRAPS);
-  static void resolve_dynamic_call  (CallInfo& result, Handle bootstrap_specifier,
+  static void resolve_dynamic_call  (CallInfo& result, int pool_index, Handle bootstrap_specifier,
                                      Symbol* method_name, Symbol* method_signature,
                                      Klass* current_klass, TRAPS);
 
--- a/src/hotspot/share/interpreter/rewriter.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/rewriter.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -48,7 +48,11 @@
       case JVM_CONSTANT_Methodref         : // fall through
         add_cp_cache_entry(i);
         break;
-      case JVM_CONSTANT_String:
+      case JVM_CONSTANT_Dynamic:
+        assert(_pool->has_dynamic_constant(), "constant pool's _has_dynamic_constant flag not set");
+        add_resolved_references_entry(i);
+        break;
+      case JVM_CONSTANT_String            : // fall through
       case JVM_CONSTANT_MethodHandle      : // fall through
       case JVM_CONSTANT_MethodType        : // fall through
         add_resolved_references_entry(i);
@@ -321,7 +325,14 @@
     address p = bcp + offset;
     int cp_index = is_wide ? Bytes::get_Java_u2(p) : (u1)(*p);
     constantTag tag = _pool->tag_at(cp_index).value();
-    if (tag.is_method_handle() || tag.is_method_type() || tag.is_string()) {
+
+    if (tag.is_method_handle() ||
+        tag.is_method_type() ||
+        tag.is_string() ||
+        (tag.is_dynamic_constant() &&
+         // keep regular ldc interpreter logic for condy primitives
+         is_reference_type(FieldType::basic_type(_pool->uncached_signature_ref_at(cp_index))))
+        ) {
       int ref_index = cp_entry_to_resolved_references(cp_index);
       if (is_wide) {
         (*bcp) = Bytecodes::_fast_aldc_w;
--- a/src/hotspot/share/interpreter/templateTable.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/templateTable.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -278,7 +278,7 @@
   def(Bytecodes::_sipush              , ubcp|____|____|____, vtos, itos, sipush              ,  _           );
   def(Bytecodes::_ldc                 , ubcp|____|clvm|____, vtos, vtos, ldc                 ,  false       );
   def(Bytecodes::_ldc_w               , ubcp|____|clvm|____, vtos, vtos, ldc                 ,  true        );
-  def(Bytecodes::_ldc2_w              , ubcp|____|____|____, vtos, vtos, ldc2_w              ,  _           );
+  def(Bytecodes::_ldc2_w              , ubcp|____|clvm|____, vtos, vtos, ldc2_w              ,  _           );
   def(Bytecodes::_iload               , ubcp|____|clvm|____, vtos, itos, iload               ,  _           );
   def(Bytecodes::_lload               , ubcp|____|____|____, vtos, ltos, lload               ,  _           );
   def(Bytecodes::_fload               , ubcp|____|____|____, vtos, ftos, fload               ,  _           );
--- a/src/hotspot/share/interpreter/templateTable.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/templateTable.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -295,6 +295,7 @@
   static void getstatic(int byte_no);
   static void putstatic(int byte_no);
   static void pop_and_check_object(Register obj);
+  static void condy_helper(Label& Done);  // shared by ldc instances
 
   static void _new();
   static void newarray();
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -766,11 +766,10 @@
 
 C2V_VMENTRY(jboolean, isCompilable,(JNIEnv *, jobject, jobject jvmci_method))
   methodHandle method = CompilerToVM::asMethod(jvmci_method);
-  // Skip redefined methods
-  if (method->is_old()) {
-    return false;
-  }
-  return !method->is_not_compilable(CompLevel_full_optimization);
+  constantPoolHandle cp = method->constMethod()->constants();
+  assert(!cp.is_null(), "npe");
+  // don't inline method when constant pool contains a CONSTANT_Dynamic
+  return !method->is_not_compilable(CompLevel_full_optimization) && !cp->has_dynamic_constant();
 C2V_END
 
 C2V_VMENTRY(jboolean, hasNeverInlineDirective,(JNIEnv *, jobject, jobject jvmci_method))
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -119,6 +119,7 @@
   nonstatic_field(ConstantPool,                _tags,                                  Array<u1>*)                                   \
   nonstatic_field(ConstantPool,                _pool_holder,                           InstanceKlass*)                               \
   nonstatic_field(ConstantPool,                _length,                                int)                                          \
+  nonstatic_field(ConstantPool,                _flags,                                 int)                                          \
                                                                                                                                      \
   nonstatic_field(ConstMethod,                 _constants,                             ConstantPool*)                                \
   nonstatic_field(ConstMethod,                 _flags,                                 u2)                                           \
@@ -415,6 +416,7 @@
   declare_constant(JVM_CONSTANT_UnresolvedClassInError)                   \
   declare_constant(JVM_CONSTANT_MethodHandleInError)                      \
   declare_constant(JVM_CONSTANT_MethodTypeInError)                        \
+  declare_constant(JVM_CONSTANT_DynamicInError)                           \
   declare_constant(JVM_CONSTANT_InternalMax)                              \
                                                                           \
   declare_constant(ArrayData::array_len_off_set)                          \
@@ -452,6 +454,7 @@
   declare_constant(CodeInstaller::INVOKE_INVALID)                         \
                                                                           \
   declare_constant(ConstantPool::CPCACHE_INDEX_TAG)                       \
+  declare_constant(ConstantPool::_has_dynamic_constant)                   \
                                                                           \
   declare_constant(ConstMethod::_has_linenumber_table)                    \
   declare_constant(ConstMethod::_has_localvariable_table)                 \
--- a/src/hotspot/share/memory/universe.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/memory/universe.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -111,6 +111,7 @@
 oop Universe::_system_thread_group                    = NULL;
 objArrayOop Universe::_the_empty_class_klass_array    = NULL;
 Array<Klass*>* Universe::_the_array_interfaces_array = NULL;
+oop Universe::_the_null_sentinel                      = NULL;
 oop Universe::_the_null_string                        = NULL;
 oop Universe::_the_min_jint_string                   = NULL;
 LatestMethodCache* Universe::_finalizer_register_cache = NULL;
@@ -195,6 +196,7 @@
   assert(_mirrors[0] == NULL && _mirrors[T_BOOLEAN - 1] == NULL, "checking");
 
   f->do_oop((oop*)&_the_empty_class_klass_array);
+  f->do_oop((oop*)&_the_null_sentinel);
   f->do_oop((oop*)&_the_null_string);
   f->do_oop((oop*)&_the_min_jint_string);
   f->do_oop((oop*)&_out_of_memory_error_java_heap);
@@ -381,6 +383,11 @@
     initialize_basic_type_klass(longArrayKlassObj(), CHECK);
   } // end of core bootstrapping
 
+  {
+    Handle tns = java_lang_String::create_from_str("<null_sentinel>", CHECK);
+    _the_null_sentinel = tns();
+  }
+
   // Maybe this could be lifted up now that object array can be initialized
   // during the bootstrapping.
 
--- a/src/hotspot/share/memory/universe.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/memory/universe.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -141,6 +141,7 @@
   static oop          _system_thread_group;           // Reference to the system thread group object
 
   static objArrayOop  _the_empty_class_klass_array;   // Canonicalized obj array of type java.lang.Class
+  static oop          _the_null_sentinel;             // A unique object pointer unused except as a sentinel for null.
   static oop          _the_null_string;               // A cache of "null" as a Java string
   static oop          _the_min_jint_string;          // A cache of "-2147483648" as a Java string
   static LatestMethodCache* _finalizer_register_cache; // static method for registering finalizable objects
@@ -322,6 +323,9 @@
 
   static Method*      do_stack_walk_method()          { return _do_stack_walk_cache->get_method(); }
 
+  static oop          the_null_sentinel()             { return _the_null_sentinel;             }
+  static address      the_null_sentinel_addr()        { return (address) &_the_null_sentinel;  }
+
   // Function to initialize these
   static void initialize_known_methods(TRAPS);
 
--- a/src/hotspot/share/oops/constantPool.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -607,7 +607,6 @@
   return symbol_at(signature_index);
 }
 
-
 int ConstantPool::impl_name_and_type_ref_index_at(int which, bool uncached) {
   int i = which;
   if (!uncached && cache() != NULL) {
@@ -621,14 +620,18 @@
     // change byte-ordering and go via cache
     i = remap_instruction_operand_from_cache(which);
   } else {
-    if (tag_at(which).is_invoke_dynamic()) {
+    if (tag_at(which).is_invoke_dynamic() ||
+        tag_at(which).is_dynamic_constant() ||
+        tag_at(which).is_dynamic_constant_in_error()) {
       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");
+  assert(!tag_at(i).is_invoke_dynamic() &&
+         !tag_at(i).is_dynamic_constant() &&
+         !tag_at(i).is_dynamic_constant_in_error(), "Must be handled above");
   jint ref_index = *int_at_addr(i);
   return extract_high_short_from_int(ref_index);
 }
@@ -672,16 +675,12 @@
 
 
 void ConstantPool::verify_constant_pool_resolve(const constantPoolHandle& this_cp, Klass* k, TRAPS) {
- if (k->is_instance_klass() || k->is_objArray_klass()) {
-    InstanceKlass* holder = this_cp->pool_holder();
-    Klass* elem = k->is_instance_klass() ? k : ObjArrayKlass::cast(k)->bottom_klass();
-
-    // The element type could be a typeArray - we only need the access check if it is
-    // an reference to another class
-    if (elem->is_instance_klass()) {
-      LinkResolver::check_klass_accessability(holder, elem, CHECK);
-    }
+  if (!(k->is_instance_klass() || k->is_objArray_klass())) {
+    return;  // short cut, typeArray klass is always accessible
   }
+  Klass* holder = this_cp->pool_holder();
+  bool fold_type_to_class = true;
+  LinkResolver::check_klass_accessability(holder, k, fold_type_to_class, CHECK);
 }
 
 
@@ -769,8 +768,8 @@
   THROW_MSG(error, message->as_C_string());
 }
 
-// If resolution for Class, MethodHandle or MethodType fails, save the exception
-// in the resolution error table, so that the same exception is thrown again.
+// If resolution for Class, Dynamic constant, MethodHandle or MethodType fails, save the
+// exception in the resolution error table, so that the same exception is thrown again.
 void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, int which,
                                             constantTag tag, TRAPS) {
   Symbol* error = PENDING_EXCEPTION->klass()->name();
@@ -806,16 +805,31 @@
   }
 }
 
+BasicType ConstantPool::basic_type_for_constant_at(int which) {
+  constantTag tag = tag_at(which);
+  if (tag.is_dynamic_constant() ||
+      tag.is_dynamic_constant_in_error()) {
+    // have to look at the signature for this one
+    Symbol* constant_type = uncached_signature_ref_at(which);
+    return FieldType::basic_type(constant_type);
+  }
+  return tag.basic_type();
+}
+
 // Called to resolve constants in the constant pool and return an oop.
 // Some constant pool entries cache their resolved oop. This is also
 // called to create oops from constants to use in arguments for invokedynamic
-oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index, TRAPS) {
+oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
+                                           int index, int cache_index,
+                                           bool* status_return, 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 objects.
     // We'll do a linear search.  This should be OK because this usage is rare.
+    // FIXME: If bootstrap specifiers stress this code, consider putting in
+    // a reverse index.  Binary search over a short array should do it.
     assert(index > 0, "valid index");
     cache_index = this_cp->cp_to_object_index(index);
   }
@@ -825,6 +839,12 @@
   if (cache_index >= 0) {
     result_oop = this_cp->resolved_references()->obj_at(cache_index);
     if (result_oop != NULL) {
+      if (result_oop == Universe::the_null_sentinel()) {
+        DEBUG_ONLY(int temp_index = (index >= 0 ? index : this_cp->object_to_cp_index(cache_index)));
+        assert(this_cp->tag_at(temp_index).is_dynamic_constant(), "only condy uses the null sentinel");
+        result_oop = NULL;
+      }
+      if (status_return != NULL)  (*status_return) = true;
       return result_oop;
       // That was easy...
     }
@@ -835,6 +855,35 @@
 
   constantTag tag = this_cp->tag_at(index);
 
+  if (status_return != NULL) {
+    // don't trigger resolution if the constant might need it
+    switch (tag.value()) {
+    case JVM_CONSTANT_Class:
+    {
+      CPKlassSlot kslot = this_cp->klass_slot_at(index);
+      int resolved_klass_index = kslot.resolved_klass_index();
+      if (this_cp->resolved_klasses()->at(resolved_klass_index) == NULL) {
+        (*status_return) = false;
+        return NULL;
+      }
+      // the klass is waiting in the CP; go get it
+      break;
+    }
+    case JVM_CONSTANT_String:
+    case JVM_CONSTANT_Integer:
+    case JVM_CONSTANT_Float:
+    case JVM_CONSTANT_Long:
+    case JVM_CONSTANT_Double:
+      // these guys trigger OOM at worst
+      break;
+    default:
+      (*status_return) = false;
+      return NULL;
+    }
+    // from now on there is either success or an OOME
+    (*status_return) = true;
+  }
+
   switch (tag.value()) {
 
   case JVM_CONSTANT_UnresolvedClass:
@@ -848,6 +897,63 @@
       break;
     }
 
+  case JVM_CONSTANT_Dynamic:
+    {
+      Klass* current_klass  = this_cp->pool_holder();
+      Symbol* constant_name = this_cp->uncached_name_ref_at(index);
+      Symbol* constant_type = this_cp->uncached_signature_ref_at(index);
+
+      // The initial step in resolving an unresolved symbolic reference to a
+      // dynamically-computed constant is to resolve the symbolic reference to a
+      // method handle which will be the bootstrap method for the dynamically-computed
+      // constant. If resolution of the java.lang.invoke.MethodHandle for the bootstrap
+      // method fails, then a MethodHandleInError is stored at the corresponding
+      // bootstrap method's CP index for the CONSTANT_MethodHandle_info. No need to
+      // set a DynamicConstantInError here since any subsequent use of this
+      // bootstrap method will encounter the resolution of MethodHandleInError.
+      oop bsm_info = this_cp->resolve_bootstrap_specifier_at(index, THREAD);
+      Exceptions::wrap_dynamic_exception(CHECK_NULL);
+      assert(bsm_info != NULL, "");
+      // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_Dynamic.
+      Handle bootstrap_specifier = Handle(THREAD, bsm_info);
+
+      // Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
+      Handle value = SystemDictionary::link_dynamic_constant(current_klass,
+                                                             index,
+                                                             bootstrap_specifier,
+                                                             constant_name,
+                                                             constant_type,
+                                                             THREAD);
+      result_oop = value();
+      Exceptions::wrap_dynamic_exception(THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        // Resolution failure of the dynamically-computed constant, save_and_throw_exception
+        // will check for a LinkageError and store a DynamicConstantInError.
+        save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
+      }
+      BasicType type = FieldType::basic_type(constant_type);
+      if (!is_reference_type(type)) {
+        // Make sure the primitive value is properly boxed.
+        // This is a JDK responsibility.
+        const char* fail = NULL;
+        if (result_oop == NULL) {
+          fail = "null result instead of box";
+        } else if (!is_java_primitive(type)) {
+          // FIXME: support value types via unboxing
+          fail = "can only handle references and primitives";
+        } else if (!java_lang_boxing_object::is_instance(result_oop, type)) {
+          fail = "primitive is not properly boxed";
+        }
+        if (fail != NULL) {
+          // Since this exception is not a LinkageError, throw exception
+          // but do not save a DynamicInError resolution result.
+          // See section 5.4.3 of the VM spec.
+          THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), fail);
+        }
+      }
+      break;
+    }
+
   case JVM_CONSTANT_String:
     assert(cache_index != _no_index_sentinel, "should have been set");
     if (this_cp->is_pseudo_string_at(index)) {
@@ -857,6 +963,7 @@
     result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
     break;
 
+  case JVM_CONSTANT_DynamicInError:
   case JVM_CONSTANT_MethodHandleInError:
   case JVM_CONSTANT_MethodTypeInError:
     {
@@ -957,15 +1064,20 @@
     // 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.
-    oop old_result = this_cp->resolved_references()->atomic_compare_exchange_oop(cache_index, result_oop, NULL);
+    oop new_result = (result_oop == NULL ? Universe::the_null_sentinel() : result_oop);
+    oop old_result = this_cp->resolved_references()
+      ->atomic_compare_exchange_oop(cache_index, new_result, NULL);
     if (old_result == NULL) {
       return result_oop;  // was installed
     } else {
       // Return the winning thread's result.  This can be different than
       // the result here for MethodHandles.
+      if (old_result == Universe::the_null_sentinel())
+        old_result = NULL;
       return old_result;
     }
   } else {
+    assert(result_oop != Universe::the_null_sentinel(), "");
     return result_oop;
   }
 }
@@ -979,13 +1091,14 @@
 
 
 oop ConstantPool::resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS) {
-  assert(this_cp->tag_at(index).is_invoke_dynamic(), "Corrupted constant pool");
-
+  assert((this_cp->tag_at(index).is_invoke_dynamic() ||
+          this_cp->tag_at(index).is_dynamic_constant()), "Corrupted constant pool");
   Handle bsm;
   int argc;
   {
-    // 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.
+    // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&mtype], plus optional arguments
+    // JVM_CONSTANT_Dynamic is an ordered pair of [bootm, name&ftype], plus optional arguments
+    // In both cases, the bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
     // It is accompanied by the optional arguments.
     int bsm_index = this_cp->invoke_dynamic_bootstrap_method_ref_index_at(index);
     oop bsm_oop = this_cp->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL);
@@ -995,30 +1108,142 @@
 
     // Extract the optional static arguments.
     argc = this_cp->invoke_dynamic_argument_count_at(index);
-    if (argc == 0)  return bsm_oop;
+
+    // if there are no static arguments, return the bsm by itself:
+    if (argc == 0 && UseBootstrapCallInfo < 2)  return bsm_oop;
 
     bsm = Handle(THREAD, bsm_oop);
   }
 
+  // We are going to return an ordered pair of {bsm, info}, using a 2-array.
   objArrayHandle info;
   {
-    objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1+argc, CHECK_NULL);
+    objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 2, CHECK_NULL);
     info = objArrayHandle(THREAD, info_oop);
   }
 
   info->obj_at_put(0, bsm());
-  for (int i = 0; i < argc; i++) {
-    int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
-    oop arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK_NULL);
-    info->obj_at_put(1+i, arg_oop);
+
+  bool use_BSCI;
+  switch (UseBootstrapCallInfo) {
+  default: use_BSCI = true;  break;  // stress mode
+  case 0:  use_BSCI = false; break;  // stress mode
+  case 1:                            // normal mode
+    // If we were to support an alternative mode of BSM invocation,
+    // we'd convert to pull mode here if the BSM could be a candidate
+    // for that alternative mode.  We can't easily test for things
+    // like varargs here, but we can get away with approximate testing,
+    // since the JDK runtime will make up the difference either way.
+    // For now, exercise the pull-mode path if the BSM is of arity 2,
+    // or if there is a potential condy loop (see below).
+    oop mt_oop = java_lang_invoke_MethodHandle::type(bsm());
+    use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2);
+    break;
+  }
+
+  // Here's a reason to use BSCI even if it wasn't requested:
+  // If a condy uses a condy argument, we want to avoid infinite
+  // recursion (condy loops) in the C code.  It's OK in Java,
+  // because Java has stack overflow checking, so we punt
+  // potentially cyclic cases from C to Java.
+  if (!use_BSCI && this_cp->tag_at(index).is_dynamic_constant()) {
+    bool found_unresolved_condy = false;
+    for (int i = 0; i < argc; i++) {
+      int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+      if (this_cp->tag_at(arg_index).is_dynamic_constant()) {
+        // potential recursion point condy -> condy
+        bool found_it = false;
+        this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
+        if (!found_it) { found_unresolved_condy = true; break; }
+      }
+    }
+    if (found_unresolved_condy)
+      use_BSCI = true;
+  }
+
+  const int SMALL_ARITY = 5;
+  if (use_BSCI && argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) {
+    // If there are only a few arguments, and none of them need linking,
+    // push them, instead of asking the JDK runtime to turn around and
+    // pull them, saving a JVM/JDK transition in some simple cases.
+    bool all_resolved = true;
+    for (int i = 0; i < argc; i++) {
+      bool found_it = false;
+      int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+      this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
+      if (!found_it) { all_resolved = false; break; }
+    }
+    if (all_resolved)
+      use_BSCI = false;
   }
 
+  if (!use_BSCI) {
+    // return {bsm, {arg...}}; resolution of arguments is done immediately, before JDK code is called
+    objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_NULL);
+    info->obj_at_put(1, args_oop);   // may overwrite with args[0] below
+    objArrayHandle args(THREAD, args_oop);
+    copy_bootstrap_arguments_at_impl(this_cp, index, 0, argc, args, 0, true, Handle(), CHECK_NULL);
+    if (argc == 1) {
+      // try to discard the singleton array
+      oop arg_oop = args->obj_at(0);
+      if (arg_oop != NULL && !arg_oop->is_array()) {
+        // JVM treats arrays and nulls specially in this position,
+        // but other things are just single arguments
+        info->obj_at_put(1, arg_oop);
+      }
+    }
+  } else {
+    // return {bsm, {arg_count, pool_index}}; JDK code must pull the arguments as needed
+    typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK_NULL);
+    ints_oop->int_at_put(0, argc);
+    ints_oop->int_at_put(1, index);
+    info->obj_at_put(1, ints_oop);
+  }
   return info();
 }
 
+void ConstantPool::copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
+                                                    int start_arg, int end_arg,
+                                                    objArrayHandle info, int pos,
+                                                    bool must_resolve, Handle if_not_available,
+                                                    TRAPS) {
+  int argc;
+  int limit = pos + end_arg - start_arg;
+  // checks: index in range [0..this_cp->length),
+  // tag at index, start..end in range [0..argc],
+  // info array non-null, pos..limit in [0..info.length]
+  if ((0 >= index    || index >= this_cp->length())  ||
+      !(this_cp->tag_at(index).is_invoke_dynamic()    ||
+        this_cp->tag_at(index).is_dynamic_constant()) ||
+      (0 > start_arg || start_arg > end_arg) ||
+      (end_arg > (argc = this_cp->invoke_dynamic_argument_count_at(index))) ||
+      (0 > pos       || pos > limit)         ||
+      (info.is_null() || limit > info->length())) {
+    // An index or something else went wrong; throw an error.
+    // Since this is an internal API, we don't expect this,
+    // so we don't bother to craft a nice message.
+    THROW_MSG(vmSymbols::java_lang_LinkageError(), "bad BSM argument access");
+  }
+  // now we can loop safely
+  int info_i = pos;
+  for (int i = start_arg; i < end_arg; i++) {
+    int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+    oop arg_oop;
+    if (must_resolve) {
+      arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK);
+    } else {
+      bool found_it = false;
+      arg_oop = this_cp->find_cached_constant_at(arg_index, found_it, CHECK);
+      if (!found_it)  arg_oop = if_not_available();
+    }
+    info->obj_at_put(info_i++, arg_oop);
+  }
+}
+
 oop ConstantPool::string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS) {
   // If the string has already been interned, this entry will be non-null
   oop str = this_cp->resolved_references()->obj_at(obj_index);
+  assert(str != Universe::the_null_sentinel(), "");
   if (str != NULL) return str;
   Symbol* sym = this_cp->unresolved_string_at(which);
   str = StringTable::intern(sym, CHECK_(NULL));
@@ -1199,6 +1424,18 @@
     }
   } break;
 
+  case JVM_CONSTANT_Dynamic:
+  {
+    int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
+    int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2);
+    int i1 = invoke_dynamic_bootstrap_specifier_index(index1);
+    int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2);
+    // separate statements and variables because CHECK_false is used
+    bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false);
+    bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false);
+    return (match_entry && match_operand);
+  } break;
+
   case JVM_CONSTANT_InvokeDynamic:
   {
     int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
@@ -1525,6 +1762,15 @@
     to_cp->method_handle_index_at_put(to_i, k1, k2);
   } break;
 
+  case JVM_CONSTANT_Dynamic:
+  case JVM_CONSTANT_DynamicInError:
+  {
+    int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
+    int k2 = from_cp->invoke_dynamic_name_and_type_ref_index_at(from_i);
+    k1 += operand_array_length(to_cp->operands());  // to_cp might already have operands
+    to_cp->dynamic_constant_at_put(to_i, k1, k2);
+  } break;
+
   case JVM_CONSTANT_InvokeDynamic:
   {
     int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
@@ -1786,6 +2032,8 @@
     case JVM_CONSTANT_NameAndType:
       return 5;
 
+    case JVM_CONSTANT_Dynamic:
+    case JVM_CONSTANT_DynamicInError:
     case JVM_CONSTANT_InvokeDynamic:
       // u1 tag, u2 bsm, u2 nt
       return 5;
@@ -1971,6 +2219,17 @@
         DBG(printf("JVM_CONSTANT_MethodType: %hd", idx1));
         break;
       }
+      case JVM_CONSTANT_Dynamic:
+      case JVM_CONSTANT_DynamicInError: {
+        *bytes = tag;
+        idx1 = extract_low_short_from_int(*int_at_addr(idx));
+        idx2 = extract_high_short_from_int(*int_at_addr(idx));
+        assert(idx2 == invoke_dynamic_name_and_type_ref_index_at(idx), "correct half of u4");
+        Bytes::put_Java_u2((address) (bytes+1), idx1);
+        Bytes::put_Java_u2((address) (bytes+3), idx2);
+        DBG(printf("JVM_CONSTANT_Dynamic: %hd %hd", idx1, idx2));
+        break;
+      }
       case JVM_CONSTANT_InvokeDynamic: {
         *bytes = tag;
         idx1 = extract_low_short_from_int(*int_at_addr(idx));
@@ -2176,6 +2435,21 @@
     case JVM_CONSTANT_MethodTypeInError :
       st->print("signature_index=%d", method_type_index_at(index));
       break;
+    case JVM_CONSTANT_Dynamic :
+    case JVM_CONSTANT_DynamicInError :
+      {
+        st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));
+        st->print(" type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index));
+        int argc = invoke_dynamic_argument_count_at(index);
+        if (argc > 0) {
+          for (int arg_i = 0; arg_i < argc; arg_i++) {
+            int arg = invoke_dynamic_argument_index_at(index, arg_i);
+            st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg);
+          }
+          st->print("}");
+        }
+      }
+      break;
     case JVM_CONSTANT_InvokeDynamic :
       {
         st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));
--- a/src/hotspot/share/oops/constantPool.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -113,9 +113,10 @@
   Array<Klass*>*       _resolved_klasses;
 
   enum {
-    _has_preresolution = 1,           // Flags
-    _on_stack          = 2,
-    _is_shared         = 4
+    _has_preresolution    = 1,       // Flags
+    _on_stack             = 2,
+    _is_shared            = 4,
+    _has_dynamic_constant = 8
   };
 
   int                  _flags;  // old fashioned bit twiddling
@@ -207,6 +208,9 @@
   // Faster than MetaspaceObj::is_shared() - used by set_on_stack()
   bool is_shared() const                     { return (_flags & _is_shared) != 0; }
 
+  bool has_dynamic_constant() const       { return (_flags & _has_dynamic_constant) != 0; }
+  void set_has_dynamic_constant()         { _flags |= _has_dynamic_constant; }
+
   // Klass holding pool
   InstanceKlass* pool_holder() const      { return _pool_holder; }
   void set_pool_holder(InstanceKlass* k)  { _pool_holder = k; }
@@ -297,6 +301,11 @@
     *int_at_addr(which) = ref_index;
   }
 
+  void dynamic_constant_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) {
+    tag_at_put(which, JVM_CONSTANT_Dynamic);
+    *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index;
+  }
+
   void invoke_dynamic_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) {
     tag_at_put(which, JVM_CONSTANT_InvokeDynamic);
     *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index;
@@ -554,11 +563,15 @@
   }
 
   int invoke_dynamic_name_and_type_ref_index_at(int which) {
-    assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
+    assert(tag_at(which).is_invoke_dynamic() ||
+           tag_at(which).is_dynamic_constant() ||
+           tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
     return extract_high_short_from_int(*int_at_addr(which));
   }
   int invoke_dynamic_bootstrap_specifier_index(int which) {
-    assert(tag_at(which).value() == JVM_CONSTANT_InvokeDynamic, "Corrupted constant pool");
+    assert(tag_at(which).is_invoke_dynamic() ||
+           tag_at(which).is_dynamic_constant() ||
+           tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
     return extract_low_short_from_int(*int_at_addr(which));
   }
   int invoke_dynamic_operand_base(int which) {
@@ -608,7 +621,7 @@
   }
 #endif //ASSERT
 
-  // layout of InvokeDynamic bootstrap method specifier (in second part of operands array):
+  // layout of InvokeDynamic and Dynamic bootstrap method specifier (in second part of operands array):
   enum {
          _indy_bsm_offset  = 0,  // CONSTANT_MethodHandle bsm
          _indy_argc_offset = 1,  // u2 argc
@@ -654,14 +667,17 @@
   // Shrink the operands array to a smaller array with new_len length
   void shrink_operands(int new_len, TRAPS);
 
-
   int invoke_dynamic_bootstrap_method_ref_index_at(int which) {
-    assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
+    assert(tag_at(which).is_invoke_dynamic() ||
+           tag_at(which).is_dynamic_constant() ||
+           tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
     int op_base = invoke_dynamic_operand_base(which);
     return operands()->at(op_base + _indy_bsm_offset);
   }
   int invoke_dynamic_argument_count_at(int which) {
-    assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
+    assert(tag_at(which).is_invoke_dynamic() ||
+           tag_at(which).is_dynamic_constant() ||
+           tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
     int op_base = invoke_dynamic_operand_base(which);
     int argc = operands()->at(op_base + _indy_argc_offset);
     DEBUG_ONLY(int end_offset = op_base + _indy_argv_offset + argc;
@@ -731,20 +747,27 @@
   enum { _no_index_sentinel = -1, _possible_index_sentinel = -2 };
  public:
 
+  BasicType basic_type_for_constant_at(int which);
+
   // Resolve late bound constants.
   oop resolve_constant_at(int index, TRAPS) {
     constantPoolHandle h_this(THREAD, this);
-    return resolve_constant_at_impl(h_this, index, _no_index_sentinel, THREAD);
+    return resolve_constant_at_impl(h_this, index, _no_index_sentinel, NULL, THREAD);
   }
 
   oop resolve_cached_constant_at(int cache_index, TRAPS) {
     constantPoolHandle h_this(THREAD, this);
-    return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, THREAD);
+    return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, NULL, 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);
+    return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, NULL, THREAD);
+  }
+
+  oop find_cached_constant_at(int pool_index, bool& found_it, TRAPS) {
+    constantPoolHandle h_this(THREAD, this);
+    return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, &found_it, THREAD);
   }
 
   oop resolve_bootstrap_specifier_at(int index, TRAPS) {
@@ -752,6 +775,15 @@
     return resolve_bootstrap_specifier_at_impl(h_this, index, THREAD);
   }
 
+  void copy_bootstrap_arguments_at(int index,
+                                   int start_arg, int end_arg,
+                                   objArrayHandle info, int pos,
+                                   bool must_resolve, Handle if_not_available, TRAPS) {
+    constantPoolHandle h_this(THREAD, this);
+    copy_bootstrap_arguments_at_impl(h_this, index, start_arg, end_arg,
+                                     info, pos, must_resolve, if_not_available, THREAD);
+  }
+
   // Klass name matches name at offset
   bool klass_name_at_matches(const InstanceKlass* k, int which);
 
@@ -833,6 +865,7 @@
 
   Symbol* impl_name_ref_at(int which, bool uncached);
   Symbol* impl_signature_ref_at(int which, bool uncached);
+
   int       impl_klass_ref_index_at(int which, bool uncached);
   int       impl_name_and_type_ref_index_at(int which, bool uncached);
   constantTag impl_tag_ref_at(int which, bool uncached);
@@ -862,8 +895,13 @@
   // Resolve string constants (to prevent allocation during compilation)
   static void resolve_string_constants_impl(const constantPoolHandle& this_cp, TRAPS);
 
-  static oop resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index, TRAPS);
+  static oop resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index,
+                                      bool* status_return, TRAPS);
   static oop resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS);
+  static void copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
+                                               int start_arg, int end_arg,
+                                               objArrayHandle info, int pos,
+                                               bool must_resolve, Handle if_not_available, TRAPS);
 
   // Exception handling
   static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception);
--- a/src/hotspot/share/oops/generateOopMap.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/generateOopMap.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -1878,13 +1878,15 @@
   ConstantPool* cp  = method()->constants();
   constantTag tag = cp->tag_at(ldc.pool_index()); // idx is index in resolved_references
   BasicType       bt  = ldc.result_type();
+#ifdef ASSERT
+  BasicType   tag_bt = tag.is_dynamic_constant() ? bt : tag.basic_type();
+  assert(bt == tag_bt, "same result");
+#endif
   CellTypeState   cts;
-  if (tag.basic_type() == T_OBJECT) {
+  if (is_reference_type(bt)) {  // could be T_ARRAY with condy
     assert(!tag.is_string_index() && !tag.is_klass_index(), "Unexpected index tag");
-    assert(bt == T_OBJECT, "Guard is incorrect");
     cts = CellTypeState::make_line_ref(bci);
   } else {
-    assert(bt != T_OBJECT, "Guard is incorrect");
     cts = valCTS;
   }
   ppush1(cts);
--- a/src/hotspot/share/opto/parse2.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/opto/parse2.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -1483,8 +1483,9 @@
     // If the constant is unresolved, run this BC once in the interpreter.
     {
       ciConstant constant = iter().get_constant();
-      if (constant.basic_type() == T_OBJECT &&
-          !constant.as_object()->is_loaded()) {
+      if (!constant.is_valid() ||
+          (constant.basic_type() == T_OBJECT &&
+           !constant.as_object()->is_loaded())) {
         int index = iter().get_constant_pool_index();
         constantTag tag = iter().get_constant_pool_tag(index);
         uncommon_trap(Deoptimization::make_trap_request
--- a/src/hotspot/share/prims/jvm.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/jvm.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -2212,6 +2212,8 @@
       result = JVM_CONSTANT_MethodType;
   } else if (tag.is_method_handle_in_error()) {
       result = JVM_CONSTANT_MethodHandle;
+  } else if (tag.is_dynamic_constant_in_error()) {
+      result = JVM_CONSTANT_Dynamic;
   }
   return result;
 }
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -497,6 +497,7 @@
     } break;
 
     // this is an indirect CP entry so it needs special handling
+    case JVM_CONSTANT_Dynamic:  // fall through
     case JVM_CONSTANT_InvokeDynamic:
     {
       // Index of the bootstrap specifier in the operands array
@@ -509,15 +510,18 @@
                                                     merge_cp_length_p, THREAD);
       if (new_bs_i != old_bs_i) {
         log_trace(redefine, class, constantpool)
-          ("InvokeDynamic entry@%d bootstrap_method_attr_index change: %d to %d",
+          ("Dynamic entry@%d bootstrap_method_attr_index change: %d to %d",
            *merge_cp_length_p, old_bs_i, new_bs_i);
       }
       if (new_ref_i != old_ref_i) {
         log_trace(redefine, class, constantpool)
-          ("InvokeDynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i);
+          ("Dynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i);
       }
 
-      (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
+      if (scratch_cp->tag_at(scratch_i).is_dynamic_constant())
+        (*merge_cp_p)->dynamic_constant_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
+      else
+        (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
       if (scratch_i != *merge_cp_length_p) {
         // The new entry in *merge_cp_p is at a different index than
         // the new entry in scratch_cp so we need to map the index values.
--- a/src/hotspot/share/prims/methodHandles.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/methodHandles.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -1359,6 +1359,87 @@
 }
 JVM_END
 
+JVM_ENTRY(void, MHN_copyOutBootstrapArguments(JNIEnv* env, jobject igcls,
+                                              jobject caller_jh, jintArray index_info_jh,
+                                              jint start, jint end,
+                                              jobjectArray buf_jh, jint pos,
+                                              jboolean resolve, jobject ifna_jh)) {
+  Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller_jh));
+  if (caller_k == NULL || !caller_k->is_instance_klass()) {
+      THROW_MSG(vmSymbols::java_lang_InternalError(), "bad caller");
+  }
+  InstanceKlass* caller = InstanceKlass::cast(caller_k);
+  typeArrayOop index_info_oop = (typeArrayOop) JNIHandles::resolve(index_info_jh);
+  if (index_info_oop == NULL ||
+      index_info_oop->klass() != Universe::intArrayKlassObj() ||
+      typeArrayOop(index_info_oop)->length() < 2) {
+      THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (0)");
+  }
+  typeArrayHandle index_info(THREAD, index_info_oop);
+  int bss_index_in_pool = index_info->int_at(1);
+  // While we are here, take a quick look at the index info:
+  if (bss_index_in_pool <= 0 ||
+      bss_index_in_pool >= caller->constants()->length() ||
+      index_info->int_at(0)
+      != caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool)) {
+      THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (1)");
+  }
+  objArrayHandle buf(THREAD, (objArrayOop) JNIHandles::resolve(buf_jh));
+  if (start < 0) {
+    for (int pseudo_index = -4; pseudo_index < 0; pseudo_index++) {
+      if (start == pseudo_index) {
+        if (start >= end || 0 > pos || pos >= buf->length())  break;
+        oop pseudo_arg = NULL;
+        switch (pseudo_index) {
+        case -4:  // bootstrap method
+          {
+            int bsm_index = caller->constants()->invoke_dynamic_bootstrap_method_ref_index_at(bss_index_in_pool);
+            pseudo_arg = caller->constants()->resolve_possibly_cached_constant_at(bsm_index, CHECK);
+            break;
+          }
+        case -3:  // name
+          {
+            Symbol* name = caller->constants()->name_ref_at(bss_index_in_pool);
+            Handle str = java_lang_String::create_from_symbol(name, CHECK);
+            pseudo_arg = str();
+            break;
+          }
+        case -2:  // type
+          {
+            Symbol* type = caller->constants()->signature_ref_at(bss_index_in_pool);
+            Handle th;
+            if (type->byte_at(0) == '(') {
+              th = SystemDictionary::find_method_handle_type(type, caller, CHECK);
+            } else {
+              th = SystemDictionary::find_java_mirror_for_type(type, caller, SignatureStream::NCDFError, CHECK);
+            }
+            pseudo_arg = th();
+            break;
+          }
+        case -1:  // argument count
+          {
+            int argc = caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool);
+            jvalue argc_value; argc_value.i = (jint)argc;
+            pseudo_arg = java_lang_boxing_object::create(T_INT, &argc_value, CHECK);
+            break;
+          }
+        }
+
+        // Store the pseudo-argument, and advance the pointers.
+        buf->obj_at_put(pos++, pseudo_arg);
+        ++start;
+      }
+    }
+    // When we are done with this there may be regular arguments to process too.
+  }
+  Handle ifna(THREAD, JNIHandles::resolve(ifna_jh));
+  caller->constants()->
+    copy_bootstrap_arguments_at(bss_index_in_pool,
+                                start, end, buf, pos,
+                                (resolve == JNI_TRUE), ifna, CHECK);
+}
+JVM_END
+
 // It is called by a Cleaner object which ensures that dropped CallSites properly
 // deallocate their dependency information.
 JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) {
@@ -1438,6 +1519,7 @@
   {CC "objectFieldOffset",         CC "(" MEM ")J",                          FN_PTR(MHN_objectFieldOffset)},
   {CC "setCallSiteTargetNormal",   CC "(" CS "" MH ")V",                     FN_PTR(MHN_setCallSiteTargetNormal)},
   {CC "setCallSiteTargetVolatile", CC "(" CS "" MH ")V",                     FN_PTR(MHN_setCallSiteTargetVolatile)},
+  {CC "copyOutBootstrapArguments", CC "(" CLS "[III[" OBJ "IZ" OBJ ")V",     FN_PTR(MHN_copyOutBootstrapArguments)},
   {CC "clearCallSiteContext",      CC "(" CTX ")V",                          FN_PTR(MHN_clearCallSiteContext)},
   {CC "staticFieldOffset",         CC "(" MEM ")J",                          FN_PTR(MHN_staticFieldOffset)},
   {CC "staticFieldBase",           CC "(" MEM ")" OBJ,                        FN_PTR(MHN_staticFieldBase)},
--- a/src/hotspot/share/runtime/globals.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/globals.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -3961,6 +3961,14 @@
   develop(bool, TraceInvokeDynamic, false,                                  \
           "trace internal invoke dynamic operations")                       \
                                                                             \
+  diagnostic(int, UseBootstrapCallInfo, 1,                                  \
+          "0: when resolving InDy or ConDy, force all BSM arguments to be " \
+          "resolved before the bootstrap method is called; 1: when a BSM "  \
+          "that may accept a BootstrapCallInfo is detected, use that API "  \
+          "to pass BSM arguments, which allows the BSM to delay their "     \
+          "resolution; 2+: stress test the BCI API by calling more BSMs "   \
+          "via that API, instead of with the eagerly-resolved array.")      \
+                                                                            \
   diagnostic(bool, PauseAtStartup,      false,                              \
           "Causes the VM to pause at startup time and wait for the pause "  \
           "file to be removed (default: ./vm.paused.<pid>)")                \
--- a/src/hotspot/share/runtime/reflection.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/reflection.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -765,27 +765,26 @@
 static oop get_mirror_from_signature(const methodHandle& method,
                                      SignatureStream* ss,
                                      TRAPS) {
-
+  Klass* accessing_klass = method->method_holder();
+  assert(accessing_klass != NULL, "method has no accessing_klass");
 
-  if (T_OBJECT == ss->type() || T_ARRAY == ss->type()) {
-    Symbol* name = ss->as_symbol(CHECK_NULL);
-    oop loader = method->method_holder()->class_loader();
-    oop protection_domain = method->method_holder()->protection_domain();
-    const Klass* k = SystemDictionary::resolve_or_fail(name,
-                                                       Handle(THREAD, loader),
-                                                       Handle(THREAD, protection_domain),
-                                                       true,
-                                                       CHECK_NULL);
-    if (log_is_enabled(Debug, class, resolve)) {
-      trace_class_resolution(k);
+  oop mirror_oop = ss->as_java_mirror(Handle(THREAD, accessing_klass->class_loader()),
+                                      Handle(THREAD, accessing_klass->protection_domain()),
+                                      SignatureStream::NCDFError,
+                                      CHECK_NULL);
+
+  // Special tracing logic for resolution of class names during reflection.
+  if (log_is_enabled(Debug, class, resolve)) {
+    Klass* result = java_lang_Class::as_Klass(mirror_oop);
+    if (result != NULL) {
+      trace_class_resolution(result);
     }
-    return k->java_mirror();
   }
 
   assert(ss->type() != T_VOID || ss->at_return_type(),
     "T_VOID should only appear as return type");
 
-  return java_lang_Class::primitive_mirror(ss->type());
+  return mirror_oop;
 }
 
 static objArrayHandle get_parameter_types(const methodHandle& method,
@@ -819,24 +818,17 @@
 }
 
 static Handle new_type(Symbol* signature, Klass* k, TRAPS) {
-  // Basic types
-  BasicType type = vmSymbols::signature_type(signature);
-  if (type != T_OBJECT) {
-    return Handle(THREAD, Universe::java_mirror(type));
+  Handle mirror = SystemDictionary::find_java_mirror_for_type(signature, k, SignatureStream::NCDFError, CHECK_(Handle()));
+
+  // Special tracing logic for resolution of class names during reflection.
+  if (log_is_enabled(Debug, class, resolve)) {
+    Klass* result = java_lang_Class::as_Klass(mirror());
+    if (result != NULL) {
+      trace_class_resolution(result);
+    }
   }
 
-  Klass* result =
-    SystemDictionary::resolve_or_fail(signature,
-                                      Handle(THREAD, k->class_loader()),
-                                      Handle(THREAD, k->protection_domain()),
-                                      true, CHECK_(Handle()));
-
-  if (log_is_enabled(Debug, class, resolve)) {
-    trace_class_resolution(result);
-  }
-
-  oop nt = result->java_mirror();
-  return Handle(THREAD, nt);
+  return mirror;
 }
 
 
--- a/src/hotspot/share/runtime/signature.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/signature.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -392,11 +392,19 @@
 
 oop SignatureStream::as_java_mirror(Handle class_loader, Handle protection_domain,
                                     FailureMode failure_mode, TRAPS) {
-  if (!is_object())
-    return Universe::java_mirror(type());
-  Klass* klass = as_klass(class_loader, protection_domain, failure_mode, CHECK_NULL);
-  if (klass == NULL)  return NULL;
-  return klass->java_mirror();
+  if (raw_length() == 1) {
+    // short-cut in a common case
+    return SystemDictionary::find_java_mirror_for_type((char) raw_byte_at(0));
+  }
+  TempNewSymbol signature = SymbolTable::new_symbol(_signature, _begin, _end, CHECK_NULL);
+  Klass* no_accessing_klass = NULL;
+  Handle mirror = SystemDictionary::find_java_mirror_for_type(signature,
+                                                              no_accessing_klass,
+                                                              class_loader,
+                                                              protection_domain,
+                                                              failure_mode,
+                                                              CHECK_NULL);
+  return mirror();
 }
 
 Symbol* SignatureStream::as_symbol_or_null() {
--- a/src/hotspot/share/runtime/signature.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/signature.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -409,6 +409,11 @@
   const jbyte* raw_bytes()  { return _signature->bytes() + _begin; }
   int          raw_length() { return _end - _begin; }
 
+  jbyte raw_byte_at(int index) {
+    assert(index >= 0 && index < raw_length(), "index overflow");
+    return _signature->byte_at(_begin + index);
+  }
+
   // return same as_symbol except allocation of new symbols is avoided.
   Symbol* as_symbol_or_null();
 
--- a/src/hotspot/share/runtime/vmStructs.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -2339,6 +2339,7 @@
   declare_constant(JVM_CONSTANT_NameAndType)                              \
   declare_constant(JVM_CONSTANT_MethodHandle)                             \
   declare_constant(JVM_CONSTANT_MethodType)                               \
+  declare_constant(JVM_CONSTANT_Dynamic)                                  \
   declare_constant(JVM_CONSTANT_InvokeDynamic)                            \
   declare_constant(JVM_CONSTANT_ExternalMax)                              \
                                                                           \
@@ -2350,6 +2351,7 @@
   declare_constant(JVM_CONSTANT_UnresolvedClassInError)                   \
   declare_constant(JVM_CONSTANT_MethodHandleInError)                      \
   declare_constant(JVM_CONSTANT_MethodTypeInError)                        \
+  declare_constant(JVM_CONSTANT_DynamicInError)                           \
   declare_constant(JVM_CONSTANT_InternalMax)                              \
                                                                           \
   /*****************************/                                         \
--- a/src/hotspot/share/utilities/constantTag.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -56,6 +56,11 @@
     case JVM_CONSTANT_MethodType :
     case JVM_CONSTANT_MethodTypeInError :
       return T_OBJECT;
+
+    case JVM_CONSTANT_Dynamic :
+    case JVM_CONSTANT_DynamicInError :
+      assert(false, "Dynamic constant has no fixed basic type");
+
     default:
       ShouldNotReachHere();
       return T_ILLEGAL;
@@ -71,6 +76,8 @@
     return JVM_CONSTANT_MethodHandle;
   case JVM_CONSTANT_MethodTypeInError:
     return JVM_CONSTANT_MethodType;
+  case JVM_CONSTANT_DynamicInError:
+    return JVM_CONSTANT_Dynamic;
   default:
     return _tag;
   }
@@ -85,6 +92,8 @@
     return JVM_CONSTANT_MethodHandleInError;
   case JVM_CONSTANT_MethodType:
     return JVM_CONSTANT_MethodTypeInError;
+  case JVM_CONSTANT_Dynamic:
+    return JVM_CONSTANT_DynamicInError;
   default:
     ShouldNotReachHere();
     return JVM_CONSTANT_Invalid;
@@ -123,6 +132,10 @@
       return "MethodType";
     case JVM_CONSTANT_MethodTypeInError :
       return "MethodType Error";
+    case JVM_CONSTANT_Dynamic :
+      return "Dynamic";
+    case JVM_CONSTANT_DynamicInError :
+      return "Dynamic Error";
     case JVM_CONSTANT_InvokeDynamic :
       return "InvokeDynamic";
     case JVM_CONSTANT_Utf8 :
--- a/src/hotspot/share/utilities/constantTag.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -43,7 +43,8 @@
   JVM_CONSTANT_UnresolvedClassInError   = 103,  // Error tag due to resolution error
   JVM_CONSTANT_MethodHandleInError      = 104,  // Error tag due to resolution error
   JVM_CONSTANT_MethodTypeInError        = 105,  // Error tag due to resolution error
-  JVM_CONSTANT_InternalMax              = 105   // Last implementation tag
+  JVM_CONSTANT_DynamicInError           = 106,  // Error tag due to resolution error
+  JVM_CONSTANT_InternalMax              = 106   // Last implementation tag
 };
 
 
@@ -80,6 +81,10 @@
     return _tag == JVM_CONSTANT_MethodTypeInError;
   }
 
+  bool is_dynamic_constant_in_error() const {
+    return _tag == JVM_CONSTANT_DynamicInError;
+  }
+
   bool is_klass_index() const       { return _tag == JVM_CONSTANT_ClassIndex; }
   bool is_string_index() const      { return _tag == JVM_CONSTANT_StringIndex; }
 
@@ -88,13 +93,14 @@
   bool is_field_or_method() const   { return is_field() || is_method() || is_interface_method(); }
   bool is_symbol() const            { return is_utf8(); }
 
-  bool is_method_type() const              { return _tag == JVM_CONSTANT_MethodType; }
-  bool is_method_handle() const            { return _tag == JVM_CONSTANT_MethodHandle; }
-  bool is_invoke_dynamic() const           { return _tag == JVM_CONSTANT_InvokeDynamic; }
+  bool is_method_type() const       { return _tag == JVM_CONSTANT_MethodType; }
+  bool is_method_handle() const     { return _tag == JVM_CONSTANT_MethodHandle; }
+  bool is_dynamic_constant() const  { return _tag == JVM_CONSTANT_Dynamic; }
+  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_method_type() || is_method_handle() || is_dynamic_constant() ||
             is_unresolved_klass());
   }
 
@@ -108,6 +114,20 @@
     _tag = tag;
   }
 
+  static constantTag ofBasicType(BasicType bt) {
+    if (is_subword_type(bt))  bt = T_INT;
+    switch (bt) {
+      case T_OBJECT: return constantTag(JVM_CONSTANT_String);
+      case T_INT:    return constantTag(JVM_CONSTANT_Integer);
+      case T_LONG:   return constantTag(JVM_CONSTANT_Long);
+      case T_FLOAT:  return constantTag(JVM_CONSTANT_Float);
+      case T_DOUBLE: return constantTag(JVM_CONSTANT_Double);
+      default:       break;
+    }
+    assert(false, "bad basic type for tag");
+    return constantTag();
+  }
+
   jbyte value() const                { return _tag; }
   jbyte error_value() const;
   jbyte non_error_value() const;
--- a/src/hotspot/share/utilities/exceptions.cpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/exceptions.cpp	Fri Sep 08 10:46:46 2017 -0700
@@ -403,6 +403,37 @@
                                    h_prot, to_utf8_safe);
 }
 
+// invokedynamic uses wrap_dynamic_exception for:
+//    - bootstrap method resolution
+//    - post call to MethodHandleNatives::linkCallSite
+// dynamically computed constant uses wrap_dynamic_exception for:
+//    - bootstrap method resolution
+//    - post call to MethodHandleNatives::linkDynamicConstant
+void Exceptions::wrap_dynamic_exception(Thread* THREAD) {
+  if (THREAD->has_pending_exception()) {
+    oop exception = THREAD->pending_exception();
+    // See the "Linking Exceptions" section for the invokedynamic instruction
+    // in JVMS 6.5.
+    if (exception->is_a(SystemDictionary::Error_klass())) {
+      // Pass through an Error, including BootstrapMethodError, any other form
+      // of linkage error, or say ThreadDeath/OutOfMemoryError
+      if (TraceMethodHandles) {
+        tty->print_cr("[constant/invoke]dynamic passes through an Error for " INTPTR_FORMAT, p2i((void *)exception));
+        exception->print();
+      }
+      return;
+    }
+
+    // Otherwise wrap the exception in a BootstrapMethodError
+    if (TraceMethodHandles) {
+      tty->print_cr("[constant/invoke]dynamic throws BSME for " INTPTR_FORMAT, p2i((void *)exception));
+      exception->print();
+    }
+    Handle nested_exception(THREAD, exception);
+    THREAD->clear_pending_exception();
+    THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
+  }
+}
 
 // Exception counting for hs_err file
 volatile int Exceptions::_stack_overflow_errors = 0;
--- a/src/hotspot/share/utilities/exceptions.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/exceptions.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -166,6 +166,8 @@
 
   static void throw_stack_overflow_exception(Thread* thread, const char* file, int line, const methodHandle& method);
 
+  static void wrap_dynamic_exception(Thread* thread);
+
   // Exception counting for error files of interesting exceptions that may have
   // caused a problem for the jvm
   static volatile int _stack_overflow_errors;
--- a/src/hotspot/share/utilities/globalDefinitions.hpp	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp	Fri Sep 08 10:46:46 2017 -0700
@@ -596,6 +596,10 @@
   return (t == T_BYTE || t == T_SHORT);
 }
 
+inline bool is_reference_type(BasicType t) {
+  return (t == T_OBJECT || t == T_ARRAY);
+}
+
 // Convert a char from a classfile signature to a BasicType
 inline BasicType char2type(char c) {
   switch( c ) {
--- a/src/java.base/share/classes/java/lang/BootstrapMethodError.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/BootstrapMethodError.java	Fri Sep 08 10:46:46 2017 -0700
@@ -26,11 +26,15 @@
 package java.lang;
 
 /**
- * Thrown to indicate that an {@code invokedynamic} instruction has
- * failed to find its bootstrap method,
- * or the bootstrap method has failed to provide a
- * {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
- * of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type}.
+ * Thrown to indicate that an {@code invokedynamic} instruction or a dynamic
+ * constant failed to resolve its bootstrap method and arguments,
+ * or for {@code invokedynamic} instruction the bootstrap method has failed to
+ * provide a
+ * {@linkplain java.lang.invoke.CallSite call site} with a
+ * {@linkplain java.lang.invoke.CallSite#getTarget target}
+ * of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type},
+ * or for a dynamic constant the bootstrap method has failed to provide a
+ * constant value of the required type.
  *
  * @author John Rose, JSR 292 EG
  * @since 1.7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package java.lang.invoke;
+
+import java.util.*;
+import jdk.internal.vm.annotation.Stable;
+
+import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
+import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
+
+/** Utility class for implementing ConstantGroup. */
+/*non-public*/
+abstract class AbstractConstantGroup implements ConstantGroup {
+    /** The size of this constant group, set permanently by the constructor. */
+    protected final int size;
+
+    /** The constructor requires the size of the constant group being represented.
+     * @param size the size of this constant group, set permanently by the constructor
+     */
+    AbstractConstantGroup(int size) {
+        this.size = size;
+    }
+
+    @Override public final int size() {
+        return size;
+    }
+
+    public abstract Object get(int index) throws LinkageError;
+
+    public abstract Object get(int index, Object ifNotPresent);
+
+    public abstract boolean isPresent(int index);
+
+    // Do not override equals or hashCode, since this type is stateful.
+
+    /**
+     * Produce a string using the non-resolving list view,
+     * where unresolved elements are presented as asterisks.
+     * @return {@code this.asList("*").toString()}
+     */
+    @Override public String toString() {
+        return asList("*").toString();
+    }
+
+    static class AsIterator implements Iterator<Object> {
+        private final ConstantGroup self;
+        private final int end;
+        private final boolean resolving;
+        private final Object ifNotPresent;
+
+        // Mutable state:
+        private int index;
+
+        private AsIterator(ConstantGroup self, int start, int end,
+                         boolean resolving, Object ifNotPresent) {
+            this.self = self;
+            this.end = end;
+            this.index = start;
+            this.resolving = resolving;
+            this.ifNotPresent = ifNotPresent;
+        }
+        AsIterator(ConstantGroup self, int start, int end) {
+            this(self, start, end, true, null);
+        }
+        AsIterator(ConstantGroup self, int start, int end,
+                 Object ifNotPresent) {
+            this(self, start, end, false, ifNotPresent);
+        }
+
+        @Override
+        public boolean hasNext() {
+            return index < end;
+        }
+
+        @Override
+        public Object next() {
+            int i = bumpIndex();
+            if (resolving)
+                return self.get(i);
+            else
+                return self.get(i, ifNotPresent);
+        }
+
+        private int bumpIndex() {
+            int i = index;
+            if (i >= end)  throw new NoSuchElementException();
+            index = i+1;
+            return i;
+        }
+    }
+
+    static class SubGroup extends AbstractConstantGroup {
+        private final ConstantGroup self;  // the real CG
+        private final int offset;  // offset within myself
+        SubGroup(ConstantGroup self, int start, int end) {
+            super(end - start);
+            this.self = self;
+            this.offset = start;
+            rangeCheck2(start, end, size);
+        }
+
+        private int mapIndex(int index) {
+            return rangeCheck1(index, size) + offset;
+        }
+
+        @Override
+        public Object get(int index) {
+            return self.get(mapIndex(index));
+        }
+
+        @Override
+        public Object get(int index, Object ifNotPresent) {
+            return self.get(mapIndex(index), ifNotPresent);
+        }
+
+        @Override
+        public boolean isPresent(int index) {
+            return self.isPresent(mapIndex(index));
+        }
+
+        @Override
+        public ConstantGroup subGroup(int start, int end) {
+            rangeCheck2(start, end, size);
+            return new SubGroup(self, offset + start, offset + end);
+        }
+
+        @Override
+        public List<Object> asList() {
+            return new AsList(self, offset, offset + size);
+        }
+
+        @Override
+        public List<Object> asList(Object ifNotPresent) {
+            return new AsList(self, offset, offset + size, ifNotPresent);
+        }
+
+        @Override
+        public int copyConstants(int start, int end,
+                                  Object[] buf, int pos) throws LinkageError {
+            rangeCheck2(start, end, size);
+            return self.copyConstants(offset + start, offset + end,
+                                      buf, pos);
+        }
+
+        @Override
+        public int copyConstants(int start, int end,
+                                  Object[] buf, int pos,
+                                  Object ifNotPresent) {
+            rangeCheck2(start, end, size);
+            return self.copyConstants(offset + start, offset + end,
+                                      buf, pos, ifNotPresent);
+        }
+    }
+
+    static class AsList extends AbstractList<Object> {
+        private final ConstantGroup self;
+        private final int size;
+        private final int offset;
+        private final boolean resolving;
+        private final Object ifNotPresent;
+
+        private AsList(ConstantGroup self, int start, int end,
+                       boolean resolving, Object ifNotPresent) {
+            this.self = self;
+            this.size = end - start;
+            this.offset = start;
+            this.resolving = resolving;
+            this.ifNotPresent = ifNotPresent;
+            rangeCheck2(start, end, self.size());
+        }
+        AsList(ConstantGroup self, int start, int end) {
+            this(self, start, end, true, null);
+        }
+        AsList(ConstantGroup self, int start, int end,
+               Object ifNotPresent) {
+            this(self, start, end, false, ifNotPresent);
+        }
+
+        private int mapIndex(int index) {
+            return rangeCheck1(index, size) + offset;
+        }
+
+        @Override public final int size() {
+            return size;
+        }
+
+        @Override public Object get(int index) {
+            if (resolving)
+                return self.get(mapIndex(index));
+            else
+                return self.get(mapIndex(index), ifNotPresent);
+        }
+
+        @Override
+        public Iterator<Object> iterator() {
+            if (resolving)
+                return new AsIterator(self, offset, offset + size);
+            else
+                return new AsIterator(self, offset, offset + size, ifNotPresent);
+        }
+
+        @Override public List<Object> subList(int start, int end) {
+            rangeCheck2(start, end, size);
+            return new AsList(self, offset + start, offset + end,
+                              resolving, ifNotPresent);
+        }
+
+        @Override public Object[] toArray() {
+            return toArray(new Object[size]);
+        }
+        @Override public <T> T[] toArray(T[] a) {
+            int pad = a.length - size;
+            if (pad < 0) {
+                pad = 0;
+                a = Arrays.copyOf(a, size);
+            }
+            if (resolving)
+                self.copyConstants(offset, offset + size, a, 0);
+            else
+                self.copyConstants(offset, offset + size, a, 0,
+                                   ifNotPresent);
+            if (pad > 0)  a[size] = null;
+            return a;
+        }
+    }
+
+    static abstract
+    class WithCache extends AbstractConstantGroup {
+        @Stable final Object[] cache;
+
+        WithCache(int size) {
+            super(size);
+            // It is caller's responsibility to initialize the cache.
+            // Initial contents are all-null, which means nothing is present.
+            cache = new Object[size];
+        }
+
+        void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
+            // Replace ifNotPresent with NOT_PRESENT,
+            // and null with RESOLVED_TO_NULL.
+            // Then forget about the user-provided ifNotPresent.
+            for (int i = 0; i < cache.length; i++) {
+                Object x = cacheContents.get(i);
+                if (x == ifNotPresent)
+                    continue;  // leave the null in place
+                if (x == null)
+                    x = RESOLVED_TO_NULL;
+                cache[i] = x;
+            }
+        }
+
+        @Override public Object get(int i) {
+            Object x = cache[i];
+            // @Stable array must use null for sentinel
+            if (x == null)  x = fillCache(i);
+            return unwrapNull(x);
+        }
+
+        @Override public Object get(int i, Object ifNotAvailable) {
+            Object x = cache[i];
+            // @Stable array must use null for sentinel
+            if (x == null)  return ifNotAvailable;
+            return unwrapNull(x);
+        }
+
+        @Override
+        public boolean isPresent(int i) {
+            return cache[i] != null;
+        }
+
+        /** hook for local subclasses */
+        Object fillCache(int i) {
+            throw new NoSuchElementException("constant group does not contain element #"+i);
+        }
+
+        /// routines for mapping between null sentinel and true resolved null
+
+        static Object wrapNull(Object x) {
+            return x == null ? RESOLVED_TO_NULL : x;
+        }
+
+        static Object unwrapNull(Object x) {
+            assert(x != null);
+            return x == RESOLVED_TO_NULL ? null : x;
+        }
+
+        // secret sentinel for an actual null resolved value, in the cache
+        static final Object RESOLVED_TO_NULL = new Object();
+
+        // secret sentinel for a "hole" in the cache:
+        static final Object NOT_PRESENT = new Object();
+
+    }
+
+    /** Skeleton implementation of BootstrapCallInfo. */
+    static
+    class BSCIWithCache<T> extends WithCache implements BootstrapCallInfo<T> {
+        private final MethodHandle bsm;
+        private final String name;
+        private final T type;
+
+        @Override public String toString() {
+            return bsm+"/"+name+":"+type+super.toString();
+        }
+
+        BSCIWithCache(MethodHandle bsm, String name, T type, int size) {
+            super(size);
+            this.type = type;
+            this.bsm = bsm;
+            this.name = name;
+            assert(type instanceof Class || type instanceof MethodType);
+        }
+
+        @Override public MethodHandle bootstrapMethod() { return bsm; }
+        @Override public String invocationName() { return name; }
+        @Override public T invocationType() { return type; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package java.lang.invoke;
+
+import java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * An interface providing full static information about a particular
+ * call to a
+ * <a href="package-summary.html#bsm">bootstrap method</a> of an
+ * dynamic call site or dynamic constant.
+ * This information includes the method itself, the associated
+ * name and type, and any associated static arguments.
+ * <p>
+ * If a bootstrap method declares exactly two arguments, and is
+ * not of variable arity, then it is fed only two arguments by
+ * the JVM, the {@linkplain Lookup lookup object} and an instance
+ * of {@code BootstrapCallInfo} which supplies the rest of the
+ * information about the call.
+ * <p>
+ * The API for accessing the static arguments allows the bootstrap
+ * method to reorder the resolution (in the constant pool) of the
+ * static arguments, and to catch errors resulting from the resolution.
+ * This mode of evaluation <em>pulls</em> bootstrap parameters from
+ * the JVM under control of the bootstrap method, as opposed to
+ * the JVM <em>pushing</em> parameters to a bootstrap method
+ * by resolving them all before the bootstrap method is called.
+ * @apiNote
+ * <p>
+ * The {@linkplain Lookup lookup object} is <em>not</em> included in this
+ * bundle of information, so as not to obscure the access control
+ * logic of the program.
+ * In cases where there are many thousands of parameters, it may
+ * be preferable to pull their resolved values, either singly or in
+ * batches, rather than wait until all of them have been resolved
+ * before a constant or call site can be used.
+ * <p>
+ * A push mode bootstrap method can be adapted to a pull mode
+ * bootstrap method, and vice versa.  For example, this generic
+ * adapter pops a push-mode bootstrap method from the beginning
+ * of the static argument list, eagerly resolves all the remaining
+ * static arguments, and invokes the popped method in push mode.
+ * The callee has no way of telling that it was not called directly
+ * from the JVM.
+ * <blockquote><pre>{@code
+static Object genericBSM(Lookup lookup, BootstrapCallInfo<Object> bsci)
+    throws Throwable {
+  ArrayList<Object> args = new ArrayList<>();
+  args.add(lookup);
+  args.add(bsci.invocationName());
+  args.add(bsci.invocationType());
+  MethodHandle bsm = (MethodHandle) bsci.get(0);
+  List<Object> restOfArgs = bsci.asList().subList(1, bsci.size();
+  // the next line eagerly resolves all remaining static arguments:
+  args.addAll(restOfArgs);
+  return bsm.invokeWithArguments(args);
+}
+ * }</pre></blockquote>
+ *
+ * <p>
+ * In the other direction, here is a combinator which pops
+ * a pull-mode bootstrap method from the beginning of a list of
+ * static argument values (already resolved), reformats all of
+ * the arguments into a pair of a lookup and a {@code BootstrapCallInfo},
+ * and invokes the popped method.  Again the callee has no way of
+ * telling it was not called directly by the JVM, except that
+ * all of the constant values will appear as resolved.
+ * Put another way, if any constant fails to resolve, the
+ * callee will not be able to catch the resulting error,
+ * since the error will be thrown by the JVM before the
+ * bootstrap method is entered.
+ * <blockquote><pre>{@code
+static Object genericBSM(Lookup lookup, String name, Object type,
+                         MethodHandle bsm, Object... args)
+    throws Throwable {
+  ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
+  BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(bsm, name, type, cons);
+  return bsm.invoke(lookup, bsci);
+}
+ * }</pre></blockquote>
+ *
+ * @since 1.10
+ */
+// public
+interface BootstrapCallInfo<T> extends ConstantGroup {
+    /** Returns the bootstrap method for this call.
+     * @return the bootstrap method
+     */
+    MethodHandle bootstrapMethod();
+
+    /** Returns the method name or constant name for this call.
+     * @return the method name or constant name
+     */
+    String invocationName();
+
+    /** Returns the method type or constant type for this call.
+     * @return the method type or constant type
+     */
+    T invocationType();
+
+    /**
+     * Make a new bootstrap call descriptor with the given components.
+     * @param bsm bootstrap method
+     * @param name invocation name
+     * @param type invocation type
+     * @param constants the additional static arguments for the bootstrap method
+     * @param <T> the type of the invocation type, either {@link MethodHandle} or {@link Class}
+     * @return a new bootstrap call descriptor with the given components
+     */
+    static <T> BootstrapCallInfo<T> makeBootstrapCallInfo(MethodHandle bsm,
+                                                          String name,
+                                                          T type,
+                                                          ConstantGroup constants) {
+        AbstractConstantGroup.BSCIWithCache<T> bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
+        final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
+        bsci.initializeCache(constants.asList(NP), NP);
+        return bsci;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.lang.invoke;
+
+import sun.invoke.util.Wrapper;
+
+import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
+import java.util.Arrays;
+
+import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
+import static java.lang.invoke.ConstantGroup.makeConstantGroup;
+import static java.lang.invoke.MethodHandleNatives.*;
+import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
+import static java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+
+final class BootstrapMethodInvoker {
+    /**
+     * Factored code for invoking a bootstrap method for invokedynamic
+     * or a dynamic constant.
+     * @param resultType the expected return type (either CallSite or a constant type)
+     * @param bootstrapMethod the BSM to call
+     * @param name the method name or constant name
+     * @param type the method type or constant type
+     * @param info information passed up from the JVM, to derive static arguments
+     * @param callerClass the class containing the resolved method call or constant load
+     * @param <T> the expected return type
+     * @return the expected value, either a CallSite or a constant value
+     */
+    static <T> T invoke(Class<T> resultType,
+                        MethodHandle bootstrapMethod,
+                        // Callee information:
+                        String name, Object type,
+                        // Extra arguments for BSM, if any:
+                        Object info,
+                        // Caller information:
+                        Class<?> callerClass) {
+        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+        Object result;
+        boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
+        boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
+        MethodHandle pullModeBSM;
+        // match the VM with the BSM
+        if (vmIsPushing) {
+            // VM is pushing arguments at us
+            pullModeBSM = null;
+            if (pullMode) {
+                bootstrapMethod = Adapters.pushMePullYou(bootstrapMethod, true);
+            }
+        } else {
+            // VM wants us to pull args from it
+            pullModeBSM = pullMode ? bootstrapMethod :
+                    Adapters.pushMePullYou(bootstrapMethod, false);
+            bootstrapMethod = null;
+        }
+        try {
+            info = maybeReBox(info);
+            if (info == null) {
+                // VM is allowed to pass up a null meaning no BSM args
+                result = bootstrapMethod.invoke(caller, name, type);
+            }
+            else if (!info.getClass().isArray()) {
+                // VM is allowed to pass up a single BSM arg directly
+                result = bootstrapMethod.invoke(caller, name, type, info);
+            }
+            else if (info.getClass() == int[].class) {
+                // VM is allowed to pass up a pair {argc, index}
+                // referring to 'argc' BSM args at some place 'index'
+                // in the guts of the VM (associated with callerClass).
+                // The format of this index pair is private to the
+                // handshake between the VM and this class only.
+                // This supports "pulling" of arguments.
+                // The VM is allowed to do this for any reason.
+                // The code in this method makes up for any mismatches.
+                BootstrapCallInfo<Object> bsci
+                    = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
+                // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
+                result = pullModeBSM.invoke(caller, bsci);
+            }
+            else {
+                // VM is allowed to pass up a full array of resolved BSM args
+                Object[] argv = (Object[]) info;
+                maybeReBoxElements(argv);
+                switch (argv.length) {
+                    case 0:
+                        result = bootstrapMethod.invoke(caller, name, type);
+                        break;
+                    case 1:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0]);
+                        break;
+                    case 2:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0], argv[1]);
+                        break;
+                    case 3:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0], argv[1], argv[2]);
+                        break;
+                    case 4:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0], argv[1], argv[2], argv[3]);
+                        break;
+                    case 5:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0], argv[1], argv[2], argv[3], argv[4]);
+                        break;
+                    case 6:
+                        result = bootstrapMethod.invoke(caller, name, type,
+                                                        argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+                        break;
+                    default:
+                        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
+                        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
+                        if (argv.length >= MAX_SAFE_SIZE) {
+                            // to be on the safe side, use invokeWithArguments which handles jumbo lists
+                            Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
+                            newargv[0] = caller;
+                            newargv[1] = name;
+                            newargv[2] = type;
+                            System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
+                            result = bootstrapMethod.invokeWithArguments(newargv);
+                            break;
+                        }
+                        MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
+                        MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+                        MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+                        result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
+                }
+            }
+            if (resultType.isPrimitive()) {
+                // Non-reference conversions are more than just plain casts.
+                // By pushing the value through a funnel of the form (T x)->x,
+                // the boxed result can be widened as needed.  See MH::asType.
+                MethodHandle funnel = MethodHandles.identity(resultType);
+                result = funnel.invoke(result);
+                // Now it is the wrapper type for resultType.
+                resultType = Wrapper.asWrapperType(resultType);
+            }
+            return resultType.cast(result);
+        }
+        catch (Error e) {
+            // Pass through an Error, including BootstrapMethodError, any other
+            // form of linkage error, such as IllegalAccessError if the bootstrap
+            // method is inaccessible, or say ThreadDeath/OutOfMemoryError
+            // See the "Linking Exceptions" section for the invokedynamic
+            // instruction in JVMS 6.5.
+            throw e;
+        }
+        catch (Throwable ex) {
+            // Wrap anything else in BootstrapMethodError
+            throw new BootstrapMethodError("bootstrap method initialization exception", ex);
+        }
+    }
+
+    /** The JVM produces java.lang.Integer values to box
+     *  CONSTANT_Integer boxes but does not intern them.
+     *  Let's intern them.  This is slightly wrong for
+     *  a {@code CONSTANT_Dynamic} which produces an
+     *  un-interned integer (e.g., {@code new Integer(0)}).
+     */
+    private static Object maybeReBox(Object x) {
+        if (x instanceof Integer) {
+            int xi = (int) x;
+            if (xi == (byte) xi)
+                x = xi;  // must rebox; see JLS 5.1.7
+        }
+        return x;
+    }
+
+    private static void maybeReBoxElements(Object[] xa) {
+        for (int i = 0; i < xa.length; i++) {
+            xa[i] = maybeReBox(xa[i]);
+        }
+    }
+
+    /** Canonical VM-aware implementation of BootstrapCallInfo.
+     * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
+     */
+    private static final class VM_BSCI<T> extends BSCIWithCache<T> {
+        private final int[] indexInfo;
+        private final Class<?> caller;  // for index resolution only
+
+        VM_BSCI(MethodHandle bsm, String name, T type,
+                Lookup lookup, int[] indexInfo) {
+            super(bsm, name, type, indexInfo[0]);
+            if (!lookup.hasPrivateAccess())  //D.I.D.
+                throw new AssertionError("bad Lookup object");
+            this.caller = lookup.lookupClass();
+            this.indexInfo = indexInfo;
+            // scoop up all the easy stuff right away:
+            prefetchIntoCache(0, size());
+        }
+
+        @Override Object fillCache(int i) {
+            Object[] buf = { null };
+            copyConstants(i, i+1, buf, 0);
+            Object res = wrapNull(buf[0]);
+            cache[i] = res;
+            int next = i + 1;
+            if (next < cache.length && cache[next] == null)
+                maybePrefetchIntoCache(next, false);  // try to prefetch
+            return res;
+        }
+
+        @Override public int copyConstants(int start, int end,
+                                           Object[] buf, int pos) {
+            int i = start, bufi = pos;
+            while (i < end) {
+                Object x = cache[i];
+                if (x == null)  break;
+                buf[bufi++] = unwrapNull(x);
+                i++;
+            }
+            // give up at first null and grab the rest in one big block
+            if (i >= end)  return i;
+            Object[] temp = new Object[end - i];
+            if (TRACE_METHOD_LINKAGE)
+                System.out.println("resolving more BSM arguments: "+
+                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
+            copyOutBootstrapArguments(caller, indexInfo,
+                                      i, end, temp, 0,
+                                      true, null);
+            for (Object x : temp) {
+                x = maybeReBox(x);
+                buf[bufi++] = x;
+                cache[i++] = wrapNull(x);
+            }
+            if (end < cache.length && cache[end] == null)
+                maybePrefetchIntoCache(end, true);  // try to prefetch
+            return i;
+        }
+
+        private static final int MIN_PF = 4;
+        private void maybePrefetchIntoCache(int i, boolean bulk) {
+            int len = cache.length;
+            assert(0 <= i && i <= len);
+            int pfLimit = i;
+            if (bulk)  pfLimit += i;  // exponential prefetch expansion
+            // try to prefetch at least MIN_PF elements
+            if (pfLimit < i + MIN_PF)  pfLimit = i + MIN_PF;
+            if (pfLimit > len || pfLimit < 0)  pfLimit = len;
+            // stop prefetching where cache is more full than empty
+            int empty = 0, nonEmpty = 0, lastEmpty = i;
+            for (int j = i; j < pfLimit; j++) {
+                if (cache[j] == null) {
+                    empty++;
+                    lastEmpty = j;
+                } else {
+                    nonEmpty++;
+                    if (nonEmpty > empty) {
+                        pfLimit = lastEmpty + 1;
+                        break;
+                    }
+                    if (pfLimit < len)  pfLimit++;
+                }
+            }
+            if (bulk && empty < MIN_PF && pfLimit < len)
+                return;  // not worth the effort
+            prefetchIntoCache(i, pfLimit);
+        }
+
+        private void prefetchIntoCache(int i, int pfLimit) {
+            if (pfLimit <= i)  return;  // corner case
+            Object[] temp = new Object[pfLimit - i];
+            if (TRACE_METHOD_LINKAGE)
+                System.out.println("prefetching BSM arguments: "+
+                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
+            copyOutBootstrapArguments(caller, indexInfo,
+                                      i, pfLimit, temp, 0,
+                                      false, NOT_PRESENT);
+            for (Object x : temp) {
+                if (x != NOT_PRESENT && cache[i] == null) {
+                    cache[i] = wrapNull(maybeReBox(x));
+                }
+                i++;
+            }
+        }
+    }
+
+    /*non-public*/ static final
+    class Adapters {
+        // skeleton for push-mode BSM which wraps a pull-mode BSM:
+        static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
+                                            MethodHandles.Lookup lookup, String name, Object type,
+                                            Object... arguments) throws Throwable {
+            ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
+            BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
+            if (TRACE_METHOD_LINKAGE)
+                System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
+            return pullModeBSM.invoke(lookup, bsci);
+        }
+
+        // skeleton for pull-mode BSM which wraps a push-mode BSM:
+        static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
+                                              MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci)
+                throws Throwable {
+            int argc = bsci.size();
+            Object arguments[] = new Object[3 + argc];
+            arguments[0] = lookup;
+            arguments[1] = bsci.invocationName();
+            arguments[2] = bsci.invocationType();
+            bsci.copyConstants(0, argc, arguments, 3);
+            if (TRACE_METHOD_LINKAGE)
+                System.out.println("pulled arguments from VM for push-mode BSM");
+            return pushModeBSM.invokeWithArguments(arguments);
+        }
+        static final MethodHandle MH_pushToBootstrapMethod;
+        static final MethodHandle MH_pullFromBootstrapMethod;
+        static {
+            final Class<?> THIS_CLASS = Adapters.class;
+            try {
+                MH_pushToBootstrapMethod = IMPL_LOOKUP
+                    .findStatic(THIS_CLASS, "pushToBootstrapMethod",
+                                MethodType.methodType(Object.class, MethodHandle.class,
+                                        Lookup.class, String.class, Object.class, Object[].class));
+                MH_pullFromBootstrapMethod = IMPL_LOOKUP
+                    .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
+                                MethodType.methodType(Object.class, MethodHandle.class,
+                                        Lookup.class, BootstrapCallInfo.class));
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+        }
+
+        /** Given a push-mode BSM (taking one argument) convert it to a
+         *  pull-mode BSM (taking N pre-resolved arguments).
+         *  This method is used when, in fact, the JVM is passing up
+         *  pre-resolved arguments, but the BSM is expecting lazy stuff.
+         *  Or, when goToPushMode is true, do the reverse transform.
+         *  (The two transforms are exactly inverse.)
+         */
+        static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
+            if (TRACE_METHOD_LINKAGE)
+                System.out.println("converting BSM to "+(goToPushMode ? "push mode" : "pull mode"));
+            assert(isPullModeBSM(bsm) == goToPushMode);  //there must be a change
+            if (goToPushMode) {
+                return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
+            } else {
+                return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/java/lang/invoke/CallSite.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/CallSite.java	Fri Sep 08 10:46:46 2017 -0700
@@ -302,65 +302,10 @@
                              Object info,
                              // Caller information:
                              Class<?> callerClass) {
-        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
         CallSite site;
         try {
-            Object binding;
-            info = maybeReBox(info);
-            if (info == null) {
-                binding = bootstrapMethod.invoke(caller, name, type);
-            } else if (!info.getClass().isArray()) {
-                binding = bootstrapMethod.invoke(caller, name, type, info);
-            } else {
-                Object[] argv = (Object[]) info;
-                maybeReBoxElements(argv);
-                switch (argv.length) {
-                    case 0:
-                        binding = bootstrapMethod.invoke(caller, name, type);
-                        break;
-                    case 1:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0]);
-                        break;
-                    case 2:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0], argv[1]);
-                        break;
-                    case 3:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0], argv[1], argv[2]);
-                        break;
-                    case 4:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0], argv[1], argv[2], argv[3]);
-                        break;
-                    case 5:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0], argv[1], argv[2], argv[3], argv[4]);
-                        break;
-                    case 6:
-                        binding = bootstrapMethod.invoke(caller, name, type,
-                                                         argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-                        break;
-                    default:
-                        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
-                        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
-                        if (argv.length >= MAX_SAFE_SIZE) {
-                            // to be on the safe side, use invokeWithArguments which handles jumbo lists
-                            Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
-                            newargv[0] = caller;
-                            newargv[1] = name;
-                            newargv[2] = type;
-                            System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
-                            binding = bootstrapMethod.invokeWithArguments(newargv);
-                        } else {
-                            MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
-                            MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
-                            MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-                            binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
-                        }
-                }
-            }
+            Object binding = BootstrapMethodInvoker.invoke(
+                    CallSite.class, bootstrapMethod, name, type, info, callerClass);
             if (binding instanceof CallSite) {
                 site = (CallSite) binding;
             } else {
@@ -369,7 +314,7 @@
                 // Throws a runtime exception defining the cause that is then
                 // in the "catch (Throwable ex)" a few lines below wrapped in
                 // BootstrapMethodError
-                throw new ClassCastException("bootstrap method failed to produce a CallSite");
+                throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
             }
             if (!site.getTarget().type().equals(type)) {
                 // See the "Linking Exceptions" section for the invokedynamic
@@ -388,22 +333,8 @@
             throw e;
         } catch (Throwable ex) {
             // Wrap anything else in BootstrapMethodError
-            throw new BootstrapMethodError("call site initialization exception", ex);
+            throw new BootstrapMethodError("CallSite bootstrap method initialization exception", ex);
         }
         return site;
     }
-
-    private static Object maybeReBox(Object x) {
-        if (x instanceof Integer) {
-            int xi = (int) x;
-            if (xi == (byte) xi)
-                x = xi;  // must rebox; see JLS 5.1.7
-        }
-        return x;
-    }
-    private static void maybeReBoxElements(Object[] xa) {
-        for (int i = 0; i < xa.length; i++) {
-            xa[i] = maybeReBox(xa[i]);
-        }
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/ConstantGroup.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package java.lang.invoke;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.function.IntFunction;
+
+/**
+ * An ordered sequence of constants, some of which may not yet
+ * be present.  This type is used by {@link BootstrapCallInfo}
+ * to represent the sequence of bootstrap arguments associated
+ * with a bootstrap method, without forcing their immediate
+ * resolution.
+ * <p>
+ * If you use the
+ * {@linkplain ConstantGroup#get(int) simple get method},
+ * the constant will be resolved, if this has not already
+ * happened.  An occasional side effect of resolution is a
+ * {@code LinkageError}, which happens if the system
+ * could not resolve the constant in question.
+ * <p>
+ * In order to peek at a constant without necessarily
+ * resolving it, use the
+ * {@linkplain ConstantGroup#get(int,Object)
+ * non-throwing get method}.
+ * This method will never throw a resolution error.
+ * Instead, if the resolution would result in an error,
+ * or if the implementation elects not to attempt
+ * resolution at this point, then the method will
+ * return the user-supplied sentinel value.
+ * <p>
+ * To iterate through the constants, resolving as you go,
+ * use the iterator provided on the {@link List}-typed view.
+ * If you supply a sentinel, resolution will be suppressed.
+ * <p>
+ * Typically the constant is drawn from a constant pool entry
+ * in the virtual machine. Constant pool entries undergo a
+ * one-time state transition from unresolved to resolved,
+ * with a permanently recorded result.  Usually that result
+ * is the desired constant value, but it may also be an error.
+ * In any case, the results displayed by a {@code ConstantGroup}
+ * are stable in the same way.  If a query to a particular
+ * constant in a {@code ConstantGroup} throws an exception once,
+ * it will throw the same kind of exception forever after.
+ * If the query returns a constant value once, it will return
+ * the same value forever after.
+ * <p>
+ * The only possible change in the status of a constant is
+ * from the unresolved to the resolved state, and that
+ * happens exactly once.  A constant will never revert to
+ * an unlinked state.  However, from the point of view of
+ * this interface, constants may appear to spontaneously
+ * resolve.  This is so because constant pools are global
+ * structures shared across threads, and because
+ * prefetching of some constants may occur, there are no
+ * strong guarantees when the virtual machine may resolve
+ * constants.
+ * <p>
+ * When choosing sentinel values, be aware that a constant
+ * pool which has {@code CONSTANT_Dynamic} entries
+ * can contain potentially any representable value,
+ * and arbitrary implementations of {@code ConstantGroup}
+ * are also free to produce arbitrary values.
+ * This means some obvious choices for sentinel values,
+ * such as {@code null}, may sometimes fail to distinguish
+ * a resolved from an unresolved constant in the group.
+ * The most reliable sentinel is a privately created object,
+ * or perhaps the {@code ConstantGroup} itself.
+ * @since 1.10
+ */
+// public
+interface ConstantGroup {
+    /// Access
+
+    /**
+     * Returns the number of constants in this group.
+     * This value never changes, for any particular group.
+     * @return the number of constants in this group
+     */
+    int size();
+
+    /**
+     * Returns the selected constant, resolving it if necessary.
+     * Throws a linkage error if resolution proves impossible.
+     * @param index which constant to select
+     * @return the selected constant
+     * @throws LinkageError if the selected constant needs resolution and cannot be resolved
+     */
+    Object get(int index) throws LinkageError;
+
+    /**
+     * Returns the selected constant,
+     * or the given sentinel value if there is none available.
+     * If the constant cannot be resolved, the sentinel will be returned.
+     * If the constant can (perhaps) be resolved, but has not yet been resolved,
+     * then the sentinel <em>may</em> be returned, at the implementation's discretion.
+     * To force resolution (and a possible exception), call {@link #get(int)}.
+     * @param index the selected constant
+     * @param ifNotPresent the sentinel value to return if the constant is not present
+     * @return the selected constant, if available, else the sentinel value
+     */
+    Object get(int index, Object ifNotPresent);
+
+    /**
+     * Returns an indication of whether a constant may be available.
+     * If it returns {@code true}, it will always return true in the future,
+     * and a call to {@link #get(int)} will never throw an exception.
+     * <p>
+     * After a normal return from {@link #get(int)} or a present
+     * value is reported from {@link #get(int,Object)}, this method
+     * must always return true.
+     * <p>
+     * If this method returns {@code false}, nothing in particular
+     *  can be inferred, since the query only concerns the internal
+     * logic of the {@code ConstantGroup} object which ensures that
+     a successful * query to a constant will always remain successful.
+     * The only way to force a permanent decision about whether
+     * a constant is available is to call {@link #get(int)} and
+     * be ready for an exception if the constant is unavailable.
+     * @param index the selected constant
+     * @return {@code true} if the selected constant is known by
+     *     this object to be present, {@code false} if it is known
+     *     not to be present or
+     */
+    boolean isPresent(int index);
+
+    /// Views
+
+    /**
+     * Create a view on this group as a {@link List} view.
+     * Any request for a constant through this view will
+     * force resolution.
+     * @return a {@code List} view on this group which will force resolution
+     */
+    default List<Object> asList() {
+        return new AbstractConstantGroup.AsList(this, 0, size());
+    }
+
+    /**
+     * Create a view on this group as a {@link List} view.
+     * Any request for a constant through this view will
+     * return the given sentinel value, if the corresponding
+     * call to {@link #get(int,Object)} would do so.
+     * @param ifNotPresent the sentinel value to return if a constant is not present
+     * @return a {@code List} view on this group which will not force resolution
+     */
+    default List<Object> asList(Object ifNotPresent) {
+        return new AbstractConstantGroup.AsList(this, 0, size(), ifNotPresent);
+    }
+
+    /**
+     * Create a view on a sub-sequence of this group.
+     * @param start the index to begin the view
+     * @param end the index to end the view
+     * @return a view on the selected sub-group
+     */
+    default ConstantGroup subGroup(int start, int end) {
+        return new AbstractConstantGroup.SubGroup(this, start, end);
+    }
+
+    /// Bulk operations
+
+    /**
+     * Copy a sequence of constant values into a given buffer.
+     * This is equivalent to {@code end-offset} separate calls to {@code get},
+     * for each index in the range from {@code offset} up to but not including {@code end}.
+     * For the first constant that cannot be resolved,
+     * a {@code LinkageError} is thrown, but only after
+     * preceding constant value have been stored.
+     * @param start index of first constant to retrieve
+     * @param end limiting index of constants to retrieve
+     * @param buf array to receive the requested values
+     * @param pos position in the array to offset storing the values
+     * @return the limiting index, {@code end}
+     * @throws LinkageError if a constant cannot be resolved
+     */
+    default int copyConstants(int start, int end,
+                              Object[] buf, int pos)
+            throws LinkageError
+    {
+        int bufBase = pos - start;  // buf[bufBase + i] = get(i)
+        for (int i = start; i < end; i++) {
+            buf[bufBase + i] = get(i);
+        }
+        return end;
+    }
+
+    /**
+     * Copy a sequence of constant values into a given buffer.
+     * This is equivalent to {@code end-offset} separate calls to {@code get},
+     * for each index in the range from {@code offset} up to but not including {@code end}.
+     * Any constants that cannot be resolved are replaced by the
+     * given sentinel value.
+     * @param start index of first constant to retrieve
+     * @param end limiting index of constants to retrieve
+     * @param buf array to receive the requested values
+     * @param pos position in the array to offset storing the values
+     * @param ifNotPresent sentinel value to store if a value is not available
+     * @return the limiting index, {@code end}
+     * @throws LinkageError if {@code resolve} is true and a constant cannot be resolved
+     */
+    default int copyConstants(int start, int end,
+                              Object[] buf, int pos,
+                              Object ifNotPresent) {
+        int bufBase = pos - start;  // buf[bufBase + i] = get(i)
+        for (int i = start; i < end; i++) {
+            buf[bufBase + i] = get(i, ifNotPresent);
+        }
+        return end;
+    }
+
+    /**
+     * Make a new constant group with the given constants.
+     * The value of {@code ifNotPresent} may be any reference.
+     * If this value is encountered as an element of the
+     * {@code constants} list, the new constant group will
+     * regard that element of the list as logically missing.
+     * If the new constant group is called upon to resolve
+     * a missing element of the group, it will refer to the
+     * given {@code constantProvider}, by calling it on the
+     * index of the missing element.
+     * The {@code constantProvider} must be stable, in the sense
+     * that the outcome of calling it on the same index twice
+     * will produce equivalent results.
+     * If {@code constantProvider} is the null reference, then
+     * it will be treated as if it were a function which raises
+     * {@link NoSuchElementException}.
+     * @param constants the elements of this constant group
+     * @param ifNotPresent sentinel value provided instead of a missing constant
+     * @param constantProvider function to call when a missing constant is resolved
+     * @return a new constant group with the given constants and resolution behavior
+     */
+    static ConstantGroup makeConstantGroup(List<Object> constants,
+                                           Object ifNotPresent,
+                                           IntFunction<Object> constantProvider) {
+        class Impl extends AbstractConstantGroup.WithCache {
+            Impl() {
+                super(constants.size());
+                initializeCache(constants, ifNotPresent);
+            }
+            @Override
+            Object fillCache(int index) {
+                if (constantProvider == null)  super.fillCache(index);
+                return constantProvider.apply(index);
+            }
+        }
+        return new Impl();
+    }
+
+    /**
+     * Make a new constant group with the given constant values.
+     * The constants will be copied from the given list into the
+     * new constant group, forcing resolution if any are missing.
+     * @param constants the constants of this constant group
+     * @return a new constant group with the given constants
+     */
+    static ConstantGroup makeConstantGroup(List<Object> constants) {
+        final Object NP = AbstractConstantGroup.WithCache.NOT_PRESENT;
+        assert(!constants.contains(NP));  // secret value
+        return makeConstantGroup(constants, NP, null);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/DynamicConstant.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.lang.invoke;
+
+/**
+ * Bootstrap methods for dynamically-computed constant.
+ */
+final class DynamicConstant {
+    // implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
+    /*non-public*/
+    static Object makeConstant(MethodHandle bootstrapMethod,
+                               // Callee information:
+                               String name, Class<?> type,
+                               // Extra arguments for BSM, if any:
+                               Object info,
+                               // Caller information:
+                               Class<?> callerClass) {
+        // BSMI.invoke handles all type checking and exception translation.
+        // If type is not a reference type, the JVM is expecting a boxed
+        // version, and will manage unboxing on the other side.
+        return BootstrapMethodInvoker.invoke(
+                type, bootstrapMethod, name, type, info, callerClass);
+    }
+}
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java	Fri Sep 08 10:46:46 2017 -0700
@@ -65,6 +65,12 @@
     static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
     static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
 
+    static native void copyOutBootstrapArguments(Class<?> caller, int[] indexInfo,
+                                                 int start, int end,
+                                                 Object[] buf, int pos,
+                                                 boolean resolve,
+                                                 Object ifNotAvailable);
+
     /** Represents a context to track nmethod dependencies on CallSite instance target. */
     static class CallSiteContext implements Runnable {
         //@Injected JVM_nmethodBucket* vmdependencies;
@@ -228,6 +234,7 @@
      * The JVM is linking an invokedynamic instruction.  Create a reified call site for it.
      */
     static MemberName linkCallSite(Object callerObj,
+                                   int indexInCP,
                                    Object bootstrapMethodObj,
                                    Object nameObj, Object typeObj,
                                    Object staticArguments,
@@ -268,9 +275,7 @@
                                           Object[] appendixResult) {
         Object bsmReference = bootstrapMethod.internalMemberName();
         if (bsmReference == null)  bsmReference = bootstrapMethod;
-        Object staticArglist = (staticArguments instanceof Object[] ?
-                                java.util.Arrays.asList((Object[]) staticArguments) :
-                                staticArguments);
+        String staticArglist = staticArglistForTrace(staticArguments);
         System.out.println("linkCallSite "+caller.getName()+" "+
                            bsmReference+" "+
                            name+type+"/"+staticArglist);
@@ -280,11 +285,89 @@
             System.out.println("linkCallSite => "+res+" + "+appendixResult[0]);
             return res;
         } catch (Throwable ex) {
+            ex.printStackTrace(); // print now in case exception is swallowed
             System.out.println("linkCallSite => throw "+ex);
             throw ex;
         }
     }
 
+    // this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
+    static Object linkDynamicConstant(Object callerObj,
+                                      int indexInCP,
+                                      Object bootstrapMethodObj,
+                                      Object nameObj, Object typeObj,
+                                      Object staticArguments) {
+        MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
+        Class<?> caller = (Class<?>)callerObj;
+        String name = nameObj.toString().intern();
+        Class<?> type = (Class<?>)typeObj;
+        if (!TRACE_METHOD_LINKAGE)
+            return linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
+        return linkDynamicConstantTracing(caller, bootstrapMethod, name, type, staticArguments);
+    }
+
+    static Object linkDynamicConstantImpl(Class<?> caller,
+                                          MethodHandle bootstrapMethod,
+                                          String name, Class<?> type,
+                                          Object staticArguments) {
+        return DynamicConstant.makeConstant(bootstrapMethod, name, type, staticArguments, caller);
+    }
+
+    private static String staticArglistForTrace(Object staticArguments) {
+        if (staticArguments instanceof Object[])
+            return "BSA="+java.util.Arrays.asList((Object[]) staticArguments);
+        if (staticArguments instanceof int[])
+            return "BSA@"+java.util.Arrays.toString((int[]) staticArguments);
+        if (staticArguments == null)
+            return "BSA0=null";
+        return "BSA1="+staticArguments;
+    }
+
+    // Tracing logic:
+    static Object linkDynamicConstantTracing(Class<?> caller,
+                                             MethodHandle bootstrapMethod,
+                                             String name, Class<?> type,
+                                             Object staticArguments) {
+        Object bsmReference = bootstrapMethod.internalMemberName();
+        if (bsmReference == null)  bsmReference = bootstrapMethod;
+        String staticArglist = staticArglistForTrace(staticArguments);
+        System.out.println("linkDynamicConstant "+caller.getName()+" "+
+                           bsmReference+" "+
+                           name+type+"/"+staticArglist);
+        try {
+            Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
+            System.out.println("linkDynamicConstantImpl => "+res);
+            return res;
+        } catch (Throwable ex) {
+            ex.printStackTrace(); // print now in case exception is swallowed
+            System.out.println("linkDynamicConstant => throw "+ex);
+            throw ex;
+        }
+    }
+
+    /** The JVM is requesting pull-mode bootstrap when it provides
+     *  a tuple of the form int[]{ argc, vmindex }.
+     *  The BSM is expected to call back to the JVM using the caller
+     *  class and vmindex to resolve the static arguments.
+     */
+    static boolean staticArgumentsPulled(Object staticArguments) {
+        return staticArguments instanceof int[];
+    }
+
+    /** A BSM runs in pull-mode if and only if its sole arguments
+     * are (Lookup, BootstrapCallInfo), or can be converted pairwise
+     * to those types, and it is not of variable arity.
+     * Excluding error cases, we can just test that the arity is a constant 2.
+     *
+     * NOTE: This method currently returns false, since pulling is not currently
+     * exposed to a BSM. When pull mode is supported the method block will be
+     * replaced with currently commented out code.
+     */
+    static boolean isPullModeBSM(MethodHandle bsm) {
+        return false;
+//        return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector();
+    }
+
     /**
      * The JVM wants a pointer to a MethodType.  Oblige it by finding or creating one.
      */
@@ -506,34 +589,43 @@
             Lookup lookup = IMPL_LOOKUP.in(callerClass);
             assert(refKindIsValid(refKind));
             return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
-        } catch (IllegalAccessException ex) {
+        } catch (ReflectiveOperationException ex) {
+            throw mapLookupExceptionToError(ex);
+        }
+    }
+
+    /**
+     * Map a reflective exception to a linkage error.
+     */
+    static LinkageError mapLookupExceptionToError(ReflectiveOperationException ex) {
+        LinkageError err;
+        if (ex instanceof IllegalAccessException) {
             Throwable cause = ex.getCause();
             if (cause instanceof AbstractMethodError) {
-                throw (AbstractMethodError) cause;
+                return (AbstractMethodError) cause;
             } else {
-                Error err = new IllegalAccessError(ex.getMessage());
-                throw initCauseFrom(err, ex);
+                err = new IllegalAccessError(ex.getMessage());
             }
-        } catch (NoSuchMethodException ex) {
-            Error err = new NoSuchMethodError(ex.getMessage());
-            throw initCauseFrom(err, ex);
-        } catch (NoSuchFieldException ex) {
-            Error err = new NoSuchFieldError(ex.getMessage());
-            throw initCauseFrom(err, ex);
-        } catch (ReflectiveOperationException ex) {
-            Error err = new IncompatibleClassChangeError();
-            throw initCauseFrom(err, ex);
+        } else if (ex instanceof NoSuchMethodException) {
+            err = new NoSuchMethodError(ex.getMessage());
+        } else if (ex instanceof NoSuchFieldException) {
+            err = new NoSuchFieldError(ex.getMessage());
+        } else {
+            err = new IncompatibleClassChangeError();
         }
+        return initCauseFrom(err, ex);
     }
 
     /**
      * Use best possible cause for err.initCause(), substituting the
      * cause for err itself if the cause has the same (or better) type.
      */
-    private static Error initCauseFrom(Error err, Exception ex) {
+    static <E extends Error> E initCauseFrom(E err, Exception ex) {
         Throwable th = ex.getCause();
-        if (err.getClass().isInstance(th))
-           return (Error) th;
+        @SuppressWarnings("unchecked")
+        final Class<E> Eclass = (Class<E>) err.getClass();
+        if (Eclass.isInstance(th))
+           return Eclass.cast(th);
         err.initCause(th == null ? ex : th);
         return err;
     }
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Fri Sep 08 10:46:46 2017 -0700
@@ -142,4 +142,13 @@
         if (obj != null || obj2 != null)  message = message + ": " + obj + ", " + obj2;
         return message;
     }
+    /*non-public*/ static void rangeCheck2(int start, int end, int size) {
+        if (0 > start || start > end || end > size)
+            throw new IndexOutOfBoundsException(start+".."+end);
+    }
+    /*non-public*/ static int rangeCheck1(int index, int size) {
+        if (0 > index || index >= size)
+            throw new IndexOutOfBoundsException(index);
+        return index;
+    }
 }
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Fri Sep 08 10:46:46 2017 -0700
@@ -98,12 +98,6 @@
      * <p>
      * This method is caller sensitive, which means that it may return different
      * values to different callers.
-     * <p>
-     * For any given caller class {@code C}, the lookup object returned by this call
-     * has equivalent capabilities to any lookup object
-     * supplied by the JVM to the bootstrap method of an
-     * <a href="package-summary.html#indyinsn">invokedynamic instruction</a>
-     * executing in the same caller class {@code C}.
      * @return a lookup object for the caller of this method, with private access
      */
     @CallerSensitive
--- a/src/java.base/share/classes/java/lang/invoke/package-info.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/package-info.java	Fri Sep 08 10:46:46 2017 -0700
@@ -24,13 +24,12 @@
  */
 
 /**
- * The {@code java.lang.invoke} package contains dynamic language support provided directly by
- * the Java core class libraries and virtual machine.
+ * The {@code java.lang.invoke} package provides low-level primitives for interacting
+ * with the Java Virtual Machine.
  *
  * <p>
- * As described in the Java Virtual Machine Specification,
- * certain types in this package have special relations to dynamic
- * language support in the virtual machine:
+ * As described in the Java Virtual Machine Specification, certain types in this package
+ * are given special treatment by the virtual machine:
  * <ul>
  * <li>The classes {@link java.lang.invoke.MethodHandle MethodHandle}
  * {@link java.lang.invoke.VarHandle VarHandle} contain
@@ -40,77 +39,106 @@
  * </li>
  *
  * <li>The JVM bytecode format supports immediate constants of
- * the classes {@link java.lang.invoke.MethodHandle MethodHandle} and {@link java.lang.invoke.MethodType MethodType}.
+ * the classes {@link java.lang.invoke.MethodHandle MethodHandle} and
+ * {@link java.lang.invoke.MethodType MethodType}.
+ * </li>
+ *
+ * <li>The {@code invokedynamic} instruction makes use of bootstrap {@code MethodHandle}
+ * constants to dynamically resolve {@code CallSite} objects for custom method invocation
+ * behavior.
+ * </li>
+ *
+ * <li>The {@code ldc} instruction makes use of bootstrap {@code MethodHandle} constants
+ * to dynamically resolve custom constant values.
  * </li>
  * </ul>
  *
- * <h1><a id="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h1>
+ * <h1><a id="jvm_mods"></a>Dynamic resolution of call sites and constants</h1>
  * The following low-level information summarizes relevant parts of the
  * Java Virtual Machine specification.  For full details, please see the
  * current version of that specification.
  *
- * Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
- * <h2><a id="indyinsn"></a>{@code invokedynamic} instructions</h2>
- * A dynamic call site is originally in an unlinked state.  In this state, there is
- * no target method for the call site to invoke.
+ * <h2><a id="indyinsn"></a>Dynamically-computed call sites</h2>
+ * An {@code invokedynamic} instruction is originally in an unlinked state.
+ * In this state, there is no target method for the instruction to invoke.
  * <p>
- * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
- * the call site must first be <em>linked</em>.
+ * Before the JVM can execute an {@code invokedynamic} instruction,
+ * the instruction must first be <em>linked</em>.
  * Linking is accomplished by calling a <em>bootstrap method</em>
- * which is given the static information content of the call site,
- * and which must produce a {@link java.lang.invoke.MethodHandle method handle}
- * that gives the behavior of the call site.
+ * which is given the static information content of the call,
+ * and which must produce a {@link java.lang.invoke.CallSite}
+ * that gives the behavior of the invocation.
  * <p>
  * Each {@code invokedynamic} instruction statically specifies its own
  * bootstrap method as a constant pool reference.
- * The constant pool reference also specifies the call site's name and type descriptor,
- * just like {@code invokevirtual} and the other invoke instructions.
+ * The constant pool reference also specifies the invocation's name and method type descriptor,
+ * just like {@code invokestatic} and the other invoke instructions.
+ *
+ * <h2><a id="condycon"></a>Dynamically-computed constants</h2>
+ * The constant pool may contain constants tagged {@code CONSTANT_Dynamic},
+ * equipped with bootstrap methods which perform their resolution.
+ * Such a <em>dynamic constant</em> is originally in an unresolved state.
+ * Before the JVM can use a dynamically-computed constant, it must first be <em>resolved</em>.
+ * Dynamically-computed constant resolution is accomplished by calling a <em>bootstrap method</em>
+ * which is given the static information content of the constant,
+ * and which must produce a value of the constant's statically declared type.
  * <p>
- * Linking starts with resolving the constant pool entry for the
- * bootstrap method, and resolving a {@link java.lang.invoke.MethodType MethodType} object for
- * the type descriptor of the dynamic call site.
- * This resolution process may trigger class loading.
- * It may therefore throw an error if a class fails to load.
- * This error becomes the abnormal termination of the dynamic
- * call site execution.
- * Linkage does not trigger class initialization.
- * <p>
- * The bootstrap method is invoked on at least three values:
+ * Each dynamically-computed constant statically specifies its own
+ * bootstrap method as a constant pool reference.
+ * The constant pool reference also specifies the constant's name and field type descriptor,
+ * just like {@code getstatic} and the other field reference instructions.
+ * (Roughly speaking, a dynamically-computed constant is to a dynamically-computed call site
+ * as a {@code CONSTANT_Fieldref} is to a {@code CONSTANT_Methodref}.)
+ *
+ * <h2><a id="bsm"></a>Execution of bootstrap methods</h2>
+ * Resolving a dynamically-computed call site or constant
+ * starts with resolving constants from the constant pool for the
+ * following items:
  * <ul>
- * <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em>
- *     in which dynamic call site occurs </li>
- * <li>a {@code String}, the method name mentioned in the call site </li>
- * <li>a {@code MethodType}, the resolved type descriptor of the call </li>
- * <li>optionally, any number of additional static arguments taken from the constant pool </li>
+ * <li>the bootstrap method, a {@code CONSTANT_MethodHandle}</li>
+ * <li>the {@code Class} or {@code MethodType} derived from
+ * type component of the {@code CONSTANT_NameAndType} descriptor</li>
+ * <li>static arguments, if any (note that static arguments can themselves be
+ * dynamically-computed constants)</li>
  * </ul>
  * <p>
- * In all cases, bootstrap method invocation is as if by
- * {@link java.lang.invoke.MethodHandle#invokeWithArguments MethodHandle.invokeWithArguments},
- * (This is also equivalent to
- * {@linkplain java.lang.invoke.MethodHandle#invoke generic invocation}
- * if the number of arguments is small enough.)
+ * The bootstrap method is then invoked, as if by
+ * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke},
+ * with the following arguments:
+ * <ul>
+ * <li>a {@code MethodHandles.Lookup}, which is a lookup object on the <em>caller class</em>
+ * in which dynamically-computed constant or call site occurs</li>
+ * <li>a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}</li>
+ * <li>a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}</li>
+ * <li>a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant </li>
+ * <li>the additional resolved static arguments, if any</li>
+ * </ul>
  * <p>
- * For an {@code invokedynamic} instruction, the
- * returned result must be convertible to a non-null reference to a
+ * For a dynamically-computed call site, the returned result must be a non-null reference to a
  * {@link java.lang.invoke.CallSite CallSite}.
- * If the returned result cannot be converted to the expected type,
- * {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
  * The type of the call site's target must be exactly equal to the type
- * derived from the dynamic call site's type descriptor and passed to
- * the bootstrap method, otherwise a {@code BootstrapMethodError} is thrown.
- * On success the call site then becomes permanently linked to the dynamic call
- * site.
+ * derived from the invocation's type descriptor and passed to
+ * the bootstrap method. If these conditions are not met, a {@code BootstrapMethodError} is thrown.
+ * On success the call site then becomes permanently linked to the {@code invokedynamic}
+ * instruction.
  * <p>
- * If an exception, {@code E} say, occurs when linking the call site then the
- * linkage fails and terminates abnormally. {@code E} is rethrown if the type of
+ * For a dynamically-computed constant, the result of the bootstrap method is cached
+ * as the resolved constant value.
+ * <p>
+ * If an exception, {@code E} say, occurs during execution of the bootstrap method, then
+ * resolution fails and terminates abnormally. {@code E} is rethrown if the type of
  * {@code E} is {@code Error} or a subclass, otherwise a
  * {@code BootstrapMethodError} that wraps {@code E} is thrown.
- * If this happens, the same {@code Error} or subclass will the thrown for all
- * subsequent attempts to execute the dynamic call site.
- * <h2>timing of linkage</h2>
- * A dynamic call site is linked just before its first execution.
+ * If this happens, the same error will be thrown for all
+ * subsequent attempts to execute the {@code invokedynamic} instruction or load the
+ * dynamically-computed constant.
+ *
+ * <h2>Timing of resolution</h2>
+ * An {@code invokedynamic} instruction is linked just before its first execution.
+ * A dynamically-computed constant is resolved just before the first time it is used
+ * (by pushing it on the stack or linking it as a bootstrap method parameter).
  * The bootstrap method call implementing the linkage occurs within
- * a thread that is attempting a first execution.
+ * a thread that is attempting a first execution or first use.
  * <p>
  * If there are several such threads, the bootstrap method may be
  * invoked in several threads concurrently.
@@ -119,7 +147,7 @@
  * In any case, every {@code invokedynamic} instruction is either
  * unlinked or linked to a unique {@code CallSite} object.
  * <p>
- * In an application which requires dynamic call sites with individually
+ * In an application which requires {@code invokedynamic} instructions with individually
  * mutable behaviors, their bootstrap methods should produce distinct
  * {@link java.lang.invoke.CallSite CallSite} objects, one for each linkage request.
  * Alternatively, an application can link a single {@code CallSite} object
@@ -127,53 +155,46 @@
  * a change to the target method will become visible at each of
  * the instructions.
  * <p>
- * If several threads simultaneously execute a bootstrap method for a single dynamic
- * call site, the JVM must choose one {@code CallSite} object and install it visibly to
+ * If several threads simultaneously execute a bootstrap method for a single dynamically-computed
+ * call site or constant, the JVM must choose one bootstrap method result and install it visibly to
  * all threads.  Any other bootstrap method calls are allowed to complete, but their
- * results are ignored, and their dynamic call site invocations proceed with the originally
- * chosen target object.
+ * results are ignored.
 
  * <p style="font-size:smaller;">
  * <em>Discussion:</em>
- * These rules do not enable the JVM to duplicate dynamic call sites,
+ * These rules do not enable the JVM to share call sites,
  * or to issue &ldquo;causeless&rdquo; bootstrap method calls.
- * Every dynamic call site transitions at most once from unlinked to linked,
+ * Every {@code invokedynamic} instruction transitions at most once from unlinked to linked,
  * just before its first invocation.
  * There is no way to undo the effect of a completed bootstrap method call.
  *
- * <h2>types of bootstrap methods</h2>
- * As long as each bootstrap method can be correctly invoked
- * by {@code MethodHandle.invoke}, its detailed type is arbitrary.
+ * <h2>Types of bootstrap methods</h2>
+ * For a dynamically-computed call site, the bootstrap method is invoked with parameter
+ * types {@code MethodHandles.Lookup}, {@code String}, {@code MethodType}, and the types
+ * of any static arguments; the return type is {@code CallSite}. For a
+ * dynamically-computed constant, the bootstrap method is invoked with parameter types
+ * {@code MethodHandles.Lookup}, {@code String}, {@code Class}, and the types of any
+ * static arguments; the return type is the type represented by the {@code Class}.
+ *
+ * Because {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} allows for
+ * adaptations between the invoked method type and the method handle's method type,
+ * there is flexibility in the declaration of the bootstrap method.
  * For example, the first argument could be {@code Object}
  * instead of {@code MethodHandles.Lookup}, and the return type
  * could also be {@code Object} instead of {@code CallSite}.
  * (Note that the types and number of the stacked arguments limit
  * the legal kinds of bootstrap methods to appropriately typed
- * static methods and constructors of {@code CallSite} subclasses.)
+ * static methods and constructors.)
  * <p>
- * If a given {@code invokedynamic} instruction specifies no static arguments,
- * the instruction's bootstrap method will be invoked on three arguments,
- * conveying the instruction's caller class, name, and method type.
- * If the {@code invokedynamic} instruction specifies one or more static arguments,
- * those values will be passed as additional arguments to the method handle.
- * (Note that because there is a limit of 255 arguments to any method,
- * at most 251 extra arguments can be supplied to a non-varargs bootstrap method,
- * since the bootstrap method
- * handle itself and its first three arguments must also be stacked.)
- * The bootstrap method will be invoked as if by {@code MethodHandle.invokeWithArguments}.
- * A variable-arity bootstrap method can accept thousands of static arguments,
- * subject only by limits imposed by the class-file format.
- * <p>
- * The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
- * For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
+ * If a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
  * If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
  * then some or all of the arguments specified here may be collected into a trailing array parameter.
  * (This is not a special rule, but rather a useful consequence of the interaction
  * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
  * and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
  * <p>
- * Given these rules, here are examples of legal bootstrap method declarations,
- * given various numbers {@code N} of extra arguments.
+ * Given these rules, here are examples of legal bootstrap method declarations for
+ * dynamically-computed call sites, given various numbers {@code N} of extra arguments.
  * The first row (marked {@code *}) will work for any number of extra arguments.
  * <table class="plain" style="vertical-align:top">
  * <caption style="display:none">Static argument types</caption>
@@ -208,28 +229,27 @@
  * {@code String}.
  * The other examples work with all types of extra arguments.
  * <p>
- * As noted above, the actual method type of the bootstrap method can vary.
- * For example, the fourth argument could be {@code MethodHandle},
- * if that is the type of the corresponding constant in
- * the {@code CONSTANT_InvokeDynamic} entry.
- * In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
- * constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
- * will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
- * (If a string constant were passed instead, by badly generated code, that cast would then fail,
- * resulting in a {@code BootstrapMethodError}.)
- * <p>
- * Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
- * argument, if it can be represented by a constant pool entry.
+ * Since dynamically-computed constants can be provided as static arguments to bootstrap
+ * methods, there are no limitations on the types of bootstrap arguments.
  * However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
- * cannot be created for bootstrap methods, since such constants cannot be directly
- * represented in the constant pool, and the invocation of the bootstrap method will
+ * cannot be <em>directly</em> supplied by {@code CONSTANT_Integer}
+ * constant pool entries, since the {@code asType} conversions do
  * not perform the necessary narrowing primitive conversions.
  * <p>
- * Extra bootstrap method arguments are intended to allow language implementors
- * to safely and compactly encode metadata.
- * In principle, the name and extra arguments are redundant,
- * since each call site could be given its own unique bootstrap method.
- * Such a practice would be likely to produce large class files and constant pools.
+ * In the above examples, the return type is always {@code CallSite},
+ * but that is not a necessary feature of bootstrap methods.
+ * In the case of a dynamically-computed call site, the only requirement is that
+ * the return type of the bootstrap method must be convertible
+ * (using the {@code asType} conversions) to {@code CallSite}, which
+ * means the bootstrap method return type might be {@code Object} or
+ * {@code ConstantCallSite}.
+ * In the case of a dynamically-resolved constant, the return type of the bootstrap
+ * method must be convertible to the type of the constant, as
+ * represented by its field type descriptor.  For example, if the
+ * dynamic constant has a field type descriptor of {@code "C"}
+ * ({@code char}) then the bootstrap method return type could be
+ * {@code Object}, {@code Character}, or {@code char}, but not
+ * {@code int} or {@code Integer}.
  *
  * @author John Rose, JSR 292 EG
  * @since 1.7
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java	Fri Sep 08 10:46:46 2017 -0700
@@ -205,6 +205,10 @@
             case ClassWriter.FLOAT:
             case ClassWriter.NAME_TYPE:
             case ClassWriter.INDY:
+            // @@@ ClassWriter.CONDY
+            // Enables MethodHandles.lookup().defineClass to function correctly
+            // when it reads the class name
+            case 17:
                 size = 5;
                 break;
             case ClassWriter.LONG:
--- a/src/java.base/share/native/include/classfile_constants.h.template	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/native/include/classfile_constants.h.template	Fri Sep 08 10:46:46 2017 -0700
@@ -107,6 +107,7 @@
     JVM_CONSTANT_NameAndType            = 12,
     JVM_CONSTANT_MethodHandle           = 15,  // JSR 292
     JVM_CONSTANT_MethodType             = 16,  // JSR 292
+    JVM_CONSTANT_Dynamic                = 17,
     JVM_CONSTANT_InvokeDynamic          = 18,
     JVM_CONSTANT_ExternalMax            = 18
 };
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java	Fri Sep 08 10:46:46 2017 -0700
@@ -30,6 +30,8 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import jdk.vm.ci.hotspot.HotSpotConstantPool;
+import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 import jdk.vm.ci.meta.ResolvedJavaType;
@@ -87,6 +89,10 @@
         if (specialClasses.stream().filter(s -> s.isAssignableFrom(klass)).findAny().isPresent()) {
             return false;
         }
+        // Skip klass with Condy until Graal is fixed.
+        if (((HotSpotConstantPool)((HotSpotResolvedObjectType) klass).getConstantPool()).hasDynamicConstant()) {
+            return false;
+        }
         return true;
     }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java	Fri Sep 08 10:46:46 2017 -0700
@@ -84,6 +84,7 @@
     public final static int CONSTANT_NameandType = 12;
     public final static int CONSTANT_MethodHandle = 15;
     public final static int CONSTANT_MethodType = 16;
+    public final static int CONSTANT_Dynamic = 17;
     public final static int CONSTANT_InvokeDynamic = 18;
     public final static int CONSTANT_Module = 19;
     public final static int CONSTANT_Package = 20;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Fri Sep 08 10:46:46 2017 -0700
@@ -426,6 +426,7 @@
             case CONSTANT_NameandType:
             case CONSTANT_Integer:
             case CONSTANT_Float:
+            case CONSTANT_Dynamic:
             case CONSTANT_InvokeDynamic:
                 bp = bp + 4;
                 break;
@@ -501,6 +502,7 @@
         case CONSTANT_MethodType:
             skipBytes(3);
             break;
+        case CONSTANT_Dynamic:
         case CONSTANT_InvokeDynamic:
             skipBytes(5);
             break;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java	Fri Sep 08 10:46:46 2017 -0700
@@ -296,7 +296,7 @@
       // change byte-ordering and go via cache
       i = remapInstructionOperandFromCache(which);
     } else {
-      if (getTagAt(which).isInvokeDynamic()) {
+      if (getTagAt(which).isInvokeDynamic() || getTagAt(which).isDynamicConstant()) {
         int poolIndex = invokeDynamicNameAndTypeRefIndexAt(which);
         Assert.that(getTagAt(poolIndex).isNameAndType(), "");
         return poolIndex;
@@ -429,10 +429,10 @@
     return res;
   }
 
-  /** Lookup for multi-operand (InvokeDynamic) entries. */
+  /** Lookup for multi-operand (InvokeDynamic, Dynamic) entries. */
   public short[] getBootstrapSpecifierAt(int i) {
     if (Assert.ASSERTS_ENABLED) {
-      Assert.that(getTagAt(i).isInvokeDynamic(), "Corrupted constant pool");
+      Assert.that(getTagAt(i).isInvokeDynamic() || getTagAt(i).isDynamicConstant(), "Corrupted constant pool");
     }
     int bsmSpec = extractLowShortFromInt(this.getIntAt(i));
     U2Array operands = getOperands();
@@ -468,6 +468,7 @@
     case JVM_CONSTANT_NameAndType:        return "JVM_CONSTANT_NameAndType";
     case JVM_CONSTANT_MethodHandle:       return "JVM_CONSTANT_MethodHandle";
     case JVM_CONSTANT_MethodType:         return "JVM_CONSTANT_MethodType";
+    case JVM_CONSTANT_Dynamic:            return "JVM_CONSTANT_Dynamic";
     case JVM_CONSTANT_InvokeDynamic:      return "JVM_CONSTANT_InvokeDynamic";
     case JVM_CONSTANT_Invalid:            return "JVM_CONSTANT_Invalid";
     case JVM_CONSTANT_UnresolvedClass:    return "JVM_CONSTANT_UnresolvedClass";
@@ -524,6 +525,7 @@
         case JVM_CONSTANT_NameAndType:
         case JVM_CONSTANT_MethodHandle:
         case JVM_CONSTANT_MethodType:
+        case JVM_CONSTANT_Dynamic:
         case JVM_CONSTANT_InvokeDynamic:
           visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true);
           break;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java	Fri Sep 08 10:46:46 2017 -0700
@@ -42,7 +42,7 @@
     public static final int JVM_CONSTANT_NameAndType        = 12;
     public static final int JVM_CONSTANT_MethodHandle       = 15;
     public static final int JVM_CONSTANT_MethodType         = 16;
-    //     static final int JVM_CONSTANT_(unused)           = 17;
+    public static final int JVM_CONSTANT_Dynamic            = 17;
     public static final int JVM_CONSTANT_InvokeDynamic      = 18;
 
     // JVM_CONSTANT_MethodHandle subtypes
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java	Fri Sep 08 10:46:46 2017 -0700
@@ -296,6 +296,18 @@
                      break;
                 }
 
+                case JVM_CONSTANT_Dynamic: {
+                    dos.writeByte(cpConstType);
+                    int value = cpool.getIntAt(ci);
+                    short bsmIndex = (short) extractLowShortFromInt(value);
+                    short nameAndTypeIndex = (short) extractHighShortFromInt(value);
+                    dos.writeShort(bsmIndex);
+                    dos.writeShort(nameAndTypeIndex);
+                    if (DEBUG) debugMessage("CP[" + ci + "] = CONDY bsm = " +
+                                            bsmIndex + ", N&T = " + nameAndTypeIndex);
+                    break;
+                }
+
                 case JVM_CONSTANT_InvokeDynamic: {
                      dos.writeByte(cpConstType);
                      int value = cpool.getIntAt(ci);
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java	Fri Sep 08 10:46:46 2017 -0700
@@ -558,6 +558,12 @@
                buf.cell(Integer.toString(cpool.getIntAt(index)));
                break;
 
+           case JVM_CONSTANT_Dynamic:
+               buf.cell("JVM_CONSTANT_Dynamic");
+               buf.cell(genLowHighShort(cpool.getIntAt(index)) +
+                        genListOfShort(cpool.getBootstrapSpecifierAt(index)));
+             break;
+
             case JVM_CONSTANT_InvokeDynamic:
                buf.cell("JVM_CONSTANT_InvokeDynamic");
                buf.cell(genLowHighShort(cpool.getIntAt(index)) +
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java	Fri Sep 08 10:46:46 2017 -0700
@@ -42,7 +42,7 @@
   private static final int JVM_CONSTANT_NameAndType             = 12;
   private static final int JVM_CONSTANT_MethodHandle            = 15;  // JSR 292
   private static final int JVM_CONSTANT_MethodType              = 16;  // JSR 292
-  //      static final int JVM_CONSTANT_(unused)                = 17;  // JSR 292 early drafts only
+  private static final int JVM_CONSTANT_Dynamic                 = 17;  // JSR 292 early drafts only
   private static final int JVM_CONSTANT_InvokeDynamic           = 18;  // JSR 292
   private static final int JVM_CONSTANT_Invalid                 = 0;   // For bad value initialization
   private static final int JVM_CONSTANT_UnresolvedClass         = 100; // Temporary tag until actual use
@@ -84,6 +84,7 @@
   public boolean isUtf8()             { return tag == JVM_CONSTANT_Utf8; }
   public boolean isMethodHandle()     { return tag == JVM_CONSTANT_MethodHandle; }
   public boolean isMethodType()       { return tag == JVM_CONSTANT_MethodType; }
+  public boolean isDynamicConstant()  { return tag == JVM_CONSTANT_Dynamic; }
   public boolean isInvokeDynamic()    { return tag == JVM_CONSTANT_InvokeDynamic; }
 
   public boolean isInvalid()          { return tag == JVM_CONSTANT_Invalid; }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Fri Sep 08 10:46:46 2017 -0700
@@ -238,6 +238,8 @@
      */
     native HotSpotResolvedJavaMethodImpl lookupMethodInPool(HotSpotConstantPool constantPool, int cpi, byte opcode);
 
+    // TODO resolving JVM_CONSTANT_Dynamic
+
     /**
      * Ensures that the type referenced by the specified {@code JVM_CONSTANT_InvokeDynamic} entry at
      * index {@code cpi} in {@code constantPool} is loaded and initialized.
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java	Fri Sep 08 10:46:46 2017 -0700
@@ -491,6 +491,14 @@
         return UNSAFE.getInt(getMetaspaceConstantPool() + config().constantPoolLengthOffset);
     }
 
+    public boolean hasDynamicConstant() {
+        return (flags() & config().constantPoolHasDynamicConstant) != 0;
+    }
+
+    private int flags() {
+        return UNSAFE.getInt(getMetaspaceConstantPool() + config().constantPoolFlagsOffset);
+    }
+
     @Override
     public Object lookupConstant(int cpi) {
         assert cpi != 0;
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java	Fri Sep 08 10:46:46 2017 -0700
@@ -214,8 +214,10 @@
     final int constantPoolTagsOffset = getFieldOffset("ConstantPool::_tags", Integer.class, "Array<u1>*");
     final int constantPoolHolderOffset = getFieldOffset("ConstantPool::_pool_holder", Integer.class, "InstanceKlass*");
     final int constantPoolLengthOffset = getFieldOffset("ConstantPool::_length", Integer.class, "int");
+    final int constantPoolFlagsOffset = getFieldOffset("ConstantPool::_flags", Integer.class, "int");
 
     final int constantPoolCpCacheIndexTag = getConstant("ConstantPool::CPCACHE_INDEX_TAG", Integer.class);
+    final int constantPoolHasDynamicConstant = getConstant("ConstantPool::_has_dynamic_constant", Integer.class);
 
     final int jvmConstantUtf8 = getConstant("JVM_CONSTANT_Utf8", Integer.class);
     final int jvmConstantInteger = getConstant("JVM_CONSTANT_Integer", Integer.class);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java	Fri Sep 08 10:46:46 2017 -0700
@@ -28,6 +28,7 @@
 import java.util.Map;
 
 import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Dynamic_info;
 import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
 import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
 import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
@@ -331,6 +332,20 @@
         return info;
     }
 
+    public CPInfo visitDynamicConstant(CONSTANT_Dynamic_info info, Map<Object, Object> translations) {
+        CONSTANT_Dynamic_info info2 = (CONSTANT_Dynamic_info) translations.get(info);
+        if (info2 == null) {
+            ConstantPool cp2 = translate(info.cp, translations);
+            if (cp2 == info.cp) {
+                info2 = info;
+            } else {
+                info2 = new CONSTANT_Dynamic_info(cp2, info.bootstrap_method_attr_index, info.name_and_type_index);
+            }
+            translations.put(info, info2);
+        }
+        return info;
+    }
+
     @Override
     public CPInfo visitLong(CONSTANT_Long_info info, Map<Object, Object> translations) {
         CONSTANT_Long_info info2 = (CONSTANT_Long_info) translations.get(info);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java	Fri Sep 08 10:46:46 2017 -0700
@@ -283,6 +283,12 @@
             return 1;
         }
 
+        public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, ClassOutputStream out) {
+            out.writeShort(info.bootstrap_method_attr_index);
+            out.writeShort(info.name_and_type_index);
+            return 1;
+        }
+
         @Override
         public Integer visitLong(CONSTANT_Long_info info, ClassOutputStream out) {
             out.writeLong(info.value);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java	Fri Sep 08 10:46:46 2017 -0700
@@ -116,6 +116,7 @@
     public static final int CONSTANT_NameAndType = 12;
     public static final int CONSTANT_MethodHandle = 15;
     public static final int CONSTANT_MethodType = 16;
+    public static final int CONSTANT_Dynamic = 17;
     public static final int CONSTANT_InvokeDynamic = 18;
     public static final int CONSTANT_Module = 19;
     public static final int CONSTANT_Package = 20;
@@ -198,6 +199,10 @@
                 pool[i] = new CONSTANT_InvokeDynamic_info(this, cr);
                 break;
 
+            case CONSTANT_Dynamic:
+                pool[i] = new CONSTANT_Dynamic_info(this, cr);
+                break;
+
             case CONSTANT_Long:
                 pool[i] = new CONSTANT_Long_info(cr);
                 i++;
@@ -352,6 +357,7 @@
         R visitInteger(CONSTANT_Integer_info info, P p);
         R visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, P p);
         R visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, P p);
+        R visitDynamicConstant(CONSTANT_Dynamic_info info, P p);
         R visitLong(CONSTANT_Long_info info, P p);
         R visitMethodref(CONSTANT_Methodref_info info, P p);
         R visitMethodHandle(CONSTANT_MethodHandle_info info, P p);
@@ -879,6 +885,44 @@
         public final int type_index;
     }
 
+    public static class CONSTANT_Dynamic_info extends CPInfo {
+        CONSTANT_Dynamic_info(ConstantPool cp, ClassReader cr) throws IOException {
+            super(cp);
+            bootstrap_method_attr_index = cr.readUnsignedShort();
+            name_and_type_index = cr.readUnsignedShort();
+        }
+
+        public CONSTANT_Dynamic_info(ConstantPool cp, int bootstrap_method_index, int name_and_type_index) {
+            super(cp);
+            this.bootstrap_method_attr_index = bootstrap_method_index;
+            this.name_and_type_index = name_and_type_index;
+        }
+
+        public int getTag() {
+            return CONSTANT_Dynamic;
+        }
+
+        public int byteLength() {
+            return 5;
+        }
+
+        @Override
+        public String toString() {
+            return "CONSTANT_Dynamic_info[bootstrap_method_index: " + bootstrap_method_attr_index + ", name_and_type_index: " + name_and_type_index + "]";
+        }
+
+        public <R, D> R accept(Visitor<R, D> visitor, D data) {
+            return visitor.visitDynamicConstant(this, data);
+        }
+
+        public CONSTANT_NameAndType_info getNameAndTypeInfo() throws ConstantPoolException {
+            return cp.getNameAndTypeInfo(name_and_type_index);
+        }
+
+        public final int bootstrap_method_attr_index;
+        public final int name_and_type_index;
+    }
+
     public static class CONSTANT_Package_info extends CPInfo {
         CONSTANT_Package_info(ConstantPool cp, ClassReader cr) throws IOException {
             super(cp);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java	Fri Sep 08 10:46:46 2017 -0700
@@ -688,6 +688,11 @@
                 return null;
             }
 
+            @Override
+            public Void visitDynamicConstant(CONSTANT_Dynamic_info info, Void aVoid) {
+                return null;
+            }
+
             public Void visitLong(CONSTANT_Long_info info, Void p) {
                 return null;
             }
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -160,6 +160,11 @@
             return false;
         }
 
+        @Override
+        public Boolean visitDynamicConstant(CONSTANT_Dynamic_info info, ConstantPool constantPool) {
+            return false;
+        }
+
         public Boolean visitLong(CONSTANT_Long_info info, ConstantPool cpool) {
             return false;
         }
--- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java	Fri Sep 08 10:46:46 2017 -0700
@@ -104,6 +104,13 @@
                 return 1;
             }
 
+            public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
+                print("#" + info.bootstrap_method_attr_index + ":#" + info.name_and_type_index);
+                tab();
+                println("// " + stringValue(info));
+                return 1;
+            }
+
             public Integer visitLong(CONSTANT_Long_info info, Void p) {
                 println(stringValue(info));
                 return 2;
@@ -246,6 +253,8 @@
                 return "InterfaceMethod";
             case CONSTANT_InvokeDynamic:
                 return "InvokeDynamic";
+            case CONSTANT_Dynamic:
+                return "Dynamic";
             case CONSTANT_NameAndType:
                 return "NameAndType";
             default:
@@ -346,6 +355,15 @@
             }
         }
 
+        public String visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
+            try {
+                String callee = stringValue(info.getNameAndTypeInfo());
+                return "#" + info.bootstrap_method_attr_index + ":" + callee;
+            } catch (ConstantPoolException e) {
+                return report(e);
+            }
+        }
+
         public String visitLong(CONSTANT_Long_info info, Void p) {
             return info.value + "l";
         }
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java	Fri Sep 08 10:46:46 2017 -0700
@@ -26,6 +26,7 @@
 package com.sun.tools.jdeprscan.scan;
 
 import com.sun.tools.classfile.ConstantPool;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Dynamic_info;
 
 /**
  * A visitor that selects constant pool entries by type and adds
@@ -70,6 +71,10 @@
         return null;
     }
 
+    public Void visitDynamicConstant(CONSTANT_Dynamic_info info, CPEntries p) {
+        return null;
+    }
+
     @Override
     public Void visitLong(ConstantPool.CONSTANT_Long_info info, CPEntries p) {
         return null;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2010, 2017, 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.
+ */
+
+package test.java.lang.invoke.lib;
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.BasicTypeHelper;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.PoolHelper;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+
+import java.io.FileOutputStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static java.lang.invoke.MethodType.fromMethodDescriptorString;
+import static java.lang.invoke.MethodType.methodType;
+
+public class InstructionHelper {
+
+    static final BasicTypeHelper BTH = new BasicTypeHelper();
+
+    static final AtomicInteger COUNT = new AtomicInteger();
+
+    static BasicClassBuilder classBuilder(MethodHandles.Lookup l) {
+        String className = l.lookupClass().getCanonicalName().replace('.', '/') + "$Code_" + COUNT.getAndIncrement();
+        return new BasicClassBuilder(className, 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ));
+    }
+
+    public static MethodHandle invokedynamic(MethodHandles.Lookup l,
+                                      String name, MethodType type,
+                                      String bsmMethodName, MethodType bsmType,
+                                      Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        byte[] byteArray = classBuilder(l)
+                .withMethod("m", type.toMethodDescriptorString(), M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new,
+                                          C -> {
+                                              for (int i = 0; i < type.parameterCount(); i++) {
+                                                  C.load(BTH.tag(cref(type.parameterType(i))), i);
+                                              }
+                                              C.invokedynamic(name, type.toMethodDescriptorString(),
+                                                              csym(l.lookupClass()), bsmMethodName, bsmType.toMethodDescriptorString(),
+                                                              staticArgs);
+                                              C.return_(BTH.tag(cref(type.returnType())));
+                                          }
+                                ))
+                .build();
+        Class<?> gc = l.defineClass(byteArray);
+        return l.findStatic(gc, "m", type);
+    }
+
+    public static MethodHandle ldcMethodHandle(MethodHandles.Lookup l,
+                                        int refKind, Class<?> owner, String name, MethodType type) throws Exception {
+        return ldc(l, MethodHandle.class,
+                   P -> P.putHandle(refKind, csym(owner), name, type.toMethodDescriptorString()));
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, Class<?> type,
+                                                  String bsmMethodName, MethodType bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldcDynamicConstant(l, name, cref(type), bsmMethodName, bsmType.toMethodDescriptorString(), staticArgs);
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, String type,
+                                                  String bsmMethodName, String bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldc(l, type,
+                   P -> P.putDynamicConstant(name, type,
+                                             csym(l.lookupClass()), bsmMethodName, bsmType,
+                                             staticArgs));
+    }
+
+    public static MethodHandle ldc(MethodHandles.Lookup l,
+                            Class<?> type,
+                            Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
+        return ldc(l, cref(type), poolFunc);
+    }
+
+    public static MethodHandle ldc(MethodHandles.Lookup l,
+                                   String type,
+                                   Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
+        String methodType = "()" + type;
+        byte[] byteArray = classBuilder(l)
+                .withMethod("m", "()" + type, M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new,
+                                          C -> {
+                                              C.ldc(null, (P, v) -> poolFunc.apply(P));
+                                              C.return_(BTH.tag(type));
+                                          }
+                                ))
+                .build();
+        Class<?> gc = l.defineClass(byteArray);
+        return l.findStatic(gc, "m", fromMethodDescriptorString(methodType, l.lookupClass().getClassLoader()));
+    }
+
+    public static String csym(Class<?> c) {
+        return c.getCanonicalName().replace('.', '/');
+    }
+
+    public static String cref(Class<?> c) {
+        return methodType(c).toMethodDescriptorString().substring(2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test bootstrap methods throwing an exception
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng BootstrapMethodJumboArgsTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 BootstrapMethodJumboArgsTest
+ */
+
+import jdk.experimental.bytecode.PoolHelper;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.stream.IntStream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class BootstrapMethodJumboArgsTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+
+    static Object bsmZero(MethodHandles.Lookup l, String name, Object type,
+                      Object... args) {
+        Object[] a = args.clone();
+        if (type instanceof MethodType) {
+            return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
+        }
+        else {
+            return a;
+        }
+    }
+
+    static Object bsmOne(MethodHandles.Lookup l, String name, Object type,
+                         Object first, Object... args) {
+        Object[] a = new Object[args.length + 1];
+        a[0] = first;
+        System.arraycopy(args, 0, a, 1, args.length);
+        if (type instanceof MethodType) {
+            return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
+        }
+        else {
+            return a;
+        }
+    }
+
+    static Object bsmTwo(MethodHandles.Lookup l, String name, Object type,
+                         Object first, Object second, Object... args) {
+        Object[] a = new Object[args.length + 2];
+        a[0] = first;
+        a[1] = second;
+        System.arraycopy(args, 0, a, 2, args.length);
+        if (type instanceof MethodType) {
+            return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
+        }
+        else {
+            return a;
+        }
+    }
+
+    static void manyStaticStrings(String[] args, PoolHelper.StaticArgListBuilder<String, String, byte[]> staticArgs) {
+        for (String s : args) {
+            staticArgs.add(s);
+        }
+    }
+
+    @Test
+    public void testCondyWithJumboArgs() throws Throwable {
+        String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object[].class,
+                    "bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+    }
+
+    @Test
+    public void testIndyWithJumboArgs() throws Throwable {
+        String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
+
+        {
+            MethodHandle mh = InstructionHelper.invokedynamic(
+                    L, "name", methodType(Object[].class),
+                    "bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.invokedynamic(
+                    L, "name", methodType(Object[].class),
+                    "bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.invokedynamic(
+                    L, "name", methodType(Object[].class),
+                    "bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
+                    S -> manyStaticStrings(expected, S));
+
+            Object[] actual = (Object[]) mh.invoke();
+            Assert.assertEquals(actual, expected);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMException.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test bootstrap methods throwing an exception
+ * @library /lib/testlibrary/bytecode  /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyBSMException
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMException
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Constructor;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyBSMException {
+
+    @Test
+    public void testThrowable() {
+        test("Throwable", BootstrapMethodError.class, Throwable.class);
+    }
+
+    @Test
+    public void testError() {
+        test("Error", Error.class);
+    }
+
+    @Test
+    public void testBootstrapMethodError() {
+        test("BootstrapMethodError", BootstrapMethodError.class);
+    }
+
+    @Test
+    public void testRuntimeException() {
+        test("RuntimeException", BootstrapMethodError.class, RuntimeException.class);
+    }
+
+    @Test
+    public void testException() throws Throwable {
+        test("Exception", BootstrapMethodError.class, Exception.class);
+    }
+
+    static void test(String message, Class<? extends Throwable>... ts) {
+        MethodHandle mh = thrower(message, ts[ts.length - 1]);
+        Throwable caught = null;
+        try {
+            mh.invoke();
+        }
+        catch (Throwable t) {
+            caught = t;
+        }
+
+        if (caught == null) {
+            Assert.fail("Throwable expected");
+        }
+
+        String actualMessage = null;
+        for (int i = 0; i < ts.length; i++) {
+            actualMessage = caught.getMessage();
+            Assert.assertNotNull(caught);
+            Assert.assertTrue(ts[i].isAssignableFrom(caught.getClass()));
+            caught = caught.getCause();
+        }
+
+        Assert.assertEquals(actualMessage, message);
+    }
+
+    static Throwable throwingBsm(MethodHandles.Lookup l, String name, Class<Throwable> type) throws Throwable {
+        Throwable t;
+        try {
+            Constructor<Throwable> c = type.getDeclaredConstructor(String.class);
+            t = c.newInstance(name);
+        }
+        catch (Exception e) {
+            throw new InternalError();
+        }
+        throw t;
+    }
+
+    static MethodHandle thrower(String message, Class<? extends Throwable> t) {
+        try {
+            return InstructionHelper.ldcDynamicConstant(
+                    MethodHandles.lookup(),
+                    message, t,
+                    "throwingBsm", methodType(Throwable.class, MethodHandles.Lookup.class, String.class, Class.class),
+                    S -> { });
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test basic invocation of bootstrap methods
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyBSMInvocation
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMInvocation
+ */
+
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.util.Collections;
+import java.util.stream.IntStream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyBSMInvocation {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+
+    @Test
+    public void testNonexistent() throws Throwable {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "name", Object.class,
+                "bsm", methodType(Object.class),
+                S -> {});
+
+        try {
+            mh.invoke();
+            Assert.fail("NoSuchMethodError expected to be thrown");
+        } catch (NoSuchMethodError e) {
+        }
+    }
+
+
+    public static Object _bsm() {
+        return "0";
+    }
+
+    public static Object _bsm(Object a1) {
+        return "0";
+    }
+
+    // Note: when pull mode is supported for a BSM this test case
+    //       will fail and must be removed
+    public static Object _bsm(Object a1, Object a2) {
+        return "0";
+    }
+
+    @Test
+    public void testWrongArity() throws Throwable {
+        for (int i = 0; i < 3; i++) {
+            final int n = i;
+            MethodType mt = methodType(Object.class)
+                    .appendParameterTypes(Collections.nCopies(n, Object.class));
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "_bsm", mt,
+                    S -> IntStream.range(0, n).forEach(S::add)
+            );
+
+            try {
+                Object r = mh.invoke();
+                Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n);
+            } catch (BootstrapMethodError e) {
+                Throwable t = e.getCause();
+                Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
+            }
+        }
+    }
+
+
+    public static Object _bsm(String[] ss) {
+        return "0";
+    }
+
+    public static Object _bsm(String a1, String a2, String a3) {
+        return "0";
+    }
+
+    @Test
+    public void testWrongSignature() throws Throwable {
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "_bsm", methodType(Object.class, String[].class),
+                    S -> {}
+            );
+
+            try {
+                Object r = mh.invoke();
+                Assert.fail("BootstrapMethodError expected to be thrown");
+            }
+            catch (BootstrapMethodError e) {
+                Throwable t = e.getCause();
+                Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
+            }
+        }
+
+        {
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "_bsm", methodType(Object.class, String.class, String.class, String.class),
+                    S -> {}
+            );
+
+            try {
+                Object r = mh.invoke();
+                Assert.fail("BootstrapMethodError expected to be thrown");
+            }
+            catch (BootstrapMethodError e) {
+                Throwable t = e.getCause();
+                Assert.assertTrue(ClassCastException.class.isAssignableFrom(t.getClass()));
+            }
+        }
+    }
+
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
+        return "0";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1) {
+        assertAll(a1);
+        return "1";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2) {
+        assertAll(a1, a2);
+        return "2";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3) {
+        assertAll(a1, a2, a3);
+        return "3";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4) {
+        assertAll(a1, a2, a3, a4);
+        return "4";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5) {
+        assertAll(a1, a2, a3, a4, a5);
+        return "5";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
+        assertAll(a1, a2, a3, a4, a5, a6);
+        return "6";
+    }
+
+    public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
+                             Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+        assertAll(a1, a2, a3, a4, a5, a6, a7);
+        return "7";
+    }
+
+    static void assertAll(Object... as) {
+        for (int i = 0; i < as.length; i++) {
+            Assert.assertEquals(as[i], i);
+        }
+    }
+
+    @Test
+    public void testArity() throws Throwable {
+        for (int i = 0; i < 8; i++) {
+            final int n = i;
+            MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
+                    .appendParameterTypes(Collections.nCopies(n, Object.class));
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "bsm", mt,
+                    S -> IntStream.range(0, n).forEach(S::add)
+                    );
+
+            Object r = mh.invoke();
+            Assert.assertEquals(r, Integer.toString(n));
+        }
+    }
+
+    @Test
+    public void testWrongNumberOfStaticArguments() throws Throwable {
+        for (int i = 1; i < 8; i++) {
+            final int n = i;
+            MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
+                    .appendParameterTypes(Collections.nCopies(n, Object.class));
+            MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                    L, "name", Object.class,
+                    "bsm", mt,
+                    S -> IntStream.range(0, n - 1).forEach(S::add)
+            );
+
+            try {
+                Object r = mh.invoke();
+                Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n);
+            } catch (BootstrapMethodError e) {
+                Throwable t = e.getCause();
+                Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test invalid name in name and type
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyBSMValidationTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMValidationTest
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyBSMValidationTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+    static final String BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class)
+            .toMethodDescriptorString();
+
+    @DataProvider
+    public Object[][] invalidSignaturesProvider() throws Exception {
+        return Stream.of(BSM_TYPE.replace("(", ""),
+                         BSM_TYPE.replace(")", ""),
+                         BSM_TYPE.replace("(", "").replace(")", ""),
+                         BSM_TYPE.replace(";)", ")"),
+                         BSM_TYPE.replace(";", ""))
+                .map(e -> new Object[]{e}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "invalidSignaturesProvider", expectedExceptions = ClassFormatError.class)
+    public void testInvalidBSMSignature(String sig) throws Exception {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "name", "Ljava/lang/Object;",
+                "bsm", sig,
+                S -> {
+                });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test invalid name in name and type
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyNameValidationTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNameValidationTest
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyNameValidationTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+    static final MethodType BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class);
+
+    @DataProvider
+    public Object[][] invalidNamesProvider() throws Exception {
+        return Stream.of("",
+                         ".",
+                         ";",
+                         "[",
+                         "/")
+                .map(e -> new Object[]{e}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "invalidNamesProvider", expectedExceptions = java.lang.ClassFormatError.class)
+    public void testInvalidNames(String name) throws Exception {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, name, Object.class,
+                "bsm", BSM_TYPE,
+                S -> {
+                });
+    }
+
+    @DataProvider
+    public Object[][] validNamesProvider() throws Exception {
+        return Stream.of("<clinit>",
+                         "<init>",
+                         "<foo>")
+                .map(e -> new Object[]{e}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "validNamesProvider")
+    public void testValidNames(String name) throws Exception {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, name, Object.class,
+                "bsm", BSM_TYPE,
+                S -> {
+                });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test nested dynamic constant declarations that are recursive
+ * @library /lib/testlibrary/bytecode
+ * @build jdk.experimental.bytecode.BasicClassBuilder
+ * @run testng CondyNestedTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNestedTest
+ */
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.MacroCodeBuilder;
+import jdk.experimental.bytecode.PoolHelper;
+import jdk.experimental.bytecode.TypeTag;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class CondyNestedTest {
+
+    /**
+     * NOTE: This is a temporary solution until asmtools is updated to support
+     * dynamic constant and jtreg is updated to include a new version of
+     * asmtools.
+     *
+     * These are the class file bytes for a class named CondyNestedTest_Code
+     * whose bytes are 1) generated by the generator() function;  2) the bytes
+     * converted to a jcod file:
+     *
+     * java -jar asmtools.jar jdec CondyNestedTest_Code.class >
+     * CondyNestedTest_Code.jcod
+     *
+     * which was then edited so that dynamic constant declarations are
+     * recursive both for an ldc or invokedynamic (specifically declaring a
+     * BSM+attributes whose static argument is a dynamic constant
+     * that refers to the same BSM+attributes); 3) the jcod file is converted
+     * back to a class file:
+     *
+     * java -jar asmtools.jar jdis [options] CondyNestedTest_Code.jcod
+     *
+     * ;and finally 4) the newly generated class file bytes are converted to
+     * a base64 representation and embedded in the following String.
+     */
+    static final String CLASS_CondyNestedTest_Code =
+            "yv66vgAAADcAQgEAEGphdmEvbGFuZy9PYmplY3QHAAEBAAY8aW5pdD4BAAMoKVYMAAMABAoAAgAFAQAE" +
+            "Q29kZQEAEGphdmEvbGFuZy9TdHJpbmcHAAgBAAZpbnRlcm4BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwwA" +
+            "CgALCgAJAAwBABNjb25keV9ic21fY29uZHlfYnNtCAAOAQAUQ29uZHlOZXN0ZWRUZXN0X0NvZGUHABAB" +
+            "ABQoKUxqYXZhL2xhbmcvT2JqZWN0OwwADgASCgARABMBABZpbmR5X2JzbUluZHlfY29uZHlfYnNtCAAV" +
+            "DAAVABIKABEAFwEAEmluZHlfYnNtX2NvbmR5X2JzbQgAGQwAGQASCgARABsBAA1TdGFja01hcFRhYmxl" +
+            "AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBABtqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5" +
+            "cGUHACABACFqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGUHACIBAB5qYXZhL2xhbmcvaW52" +
+            "b2tlL01ldGhvZEhhbmRsZXMHACQBAAhjb25zdGFudAEARChMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xh" +
+            "bmcvT2JqZWN0OylMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7DAAmACcKACUAKAEAIihMamF2" +
+            "YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7KVYMAAMAKgoAIwArAQADYnNtAQBxKExqYXZhL2xhbmcv" +
+            "aW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2Jq" +
+            "ZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAdic21JbmR5AQB6KExqYXZh" +
+            "L2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xh" +
+            "bmcvT2JqZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsBAAlE" +
+            "VU1NWV9BUkcIADEMAC0ALgoAEQAzDwYANAEABG5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsMADYANxEA" +
+            "AAA4EQABADgMAC8AMAoAEQA7DwYAPAwANgALEgACAD4SAAEAPgEAEEJvb3RzdHJhcE1ldGhvZHMAAAAR" +
+            "AAIAAAAAAAcAAQADAAQAAQAHAAAAEQABAAEAAAAFKrcABrEAAAAAAAkAHgAfAAEABwAAAEIAAgACAAAA" +
+            "JioDMrYADUwrEg+mAAe4ABSxKxIWpgAHuAAYsSsSGqYAB7gAHLGxAAAAAQAdAAAACgAD/AARBwAJCQkA" +
+            "CQAtAC4AAQAHAAAALQAEAAQAAAAYLMEAIQOfABG7ACNZEgkruAAptwAssCuwAAAAAQAdAAAAAwABFgAJ" +
+            "AC8AMAABAAcAAAAaAAQABAAAAA67ACNZEgkruAAptwAssAAAAAAACQAOABIAAQAHAAAADwABAAAAAAAD" +
+            "EjqwAAAAAAAJABUAEgABAAcAAAASAAEAAAAAAAa6AD8AALAAAAAAAAkAGQASAAEABwAAABIAAQAAAAAA" +
+            "BroAQAAAsAAAAAAAAQBBAAAAFAADADUAAQAyADUAAQA6AD0AAQA6";
+
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+    static final Class[] THROWABLES = {InvocationTargetException.class, StackOverflowError.class};
+
+    Class<?> c;
+
+    public static byte[] generator() throws Exception {
+        String genClassName = L.lookupClass().getSimpleName() + "_Code";
+        String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
+        String bsmIndyDescriptor = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
+
+        byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ))
+                .withMethod("main", "([Ljava/lang/String;)V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C -> {
+                                    C.aload_0().iconst_0().aaload();
+                                    C.invokevirtual("java/lang/String", "intern", "()Ljava/lang/String;", false);
+                                    C.astore_1();
+
+                                    C.aload_1();
+                                    C.ldc("condy_bsm_condy_bsm");
+                                    C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE1");
+                                    C.invokestatic(genClassName, "condy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
+
+                                    C.label("CASE1");
+                                    C.aload_1();
+                                    C.ldc("indy_bsmIndy_condy_bsm");
+                                    C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE2");
+                                    C.invokestatic(genClassName, "indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", false).return_();
+
+                                    C.label("CASE2");
+                                    C.aload_1();
+                                    C.ldc("indy_bsm_condy_bsm");
+                                    C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE3");
+                                    C.invokestatic(genClassName, "indy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
+
+                                    C.label("CASE3");
+                                    C.return_();
+                                }))
+                .withMethod("bsm", bsmDescriptor, M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C -> {
+                                    C.aload_2();
+                                    C.instanceof_("java/lang/invoke/MethodType");
+                                    C.iconst_0();
+                                    C.ifcmp(TypeTag.I, MacroCodeBuilder.CondKind.EQ, "CONDY");
+                                    C.new_("java/lang/invoke/ConstantCallSite").dup();
+                                    C.ldc("java/lang/String", PoolHelper::putClass);
+                                    C.aload_1();
+                                    C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
+                                    C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
+                                    C.areturn();
+                                    C.label("CONDY");
+                                    C.aload_1().areturn();
+                                }))
+                .withMethod("bsmIndy", bsmIndyDescriptor, M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C -> {
+                                    C.new_("java/lang/invoke/ConstantCallSite").dup();
+                                    C.ldc("java/lang/String", PoolHelper::putClass);
+                                    C.aload_1();
+                                    C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
+                                    C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
+                                    C.areturn();
+                                }))
+                .withMethod("condy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
+                                              S -> S.add(null, (P, v) -> {
+                                                  return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
+                                                                              S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
+                                              }))
+                                                .areturn()))
+                .withMethod("indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsmIndy", bsmIndyDescriptor,
+                                                        S -> S.add(null, (P, v) -> {
+                                                            return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
+                                                                                        S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
+                                                        }))
+                                                .areturn()))
+                .withMethod("indy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
+                                                        S -> S.add(null, (P, v) -> {
+                                                            return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
+                                                                                        S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
+                                                        }))
+                                                .areturn()))
+                .build();
+
+        File f = new File(genClassName + ".class");
+        if (f.getParentFile() != null) {
+            f.getParentFile().mkdirs();
+        }
+        new FileOutputStream(f).write(byteArray);
+        return byteArray;
+
+    }
+
+    static void test(Method m, Class<? extends Throwable>... ts) {
+        Throwable caught = null;
+        try {
+            m.invoke(null);
+        }
+        catch (Throwable t) {
+            caught = t;
+        }
+
+        if (caught == null) {
+            Assert.fail("Throwable expected");
+        }
+
+        String actualMessage = null;
+        for (int i = 0; i < ts.length; i++) {
+            actualMessage = caught.getMessage();
+            Assert.assertNotNull(caught);
+            Assert.assertTrue(ts[i].isAssignableFrom(caught.getClass()));
+            caught = caught.getCause();
+        }
+    }
+
+    @BeforeClass
+    public void generateClass() throws Exception {
+        byte[] ba = Base64.getDecoder().decode(CLASS_CondyNestedTest_Code);
+        ClassLoader l = new ClassLoader(CondyReturnPrimitiveTest.class.getClassLoader()) {
+            @Override
+            protected Class<?> findClass(String name) throws ClassNotFoundException {
+                if (name == "CondyNestedTest_Code") {
+                    return defineClass(name, ba, 0, ba.length);
+                }
+                return null;
+            }
+        };
+
+        c = l.loadClass("CondyNestedTest_Code");
+    }
+
+    /**
+     * Testing an ldc of a dynamic constant, C say, with a BSM whose static
+     * argument is C.
+     */
+    @Test
+    public void testCondyBsmCondyBsm() throws Exception {
+        test("condy_bsm_condy_bsm", THROWABLES);
+    }
+
+    /**
+     * Testing an invokedynamic with a BSM whose static argument is a constant
+     * dynamic, C say, with a BSM whose static argument is C.
+     */
+    @Test
+    public void testIndyBsmIndyCondyBsm() throws Exception {
+        test("indy_bsmIndy_condy_bsm", THROWABLES);
+    }
+
+    /**
+     * Testing an invokedynamic with a BSM, B say, whose static argument is
+     * a dynamic constant, C say, that uses BSM B.
+     */
+    @Test
+    public void testIndyBsmCondyBsm() throws Exception {
+        test("indy_bsm_condy_bsm", THROWABLES);
+    }
+
+    void test(String methodName, Class<? extends Throwable>... ts) throws Exception {
+        Method m = c.getMethod(methodName);
+        m.setAccessible(true);
+        test(m, ts);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,264 @@
+class CondyNestedTest_Code {
+  0xCAFEBABE;
+  0; // minor version
+  55; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Utf8 "java/lang/Object"; // #1    
+    class #1; // #2    
+    Utf8 "<init>"; // #3    
+    Utf8 "()V"; // #4    
+    NameAndType #3 #4; // #5    
+    Method #2 #5; // #6    
+    Utf8 "Code"; // #7    
+    Utf8 "java/lang/String"; // #8    
+    class #8; // #9    
+    Utf8 "intern"; // #10    
+    Utf8 "()Ljava/lang/String;"; // #11    
+    NameAndType #10 #11; // #12    
+    Method #9 #12; // #13    
+    Utf8 "condy_bsm_condy_bsm"; // #14    
+    String #14; // #15    
+    Utf8 "CondyNestedTest_Code"; // #16    
+    class #16; // #17    
+    Utf8 "()Ljava/lang/Object;"; // #18    
+    NameAndType #14 #18; // #19    
+    Method #17 #19; // #20    
+    Utf8 "indy_bsmIndy_condy_bsm"; // #21    
+    String #21; // #22    
+    NameAndType #21 #18; // #23    
+    Method #17 #23; // #24    
+    Utf8 "indy_bsm_condy_bsm"; // #25    
+    String #25; // #26    
+    NameAndType #25 #18; // #27    
+    Method #17 #27; // #28    
+    Utf8 "StackMapTable"; // #29    
+    Utf8 "main"; // #30    
+    Utf8 "([Ljava/lang/String;)V"; // #31    
+    Utf8 "java/lang/invoke/MethodType"; // #32    
+    class #32; // #33    
+    Utf8 "java/lang/invoke/ConstantCallSite"; // #34    
+    class #34; // #35    
+    Utf8 "java/lang/invoke/MethodHandles"; // #36    
+    class #36; // #37    
+    Utf8 "constant"; // #38    
+    Utf8 "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;"; // #39    
+    NameAndType #38 #39; // #40    
+    Method #37 #40; // #41    
+    Utf8 "(Ljava/lang/invoke/MethodHandle;)V"; // #42    
+    NameAndType #3 #42; // #43    
+    Method #35 #43; // #44    
+    Utf8 "bsm"; // #45    
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; // #46    
+    Utf8 "bsmIndy"; // #47    
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; // #48    
+    Utf8 "DUMMY_ARG"; // #49    
+    String #49; // #50    
+    NameAndType #45 #46; // #51    
+    Method #17 #51; // #52    
+    MethodHandle 6b #52; // #53    
+    Utf8 "name"; // #54    
+    Utf8 "Ljava/lang/String;"; // #55    
+    NameAndType #54 #55; // #56    
+    ConstantDynamic 0s #56; // #57    
+    ConstantDynamic 1s #56; // #58    
+    NameAndType #47 #48; // #59    
+    Method #17 #59; // #60    
+    MethodHandle 6b #60; // #61    
+    NameAndType #54 #11; // #62    
+    InvokeDynamic 2s #62; // #63    
+    InvokeDynamic 1s #62; // #64    
+    Utf8 "BootstrapMethods"; // #65    
+  } // Constant Pool
+
+  0x0000; // access
+  #17;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // fields
+  } // fields
+
+  [] { // methods
+    { // Member
+      0x0001; // access
+      #3; // name_cpx
+      #4; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70006B1;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #30; // name_cpx
+      #31; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          2; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2A0332B6000D4C2B;
+            0x120FA60007B80014;
+            0xB12B1216A60007B8;
+            0x0018B12B121AA600;
+            0x07B8001CB1B1;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#29) { // StackMapTable
+              [] { // 
+                252b, 17, []z{O,9}; // append_frame 1
+                9b; // same_frame
+                9b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #45; // name_cpx
+      #46; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          4; // max_stack
+          4; // max_locals
+          Bytes[]{
+            0x2CC10021039F0011;
+            0xBB00235912092BB8;
+            0x0029B7002CB02BB0;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#29) { // StackMapTable
+              [] { // 
+                22b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #47; // name_cpx
+      #48; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          4; // max_stack
+          4; // max_locals
+          Bytes[]{
+            0xBB00235912092BB8;
+            0x0029B7002CB0;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #14; // name_cpx
+      #18; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0x123AB0;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #21; // name_cpx
+      #18; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0xBA003F0000B0;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member
+      0x0009; // access
+      #25; // name_cpx
+      #18; // sig_cpx
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0xBA00400000B0;
+          };
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [] { // Attributes
+    Attr(#65) { // BootstrapMethods
+      [] { // bootstrap_methods
+        {  //  bootstrap_method
+          #53; // bootstrap_method_ref
+          [] { // bootstrap_arguments
+            #50;
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+        ;
+        {  //  bootstrap_method
+          #53; // bootstrap_method_ref
+          [] { // bootstrap_arguments
+            #58;
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+        ;
+        {  //  bootstrap_method
+          #61; // bootstrap_method_ref
+          [] { // bootstrap_arguments
+            #58;
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class CondyNestedTest_Code
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186211
+ * @summary Test basic invocation of multiple ldc's of the same dynamic constant that fail resolution
+ * @requires os.arch == "x86_64"
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder
+ * @run testng CondyRepeatFailedResolution
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution
+ */
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@Test
+public class CondyRepeatFailedResolution {
+    // Counter used to determine if a given BSM is invoked more than once
+    static int bsm_called = 0;
+
+    // Generated class with methods containing condy ldc
+    Class<?> gc;
+
+    // Bootstrap method used to represent primitive values
+    // that cannot be represented directly in the constant pool,
+    // such as byte, and for completeness of testing primitive values
+    // that can be represented directly, such as double or long that
+    // take two slots
+    public static Object intConversion(MethodHandles.Lookup l,
+                                       String constantName,
+                                       Class<?> constantType,
+                                       int value) throws Throwable {
+        ++bsm_called;
+        // replace constantName with a bogus value to trigger failed resolution
+        constantName = "Foo";
+
+        switch (constantName) {
+            case "B":
+                return (byte) value;
+            case "C":
+                return (char) value;
+            case "D":
+                return (double) value;
+            case "F":
+                return (float) value;
+            case "I":
+                return value;
+            case "J":
+                return (long) value;
+            case "S":
+                return (short) value;
+            case "Z":
+                return value > 0;
+            case "nullRef":
+                return null;
+            case "string":
+                return "string";
+            case "stringArray":
+                return new String[]{"string", "string"};
+            default:
+                throw new BootstrapMethodError("Failure to generate a dynamic constant");
+        }
+    }
+
+    @BeforeClass
+    public void generateClass() throws Exception {
+        String genClassName = CondyRepeatFailedResolution.class.getSimpleName() + "$Code";
+        String bsmClassName = CondyRepeatFailedResolution.class.getCanonicalName().replace('.', '/');
+        String bsmMethodName = "intConversion";
+        String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
+                                                     String.class, Class.class, int.class).toMethodDescriptorString();
+
+        byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ))
+                .withMethod("B", "()B", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Byte.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("C", "()C", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Character.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("D", "()D", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .dreturn()
+                                ))
+                .withMethod("D_AsType", "()D", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .dreturn()
+                                ))
+                .withMethod("F", "()F", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .freturn()
+                                ))
+                .withMethod("F_AsType", "()F", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .freturn()
+                                ))
+                .withMethod("I", "()I", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("J", "()J", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .lreturn()
+                                ))
+                .withMethod("J_AsType", "()J", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .lreturn()
+                                ))
+                .withMethod("S", "()S", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Short.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("Z_F", "()Z", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(0))
+                                                .ireturn()
+                                ))
+                .withMethod("Z_T", "()Z", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(1))
+                                                .ireturn()
+                                ))
+                .withMethod("null", "()Ljava/lang/Object;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .withMethod("string", "()Ljava/lang/String;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .withMethod("stringArray", "()[Ljava/lang/String;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .build();
+
+        // For debugging purposes
+        new FileOutputStream(new File(genClassName + ".class")).write(byteArray);
+
+        gc = MethodHandles.lookup().defineClass(byteArray);
+    }
+
+    @Test
+    public void testPrimitives() throws Exception {
+        testConstants();
+    }
+
+    @Test
+    public void testRefs() throws Exception {
+        testConstant("string", "string");
+        testConstant("stringArray", new String[]{"string", "string"});
+    }
+
+    void testConstants() throws Exception {
+        // Note: for the _asType methods the BSM returns an int which is
+        // then converted by an asType transformation
+
+        testConstant("B", Byte.MAX_VALUE);
+        testConstant("C", Character.MAX_VALUE);
+        testConstant("D", (double) Integer.MAX_VALUE);
+        testConstant("D_AsType", (double) Integer.MAX_VALUE);
+        testConstant("F", (float) Integer.MAX_VALUE);
+        testConstant("F_AsType", (float) Integer.MAX_VALUE);
+        testConstant("I", Integer.MAX_VALUE);
+        testConstant("J", (long) Integer.MAX_VALUE);
+        testConstant("J_AsType", (long) Integer.MAX_VALUE);
+        testConstant("S", Short.MAX_VALUE);
+        testConstant("Z_F", false);
+        testConstant("Z_T", true);
+        testConstant("null", null);
+    }
+
+    void testConstant(String name, Object expected) throws Exception {
+        Method m = gc.getDeclaredMethod(name);
+
+        bsm_called = 0;
+        try {
+            Object r1 = m.invoke(null);
+            Assert.fail("InvocationTargetException expected to be thrown after first invocation");
+        } catch (InvocationTargetException e1) {
+            // bsm_called should have been incremented prior to the exception
+            Assert.assertEquals(bsm_called, 1);
+            Assert.assertTrue(e1.getCause() instanceof BootstrapMethodError);
+            // Try invoking method again to ensure that the bootstrap
+            // method is not invoked twice and a resolution failure
+            // results.
+            try {
+                Object r2 = m.invoke(null);
+                Assert.fail("InvocationTargetException expected to be thrown after second invocation");
+            } catch (InvocationTargetException e2) {
+                // bsm_called should remain at 1 since the bootstrap
+                // method should not have been invoked.
+                Assert.assertEquals(bsm_called, 1);
+                Assert.assertTrue(e2.getCause() instanceof BootstrapMethodError);
+            } catch (Throwable t2) {
+                Assert.fail("InvocationTargetException expected to be thrown");
+            }
+        } catch (Throwable t1) {
+                Assert.fail("InvocationTargetException expected to be thrown");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test for condy BSMs returning primitive values or null
+ * @requires os.arch == "x86_64"
+ * @library /lib/testlibrary/bytecode
+ * @build jdk.experimental.bytecode.BasicClassBuilder
+ * @run testng CondyReturnPrimitiveTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyReturnPrimitiveTest
+ */
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Test
+public class CondyReturnPrimitiveTest {
+    // Counter for number of BSM calls
+    // Use of an AtomicInteger is not strictly necessary in this test
+    // since the BSM is not be called concurrently, but in general
+    // a BSM can be called concurrently for linking different or the *same*
+    // constant so care should be taken if a BSM operates on shared state
+    static final AtomicInteger callCount = new AtomicInteger();
+    // Generated class with methods containing condy ldc
+    Class<?> gc;
+
+    // Bootstrap method used to represent primitive values
+    // that cannot be represented directly in the constant pool,
+    // such as byte, and for completeness of testing primitive values
+    // that can be represented directly, such as double or long that
+    // take two slots
+    public static Object intConversion(MethodHandles.Lookup l,
+                                       String constantName,
+                                       Class<?> constantType,
+                                       int value) {
+        callCount.getAndIncrement();
+
+        switch (constantName) {
+            case "B":
+                return (byte) value;
+            case "C":
+                return (char) value;
+            case "D":
+                return (double) value;
+            case "F":
+                return (float) value;
+            case "I":
+                return value;
+            case "J":
+                return (long) value;
+            case "S":
+                return (short) value;
+            case "Z":
+                return value > 0;
+            case "nullRef":
+                return null;
+            case "string":
+                return "string";
+            case "stringArray":
+                return new String[]{"string", "string"};
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @BeforeClass
+    public void generateClass() throws Exception {
+        String genClassName = CondyReturnPrimitiveTest.class.getSimpleName() + "$Code";
+        String bsmClassName = CondyReturnPrimitiveTest.class.getCanonicalName().replace('.', '/');
+        String bsmMethodName = "intConversion";
+        String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
+                                                     String.class, Class.class, int.class).toMethodDescriptorString();
+
+        byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ))
+                .withMethod("B", "()B", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Byte.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("C", "()C", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Character.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("D", "()D", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .dreturn()
+                                ))
+                .withMethod("D_AsType", "()D", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .dreturn()
+                                ))
+                .withMethod("F", "()F", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .freturn()
+                                ))
+                .withMethod("F_AsType", "()F", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .freturn()
+                                ))
+                .withMethod("I", "()I", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("J", "()J", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .lreturn()
+                                ))
+                .withMethod("J_AsType", "()J", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .lreturn()
+                                ))
+                .withMethod("S", "()S", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Short.MAX_VALUE))
+                                                .ireturn()
+                                ))
+                .withMethod("Z_F", "()Z", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(0))
+                                                .ireturn()
+                                ))
+                .withMethod("Z_T", "()Z", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(1))
+                                                .ireturn()
+                                ))
+                .withMethod("null", "()Ljava/lang/Object;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .withMethod("string", "()Ljava/lang/String;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .withMethod("stringArray", "()[Ljava/lang/String;", M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
+                                              S -> S.add(Integer.MAX_VALUE))
+                                                .areturn()
+                                ))
+                .build();
+
+        // For debugging purposes
+        new FileOutputStream(new File(genClassName + ".class")).write(byteArray);
+
+        gc = MethodHandles.lookup().defineClass(byteArray);
+    }
+
+    @Test
+    public void testPrimitives() throws Exception {
+        testConstants();
+        int expectedCallCount = callCount.get();
+
+        // Ensure when run a second time that the bootstrap method is not
+        // invoked and the constants are cached
+        testConstants();
+        Assert.assertEquals(callCount.get(), expectedCallCount);
+    }
+
+    @Test
+    public void testRefs() throws Exception {
+        testConstant("string", "string");
+        testConstant("stringArray", new String[]{"string", "string"});
+    }
+
+    void testConstants() throws Exception {
+        // Note: for the _asType methods the BSM returns an int which is
+        // then converted by an asType transformation
+
+        testConstant("B", Byte.MAX_VALUE);
+        testConstant("C", Character.MAX_VALUE);
+        testConstant("D", (double) Integer.MAX_VALUE);
+        testConstant("D_AsType", (double) Integer.MAX_VALUE);
+        testConstant("F", (float) Integer.MAX_VALUE);
+        testConstant("F_AsType", (float) Integer.MAX_VALUE);
+        testConstant("I", Integer.MAX_VALUE);
+        testConstant("J", (long) Integer.MAX_VALUE);
+        testConstant("J_AsType", (long) Integer.MAX_VALUE);
+        testConstant("S", Short.MAX_VALUE);
+        testConstant("Z_F", false);
+        testConstant("Z_T", true);
+        testConstant("null", null);
+    }
+
+    void testConstant(String name, Object expected) throws Exception {
+        Method m = gc.getDeclaredMethod(name);
+        Assert.assertEquals(m.invoke(null), expected);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test bootstrap arguments for condy
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyStaticArgumentsTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyStaticArgumentsTest
+ */
+
+import jdk.experimental.bytecode.PoolHelper;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.StringJoiner;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyStaticArgumentsTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+    static class BSMInfo {
+        final String methodName;
+        final MethodHandle handle;
+        final String descriptor;
+
+        BSMInfo(String name) {
+            methodName = name;
+
+            Method m = Stream.of(CondyStaticArgumentsTest.class.getDeclaredMethods())
+                    .filter(x -> x.getName().equals(methodName)).findFirst()
+                    .get();
+            try {
+                handle = MethodHandles.lookup().unreflect(m);
+            }
+            catch (Exception e) {
+                throw new Error(e);
+            }
+            descriptor = handle.type().toMethodDescriptorString();
+        }
+
+        static BSMInfo of(String name) {
+            return new BSMInfo(name);
+        }
+    }
+
+    static String basicArgs(MethodHandles.Lookup l, String name, Class<?> type,
+                            int i, long j, float f, double d,
+                            Class<?> c, String s,
+                            MethodType mt, MethodHandle mh) {
+        return new StringJoiner("-")
+                .add(name)
+                .add(type.getSimpleName())
+                .add(Integer.toString(i))
+                .add(Long.toString(j))
+                .add(Float.toString(f))
+                .add(Double.toString(d))
+                .add(c.getSimpleName())
+                .add(s)
+                .add(mt.toString())
+                .add(Integer.toString(mh.type().parameterCount()))
+                .toString();
+    }
+
+    @Test
+    public void testBasicArgs() throws Throwable {
+        BSMInfo bi = BSMInfo.of("basicArgs");
+        MethodHandleInfo mhi = MethodHandles.lookup().revealDirect(bi.handle);
+
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "constant-name", String.class,
+                bi.methodName, bi.handle.type(),
+                S -> S.add(1).add(2L).add(3.0f).add(4.0d)
+                        .add("java/lang/Number", PoolHelper::putClass)
+                        .add("something", PoolHelper::putString)
+                        .add("(IJFD)V", PoolHelper::putMethodType)
+                        .add(mhi, (P, Z) -> {
+                            return P.putHandle(mhi.getReferenceKind(), "CondyStaticArgumentsTest", mhi.getName(), bi.descriptor);
+                        }));
+
+        Assert.assertEquals(mh.invoke(), "constant-name-String-1-2-3.0-4.0-Number-something-(int,long,float,double)void-11");
+    }
+
+
+    static MathContext mathContext(MethodHandles.Lookup l, String value, Class<?> type) {
+        switch (value) {
+            case "UNLIMITED":
+                return MathContext.UNLIMITED;
+            case "DECIMAL32":
+                return MathContext.DECIMAL32;
+            case "DECIMAL64":
+                return MathContext.DECIMAL64;
+            case "DECIMAL128":
+                return MathContext.DECIMAL128;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    static BigDecimal bigDecimal(MethodHandles.Lookup l, String name, Class<?> type,
+                                 String value, MathContext mc) {
+        return new BigDecimal(value, mc);
+    }
+
+    static String condyWithCondy(MethodHandles.Lookup l, String name, Class<?> type,
+                                 BigDecimal d) {
+        return new StringJoiner("-")
+                .add(name)
+                .add(type.getSimpleName())
+                .add(d.toString())
+                .add(Integer.toString(d.precision()))
+                .toString();
+    }
+
+    static <E> int bigDecimalPoolHelper(String value, String mc, PoolHelper<String, String, E> P) {
+        BSMInfo bi = BSMInfo.of("bigDecimal");
+        return P.putDynamicConstant("big-decimal", "Ljava/math/BigDecimal;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
+                                    S -> S.add(value, PoolHelper::putString)
+                                            .add(mc, (P2, s) -> {
+                                                return mathContextPoolHelper(s, P2);
+                                            }));
+    }
+
+    static <E> int mathContextPoolHelper(String mc, PoolHelper<String, String, E> P) {
+        BSMInfo bi = BSMInfo.of("mathContext");
+        return P.putDynamicConstant(mc, "Ljava/math/MathContext;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
+                                    S -> {
+                                    });
+    }
+
+    @Test
+    public void testCondyWithCondy() throws Throwable {
+        BSMInfo bi = BSMInfo.of("condyWithCondy");
+
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "big-decimal-math-context", String.class,
+                bi.methodName, bi.handle.type(),
+                S -> S.add(null, (P, v) -> {
+                    return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
+                }));
+        Assert.assertEquals(mh.invoke(), "big-decimal-math-context-String-3.141593-7");
+    }
+
+
+    static ConstantCallSite indyWithCondy(MethodHandles.Lookup l, String name, MethodType type,
+                                          BigDecimal d) {
+        String s = new StringJoiner("-")
+                .add(name)
+                .add(type.toMethodDescriptorString())
+                .add(d.toString())
+                .add(Integer.toString(d.precision()))
+                .toString();
+        return new ConstantCallSite(MethodHandles.constant(String.class, s));
+    }
+
+    @Test
+    public void testIndyWithCondy() throws Throwable {
+        BSMInfo bi = BSMInfo.of("indyWithCondy");
+
+        MethodHandle mh = InstructionHelper.invokedynamic(
+                L, "big-decimal-math-context", methodType(String.class),
+                bi.methodName, bi.handle.type(),
+                S -> S.add(null, (P, v) -> {
+                    return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
+                }));
+        Assert.assertEquals(mh.invoke(), "big-decimal-math-context-()Ljava/lang/String;-3.141593-7");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test invalid name in name and type
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyTypeValidationTest
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyTypeValidationTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+    static final String BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class)
+            .toMethodDescriptorString();
+
+    @DataProvider
+    public Object[][] invalidTypesProvider() throws Exception {
+        return Stream.of(
+//                         ByteCode API checks for the following invalid types
+//                         "",
+//                         "[",
+//                         "A",
+//                         "a",
+                "L/java/lang/Object",
+                Stream.generate(() -> "[").limit(256).collect(Collectors.joining("", "", "I")))
+                .map(e -> new Object[]{e}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "invalidTypesProvider", expectedExceptions = ClassFormatError.class)
+    public void testInvalidTypes(String type) throws Exception {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "name", type,
+                "bsm", BSM_TYPE,
+                S -> {
+                });
+    }
+
+    @DataProvider
+    public Object[][] validTypesProvider() throws Exception {
+        List<String> t = new ArrayList<>(List.of("B", "C", "D", "F", "I", "J", "Ljava/lang/Object;", "S", "Z"));
+        int l = t.size();
+        for (int i = 0; i < l; i++) {
+            t.add("[" + t.get(i));
+        }
+
+        return t.stream()
+                .map(e -> new Object[]{e}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "validTypesProvider")
+    public void testValidTypes(String type) throws Exception {
+        MethodHandle mh = InstructionHelper.ldcDynamicConstant(
+                L, "name", type,
+                "bsm", BSM_TYPE,
+                S -> {
+                });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Stress test ldc to ensure HotSpot correctly manages oop maps
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyWithGarbageTest
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWithGarbageTest
+ */
+
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+
+import static java.lang.invoke.MethodType.methodType;
+import static test.java.lang.invoke.lib.InstructionHelper.cref;
+import static test.java.lang.invoke.lib.InstructionHelper.csym;
+
+public class CondyWithGarbageTest {
+    static final MethodHandles.Lookup L = MethodHandles.lookup();
+
+    @Test
+    public void testString() throws Throwable {
+        MethodHandle mh = lcdStringBasher();
+        int l = 0;
+        for (int i = 0; i < 100000; i++) {
+            l += +((String) mh.invoke()).length();
+        }
+        Assert.assertTrue(l > 0);
+    }
+
+    public static Object bsmString(MethodHandles.Lookup l,
+                                   String constantName,
+                                   Class<?> constantType) {
+        return new StringBuilder(constantName).toString();
+    }
+
+    static MethodHandle lcdStringBasher() throws Exception {
+        byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$String", 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ))
+                .withMethod("m", "()" + cref(String.class), M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C -> {
+                                              C.new_(csym(StringBuilder.class))
+                                                      .dup()
+                                                      .invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
+                                                      .astore_0();
+
+                                              for (int i = 10; i < 100; i++) {
+                                                  ldcString(C, Integer.toString(i));
+                                                  C.astore_1().aload_0().aload_1();
+                                                  C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
+                                                  C.pop();
+                                              }
+
+                                              C.aload_0();
+                                              C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
+                                              C.areturn();
+                                          }
+                                ))
+                .build();
+
+        Class<?> gc = L.defineClass(byteArray);
+        return L.findStatic(gc, "m", methodType(String.class));
+    }
+
+    static void ldcString(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
+        C.ldc(name, cref(String.class),
+              csym(L.lookupClass()),
+              "bsmString",
+              methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
+              S -> {
+              });
+    }
+
+
+    @Test
+    public void testStringArray() throws Throwable {
+        MethodHandle mh = lcdStringArrayBasher();
+        int l = 0;
+        for (int i = 0; i < 100000; i++) {
+            l += +((String) mh.invoke()).length();
+        }
+        Assert.assertTrue(l > 0);
+    }
+
+    public static Object bsmStringArray(MethodHandles.Lookup l,
+                                        String constantName,
+                                        Class<?> constantType) {
+        return new String[]{new StringBuilder(constantName).toString()};
+    }
+
+    static MethodHandle lcdStringArrayBasher() throws Exception {
+        byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$StringArray", 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ))
+                .withMethod("m", "()" + cref(String.class), M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new, C -> {
+                                              C.new_(csym(StringBuilder.class))
+                                                      .dup()
+                                                      .invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
+                                                      .astore_0();
+
+                                              for (int i = 10; i < 100; i++) {
+                                                  ldcStringArray(C, Integer.toString(i));
+                                                  C.bipush(0).aaload().astore_1();
+                                                  C.aload_0().aload_1();
+                                                  C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
+                                                  C.pop();
+                                              }
+
+                                              C.aload_0();
+                                              C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
+                                              C.areturn();
+                                          }
+                                ))
+                .build();
+
+        Class<?> gc = L.defineClass(byteArray);
+        return L.findStatic(gc, "m", methodType(String.class));
+    }
+
+    static void ldcStringArray(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
+        C.ldc(name, cref(String[].class),
+              csym(L.lookupClass()),
+              "bsmStringArray",
+              methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
+              S -> {
+              });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyWrongType.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8186046
+ * @summary Test bootstrap methods returning the wrong type
+ * @requires os.arch == "x86_64"
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
+ * @run testng CondyWrongType
+ * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWrongType
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class CondyWrongType {
+
+    @DataProvider
+    public Object[][] primitivesProvider() throws Exception {
+        Map<String, Class<?>> typeMap = Map.of(
+                "B", byte.class,
+                "C", char.class,
+                "D", double.class,
+                "F", float.class,
+                "I", int.class,
+                "J", long.class,
+                "S", short.class,
+                "Z", boolean.class
+                );
+
+        List<Object[]> cases = new ArrayList<>();
+        for (String name : typeMap.keySet()) {
+            MethodHandle zero = MethodHandles.zero(typeMap.get(name));
+            for (String type : typeMap.keySet()) {
+                // Use asType transformation to detect if primitive conversion
+                // is supported from the BSM value type to the dynamic constant type
+                boolean pass = true;
+                try {
+                    zero.asType(MethodType.methodType(typeMap.get(type)));
+                }
+                catch (WrongMethodTypeException e) {
+                    pass = false;
+                }
+                cases.add(new Object[] { name, type, pass});
+            }
+        }
+
+        return cases.stream().toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "primitivesProvider")
+    public void testPrimitives(String name, String type, boolean pass) {
+        test(name, type, pass);
+    }
+
+    @Test
+    public void testReferences() {
+        test("String", "Ljava/math/BigDecimal;", false);
+        test("BigDecimal", "Ljava/lang/String;", false);
+    }
+
+    @Test
+    public void testReferenceAndPrimitives() {
+        test("String", "B", false);
+        test("String", "C", false);
+        test("String", "D", false);
+        test("String", "F", false);
+        test("String", "I", false);
+        test("String", "J", false);
+        test("String", "S", false);
+        test("String", "Z", false);
+    }
+
+    static void test(String name, String type, boolean pass) {
+        MethodHandle mh = caster(name, type);
+        Throwable caught = null;
+        try {
+            mh.invoke();
+        }
+        catch (Throwable t) {
+            caught = t;
+        }
+
+        if (caught == null) {
+            if (pass) {
+                return;
+            }
+            else {
+                Assert.fail("Throwable expected");
+            }
+        }
+        else if (pass) {
+            Assert.fail("Throwable not expected");
+        }
+
+        Assert.assertTrue(BootstrapMethodError.class.isAssignableFrom(caught.getClass()));
+        caught = caught.getCause();
+        Assert.assertNotNull(caught);
+        Assert.assertTrue(ClassCastException.class.isAssignableFrom(caught.getClass()));
+    }
+
+    static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
+        switch (name) {
+            case "B":
+                return (byte) 1;
+            case "C":
+                return 'A';
+            case "D":
+                return 1.0;
+            case "F":
+                return 1.0f;
+            case "I":
+                return 1;
+            case "J":
+                return 1L;
+            case "S":
+                return (short) 1;
+            case "Z":
+                return true;
+            case "String":
+                return "string";
+            case "BigDecimal":
+                return BigDecimal.ONE;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    static MethodHandle caster(String name, String type) {
+        try {
+            return InstructionHelper.ldcDynamicConstant(
+                    MethodHandles.lookup(),
+                    name, type,
+                    "bsm", methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
+                    S -> { });
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+/**
+ * Base builder.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+public class AbstractBuilder<S, T, E, D extends AbstractBuilder<S, T, E, D>> {
+    /**
+     * The helper to build the constant pool.
+     */
+    protected final PoolHelper<S, T, E> poolHelper;
+
+    /**
+     * The helper to use to manipulate type descriptors.
+     */
+    protected final TypeHelper<S, T> typeHelper;
+
+    /**
+     * Create a builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    AbstractBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        this.poolHelper = poolHelper;
+        this.typeHelper = typeHelper;
+    }
+
+    @SuppressWarnings("unchecked")
+    D thisBuilder() {
+        return (D) this;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntBiFunction;
+
+public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> {
+
+    GrowableByteBuffer annoAttribute;
+    int nannos;
+
+    AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.annoAttribute = new GrowableByteBuffer();
+        annoAttribute.writeChar(0);
+    }
+
+    public enum Kind {
+        RUNTIME_VISIBLE,
+        RUNTIME_INVISIBLE;
+    }
+
+    enum Tag {
+        B('B'),
+        C('C'),
+        D('D'),
+        F('F'),
+        I('I'),
+        J('J'),
+        S('S'),
+        Z('Z'),
+        STRING('s'),
+        ENUM('e'),
+        CLASS('c'),
+        ANNO('@'),
+        ARRAY('[');
+
+        char tagChar;
+
+        Tag(char tagChar) {
+            this.tagChar = tagChar;
+        }
+    }
+
+    AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+        annoAttribute.writeChar(poolHelper.putType(annoType));
+        int offset = annoAttribute.offset;
+        annoAttribute.writeChar(0);
+        if (annotationBuilder != null) {
+            AnnotationElementBuilder _builder = new AnnotationElementBuilder();
+            int nelems = _builder.withElements(annotationBuilder);
+            patchCharAt(offset, nelems);
+        }
+        nannos++;
+        return this;
+    }
+
+    byte[] build() {
+        patchCharAt(0, nannos);
+        return annoAttribute.bytes();
+    }
+
+    private void patchCharAt(int offset, int newChar) {
+        int prevOffset = annoAttribute.offset;
+        try {
+            annoAttribute.offset = offset;
+            annoAttribute.writeChar(newChar);
+        } finally {
+            annoAttribute.offset = prevOffset;
+        }
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    static Consumer NO_BUILDER =
+            new Consumer() {
+                @Override
+                public void accept(Object o) {
+                    //do nothing
+                }
+            };
+
+    public class AnnotationElementBuilder {
+
+        int nelems;
+
+        public AnnotationElementBuilder withString(String name, String s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeStringValue(s);
+            return this;
+        }
+
+        private void writeStringValue(String s) {
+            annoAttribute.writeByte(Tag.STRING.tagChar);
+            annoAttribute.writeChar(poolHelper.putUtf8(s));
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withClass(String name, T s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeClassValue(s);
+            return this;
+        }
+
+        private void writeClassValue(T s) {
+            annoAttribute.writeByte(Tag.CLASS.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(s));
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withEnum(String name, T enumType, int constant) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeEnumValue(enumType, constant);
+            return this;
+        }
+
+        private void writeEnumValue(T enumType, int constant) {
+            annoAttribute.writeByte(Tag.ENUM.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(enumType));
+            annoAttribute.writeChar(constant);
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeAnnotationValue(annoType, annotationBuilder);
+            return this;
+        }
+
+        private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annoAttribute.writeByte(Tag.ANNO.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(annoType));
+            int offset = annoAttribute.offset;
+            annoAttribute.writeChar(0);
+            int nelems = withNestedElements(annotationBuilder);
+            patchCharAt(offset, nelems);
+            this.nelems++;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, char c) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, short s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, byte b) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, int i) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, float f) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, long l) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, double d) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, boolean b) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
+            return this;
+        }
+
+        private <Z> void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc) {
+            annoAttribute.writeByte(tag.tagChar);
+            annoAttribute.writeChar(poolFunc.applyAsInt(poolHelper, value));
+            nelems++;
+        }
+
+        AnnotationElementBuilder withStrings(String name, String... ss) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ss.length);
+            for (String s : ss) {
+                writeStringValue(s);
+            }
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        AnnotationElementBuilder withClasses(String name, T... cc) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(cc.length);
+            for (T c : cc) {
+                writeClassValue(c);
+            }
+            return this;
+        }
+
+        AnnotationElementBuilder withEnums(String name, T enumType, int... constants) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(constants.length);
+            for (int c : constants) {
+                writeEnumValue(enumType, c);
+            }
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(annotationBuilders.length);
+            for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) {
+                writeAnnotationValue(annoType, annotationBuilder);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, char... cc) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(cc.length);
+            for (char c : cc) {
+                writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, short... ss) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ss.length);
+            for (short s : ss) {
+                writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, byte... bb) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(bb.length);
+            for (byte b : bb) {
+                writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, int... ii) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ii.length);
+            for (int i : ii) {
+                writePrimitiveValue(Tag.I, i,  PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, float... ff) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ff.length);
+            for (float f : ff) {
+                writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, long... ll) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ll.length);
+            for (long l : ll) {
+                writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, double... dd) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(dd.length);
+            for (double d : dd) {
+                writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, boolean... bb) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(bb.length);
+            for (boolean b : bb) {
+                writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            return withElements(new AnnotationElementBuilder(), annotationBuilder);
+        }
+
+        int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            return withElements(this, annotationBuilder);
+        }
+
+        private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annotationBuilder.accept(builder);
+            return builder.nelems;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+/**
+ * Base builder for attribute containing class file entities.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+ public class AttributeBuilder<S, T, E, D extends AttributeBuilder<S, T, E, D>>
+        extends AbstractBuilder<S, T, E, D> {
+
+     /**
+      * The number of attributes.
+      */
+    protected int nattrs;
+
+    /**
+     * The attributes represented as bytes.
+     */
+    protected GrowableByteBuffer attributes = new GrowableByteBuffer();
+
+    /**
+     * Create an attribute builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    public AttributeBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+    }
+
+    /**
+     * Add a class file Attribute.  Defined as:
+     * <pre>
+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * </pre>
+     *
+     * @param name the attribute name
+     * @param bytes the bytes of the attribute info
+     * @return this builder, for chained calls
+     */
+    public D withAttribute(CharSequence name, byte[] bytes) {
+        attributes.writeChar(poolHelper.putUtf8(name));
+        attributes.writeInt(bytes.length);
+        attributes.writeBytes(bytes);
+        nattrs++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a class file Attribute, using a writer.  Defined as:
+     * <pre>
+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * </pre>
+     *
+     * @param <Z> the type of the object representing the attribute
+     * @param name the attribute name
+     * @param attr the representation of the attribute
+     * @param attrWriter the writer which transform the attribute representation into bytes
+     * @return this builder, for chained calls
+     */
+    public <Z> D withAttribute(CharSequence name, Z attr, AttributeWriter<S, T, E, Z> attrWriter) {
+        attributes.writeChar(poolHelper.putUtf8(name));
+        int offset = attributes.offset;
+        attributes.writeInt(0);
+        attrWriter.write(attr, poolHelper, attributes);
+        int len = attributes.offset - offset - 4;
+        attributes.withOffset(offset, buf -> buf.writeInt(len));
+        nattrs++;
+        return thisBuilder();
+    }
+
+     /**
+      * Writer for transforming attribute representations to bytes
+      *
+      * @param <S> the type of symbol representation
+      * @param <T> the type of type descriptors representation
+      * @param <E> the type of pool entries
+      * @param <A> the type of the object representing the attribute
+      */
+    public interface AttributeWriter<S, T, E, A> {
+
+        /**
+         * Write an attribute representation into a byte buffer.
+         *
+         * @param attr the representation of the attribute
+         * @param poolHelper the constant pool helper
+         * @param buf the buffer to collect the bytes
+         */
+        void write(A attr, PoolHelper<S, T, E> poolHelper, GrowableByteBuffer buf);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+public class BasicClassBuilder extends ClassBuilder<String, String, BasicClassBuilder> {
+
+    public BasicClassBuilder(String thisClass, int majorVersion, int minorVersion) {
+        this();
+        withMinorVersion(minorVersion);
+        withMajorVersion(majorVersion);
+        withThisClass(thisClass);
+    }
+
+    public BasicClassBuilder() {
+        super(new BytePoolHelper<>(s->s, s->s), new BasicTypeHelper());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.Iterator;
+
+/**
+ * Helper to create and manipulate type descriptors, where type descriptors
+ * are represented as JVM type descriptor strings and symbols are represented
+ * as name strings
+ */
+public class BasicTypeHelper implements TypeHelper<String, String> {
+
+    @Override
+    public String elemtype(String s) {
+        if (!s.startsWith("[")) {
+            throw new IllegalStateException();
+        }
+        return s.substring(1);
+    }
+
+    @Override
+    public String arrayOf(String s) {
+        return "[" + s;
+    }
+
+    @Override
+    public String type(String s) {
+        return "L" + s + ";";
+    }
+
+    @Override
+    public TypeTag tag(String s) {
+        switch (s.charAt(0)) {
+            case '[':
+            case 'L':
+                return TypeTag.A;
+            case 'B':
+            case 'C':
+            case 'Z':
+            case 'S':
+            case 'I':
+                return TypeTag.I;
+            case 'F':
+                return TypeTag.F;
+            case 'J':
+                return TypeTag.J;
+            case 'D':
+                return TypeTag.D;
+            case 'V':
+                return TypeTag.V;
+            case 'Q':
+                return TypeTag.Q;
+            default:
+                throw new IllegalStateException("Bad type: " + s);
+        }
+    }
+
+    @Override
+    public String nullType() {
+        // Needed in TypedCodeBuilder; ACONST_NULL pushes a 'null' onto the stack,
+        // and stack maps handle null differently
+        return "<null>";
+    }
+
+    @Override
+    public String commonSupertype(String t1, String t2) {
+        if (t1.equals(t2)) {
+            return t1;
+        } else {
+            try {
+                Class<?> c1 = from(t1);
+                Class<?> c2 = from(t2);
+                if (c1.isAssignableFrom(c2)) {
+                    return t1;
+                } else if (c2.isAssignableFrom(c1)) {
+                    return t2;
+                } else {
+                    return "Ljava/lang/Object;";
+                }
+            } catch (Exception e) {
+                return null;
+            }
+        }
+    }
+
+    public Class<?> from(String desc) throws ReflectiveOperationException {
+        if (desc.startsWith("[")) {
+            return Class.forName(desc.replaceAll("/", "."));
+        } else {
+            return Class.forName(symbol(desc).replaceAll("/", "."));
+        }
+    }
+
+    @Override
+    public Iterator<String> parameterTypes(String s) {
+        //TODO: gracefully non-method types
+        return new Iterator<String>() {
+            int ch = 1;
+
+            @Override
+            public boolean hasNext() {
+                return s.charAt(ch) != ')';
+            }
+
+            @Override
+            public String next() {
+                char curr = s.charAt(ch);
+                switch (curr) {
+                    case 'C':
+                    case 'B':
+                    case 'S':
+                    case 'I':
+                    case 'J':
+                    case 'F':
+                    case 'D':
+                    case 'Z':
+                        ch++;
+                        return String.valueOf(curr);
+                    case '[':
+                        ch++;
+                        return "[" + next();
+                    case 'L':
+                    case 'Q':
+                        StringBuilder builder = new StringBuilder();
+                        while (curr != ';') {
+                            builder.append(curr);
+                            curr = s.charAt(++ch);
+                        }
+                        builder.append(';');
+                        ch++;
+                        return builder.toString();
+                    default:
+                        throw new AssertionError("cannot parse string: " + s);
+                }
+            }
+        };
+    }
+
+    @Override
+    public String symbolFrom(String s) {
+        return s;
+    }
+
+    @Override
+    public String fromTag(TypeTag tag) {
+        return tag.name();
+    }
+
+    @Override
+    public String symbol(String type) {
+        return (type.startsWith("L") || type.startsWith("Q")) ? type.substring(1, type.length() - 1) : type;
+    }
+
+    @Override
+    public String returnType(String s) {
+        return s.substring(s.indexOf(')') + 1, s.length());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,747 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.lang.invoke.MethodHandleInfo;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntBiFunction;
+
+/**
+ * A helper for building and tracking constant pools whose entries are
+ * represented as byte arrays.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ */
+public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> {
+
+    GrowableByteBuffer pool = new GrowableByteBuffer();
+    GrowableByteBuffer bsm_attr = new GrowableByteBuffer();
+    //Map<PoolKey, PoolKey> indicesMap = new HashMap<>();
+    int currentIndex = 1;
+    int currentBsmIndex = 0;
+
+    KeyMap<PoolKey> entries = new KeyMap<>();
+    KeyMap<BsmKey> bootstraps = new KeyMap<>();
+    PoolKey key = new PoolKey();
+    BsmKey bsmKey = new BsmKey();
+
+    Function<S, String> symbolToString;
+    Function<T, String> typeToString;
+
+    public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) {
+        this.symbolToString = symbolToString;
+        this.typeToString = typeToString;
+    }
+
+    static class KeyMap<K extends AbstractKey<K>> {
+
+        @SuppressWarnings("unchecked")
+        K[] table = (K[])new AbstractKey<?>[0x10];
+        int nelems;
+
+        public void enter(K e) {
+            if (nelems * 3 >= (table.length - 1) * 2)
+                dble();
+            int hash = getIndex(e);
+            K old = table[hash];
+            if (old == null) {
+                nelems++;
+            }
+            e.next = old;
+            table[hash] = e;
+        }
+
+        protected K lookup(K other) {
+            K e = table[getIndex(other)];
+            while (e != null && !e.equals(other))
+                e = e.next;
+            return e;
+        }
+
+        /**
+         * Look for slot in the table.
+         * We use open addressing with double hashing.
+         */
+        int getIndex(K e) {
+            int hashMask = table.length - 1;
+            int h = e.hashCode();
+            int i = h & hashMask;
+            // The expression below is always odd, so it is guaranteed
+            // to be mutually prime with table.length, a power of 2.
+            int x = hashMask - ((h + (h >> 16)) << 1);
+            for (; ; ) {
+                K e2 = table[i];
+                if (e2 == null)
+                    return i;
+                else if (e.hash == e2.hash)
+                    return i;
+                i = (i + x) & hashMask;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private void dble() {
+            K[] oldtable = table;
+            table = (K[])new AbstractKey<?>[oldtable.length * 2];
+            int n = 0;
+            for (int i = oldtable.length; --i >= 0; ) {
+                K e = oldtable[i];
+                if (e != null) {
+                    table[getIndex(e)] = e;
+                    n++;
+                }
+            }
+            // We don't need to update nelems for shared inherited scopes,
+            // since that gets handled by leave().
+            nelems = n;
+        }
+    }
+
+    public static abstract class AbstractKey<K extends AbstractKey<K>> {
+        int hash;
+        int index = -1;
+        K next;
+
+        abstract K dup();
+
+        public abstract boolean equals(Object o);
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+
+        void at(int index) {
+            this.index = index;
+        }
+    }
+
+    public static class PoolKey extends AbstractKey<PoolKey> {
+        PoolTag tag;
+        Object o1;
+        Object o2;
+        Object o3;
+        Object o4;
+        int size = -1;
+
+        void setUtf8(CharSequence s) {
+            tag = PoolTag.CONSTANT_UTF8;
+            o1 = s;
+            size = 1;
+            hash = tag.tag | (s.hashCode() << 1);
+        }
+
+        void setClass(String clazz) {
+            tag = PoolTag.CONSTANT_CLASS;
+            o1 = clazz;
+            size = 1;
+            hash = tag.tag | (clazz.hashCode() << 1);
+        }
+
+        void setNameAndType(CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_NAMEANDTYPE;
+            o1 = name;
+            o2 = type;
+            size = 2;
+            hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) {
+            tag = poolTag;
+            o1 = owner;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setInvokeDynamic(int bsmIndex, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_INVOKEDYNAMIC;
+            o1 = bsmIndex;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setDynamicConstant(int bsmIndex, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_DYNAMIC;
+            o1 = bsmIndex;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setString(String s) {
+            tag = PoolTag.CONSTANT_STRING;
+            o1 = s;
+            size = 1;
+            hash = tag.tag | (s.hashCode() << 1);
+        }
+
+        void setInteger(Integer i) {
+            tag = PoolTag.CONSTANT_INTEGER;
+            o1 = i;
+            size = 1;
+            hash = tag.tag | (i.hashCode() << 1);
+        }
+
+        void setFloat(Float f) {
+            tag = PoolTag.CONSTANT_FLOAT;
+            o1 = f;
+            size = 1;
+            hash = tag.tag | (f.hashCode() << 1);
+        }
+
+        void setLong(Long l) {
+            tag = PoolTag.CONSTANT_LONG;
+            o1 = l;
+            size = 1;
+            hash = tag.tag | (l.hashCode() << 1);
+        }
+
+        void setDouble(Double d) {
+            tag = PoolTag.CONSTANT_DOUBLE;
+            o1 = d;
+            size = 1;
+            hash = tag.tag | (d.hashCode() << 1);
+        }
+
+        void setMethodType(String type) {
+            tag = PoolTag.CONSTANT_METHODTYPE;
+            o1 = type;
+            size = 1;
+            hash = tag.tag | (type.hashCode() << 1);
+        }
+
+        void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_METHODHANDLE;
+            o1 = bsmKind;
+            o2 = owner;
+            o3 = name;
+            o4 = type;
+            size = 4;
+            hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            PoolKey that = (PoolKey) obj;
+            if (tag != that.tag) return false;
+            switch (size) {
+                case 1:
+                    if (!o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 2:
+                    if (!o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 3:
+                    if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 4:
+                    if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+            }
+            return true;
+        }
+
+        PoolKey dup() {
+            PoolKey poolKey = new PoolKey();
+            poolKey.tag = tag;
+            poolKey.size = size;
+            poolKey.hash = hash;
+            poolKey.o1 = o1;
+            poolKey.o2 = o2;
+            poolKey.o3 = o3;
+            poolKey.o4 = o4;
+            return poolKey;
+        }
+    }
+
+    static class BsmKey extends AbstractKey<BsmKey> {
+        String bsmClass;
+        CharSequence bsmName;
+        String bsmType;
+        List<Integer> bsmArgs;
+
+        void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) {
+            this.bsmClass = bsmClass;
+            this.bsmName = bsmName;
+            this.bsmType = bsmType;
+            this.bsmArgs = bsmArgs;
+            hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs);
+        }
+
+        BsmKey dup() {
+            BsmKey bsmKey = new BsmKey();
+            bsmKey.bsmClass = bsmClass;
+            bsmKey.bsmName = bsmName;
+            bsmKey.bsmType = bsmType;
+            bsmKey.bsmArgs = bsmArgs;
+            bsmKey.hash = hash;
+            return bsmKey;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof BsmKey) {
+                BsmKey that = (BsmKey)obj;
+                return Objects.equals(bsmClass, that.bsmClass) &&
+                        Objects.equals(bsmName, that.bsmName) &&
+                        Objects.equals(bsmType, that.bsmType) &&
+                        Objects.deepEquals(bsmArgs, that.bsmArgs);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public int putClass(S symbol) {
+        return putClassInternal(symbolToString.apply(symbol));
+    }
+
+    private int putClassInternal(String symbol) {
+        key.setClass(symbol);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int utf8_idx = putUtf8(symbol);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_CLASS.tag);
+            pool.writeChar(utf8_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putFieldRef(S owner, CharSequence name, T type) {
+        return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type);
+    }
+
+    @Override
+    public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+        return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF,
+                owner, name, type);
+    }
+
+    int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) {
+        return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type));
+    }
+
+    int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) {
+        key.setMemberRef(poolTag, owner, name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int owner_idx = putClassInternal(owner);
+            int nameAndType_idx = putNameAndType(name, type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(poolTag.tag);
+            pool.writeChar(owner_idx);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putInt(int i) {
+        key.setInteger(i);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_INTEGER.tag);
+            pool.writeInt(i);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putFloat(float f) {
+        key.setFloat(f);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_FLOAT.tag);
+            pool.writeFloat(f);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putLong(long l) {
+        key.setLong(l);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_LONG.tag);
+            pool.writeLong(l);
+            currentIndex++;
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putDouble(double d) {
+        key.setDouble(d);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag);
+            pool.writeDouble(d);
+            currentIndex++;
+        }
+        return poolKey.index;
+    }
+
+
+    @Override
+    public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
+    }
+
+    @Override
+    public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        return putDynamicConstantInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
+    }
+
+    private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
+        key.setInvokeDynamic(bsmIndex, invokedName, invokedType);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int nameAndType_idx = putNameAndType(invokedName, invokedType);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_INVOKEDYNAMIC.tag);
+            pool.writeChar(bsmIndex);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    private int putDynamicConstantInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
+        key.setDynamicConstant(bsmIndex, constName, constType);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int nameAndType_idx = putNameAndType(constName, constType);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_DYNAMIC.tag);
+            pool.writeChar(bsmIndex);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder();
+        staticArgs.accept(staticArgsBuilder);
+        List<Integer> static_idxs = staticArgsBuilder.indexes;
+        bsmKey.set(bsmClass, bsmName, bsmType, static_idxs);
+        BsmKey poolKey = bootstraps.lookup(bsmKey);
+        if (poolKey == null) {
+            poolKey = bsmKey.dup();
+            int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType);
+            poolKey.at(currentBsmIndex++);
+            bootstraps.enter(poolKey);
+            bsm_attr.writeChar(bsm_ref);
+            bsm_attr.writeChar(static_idxs.size());
+            for (int i : static_idxs) {
+                bsm_attr.writeChar(i);
+            }
+        }
+        return poolKey.index;
+    }
+    //where
+        class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
+
+            List<Integer> indexes = new ArrayList<>();
+
+            public ByteStaticArgListBuilder add(int i) {
+                indexes.add(putInt(i));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(float f) {
+                indexes.add(putFloat(f));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(long l) {
+                indexes.add(putLong(l));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(double d) {
+                indexes.add(putDouble(d));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(String s) {
+                indexes.add(putString(s));
+                return this;
+            }
+            @Override
+            public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
+                indexes.add(putHandle(refKind, owner, name, type));
+                return this;
+            }
+            public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
+                indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+                indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
+                return this;
+            }
+        }
+
+    @Override
+    public int putMethodType(T s) {
+        return putMethodTypeInternal(typeToString.apply(s));
+    }
+
+    private int putMethodTypeInternal(String s) {
+        key.setMethodType(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int desc_idx = putUtf8(s);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag);
+            pool.writeChar(desc_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putHandle(int refKind, S owner, CharSequence name, T type) {
+        return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type));
+    }
+
+    private int putHandleInternal(int refKind, String owner, CharSequence name, String type) {
+        key.setMethodHandle(refKind, owner, name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int ref_idx = putMemberRefInternal(fromKind(refKind), owner, name, type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag);
+            pool.writeByte(refKind);
+            pool.writeChar(ref_idx);
+        }
+        return poolKey.index;
+    }
+
+    PoolTag fromKind(int bsmKind) {
+        switch (bsmKind) {
+            case 1: // REF_getField
+            case 2: // REF_getStatic
+            case 3: // REF_putField
+            case 4: // REF_putStatic
+                return PoolTag.CONSTANT_FIELDREF;
+            case 5: // REF_invokeVirtual
+            case 6: // REF_invokeStatic
+            case 7: // REF_invokeSpecial
+            case 8: // REF_newInvokeSpecial
+                return PoolTag.CONSTANT_METHODREF;
+            case 9: // REF_invokeInterface
+                return PoolTag.CONSTANT_INTERFACEMETHODREF;
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public int putType(T s) {
+        return putUtf8(typeToString.apply(s));
+    }
+
+    public int putUtf8(CharSequence s) {
+        key.setUtf8(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_UTF8.tag);
+            putUTF8Internal(s);
+        }
+        return poolKey.index;
+    }
+
+    /**
+     * Puts an UTF8 string into this byte vector. The byte vector is
+     * automatically enlarged if necessary.
+     *
+     * @param s a String whose UTF8 encoded length must be less than 65536.
+     * @return this byte vector.
+     */
+    void putUTF8Internal(final CharSequence s) {
+        int charLength = s.length();
+        if (charLength > 65535) {
+            throw new IllegalArgumentException();
+        }
+        // optimistic algorithm: instead of computing the byte length and then
+        // serializing the string (which requires two loops), we assume the byte
+        // length is equal to char length (which is the most frequent case), and
+        // we start serializing the string right away. During the serialization,
+        // if we find that this assumption is wrong, we continue with the
+        // general method.
+        pool.writeChar(charLength);
+        for (int i = 0; i < charLength; ++i) {
+            char c = s.charAt(i);
+            if (c >= '\001' && c <= '\177') {
+                pool.writeByte((byte) c);
+            } else {
+                encodeUTF8(s, i, 65535);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Puts an UTF8 string into this byte vector. The byte vector is
+     * automatically enlarged if necessary. The string length is encoded in two
+     * bytes before the encoded characters, if there is space for that (i.e. if
+     * this.length - i - 2 >= 0).
+     *
+     * @param s             the String to encode.
+     * @param i             the index of the first character to encode. The previous
+     *                      characters are supposed to have already been encoded, using
+     *                      only one byte per character.
+     * @param maxByteLength the maximum byte length of the encoded string, including the
+     *                      already encoded characters.
+     * @return this byte vector.
+     */
+    void encodeUTF8(final CharSequence s, int i, int maxByteLength) {
+        int charLength = s.length();
+        int byteLength = i;
+        char c;
+        for (int j = i; j < charLength; ++j) {
+            c = s.charAt(j);
+            if (c >= '\001' && c <= '\177') {
+                byteLength++;
+            } else if (c > '\u07FF') {
+                byteLength += 3;
+            } else {
+                byteLength += 2;
+            }
+        }
+        if (byteLength > maxByteLength) {
+            throw new IllegalArgumentException();
+        }
+        int byteLengthFinal = byteLength;
+        pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal));
+        for (int j = i; j < charLength; ++j) {
+            c = s.charAt(j);
+            if (c >= '\001' && c <= '\177') {
+                pool.writeChar((byte) c);
+            } else if (c > '\u07FF') {
+                pool.writeChar((byte) (0xE0 | c >> 12 & 0xF));
+                pool.writeChar((byte) (0x80 | c >> 6 & 0x3F));
+                pool.writeChar((byte) (0x80 | c & 0x3F));
+            } else {
+                pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F));
+                pool.writeChar((byte) (0x80 | c & 0x3F));
+            }
+        }
+    }
+
+    @Override
+    public int putString(String s) {
+        key.setString(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int utf8_index = putUtf8(s);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_STRING.tag);
+            pool.writeChar(utf8_index);
+        }
+        return poolKey.index;
+    }
+
+    int putNameAndType(CharSequence name, String type) {
+        key.setNameAndType(name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int name_idx = putUtf8(name);
+            int type_idx = putUtf8(type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag);
+            pool.writeChar(name_idx);
+            pool.writeChar(type_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int size() {
+        return currentIndex - 1;
+    }
+
+    @Override
+    public byte[] entries() {
+        return pool.bytes();
+    }
+
+    <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
+        if (currentBsmIndex > 0) {
+            GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
+            bsmAttrBuf.writeChar(currentBsmIndex);
+            bsmAttrBuf.writeBytes(bsm_attr);
+            cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+/**
+ * Base class builder. The base of higher level class builders.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <C> the type of this builder
+ */
+public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>>
+        extends DeclBuilder<S, T, byte[], C> {
+
+    /**
+     * The helper to use to manipulate type descriptors.
+     */
+    protected TypeHelper<S, T> typeHelper;
+
+    /**
+     * The symbol for the class being built.
+     */
+    protected S thisClass;
+
+    /**
+     * The super-interfaces of the class being built..
+     */
+    protected GrowableByteBuffer interfaces = new GrowableByteBuffer();
+
+    /**
+     * The fields of the class being built.
+     */
+    protected GrowableByteBuffer fields = new GrowableByteBuffer();
+
+    /**
+     * The methods of the class being built.
+     */
+    protected GrowableByteBuffer methods = new GrowableByteBuffer();
+
+    int majorVersion;
+    int minorVersion;
+    int flags;
+    int superclass;
+    int nmethods, nfields, ninterfaces;
+
+    /**
+     * Create a class builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    public ClassBuilder(BytePoolHelper<S, T> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.typeHelper = typeHelper;
+    }
+
+    /**
+     * Set the minor class file version.
+     *
+     * @param minorVersion the minor version number
+     * @return this builder, for chained calls
+     */
+    public C withMinorVersion(int minorVersion) {
+        this.minorVersion = minorVersion;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the major class file version.
+     *
+     * @param majorVersion the major version number
+     * @return this builder, for chained calls
+     */
+    public C withMajorVersion(int majorVersion) {
+        this.majorVersion = majorVersion;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the class symbol
+     *
+     * @param thisClass the class symbol
+     * @return this builder, for chained calls
+     */
+    public C withThisClass(S thisClass) {
+        this.thisClass = thisClass;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the class access flags
+     *
+     * @param flags an array of {@code Flag}
+     * @return this builder, for chained calls
+     */
+    @Override
+    public C withFlags(Flag... flags) {
+        for (Flag f : flags) {
+            this.flags |= f.flag;
+        }
+        return thisBuilder();
+    }
+
+    /**
+     * Set the superclass
+     *
+     * @param sup the superclass symbol
+     * @return this builder, for chained calls
+     */
+    public C withSuperclass(S sup) {
+        this.superclass = poolHelper.putClass(sup);
+        return thisBuilder();
+    }
+
+    /**
+     * Add a super interface.
+     *
+     * @param sup an interface symbol
+     * @return this builder, for chained calls
+     */
+    public C withSuperinterface(S sup) {
+        this.interfaces.writeChar(poolHelper.putClass(sup));
+        ninterfaces++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a field.
+     *
+     * @param name the name of the field
+     * @param type the type descriptor of the field
+     * @return this builder, for chained calls
+     */
+    public final C withField(CharSequence name, T type) {
+        return withField(name, type, FB -> {
+        });
+    }
+
+    /**
+     * Add a field.
+     *
+     * @param name the name of the field
+     * @param type the type descriptor of the field
+     * @param fieldConfig access to the {@code FieldBuilder} to allow clients to
+     * adjust flags, annotations and bytecode attributes on the field declaration
+     * @return this builder, for chained calls
+     */
+    public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldConfig) {
+        FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper);
+        fieldConfig.accept(F);
+        F.build(fields);
+        nfields++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a method
+     *
+     * @param name the name of the method
+     * @param type the type descriptor of the method
+     * @return this builder, for chained calls
+     */
+    public final C withMethod(CharSequence name, T type) {
+        return withMethod(name, type, MB -> {
+        });
+    }
+
+    /**
+     * Add a method
+     *
+     * @param name the name of the method
+     * @param type the type descriptor of the method
+     * @param methodConfig access to the {@code MethodBuilder} to allow clients to
+     * adjust flags, annotations and bytecode attributes on the method declaration
+     * @return this builder, for chained calls
+     */
+    public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodConfig) {
+        MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper);
+        methodConfig.accept(M);
+        M.build(methods);
+        nmethods++;
+        return thisBuilder();
+    }
+
+    /**
+     * Build the constant pool into a byte array.
+     *
+     * @return a representation of this constant pool as a byte array
+     */
+    @SuppressWarnings("unchecked")
+    public byte[] build() {
+        ((BytePoolHelper<S, T>)poolHelper).addAttributes(this);
+        addAnnotations();
+        int thisClassIdx = poolHelper.putClass(thisClass);
+        byte[] poolBytes = poolHelper.entries();
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        buf.writeInt(0xCAFEBABE);
+        buf.writeChar(minorVersion);
+        buf.writeChar(majorVersion);
+        buf.writeChar(poolHelper.size() + 1);
+        buf.writeBytes(poolBytes);
+        buf.writeChar(flags);
+        buf.writeChar(thisClassIdx);
+        buf.writeChar(superclass);
+        buf.writeChar(ninterfaces);
+        if (ninterfaces > 0) {
+            buf.writeBytes(interfaces);
+        }
+        buf.writeChar(nfields);
+        buf.writeBytes(fields);
+        buf.writeChar(nmethods);
+        buf.writeBytes(methods);
+        buf.writeChar(nattrs);
+        buf.writeBytes(attributes);
+        return buf.bytes();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.PoolHelper.StaticArgListBuilder;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+
+/**
+ * Builder for class file code attributes. A code attribute is defined:
+ * <pre>
+ * {@code
+ * Code_attribute {
+ *    u2 attribute_name_index;
+ *    u4 attribute_length;
+ *    u2 max_stack;
+ *    u2 max_locals;
+ *    u4 code_length;
+ *    u1 code[code_length];
+ *    u2 exception_table_length;
+ *    {   u2 start_pc;
+ *        u2 end_pc;
+ *        u2 handler_pc;
+ *        u2 catch_type;
+ *    } exception_table[exception_table_length];
+ *    u2 attributes_count;
+ *    attribute_info attributes[attributes_count];
+ * } }
+ * </pre>
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <C> the type of this code builder
+ */
+public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> {
+
+    protected GrowableByteBuffer code = new GrowableByteBuffer();
+    GrowableByteBuffer catchers = new GrowableByteBuffer();
+    GrowableByteBuffer stackmaps = new GrowableByteBuffer();
+    MethodBuilder<S, T, E> methodBuilder;
+    int ncatchers;
+    int stacksize = -1;
+    int localsize = -1;
+    int nstackmaps = 0;
+
+    public enum JumpMode {
+        NARROW,
+        WIDE;
+    }
+
+    CodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder.poolHelper, methodBuilder.typeHelper);
+        this.methodBuilder = methodBuilder;
+    }
+
+    public C getstatic(S owner, CharSequence name, T type) {
+        emitOp(Opcode.GETSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putstatic(S owner, CharSequence name, T type) {
+        emitOp(Opcode.PUTSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C getfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.GETFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C vgetfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.VGETFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.PUTFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C invokevirtual(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKEVIRTUAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokespecial(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESPECIAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokestatic(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESTATIC, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokeinterface(S owner, CharSequence name, T type) {
+        emitOp(Opcode.INVOKEINTERFACE, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, true));
+        int nargs = 1;
+        Iterator<T> it = typeHelper.parameterTypes(type);
+        while (it.hasNext()) {
+            nargs += typeHelper.tag(it.next()).width;
+        }
+        code.writeByte(nargs);
+        code.writeByte(0);
+        return thisBuilder();
+    }
+
+    public C invokedynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+        emitOp(Opcode.INVOKEDYNAMIC, invokedType);
+        code.writeChar(poolHelper.putInvokeDynamic(invokedName, invokedType, bsmClass, bsmName, bsmType, staticArgs));
+        code.writeChar(0); //padding
+        return thisBuilder();
+    }
+
+    public C new_(S clazz) {
+        emitOp(Opcode.NEW, clazz);
+        code.writeChar(poolHelper.putClass(clazz));
+        return thisBuilder();
+    }
+
+    public C vnew_(S clazz, CharSequence name, T desc) {
+        emitOp(Opcode.VNEW, clazz);
+        code.writeChar(poolHelper.putMethodRef(clazz, name, desc, false));
+        return thisBuilder();
+    }
+
+    public C vnewarray(S array) {
+        emitOp(Opcode.VNEWARRAY, array);
+        code.writeChar(poolHelper.putClass(array));
+        return thisBuilder();
+    }
+
+    public C newarray(TypeTag tag) {
+        emitOp(Opcode.NEWARRAY, tag);
+        int newarraycode = tag.newarraycode;
+        if (newarraycode == -1) {
+            throw new IllegalStateException("Bad tag " + tag);
+        }
+        code.writeByte(newarraycode);
+        return thisBuilder();
+    }
+
+    public C anewarray(S array) {
+        emitOp(Opcode.ANEWARRAY, array);
+        code.writeChar(poolHelper.putClass(array));
+        return thisBuilder();
+    }
+
+    public C checkcast(S target) {
+        emitOp(Opcode.CHECKCAST);
+        code.writeChar(poolHelper.putClass(target));
+        return thisBuilder();
+    }
+
+    public C instanceof_(S target) {
+        emitOp(Opcode.INSTANCEOF);
+        code.writeChar(poolHelper.putClass(target));
+        return thisBuilder();
+    }
+
+    public C multianewarray(S array, byte dims) {
+        emitOp(Opcode.MULTIANEWARRAY, new Object[]{array, dims});
+        code.writeChar(poolHelper.putClass(array)).writeByte(dims);
+        return thisBuilder();
+    }
+
+    public C multivnewarray(S array, byte dims) {
+        emitOp(Opcode.MULTIVNEWARRAY, new Object[]{array, dims});
+        code.writeChar(poolHelper.putClass(array)).writeByte(dims);
+        return thisBuilder();
+    }
+
+    public C vbox(S target) {
+        emitOp(Opcode.VBOX, target);
+        code.writeChar(poolHelper.putClass(target));
+        return thisBuilder();
+    }
+
+    public C vunbox(S target) {
+        emitOp(Opcode.VUNBOX, target);
+        code.writeChar(poolHelper.putClass(target));
+        return thisBuilder();
+    }
+
+    public C ldc(int i) {
+        return ldc(pool -> pool.putInt(i), false);
+    }
+
+    public C ldc(long l) {
+        return ldc(pool -> pool.putLong(l), true);
+    }
+
+    public C ldc(float f) {
+        return ldc(pool -> pool.putFloat(f), false);
+    }
+
+    public C ldc(double d) {
+        return ldc(pool -> pool.putDouble(d), true);
+    }
+
+    public C ldc(String s) {
+        return ldc(pool -> pool.putString(s), false);
+    }
+
+    public C ldc(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+        boolean fat = typeHelper.tag(constType).width() == 2;
+        return ldc(pool -> pool.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs), fat);
+    }
+
+    public <Z> C ldc(Z z, BiFunction<PoolHelper<S, T, E>, Z, Integer> poolFunc) {
+        return ldc(pool -> poolFunc.apply(pool, z), false);
+    }
+
+    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+        // @@@ This should probably be abstract
+        int index = indexFunc.applyAsInt(poolHelper);
+        return ldc(index, null, fat);
+    }
+
+    protected final C ldc(int index, T type, boolean fat) {
+        if (fat) {
+            emitOp(Opcode.LDC2_W, type);
+            code.writeChar(index);
+        } else if (index > 63) {
+            emitOp(Opcode.LDC_W, type);
+            code.writeChar(index);
+        } else {
+            emitOp(Opcode.LDC, type);
+            code.writeByte(index);
+        }
+        return thisBuilder();
+    }
+
+    //other non-CP dependent opcodes
+    public C areturn() {
+        return emitOp(Opcode.ARETURN);
+    }
+
+    public C ireturn() {
+        return emitOp(Opcode.IRETURN);
+    }
+
+    public C freturn() {
+        return emitOp(Opcode.FRETURN);
+    }
+
+    public C lreturn() {
+        return emitOp(Opcode.LRETURN);
+    }
+
+    public C dreturn() {
+        return emitOp(Opcode.DRETURN);
+    }
+
+    public C return_() {
+        return emitOp(Opcode.RETURN);
+    }
+
+    public C vreturn() {
+        return emitOp(Opcode.VRETURN);
+    }
+
+    protected C emitWideIfNeeded(Opcode opcode, int n) {
+        boolean wide = n > Byte.MAX_VALUE;
+        if (wide) {
+            wide();
+        }
+        emitOp(opcode, n);
+        if (wide) {
+            code.writeChar(n);
+        } else {
+            code.writeByte(n);
+        }
+        return thisBuilder();
+    }
+
+    protected C emitWideIfNeeded(Opcode opcode, int n, int v) {
+        boolean wide = n > Byte.MAX_VALUE || v > Byte.MAX_VALUE;
+        if (wide) {
+            wide();
+        }
+        emitOp(opcode, n);
+        if (wide) {
+            code.writeChar(n).writeChar(v);
+        } else {
+            code.writeByte(n).writeByte(v);
+        }
+        return thisBuilder();
+    }
+
+    public TypedBuilder typed(TypeTag typeTag) {
+        return typed(typeTag, _unused -> new TypedBuilder());
+    }
+
+    protected <TB extends TypedBuilder> TB typed(TypeTag typeTag, Function<TypeTag, TB> typedBuilderFunc) {
+        emitOp(Opcode.TYPED);
+        code.writeChar(poolHelper.putType(typeHelper.fromTag(typeTag)));
+        return typedBuilderFunc.apply(typeTag);
+    }
+
+    public class TypedBuilder {
+        public C aload_0() {
+            return CodeBuilder.this.aload_0();
+        }
+
+        public C aload_1() {
+            return CodeBuilder.this.aload_1();
+        }
+
+        public C aload_2() {
+            return CodeBuilder.this.aload_2();
+        }
+
+        public C aload_3() {
+            return CodeBuilder.this.aload_3();
+        }
+
+        public C aload(int n) {
+            return CodeBuilder.this.aload(n);
+        }
+
+        public C astore_0() {
+            return CodeBuilder.this.astore_0();
+        }
+
+        public C astore_1() {
+            return CodeBuilder.this.astore_1();
+        }
+
+        public C astore_2() {
+            return CodeBuilder.this.astore_2();
+        }
+
+        public C astore_3() {
+            return CodeBuilder.this.astore_3();
+        }
+
+        public C astore(int n) {
+            return CodeBuilder.this.astore(n);
+        }
+
+        public C aaload() {
+            return CodeBuilder.this.aaload();
+        }
+
+        public C aastore() {
+            return CodeBuilder.this.aastore();
+        }
+
+        public C areturn() {
+            return CodeBuilder.this.areturn();
+        }
+
+        public C anewarray(S s) {
+            return CodeBuilder.this.anewarray(s);
+        }
+
+        public C aconst_null() {
+            return CodeBuilder.this.aconst_null();
+        }
+
+        public C if_acmpeq(short target) {
+            return CodeBuilder.this.if_acmpeq(target);
+        }
+
+        public C if_acmpne(short target) {
+            return CodeBuilder.this.if_acmpeq(target);
+        }
+    }
+
+    public C vload(int i) {
+        return emitWideIfNeeded(Opcode.VLOAD, i);
+    }
+
+    public C aload(int i) {
+        return emitWideIfNeeded(Opcode.ALOAD, i);
+    }
+
+    public C iload(int i) {
+        return emitWideIfNeeded(Opcode.ILOAD, i);
+    }
+
+    public C fload(int i) {
+        return emitWideIfNeeded(Opcode.FLOAD, i);
+    }
+
+    public C lload(int i) {
+        return emitWideIfNeeded(Opcode.LLOAD, i);
+    }
+
+    public C dload(int i) {
+        return emitWideIfNeeded(Opcode.DLOAD, i);
+    }
+
+    public C aload_0() {
+        return emitOp(Opcode.ALOAD_0);
+    }
+
+    public C iload_0() {
+        return emitOp(Opcode.ILOAD_0);
+    }
+
+    public C fload_0() {
+        return emitOp(Opcode.FLOAD_0);
+    }
+
+    public C lload_0() {
+        return emitOp(Opcode.LLOAD_0);
+    }
+
+    public C dload_0() {
+        return emitOp(Opcode.DLOAD_0);
+    }
+
+    public C aload_1() {
+        return emitOp(Opcode.ALOAD_1);
+    }
+
+    public C iload_1() {
+        return emitOp(Opcode.ILOAD_1);
+    }
+
+    public C fload_1() {
+        return emitOp(Opcode.FLOAD_1);
+    }
+
+    public C lload_1() {
+        return emitOp(Opcode.LLOAD_1);
+    }
+
+    public C dload_1() {
+        return emitOp(Opcode.DLOAD_1);
+    }
+
+    public C aload_2() {
+        return emitOp(Opcode.ALOAD_2);
+    }
+
+    public C iload_2() {
+        return emitOp(Opcode.ILOAD_2);
+    }
+
+    public C fload_2() {
+        return emitOp(Opcode.FLOAD_2);
+    }
+
+    public C lload_2() {
+        return emitOp(Opcode.LLOAD_2);
+    }
+
+    public C dload_2() {
+        return emitOp(Opcode.DLOAD_2);
+    }
+
+    public C aload_3() {
+        return emitOp(Opcode.ALOAD_3);
+    }
+
+    public C iload_3() {
+        return emitOp(Opcode.ILOAD_3);
+    }
+
+    public C fload_3() {
+        return emitOp(Opcode.FLOAD_3);
+    }
+
+    public C lload_3() {
+        return emitOp(Opcode.LLOAD_3);
+    }
+
+    public C dload_3() {
+        return emitOp(Opcode.DLOAD_3);
+    }
+
+    public C vstore(int i) {
+        return emitWideIfNeeded(Opcode.VSTORE, i);
+    }
+
+    public C astore(int i) {
+        return emitWideIfNeeded(Opcode.ASTORE, i);
+    }
+
+    public C istore(int i) {
+        return emitWideIfNeeded(Opcode.ISTORE, i);
+    }
+
+    public C fstore(int i) {
+        return emitWideIfNeeded(Opcode.FSTORE, i);
+    }
+
+    public C lstore(int i) {
+        return emitWideIfNeeded(Opcode.LSTORE, i);
+    }
+
+    public C dstore(int i) {
+        return emitWideIfNeeded(Opcode.DSTORE, i);
+    }
+
+    public C astore_0() {
+        return emitOp(Opcode.ASTORE_0);
+    }
+
+    public C istore_0() {
+        return emitOp(Opcode.ISTORE_0);
+    }
+
+    public C fstore_0() {
+        return emitOp(Opcode.FSTORE_0);
+    }
+
+    public C lstore_0() {
+        return emitOp(Opcode.LSTORE_0);
+    }
+
+    public C dstore_0() {
+        return emitOp(Opcode.DSTORE_0);
+    }
+
+    public C astore_1() {
+        return emitOp(Opcode.ASTORE_1);
+    }
+
+    public C istore_1() {
+        return emitOp(Opcode.ISTORE_1);
+    }
+
+    public C fstore_1() {
+        return emitOp(Opcode.FSTORE_1);
+    }
+
+    public C lstore_1() {
+        return emitOp(Opcode.LSTORE_1);
+    }
+
+    public C dstore_1() {
+        return emitOp(Opcode.DSTORE_1);
+    }
+
+    public C astore_2() {
+        return emitOp(Opcode.ASTORE_2);
+    }
+
+    public C istore_2() {
+        return emitOp(Opcode.ISTORE_2);
+    }
+
+    public C fstore_2() {
+        return emitOp(Opcode.FSTORE_2);
+    }
+
+    public C lstore_2() {
+        return emitOp(Opcode.LSTORE_2);
+    }
+
+    public C dstore_2() {
+        return emitOp(Opcode.DSTORE_2);
+    }
+
+    public C astore_3() {
+        return emitOp(Opcode.ASTORE_3);
+    }
+
+    public C istore_3() {
+        return emitOp(Opcode.ISTORE_3);
+    }
+
+    public C fstore_3() {
+        return emitOp(Opcode.FSTORE_3);
+    }
+
+    public C lstore_3() {
+        return emitOp(Opcode.LSTORE_3);
+    }
+
+    public C dstore_3() {
+        return emitOp(Opcode.DSTORE_3);
+    }
+
+    //...
+
+    public C iaload() {
+        return emitOp(Opcode.IALOAD);
+    }
+
+    public C laload() {
+        return emitOp(Opcode.LALOAD);
+    }
+
+    public C faload() {
+        return emitOp(Opcode.FALOAD);
+    }
+
+    public C daload() {
+        return emitOp(Opcode.DALOAD);
+    }
+
+    public C vaload() {
+        return emitOp(Opcode.VALOAD);
+    }
+
+    public C aaload() {
+        return emitOp(Opcode.AALOAD);
+    }
+
+    public C baload() {
+        return emitOp(Opcode.BALOAD);
+    }
+
+    public C caload() {
+        return emitOp(Opcode.CALOAD);
+    }
+
+    public C saload() {
+        return emitOp(Opcode.SALOAD);
+    }
+
+    public C iastore() {
+        return emitOp(Opcode.IASTORE);
+    }
+
+    public C lastore() {
+        return emitOp(Opcode.LASTORE);
+    }
+
+    public C fastore() {
+        return emitOp(Opcode.FASTORE);
+    }
+
+    public C dastore() {
+        return emitOp(Opcode.DASTORE);
+    }
+
+    public C vastore() {
+        return emitOp(Opcode.VASTORE);
+    }
+
+    public C aastore() {
+        return emitOp(Opcode.AASTORE);
+    }
+
+    public C bastore() {
+        return emitOp(Opcode.BASTORE);
+    }
+
+    public C castore() {
+        return emitOp(Opcode.CASTORE);
+    }
+
+    public C sastore() {
+        return emitOp(Opcode.SASTORE);
+    }
+
+    public C nop() {
+        return emitOp(Opcode.NOP);
+    }
+
+    public C aconst_null() {
+        return emitOp(Opcode.ACONST_NULL);
+    }
+
+    public C iconst_0() {
+        return emitOp(Opcode.ICONST_0);
+    }
+
+    public C iconst_1() {
+        return emitOp(Opcode.ICONST_1);
+    }
+
+    public C iconst_2() {
+        return emitOp(Opcode.ICONST_2);
+    }
+
+    public C iconst_3() {
+        return emitOp(Opcode.ICONST_3);
+    }
+
+    public C iconst_4() {
+        return emitOp(Opcode.ICONST_4);
+    }
+
+    public C iconst_5() {
+        return emitOp(Opcode.ICONST_5);
+    }
+
+    public C iconst_m1() {
+        return emitOp(Opcode.ICONST_M1);
+    }
+
+    public C lconst_0() {
+        return emitOp(Opcode.LCONST_0);
+    }
+
+    public C lconst_1() {
+        return emitOp(Opcode.LCONST_1);
+    }
+
+    public C fconst_0() {
+        return emitOp(Opcode.FCONST_0);
+    }
+
+    public C fconst_1() {
+        return emitOp(Opcode.FCONST_1);
+    }
+
+    public C fconst_2() {
+        return emitOp(Opcode.FCONST_2);
+    }
+
+    public C dconst_0() {
+        return emitOp(Opcode.DCONST_0);
+    }
+
+    public C dconst_1() {
+        return emitOp(Opcode.DCONST_1);
+    }
+
+    public C sipush(int s) {
+        emitOp(Opcode.SIPUSH);
+        code.writeChar(s);
+        return thisBuilder();
+    }
+
+    public C bipush(int b) {
+        emitOp(Opcode.BIPUSH);
+        code.writeByte(b);
+        return thisBuilder();
+    }
+
+    public C pop() {
+        return emitOp(Opcode.POP);
+    }
+
+    public C pop2() {
+        return emitOp(Opcode.POP2);
+    }
+
+    public C dup() {
+        return emitOp(Opcode.DUP);
+    }
+
+    public C dup_x1() {
+        return emitOp(Opcode.DUP_X1);
+    }
+
+    public C dup_x2() {
+        return emitOp(Opcode.DUP_X2);
+    }
+
+    public C dup2() {
+        return emitOp(Opcode.DUP2);
+    }
+
+    public C dup2_x1() {
+        return emitOp(Opcode.DUP2_X1);
+    }
+
+    public C dup2_x2() {
+        return emitOp(Opcode.DUP2_X2);
+    }
+
+    public C swap() {
+        return emitOp(Opcode.SWAP);
+    }
+
+    public C iadd() {
+        return emitOp(Opcode.IADD);
+    }
+
+    public C ladd() {
+        return emitOp(Opcode.LADD);
+    }
+
+    public C fadd() {
+        return emitOp(Opcode.FADD);
+    }
+
+    public C dadd() {
+        return emitOp(Opcode.DADD);
+    }
+
+    public C isub() {
+        return emitOp(Opcode.ISUB);
+    }
+
+    public C lsub() {
+        return emitOp(Opcode.LSUB);
+    }
+
+    public C fsub() {
+        return emitOp(Opcode.FSUB);
+    }
+
+    public C dsub() {
+        return emitOp(Opcode.DSUB);
+    }
+
+    public C imul() {
+        return emitOp(Opcode.IMUL);
+    }
+
+    public C lmul() {
+        return emitOp(Opcode.LMUL);
+    }
+
+    public C fmul() {
+        return emitOp(Opcode.FMUL);
+    }
+
+    public C dmul() {
+        return emitOp(Opcode.DMUL);
+    }
+
+    public C idiv() {
+        return emitOp(Opcode.IDIV);
+    }
+
+    public C ldiv() {
+        return emitOp(Opcode.LDIV);
+    }
+
+    public C fdiv() {
+        return emitOp(Opcode.FDIV);
+    }
+
+    public C ddiv() {
+        return emitOp(Opcode.DDIV);
+    }
+
+    public C irem() {
+        return emitOp(Opcode.IREM);
+    }
+
+    public C lrem() {
+        return emitOp(Opcode.LREM);
+    }
+
+    public C frem() {
+        return emitOp(Opcode.FREM);
+    }
+
+    public C drem() {
+        return emitOp(Opcode.DREM);
+    }
+
+    public C ineg() {
+        return emitOp(Opcode.INEG);
+    }
+
+    public C lneg() {
+        return emitOp(Opcode.LNEG);
+    }
+
+    public C fneg() {
+        return emitOp(Opcode.FNEG);
+    }
+
+    public C dneg() {
+        return emitOp(Opcode.DNEG);
+    }
+
+    public C ishl() {
+        return emitOp(Opcode.ISHL);
+    }
+
+    public C lshl() {
+        return emitOp(Opcode.LSHL);
+    }
+
+    public C ishr() {
+        return emitOp(Opcode.ISHR);
+    }
+
+    public C lshr() {
+        return emitOp(Opcode.LSHR);
+    }
+
+    public C iushr() {
+        return emitOp(Opcode.IUSHR);
+    }
+
+    public C lushr() {
+        return emitOp(Opcode.LUSHR);
+    }
+
+    public C iand() {
+        return emitOp(Opcode.IAND);
+    }
+
+    public C land() {
+        return emitOp(Opcode.LAND);
+    }
+
+    public C ior() {
+        return emitOp(Opcode.IOR);
+    }
+
+    public C lor() {
+        return emitOp(Opcode.LOR);
+    }
+
+    public C ixor() {
+        return emitOp(Opcode.IXOR);
+    }
+
+    public C lxor() {
+        return emitOp(Opcode.LXOR);
+    }
+
+    public C iinc(int index, int val) {
+        return emitWideIfNeeded(Opcode.IINC, index, val);
+    }
+
+    public C i2l() {
+        return emitOp(Opcode.I2L);
+    }
+
+    public C i2f() {
+        return emitOp(Opcode.I2F);
+    }
+
+    public C i2d() {
+        return emitOp(Opcode.I2D);
+    }
+
+    public C l2i() {
+        return emitOp(Opcode.L2I);
+    }
+
+    public C l2f() {
+        return emitOp(Opcode.L2F);
+    }
+
+    public C l2d() {
+        return emitOp(Opcode.L2D);
+    }
+
+    public C f2i() {
+        return emitOp(Opcode.F2I);
+    }
+
+    public C f2l() {
+        return emitOp(Opcode.F2L);
+    }
+
+    public C f2d() {
+        return emitOp(Opcode.F2D);
+    }
+
+    public C d2i() {
+        return emitOp(Opcode.D2I);
+    }
+
+    public C d2l() {
+        return emitOp(Opcode.D2L);
+    }
+
+    public C d2f() {
+        return emitOp(Opcode.D2F);
+    }
+
+    public C i2b() {
+        return emitOp(Opcode.I2B);
+    }
+
+    public C i2c() {
+        return emitOp(Opcode.I2C);
+    }
+
+    public C i2s() {
+        return emitOp(Opcode.I2S);
+    }
+
+    public C lcmp() {
+        return emitOp(Opcode.LCMP);
+    }
+
+    public C fcmpl() {
+        return emitOp(Opcode.FCMPL);
+    }
+
+    public C fcmpg() {
+        return emitOp(Opcode.FCMPG);
+    }
+
+    public C dcmpl() {
+        return emitOp(Opcode.DCMPL);
+    }
+
+    public C dcmpg() {
+        return emitOp(Opcode.DCMPG);
+    }
+
+    public C ifeq(short target) {
+        return emitNarrowJumpOp(Opcode.IFEQ, target);
+    }
+
+    public C ifne(short target) {
+        return emitNarrowJumpOp(Opcode.IFNE, target);
+    }
+
+    public C iflt(short target) {
+        return emitNarrowJumpOp(Opcode.IFLT, target);
+    }
+
+    public C ifge(short target) {
+        return emitNarrowJumpOp(Opcode.IFGE, target);
+    }
+
+    public C ifgt(short target) {
+        return emitNarrowJumpOp(Opcode.IFGT, target);
+    }
+
+    public C ifle(short target) {
+        return emitNarrowJumpOp(Opcode.IFLE, target);
+    }
+
+    public C if_icmpeq(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPEQ, target);
+    }
+
+    public C if_icmpne(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPNE, target);
+    }
+
+    public C if_icmplt(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPLT, target);
+    }
+
+    public C if_icmpge(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPGE, target);
+    }
+
+    public C if_icmpgt(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPGT, target);
+    }
+
+    public C if_icmple(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPLE, target);
+    }
+
+    public C if_acmpeq(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ACMPEQ, target);
+    }
+
+    public C if_acmpne(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ACMPNE, target);
+    }
+
+    public C goto_(short target) {
+        return emitNarrowJumpOp(Opcode.GOTO_, target);
+    }
+
+    public C jsr(short target) {
+        return emitNarrowJumpOp(Opcode.JSR, target);
+    }
+
+    public C ret(int index) {
+        return emitWideIfNeeded(Opcode.RET, index);
+    }
+
+    public C tableswitch(int low, int high, int defaultTarget, int... targets) {
+        if (high - low + 1 != targets.length) throw new IllegalStateException("Bad targets length");
+        emitOp(Opcode.TABLESWITCH);
+        //padding
+        int start = code.offset;
+        if ((start % 4) != 0) {
+            //add padding
+            for (int i = 0; i < 4 - (start % 4); i++) {
+                code.writeByte(0);
+            }
+        }
+        code.writeInt(defaultTarget)
+                .writeInt(low)
+                .writeInt(high);
+        for (int target : targets) {
+            code.writeInt(target);
+        }
+        return thisBuilder();
+    }
+
+    public C lookupswitch(int defaultTarget, int... npairs) {
+        if (npairs.length % 2 != 0) throw new IllegalStateException("Bad npairs length");
+        emitOp(Opcode.LOOKUPSWITCH);
+        //padding
+        int start = code.offset;
+        for (int i = 0; i < (4 - (start % 4)); i++) {
+            code.writeByte(0);
+        }
+        code.writeInt(defaultTarget)
+                .writeInt(npairs.length / 2);
+        for (int i = 0; i < npairs.length; i += 2) {
+            code.writeInt(npairs[i]);
+            code.writeInt(npairs[i + 1]);
+        }
+        return thisBuilder();
+    }
+
+    public C arraylength() {
+        return emitOp(Opcode.ARRAYLENGTH);
+    }
+
+    public C athrow() {
+        return emitOp(Opcode.ATHROW);
+    }
+
+    public C monitorenter() {
+        return emitOp(Opcode.MONITORENTER);
+    }
+
+    public C monitorexit() {
+        return emitOp(Opcode.MONITOREXIT);
+    }
+
+    public C wide() {
+        return emitOp(Opcode.WIDE);
+    }
+
+    public C if_null(short offset) {
+        return emitNarrowJumpOp(Opcode.IF_NULL, offset);
+    }
+
+    public C if_nonnull(short offset) {
+        return emitNarrowJumpOp(Opcode.IF_NONNULL, offset);
+    }
+
+    public C goto_w(int target) {
+        return emitWideJumpOp(Opcode.GOTO_W, target);
+    }
+
+    public C jsr_w(int target) {
+        return emitWideJumpOp(Opcode.JSR_W, target);
+    }
+
+    public C withCatch(S type, int start, int end, int offset) {
+        catchers.writeChar(start);
+        catchers.writeChar(end);
+        catchers.writeChar(offset);
+        catchers.writeChar(type != null ? poolHelper.putClass(type) : 0);
+        ncatchers++;
+        return thisBuilder();
+    }
+
+    public C withLocalSize(int localsize) {
+        this.localsize = localsize;
+        return thisBuilder();
+    }
+
+    public C withStackSize(int stacksize) {
+        this.stacksize = stacksize;
+        return thisBuilder();
+    }
+
+    protected int localsize() {
+        return localsize;
+    }
+
+    void build(GrowableByteBuffer buf) {
+        buf.writeChar(stacksize); //max stack size
+        buf.writeChar(localsize()); //max locals
+        buf.writeInt(code.offset);
+        buf.writeBytes(code);
+        buf.writeChar(ncatchers);
+        buf.writeBytes(catchers);
+        buf.writeChar(nattrs); //attributes
+        buf.writeBytes(attributes);
+    }
+
+    byte[] build() {
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        build(buf);
+        return buf.bytes();
+    }
+
+    protected C emitNarrowJumpOp(Opcode opcode, short target) {
+        emitOp(opcode);
+        emitOffset(code, JumpMode.NARROW, target);
+        return thisBuilder();
+    }
+
+    protected C emitWideJumpOp(Opcode opcode, int target) {
+        emitOp(opcode);
+        emitOffset(code, JumpMode.WIDE, target);
+        return thisBuilder();
+    }
+
+    protected C emitOp(Opcode opcode) {
+        return emitOp(opcode, null);
+    }
+
+    protected C emitOp(Opcode opcode, Object optPoolValue) {
+        code.writeByte(opcode.code);
+        return thisBuilder();
+    }
+
+    protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+        if (jumpMode == JumpMode.NARROW) {
+            buf.writeChar((short) offset);
+        } else {
+            buf.writeInt(offset);
+        }
+    }
+
+    int offset() {
+        return code.offset;
+    }
+
+    /*** stackmap support ***/
+
+    /**
+     * The tags and constants used in compressed stackmap.
+     */
+    static final int SAME_FRAME_SIZE = 64;
+    static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
+    static final int SAME_FRAME_EXTENDED = 251;
+    static final int FULL_FRAME = 255;
+    static final int MAX_LOCAL_LENGTH_DIFF = 4;
+
+    @SuppressWarnings("unchecked")
+    private void writeStackMapType(T t) {
+        if (t == null) {
+            stackmaps.writeByte(0);
+        } else {
+            switch (typeHelper.tag(t)) {
+                case B:
+                case C:
+                case S:
+                case I:
+                case Z:
+                    stackmaps.writeByte(1);
+                    break;
+                case F:
+                    stackmaps.writeByte(2);
+                    break;
+                case D:
+                    stackmaps.writeByte(3);
+                    break;
+                case J:
+                    stackmaps.writeByte(4);
+                    break;
+                case A:
+                    if (t == typeHelper.nullType()) {
+                        stackmaps.writeByte(5); //null
+                    } else {
+                        //TODO: uninit this, top?
+                        stackmaps.writeByte(7);
+                        stackmaps.writeChar(poolHelper.putClass(typeHelper.symbol(t)));
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("Bad type");
+            }
+        }
+    }
+
+    public void sameFrame(int offsetDelta) {
+        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+                offsetDelta : SAME_FRAME_EXTENDED;
+        stackmaps.writeByte(frameType);
+        if (frameType == SAME_FRAME_EXTENDED) {
+            stackmaps.writeChar(offsetDelta);
+        }
+    }
+
+    public void sameLocals1StackItemFrame(int offsetDelta, T stackItem) {
+        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+                (SAME_FRAME_SIZE + offsetDelta) : SAME_LOCALS_1_STACK_ITEM_EXTENDED;
+        stackmaps.writeByte(frameType);
+        if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
+            stackmaps.writeChar(offsetDelta);
+        }
+        writeStackMapType(stackItem);
+    }
+
+    public void appendFrame(int offsetDelta, int prevLocalsSize, List<T> locals) {
+        int frameType = SAME_FRAME_EXTENDED + (locals.size() - prevLocalsSize);
+        stackmaps.writeByte(frameType);
+        stackmaps.writeChar(offsetDelta);
+        for (int i = prevLocalsSize; i < locals.size(); i++) {
+            writeStackMapType(locals.get(i));
+        }
+    }
+
+    public void chopFrame(int offsetDelta, int droppedVars) {
+        int frameType = SAME_FRAME_EXTENDED - droppedVars;
+        stackmaps.writeByte(frameType);
+        stackmaps.writeChar(offsetDelta);
+    }
+
+    public void fullFrame(int offsetDelta, List<T> locals, List<T> stackItems) {
+        stackmaps.writeByte(FULL_FRAME);
+        stackmaps.writeChar(offsetDelta);
+        stackmaps.writeChar(locals.size());
+        for (T local : locals) {
+            writeStackMapType(local);
+        }
+
+        stackmaps.writeChar(stackItems.size());
+        for (T stackType : stackItems) {
+            writeStackMapType(stackType);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+/**
+ * Declaration (class, class member, ...) builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+public class DeclBuilder<S, T, E, D extends DeclBuilder<S, T, E, D>>
+        extends AttributeBuilder<S, T, E, D> {
+
+    /**
+     * The access flags of the declaration, as bit flags.
+     */
+    protected int flags;
+
+    AnnotationsBuilder<S, T, E> runtimeInvisibleAnnotations;
+    AnnotationsBuilder<S, T, E> runtimeVisibleAnnotations;
+
+    /**
+     * Create a declaration builder,
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    DeclBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+    }
+
+    /**
+     * Specify the class file flags for this declaration.
+     *
+     * @param flags the flags as {@code Flag} objects
+     * @return this builder, for chained calls
+     */
+    public D withFlags(Flag... flags) {
+        for (Flag f : flags) {
+            this.flags |= f.flag;
+        }
+        return thisBuilder();
+    }
+
+    /**
+     * Specify, via bits, the class file flags for this declaration.
+     *
+     * @param flags the flags as bit settings
+     * @return this builder, for chained calls
+     */
+    public D withFlags(int flags) {
+        withFlags(Flag.parse(flags));
+        return thisBuilder();
+    }
+
+    public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType) {
+        getAnnotations(kind).withAnnotation(annoType, null);
+        return thisBuilder();
+    }
+
+    public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
+        getAnnotations(kind).withAnnotation(annoType, annotations);
+        return thisBuilder();
+    }
+
+    private AnnotationsBuilder<S, T, E> getAnnotations(AnnotationsBuilder.Kind kind) {
+        switch (kind) {
+            case RUNTIME_INVISIBLE:
+                if (runtimeInvisibleAnnotations == null) {
+                    runtimeInvisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
+                }
+                return runtimeInvisibleAnnotations;
+            case RUNTIME_VISIBLE:
+                if (runtimeVisibleAnnotations == null) {
+                    runtimeVisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
+                }
+                return runtimeVisibleAnnotations;
+        }
+        throw new IllegalStateException();
+    }
+
+    void addAnnotations() {
+        if (runtimeVisibleAnnotations != null) {
+            withAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotations.build());
+        }
+        if (runtimeInvisibleAnnotations != null) {
+            withAttribute("RuntimeInvisibleAnnotations", runtimeVisibleAnnotations.build());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+/**
+ * Field builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptor representation
+ * @param <E> the type of pool entries
+ */
+public class FieldBuilder<S, T, E> extends MemberBuilder<S, T, E, FieldBuilder<S, T, E>> {
+    public FieldBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(name, type, poolHelper, typeHelper);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.EnumSet;
+
+public enum Flag {
+    ACC_PUBLIC(0x0001),
+    ACC_PROTECTED(0x0004),
+    ACC_PRIVATE(0x0002),
+    ACC_INTERFACE(0x0200),
+    ACC_ENUM(0x4000),
+    ACC_ANNOTATION(0x2000),
+    ACC_SUPER(0x0020),
+    ACC_ABSTRACT(0x0400),
+    ACC_VOLATILE(0x0040),
+    ACC_TRANSIENT(0x0080),
+    ACC_SYNTHETIC(0x1000),
+    ACC_STATIC(0x0008),
+    ACC_FINAL(0x0010),
+    ACC_SYNCHRONIZED(0x0020),
+    ACC_BRIDGE(0x0040),
+    ACC_VARARGS(0x0080),
+    ACC_NATIVE(0x0100),
+    ACC_STRICT(0x0800);
+
+    public int flag;
+
+    Flag(int flag) {
+        this.flag = flag;
+    }
+
+    static Flag[] parse(int flagsMask) {
+        EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
+        for (Flag f : Flag.values()) {
+            if ((f.flag & flagsMask) != 0) {
+                flags.add(f);
+            }
+        }
+        return flags.stream().toArray(Flag[]::new);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+public class GrowableByteBuffer {
+
+    public GrowableByteBuffer() {
+    }
+
+    byte[] elems = new byte[64];
+    int offset = 0;
+
+    public GrowableByteBuffer writeByte(int b) {
+        return writeBytes(1, b);
+    }
+
+    public GrowableByteBuffer writeChar(int x) {
+        return writeBytes(2, x);
+    }
+
+    public GrowableByteBuffer writeInt(int x) {
+        return writeBytes(4, x);
+    }
+
+    public GrowableByteBuffer writeFloat(float x) {
+        return writeInt(Float.floatToIntBits(x));
+    }
+
+    public GrowableByteBuffer writeLong(long x) {
+        return writeBytes(8, x);
+    }
+
+    public GrowableByteBuffer writeDouble(double x) {
+        writeLong(Double.doubleToLongBits(x));
+        return this;
+    }
+
+    public GrowableByteBuffer writeBytes(byte[] barr) {
+        expandIfNeeded(barr.length);
+        System.arraycopy(barr, 0, elems, offset, barr.length);
+        offset += barr.length;
+        return this;
+    }
+
+    public GrowableByteBuffer writeBytes(GrowableByteBuffer bb) {
+        expandIfNeeded(bb.offset);
+        System.arraycopy(bb.elems, 0, elems, offset, bb.offset);
+        offset += bb.offset;
+        return this;
+    }
+
+    public GrowableByteBuffer withOffset(int offset, Consumer<GrowableByteBuffer> actions) {
+        int prevOffset = this.offset;
+        this.offset = offset;
+        actions.accept(this);
+        this.offset = prevOffset;
+        return this;
+    }
+
+    private GrowableByteBuffer writeBytes(int size, long x) {
+        expandIfNeeded(size);
+        for (int i = 0; i < size; i++) {
+            elems[offset++] = (byte) ((x >> 8 * (size - i - 1)) & 0xFF);
+        }
+        return this;
+    }
+
+    void expandIfNeeded(int increment) {
+        if (offset + increment > elems.length) {
+            int newsize = elems.length * 2;
+            while (offset + increment > newsize) {
+                newsize *= 2;
+            }
+            byte[] newelems = new byte[newsize];
+            System.arraycopy(elems, 0, newelems, 0, offset);
+            elems = newelems;
+        }
+    }
+
+    public byte[] bytes() {
+        byte[] bytes = new byte[offset];
+        System.arraycopy(elems, 0, bytes, 0, offset);
+        return bytes;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/IsolatedMethodBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+public class IsolatedMethodBuilder extends MethodBuilder<Class<?>, String, Object[]> {
+
+    public IsolatedMethodBuilder(Lookup lookup, String name, String type) {
+        super(null, name, type, new IsolatedMethodPoolHelper(lookup), null);
+    }
+
+    static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, Object[]> {
+        Map<Object, Integer> constants = new HashMap<>();
+        Lookup lookup;
+
+        private IsolatedMethodPoolHelper(Lookup lookup) {
+            this.lookup = lookup;
+        }
+
+        @Override
+        public int putClass(Class<?> symbol) {
+            return putIfAbsent(symbol);
+        }
+
+        @Override
+        public int putFieldRef(Class<?> owner, CharSequence name, String type) {
+            try {
+                Field f = owner.getDeclaredField(name.toString()); //TODO: we should unreflect for a var handle
+                return putIfAbsent(lookup.unreflectGetter(f));
+            } catch (Throwable ex) {
+                ex.printStackTrace();
+                return -1;
+            }
+        }
+
+        @Override
+        public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) {
+            try {
+                Method m = owner.getDeclaredMethod(name.toString()); //we should unreflect according to method vs. constructor
+                //and static vs. private etc.
+                return putIfAbsent(lookup.unreflect(m));
+            } catch (Throwable ex) {
+                ex.printStackTrace();
+                return -1;
+            }
+        }
+
+        @Override
+        public int putInt(int i) {
+            return putIfAbsent(i);
+        }
+
+        @Override
+        public int putFloat(float f) {
+            return putIfAbsent(f);
+        }
+
+        @Override
+        public int putLong(long l) {
+            return putIfAbsent(l);
+        }
+
+        @Override
+        public int putDouble(double d) {
+            return putIfAbsent(d);
+        }
+
+        @Override
+        public int putString(String s) {
+            return putIfAbsent(s);
+        }
+
+        @Override
+        public int putInvokeDynamic(CharSequence invokedName, String invokedType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
+            return 0; //???
+        }
+
+        @Override
+        public int putDynamicConstant(CharSequence constName, String constType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
+            return 0; //???
+        }
+
+        @Override
+        public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) {
+            return 0; //???
+        }
+
+        @Override
+        public int putMethodType(String s) {
+            return 0; //???
+        }
+
+        @Override
+        public int putUtf8(CharSequence s) {
+            return putIfAbsent(s);
+        }
+
+        @Override
+        public int putType(String s) {
+            return putIfAbsent(s);
+        }
+
+        @Override
+        public int size() {
+            return constants.size();
+        }
+
+        @Override
+        public Object[] entries() {
+            return constants.keySet().toArray();
+        }
+
+        int putIfAbsent(Object o) {
+            int nextIndex = constants.size() + 1;
+            Object res = constants.putIfAbsent(o, nextIndex);
+            return res == null ?
+                    nextIndex : (Integer)res;
+        }
+    }
+
+    public Object[] entries() {
+        return poolHelper.entries();
+    }
+
+    @Override
+    public byte[] build() {
+        byte[] arr = super.build();
+        int codelength_offset = 2 + 2 + 2 + 2 +
+                2 + 4 + 2 + 2;
+        int code_offset = codelength_offset + 4;
+        int length = ByteBuffer.wrap(arr).getInt(codelength_offset);
+        byte[] opcodes = new byte[length];
+        System.arraycopy(arr, code_offset, opcodes, 0, length);
+        return opcodes;
+    }
+
+    public static void main(String[] args) {
+        IsolatedMethodBuilder imb =  new IsolatedMethodBuilder(MethodHandles.lookup(), "foo", "(java/lang/String;)I");
+        imb.withCode(C ->
+                    C.aload_0()
+                     .invokevirtual(String.class, "length", "()I", false)
+                     .ireturn());
+        byte[] opcodes = imb.build();
+        System.out.println(Arrays.toString(opcodes));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> {
+
+    JumpMode jumpMode = JumpMode.NARROW;
+
+    Map<CharSequence, Integer> labels = new HashMap<>();
+    List<PendingJump> pendingJumps = new LinkedList<>();
+
+    class PendingJump {
+        CharSequence label;
+        int pc;
+
+        PendingJump(CharSequence label, int pc) {
+            this.label = label;
+            this.pc = pc;
+        }
+
+        boolean resolve(CharSequence label, int offset) {
+            if (this.label.equals(label)) {
+                //patch offset
+                code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc));
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public enum InvocationKind {
+        INVOKESTATIC,
+        INVOKEVIRTUAL,
+        INVOKESPECIAL,
+        INVOKEINTERFACE;
+    }
+
+    public enum FieldAccessKind {
+        STATIC,
+        INSTANCE;
+    }
+
+    public enum CondKind {
+        EQ(0),
+        NE(1),
+        LT(2),
+        GE(3),
+        GT(4),
+        LE(5);
+
+        int offset;
+
+        CondKind(int offset) {
+            this.offset = offset;
+        }
+
+        public CondKind negate() {
+            switch (this) {
+                case EQ:
+                    return NE;
+                case NE:
+                    return EQ;
+                case LT:
+                    return GE;
+                case GE:
+                    return LT;
+                case GT:
+                    return LE;
+                case LE:
+                    return GT;
+                default:
+                    throw new IllegalStateException("Unknown cond");
+            }
+        }
+    }
+
+    static class WideJumpException extends RuntimeException {
+        static final long serialVersionUID = 42L;
+    }
+
+    public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder);
+    }
+
+    public C load(TypeTag type, int n) {
+        if (type == TypeTag.Q) {
+            return vload(n);
+        } else {
+            switch (n) {
+                case 0:
+                    return emitOp(Opcode.ILOAD_0.at(type, 4));
+                case 1:
+                    return emitOp(Opcode.ILOAD_1.at(type, 4));
+                case 2:
+                    return emitOp(Opcode.ILOAD_2.at(type, 4));
+                case 3:
+                    return emitOp(Opcode.ILOAD_3.at(type, 4));
+                default:
+                    return emitWideIfNeeded(Opcode.ILOAD.at(type), n);
+            }
+        }
+    }
+
+    public C store(TypeTag type, int n) {
+        if (type == TypeTag.Q) {
+            return vstore(n);
+        } else {
+            switch (n) {
+                case 0:
+                    return emitOp(Opcode.ISTORE_0.at(type, 4));
+                case 1:
+                    return emitOp(Opcode.ISTORE_1.at(type, 4));
+                case 2:
+                    return emitOp(Opcode.ISTORE_2.at(type, 4));
+                case 3:
+                    return emitOp(Opcode.ISTORE_3.at(type, 4));
+                default:
+                    return emitWideIfNeeded(Opcode.ISTORE.at(type), n);
+            }
+        }
+    }
+
+    public C arrayload(TypeTag type) {
+        return emitOp(Opcode.IALOAD.at(type));
+    }
+
+    public C arraystore(TypeTag type, int n) {
+        return emitOp(Opcode.IASTORE.at(type));
+    }
+
+    public C const_(int i) {
+        switch (i) {
+            case -1:
+                return iconst_m1();
+            case 0:
+                return iconst_0();
+            case 1:
+                return iconst_1();
+            case 2:
+                return iconst_2();
+            case 3:
+                return iconst_3();
+            case 4:
+                return iconst_4();
+            case 5:
+                return iconst_5();
+            default:
+                if (i > 0 && i <= Byte.MAX_VALUE) {
+                    return bipush(i);
+                } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
+                    return sipush(i);
+                } else {
+                    return ldc(i);
+                }
+        }
+    }
+
+    public C const_(long l) {
+        if (l == 0) {
+            return lconst_0();
+        } else if (l == 1) {
+            return lconst_1();
+        } else {
+            return ldc(l);
+        }
+    }
+
+    public C const_(float f) {
+        if (f == 0) {
+            return fconst_0();
+        } else if (f == 1) {
+            return fconst_1();
+        } else if (f == 2) {
+            return fconst_2();
+        } else {
+            return ldc(f);
+        }
+    }
+
+    public C const_(double d) {
+        if (d == 0) {
+            return dconst_0();
+        } else if (d == 1) {
+            return dconst_1();
+        } else {
+            return ldc(d);
+        }
+    }
+
+    public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+        switch (fak) {
+            case INSTANCE:
+                return getfield(owner, name, type);
+            case STATIC:
+                return getstatic(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+        switch (fak) {
+            case INSTANCE:
+                return putfield(owner, name, type);
+            case STATIC:
+                return putstatic(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) {
+        switch (ik) {
+            case INVOKESTATIC:
+                return invokestatic(owner, name, type, isInterface);
+            case INVOKEVIRTUAL:
+                return invokevirtual(owner, name, type, isInterface);
+            case INVOKESPECIAL:
+                return invokespecial(owner, name, type, isInterface);
+            case INVOKEINTERFACE:
+                if (!isInterface) throw new AssertionError();
+                return invokeinterface(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C add(TypeTag type) {
+        return emitOp(Opcode.IADD.at(type));
+    }
+
+    public C sub(TypeTag type) {
+        return emitOp(Opcode.ISUB.at(type));
+    }
+
+    public C mul(TypeTag type) {
+        return emitOp(Opcode.IMUL.at(type));
+    }
+
+    public C div(TypeTag type) {
+        return emitOp(Opcode.IDIV.at(type));
+    }
+
+    public C rem(TypeTag type) {
+        return emitOp(Opcode.IREM.at(type));
+    }
+
+    public C neg(TypeTag type) {
+        return emitOp(Opcode.INEG.at(type));
+    }
+
+    public C shl(TypeTag type) {
+        return emitOp(Opcode.ISHL.at(type));
+    }
+
+    public C shr(TypeTag type) {
+        return emitOp(Opcode.ISHR.at(type));
+    }
+
+    public C ushr(TypeTag type) {
+        return emitOp(Opcode.ISHR.at(type));
+    }
+
+    public C and(TypeTag type) {
+        return emitOp(Opcode.IAND.at(type));
+    }
+
+    public C or(TypeTag type) {
+        return emitOp(Opcode.IOR.at(type));
+    }
+
+    public C xor(TypeTag type) {
+        return emitOp(Opcode.IXOR.at(type));
+    }
+
+    public C return_(TypeTag type) {
+        switch (type) {
+            case V:
+                return return_();
+            case Q:
+                return vreturn();
+            default:
+                return emitOp(Opcode.IRETURN.at(type));
+        }
+    }
+
+    @Override
+    public LabelledTypedBuilder typed(TypeTag typeTag) {
+        return super.typed(typeTag, _unused -> new LabelledTypedBuilder());
+    }
+
+    public class LabelledTypedBuilder extends TypedBuilder {
+        public C if_acmpeq(CharSequence target) {
+            return ifcmp(TypeTag.A, CondKind.EQ, target);
+        }
+
+        public C if_acmpne(CharSequence target) {
+            return ifcmp(TypeTag.A, CondKind.NE, target);
+        }
+    }
+
+    public C conv(TypeTag from, TypeTag to) {
+        switch (from) {
+            case B:
+            case C:
+            case S:
+                switch (to) {
+                    case J:
+                        return i2l();
+                    case F:
+                        return i2f();
+                    case D:
+                        return i2d();
+                }
+                break;
+            case I:
+                switch (to) {
+                    case J:
+                        return i2l();
+                    case F:
+                        return i2f();
+                    case D:
+                        return i2d();
+                    case B:
+                        return i2b();
+                    case C:
+                        return i2c();
+                    case S:
+                        return i2s();
+                }
+                break;
+            case J:
+                switch (to) {
+                    case I:
+                        return l2i();
+                    case F:
+                        return l2f();
+                    case D:
+                        return l2d();
+                }
+                break;
+            case F:
+                switch (to) {
+                    case I:
+                        return f2i();
+                    case J:
+                        return f2l();
+                    case D:
+                        return f2d();
+                }
+                break;
+            case D:
+                switch (to) {
+                    case I:
+                        return d2i();
+                    case J:
+                        return d2l();
+                    case F:
+                        return d2f();
+                }
+                break;
+        }
+        //no conversion is necessary - do nothing!
+        return thisBuilder();
+    }
+
+    public C if_null(CharSequence label) {
+        return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label);
+    }
+
+    public C if_nonnull(CharSequence label) {
+        return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label);
+    }
+
+    public C ifcmp(TypeTag type, CondKind cond, CharSequence label) {
+        switch (type) {
+            case I:
+                return emitCondJump(Opcode.IF_ICMPEQ, cond, label);
+            case A:
+                return emitCondJump(Opcode.IF_ACMPEQ, cond, label);
+            case J:
+                return lcmp().emitCondJump(Opcode.IFEQ, cond, label);
+            case D:
+                return dcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+            case F:
+                return fcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+            default:
+                throw new IllegalArgumentException("Bad cmp type");
+        }
+    }
+
+    public C goto_(CharSequence label) {
+        emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
+        emitOffset(code, jumpMode, labelOffset(label));
+        return thisBuilder();
+    }
+
+    protected int labelOffset(CharSequence label) {
+        int pc = code.offset - 1;
+        Integer labelPc = labels.get(label);
+        if (labelPc == null) {
+            addPendingJump(label, pc);
+        }
+        return labelPc == null ? 0 : (labelPc - pc);
+    }
+
+    public C label(CharSequence s) {
+        int pc = code.offset;
+        Object old = labels.put(s, pc);
+        if (old != null) {
+            throw new IllegalStateException("label already exists");
+        }
+        resolveJumps(s, pc);
+        return thisBuilder();
+    }
+
+    //FIXME: address this jumpy mess - i.e. offset and state update work against each other!
+    public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) {
+        return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label);
+    }
+
+    public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) {
+        if (jumpMode == JumpMode.NARROW) {
+            emitOp(pos);
+            emitOffset(code, jumpMode, labelOffset(label));
+        } else {
+            emitOp(neg);
+            emitOffset(code, JumpMode.NARROW, 8);
+            goto_w(labelOffset(label));
+        }
+        return thisBuilder();
+    }
+
+    void addPendingJump(CharSequence label, int pc) {
+        pendingJumps.add(new PendingJump(label, pc));
+    }
+
+    void resolveJumps(CharSequence label, int pc) {
+        Iterator<PendingJump> jumpsIt = pendingJumps.iterator();
+        while (jumpsIt.hasNext()) {
+            PendingJump jump = jumpsIt.next();
+            if (jump.resolve(label, pc)) {
+                jumpsIt.remove();
+            }
+        }
+    }
+
+    @Override
+    protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+        if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) {
+            throw new WideJumpException();
+        }
+        super.emitOffset(buf, jumpMode, offset);
+    }
+
+    public C jsr(CharSequence label) {
+        emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W);
+        emitOffset(code, jumpMode, labelOffset(label));
+        return thisBuilder();
+    }
+
+    @SuppressWarnings("unchecked")
+    public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+        int start = code.offset;
+        tryBlock.accept((C) this);
+        int end = code.offset;
+        CatchBuilder catchBuilder = makeCatchBuilder(start, end);
+        catchBlocks.accept(catchBuilder);
+        catchBuilder.build();
+        return thisBuilder();
+    }
+
+    void clear() {
+        code.offset = 0;
+        catchers.offset = 0;
+        ncatchers = 0;
+        labels.clear();
+        pendingJumps = null;
+    }
+
+    protected CatchBuilder makeCatchBuilder(int start, int end) {
+        return new CatchBuilder(start, end);
+    }
+
+    public class CatchBuilder {
+        int start, end;
+
+        String endLabel = labelName();
+
+        Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>();
+        public Consumer<? super C> finalizer;
+        List<Integer> pendingGaps = new ArrayList<>();
+
+        public CatchBuilder(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+            catchers.put(exc, catcher);
+            return this;
+        }
+
+        public CatchBuilder withFinally(Consumer<? super C> finalizer) {
+            this.finalizer = finalizer;
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        void build() {
+            if (finalizer != null) {
+                finalizer.accept((C) MacroCodeBuilder.this);
+            }
+            goto_(endLabel);
+            for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) {
+                emitCatch(catcher_entry.getKey(), catcher_entry.getValue());
+            }
+            if (finalizer != null) {
+                emitFinalizer();
+            }
+            resolveJumps(endLabel, code.offset);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected void emitCatch(S exc, Consumer<? super C> catcher) {
+            int offset = code.offset;
+            MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+            catcher.accept((C) MacroCodeBuilder.this);
+            if (finalizer != null) {
+                int startFinalizer = code.offset;
+                finalizer.accept((C) MacroCodeBuilder.this);
+                pendingGaps.add(startFinalizer);
+                pendingGaps.add(code.offset);
+            }
+            goto_(endLabel);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected void emitFinalizer() {
+            int offset = code.offset;
+            pop();
+            for (int i = 0; i < pendingGaps.size(); i += 2) {
+                MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset);
+            }
+            MacroCodeBuilder.this.withCatch(null, start, end, offset);
+            finalizer.accept((C) MacroCodeBuilder.this);
+        }
+
+//        @SuppressWarnings("unchecked")
+//        CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+//            int offset = code.offset;
+//            MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+//            catcher.accept((C)MacroCodeBuilder.this);
+//            return this;
+//        }
+//
+//        @SuppressWarnings("unchecked")
+//        CatchBuilder withFinally(Consumer<? super C> catcher) {
+//            int offset = code.offset;
+//            MacroCodeBuilder.this.withCatch(null, start, end, offset);
+//            catcher.accept((C)MacroCodeBuilder.this);
+//            return this;
+//        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public C switch_(Consumer<? super SwitchBuilder> consumer) {
+        int start = code.offset;
+        SwitchBuilder sb = makeSwitchBuilder();
+        consumer.accept(sb);
+        int nlabels = sb.cases.size();
+        switch (sb.switchCode()) {
+            case LOOKUPSWITCH: {
+                int[] lookupOffsets = new int[nlabels * 2];
+                int i = 0;
+                for (Integer v : sb.cases.keySet()) {
+                    lookupOffsets[i] = v;
+                    i += 2;
+                }
+                lookupswitch(0, lookupOffsets);
+                //backpatch lookup
+                int curr = code.offset - (8 * nlabels) - 8;
+                int defaultOffset = code.offset - start;
+                code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+                sb.defaultCase.accept((C) this);
+                curr += 12;
+                for (Consumer<? super C> case_ : sb.cases.values()) {
+                    int offset = code.offset;
+                    code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start));
+                    case_.accept((C) this);
+                    curr += 8;
+                }
+                break;
+            }
+            case TABLESWITCH: {
+                int[] tableOffsets = new int[sb.hi - sb.lo + 1];
+                tableswitch(sb.lo, sb.hi, 0, tableOffsets);
+                //backpatch table
+                int curr = code.offset - (4 * tableOffsets.length) - 12;
+                int defaultOffset = code.offset - start;
+                code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+                sb.defaultCase.accept((C) this);
+                curr += 12;
+                int lastCasePc = -1;
+                for (int i = sb.lo; i <= sb.hi; i++) {
+                    Consumer<? super C> case_ = sb.cases.get(i);
+                    if (case_ != null) {
+                        lastCasePc = code.offset;
+                        case_.accept((C) this);
+                    }
+                    int offset = lastCasePc - start;
+                    code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset));
+                    curr += 4;
+                }
+            }
+        }
+        resolveJumps(sb.endLabel, code.offset);
+        return thisBuilder();
+    }
+
+    private static int labelCount = 0;
+
+    String labelName() {
+        return "label" + labelCount++;
+    }
+
+    protected SwitchBuilder makeSwitchBuilder() {
+        return new SwitchBuilder();
+    }
+
+    public class SwitchBuilder {
+        Map<Integer, Consumer<? super C>> cases = new TreeMap<>();
+        int lo = Integer.MAX_VALUE;
+        int hi = Integer.MIN_VALUE;
+        String endLabel = labelName();
+
+        public Consumer<? super C> defaultCase;
+
+        @SuppressWarnings("unchecked")
+        public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+            if (value > hi) {
+                hi = value;
+            }
+            if (value < lo) {
+                lo = value;
+            }
+            if (!fallthrough) {
+                Consumer<? super C> prevCase = case_;
+                case_ = C -> {
+                    prevCase.accept(C);
+                    C.goto_(endLabel);
+                };
+            }
+            cases.put(value, case_);
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+            if (this.defaultCase != null) {
+                throw new IllegalStateException("default already set");
+            }
+            this.defaultCase = defaultCase;
+            return this;
+        }
+
+        Opcode switchCode() {
+            int nlabels = cases.size();
+            // Determine whether to issue a tableswitch or a lookupswitch
+            // instruction.
+            long table_space_cost = 4 + ((long) hi - lo + 1); // words
+            long lookup_space_cost = 3 + 2 * (long) nlabels;
+            return
+                    nlabels > 0 &&
+                            table_space_cost <= lookup_space_cost
+                            ?
+                            Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+/**
+ * Class member builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <M> the type of this builder
+ */
+public class MemberBuilder<S, T, E, M extends MemberBuilder<S, T, E, M>> extends DeclBuilder<S, T, E, M> {
+
+    CharSequence name;
+    T desc;
+
+    /**
+     * Create a member builder.
+     *
+     * @param name the name of the class member
+     * @param type the type descriptor of the class member
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    MemberBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.name = name;
+        this.desc = type;
+    }
+
+    /**
+     * Build the member.
+     *
+     * @param buf the {@code GrowableByteBuffer} to build the member into
+     */
+    protected void build(GrowableByteBuffer buf) {
+        addAnnotations();
+        buf.writeChar(flags);
+        buf.writeChar(poolHelper.putUtf8(name));
+        buf.writeChar(poolHelper.putType(desc));
+        buf.writeChar(nattrs);
+        buf.writeBytes(attributes);
+    }
+
+    /**
+     * Build the member.
+     *
+     * @return a byte array representation of the member
+     */
+    protected byte[] build() {
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        addAnnotations();
+        build(buf);
+        return buf.bytes();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.CodeBuilder.JumpMode;
+
+import java.util.Iterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class MethodBuilder<S, T, E> extends MemberBuilder<S, T, E, MethodBuilder<S, T, E>> {
+
+    S thisClass;
+    ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations;
+    ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations;
+
+    public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) {
+        super(name, type, pool, typeHelper);
+        this.thisClass = thisClass;
+    }
+
+    public <C extends CodeBuilder<S, T, E, ?>> MethodBuilder<S, T, E> withCode(Function<? super MethodBuilder<S, T, E>, ? extends C> func,
+                                                                               Consumer<? super C> code) {
+        C codeBuilder = func.apply(this);
+        int start = attributes.offset;
+        try {
+            code.accept(codeBuilder);
+        } catch (MacroCodeBuilder.WideJumpException ex) {
+            //wide jumps! Redo the code
+            ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).jumpMode = JumpMode.WIDE;
+            ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).clear();
+            code.accept(codeBuilder);
+        }
+
+        attributes.writeChar(poolHelper.putUtf8("Code"));
+        attributes.writeInt(0);
+        codeBuilder.build(attributes);
+        int length = attributes.offset - start;
+        //avoid using lambda here
+        int prevOffset = attributes.offset;
+        try {
+            attributes.offset = start + 2;
+            attributes.writeInt(length - 6);
+        } finally {
+            attributes.offset = prevOffset;
+        }
+        nattrs++;
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withCode(Consumer<? super CodeBuilder<S, T, E, ?>> code) {
+        return withCode(CodeBuilder::new, code);
+    }
+
+    @SuppressWarnings({"varargs", "unchecked"})
+    public MethodBuilder<S, T, E> withExceptions(S... exceptions) {
+        attributes.writeChar(poolHelper.putUtf8("Exceptions"));
+        attributes.writeInt(2 + (2 * exceptions.length));
+        attributes.writeChar(exceptions.length);
+        for (S exception : exceptions) {
+            attributes.writeChar(poolHelper.putClass(exception));
+        }
+        nattrs++;
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) {
+        getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null);
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
+        getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, annotations);
+        return this;
+    }
+
+    private ParameterAnnotationsBuilder getParameterAnnotations(AnnotationsBuilder.Kind kind) {
+        switch (kind) {
+            case RUNTIME_INVISIBLE:
+                if (runtimeInvisibleParameterAnnotations == null) {
+                    runtimeInvisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+                }
+                return runtimeInvisibleParameterAnnotations;
+            case RUNTIME_VISIBLE:
+                if (runtimeVisibleParameterAnnotations == null) {
+                    runtimeVisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+                }
+                return runtimeVisibleParameterAnnotations;
+        }
+        throw new IllegalStateException();
+    }
+
+    class ParameterAnnotationsBuilder {
+
+        GrowableByteBuffer parameterAnnos = new GrowableByteBuffer();
+
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        AnnotationsBuilder<S, T, E>[] builders = new AnnotationsBuilder[nparams()];
+
+        ParameterAnnotationsBuilder() {
+            for (int i = 0; i < builders.length; i++) {
+                builders[i] = new AnnotationsBuilder<>(poolHelper, typeHelper);
+            }
+        }
+
+        byte[] build() {
+            parameterAnnos.writeByte(builders.length);
+            for (AnnotationsBuilder<S, T, E> builder : builders) {
+                parameterAnnos.writeBytes(builder.build());
+            }
+            return parameterAnnos.bytes();
+        }
+
+        int nparams() {
+            Iterator<T> paramsIt = typeHelper.parameterTypes(desc);
+            int nparams = 0;
+            while (paramsIt.hasNext()) {
+                paramsIt.next();
+                nparams++;
+            }
+            return nparams;
+        }
+    }
+
+    @Override
+    void addAnnotations() {
+        super.addAnnotations();
+        if (runtimeInvisibleParameterAnnotations != null) {
+            withAttribute("RuntimeInvisibleParameterAnnotations", runtimeInvisibleParameterAnnotations.build());
+        }
+        if (runtimeVisibleParameterAnnotations != null) {
+            withAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotations.build());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
+
+public enum Opcode {
+
+    NOP(0),
+    ACONST_NULL(1),
+    ICONST_M1(2),
+    ICONST_0(3),
+    ICONST_1(4),
+    ICONST_2(5),
+    ICONST_3(6),
+    ICONST_4(7),
+    ICONST_5(8),
+    LCONST_0(9),
+    LCONST_1(10),
+    FCONST_0(11),
+    FCONST_1(12),
+    FCONST_2(13),
+    DCONST_0(14),
+    DCONST_1(15),
+    BIPUSH(16),
+    SIPUSH(17),
+    LDC(18),
+    LDC_W(19),
+    LDC2_W(20),
+    ILOAD(21),
+    LLOAD(22),
+    FLOAD(23),
+    DLOAD(24),
+    ALOAD(25),
+    ILOAD_0(26),
+    ILOAD_1(27),
+    ILOAD_2(28),
+    ILOAD_3(29),
+    LLOAD_0(30),
+    LLOAD_1(31),
+    LLOAD_2(32),
+    LLOAD_3(33),
+    FLOAD_0(34),
+    FLOAD_1(35),
+    FLOAD_2(36),
+    FLOAD_3(37),
+    DLOAD_0(38),
+    DLOAD_1(39),
+    DLOAD_2(40),
+    DLOAD_3(41),
+    ALOAD_0(42),
+    ALOAD_1(43),
+    ALOAD_2(44),
+    ALOAD_3(45),
+    IALOAD(46),
+    LALOAD(47),
+    FALOAD(48),
+    DALOAD(49),
+    AALOAD(50),
+    BALOAD(51),
+    CALOAD(52),
+    SALOAD(53),
+    ISTORE(54),
+    LSTORE(55),
+    FSTORE(56),
+    DSTORE(57),
+    ASTORE(58),
+    ISTORE_0(59),
+    ISTORE_1(60),
+    ISTORE_2(61),
+    ISTORE_3(62),
+    LSTORE_0(63),
+    LSTORE_1(64),
+    LSTORE_2(65),
+    LSTORE_3(66),
+    FSTORE_0(67),
+    FSTORE_1(68),
+    FSTORE_2(69),
+    FSTORE_3(70),
+    DSTORE_0(71),
+    DSTORE_1(72),
+    DSTORE_2(73),
+    DSTORE_3(74),
+    ASTORE_0(75),
+    ASTORE_1(76),
+    ASTORE_2(77),
+    ASTORE_3(78),
+    IASTORE(79),
+    LASTORE(80),
+    FASTORE(81),
+    DASTORE(82),
+    AASTORE(83),
+    BASTORE(84),
+    CASTORE(85),
+    SASTORE(86),
+    POP(87),
+    POP2(88),
+    DUP(89),
+    DUP_X1(90),
+    DUP_X2(91),
+    DUP2(92),
+    DUP2_X1(93),
+    DUP2_X2(94),
+    SWAP(95),
+    IADD(96),
+    LADD(97),
+    FADD(98),
+    DADD(99),
+    ISUB(100),
+    LSUB(101),
+    FSUB(102),
+    DSUB(103),
+    IMUL(104),
+    LMUL(105),
+    FMUL(106),
+    DMUL(107),
+    IDIV(108),
+    LDIV(109),
+    FDIV(110),
+    DDIV(111),
+    IREM(112),
+    LREM(113),
+    FREM(114),
+    DREM(115),
+    INEG(116),
+    LNEG(117),
+    FNEG(118),
+    DNEG(119),
+    ISHL(120),
+    LSHL(121),
+    ISHR(122),
+    LSHR(123),
+    IUSHR(124),
+    LUSHR(125),
+    IAND(126),
+    LAND(127),
+    IOR(128),
+    LOR(129),
+    IXOR(130),
+    LXOR(131),
+    IINC(132),
+    I2L(133),
+    I2F(134),
+    I2D(135),
+    L2I(136),
+    L2F(137),
+    L2D(138),
+    F2I(139),
+    F2L(140),
+    F2D(141),
+    D2I(142),
+    D2L(143),
+    D2F(144),
+    I2B(145),
+    I2C(146),
+    I2S(147),
+    LCMP(148),
+    FCMPL(149),
+    FCMPG(150),
+    DCMPL(151),
+    DCMPG(152),
+    IFEQ(153),
+    IFNE(154),
+    IFLT(155),
+    IFGE(156),
+    IFGT(157),
+    IFLE(158),
+    IF_ICMPEQ(159),
+    IF_ICMPNE(160),
+    IF_ICMPLT(161),
+    IF_ICMPGE(162),
+    IF_ICMPGT(163),
+    IF_ICMPLE(164),
+    IF_ACMPEQ(165),
+    IF_ACMPNE(166),
+    GOTO_(167),
+    JSR(168),
+    RET(169),
+    TABLESWITCH(170),
+    LOOKUPSWITCH(171),
+    IRETURN(172),
+    LRETURN(173),
+    FRETURN(174),
+    DRETURN(175),
+    ARETURN(176),
+    RETURN(177),
+    GETSTATIC(178),
+    PUTSTATIC(179),
+    GETFIELD(180),
+    PUTFIELD(181),
+    INVOKEVIRTUAL(182),
+    INVOKESPECIAL(183),
+    INVOKESTATIC(184),
+    INVOKEINTERFACE(185),
+    INVOKEDYNAMIC(186),
+    NEW(187),
+    NEWARRAY(188),
+    ANEWARRAY(189),
+    ARRAYLENGTH(190),
+    ATHROW(191),
+    CHECKCAST(192),
+    INSTANCEOF(193),
+    MONITORENTER(194),
+    MONITOREXIT(195),
+    WIDE(196),
+    MULTIANEWARRAY(197),
+    IF_NULL(198),
+    IF_NONNULL(199),
+    GOTO_W(200),
+    JSR_W(201),
+    VLOAD(203),
+    VSTORE(204),
+    VALOAD(205),
+    VASTORE(206),
+    VNEW(207),
+    VNEWARRAY(208),
+    MULTIVNEWARRAY(209),
+    VRETURN(210),
+    VGETFIELD(211),
+    TYPED(212),
+    VBOX(216),
+    VUNBOX(217);
+
+    int code;
+
+    Opcode(int code) {
+        this.code = code;
+    }
+
+    protected Opcode at(TypeTag type) {
+        return at(type, 1);
+    }
+
+    protected Opcode at(CondKind cond) {
+        return at(cond.offset, 1);
+    }
+
+    protected Opcode at(TypeTag type, int multiplier) {
+        return at(type.offset, multiplier);
+    }
+
+    private Opcode at(int offset, int multiplier) {
+        if (offset < 0) throw new AssertionError();
+        return Opcode.values()[code + (multiplier * offset)];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntBiFunction;
+
+/**
+ * An interface for building and tracking constant pools.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ */
+public interface PoolHelper<S, T, E> {
+    int putClass(S symbol);
+
+    int putFieldRef(S owner, CharSequence name, T type);
+
+    int putMethodRef(S owner, CharSequence name, T type, boolean isInterface);
+
+    int putUtf8(CharSequence s);
+
+    int putInt(int i);
+
+    int putFloat(float f);
+
+    int putLong(long l);
+
+    int putDouble(double d);
+
+    int putString(String s);
+
+    int putType(T t);
+
+    int putMethodType(T t);
+
+    int putHandle(int refKind, S owner, CharSequence name, T type);
+
+    int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+    int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+    int size();
+
+    E entries();
+
+    interface StaticArgListBuilder<S, T, E> {
+        StaticArgListBuilder<S, T, E> add(int i);
+        StaticArgListBuilder<S, T, E> add(float f);
+        StaticArgListBuilder<S, T, E> add(long l);
+        StaticArgListBuilder<S, T, E> add(double d);
+        StaticArgListBuilder<S, T, E> add(String s);
+        StaticArgListBuilder<S, T, E> add(int refKind, S owner, CharSequence name, T type);
+        <Z> StaticArgListBuilder<S, T, E> add(Z z, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc);
+        StaticArgListBuilder<S, T, E> add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgList);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+public enum PoolTag {
+    CONSTANT_UTF8(1),
+    CONSTANT_UNICODE(2),
+    CONSTANT_INTEGER(3),
+    CONSTANT_FLOAT(4),
+    CONSTANT_LONG(5),
+    CONSTANT_DOUBLE(6),
+    CONSTANT_CLASS(7),
+    CONSTANT_STRING(8),
+    CONSTANT_FIELDREF(9),
+    CONSTANT_METHODREF(10),
+    CONSTANT_INTERFACEMETHODREF(11),
+    CONSTANT_NAMEANDTYPE(12),
+    CONSTANT_METHODHANDLE(15),
+    CONSTANT_METHODTYPE(16),
+    CONSTANT_DYNAMIC(17),
+    CONSTANT_INVOKEDYNAMIC(18);
+
+    public final int tag;
+
+    PoolTag(int tag) {
+        this.tag = tag;
+    }
+
+    static PoolTag from(int tag) {
+        return values()[tag - 1];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+public interface Type {
+    TypeTag getTag();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.Iterator;
+
+/**
+ * Helper to create and manipulate type descriptors of T.
+ *
+ * @param <S> the type of symbols
+ * @param <T> the type of type descriptors
+ */
+public interface TypeHelper<S, T> {
+    /**
+     * Return the type descriptor of an element given the type
+     * descriptor of an array.
+     *
+     * @param t the type descriptor of the array
+     * @return the element type
+     */
+    T elemtype(T t);
+
+    /**
+     * Return the type descriptor of an array given the type descriptor
+     * of an element.
+     *
+     * @param t the type descriptor of the element
+     * @return the type descriptor of the array
+     */
+    T arrayOf(T t);
+
+    /**
+     * Return an iterator over the type descriptors of the parameters of a
+     * method.
+       *
+     * @param t the method type descriptor
+     * @return an iterator over the type descriptors of the parameters
+     */
+    Iterator<T> parameterTypes(T t);
+
+    /**
+     * Return the type descriptor of a {@code TypeTag}.
+     *
+     * @param tag the {@code TypeTag} of a primitive type
+     * @return the type descriptor of the primitive type
+     */
+    T fromTag(TypeTag tag);
+
+    /**
+     * Return the return type descriptor of a method.
+     *
+     * @param t the method type descriptor
+     * @return the return type descriptor
+     */
+    T returnType(T t);
+
+    /**
+     * Return the type descriptor for a symbol.
+     *
+     * @param s the symbol
+     * @return the type descriptor
+     */
+    T type(S s);
+
+    /**
+     * Return the symbol corresponding to a type descriptor.
+     *
+     * @param type the type descriptor
+     * @return the symbol
+     */
+    S symbol(T type);
+
+    /**
+     * Return the {@code TypeTag} corresponding to a type descriptor.  Reference
+     * types return {@code TypeTag.A}.
+     *
+     * @param t a type descriptor
+     * @return the corresponding {@code TypeTag}
+     */
+    TypeTag tag(T t);
+
+    /**
+     * Return the symbol corresponding to a JVM type descriptor string.
+     *
+     * @param s a JVM type descriptor string
+     * @return the corresponding symbol
+     */
+    S symbolFrom(String s);
+
+    /**
+     * Return the common supertype descriptor of two type descriptors.
+     *
+     * @param t1 a type descriptor
+     * @param t2 a type descriptor
+     * @return the common supertype descriptor
+     */
+    T commonSupertype(T t1, T t2);
+
+    /**
+     * Return the type descriptor for the null type.
+     *
+     * @return the type descriptor for the null type
+     */
+    T nullType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+public enum TypeTag implements Type {
+    /**
+     * byte
+     */
+    B("B", 0, 1, 8),
+    /**
+     * short
+     */
+    S("S", 0, 1, 9),
+    /**
+     * int
+     */
+    I("I", 0, 1, 10),
+    /**
+     * float
+     */
+    F("F", 2, 1, 6),
+    /**
+     * long
+     */
+    J("J", 1, 2, 11),
+    /**
+     * double
+     */
+    D("D", 3, 2, 7),
+    /**
+     * Reference type
+     */
+    A("A", 4, 1, -1),
+    /**
+     * char
+     */
+    C("C", 0, 1, 5),
+    /**
+     * boolean
+     */
+    Z("Z", 0, 1, 4),
+    /**
+     * void
+     */
+    V("V", -1, -1, -1),
+    /**
+     * Value type
+     */
+    Q("Q", -1, 1, -1);
+
+    String typeStr;
+    int offset;
+    int width;
+    int newarraycode;
+
+    TypeTag(String typeStr, int offset, int width, int newarraycode) {
+        this.typeStr = typeStr;
+        this.offset = offset;
+        this.width = width;
+        this.newarraycode = newarraycode;
+    }
+
+    static TypeTag commonSupertype(TypeTag t1, TypeTag t2) {
+        if (t1.isIntegral() && t2.isIntegral()) {
+            int p1 = t1.ordinal();
+            int p2 = t2.ordinal();
+            return (p1 <= p2) ? t2 : t1;
+        } else {
+            return null;
+        }
+    }
+
+    public int width() {
+        return width;
+    }
+
+    boolean isIntegral() {
+        switch (this) {
+            case B:
+            case S:
+            case I:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public TypeTag getTag() {
+        return this;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,1215 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package jdk.experimental.bytecode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+
+public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> {
+
+    State lastStackMapState;
+    int lastStackMapPc = -1;
+    Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>();
+    protected State state;
+    int depth = 0;
+    int currLocalOffset = 0;
+
+    class StatefulPendingJump extends PendingJump {
+
+        State state;
+
+        StatefulPendingJump(CharSequence label, int pc, State state) {
+            super(label, pc);
+            this.state = state;
+        }
+
+        @Override
+        boolean resolve(CharSequence label, int pc) {
+            boolean b = super.resolve(label, pc);
+            if (b) {
+                TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
+            }
+            return b;
+        }
+    }
+
+    class LocalVarInfo {
+        CharSequence name;
+        int offset;
+        int depth;
+        TypeTag type;
+
+        LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) {
+            this.name = name;
+            this.offset = offset;
+            this.depth = depth;
+            this.type = type;
+        }
+    }
+
+    public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder);
+        T t = methodBuilder.desc;
+        state = new State();
+        if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) {
+            T clazz = typeHelper.type(methodBuilder.thisClass);
+            state.load(clazz, currLocalOffset++); //TODO: uninit??
+        }
+        Iterator<T> paramsIt = typeHelper.parameterTypes(t);
+        while (paramsIt.hasNext()) {
+            T p = paramsIt.next();
+            state.load(p, currLocalOffset);
+            currLocalOffset += typeHelper.tag(p).width;
+        }
+        lastStackMapState = state.dup();
+        stacksize = state.stack.size();
+        localsize = state.locals.size();
+    }
+
+    @Override
+    protected C emitOp(Opcode opcode, Object optPoolValue) {
+        updateState(opcode, optPoolValue);
+        return super.emitOp(opcode, optPoolValue);
+    }
+
+    @Override
+    protected SwitchBuilder makeSwitchBuilder() {
+        return new TypedSwitchBuilder();
+    }
+
+    class TypedSwitchBuilder extends SwitchBuilder {
+
+        @Override
+        public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+            super.withCase(value, c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    case_.accept(c);
+                    state = prevState;
+                });
+            }, fallthrough);
+            return this;
+        }
+
+        @Override
+        public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+            super.withDefault(c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    defaultCase.accept(c);
+                    state = prevState;
+                });
+            });
+            return this;
+        }
+    }
+
+    @Override
+    public StatefulTypedBuilder typed(TypeTag tag) {
+        return super.typed(tag, StatefulTypedBuilder::new);
+    }
+
+    public class StatefulTypedBuilder extends LabelledTypedBuilder {
+
+        TypeTag tag;
+
+        StatefulTypedBuilder(TypeTag tag) {
+            this.tag = tag;
+        }
+
+        @Override
+        public C astore_0() {
+            return storeAndUpdate(super::astore_0);
+        }
+
+        @Override
+        public C astore_1() {
+            return storeAndUpdate(super::astore_1);
+        }
+
+        @Override
+        public C astore_2() {
+            return storeAndUpdate(super::astore_2);
+        }
+
+        @Override
+        public C astore_3() {
+            return storeAndUpdate(super::astore_3);
+        }
+
+        @Override
+        public C astore(int n) {
+            return storeAndUpdate(() -> super.astore(n));
+        }
+
+        @Override
+        public C aastore() {
+            return storeAndUpdate(super::aastore);
+        }
+
+        @Override
+        public C areturn() {
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            return super.areturn();
+        }
+
+        @Override
+        public C anewarray(S s) {
+            super.anewarray(s);
+            state.pop();
+            state.push(typeHelper.arrayOf(typeHelper.type(s)));
+            return thisBuilder();
+        }
+
+        @Override
+        public C aconst_null() {
+            super.aconst_null();
+            state.pop();
+            state.push(tag);
+            return thisBuilder();
+        }
+
+        public C if_acmpeq(CharSequence label) {
+            return jumpAndUpdate(() -> super.if_acmpeq(label));
+        }
+
+        public C if_acmpne(CharSequence label) {
+            return jumpAndUpdate(() -> super.if_acmpne(label));
+        }
+
+        private C storeAndUpdate(Supplier<C> op) {
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            return op.get();
+        }
+
+        private C jumpAndUpdate(Supplier<C> op) {
+            state.pop(tag);
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            state.push(typeHelper.nullType());
+            return op.get();
+        }
+    }
+
+    public class State {
+        public final ArrayList<T> stack;
+        public final Vector<T> locals;
+        boolean alive;
+
+        State(ArrayList<T> stack, Vector<T> locals) {
+            this.stack = stack;
+            this.locals = locals;
+        }
+
+        State() {
+            this(new ArrayList<>(), new Vector<>());
+        }
+
+        void push(TypeTag tag) {
+            switch (tag) {
+                case A:
+                case V:
+                    throw new IllegalStateException("Bad type tag");
+                default:
+                    push(typeHelper.fromTag(tag));
+            }
+        }
+
+        void push(T t) {
+            stack.add(t);
+            if (width(t) == 2) {
+                stack.add(null);
+            }
+            if (stack.size() > stacksize) {
+                stacksize = stack.size();
+            }
+        }
+
+        T peek() {
+            return stack.get(stack.size() - 1);
+        }
+
+        T tosType() {
+            T tos = peek();
+            if (tos == null) {
+                //double slot
+                tos = stack.get(stack.size() - 2);
+            }
+            return tos;
+        }
+
+        T popInternal() {
+            return stack.remove(stack.size() - 1);
+        }
+
+        @SuppressWarnings("unchecked")
+        T pop() {
+            if (stack.size() == 0 || peek() == null) throw new IllegalStateException();
+            return popInternal();
+        }
+
+        T pop2() {
+            T o = stack.get(stack.size() - 2);
+            TypeTag t = typeHelper.tag(o);
+            if (t.width != 2) throw new IllegalStateException();
+            popInternal();
+            popInternal();
+            return o;
+        }
+
+        T pop(TypeTag t) {
+            return (t.width() == 2) ?
+                pop2() : pop();
+        }
+
+        void load(TypeTag tag, int index) {
+            if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag");
+            load(typeHelper.fromTag(tag), index);
+        }
+
+        void load(T t, int index) {
+            ensureDefined(index);
+            locals.set(index, t);
+            if (width(t) == 2) {
+                locals.add(null);
+            }
+            if (locals.size() > localsize) {
+                localsize = locals.size();
+            }
+        }
+
+        void ensureDefined(int index) {
+            if (index >= locals.size()) {
+                locals.setSize(index + 1);
+            }
+        }
+
+        State dup() {
+            State newState = new State(new ArrayList<>(stack), new Vector<>(locals));
+            return newState;
+        }
+
+        State merge(State that) {
+            if (!alive) { return that; }
+            if (that.stack.size() != stack.size()) {
+                throw new IllegalStateException("Bad stack size at merge point");
+            }
+            for (int i = 0; i < stack.size(); i++) {
+                T t1 = stack.get(i);
+                T t2 = that.stack.get(i);
+                stack.set(i, merge(t1, t2, "Bad stack type at merge point"));
+            }
+            int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size();
+            for (int i = 0; i < nlocals; i++) {
+                T t1 = locals.get(i);
+                T t2 = that.locals.get(i);
+                locals.set(i, merge(t1, t2, "Bad local type at merge point"));
+            }
+            if (locals.size() > nlocals) {
+                for (int i = nlocals; i < locals.size(); i++) {
+                    locals.remove(i);
+                }
+            }
+            return this;
+        }
+
+        T merge(T t1, T t2, String msg) {
+            if (t1 == null && t2 == null) {
+                return t1;
+            }
+            T res;
+            TypeTag tag1 = typeHelper.tag(t1);
+            TypeTag tag2 = typeHelper.tag(t2);
+            if (tag1 != TypeTag.A && tag2 != TypeTag.A &&
+                    tag1 != TypeTag.Q && tag2 != TypeTag.Q) {
+                res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2));
+            } else if (t1 == typeHelper.nullType()) {
+                res = t2;
+            } else if (t2 == typeHelper.nullType()) {
+                res = t1;
+            } else {
+                res = typeHelper.commonSupertype(t1, t2);
+            }
+            if (res == null) {
+                throw new IllegalStateException(msg);
+            }
+            return res;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[locals = %s, stack = %s]", locals, stack);
+        }
+    }
+
+    int width(T o) {
+        return o == typeHelper.nullType() ?
+                TypeTag.A.width() :
+                typeHelper.tag(o).width;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updateState(Opcode op, Object optValue) {
+        switch (op) {
+            case VALOAD:
+            case AALOAD:
+                state.pop();
+                state.push(typeHelper.elemtype(state.pop()));
+                break;
+            case GOTO_:
+                state.alive = false;
+                break;
+            case NOP:
+            case INEG:
+            case LNEG:
+            case FNEG:
+            case DNEG:
+                break;
+            case ACONST_NULL:
+                state.push(typeHelper.nullType());
+                break;
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+                state.push(TypeTag.I);
+                break;
+            case LCONST_0:
+            case LCONST_1:
+                state.push(TypeTag.J);
+                break;
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+                state.push(TypeTag.F);
+                break;
+            case DCONST_0:
+            case DCONST_1:
+                state.push(TypeTag.D);
+                break;
+            case ILOAD_0:
+            case FLOAD_0:
+            case ALOAD_0:
+            case LLOAD_0:
+            case DLOAD_0:
+                state.push(state.locals.get(0));
+                break;
+            case ILOAD_1:
+            case FLOAD_1:
+            case ALOAD_1:
+            case LLOAD_1:
+            case DLOAD_1:
+                state.push(state.locals.get(1));
+                break;
+            case ILOAD_2:
+            case FLOAD_2:
+            case ALOAD_2:
+            case LLOAD_2:
+            case DLOAD_2:
+                state.push(state.locals.get(2));
+                break;
+            case ILOAD_3:
+            case FLOAD_3:
+            case ALOAD_3:
+            case LLOAD_3:
+            case DLOAD_3:
+                state.push(state.locals.get(3));
+                break;
+            case ILOAD:
+            case FLOAD:
+            case ALOAD:
+            case LLOAD:
+            case DLOAD:
+            case VLOAD:
+                state.push(state.locals.get((Integer) optValue));
+                break;
+            case IALOAD:
+            case BALOAD:
+            case CALOAD:
+            case SALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case LALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case FALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case DALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case ISTORE_0:
+            case FSTORE_0:
+            case ASTORE_0:
+                state.load(state.pop(), 0);
+                break;
+            case ISTORE_1:
+            case FSTORE_1:
+            case ASTORE_1:
+                state.load(state.pop(), 1);
+                break;
+            case ISTORE_2:
+            case FSTORE_2:
+            case ASTORE_2:
+                state.load(state.pop(), 2);
+                break;
+            case ISTORE_3:
+            case FSTORE_3:
+            case ASTORE_3:
+                state.load(state.pop(), 3);
+                break;
+            case ISTORE:
+            case FSTORE:
+            case ASTORE:
+            case VSTORE:
+                state.load(state.pop(), (int) optValue);
+                break;
+            case LSTORE_0:
+            case DSTORE_0:
+                state.load(state.pop2(), 0);
+                break;
+            case LSTORE_1:
+            case DSTORE_1:
+                state.load(state.pop2(), 1);
+                break;
+            case LSTORE_2:
+            case DSTORE_2:
+                state.load(state.pop2(), 2);
+                break;
+            case LSTORE_3:
+            case DSTORE_3:
+                state.load(state.pop2(), 3);
+                break;
+            case LSTORE:
+            case DSTORE:
+                state.load(state.pop2(), (int) optValue);
+                break;
+            case POP:
+            case LSHR:
+            case LSHL:
+            case LUSHR:
+                state.pop();
+                break;
+            case VRETURN:
+            case ARETURN:
+            case IRETURN:
+            case FRETURN:
+                state.pop();
+                break;
+            case ATHROW:
+                state.pop();
+                break;
+            case POP2:
+                state.pop2();
+                break;
+            case LRETURN:
+            case DRETURN:
+                state.pop2();
+                break;
+            case DUP:
+                state.push(state.peek());
+                break;
+            case RETURN:
+                break;
+            case ARRAYLENGTH:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case ISUB:
+            case IADD:
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case VASTORE:
+            case AASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LAND:
+            case LOR:
+            case LXOR:
+            case LREM:
+            case LDIV:
+            case LMUL:
+            case LSUB:
+            case LADD:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case LCMP:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case L2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case I2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case I2F:
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case I2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case L2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case L2D:
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case F2I:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case F2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case F2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case D2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case D2L:
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case D2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case TABLESWITCH:
+            case LOOKUPSWITCH:
+                state.pop();
+                break;
+            case DUP_X1: {
+                T val1 = state.pop();
+                T val2 = state.pop();
+                state.push(val1);
+                state.push(val2);
+                state.push(val1);
+                break;
+            }
+            case BASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case I2B:
+            case I2C:
+            case I2S:
+                break;
+            case FMUL:
+            case FADD:
+            case FSUB:
+            case FDIV:
+            case FREM:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case CASTORE:
+            case IASTORE:
+            case FASTORE:
+            case SASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LASTORE:
+            case DASTORE:
+                state.pop2();
+                state.pop();
+                state.pop();
+                break;
+            case DUP2:
+                if (state.peek() != null) {
+                    //form 1
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    //form 2
+                    T value = state.pop2();
+                    state.push(value);
+                    state.push(value);
+                }
+                break;
+            case DUP2_X1:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    T value1 = state.pop2();
+                    T value2 = state.pop();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+                break;
+            case DUP2_X2:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    if (state.peek() != null) {
+                        // form 1
+                        T value3 = state.pop();
+                        T value4 = state.pop();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value4);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 3
+                        T value3 = state.pop2();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                } else {
+                    T value1 = state.pop2();
+                    if (state.peek() != null) {
+                        // form 2
+                        T value2 = state.pop();
+                        T value3 = state.pop();
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 4
+                        T value2 = state.pop2();
+                        state.push(value1);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                }
+                break;
+            case DUP_X2: {
+                T value1 = state.pop();
+                if (state.peek() != null) {
+                    // form 1
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    // form 2
+                    T value2 = state.pop2();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+            }
+            break;
+            case FCMPL:
+            case FCMPG:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case DCMPL:
+            case DCMPG:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case SWAP: {
+                T value1 = state.pop();
+                T value2 = state.pop();
+                state.push(value1);
+                state.push(value2);
+                break;
+            }
+            case DADD:
+            case DSUB:
+            case DMUL:
+            case DDIV:
+            case DREM:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case RET:
+                break;
+            case WIDE:
+                // must be handled by the caller.
+                return;
+            case MONITORENTER:
+            case MONITOREXIT:
+                state.pop();
+                break;
+            case VNEW:
+            case NEW:
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case NEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
+                break;
+            case ANEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.arrayOf(typeHelper.type((S)optValue))));
+                break;
+            case VNEWARRAY:
+            case VBOX:
+            case VUNBOX:
+                state.pop();
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case MULTIVNEWARRAY:
+            case MULTIANEWARRAY:
+                for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) {
+                    state.pop();
+                }
+                state.push(typeHelper.type((S) ((Object[]) optValue)[0]));
+                break;
+            case INVOKEINTERFACE:
+            case INVOKEVIRTUAL:
+            case INVOKESPECIAL:
+            case INVOKESTATIC:
+            case INVOKEDYNAMIC:
+                processInvoke(op, (T) optValue);
+                break;
+            case GETSTATIC:
+                state.push((T) optValue);
+                break;
+            case VGETFIELD:
+            case GETFIELD:
+                state.pop();
+                state.push((T) optValue);
+                break;
+            case PUTSTATIC: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                break;
+            }
+            case PUTFIELD: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                state.pop();
+                break;
+            }
+            case BIPUSH:
+            case SIPUSH:
+                state.push(TypeTag.I);
+                break;
+            case LDC:
+            case LDC_W:
+            case LDC2_W:
+                state.push((T)optValue);
+                break;
+            case IF_ACMPEQ:
+            case IF_ICMPEQ:
+            case IF_ACMPNE:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ICMPLT:
+            case IF_ICMPNE:
+                state.pop();
+                state.pop();
+                break;
+            case IF_NONNULL:
+            case IF_NULL:
+            case IFEQ:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case IFLT:
+            case IFNE:
+                state.pop();
+                break;
+            case INSTANCEOF:
+                state.pop();
+                state.push(TypeTag.Z);
+                break;
+            case TYPED:
+            case CHECKCAST:
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Unsupported opcode: " + op);
+        }
+    }
+
+    void processInvoke(Opcode opcode, T invokedType) {
+        Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType);
+        while (paramsIt.hasNext()) {
+            T t = paramsIt.next();
+            TypeTag tag = typeHelper.tag(t);
+            if (tag.width == 2) {
+                state.popInternal();
+                state.popInternal();
+            } else {
+                state.popInternal();
+            }
+        }
+        if (opcode != Opcode.INVOKESTATIC && opcode != Opcode.INVOKEDYNAMIC) {
+            state.pop(); //receiver
+        }
+        T retType = typeHelper.returnType(invokedType);
+        TypeTag retTag = typeHelper.tag(retType);
+        if (retTag != TypeTag.V)
+            state.push(retType);
+    }
+
+    @Override
+    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+        LdcPoolHelper ldcPoolHelper = new LdcPoolHelper();
+        int index = indexFunc.applyAsInt(ldcPoolHelper);
+        fat = typeHelper.tag(ldcPoolHelper.type).width() == 2;
+        return super.ldc(index, ldcPoolHelper.type, fat);
+    }
+    //where
+        class LdcPoolHelper implements PoolHelper<S, T, E> {
+
+            T type;
+
+            @Override
+            public int putClass(S symbol) {
+                type = typeHelper.type(symbol);
+                return poolHelper.putClass(symbol);
+            }
+
+            @Override
+            public int putInt(int i) {
+                type = typeHelper.fromTag(TypeTag.I);
+                return poolHelper.putInt(i);
+            }
+
+            @Override
+            public int putFloat(float f) {
+                type = typeHelper.fromTag(TypeTag.F);
+                return poolHelper.putFloat(f);
+            }
+
+            @Override
+            public int putLong(long l) {
+                type = typeHelper.fromTag(TypeTag.J);
+                return poolHelper.putLong(l);
+            }
+
+            @Override
+            public int putDouble(double d) {
+                type = typeHelper.fromTag(TypeTag.D);
+                return poolHelper.putDouble(d);
+            }
+
+            @Override
+            public int putString(String s) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
+                return poolHelper.putString(s);
+            }
+
+            @Override
+            public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                type = constType;
+                return poolHelper.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs);
+            }
+
+            @Override
+            public int putFieldRef(S owner, CharSequence name, T type) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putUtf8(CharSequence s) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putType(T t) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodType(T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
+                return poolHelper.putMethodType(t);
+            }
+
+            @Override
+            public int putHandle(int refKind, S owner, CharSequence name, T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
+                return poolHelper.putHandle(refKind, owner, name, t);
+            }
+
+            @Override
+            public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int size() {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public E entries() {
+                throw new IllegalStateException();
+            }
+    }
+
+    public C load(int index) {
+        return load(typeHelper.tag(state.locals.get(index)), index);
+    }
+
+    public C store(int index) {
+        return store(typeHelper.tag(state.tosType()), index);
+    }
+
+    @Override
+    public C withLocalSize(int localsize) {
+        throw new IllegalStateException("Local size automatically computed");
+    }
+
+    @Override
+    public C withStackSize(int stacksize) {
+        throw new IllegalStateException("Stack size automatically computed");
+    }
+
+    public C withLocal(CharSequence name, T type) {
+        int offset = currLocalOffset;
+        TypeTag tag = typeHelper.tag(type);
+        lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
+        state.load(type, offset);
+        currLocalOffset += tag.width;
+        return thisBuilder();
+    }
+
+    public C load(CharSequence local) {
+        return load(lvarOffsets.get(local).offset);
+    }
+
+    public C store(CharSequence local) {
+        return store(lvarOffsets.get(local).offset);
+    }
+
+    @Override
+    public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+        return super.withTry(c -> {
+            withLocalScope(() -> {
+                tryBlock.accept(c);
+            });
+        }, catchBlocks);
+    }
+
+    @Override
+    protected CatchBuilder makeCatchBuilder(int start, int end) {
+        return new TypedCatchBuilder(start, end);
+    }
+
+    class TypedCatchBuilder extends CatchBuilder {
+
+        State initialState = state.dup();
+
+        TypedCatchBuilder(int start, int end) {
+            super(start, end);
+        }
+
+        @Override
+        protected void emitCatch(S exc, Consumer<? super C> catcher) {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(exc));
+                emitStackMap(code.offset);
+                super.emitCatch(exc, catcher);
+                state = initialState;
+            });
+        }
+
+        @Override
+        protected void emitFinalizer() {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
+                emitStackMap(code.offset);
+                super.emitFinalizer();
+            });
+        }
+    }
+
+    protected void withLocalScope(Runnable runnable) {
+        int prevDepth = depth;
+        try {
+            depth++;
+            runnable.run();
+        } finally {
+            Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
+            while (lvarIt.hasNext()) {
+                LocalVarInfo lvi = lvarIt.next().getValue();
+                if (lvi.depth == depth) {
+                    int width = lvi.type.width;
+                    currLocalOffset -= width;
+                    lvarIt.remove();
+                }
+            }
+            depth = prevDepth;
+        }
+    }
+
+    @Override
+    void addPendingJump(CharSequence label, int pc) {
+        pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
+    }
+
+    @Override
+    void resolveJumps(CharSequence label, int pc) {
+        super.resolveJumps(label, pc);
+        emitStackMap(pc);
+    }
+
+    //TODO: optimize stackmap generation by avoiding intermediate classes
+    protected void emitStackMap(int pc) {
+        //stack map generation
+        if (pc > lastStackMapPc) {
+            writeStackMapFrame(pc);
+            lastStackMapState = state.dup();
+            lastStackMapPc = pc;
+            nstackmaps++;
+        }
+    }
+
+    @Override
+    void build(GrowableByteBuffer buf) {
+        if (stacksize == -1) {
+            throw new IllegalStateException("Bad stack size");
+        }
+        if (localsize == -1) {
+            throw new IllegalStateException("Bad locals size");
+        }
+        if (nstackmaps > 0) {
+            GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
+            stackmapsAttr.writeChar(nstackmaps);
+            stackmapsAttr.writeBytes(stackmaps);
+            withAttribute("StackMapTable", stackmapsAttr.bytes());
+        }
+        super.build(buf);
+    }
+
+    /**
+     * Compare this frame with the previous frame and produce
+     * an entry of compressed stack map frame.
+     */
+    void writeStackMapFrame(int pc) {
+        List<T> locals = state.locals;
+        List<T> stack = state.stack;
+        List<T> prev_locals = lastStackMapState.locals;
+        int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
+        if (stack.size() == 1) {
+            if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
+                sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
+                return;
+            }
+        } else if (stack.size() == 0) {
+            int diff_length = prev_locals.size() - locals.size();
+            if (diff_length == 0) {
+                sameFrame(offset_delta);
+                return;
+            } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
+                appendFrame(offset_delta, prev_locals.size(), locals);
+                return;
+            } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
+                chopFrame(offset_delta, diff_length);
+                return;
+            }
+        }
+        fullFrame(offset_delta, locals, stack);
+    }
+}
--- a/test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java	Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, 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
@@ -805,6 +805,25 @@
     }
 
     @Override
+    public String visitDynamicConstant(ConstantPool.CONSTANT_Dynamic_info c, Integer p) {
+        String value = slist.get(p);
+        if (value == null) {
+            try {
+                value = bsmlist.get(c.bootstrap_method_attr_index) + " "
+                        + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
+                slist.set(p, value);
+                xpool.add(new Element("CONSTANT_Dynamic",
+                                      new String[]{"id", p.toString()},
+                                      value));
+
+            } catch (ConstantPoolException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return value;
+    }
+
+    @Override
     public String visitLong(CONSTANT_Long_info c, Integer p) {
         String value = slist.get(p);
         if (value == null) {
--- a/test/langtools/tools/javac/lambda/ByteCodeTest.java	Wed Jan 31 10:55:49 2018 -0800
+++ b/test/langtools/tools/javac/lambda/ByteCodeTest.java	Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -541,6 +541,22 @@
         }
 
         @Override
+        public String visitDynamicConstant(CONSTANT_Dynamic_info c, Integer p) {
+
+            String value = slist.get(p);
+            if (value == null) {
+                try {
+                    value = bsmMap.get(c.bootstrap_method_attr_index) + " "
+                            + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
+                    slist.set(p, value);
+                } catch (ConstantPoolException ex) {
+                    ex.printStackTrace();
+                }
+            }
+            return value;
+        }
+
+        @Override
         public String visitLong(CONSTANT_Long_info c, Integer p) {
 
             String value = slist.get(p);