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)