--- old/make/hotspot/symbols/symbols-unix 2018-07-16 12:06:36.000000000 -0700 +++ new/make/hotspot/symbols/symbols-unix 2018-07-16 12:06:35.000000000 -0700 @@ -102,6 +102,7 @@ JVM_GetFieldTypeAnnotations JVM_GetInheritedAccessControlContext JVM_GetInterfaceVersion +JVM_GetLocalValueTypes JVM_GetManagement JVM_GetMethodIxArgsSize JVM_GetMethodIxByteCode --- old/src/hotspot/share/include/jvm.h 2018-07-16 12:06:39.000000000 -0700 +++ new/src/hotspot/share/include/jvm.h 2018-07-16 12:06:38.000000000 -0700 @@ -522,6 +522,9 @@ JNIEXPORT jint JNICALL JVM_GetClassAccessFlags(JNIEnv *env, jclass cls); +JNIEXPORT jobjectArray JNICALL +JVM_GetLocalValueTypes(JNIEnv *env, jclass cls); + /* The following two reflection routines are still needed due to startup time issues */ /* * java.lang.reflect.Method --- old/src/hotspot/share/oops/constantPool.cpp 2018-07-16 12:06:44.000000000 -0700 +++ new/src/hotspot/share/oops/constantPool.cpp 2018-07-16 12:06:42.000000000 -0700 @@ -443,7 +443,11 @@ bool opinion0 = resolved_klass->is_value(); bool opinion1 = this_cp->pool_holder()->is_declared_value_type(resolved_klass->name()); if (opinion0 != opinion1) { - THROW(vmSymbols::java_lang_IncompatibleClassChangeError()); + ResourceMark rm; + stringStream ss; + ss.print("constant pool %s inconsistent value type: %s", + this_cp->pool_holder()->external_name(), resolved_klass->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string()); } } --- old/src/hotspot/share/oops/instanceKlass.cpp 2018-07-16 12:06:47.000000000 -0700 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2018-07-16 12:06:46.000000000 -0700 @@ -3294,7 +3294,10 @@ bool opinion2 = k2->is_declared_value_type(name); if (sym != name) name->decrement_refcount(); if (opinion1 != opinion2) { - THROW(vmSymbols::java_lang_IncompatibleClassChangeError()); + stringStream ss; + ss.print("signature %s inconsistent value type: %s %s", + signature->as_C_string(), k1->external_name(), k2->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string()); } } } @@ -3323,7 +3326,10 @@ bool opinion2 = k2->is_declared_value_type(name); name->decrement_refcount(); if (opinion1 != opinion2) { - THROW(vmSymbols::java_lang_IncompatibleClassChangeError()); + stringStream ss; + ss.print("symbol %s inconsistent value type: %s %s", + sym->as_C_string(), k1->external_name(), k2->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string()); } } --- old/src/hotspot/share/prims/jvm.cpp 2018-07-16 12:06:50.000000000 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2018-07-16 12:06:49.000000000 -0700 @@ -1894,6 +1894,31 @@ } JVM_END +JVM_ENTRY(jobjectArray, JVM_GetLocalValueTypes(JNIEnv* env, jclass cls)) +{ + JVMWrapper("JVM_GetLocalValueTypes"); + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls)); + assert(k->is_instance_klass(), "must be"); + InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->has_value_types_attribute()) { + Array* value_types = ik->value_types(); + int length = value_types->length(); + + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), length, CHECK_NULL); + objArrayHandle result(THREAD, r); + for (int i=0; i < length; i++) { + Symbol* vt = value_types->at(i)._class_name; + Handle s = java_lang_String::create_from_symbol(vt, CHECK_0); + result->obj_at_put(i, s()); + } + return (jobjectArray)JNIHandles::make_local(THREAD, result()); + } else { + return NULL; + } +} +JVM_END + + // Constant pool access ////////////////////////////////////////////////////////// --- old/src/java.base/share/classes/java/lang/Class.java 2018-07-16 12:06:53.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/Class.java 2018-07-16 12:06:52.000000000 -0700 @@ -58,6 +58,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; import jdk.internal.HotSpotIntrinsicCandidate; @@ -458,7 +459,6 @@ * Returns {@code true} if this class is a value class. * * @return {@code true} if this class is a value class. - * @since 11 */ public boolean isValue() { int mods = this.getModifiers(); @@ -475,6 +475,29 @@ } /** + * Returns the names listed in the {@code "ValueTypes"} attribute. + */ + Set getDeclaredValueTypeNames() { + Set names = declaredValueTypeNames; + if (names == null) { + String[] lvts = getLocalValueTypes0(); + if (lvts != null) { + for (int i=0; i < lvts.length; i++) { + lvts[i] = lvts[i].replace('/', '.'); + } + names = Set.of(lvts); + } else { + names = Set.of(); + } + declaredValueTypeNames = names; + } + return declaredValueTypeNames; + } + + private transient volatile Set declaredValueTypeNames; + private native String[] getLocalValueTypes0(); + + /** * Creates a new instance of the class represented by this {@code Class} * object. The class is instantiated as if by a {@code new} * expression with an empty argument list. The class is initialized if it --- old/src/java.base/share/classes/java/lang/System.java 2018-07-16 12:06:56.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/System.java 2018-07-16 12:06:55.000000000 -0700 @@ -54,6 +54,7 @@ import java.util.Properties; import java.util.PropertyPermission; import java.util.ResourceBundle; +import java.util.Set; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2157,6 +2158,11 @@ public byte[] getBytesUTF8NoRepl(String s) { return StringCoding.getBytesUTF8NoRepl(s); } + + public Set getDeclaredValueTypeNames(Class current) { + return current.getDeclaredValueTypeNames(); + } + }); } } --- old/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java 2018-07-16 12:06:58.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java 2018-07-16 12:06:57.000000000 -0700 @@ -25,6 +25,8 @@ package java.lang.invoke; +import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.SharedSecrets; import jdk.internal.org.objectweb.asm.*; import sun.invoke.util.BytecodeDescriptor; import jdk.internal.misc.Unsafe; @@ -35,6 +37,7 @@ import java.lang.reflect.Constructor; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.PropertyPermission; @@ -50,6 +53,7 @@ */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final int CLASSFILE_VERSION = 52; private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); @@ -150,8 +154,8 @@ MethodType[] additionalBridges) throws LambdaConversionException { super(caller, invokedType, samMethodName, samMethodType, - implMethod, instantiatedMethodType, - isSerializable, markerInterfaces, additionalBridges); + implMethod, instantiatedMethodType, + isSerializable, markerInterfaces, additionalBridges); implMethodClassName = implClass.getName().replace('.', '/'); implMethodName = implInfo.getName(); implMethodDesc = implInfo.getMethodType().toMethodDescriptorString(); @@ -297,6 +301,18 @@ else if (accidentallySerializable) generateSerializationHostileMethods(); + // add ValueTypes attribute + Set valueTypeNames = JLA.getDeclaredValueTypeNames(targetClass); + if (!valueTypeNames.isEmpty()) { + ValueTypesAttributeBuilder builder = new ValueTypesAttributeBuilder(valueTypeNames); + builder.add(invokedType) + .add(samMethodType) + .add(implMethodType) + .add(instantiatedMethodType) + .add(additionalBridges); + if (!builder.isEmpty()) + cw.visitAttribute(builder.build()); + } cw.visitEnd(); // Define the generated class in this VM. @@ -545,4 +561,66 @@ } } + /* + * Build ValueTypes attribute + */ + static class ValueTypesAttributeBuilder { + private final Set declaredValueTypes; + private final Set valueTypes; + ValueTypesAttributeBuilder(Set valueTypeNames) { + this.declaredValueTypes = valueTypeNames; + this.valueTypes = new HashSet<>(); + } + + /* + * Add the value types referenced in the given MethodType. + */ + ValueTypesAttributeBuilder add(MethodType mt) { + // parameter types + for (Class paramType : mt.ptypes()) { + if (isDeclaredValueType(paramType)) + valueTypes.add(paramType.getName()); + } + // return type + if (isDeclaredValueType(mt.returnType())) + valueTypes.add(mt.returnType().getName()); + return this; + } + + ValueTypesAttributeBuilder add(MethodType... mtypes) { + for (MethodType mt : mtypes) { + add(mt); + } + return this; + } + + boolean isDeclaredValueType(Class c) { + while (c.isArray()) + c = c.getComponentType(); + return declaredValueTypes.contains(c.getName()); + } + + boolean isEmpty() { + return valueTypes.isEmpty(); + } + + Attribute build() { + return new Attribute("ValueTypes") { + @Override + protected ByteVector write(ClassWriter cw, + byte[] code, + int len, + int maxStack, + int maxLocals) { + ByteVector attr = new ByteVector(); + attr.putShort(valueTypes.size()); + for (String cn : valueTypes) { + attr.putShort(cw.newClass(cn.replace('.', '/'))); + } + return attr; + } + }; + } + } + } --- old/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java 2018-07-16 12:07:00.000000000 -0700 +++ new/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java 2018-07-16 12:06:59.000000000 -0700 @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -273,4 +274,10 @@ * @throws IllegalArgumentException for malformed surrogates */ byte[] getBytesUTF8NoRepl(String s); + + /** + * Returns the names listed in {@code "ValueTypes"} attribute + * of the current class. + */ + Set getDeclaredValueTypeNames(Class current); } --- old/src/java.base/share/native/libjava/Class.c 2018-07-16 12:07:02.000000000 -0700 +++ new/src/java.base/share/native/libjava/Class.c 2018-07-16 12:07:01.000000000 -0700 @@ -67,6 +67,7 @@ {"getProtectionDomain0", "()" PD, (void *)&JVM_GetProtectionDomain}, {"getDeclaredClasses0", "()[" CLS, (void *)&JVM_GetDeclaredClasses}, {"getDeclaringClass0", "()" CLS, (void *)&JVM_GetDeclaringClass}, + {"getLocalValueTypes0", "()[" STR, (void *)&JVM_GetLocalValueTypes}, {"getSimpleBinaryName0", "()" STR, (void *)&JVM_GetSimpleBinaryName}, {"getGenericSignature0", "()" STR, (void *)&JVM_GetClassSignature}, {"getRawAnnotations", "()" BA, (void *)&JVM_GetClassAnnotations},