8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy
Reviewed-by: vlivanov, jrose
--- 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());
+ }
+}