# HG changeset patch # User redestad # Date 1447693296 -3600 # Mon Nov 16 18:01:36 2015 +0100 # Node ID 3e2034690cf655f7ebbcb2698a48bd300cadb382 # Parent 68adc82d5090d6999cba0a6d148a05571ae0cc33 8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy Reviewed-by: vlivanov, jrose diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1704,83 +1704,105 @@ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); static LambdaForm identityForm(BasicType type) { - return LF_identityForm[type.ordinal()]; + int ord = type.ordinal(); + LambdaForm form = LF_identity[ord]; + if (form != null) { + return form; + } + createFormsFor(type); + return LF_identity[ord]; } + static LambdaForm zeroForm(BasicType type) { - return LF_zeroForm[type.ordinal()]; + int ord = type.ordinal(); + LambdaForm form = LF_zero[ord]; + if (form != null) { + return form; + } + createFormsFor(type); + return LF_zero[ord]; } + static NamedFunction identity(BasicType type) { - return NF_identity[type.ordinal()]; + int ord = type.ordinal(); + NamedFunction function = NF_identity[ord]; + if (function != null) { + return function; + } + createFormsFor(type); + return NF_identity[ord]; } + static NamedFunction constantZero(BasicType type) { - return NF_zero[type.ordinal()]; + int ord = type.ordinal(); + NamedFunction function = NF_zero[ord]; + if (function != null) { + return function; + } + createFormsFor(type); + return NF_zero[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(); + + 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]; + + private static void createFormsFor(BasicType type) { + synchronized (LF_identity) { + 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); + MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass); - // Look up some symbolic names. It might not be necessary to have these, + // 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 = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic); + MemberName zeMem = null; try { - zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, NoSuchMethodException.class); 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); + } } catch (IllegalAccessException|NoSuchMethodException ex) { throw newInternalError(ex); } - - NamedFunction idFun = new NamedFunction(idMem); - LambdaForm 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); - } - LF_identityForm[ord] = idForm; - NF_identity[ord] = idFun; + idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm)); - NamedFunction zeFun = new NamedFunction(zeMem); - LambdaForm zeForm; - if (isVoid) { - zeForm = idForm; - } else { + 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)); } - LF_zeroForm[ord] = zeForm; + NF_zero[ord] = zeFun; - - assert(idFun.isIdentity()); - assert(zeFun.isConstantZero()); - assert(new Name(zeFun).isConstantZero()); - } - - // 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 zeFun = NF_zero[ord]; - LambdaForm zeForm = LF_zeroForm[ord]; - MemberName zeMem = zeFun.member; - zeFun.resolvedHandle = SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm); + LF_identity[ord] = idForm; + LF_zero[ord] = zeForm; + NF_identity[ord] = idFun; assert(idFun.isIdentity()); assert(zeFun.isConstantZero()); @@ -1794,13 +1816,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. @@ -1830,7 +1851,6 @@ // Put this last, so that previous static inits can run before. static { - createIdentityForms(); if (USE_PREDEFINED_INTERPRET_METHODS) computeInitialPreparedForms(); NamedFunction.initializeInvokers(); diff --git a/src/java.base/share/classes/sun/invoke/util/Wrapper.java b/src/java.base/share/classes/sun/invoke/util/Wrapper.java --- a/src/java.base/share/classes/sun/invoke/util/Wrapper.java +++ b/src/java.base/share/classes/sun/invoke/util/Wrapper.java @@ -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 zero(Class type) { return convert(zero, type); } + public T zero(Class 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") diff --git a/test/sun/invoke/util/WrapperTest.java b/test/sun/invoke/util/WrapperTest.java new file mode 100644 --- /dev/null +++ b/test/sun/invoke/util/WrapperTest.java @@ -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/othervm 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()); + } +}