8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy
authorredestad
Tue, 17 Nov 2015 12:43:09 +0100 (2015-11-17)
changeset 33842 d261fb6ab872
parent 33841 1d1d1ea6a5f2
child 33843 8fdd836f35bc
8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy Reviewed-by: vlivanov, jrose
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java
jdk/test/sun/invoke/util/WrapperTest.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Tue Nov 17 11:51:45 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Tue Nov 17 12:43:09 2015 +0100
@@ -1707,88 +1707,108 @@
     private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
 
     static LambdaForm identityForm(BasicType type) {
-        return LF_identityForm[type.ordinal()];
-    }
-    static LambdaForm zeroForm(BasicType type) {
-        return LF_zeroForm[type.ordinal()];
+        int ord = type.ordinal();
+        LambdaForm form = LF_identity[ord];
+        if (form != null) {
+            return form;
+        }
+        createFormsFor(type);
+        return LF_identity[ord];
     }
-    static NamedFunction identity(BasicType type) {
-        return NF_identity[type.ordinal()];
+
+    static LambdaForm zeroForm(BasicType type) {
+        int ord = type.ordinal();
+        LambdaForm form = LF_zero[ord];
+        if (form != null) {
+            return form;
+        }
+        createFormsFor(type);
+        return LF_zero[ord];
     }
-    static NamedFunction constantZero(BasicType type) {
-        return NF_zero[type.ordinal()];
+
+    static NamedFunction identity(BasicType type) {
+        int ord = type.ordinal();
+        NamedFunction function = NF_identity[ord];
+        if (function != null) {
+            return function;
+        }
+        createFormsFor(type);
+        return NF_identity[ord];
     }
-    private static final LambdaForm[] LF_identityForm = new LambdaForm[TYPE_LIMIT];
-    private static final LambdaForm[] LF_zeroForm = new LambdaForm[TYPE_LIMIT];
-    private static final NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT];
-    private static final NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT];
-    private static void createIdentityForms() {
-        for (BasicType type : BasicType.ALL_TYPES) {
-            int ord = type.ordinal();
-            char btChar = type.basicTypeChar();
-            boolean isVoid = (type == V_TYPE);
-            Class<?> btClass = type.btClass;
-            MethodType zeType = MethodType.methodType(btClass);
-            MethodType idType = isVoid ? zeType : zeType.appendParameterTypes(btClass);
+
+    static NamedFunction constantZero(BasicType type) {
+        int ord = type.ordinal();
+        NamedFunction function = NF_zero[ord];
+        if (function != null) {
+            return function;
+        }
+        createFormsFor(type);
+        return NF_zero[ord];
+    }
+
+    private static final @Stable LambdaForm[] LF_identity = new LambdaForm[TYPE_LIMIT];
+    private static final @Stable LambdaForm[] LF_zero = new LambdaForm[TYPE_LIMIT];
+    private static final @Stable NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT];
+    private static final @Stable NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT];
 
-            // Look up some symbolic names.  It might not be necessary to have these,
-            // but if we need to emit direct references to bytecodes, it helps.
-            // Zero is built from a call to an identity function with a constant zero input.
-            MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic);
-            MemberName zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic);
-            try {
+    private static synchronized void createFormsFor(BasicType type) {
+        final int ord = type.ordinal();
+        LambdaForm idForm = LF_identity[ord];
+        if (idForm != null) {
+            return;
+        }
+        char btChar = type.basicTypeChar();
+        boolean isVoid = (type == V_TYPE);
+        Class<?> btClass = type.btClass;
+        MethodType zeType = MethodType.methodType(btClass);
+        MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass);
+
+        // Look up symbolic names.  It might not be necessary to have these,
+        // but if we need to emit direct references to bytecodes, it helps.
+        // Zero is built from a call to an identity function with a constant zero input.
+        MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic);
+        MemberName zeMem = null;
+        try {
+            idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class);
+            if (!isVoid) {
+                zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic);
                 zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, NoSuchMethodException.class);
-                idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class);
-            } catch (IllegalAccessException|NoSuchMethodException ex) {
-                throw newInternalError(ex);
             }
-
-            NamedFunction idFun = new NamedFunction(idMem);
-            LambdaForm idForm;
-            if (isVoid) {
-                Name[] idNames = new Name[] { argument(0, L_TYPE) };
-                idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT);
-            } else {
-                Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
-                idForm = new LambdaForm(idMem.getName(), 2, idNames, 1);
-            }
-            LF_identityForm[ord] = idForm;
-            NF_identity[ord] = idFun;
-
-            NamedFunction zeFun = new NamedFunction(zeMem);
-            LambdaForm zeForm;
-            if (isVoid) {
-                zeForm = idForm;
-            } else {
-                Object zeValue = Wrapper.forBasicType(btChar).zero();
-                Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
-                zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1);
-            }
-            LF_zeroForm[ord] = zeForm;
-            NF_zero[ord] = zeFun;
-
-            assert(idFun.isIdentity());
-            assert(zeFun.isConstantZero());
-            assert(new Name(zeFun).isConstantZero());
+        } catch (IllegalAccessException|NoSuchMethodException ex) {
+            throw newInternalError(ex);
         }
 
