< prev index next >

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

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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

@@ -24,11 +24,10 @@
  */
 
 package java.lang.invoke;
 
 import jdk.internal.misc.Unsafe;
-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;
 import jdk.internal.vm.annotation.ForceInline;

@@ -43,10 +42,11 @@
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
+import static java.lang.invoke.MethodHandles.Lookup.*;
 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

@@ -190,12 +190,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.

@@ -203,24 +201,19 @@
         // 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);
-        }
-
+        Properties props = GetPropertyAction.privilegedGetProperties();
         final String strategy =
-                VM.getSavedProperty("java.lang.invoke.stringConcat");
+                props.getProperty("java.lang.invoke.stringConcat");
         CACHE_ENABLE = Boolean.parseBoolean(
-                VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
+                props.getProperty("java.lang.invoke.stringConcat.cache"));
         DEBUG = Boolean.parseBoolean(
-                VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
+                props.getProperty("java.lang.invoke.stringConcat.debug"));
         final String dumpPath =
-                VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
+                props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
 
         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
     }

@@ -1144,12 +1137,11 @@
             mv.visitEnd();
             cw.visitEnd();
 
             byte[] classBytes = cw.toByteArray();
             try {
-                Class<?> hostClass = lookup.lookupClass();
-                Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
+                Class<?> innerClass = lookup.defineClassWithNoCheck(classBytes, HIDDEN_NESTMATE);
                 UNSAFE.ensureClassInitialized(innerClass);
                 dumpIfEnabled(innerClass.getName(), classBytes);
                 return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
             } catch (Exception e) {
                 dumpIfEnabled(className + "$$FAILED", classBytes);

@@ -1525,37 +1517,10 @@
             // no instantiation
         }
 
         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
 
-            // Fast-path two-argument Object + Object concatenations
-            if (recipe.getElements().size() == 2) {
-                // Two object arguments
-                if (mt.parameterCount() == 2 &&
-                        !mt.parameterType(0).isPrimitive() &&
-                        !mt.parameterType(1).isPrimitive()) {
-                    return SIMPLE;
-                }
-                // One element is a constant
-                if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) {
-                    MethodHandle mh = SIMPLE;
-                    // Insert constant element
-
-                    // First recipe element is a constant
-                    if (recipe.getElements().get(0).getTag() == TAG_CONST &&
-                        recipe.getElements().get(1).getTag() != TAG_CONST) {
-                        return MethodHandles.insertArguments(mh, 0,
-                                recipe.getElements().get(0).getValue());
-                    } else if (recipe.getElements().get(1).getTag() == TAG_CONST &&
-                               recipe.getElements().get(0).getTag() != TAG_CONST) {
-                        return MethodHandles.insertArguments(mh, 1,
-                                recipe.getElements().get(1).getValue());
-                    }
-                    // else... fall-through to slow-path
-                }
-            }
-
             // Create filters and obtain filtered parameter types. Filters would be used in the beginning
             // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
             // The filtered argument type list is used all over in the combinators below.
             Class<?>[] ptypes = mt.parameterArray();
             MethodHandle[] filters = null;

@@ -1659,10 +1624,17 @@
             }
 
             return mh;
         }
 
+        @ForceInline
+        private static byte[] newArray(long indexCoder) {
+            byte coder = (byte)(indexCoder >> 32);
+            int index = (int)indexCoder;
+            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
+        }
+
         private static MethodHandle prepender(Class<?> cl) {
             return PREPENDERS.computeIfAbsent(cl, PREPEND);
         }
 
         private static MethodHandle mixer(Class<?> cl) {

@@ -1685,31 +1657,31 @@
                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
                         Wrapper.asPrimitiveType(c));
             }
         };
 
-        private static final MethodHandle SIMPLE;
         private static final MethodHandle NEW_STRING;
         private static final MethodHandle NEW_ARRAY;
         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
         private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
         private static final long INITIAL_CODER;
+        static final Class<?> STRING_HELPER;
 
         static {
             try {
+                STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", 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);
+            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
         }
     }
 
     /**
      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally

@@ -1718,12 +1690,26 @@
     private static final class Stringifiers {
         private Stringifiers() {
             // no instantiation
         }
 
-        private static final MethodHandle OBJECT_INSTANCE =
-            lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
+        private static class ObjectStringifier {
+
+            // We need some additional conversion for Objects in general, because String.valueOf(Object)
+            // may return null. String conversion rules in Java state we need to produce "null" String
+            // in this case, so we provide a customized version that deals with this problematic corner case.
+            private static String valueOf(Object value) {
+                String s;
+                return (value == null || (s = value.toString()) == null) ? "null" : s;
+            }
+
+            // Could have used MethodHandles.lookup() instead of Lookup.IMPL_LOOKUP, if not for the fact
+            // java.lang.invoke Lookups are explicitly forbidden to be retrieved using that API.
+            private static final MethodHandle INSTANCE =
+                    lookupStatic(Lookup.IMPL_LOOKUP, ObjectStringifier.class, "valueOf", String.class, Object.class);
+
+        }
 
         private static class FloatStringifiers {
             private static final MethodHandle FLOAT_INSTANCE =
                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
 

@@ -1763,11 +1749,11 @@
          * @param t class to stringify
          * @return stringifier; null, if not available
          */
         static MethodHandle forMost(Class<?> t) {
             if (!t.isPrimitive()) {
-                return OBJECT_INSTANCE;
+                return ObjectStringifier.INSTANCE;
             } else if (t == float.class) {
                 return FloatStringifiers.FLOAT_INSTANCE;
             } else if (t == double.class) {
                 return FloatStringifiers.DOUBLE_INSTANCE;
             }
< prev index next >