Val E created GROOVY-11782:
------------------------------
Summary: 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
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}
-not that the very first call indeed failed to unbox the 0 and threw an
exception, however immediately after the second correct calls, 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, 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)