changeset 51266:b0224b61fd22 lworld

8207194: [lworld] Update InnerClassLambdaMetafactory to add ValueTypes attribute in generated class Reviewed-by: forax, hseigel
author mchung
date Mon, 16 Jul 2018 14:29:31 -0700
parents 54f37cb89cb3
children fa4241b022ea
files make/hotspot/symbols/symbols-unix src/hotspot/share/include/jvm.h src/hotspot/share/oops/constantPool.cpp src/hotspot/share/oops/instanceKlass.cpp src/hotspot/share/prims/jvm.cpp src/java.base/share/classes/java/lang/Class.java src/java.base/share/classes/java/lang/System.java src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java src/java.base/share/native/libjava/Class.c
diffstat 10 files changed, 160 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/make/hotspot/symbols/symbols-unix	Mon Jul 16 22:09:28 2018 +0530
+++ b/make/hotspot/symbols/symbols-unix	Mon Jul 16 14:29:31 2018 -0700
@@ -102,6 +102,7 @@
 JVM_GetFieldTypeAnnotations
 JVM_GetInheritedAccessControlContext
 JVM_GetInterfaceVersion
+JVM_GetLocalValueTypes
 JVM_GetManagement
 JVM_GetMethodIxArgsSize
 JVM_GetMethodIxByteCode
--- a/src/hotspot/share/include/jvm.h	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/hotspot/share/include/jvm.h	Mon Jul 16 14:29:31 2018 -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
--- a/src/hotspot/share/oops/constantPool.cpp	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/hotspot/share/oops/constantPool.cpp	Mon Jul 16 14:29:31 2018 -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());
   }
 }
 
--- a/src/hotspot/share/oops/instanceKlass.cpp	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/hotspot/share/oops/instanceKlass.cpp	Mon Jul 16 14:29:31 2018 -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());
   }
 }
 
--- a/src/hotspot/share/prims/jvm.cpp	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/hotspot/share/prims/jvm.cpp	Mon Jul 16 14:29:31 2018 -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<ValueTypes>* 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 //////////////////////////////////////////////////////////
 
--- a/src/java.base/share/classes/java/lang/Class.java	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/Class.java	Mon Jul 16 14:29:31 2018 -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<String> getDeclaredValueTypeNames() {
+        Set<String> 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 names;
+    }
+
+    private transient Set<String> 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
--- a/src/java.base/share/classes/java/lang/System.java	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/System.java	Mon Jul 16 14:29:31 2018 -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<String> getDeclaredValueTypeNames(Class<?> current) {
+                return current.getDeclaredValueTypeNames();
+            }
+
         });
     }
 }
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Mon Jul 16 14:29:31 2018 -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<String> 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<String> declaredValueTypes;
+        private final Set<String> valueTypes;
+        ValueTypesAttributeBuilder(Set<String> 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;
+        }
+
+        private 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;
+                }
+            };
+        }
+    }
+
 }
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Mon Jul 16 14:29:31 2018 -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<String> getDeclaredValueTypeNames(Class<?> current);
 }
--- a/src/java.base/share/native/libjava/Class.c	Mon Jul 16 22:09:28 2018 +0530
+++ b/src/java.base/share/native/libjava/Class.c	Mon Jul 16 14:29:31 2018 -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},