8241100: Make Boolean, Character, Byte, and Short implement Constable

Reviewed-by: jrose, briangoetz, psandoz
This commit is contained in:
Jorn Vernee 2020-05-04 09:41:01 -07:00
parent 2d8bea8c1d
commit 31041d406a
8 changed files with 330 additions and 14 deletions

View File

@ -27,6 +27,15 @@ package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Optional;
import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL;
import static java.lang.constant.ConstantDescs.CD_Boolean;
/**
* The Boolean class wraps a value of the primitive type
* {@code boolean} in an object. An object of type
@ -43,7 +52,7 @@ import jdk.internal.HotSpotIntrinsicCandidate;
* @since 1.0
*/
public final class Boolean implements java.io.Serializable,
Comparable<Boolean>
Comparable<Boolean>, Constable
{
/**
* The {@code Boolean} object corresponding to the primitive
@ -344,4 +353,16 @@ public final class Boolean implements java.io.Serializable,
public static boolean logicalXor(boolean a, boolean b) {
return a ^ b;
}
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* instance.
*
* @return an {@link Optional} describing the {@linkplain Boolean} instance
* @since 15
*/
@Override
public Optional<DynamicConstantDesc<Boolean>> describeConstable() {
return Optional.of(value ? ConstantDescs.TRUE : ConstantDescs.FALSE);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2020, 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
@ -28,6 +28,15 @@ package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.util.Optional;
import static java.lang.constant.ConstantDescs.BSM_EXPLICIT_CAST;
import static java.lang.constant.ConstantDescs.CD_byte;
import static java.lang.constant.ConstantDescs.CD_int;
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
/**
*
* The {@code Byte} class wraps a value of primitive type {@code byte}
@ -44,7 +53,7 @@ import jdk.internal.misc.VM;
* @see java.lang.Number
* @since 1.1
*/
public final class Byte extends Number implements Comparable<Byte> {
public final class Byte extends Number implements Comparable<Byte>, Constable {
/**
* A constant holding the minimum value a {@code byte} can
@ -77,6 +86,18 @@ public final class Byte extends Number implements Comparable<Byte> {
return Integer.toString((int)b, 10);
}
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* instance.
*
* @return an {@link Optional} describing the {@linkplain Byte} instance
* @since 15
*/
@Override
public Optional<DynamicConstantDesc<Byte>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_byte, intValue()));
}
private static class ByteCache {
private ByteCache() {}

View File

@ -25,14 +25,22 @@
package java.lang;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import static java.lang.constant.ConstantDescs.BSM_EXPLICIT_CAST;
import static java.lang.constant.ConstantDescs.CD_char;
import static java.lang.constant.ConstantDescs.CD_int;
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
/**
* The {@code Character} class wraps a value of the primitive
* type {@code char} in an object. An object of class
@ -122,7 +130,7 @@ import jdk.internal.misc.VM;
* @since 1.0
*/
public final
class Character implements java.io.Serializable, Comparable<Character> {
class Character implements java.io.Serializable, Comparable<Character>, Constable {
/**
* The minimum radix available for conversion to and from strings.
* The constant value of this field is the smallest value permitted
@ -602,6 +610,17 @@ class Character implements java.io.Serializable, Comparable<Character> {
*/
public static final int MAX_CODE_POINT = 0X10FFFF;
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* instance.
*
* @return an {@link Optional} describing the {@linkplain Character} instance
* @since 15
*/
@Override
public Optional<DynamicConstantDesc<Character>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_char, (int) value));
}
/**
* Instances of this class represent particular subsets of the Unicode

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2020, 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
@ -28,6 +28,15 @@ package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.util.Optional;
import static java.lang.constant.ConstantDescs.BSM_EXPLICIT_CAST;
import static java.lang.constant.ConstantDescs.CD_int;
import static java.lang.constant.ConstantDescs.CD_short;
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
/**
* The {@code Short} class wraps a value of primitive type {@code
* short} in an object. An object of type {@code Short} contains a
@ -43,7 +52,7 @@ import jdk.internal.misc.VM;
* @see java.lang.Number
* @since 1.1
*/
public final class Short extends Number implements Comparable<Short> {
public final class Short extends Number implements Comparable<Short>, Constable {
/**
* A constant holding the minimum value a {@code short} can
@ -203,6 +212,18 @@ public final class Short extends Number implements Comparable<Short> {
return valueOf(s, 10);
}
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* instance.
*
* @return an {@link Optional} describing the {@linkplain Short} instance
* @since 15
*/
@Override
public Optional<DynamicConstantDesc<Short>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_short, intValue()));
}
private static class ShortCache {
private ShortCache() {}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, 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
@ -194,10 +194,18 @@ public final class ConstantDescs {
= ofConstantBootstrap(CD_ConstantBootstraps, "enumConstant",
CD_Enum);
/**
* {@link MethodHandleDesc} representing {@link ConstantBootstraps#getStaticFinal(Lookup, String, Class, Class) ConstantBootstraps.getStaticFinal}
* @since 15
*/
public static final DirectMethodHandleDesc BSM_GET_STATIC_FINAL
= ofConstantBootstrap(CD_ConstantBootstraps, "getStaticFinal",
CD_Object, CD_Class);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#nullConstant(Lookup, String, Class) ConstantBootstraps.nullConstant} */
public static final DirectMethodHandleDesc BSM_NULL_CONSTANT
= ofConstantBootstrap(CD_ConstantBootstraps, "nullConstant",
ConstantDescs.CD_Object);
CD_Object);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#fieldVarHandle(Lookup, String, Class, Class, Class) ConstantBootstraps.fieldVarHandle} */
public static final DirectMethodHandleDesc BSM_VARHANDLE_FIELD
@ -219,6 +227,14 @@ public final class ConstantDescs {
= ofConstantBootstrap(CD_ConstantBootstraps, "invoke",
CD_Object, CD_MethodHandle, CD_Object.arrayType());
/**
* {@link MethodHandleDesc} representing {@link ConstantBootstraps#explicitCast(Lookup, String, Class, Object)} ConstantBootstraps.explicitCast}
* @since 15
*/
public static final DirectMethodHandleDesc BSM_EXPLICIT_CAST
= ofConstantBootstrap(CD_ConstantBootstraps, "explicitCast",
CD_Object, CD_Object);
/** {@link ClassDesc} representing the primitive type {@code int} */
public static final ClassDesc CD_int = ClassDesc.ofDescriptor("I");
@ -251,6 +267,22 @@ public final class ConstantDescs {
= DynamicConstantDesc.ofNamed(ConstantDescs.BSM_NULL_CONSTANT,
DEFAULT_NAME, ConstantDescs.CD_Object);
/**
* Nominal descriptor representing the constant {@linkplain Boolean#TRUE}
* @since 15
*/
public static final DynamicConstantDesc<Boolean> TRUE
= DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL,
"TRUE", CD_Boolean, CD_Boolean);
/**
* Nominal descriptor representing the constant {@linkplain Boolean#TRUE}
* @since 15
*/
public static final DynamicConstantDesc<Boolean> FALSE
= DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL,
"FALSE", CD_Boolean, CD_Boolean);
static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE
= MethodHandleDesc.ofMethod(Kind.VIRTUAL, CD_MethodHandle, "asType",
MethodTypeDesc.of(CD_MethodHandle, CD_MethodType));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2020, 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
@ -355,6 +355,71 @@ public final class ConstantBootstraps {
return MethodHandles.arrayElementVarHandle(validateClassAccess(lookup, arrayClass));
}
/**
* Applies a conversion from a source type to a destination type.
* <p>
* Given a destination type {@code dstType} and an input
* value {@code value}, one of the following will happen:
* <ul>
* <li>If {@code dstType} is {@code void.class},
* a {@link ClassCastException} is thrown.
* <li>If {@code dstType} is {@code Object.class}, {@code value} is returned as is.
* </ul>
* <p>
* Otherwise one of the following conversions is applied to {@code value}:
* <ol>
* <li>If {@code dstType} is a reference type, a reference cast
* is applied to {@code value} as if by calling {@code dstType.cast(value)}.
* <li>If {@code dstType} is a primitive type, then, if the runtime type
* of {@code value} is a primitive wrapper type (such as {@link Integer}),
* a Java unboxing conversion is applied {@jls 5.1.8} followed by a
* Java casting conversion {@jls 5.5} converting either directly to
* {@code dstType}, or, if {@code dstType} is {@code boolean},
* to {@code int}, which is then converted to either {@code true}
* or {@code false} depending on whether the least-significant-bit
* is 1 or 0 respectively. If the runtime type of {@code value} is
* not a primitive wrapper type a {@link ClassCastException} is thrown.
* </ol>
* <p>
* The result is the same as when using the following code:
* <blockquote><pre>{@code
* MethodHandle id = MethodHandles.identity(dstType);
* MethodType mt = MethodType.methodType(dstType, Object.class);
* MethodHandle conv = MethodHandles.explicitCastArguments(id, mt);
* return conv.invoke(value);
* }</pre></blockquote>
*
* @param lookup unused
* @param name unused
* @param dstType the destination type of the conversion
* @param value the value to be converted
* @return the converted value
* @throws ClassCastException when {@code dstType} is {@code void},
* when a cast per (1) fails, or when {@code dstType} is a primitive type
* and the runtime type of {@code value} is not a primitive wrapper type
* (such as {@link Integer})
*
* @since 15
*/
public static Object explicitCast(MethodHandles.Lookup lookup, String name, Class<?> dstType, Object value)
throws ClassCastException {
if (dstType == void.class)
throw new ClassCastException("Can not convert to void");
if (dstType == Object.class)
return value;
MethodHandle id = MethodHandles.identity(dstType);
MethodType mt = MethodType.methodType(dstType, Object.class);
MethodHandle conv = MethodHandles.explicitCastArguments(id, mt);
try {
return conv.invoke(value);
} catch (ClassCastException e) {
throw e; // specified, let CCE through
} catch (Throwable throwable) {
throw new InternalError(throwable); // Not specified, throw InternalError
}
}
private static <T> Class<T> validateClassAccess(MethodHandles.Lookup lookup, Class<T> type) {
try {
lookup.accessClass(type);

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020, 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.
*/
/*
* @test
* @run testng ConvertTest
*/
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.invoke.ConstantBootstraps;
import java.math.BigInteger;
import static org.testng.Assert.assertEquals;
public class ConvertTest {
@DataProvider
public static Object[][] cceInputs() {
return new Object[][]{
{ void.class, null },
{ Integer.class, "a" },
{ int.class, BigInteger.ZERO },
};
}
@Test(dataProvider = "cceInputs", expectedExceptions = ClassCastException.class)
public void testBadConversion(Class<?> dstType, Object value) {
ConstantBootstraps.explicitCast(null, null, dstType, value);
}
@DataProvider
public static Object[][] goodInputs() {
Object o = new Object();
return new Object[][]{
{ Object.class, null, null },
{ Object.class, o, o },
{ String.class, "abc", "abc" },
{ short.class, 10, (short) 10 },
{ int.class, (short) 10, 10 },
{ boolean.class, 1, true },
{ boolean.class, 2, false },
{ int.class, true, 1 },
{ int.class, false, 0 },
{ int.class, 10, 10 },
{ Integer.class, 10, 10 },
{ Object.class, 10, 10 },
{ Number.class, 10, 10 },
};
}
@Test(dataProvider = "goodInputs")
public void testSuccess(Class<?> dstType, Object value, Object expected) {
Object actual = ConstantBootstraps.explicitCast(null, null, dstType, value);
assertEquals(actual, expected);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, 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.
*/
/*
* @test
* @run testng DescribeResolveTest
*/
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.invoke.MethodHandles;
import static org.testng.Assert.assertEquals;
public class DescribeResolveTest {
@DataProvider
public static Object[][] constables() {
return new Object[][]{
{ true },
{ false },
{ (short) 10 },
{ (byte) 10 },
{ (char) 10 },
};
}
@Test(dataProvider = "constables")
public void testDescribeResolve(Constable constable) throws ReflectiveOperationException {
ConstantDesc desc = constable.describeConstable().orElseThrow();
Object resolved = desc.resolveConstantDesc(MethodHandles.lookup());
assertEquals(constable, resolved);
}
}