< prev index next >

src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com
rev 58567 : [mq]: rename-isHidden

@@ -23,11 +23,12 @@
  * questions.
  */
 
 package java.lang.invoke;
 
-import jdk.internal.misc.Unsafe;
+import jdk.internal.access.JavaLangAccess;
+import jdk.internal.access.SharedSecrets;
 import jdk.internal.misc.VM;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Label;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.Opcodes;

@@ -40,10 +41,13 @@
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
+import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodType.methodType;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 
 /**
  * <p>Methods to facilitate the creation of String concatenation methods, that
  * can be used to efficiently concatenate a known number of arguments of known

@@ -131,10 +135,12 @@
     /**
      * Default strategy to use for concatenation.
      */
     private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
 
+    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
+
     private enum Strategy {
         /**
          * Bytecode generator, calling into {@link java.lang.StringBuilder}.
          */
         BC_SB,

@@ -187,12 +193,10 @@
     /**
      * Dump generated classes to disk, for debugging purposes.
      */
     private static final ProxyClassesDumper DUMPER;
 
-    private static final Class<?> STRING_HELPER;
-
     static {
         // In case we need to double-back onto the StringConcatFactory during this
         // static initialization, make sure we have the reasonable defaults to complete
         // the static initialization properly. After that, actual users would use
         // the proper values we have read from the properties.

@@ -200,16 +204,10 @@
         // CACHE_ENABLE = false; // implied
         // CACHE = null;         // implied
         // DEBUG = false;        // implied
         // DUMPER = null;        // implied
 
-        try {
-            STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
-        } catch (Throwable e) {
-            throw new AssertionError(e);
-        }
-
         final String strategy =
                 VM.getSavedProperty("java.lang.invoke.stringConcat");
         CACHE_ENABLE = Boolean.parseBoolean(
                 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
         DEBUG = Boolean.parseBoolean(

@@ -716,40 +714,27 @@
                 : args;
     }
 
     private static String getClassName(Class<?> hostClass) throws StringConcatException {
         /*
-          When cache is enabled, we want to cache as much as we can.
-
-          However, there are two peculiarities:
+          The generated class is in the same package as the host class as
+          it's the implementation of the string concatenation for the host class.
 
-           a) The generated class should stay within the same package as the
-              host class, to allow Unsafe.defineAnonymousClass access controls
-              to work properly. JDK may choose to fail with IllegalAccessException
-              when accessing a VM anonymous class with non-privileged callers,
-              see JDK-8058575.
-
-           b) If we mark the stub with some prefix, say, derived from the package
-              name because of (a), we can technically use that stub in other packages.
-              But the call stack traces would be extremely puzzling to unsuspecting users
-              and profiling tools: whatever stub wins the race, would be linked in all
-              similar callsites.
-
-           Therefore, we set the class prefix to match the host class package, and use
-           the prefix as the cache key too. This only affects BC_* strategies, and only when
-           cache is enabled.
+          When cache is enabled, we want to cache as much as we can.
          */
 
         switch (STRATEGY) {
             case BC_SB:
             case BC_SB_SIZED:
             case BC_SB_SIZED_EXACT: {
                 if (CACHE_ENABLE) {
                     String pkgName = hostClass.getPackageName();
-                    return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
+                    return (!pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
                 } else {
-                    return hostClass.getName().replace('.', '/') + "$$StringConcat";
+                    String name = hostClass.isHidden() ? hostClass.getName().replace('/', '_')
+                                                            : hostClass.getName();
+                    return name.replace('.', '/') + "$$StringConcat";
                 }
             }
             case MH_SB_SIZED:
             case MH_SB_SIZED_EXACT:
             case MH_INLINE_SIZED_EXACT:

@@ -817,11 +802,11 @@
      *
      * <p>This strategy spins up the bytecode that has the same StringBuilder
      * chain javac would otherwise emit. This strategy uses only the public API,
      * and comes as the baseline for the current JDK behavior. On other words,
      * this strategy moves the javac generated bytecode to runtime. The
-     * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
+     * generated bytecode is loaded via Lookup::defineClass, but with
      * the caller class coming from the BSM -- in other words, the protection
      * guarantees are inherited from the method where invokedynamic was
      * originally called. This means, among other things, that the bytecode is
      * verified for all non-JDK uses.
      *

@@ -846,11 +831,10 @@
      * StringBuilder should have. The conversion is done via the public
      * String.valueOf and/or Object.toString methods, and does not touch any
      * private String API.
      */
     private static final class BytecodeStringBuilderStrategy {
-        static final Unsafe UNSAFE = Unsafe.getUnsafe();
         static final int CLASSFILE_VERSION = 52;
         static final String METHOD_NAME = "concat";
 
         private BytecodeStringBuilderStrategy() {
             // no instantiation

@@ -859,11 +843,11 @@
         private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 
             cw.visit(CLASSFILE_VERSION,
                     ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
-                    className,  // Unsafe.defineAnonymousClass would append an unique ID
+                    className,
                     null,
                     "java/lang/Object",
                     null
             );
 

@@ -872,10 +856,11 @@
                     METHOD_NAME,
                     args.toMethodDescriptorString(),
                     null,
                     null);
 
+            // use of @ForceInline no longer has any effect
             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
             mv.visitCode();
 
             Class<?>[] arr = args.parameterArray();
             boolean[] guaranteedNonNull = new boolean[arr.length];

@@ -1141,15 +1126,13 @@
             mv.visitEnd();
             cw.visitEnd();
 
             byte[] classBytes = cw.toByteArray();
             try {
-                Class<?> hostClass = lookup.lookupClass();
-                Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
-                UNSAFE.ensureClassInitialized(innerClass);
-                dumpIfEnabled(innerClass.getName(), classBytes);
-                return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
+                Class<?> innerClass = lookup.defineHiddenClass(classBytes, true, STRONG).lookupClass();
+                dumpIfEnabled(className, classBytes);
+                return lookup.findStatic(innerClass, METHOD_NAME, args);
             } catch (Exception e) {
                 dumpIfEnabled(className + "$$FAILED", classBytes);
                 throw new StringConcatException("Exception while spinning the class", e);
             }
         }

@@ -1268,12 +1251,12 @@
      *
      * <p>This strategy avoids spinning up the bytecode by building the
      * computation on MethodHandle combinators. The computation is built with
      * public MethodHandle APIs, resolved from a public Lookup sequence, and
      * ends up calling the public StringBuilder API. Therefore, this strategy
-     * does not use any private API at all, even the Unsafe.defineAnonymousClass,
-     * since everything is handled under cover by java.lang.invoke APIs.
+     * does not use any private API at all since everything is handled under
+     * cover by java.lang.invoke APIs.
      *
      * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
      * sized exactly".</b>
      *
      * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first

@@ -1281,11 +1264,10 @@
      * StringBuilder should have. The conversion is done via the public
      * String.valueOf and/or Object.toString methods, and does not touch any
      * private String API.
      */
     private static final class MethodHandleStringBuilderStrategy {
-
         private MethodHandleStringBuilderStrategy() {
             // no instantiation
         }
 
         private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {

@@ -1459,10 +1441,12 @@
                 sum += v;
             }
             return sum;
         }
 
+        private static final Lookup MHSBS_LOOKUP = lookup();
+
         private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
 
         // This one is deliberately non-lambdified to optimize startup time:
         private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
             @Override

@@ -1472,13 +1456,13 @@
                 } else if (cnt <= 8) {
                     // Variable-arity collectors are not as efficient as small-count methods,
                     // unroll some initial sizes.
                     Class<?>[] cls = new Class<?>[cnt];
                     Arrays.fill(cls, int.class);
-                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
+                    return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
                 } else {
-                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
+                    return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
                             .asCollector(int[].class, cnt - 1);
                 }
             }
         };
 

@@ -1489,12 +1473,12 @@
             Lookup publicLookup = MethodHandles.publicLookup();
             NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
             STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
             BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
             if (DEBUG) {
-                BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
-                        MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
+                BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class,
+                        "toStringChecked", String.class, StringBuilder.class);
             } else {
                 BUILDER_TO_STRING_CHECKED = null;
             }
         }
 

@@ -1514,12 +1498,10 @@
      * particular implementation details for String, this opens the door for
      * building a very optimal concatenation sequence. This is the only strategy
      * that requires porting if there are private JDK changes occur.
      */
     private static final class MethodHandleInlineCopyStrategy {
-        static final Unsafe UNSAFE = Unsafe.getUnsafe();
-
         private MethodHandleInlineCopyStrategy() {
             // no instantiation
         }
 
         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {

@@ -1734,21 +1716,21 @@
 
         // This one is deliberately non-lambdified to optimize startup time:
         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
-                        String.class, Wrapper.asPrimitiveType(c), String.class);
+                return JLA.stringConcatHelper("prepend",
+                            methodType(long.class, long.class, byte[].class,
+                                       String.class, Wrapper.asPrimitiveType(c), String.class));
             }
         };
 
         // This one is deliberately non-lambdified to optimize startup time:
         private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
-                        Wrapper.asPrimitiveType(c));
+                return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));
             }
         };
 
         private static final MethodHandle SIMPLE;
         private static final MethodHandle NEW_STRING;

@@ -1757,22 +1739,22 @@
         private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
         private static final long INITIAL_CODER;
 
         static {
             try {
-                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
+                MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class));
                 INITIAL_CODER = (long) initCoder.invoke();
             } catch (Throwable e) {
                 throw new AssertionError(e);
             }
 
             PREPENDERS = new ConcurrentHashMap<>();
             MIXERS = new ConcurrentHashMap<>();
 
-            SIMPLE     = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
-            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
-            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
+            SIMPLE     = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class));
+            NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class));
+            NEW_ARRAY  = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class));
         }
     }
 
     /**
      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally

@@ -1782,11 +1764,11 @@
         private Stringifiers() {
             // no instantiation
         }
 
         private static final MethodHandle OBJECT_INSTANCE =
-            lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
+                JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class));
 
         private static class FloatStringifiers {
             private static final MethodHandle FLOAT_INSTANCE =
                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
 
< prev index next >