JDK-8060483 : NPE with explicitCastArguments unboxing null
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 8u40,9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2014-10-14
  • Updated: 2017-02-21
  • Resolved: 2014-10-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
8u40Fixed 9 b37Fixed
Related Reports
Blocks :  
Relates :  
Relates :  
package jdk.nashorn.test.models;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class NullProvider {
    public static Boolean getBoolean() { return null; }

    public static void main(String[] args) throws Throwable {
        MethodHandle mh = MethodHandles.lookup().findStatic(NullProvider.class, "getBoolean", MethodType.methodType(Boolean.class));
        mh = MethodHandles.explicitCastArguments(mh, MethodType.methodType(boolean.class));


Exception in thread "main" java.lang.NullPointerException
	at sun.invoke.util.ValueConversions.unboxBoolean(ValueConversions.java:95)
	at jdk.nashorn.test.models.NullProvider.main(NullProvider.java:13)

Expected result would be printing "false", as per documentation for explicitCastArgument that says "If T0 is a reference and T1 a primitive, and if the reference is null at runtime, a zero value is introduced."

This happens with 9 builds (e.g. just reproduced it with 9 b34). 8 builds (e.g. recent 8u40 early access release builds) seem to behave okay.
We need this backported to 8u-dev too in order for its dependent JDK-8059443 itself be backported, to make it into 8u40.

Suggested fix: diff --git a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java --- a/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -889,11 +889,6 @@ * 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 @@ // 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);

It's a regression, hence P2.

The bug is in MethodType.explicitCastEquivalentToAsType() which erroneously decides that it's correct to replace MHs.explicitCastArguments() w/ MT.asType() conversion. The problematic conversion is: ()Boolean => ()boolean

Worth noting that the bug is present with any wrapper-to-primitive conversion, not just Boolean to boolean (Integer to int, Double to double, etc.)