8142487: Cleanup sun.invoke.util.Wrapper zeroes to be both reliable and lazy
Reviewed-by: vlivanov, jrose
This commit is contained in:
parent
30545d614d
commit
5b5c2d0925
@ -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();
|
||||
|
@ -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")
|
||||
|
108
jdk/test/sun/invoke/util/WrapperTest.java
Normal file
108
jdk/test/sun/invoke/util/WrapperTest.java
Normal 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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user