8060483: NPE with explicitCastArguments unboxing null
Reviewed-by: attila, lagergren
This commit is contained in:
parent
06ca522eff
commit
0c95b0fb5f
@ -889,11 +889,6 @@ class MethodType implements java.io.Serializable {
|
||||
* with MHs.eCE.
|
||||
* 3a. unboxing conversions can be followed by the full matrix of primitive conversions
|
||||
* 3b. unboxing of null is permitted (creates a zero primitive value)
|
||||
* Most unboxing conversions, like {@code Object->int}, has potentially
|
||||
* different behaviors for asType vs. MHs.eCE, because the dynamic value
|
||||
* might be a wrapper of a type that requires narrowing, like {@code (Object)1L->byte}.
|
||||
* The equivalence is only certain if the static src type is a wrapper,
|
||||
* and the conversion will be a widening one.
|
||||
* Other than interfaces, reference-to-reference conversions are the same.
|
||||
* Boxing primitives to references is the same for both operators.
|
||||
*/
|
||||
@ -904,11 +899,8 @@ class MethodType implements java.io.Serializable {
|
||||
// Or a boxing conversion, which is always to an exact wrapper class.
|
||||
return canConvert(src, dst);
|
||||
} else if (dst.isPrimitive()) {
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
// Watch out: If src is Number or Object, we could get dynamic narrowing conversion.
|
||||
// The conversion is known to be widening only if the wrapper type is statically visible.
|
||||
return (Wrapper.isWrapperType(src) &&
|
||||
dw.isConvertibleFrom(Wrapper.forWrapperType(src)));
|
||||
// Unboxing behavior is different between MHs.eCA & MH.asType (see 3b).
|
||||
return false;
|
||||
} else {
|
||||
// R->R always works, but we have to avoid a check-cast to an interface.
|
||||
return !dst.isInterface() || dst.isAssignableFrom(src);
|
||||
|
@ -26,19 +26,19 @@
|
||||
package sun.invoke.util;
|
||||
|
||||
public enum Wrapper {
|
||||
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)),
|
||||
// wrapperType primitiveType char zero emptyArray format
|
||||
BOOLEAN( Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned( 1)),
|
||||
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
|
||||
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
|
||||
SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)),
|
||||
CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
|
||||
INT(Integer.class, int.class, 'I', (Integer)/*(int)*/0, new int[0], Format.signed(32)),
|
||||
LONG(Long.class, long.class, 'J', (Long)(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)),
|
||||
//NULL(Null.class, null.class, 'N', null, null, Format.other(1)),
|
||||
OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)),
|
||||
BYTE ( Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed( 8)),
|
||||
SHORT ( Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed( 16)),
|
||||
CHAR (Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
|
||||
INT ( Integer.class, int.class, 'I', (Integer)/*(int)*/0, new int[0], Format.signed( 32)),
|
||||
LONG ( Long.class, long.class, 'J', (Long)(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)),
|
||||
// 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, null, Format.other( 0)),
|
||||
;
|
||||
|
||||
private final Class<?> wrapperType;
|
||||
|
86
jdk/test/java/lang/invoke/ExplicitCastArgumentsTest.java
Normal file
86
jdk/test/java/lang/invoke/ExplicitCastArgumentsTest.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 java.lang.invoke;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
/* @test
|
||||
* @summary unit tests for MethodHandles.explicitCastArguments()
|
||||
*
|
||||
* @run main/bootclasspath java.lang.invoke.ExplicitCastArgumentsTest
|
||||
*/
|
||||
public class ExplicitCastArgumentsTest {
|
||||
private static final boolean VERBOSE = Boolean.getBoolean("verbose");
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
for (Wrapper from : Wrapper.values()) {
|
||||
for (Wrapper to : Wrapper.values()) {
|
||||
if (from == Wrapper.VOID || to == Wrapper.VOID) continue;
|
||||
testRef2Prim (from, to);
|
||||
}
|
||||
}
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
|
||||
public static void testRef2Prim(Wrapper from, Wrapper to) throws Throwable {
|
||||
// MHs.eCA javadoc:
|
||||
// If T0 is a reference and T1 a primitive, and if the reference is null at runtime, a zero value is introduced.
|
||||
test(from.wrapperType(), to.primitiveType(), null, false);
|
||||
}
|
||||
|
||||
public static void test(Class<?> from, Class<?> to, Object param, boolean failureExpected) throws Throwable {
|
||||
if (VERBOSE) System.out.printf("%-10s => %-10s: %5s: ", from.getSimpleName(), to.getSimpleName(), param);
|
||||
|
||||
MethodHandle original = MethodHandles.identity(from);
|
||||
MethodType newType = original.type().changeReturnType(to);
|
||||
|
||||
try {
|
||||
MethodHandle target = MethodHandles.explicitCastArguments(original, newType);
|
||||
Object result = target.invokeWithArguments(param);
|
||||
|
||||
if (VERBOSE) {
|
||||
String resultStr;
|
||||
if (result != null) {
|
||||
resultStr = String.format("%10s (%10s)", "'"+result+"'", result.getClass().getSimpleName());
|
||||
} else {
|
||||
resultStr = String.format("%10s", result);
|
||||
}
|
||||
System.out.println(resultStr);
|
||||
}
|
||||
|
||||
if (failureExpected) {
|
||||
String msg = String.format("No exception thrown: %s => %s; parameter: %s", from, to, param);
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
} catch (AssertionError e) {
|
||||
throw e; // report test failure
|
||||
} catch (Throwable e) {
|
||||
if (VERBOSE) System.out.printf("%s: %s\n", e.getClass(), e.getMessage());
|
||||
if (!failureExpected) {
|
||||
String msg = String.format("Unexpected exception was thrown: %s => %s; parameter: %s", from, to, param);
|
||||
throw new AssertionError(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user