8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy

Reviewed-by: vlivanov, jrose
This commit is contained in:
Claes Redestad 2015-11-17 12:43:09 +01:00
parent 30545d614d
commit 5b5c2d0925
3 changed files with 238 additions and 88 deletions

View File

@ -1707,88 +1707,108 @@ class LambdaForm {
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();
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 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 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 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));
NamedFunction zeFun = NF_zero[ord];
LambdaForm zeForm = LF_zeroForm[ord];
MemberName zeMem = zeFun.member;
zeFun.resolvedHandle = SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm);
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));
assert(idFun.isIdentity());
assert(zeFun.isConstantZero());
assert(new Name(zeFun).isConstantZero());
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_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 @@ class LambdaForm {
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 @@ class LambdaForm {
// Put this last, so that previous static inits can run before.
static {
createIdentityForms();
if (USE_PREDEFINED_INTERPRET_METHODS)
computeInitialPreparedForms();
NamedFunction.initializeInvokers();

View File

@ -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 enum Wrapper {
public String detailString() {
return wrapperSimpleName+
java.util.Arrays.asList(wrapperType, primitiveType,
basicTypeChar, zero,
basicTypeChar, zero(),
"0x"+Integer.toHexString(format));
}
@ -223,13 +221,39 @@ public enum Wrapper {
* 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 @@ public enum Wrapper {
}
} else if (x == null) {
@SuppressWarnings("unchecked")
T z = (T) zero;
T z = (T) zero();
return z;
}
@SuppressWarnings("unchecked")

View File

@ -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());
}
}