You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Eric Milles (Jira)" <ji...@apache.org> on 2025/10/12 14:15:00 UTC
[jira] [Assigned] (GROOVY-11782) Inconsistent unboxing of null, and resulting exceptions
[ https://issues.apache.org/jira/browse/GROOVY-11782?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Eric Milles reassigned GROOVY-11782:
------------------------------------
Assignee: Eric Milles
> Inconsistent unboxing of null, and resulting exceptions
> -------------------------------------------------------
>
> Key: GROOVY-11782
> URL: https://issues.apache.org/jira/browse/GROOVY-11782
> Project: Groovy
> Issue Type: Bug
> Environment: JDK21
> Groovy 4.0.26
> Reporter: Val E
> Assignee: Eric Milles
> Priority: Minor
>
> So I ran into a weird optimization bug, where an expected exception gets thrown once for an incorrect invocation, will then produce 0 for the exact same incorrect invocations, if its followed by a 'correct' invocation.
>
> Attached is a super basic pure java class, with a very basic arithmetic method
> {code:java}
> package test;
> public class JavaTarget {
> public static double primitiveMethod(double value) {
> System.out.println("\tJavaTarget.primitiveMethod v="+value);
> return value - 3.0;
> }
> }{code}
> The innards of the class are not important, but do note that is take a primitive double param and returns a primitive double after subtracting 3 from the param
> Now when groovy tries to call this class, from a closure, with a Double param, we generally expect type coercion, boxing and unboxng, to kick in and everything should still work, and it does under normal circumstances.
> However the bug occurs when you call with a Double v= null param, in which case type coercion fails, because we cant unbox a null to a double, as expected. If you then call it with a good Double, ex Double v=2.0; all subsequent calls with Double v=null will be coerced to 0 and no more exception will be thrown.
> Test groovy script to illustrate; again vary basic loop 3 times, call java target with null and with a 2.0
> {code:java}
> Closure javaTargetCaller = {Double v ->
> println "\tcall JavaTarget.primitiveMethod($v)"
> JavaTarget.primitiveMethod(v)
> }
> for(int i = 0; i<3; i++){
> try{
> println "CALL[$i] | with null"
> Object a = javaTargetCaller(null)
> println "\tresult with v=null | *NOT-OK* | expecting exception, got "+a+" --"+a.getClass()
> }catch(Exception x){
> println "\tresult with v=null| *OK* |expecting exception, got exception --\n"+x
> }
> println "CALL[$i] | with v=-2"
> Object b = javaTargetCaller(-2)
> println "\tresult with v=-2: expecting -5.0 : got "+b
> assert b==-5
> }{code}
> You will note the first iteration the call will produce an exception as expected, however the next call with a param of -2, will cache something in the dynamic resolution, and all subsequent calls produce a number,
> {panel:title=Output}
> CALL[0] | with null
> call JavaTarget.primitiveMethod(null)
> result with v=null| *OK* |expecting exception, got exception –
> groovy.lang.MissingMethodException: No signature of method: static test.JavaTarget.primitiveMethod() is applicable for argument types: (null) values: [null]
> Possible solutions: primitiveMethod(double)
> CALL[0] | with v=-2
> call JavaTarget.primitiveMethod(-2.0)
> JavaTarget.primitiveMethod v=-2.0
> result with v=-2: expecting -5.0 : got -5.0
> CALL[1] | with null
> call JavaTarget.primitiveMethod(null)
> JavaTarget.primitiveMethod v=0.0
> result with v=null | *NOT-OK* | expecting exception, got -3.0 --class java.lang.Double
> CALL[1] | with v=-2
> call JavaTarget.primitiveMethod(-2.0)
> JavaTarget.primitiveMethod v=-2.0
> result with v=-2: expecting -5.0 : got -5.0
> CALL[2] | with null
> call JavaTarget.primitiveMethod(null)
> JavaTarget.primitiveMethod v=0.0
> result with v=null | *NOT-OK* | expecting exception, got -3.0 --class java.lang.Double
> CALL[2] | with v=-2
> call JavaTarget.primitiveMethod(-2.0)
> JavaTarget.primitiveMethod v=-2.0
> result with v=-2: expecting -5.0 : got -5.0
> {panel}
> -note that the very first call indeed failed to unbox the null and threw an exception, however immediately after the second correct call, the nulls start getting unboxed to 0 and no more exception get thrown.
> -it gets more inconsistent, if you change the order and call with a -2 first, then the nulls start getting unboxed to 0 immediately, and no exception is thrown ever
> Almost like some site call somewhere learns how to unbox a Double null, but it depends on the order in which it will be called.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)