changeset 47512:8e620e036707 condy-folding

Automatic merge with condy
author mcimadamore
date Fri, 20 Oct 2017 14:55:01 +0200
parents 14b4ecde7b20 264ca769ed47
children 31dc3ce9ed87
files
diffstat 10 files changed, 349 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/classFileParser.cpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -203,8 +203,7 @@
         break;
       }
       case JVM_CONSTANT_Dynamic : {
-        // TODO major version check
-        if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
+        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);
--- a/src/hotspot/share/classfile/verifier.hpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/classfile/verifier.hpp	Fri Oct 20 14:55:01 2017 +0200
@@ -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       = 53  // change to 54 when class file version is bumped
   };
   typedef enum { ThrowException, NoException } Mode;
 
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -411,6 +411,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)                          \
--- a/src/hotspot/share/oops/constantPool.cpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/oops/constantPool.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -600,14 +600,18 @@
     // change byte-ordering and go via cache
     i = remap_instruction_operand_from_cache(which);
   } else {
-    if (tag_at(which).is_invoke_dynamic() || tag_at(which).is_dynamic_constant()) {
+    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);
 }
@@ -760,8 +764,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();
@@ -799,7 +803,8 @@
 
 BasicType ConstantPool::basic_type_for_constant_at(int which) {
   constantTag tag = tag_at(which);
-  if (tag.is_dynamic_constant()) {
+  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);
@@ -890,16 +895,23 @@
 
   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);
+      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);
 
-      Handle bootstrap_specifier;
+      // 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.
-      bootstrap_specifier = Handle(THREAD, bsm_info);
+      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,
@@ -909,7 +921,12 @@
                                                              constant_type,
                                                              THREAD);
       result_oop = value();
-      Exceptions::wrap_dynamic_exception(CHECK_NULL);
+      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.
@@ -924,6 +941,9 @@
           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);
         }
       }
@@ -939,6 +959,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:
     {
@@ -1738,6 +1759,7 @@
   } 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);
@@ -2007,6 +2029,7 @@
       return 5;
 
     case JVM_CONSTANT_Dynamic:
+    case JVM_CONSTANT_DynamicInError:
     case JVM_CONSTANT_InvokeDynamic:
       // u1 tag, u2 bsm, u2 nt
       return 5;
@@ -2192,7 +2215,8 @@
         DBG(printf("JVM_CONSTANT_MethodType: %hd", idx1));
         break;
       }
-      case JVM_CONSTANT_Dynamic: {
+      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));
@@ -2408,6 +2432,7 @@
       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));
--- a/src/hotspot/share/oops/constantPool.hpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/oops/constantPool.hpp	Fri Oct 20 14:55:01 2017 +0200
@@ -558,12 +558,14 @@
 
   int invoke_dynamic_name_and_type_ref_index_at(int which) {
     assert(tag_at(which).is_invoke_dynamic() ||
-           tag_at(which).is_dynamic_constant(), "Corrupted constant pool");
+           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).is_invoke_dynamic() ||
-           tag_at(which).is_dynamic_constant(), "Corrupted constant pool");
+           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) {
@@ -661,13 +663,15 @@
 
   int invoke_dynamic_bootstrap_method_ref_index_at(int which) {
     assert(tag_at(which).is_invoke_dynamic() ||
-           tag_at(which).is_dynamic_constant(), "Corrupted constant pool");
+           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() ||
-           tag_at(which).is_dynamic_constant(), "Corrupted constant pool");
+           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;
--- a/src/hotspot/share/prims/jvm.cpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/prims/jvm.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -2237,6 +2237,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/runtime/vmStructs.cpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -2332,6 +2332,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	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/utilities/constantTag.cpp	Fri Oct 20 14:55:01 2017 +0200
@@ -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
@@ -58,6 +58,7 @@
       return T_OBJECT;
 
     case JVM_CONSTANT_Dynamic :
+    case JVM_CONSTANT_DynamicInError :
       assert(false, "Dynamic constant has no fixed basic type");
 
     default:
@@ -75,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;
   }
@@ -89,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;
--- a/src/hotspot/share/utilities/constantTag.hpp	Thu Oct 19 22:05:46 2017 +0200
+++ b/src/hotspot/share/utilities/constantTag.hpp	Fri Oct 20 14:55:01 2017 +0200
@@ -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; }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java	Fri Oct 20 14:55:01 2017 +0200
@@ -0,0 +1,283 @@
+/*
+ * 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
+ * @library /lib/testlibrary/bytecode /java/lang/invoke/common
+ * @build jdk.experimental.bytecode.BasicClassBuilder
+ * @run testng 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, 53, 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");
+        }
+    }
+}