-        // Do this in a separate pass, so that SimpleMethodHandle.make can see the tables.
-        for (BasicType type : BasicType.ALL_TYPES) {
-            int ord = type.ordinal();
-            NamedFunction idFun = NF_identity[ord];
-            LambdaForm idForm = LF_identityForm[ord];
-            MemberName idMem = idFun.member;
-            idFun.resolvedHandle = SimpleMethodHandle.make(idMem.getInvocationType(), idForm);
+        NamedFunction idFun;
+        LambdaForm zeForm;
+        NamedFunction zeFun;
+        if (isVoid) {
+            Name[] idNames = new Name[] { argument(0, L_TYPE) };
+            idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT);
+            idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
+
+            assert(zeMem == null);
+            zeForm = idForm;
+            zeFun = idFun;
+        } else {
+            Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
+            idForm = new LambdaForm(idMem.getName(), 2, idNames, 1);
+            idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
 
-            NamedFunction zeFun = NF_zero[ord];
-            LambdaForm zeForm = LF_zeroForm[ord];
-            MemberName zeMem = zeFun.member;
-            zeFun.resolvedHandle = SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm);
+            assert(zeMem != null);
+            Object zeValue = Wrapper.forBasicType(btChar).zero();
+            Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
+            zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1);
+            zeFun = new NamedFunction(zeMem, SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm));
+        }
 
-            assert(idFun.isIdentity());
-            assert(zeFun.isConstantZero());
-            assert(new Name(zeFun).isConstantZero());
-        }
+        LF_zero[ord] = zeForm;
+        NF_zero[ord] = zeFun;
+        LF_identity[ord] = idForm;
+        NF_identity[ord] = idFun;
+
+        assert(idFun.isIdentity());
+        assert(zeFun.isConstantZero());
+        assert(new Name(zeFun).isConstantZero());
     }
 
     // Avoid appealing to ValueConversions at bootstrap time:
@@ -1797,13 +1817,12 @@
     private static float identity_F(float x) { return x; }
     private static double identity_D(double x) { return x; }
     private static Object identity_L(Object x) { return x; }
-    private static void identity_V() { return; }  // same as zeroV, but that's OK
+    private static void identity_V() { return; }
     private static int zero_I() { return 0; }
     private static long zero_J() { return 0; }
     private static float zero_F() { return 0; }
     private static double zero_D() { return 0; }
     private static Object zero_L() { return null; }
-    private static void zero_V() { return; }
 
     /**
      * Internal marker for byte-compiled LambdaForms.
@@ -1833,7 +1852,6 @@
 
     // Put this last, so that previous static inits can run before.
     static {
-        createIdentityForms();
         if (USE_PREDEFINED_INTERPRET_METHODS)
             computeInitialPreparedForms();
         NamedFunction.initializeInvokers();
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Tue Nov 17 11:51:45 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Tue Nov 17 12:43:09 2015 +0100
@@ -26,36 +26,34 @@
 package sun.invoke.util;
 
 public enum Wrapper {
-    //        wrapperType    primitiveType  char            zero         emptyArray          format
-    BOOLEAN(  Boolean.class, boolean.class, 'Z',          Boolean.FALSE, new boolean[0], Format.unsigned( 1)),
+    //        wrapperType    primitiveType  char     emptyArray          format
+    BOOLEAN(  Boolean.class, boolean.class, 'Z', new boolean[0], Format.unsigned( 1)),
     // These must be in the order defined for widening primitive conversions in JLS 5.1.2
     // Avoid boxing integral types here to defer initialization of internal caches
-    BYTE   (     Byte.class,    byte.class, 'B',      new Byte((byte)0), new    byte[0], Format.signed(   8)),
-    SHORT  (    Short.class,   short.class, 'S',    new Short((short)0), new   short[0], Format.signed(  16)),
-    CHAR   (Character.class,    char.class, 'C', new Character((char)0), new    char[0], Format.unsigned(16)),
-    INT    (  Integer.class,     int.class, 'I',         new Integer(0), new     int[0], Format.signed(  32)),
-    LONG   (     Long.class,    long.class, 'J',            new Long(0), new    long[0], Format.signed(  64)),
-    FLOAT  (    Float.class,   float.class, 'F',        (Float)(float)0, new   float[0], Format.floating(32)),
-    DOUBLE (   Double.class,  double.class, 'D',      (Double)(double)0, new  double[0], Format.floating(64)),
-    OBJECT (   Object.class,  Object.class, 'L',                   null, new  Object[0], Format.other(    1)),
+    BYTE   (     Byte.class,    byte.class, 'B', new    byte[0], Format.signed(   8)),
+    SHORT  (    Short.class,   short.class, 'S', new   short[0], Format.signed(  16)),
+    CHAR   (Character.class,    char.class, 'C', new    char[0], Format.unsigned(16)),
+    INT    (  Integer.class,     int.class, 'I', new     int[0], Format.signed(  32)),
+    LONG   (     Long.class,    long.class, 'J', new    long[0], Format.signed(  64)),
+    FLOAT  (    Float.class,   float.class, 'F', new   float[0], Format.floating(32)),
+    DOUBLE (   Double.class,  double.class, 'D', new  double[0], Format.floating(64)),
+    OBJECT (   Object.class,  Object.class, 'L', new  Object[0], Format.other(    1)),
     // VOID must be the last type, since it is "assignable" from any other type:
-    VOID   (     Void.class,    void.class, 'V',                   null,           null, Format.other(    0)),
+    VOID   (     Void.class,    void.class, 'V',           null, Format.other(    0)),
     ;
 
     private final Class<?> wrapperType;
     private final Class<?> primitiveType;
     private final char     basicTypeChar;
-    private final Object   zero;
     private final Object   emptyArray;
     private final int      format;
     private final String   wrapperSimpleName;
     private final String   primitiveSimpleName;
 
-    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
+    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object emptyArray, int format) {
         this.wrapperType = wtype;
         this.primitiveType = ptype;
         this.basicTypeChar = tchar;
-        this.zero = zero;
         this.emptyArray = emptyArray;
         this.format = format;
         this.wrapperSimpleName = wtype.getSimpleName();
@@ -66,7 +64,7 @@
     public String detailString() {
         return wrapperSimpleName+
                 java.util.Arrays.asList(wrapperType, primitiveType,
-                basicTypeChar, zero,
+                basicTypeChar, zero(),
                 "0x"+Integer.toHexString(format));
     }
 
@@ -223,13 +221,39 @@
      *  type.  (For void, it is what a reflective method returns
      *  instead of no value at all.)
      */
