[
https://issues.apache.org/jira/browse/GROOVY-10535?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17530934#comment-17530934
]
Eric Milles edited comment on GROOVY-10535 at 5/2/22 9:04 PM:
--------------------------------------------------------------
[~paulk] [~daniel_sun] [~blackdrag]
Here is my idea for creating a method handle that is null-safe and survives
call site caching. The commented-out statement is today's logic. Instead of
an argument-sensitive solution, I'm creating a method handle that checks for
null and produces {{false}} or {{FALSE}} (depending on the target type of
boolean or Boolean). And for non-null, {{asBoolean}} is invoked, which handles
boolean and Boolean as well. Basically, Boolean#valueOf is no longer leveraged.
{code:java}
private void handleBoolean() {
if (handle != null) return;
// boolean->boolean, Boolean->boolean, boolean->Boolean are handled
by the compiler
// which leaves (T)Z and (T)Boolean, where T is the static type but
runtime type of T might be Boolean
boolean primitive = (staticTargetType == boolean.class);
if (!primitive && staticTargetType != Boolean.class) return;
/*
if (args[0] == null) {
if (primitive) {
handle = MethodHandles.constant(boolean.class,
Boolean.FALSE);
handle = MethodHandles.dropArguments(handle, 0,
staticSourceType);
} else {
handle = BOOLEAN_IDENTITY; // null of type Boolean
}
} else if (args[0] instanceof Boolean) {
handle = BOOLEAN_IDENTITY;
} else {
// call asBoolean
name = "asBoolean";
super.setCallSiteTarget();
}
*/
name = "asBoolean";
super.setCallSiteTarget();
MethodHandle ifNull =
IS_NULL.asType(MethodType.methodType(boolean.class, staticSourceType));
MethodHandle thenWhat, elseCallAsBoolean = handle;
if (primitive) {
thenWhat =
MethodHandles.dropArguments(MethodHandles.constant(boolean.class,
Boolean.FALSE), 0, staticSourceType);
} else {
thenWhat =
MethodHandles.identity(staticSourceType).asType(MethodType.methodType(Boolean.class,
staticSourceType));
}
handle = MethodHandles.guardWithTest(ifNull, thenWhat,
elseCallAsBoolean);
}
{code}
I'm not too familiar with all this {{MethodHandle}} stuff, so forgive me if I
went down a strange path.
was (Author: emilles):
[~paulk] [~daniel_sun] [~blackdrag]
Here is my idea for creating a method handle that is null-safe and survives
call site caching. The commented-out statement is today's logic. Instead of
an argument-sensitive solution, I'm creating a method handle that checks for
null and produces {{false}} or {{FALSE}} (depending on the target type of
boolean or Boolean). And for non-null, {{asBoolean}} is invoked, which handles
boolean and Boolean as well. Basically, Boolean#valueOf is no longer leveraged.
{code:java}
private void handleBoolean() {
if (handle != null) return;
// boolean->boolean, Boolean->boolean, boolean->Boolean are handled
by the compiler
// which leaves (T)Z and (T)Boolean, where T is the static type but
runtime type of T might be Boolean
boolean primitive = (staticTargetType == boolean.class);
if (!primitive && staticTargetType != Boolean.class) return;
/*
if (args[0] == null) {
if (primitive) {
handle = MethodHandles.constant(boolean.class,
Boolean.FALSE);
handle = MethodHandles.dropArguments(handle, 0,
staticSourceType);
} else {
handle = BOOLEAN_IDENTITY; // null of type Boolean
}
} else if (args[0] instanceof Boolean) {
handle = BOOLEAN_IDENTITY;
} else {
// call asBoolean
name = "asBoolean";
super.setCallSiteTarget();
}
*/
name = "asBoolean";
super.setCallSiteTarget();
MethodHandle elseCallAsBoolean = handle;
MethodHandle ifNull = IS_NULL.bindTo(staticSourceType);
MethodHandle thenFalse = MethodHandles.constant(staticTargetType,
Boolean.FALSE);
thenFalse = MethodHandles.dropArguments(thenFalse, 0,
staticSourceType);
handle = MethodHandles.guardWithTest(ifNull, thenFalse,
elseCallAsBoolean);
}
{code}
I'm not too familiar with all this {{MethodHandle}} stuff, so forgive me if I
went down a strange path.
Update: for Boolean I need to return null, so that will be fixed...
> IF condition on empty Collection has different behavior than null Collection
> ----------------------------------------------------------------------------
>
> Key: GROOVY-10535
> URL: https://issues.apache.org/jira/browse/GROOVY-10535
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 3.0.10
> Environment: Groovy 3.0.10/OpenJDK 17.0.2 - Ubuntu 21.10
> Reporter: Rodolfo Yanke
> Assignee: Eric Milles
> Priority: Major
>
> I believe this code should print "something" but doesn't work:
> {code:java}
> @CompileStatic
> class NotWorkingExample {
> static void main(String[] args) {
> Collection<String> values = null
> for (i in 0..<200_000) {
> printSomethingIfNotEmpty(values)
> }
> //never printed but it should
> values = ['A']
> printSomethingIfNotEmpty(values)
> }
> static printSomethingIfNotEmpty(Collection<String> values) {
> if(values) {
> println 'something'
> }
> }
> }
> {code}
> This one does print "something" because we pass an empty collection []
> instead of null:
> {code:java}
> @CompileStatic
> class ItWorks {
> static void main(String[] args) {
> Collection<String> values = []
> for (i in 0..<200_000) {
> printSomethingIfNotEmpty(values)
> }
> //it works because [] was passed in the previous 200k calls
> values = ['A']
> printSomethingIfNotEmpty(values)
> }
> static printSomethingIfNotEmpty(Collection<String> values) {
> if(values) {
> println 'something'
> }
> }
> }{code}
> Some optimization is done differently when the condition is skipped too many
> times. Both classes should output "something" on the last method call.
> Thank you for the support.
--
This message was sent by Atlassian Jira
(v8.20.7#820007)