Paul King created GROOVY-12628:
----------------------------------

             Summary: Assigning a primitive numeric value to a Boolean (boxed) 
target throws WrongMethodTypeException at run time instead of applying Groovy 
truth
                 Key: GROOVY-12628
                 URL: https://issues.apache.org/jira/browse/GROOVY-12628
             Project: Groovy
          Issue Type: Bug
            Reporter: Paul King


h2. Summary

Assigning a primitive numeric value to a {{Boolean}} (boxed) target throws 
{{java.lang.invoke.WrongMethodTypeException}} at run time instead of applying 
Groovy truth. Primitive {{boolean}} targets, boxed sources and {{as Boolean}} 
all work.

{code:groovy}
Boolean b = 1        // java.lang.invoke.WrongMethodTypeException: cannot 
convert MethodHandle(int)int to (int)Boolean
{code}

The same failure, in dynamic, {{@TypeChecked}} and {{@CompileStatic}} code 
alike:

{code:groovy}
Boolean b = 0
Boolean b = 1L
Boolean b = 1.0d
int i = 1; Boolean b = i
class C { Boolean b = 1 }
Boolean m() { 1 }
{code}

These work:

{code:groovy}
boolean b = 1          // true  (primitive target)
Boolean b = 'x'        // true  (reference source)
Object o = 1; Boolean b = o   // true
1 as Boolean           // true
{code}

{{(Boolean) 1}} in dynamic code fails the same way (STC rejects the explicit 
cast at compile time, so it is unaffected).

h2. Versions

Fails on 4.0.27, 4.0.32, 5.0.6 and 6.0.0. Works on 3.0.25, including its indy 
jar: Groovy 3 compiles the assignment as 
{{ScriptBytecodeAdapter.castToType(Integer.valueOf(1), Boolean)}}, whereas 
Groovy 4+ emits {{invokedynamic cast:(I)Ljava/lang/Boolean;}} with the 
primitive source type.

h2. Cause

{{Selector.CastSelector.handleBoolean}} (after the rewrite for GROOVY-10535 / 
GROOVY-10596) builds a {{guardWithTest(isNull, thenNull, asBoolean)}} handle. 
For a boxed {{Boolean}} target the {{thenNull}} branch is

{code:java}
MethodHandles.identity(staticSourceType).asType(MethodType.methodType(Boolean.class,
 staticSourceType));
{code}

With a primitive {{staticSourceType}} ({{int}}, {{long}}, {{double}}, ...) this 
asks {{asType}} to convert {{(int)int}} to {{(int)Boolean}}, which is not a 
permitted conversion (a primitive can only box to its own wrapper), so the 
handle construction itself throws. The branch is also unreachable for a 
primitive source, which can never be {{null}}.

h2. Suggested fix

In {{handleBoolean}}, when {{staticSourceType.isPrimitive()}} skip the null 
guard and install the {{asBoolean}} handle directly. Alternatively build the 
null branch as 
{{MethodHandles.dropArguments(MethodHandles.constant(Boolean.class, null), 0, 
staticSourceType)}}, which is valid for any source type.

Expected results: {{Boolean b = 1}} is {{true}}, {{Boolean b = 0}} is 
{{false}}, as in Groovy 3 and as for the primitive {{boolean}} target.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to