-    public Object zero() { return zero; }
+    public Object zero() {
+        switch (this) {
+            case BOOLEAN:
+                return Boolean.FALSE;
+            case INT:
+                return (Integer)0;
+            case BYTE:
+                return (Byte)(byte)0;
+            case CHAR:
+                return (Character)(char)0;
+            case SHORT:
+                return (Short)(short)0;
+            case LONG:
+                return (Long)(long)0;
+            case FLOAT:
+                return FLOAT_ZERO;
+            case DOUBLE:
+                return DOUBLE_ZERO;
+            case VOID:
+            case OBJECT:
+            default:
+                return null;
+        }
+    }
+
+    private static final Object DOUBLE_ZERO = (Double)(double)0;
+    private static final Object FLOAT_ZERO = (Float)(float)0;
 
     /** Produce a zero value for the given wrapper type T.
      *  The optional argument must a type compatible with this wrapper.
      *  Equivalent to {@code this.cast(this.zero(), type)}.
      */
-    public <T> T zero(Class<T> type) { return convert(zero, type); }
+    public <T> T zero(Class<T> type) { return convert(zero(), type); }
 
     /** Return the wrapper that wraps values of the given type.
      *  The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
@@ -474,7 +498,7 @@
             }
         } else if (x == null) {
             @SuppressWarnings("unchecked")
-            T z = (T) zero;
+            T z = (T) zero();
             return z;
         }
         @SuppressWarnings("unchecked")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/invoke/util/WrapperTest.java	Tue Nov 17 12:43:09 2015 +0100
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+package test.sun.invoke.util;
+
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.Wrapper;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandle;
+import java.io.Serializable;
+import java.util.Arrays;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/* @test
+ * @summary unit tests to assert Wrapper zero identities and conversion behave correctly
+ * @modules java.base/sun.invoke.util
+ * @compile -XDignore.symbol.file WrapperTest.java
+ * @run junit test.sun.invoke.util.WrapperTest
+ */
+public class WrapperTest {
+
+    @Test
+    public void testShortZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Short.class, (short)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(short.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (short)0);
+        assertTrue(x == Short.valueOf((short)0));
+        assertTrue(x == Wrapper.SHORT.zero());
+    }
+
+    @Test
+    public void testIntZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Integer.class, 42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(int.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, 0);
+        assertTrue(x == Integer.valueOf(0));
+        assertTrue(x == Wrapper.INT.zero());
+    }
+
+    @Test
+    public void testLongZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Long.class, 42L);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(long.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, 0L);
+        assertTrue(x == Long.valueOf(0));
+        assertTrue(x == Wrapper.LONG.zero());
+    }
+
+    @Test
+    public void testByteZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Byte.class, (byte)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(byte.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (byte)0);
+        assertTrue(x == Byte.valueOf((byte)0));
+        assertTrue(x == Wrapper.BYTE.zero());
+    }
+
+    @Test
+    public void testCharacterZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Character.class, (char)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(char.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (char)0);
+        assertTrue(x == Character.valueOf((char)0));
+        assertTrue(x == Wrapper.CHAR.zero());
+    }
+}