[jira] [Commented] (GROOVY-10856) MethodSelectionException when calling respondsTo()
[ https://issues.apache.org/jira/browse/GROOVY-10856?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17642108#comment-17642108 ] Rachel Greenham commented on GROOVY-10856: -- {quote}Update: Sorry, I misunderstood. {{respondsTo}} is not supposed to throw and you are saying it is throwing {{{}MethodSelectionException{}}}, correct? {quote} Correct {quote}In your example, if you call {{respondsTo('myMethod', [name: ''])}} does it give an empty list due to the missing "number" element? {quote} No, which feels right to me. It's answering correctly that there's a method that will take Map, which isn't dependent on the Map having the right contents. The documentation does say "responds to a method with the given name and arguments {*}types{*}" (my emphasis) the types, not the values. It feels right to me that that should fail when the method's actually called, as it is. I think whether it should do as you describe is a separate question (I'd vote no fwiw, partly as I want the respondsTo to be quick and happy to let an inadequately-filled map fail at invoke-time). But it's off-topic here. This is really just about what specifically happens when it's given a null. If you give otherwise-wrong parameters, eg: just one of the 42 or 'Arthur' from the above examples, it - correctly - returns an empty list. and if, eg: you do respondsTo(42,null) it correctly still returns the canonical method, but null, alone, specifically, kills it, and seems like it shouldn't. It should either: catch MethodSelectionException and return an empty list, or return the map method, as when you just try to call it with null, it's @NamedVariant's map method that throws an IllegalArgumentException, so presumably *something's* decided that's the one to call... Either answer could be argued for (though to me the first would be preferable: if it can't decide, then neither); it should return *something* though, and not throwing exceptions it promises not to throw. :) I'm actually wondering if in my use-case (wrt my preferred solution here) I shouldn't be using metaClass.getMetaMethod(name, args) instead. It throws the same exception of course, but doesn't make any promises not to so it's not a bug that it does. I'm wondering it so much that I wonder why I didn't do that the first time, and what I've forgotten and should have commented about the matter... > MethodSelectionException when calling respondsTo() > -- > > Key: GROOVY-10856 > URL: https://issues.apache.org/jira/browse/GROOVY-10856 > Project: Groovy > Issue Type: Bug > Components: Documentation >Affects Versions: 4.0.6 > Environment: Seen in Groovy 4.0.6 on Linux x64 with java 17.0.5 and > MacOS aarch64 in Java 19.0.1. I don't think any of that is significant. >Reporter: Rachel Greenham >Priority: Minor > Attachments: responds-to-issue.groovy > > > if you have a method which takes certain parameters, and annotate it > {{@NamedVariant}}, and then try a {{respondsTo}} with a null, or an empty > array, you get this exception, listing both the methods that are there, > rather than the expected groovy-falsehood (empty list) for no matching > methods. > This is related to > # GROOVY-8248 > # GROOVY-8660 > but is different in scope because this is about the behaviour, specifically, > of MetaObjectProtocol::respondsTo, which states clearly in the documentation > that: > {quote}This method is "safe" in that it will always return a value and never > throw an exception > {quote} > ... which turns out not to be true, and should be. The behaviour may not be > wrong for the calls described in the other two bugs, but > {{MetaObjectProtocol::respondsTo}}, specifically, should be catching this, > and returning, as promised, a groovy falsehood. (probably an empty list, > which is correct as neither of the methods match.) > Attached, a small script demonstrating this, which shows the problem clearly > if simply run in groovy console. > {code:groovy} > import groovy.transform.* > @NamedVariant > def myMethod(int number, String name) { > println "number: ${number}; name: ${name}" > } > println myMethod(42, 'Arthur') > println myMethod(name: 'Arthur', number: 42) > def result1 = respondsTo('myMethod', 42, 'Arthur') > println "positive match: ${result1}" > def result2 = respondsTo('myMethod', [number: 42, name: 'Arthur']) > println "positive match: ${result2}" > def result3 = respondsTo('myMethod', null) > // expected an empty list, a groovy-falsehood > {code} -- This message was sent by Atlassian Jira (v8.20.10#820010)
[jira] [Created] (GROOVY-10856) MethodSelectionException when calling respondsTo()
Rachel Greenham created GROOVY-10856: Summary: MethodSelectionException when calling respondsTo() Key: GROOVY-10856 URL: https://issues.apache.org/jira/browse/GROOVY-10856 Project: Groovy Issue Type: Bug Components: Documentation Affects Versions: 4.0.6 Environment: Seen in Groovy 4.0.6 on Linux x64 with java 17.0.5 and MacOS aarch64 in Java 19.0.1. I don't think any of that is significant. Reporter: Rachel Greenham Attachments: responds-to-issue.groovy if you have a method which takes certain parameters, and annotate it {{{}@NamedVariant{}}}, and then try a respondsTo with a null, or an empty array, you get this exception, listing both the methods that are there, rather than the expected groovy-falsehood (empty list) for no matching methods. This is related to # GROOVY-8248 # GROOVY-8660 but is different in scope because this is about the behaviour, specifically, of MetaObjectProtocol::respondsTo, which states clearly in the documentation that: {quote}This method is "safe" in that it will always return a value and never throw an exception {quote} ... which turns out not to be true, and should be. The behaviour may not be wrong for the calls described in the other two bugs, but MetaObjectProtocol::respondsTo, specifically, should be catching this, and returning, as promised, a groovy falsehood. (probably an empty list, which is correct as neither of the methods match.) Attached, a small script demonstrating this, which shows the problem clearly if simply run in groovy console. -- This message was sent by Atlassian Jira (v8.20.10#820010)
[jira] [Updated] (GROOVY-10304) XmlSlurper failing to handle namespaces
[ https://issues.apache.org/jira/browse/GROOVY-10304?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ] Rachel Greenham updated GROOVY-10304: - Description: I would love to be told how I'm doing this wrong. :D My ultimate goal here is to use XmlSlurper on a StreamingSAXBuilder to prepare a document in a standard way, then modify it, without having to render it to an intermediate file or a large string in memory. I think I've narrowed the main problem down to XmlSlurper, although there are other issues with StreamingSAXBuilder. But it seems so basic, such a non-unusual thing to do that I feel sure it's just me doing it wrong... Try the following in groovyConsole: {code:groovy} import groovy.xml.* def src = ''' hello ''' def xml = new XmlSlurper() def gpr = xml.parseText(src) def serialized = XmlUtil.serialize(gpr) println serialized assert serialized == src {code} The assertion fails: {noformat} groovy> import groovy.xml.* groovy> def src = ''' groovy> hello groovy> groovy> ''' groovy> def xml = new XmlSlurper() groovy> def gpr = xml.parseText(src) groovy> def serialized = XmlUtil.serialize(gpr) groovy> println serialized groovy> assert serialized == src hello Exception thrown Assertion failed: assert serialized == src | | | | | '\n hello\n\n' | false '\n hello\n\n' at ConsoleScript49.run(ConsoleScript49:11) at jdk.internal.reflect.GeneratedMethodAccessor78.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) {noformat} What's with all the "tag0"? I have a forked XmlSlurper and slurpersupport.Node that I'm currently using to work around the above problem. But it doesn't solve the one below. And I'm sure it's the wrong approach anyway. It gets worse if actually trying to slurp a StreamingSAXBuilder directly: {code:groovy} import groovy.xml.* def expected = ''' hello ''' def doc = new StreamingSAXBuilder().bind { mkp.declareNamespace('': 'uri:urn') outer { inner 'hello' } } def xml = new XmlSlurper() doc(xml) def gpr = xml.document def serialized = XmlUtil.serialize(gpr) println serialized assert serialized == expected {code} gives the output: {noformat} groovy> import groovy.xml.* groovy> def expected = ''' groovy> hello groovy> groovy> ''' groovy> def doc = new StreamingSAXBuilder().bind { groovy> mkp.declareNamespace('': 'uri:urn') groovy> outer { groovy> inner 'hello' groovy> } groovy> } groovy> def xml = new XmlSlurper() groovy> doc(xml) groovy> def gpr = xml.document groovy> def serialized = XmlUtil.serialize(gpr) groovy> println serialized groovy> assert serialized == expected [Fatal Error] :2:70: The prefix "xmlns" cannot be bound to any namespace explicitly; neither can the namespace for "xmlns" be bound to any prefix explicitly. Exception thrown groovy.lang.GroovyRuntimeException: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 70; The prefix "xmlns" cannot be bound to any namespace explicitly; neither can the namespace for "xmlns" be bound to any prefix explicitly. at ConsoleScript0.run(ConsoleScript0:16) at jdk.internal.reflect.GeneratedMethodAccessor78.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) {noformat} It's not super-clear from the above but the error is thrown inside XmlUtil::serialize. The top of the stack trace from running it in my forked version (useful maybe for the XmlUtil line numbers, as XmlUtil has *not* been forked here: {noformat} groovy.lang.GroovyRuntimeException: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 70; The prefix "xmlns" cannot be bound to any namespace explicitly; neither can the namespace for "xmlns" be bound to any prefix explicitly. at groovy.xml.XmlUtil.serialize(XmlUtil.java:475) at groovy.xml.XmlUtil.serialize(XmlUtil.java:461) at groovy.xml.XmlUtil.serialize(XmlUtil.java:191) at groovy.xml.XmlUtil.serialize(XmlUtil.java:160) at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:318) at uk.co.merus.groovy.xml.XmlSlurperTest.testSlurpingFromSAXBuilderWithNamespace(XmlSlurperTest.groovy:306) {noformat} Possibly-relevant old bug: GROOVY-5879, however while in my forked version of StreamingSAXBuilder I tried porting in the fix applied in that bug to StreamingMarkupBuilder, it didn't help in this case. was: I would love to be told how I'm doing this wrong. :D My ultimate goal here is to use XmlSlurper on a StreamingSAXBuilder to prepare a document in a standard way, then modify it, without having to render it to an intermediate file or a large string in memory. I think I've narrowed the main problem down to
[jira] [Created] (GROOVY-10304) XmlSlurper failing to handle namespaces
Rachel Greenham created GROOVY-10304: Summary: XmlSlurper failing to handle namespaces Key: GROOVY-10304 URL: https://issues.apache.org/jira/browse/GROOVY-10304 Project: Groovy Issue Type: Bug Components: XML Processing Affects Versions: 3.0.9 Environment: MacOS, although it doesn't look like something that would vary by platform. Reporter: Rachel Greenham Attachments: slurpFromSaxBuilder.groovy, slurpFromString.groovy I would love to be told how I'm doing this wrong. :D My ultimate goal here is to use XmlSlurper on a StreamingSAXBuilder to prepare a document in a standard way, then modify it, without having to render it to an intermediate file or a large string in memory. I think I've narrowed the main problem down to XmlSlurper, although there are other issues with StreamingSAXBuilder. But it seems so basic, such a non-unusual thing to do that I feel sure it's just me doing it wrong... Try the following in groovyConsole: {code:groovy} import groovy.xml.* def src = ''' hello ''' def xml = new XmlSlurper() def gpr = xml.parseText(src) def serialized = XmlUtil.serialize(gpr) println serialized assert serialized == src {code} The assertion fails: {noformat} groovy> import groovy.xml.* groovy> def src = ''' groovy> hello groovy> groovy> ''' groovy> def xml = new XmlSlurper() groovy> def gpr = xml.parseText(src) groovy> def serialized = XmlUtil.serialize(gpr) groovy> println serialized groovy> assert serialized == src hello Exception thrown Assertion failed: assert serialized == src | | | | | '\n hello\n\n' | false '\n hello\n\n' at ConsoleScript49.run(ConsoleScript49:11) at jdk.internal.reflect.GeneratedMethodAccessor78.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) {noformat} What's with all the "tag0"? I have a forked XmlSlurper and slurpersupport.Node that I'm currently using to work around the above problem. But it doesn't solve the one below. And I'm sure it's the wrong approach anyway. It gets worse if actually trying to slurp a StreamingSAXBuilder directly: {code:groovy} import groovy.xml.* def expected = ''' hello ''' def doc = new StreamingSAXBuilder().bind { mkp.declareNamespace('': 'uri:urn') outer { inner 'hello' } } def xml = new XmlSlurper() doc(xml) def gpr = xml.document def serialized = XmlUtil.serialize(gpr) println serialized assert serialized == expected {code} gives the output: {noformat} groovy> import groovy.xml.* groovy> def expected = ''' groovy> hello groovy> groovy> ''' groovy> def doc = new StreamingSAXBuilder().bind { groovy> mkp.declareNamespace('': 'uri:urn') groovy> outer { groovy> inner 'hello' groovy> } groovy> } groovy> def xml = new XmlSlurper() groovy> doc(xml) groovy> def gpr = xml.document groovy> def serialized = XmlUtil.serialize(gpr) groovy> println serialized groovy> assert serialized == expected [Fatal Error] :2:70: The prefix "xmlns" cannot be bound to any namespace explicitly; neither can the namespace for "xmlns" be bound to any prefix explicitly. Exception thrown groovy.lang.GroovyRuntimeException: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 70; The prefix "xmlns" cannot be bound to any namespace explicitly; neither can the namespace for "xmlns" be bound to any prefix explicitly. at ConsoleScript0.run(ConsoleScript0:16) at jdk.internal.reflect.GeneratedMethodAccessor78.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) {noformat} Possibly-relevant old bug: GROOVY-5879, however while in my forked version of StreamingSAXBuilder I tried porting in the fix applied in that bug to StreamingMarkupBuilder, it didn't help in this case. -- This message was sent by Atlassian Jira (v8.3.4#803005)
[jira] [Commented] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17360668#comment-17360668 ] Rachel Greenham commented on GROOVY-10099: -- Thoughts that keep me awake at night: Am I making a category error here? In dynamic compiling mode is it actually a big faux-pas in principle to be using type data known at compile time to affect the code being compiled? Looking through the docs last night I was noticing how - outside of @CompileStatic or @TypeChecked, the use of types seemed to be just for * Self-documentation * Invoking *from* a type-checked setting * Allowing IDEs to offer autocomplete suggestions Nothing about making the slightest difference to the code that runs. I had just thought, well, the information's *available* while compiling, and lost at runtime, we should be using it to decide what to do, but is that kind of opportunism and pragmatism breaking some actual rule and principle of dynamic programming in force here? And that's why one answer to the actual problem might be: use @CompileStatic. Someone would've said something, right? :) > A single null argument to a varargs parameter is received as null > - > > Key: GROOVY-10099 > URL: https://issues.apache.org/jira/browse/GROOVY-10099 > Project: Groovy > Issue Type: Bug > Environment: Observed on Groovy 3.0.8 on macOS Big Sur (Intel), but I > don't think that's relevant; it'll be everywhere. >Reporter: Rachel Greenham >Priority: Major > Fix For: 4.x > > Attachments: VarArgsTest.groovy, VarArgsTest.jsh > > Time Spent: 1h 10m > Remaining Estimate: 0h > > (NB: I would set the priority to P2 default to be triaged, but I seem not to > have that option, so I left it at the default I was presented with.) > When calling a method with a varargs parameter with a single value, that > value is wrapped into an array of length 1. This is the behaviour in Java, > and is the expected behaviour, and _most_ of the time is the behaviour in > Groovy too. > But when that single value is null, Groovy will instead just pass the null > into the method. Java will not do this: You'll get an array with a single > null in it. Because Groovy's behaviour is unexpected, especially when > interfacing with Java code, NullPointerExceptions can often ensue. > Adding to the inconsistencies, if the Groovy code calling the method is in a > {{@CompileStatic}} context, it behaves just like Java, and the method > (whether or not _it_ is statically compiled or a dynamic Groovy method) > receives an array with a null in it. > So the behaviour in Groovy is inconsistent, both with itself in a > {{@CompileStatic}} situation, and with Java, and is requiring workarounds in > Java code to handle this normally-impossible eventuality. (Even if no varargs > parameter is given you get an empty array, as in fact you do in Groovy too.) > This may be an "early instalment weirdness": There's an ancient ticket, from > not long after varargs were introduced into Java, which appears to have > argued - successfully at the time - that the normal behaviour is a bug: > https://issues.apache.org/jira/browse/GROOVY-1026 > Further adding to the confusion may be that Groovy usually elides the > difference between an {{Object[]}} parameter and an {{Object...}} parameter: > They both behave the same. > The offending code appears to be in > org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines > 200-215 in master at the time of writing, which even includes a comment that > "if the last argument is null, then we don't have to do anything", with which > I respectfully disagree. :) That behaviour should be to return an array with > a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to > make a new one.) > In principle it's an easy fix (although I've left tagging to others as this > is my first issue here), but there'd be an obvious nervousness about changing > behaviour like this when there might be a lot of old code out there depending > on it behaving the way it does now. OTOH the way it behaves now is breaking > the expectations of those of us coming to Groovy from a lifetime of Java... > Attachments: > VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, > demonstrating the behaviour. The behaviour is the same regardless of whether > the console is launched with the -indy option. (The issue was initially > observed in indy.) The dynamic portion of the test, when run, ends in a > NullPointerException as Arrays.asList is not expecting a null varargs > parameter. Output seen (-indy or not): > > {code:java} > name: the static name 1 > params is null? false > params length is 1 > [blah] > name: the static name 2
[jira] [Commented] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17360099#comment-17360099 ] Rachel Greenham commented on GROOVY-10099: -- Seems to be an argument for ignoring issues because it's easier than fixing them, which seems to have been the fate of all those issues quoted: No debate, just... ignored and left to wither. It feels like this one only got attention (for which thank you, by the way) because it was followed up by an actual PR which had to be responded to. :) (Seriously, there's nothing wrong with being a voice for caution, but it feels like it invites a robust defence, so here goes: :) ) So the advice is: "If you need to call a varargs method, do so via a statically-compiled intermediate method, so it doesn't blow up in certain situations." It's a bit... rubbish, isn't it? If you're in @CompileStatic anyway for other reasons, like "this bit needs to run faster", fine, but having to do it, and lose the _benefits_ of a dynamic language, just to get predictable behaviour out of varargs calls, is extremely retrograde. I'm trying to get Groovy integrated into our platform. We already have a way of invoking scripts that basically goes through a method on a context object that looks like this (names have been changed): {code:java} public Object perform(String scriptName, Object... args) {...}{code} Which will now preferentially find and execute a groovy script, and fall back to scripts. And it may be called trivially both from scripts in the legacy scripting language (obscure, older-than-groovy, barrier-to-hiring), and from Java code, like: {code:java} perform("/misc/sanitize-basket", session.getBasket());{code} Which could be used verbatim in Groovy, though more idiomatically like: {code:java} perform '/misc/sanitize-basket', session.basket{code} Only, when you do this - either - in the rare-but-valid case of that Session::getBasket method returning null, the target script blows up, because its generated code looks like: {code:java} public Object run(Object[] args) { Basket basket = (Basket)args[0]; if (basket != null) { ... {code} But only when called from Groovy. Not from anywhere else, because normal varargs behaviour is, if basket is null you still get the array with a null basket, because the argument _type_... is Basket. It's known by the compiler so it wraps it up. If I _wanted_ to call it with a null constant, or obtain it untyped, I'd _expect_ to have to cast it, in case it was null: {code:java} perform '/misc/sanitize-basket', (Basket)null perform '/misc/sanitize-basket', session.attrs["basket-${name}"] as Basket {code} ... but at the moment that wouldn't even work. It's an acceptance issue. Imagine the conversation: {quote}"So you changed perform so it catches nulls and makes an array which... hopefully doesn't break anything else..." "The scripts have to be given an args array the length of the number of declared parameters, but if they're not declaring any they don't even look at it. So that should be safe if it's not null. I don't think they ever let you actually declare an array as a parameter. It's one of the limitations." "Hmm. But otherwise the workaround is, to call varargs methods via another intermediate method compiled statically?" "Yeah. Or you can make it an array here, like: {{}} {code:java} [session.basket] as Basket[]{code} "So now you're making an ArrayList and toArraying it. Every time." "Uh, yeah. Or you can make the array directly, like we did in Java before varargs." __ " {code:java} session.basket ?: Basket.ARRAY_WITH_NULL {code} ? :D" "Did we look at JRuby yet?"{color:#172b4d} {color} {quote} :) I can't stress enough that the PR is attempting to intervene only where the expression type is known, and known not to be dynamically typed or an array. Maybe there's bugs in that identification, so we fix those if they arise. But as for the principle of it: if the type is known, when is it ever right to be knowingly sending a non-array argument to an array parameter? Such that it could only escape an immediate ClassCastException if it's null, or if it's intercepted and turned into an identical array anyway at runtime. And I don't care about the no-cast null constant case. I thought it made sense to make it consistent with @CompileStatic behaviour, but that's not a hill I want to die on. It can go on instead being treated as a dynamically-typed expression. This is only fixing what happens when we _have_ the type. The reporter of GROOVY-6146 appeared to think the same way: Marking in bold the important bit: that even when you cast it explicitly to String it ignores you and still treats it like it's an array. This PR would fix that bug too. Does it break the mitigations people might have put in to get around this? * If they ensure the argument can never be null? Then no: It's just turning
[jira] [Commented] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17349833#comment-17349833 ] Rachel Greenham commented on GROOVY-10099: -- That fix won't work. It breaks too much. I'm sure you're as unsurprised as I am. I had fun though. :D Fundamentally you can't fix it at runtime because you can't tell if a single null value is a null object reference or a null array reference. And there's much that depends on the implicit assumption that it's an array reference. For instance, if you change it, you can't set or initialise a POGO's array-typed field to null. Which doesn't break immediately, it breaks later, when something tries to use it. If this can be fixed anywhere, it's in the compiler. Maybe the AstBuilder? It has more information. After all, it works as expected in {{@CompileStatic}}. Even in {{@CompileDynamic}} mode, it presumably has (or could have) a better idea of what a given _expression type_ is; at least quite a lot of the time: if it's a call to a real method, or a typed object reference, or a cast or type-coercion in the expression itself. It would be able to tell if the expression's _type_ is an array type or not. And if it's not an array type, and the parameter it fits to is a varargs parameter, whatever value it evaluates to at runtime (including null) should be wrapped in an array of one, and it can write that array-wrapping operation into the compiled code. Like it does in {{@CompileStatic}}, like javac does... As with {{@TypeChecked}} mode, it would still be writing an invokeMethod call, but it would wrap that parameter in an array. Then all the runtime invoke code sees is that array and it just passes it through. And if it _hasn't_ got a definitive idea of the expression type, because it's an untyped variable, or an expression that _must_ be resolved to a dynamic call (ie: something that {{@CompileStatic}} would balk at), it would just pass it in as it is, the same behaviour as now, so it breaks nothing. And if it doesn't behave how the coder expects, they'd be able to resolve it by disambiguating the type of that expression in their own code (eg: with a cast, a type coercion, or declaring a variable or method return type properly). So it wouldn't solve it everywhere, 100%, as it couldn't solve it for dynamic expressions of unknowable type at compiletime. But solving it for known expression types would reduce the footprint of the problem a _lot_, I expect, and put the rest in the hands of the coder to eg: put a cast in if necessary. This would have solved my original problem, as it was a situation where the expression type was known, or at least knowable. But it was just one line in a script that didn't otherwise need to be {{@CompileStatic}}. I'm not averse to trying that approach, but I don't know where to start... > A single null argument to a varargs parameter is received as null > - > > Key: GROOVY-10099 > URL: https://issues.apache.org/jira/browse/GROOVY-10099 > Project: Groovy > Issue Type: Bug > Environment: Observed on Groovy 3.0.8 on macOS Big Sur (Intel), but I > don't think that's relevant; it'll be everywhere. >Reporter: Rachel Greenham >Priority: Major > Fix For: 4.x > > Attachments: VarArgsTest.groovy, VarArgsTest.jsh > > > (NB: I would set the priority to P2 default to be triaged, but I seem not to > have that option, so I left it at the default I was presented with.) > When calling a method with a varargs parameter with a single value, that > value is wrapped into an array of length 1. This is the behaviour in Java, > and is the expected behaviour, and _most_ of the time is the behaviour in > Groovy too. > But when that single value is null, Groovy will instead just pass the null > into the method. Java will not do this: You'll get an array with a single > null in it. Because Groovy's behaviour is unexpected, especially when > interfacing with Java code, NullPointerExceptions can often ensue. > Adding to the inconsistencies, if the Groovy code calling the method is in a > {{@CompileStatic}} context, it behaves just like Java, and the method > (whether or not _it_ is statically compiled or a dynamic Groovy method) > receives an array with a null in it. > So the behaviour in Groovy is inconsistent, both with itself in a > {{@CompileStatic}} situation, and with Java, and is requiring workarounds in > Java code to handle this normally-impossible eventuality. (Even if no varargs > parameter is given you get an empty array, as in fact you do in Groovy too.) > This may be an "early instalment weirdness": There's an ancient ticket, from > not long after varargs were introduced into Java, which appears to have > argued - successfully at the time -
[jira] [Commented] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17348592#comment-17348592 ] Rachel Greenham commented on GROOVY-10099: -- I'll go at it at the weekend. I was wondering if part of the original confusion/debate was because in groovy Object[] and Object... are treated interchangeably, based simply on whether the last parameter's type is an array type, whereas that's not the case in Java. Therefore, in the immediate aftermath of Java getting varargs, GROOVY_1026 maybe was an attempt at keeping things behaving as they did before, for Object[] methods. I don't think we can tell if a Groovy method was declared with Object..., as its Method::isVarArgs just returns true either way, but Java does let one distinguish. If we need to reduce the footprint of the change it might be to restrict it to calling Java classes' (does not implement GroovyObject) methods where isVarArgs() returns true, cache that in CachedMethod because it's those java methods that tend to assume they'll never receive null. But I expect the first preference would be to have a simpler change that is consistent everywhere. If it doesn't tear the world down around our ears! > A single null argument to a varargs parameter is received as null > - > > Key: GROOVY-10099 > URL: https://issues.apache.org/jira/browse/GROOVY-10099 > Project: Groovy > Issue Type: Bug > Environment: Observed on Groovy 3.0.8 on macOS Big Sur (Intel), but I > don't think that's relevant; it'll be everywhere. >Reporter: Rachel Greenham >Priority: Major > Fix For: 4.x > > Attachments: VarArgsTest.groovy, VarArgsTest.jsh > > > (NB: I would set the priority to P2 default to be triaged, but I seem not to > have that option, so I left it at the default I was presented with.) > When calling a method with a varargs parameter with a single value, that > value is wrapped into an array of length 1. This is the behaviour in Java, > and is the expected behaviour, and _most_ of the time is the behaviour in > Groovy too. > But when that single value is null, Groovy will instead just pass the null > into the method. Java will not do this: You'll get an array with a single > null in it. Because Groovy's behaviour is unexpected, especially when > interfacing with Java code, NullPointerExceptions can often ensue. > Adding to the inconsistencies, if the Groovy code calling the method is in a > {{@CompileStatic}} context, it behaves just like Java, and the method > (whether or not _it_ is statically compiled or a dynamic Groovy method) > receives an array with a null in it. > So the behaviour in Groovy is inconsistent, both with itself in a > {{@CompileStatic}} situation, and with Java, and is requiring workarounds in > Java code to handle this normally-impossible eventuality. (Even if no varargs > parameter is given you get an empty array, as in fact you do in Groovy too.) > This may be an "early instalment weirdness": There's an ancient ticket, from > not long after varargs were introduced into Java, which appears to have > argued - successfully at the time - that the normal behaviour is a bug: > https://issues.apache.org/jira/browse/GROOVY-1026 > Further adding to the confusion may be that Groovy usually elides the > difference between an {{Object[]}} parameter and an {{Object...}} parameter: > They both behave the same. > The offending code appears to be in > org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines > 200-215 in master at the time of writing, which even includes a comment that > "if the last argument is null, then we don't have to do anything", with which > I respectfully disagree. :) That behaviour should be to return an array with > a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to > make a new one.) > In principle it's an easy fix (although I've left tagging to others as this > is my first issue here), but there'd be an obvious nervousness about changing > behaviour like this when there might be a lot of old code out there depending > on it behaving the way it does now. OTOH the way it behaves now is breaking > the expectations of those of us coming to Groovy from a lifetime of Java... > Attachments: > VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, > demonstrating the behaviour. The behaviour is the same regardless of whether > the console is launched with the -indy option. (The issue was initially > observed in indy.) The dynamic portion of the test, when run, ends in a > NullPointerException as Arrays.asList is not expecting a null varargs > parameter. Output seen (-indy or not): > > {code:java} > name: the static name 1 > params is null? false > params
[jira] [Commented] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17348314#comment-17348314 ] Rachel Greenham commented on GROOVY-10099: -- Noting that the current behaviour is deliberate and specifically tested for in VarargsMethodTest.groovy, so that too would need to be changed, so implying some kind of discussion on _whether_ to do so... Should I just submit the patch, including the test change, and if so, as a PR on GitHub? > A single null argument to a varargs parameter is received as null > - > > Key: GROOVY-10099 > URL: https://issues.apache.org/jira/browse/GROOVY-10099 > Project: Groovy > Issue Type: Bug > Environment: Observed on Groovy 3.0.8 on macOS Big Sur (Intel), but I > don't think that's relevant; it'll be everywhere. >Reporter: Rachel Greenham >Priority: Major > Fix For: 4.x > > Attachments: VarArgsTest.groovy, VarArgsTest.jsh > > > (NB: I would set the priority to P2 default to be triaged, but I seem not to > have that option, so I left it at the default I was presented with.) > When calling a method with a varargs parameter with a single value, that > value is wrapped into an array of length 1. This is the behaviour in Java, > and is the expected behaviour, and _most_ of the time is the behaviour in > Groovy too. > But when that single value is null, Groovy will instead just pass the null > into the method. Java will not do this: You'll get an array with a single > null in it. Because Groovy's behaviour is unexpected, especially when > interfacing with Java code, NullPointerExceptions can often ensue. > Adding to the inconsistencies, if the Groovy code calling the method is in a > {{@CompileStatic}} context, it behaves just like Java, and the method > (whether or not _it_ is statically compiled or a dynamic Groovy method) > receives an array with a null in it. > So the behaviour in Groovy is inconsistent, both with itself in a > {{@CompileStatic}} situation, and with Java, and is requiring workarounds in > Java code to handle this normally-impossible eventuality. (Even if no varargs > parameter is given you get an empty array, as in fact you do in Groovy too.) > This may be an "early instalment weirdness": There's an ancient ticket, from > not long after varargs were introduced into Java, which appears to have > argued - successfully at the time - that the normal behaviour is a bug: > https://issues.apache.org/jira/browse/GROOVY-1026 > Further adding to the confusion may be that Groovy usually elides the > difference between an {{Object[]}} parameter and an {{Object...}} parameter: > They both behave the same. > The offending code appears to be in > org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines > 200-215 in master at the time of writing, which even includes a comment that > "if the last argument is null, then we don't have to do anything", with which > I respectfully disagree. :) That behaviour should be to return an array with > a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to > make a new one.) > In principle it's an easy fix (although I've left tagging to others as this > is my first issue here), but there'd be an obvious nervousness about changing > behaviour like this when there might be a lot of old code out there depending > on it behaving the way it does now. OTOH the way it behaves now is breaking > the expectations of those of us coming to Groovy from a lifetime of Java... > Attachments: > VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, > demonstrating the behaviour. The behaviour is the same regardless of whether > the console is launched with the -indy option. (The issue was initially > observed in indy.) The dynamic portion of the test, when run, ends in a > NullPointerException as Arrays.asList is not expecting a null varargs > parameter. Output seen (-indy or not): > > {code:java} > name: the static name 1 > params is null? false > params length is 1 > [blah] > name: the static name 2 > params is null? false > params length is 2 > [blah, blue] > name: the static name 3 with blah=null > params is null? false > params length is 1 > [null] > Arrays.asList(blah)? [null] > name: the dynamic name 1 > params is null? false > params length is 1 > [blah] > name: the dynamic name 2 > params is null? false > params length is 2 > [blah, blue] > name: the dynamic name 3 with blah=null > params is null? true > Exception thrown > java.lang.NullPointerException > ...{code} > (etc. stack trace not shown for formatting reasons.) > VarArgsTest.jsh - a jshell script demonstrating Java's behaviour, very > similar to the groovy test, but omitting the dynamic portion of the test for > obvious
[jira] [Updated] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ] Rachel Greenham updated GROOVY-10099: - Description: (NB: I would set the priority to P2 default to be triaged, but I seem not to have that option, so I left it at the default I was presented with.) When calling a method with a varargs parameter with a single value, that value is wrapped into an array of length 1. This is the behaviour in Java, and is the expected behaviour, and _most_ of the time is the behaviour in Groovy too. But when that single value is null, Groovy will instead just pass the null into the method. Java will not do this: You'll get an array with a single null in it. Because Groovy's behaviour is unexpected, especially when interfacing with Java code, NullPointerExceptions can often ensue. Adding to the inconsistencies, if the Groovy code calling the method is in a {{@CompileStatic}} context, it behaves just like Java, and the method (whether or not _it_ is statically compiled or a dynamic Groovy method) receives an array with a null in it. So the behaviour in Groovy is inconsistent, both with itself in a {{@CompileStatic}} situation, and with Java, and is requiring workarounds in Java code to handle this normally-impossible eventuality. (Even if no varargs parameter is given you get an empty array, as in fact you do in Groovy too.) This may be an "early instalment weirdness": There's an ancient ticket, from not long after varargs were introduced into Java, which appears to have argued - successfully at the time - that the normal behaviour is a bug: https://issues.apache.org/jira/browse/GROOVY-1026 Further adding to the confusion may be that Groovy usually elides the difference between an {{Object[]}} parameter and an {{Object...}} parameter: They both behave the same. The offending code appears to be in org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines 200-215 in master at the time of writing, which even includes a comment that "if the last argument is null, then we don't have to do anything", with which I respectfully disagree. :) That behaviour should be to return an array with a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to make a new one.) In principle it's an easy fix (although I've left tagging to others as this is my first issue here), but there'd be an obvious nervousness about changing behaviour like this when there might be a lot of old code out there depending on it behaving the way it does now. OTOH the way it behaves now is breaking the expectations of those of us coming to Groovy from a lifetime of Java... Attachments: VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, demonstrating the behaviour. The behaviour is the same regardless of whether the console is launched with the -indy option. (The issue was initially observed in indy.) The dynamic portion of the test, when run, ends in a NullPointerException as Arrays.asList is not expecting a null varargs parameter. Output seen (-indy or not): {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2 [blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] name: the dynamic name 1 params is null? false params length is 1 [blah] name: the dynamic name 2 params is null? false params length is 2 [blah, blue] name: the dynamic name 3 with blah=null params is null? true Exception thrown java.lang.NullPointerException ...{code} (etc. stack trace not shown for formatting reasons.) VarArgsTest.jsh - a jshell script demonstrating Java's behaviour, very similar to the groovy test, but omitting the dynamic portion of the test for obvious reasons. (The statements in the Groovy script ending in semicolons are left that way precisely to mark that they're identical to the Java test.) Runnable with {code:java} jshell PRINTING VarArgsTest.jsh {code} Output seen: {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2 [blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] {code} was: (NB: I would set the priority to P2 default to be triaged, but I seem not to have that option, so I left it at the default I was presented with.) When calling a method with a varargs parameter with a single value, that value is wrapped into an array of length 1. This is the behaviour in Java, and is the expected behaviour, and _most_ of the time is the behaviour in Groovy too. But when that single value is null, Groovy will instead just pass the null into the method. Java will not do this: You'll get an array with a single
[jira] [Updated] (GROOVY-10099) A single null argument to a varargs parameter is received as null
[ https://issues.apache.org/jira/browse/GROOVY-10099?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ] Rachel Greenham updated GROOVY-10099: - Description: (NB: I would set the priority to P2 default to be triaged, but I seem not to have that option, so I left it at the default I was presented with.) When calling a method with a varargs parameter with a single value, that value is wrapped into an array of length 1. This is the behaviour in Java, and is the expected behaviour, and _most_ of the time is the behaviour in Groovy too. But when that single value is null, Groovy will instead just pass the null into the method. Java will not do this: You'll get an array with a single null in it. Because Groovy's behaviour is unexpected, especially when interfacing with Java code, NullPointerExceptions can often ensue. Adding to the inconsistencies, if the Groovy code calling the method is in a {{@CompileStatic}} context, it behaves just like Java, and the method (whether or not _it_ is statically compiled or a dynamic Groovy method) receives an array with a null in it. So the behaviour in Groovy is inconsistent, both with itself in a {{@CompileStatic}} situation, and with Java, and is requiring workarounds in Java code to handle this normally-impossible eventuality. (Even if no varargs parameter is given you get an empty array, as in fact you do in Groovy too.) This may be an "early instalment weirdness": There's an ancient ticket, from not long after varargs were introduced into Java, which appears to have argued - successfully at the time - that the normal behaviour is a bug: https://issues.apache.org/jira/browse/GROOVY-1026 Further adding to the confusion may be that Groovy usually elides the difference between an {{Object[]}} parameter and an {{Object...}} parameter: They both behave the same. The offending code appears to be in org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines 200-215 in master at the time of writing, which even includes a comment that "if the last argument is null, then we don't have to do anything", with which I respectfully disagree. :) That behaviour should be to return an array with a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to make a new one.) In principle it's an easy fix (although I've left tagging to others as this is my first issue here), but there'd be an obvious nervousness about changing behaviour like this when there might be a lot of old code out there depending on it behaving the way it does now. OTOH the way it behaves now is breaking the expectations of those of us coming to Groovy from a lifetime of Java... Attachments: VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, demonstrating the behaviour. The behaviour is the same regardless of whether the console is launched with the -indy option. (The issue was initially observed in indy.) The dynamic portion of the test, when run, ends in a NullPointerException as Arrays.asList is not expecting a null varargs parameter. Output seen (-indy or not): {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2[blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] name: the dynamic name 1 params is null? false params length is 1 [blah] name: the dynamic name 2 params is null? false params length is 2[blah, blue] name: the dynamic name 3 with blah=null params is null? true Exception thrown java.lang.NullPointerException ...{code} (etc. stack trace not shown for formatting reasons.) VarArgsTest.jsh - a jshell script demonstrating Java's behaviour, very similar to the groovy test, but omitting the dynamic portion of the test for obvious reasons. (The statements in the Groovy script ending in semicolons are left that way precisely to mark that they're identical to the Java test.) Runnable with {code:java} jshell PRINTING VarArgsTest.jsh {code} Output seen: {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2 [blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] {code} was: (NB: I would set the priority to P2 default to be triaged, but I seem not to have that option, so I left it at the default I was presented with.) When calling a method with a varargs parameter with a single value, that value is wrapped into an array of length 1. This is the behaviour in Java, and is the expected behaviour, and _most_ of the time is the behaviour in Groovy too. But when that single value is null, Groovy will instead just pass the null into the method. Java will not do this: You'll get an array with a single null
[jira] [Created] (GROOVY-10099) A single null argument to a varargs parameter is received as null
Rachel Greenham created GROOVY-10099: Summary: A single null argument to a varargs parameter is received as null Key: GROOVY-10099 URL: https://issues.apache.org/jira/browse/GROOVY-10099 Project: Groovy Issue Type: Bug Environment: Observed on Groovy 3.0.8 on macOS Big Sur (Intel), but I don't think that's relevant; it'll be everywhere. Reporter: Rachel Greenham Attachments: VarArgsTest.groovy, VarArgsTest.jsh (NB: I would set the priority to P2 default to be triaged, but I seem not to have that option, so I left it at the default I was presented with.) When calling a method with a varargs parameter with a single value, that value is wrapped into an array of length 1. This is the behaviour in Java, and is the expected behaviour, and _most_ of the time is the behaviour in Groovy too. But when that single value is null, Groovy will instead just pass the null into the method. Java will not do this: You'll get an array with a single null in it. Because Groovy's behaviour is unexpected, especially when interfacing with Java code, NullPointerExceptions can often ensue. Adding to the inconsistencies, if the Groovy code calling the method is in a {{@CompileStatic}} context, it behaves just like Java, and the method (whether or not _it_ is statically compiled or a dynamic Groovy method) receives an array with a null in it. So the behaviour in Groovy is inconsistent, both with itself in a {{@CompileStatic}} situation, and with Java, and is requiring workarounds in Java code to handle this normally-impossible eventuality. (Even if no varargs parameter is given you get an empty array, as in fact you do in Groovy too.) This may be an "early instalment weirdness": There's an ancient ticket, from not long after varargs were introduced into Java, which appears to have argued - successfully at the time - that the normal behaviour is a bug: https://issues.apache.org/jira/browse/GROOVY-1026 Further adding to the confusion may be that Groovy usually elides the difference between an {{Object[]}} parameter and an {{Object...}} parameter: They both behave the same. The offending code appears to be in org.codehaus.groovy.reflection.ParameterTypes.java in method fitToVars, lines 200-215 in master at the time of writing, which even includes a comment that "if the last argument is null, then we don't have to do anything", with which I respectfully disagree. :) That behaviour should be to return an array with a single null in it (Handily, MetaClassHelper.ARRAY_WITH_NULL saves having to make a new one.) In principle it's an easy fix (although I've left tagging to others as this is my first issue here), but there'd be an obvious nervousness about changing behaviour like this when there might be a lot of old code out there depending on it behaving the way it does now. OTOH the way it behaves now is breaking the expectations of those of us coming to Groovy from a lifetime of Java... Attachments: VarArgsTest.groovy - a script saved from, and runnable in, groovyConsole, demonstrating the behaviour. The behaviour is the same regardless of whether the console is launched with the --indy option. (The issue was initially observed in indy.) The dynamic portion of the test, when run, ends in a NullPointerException as Arrays.asList is not expecting a null varargs parameter. Output seen (--indy or not): {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2[blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] name: the dynamic name 1 params is null? false params length is 1 [blah] name: the dynamic name 2 params is null? false params length is 2[blah, blue] name: the dynamic name 3 with blah=null params is null? true Exception thrown java.lang.NullPointerException ...{code} (etc. stack trace not shown for formatting reasons.) VarArgsTest.jsh - a jshell script demonstrating Java's behaviour, very similar to the groovy test, but omitting the dynamic portion of the test for obvious reasons. (The statements in the Groovy script ending in semicolons are left that way precisely to mark that they're identical to the Java test.) Runnable with {code:java} jshell PRINTING VarArgsTest.jsh {code} Output seen: {code:java} name: the static name 1 params is null? false params length is 1 [blah] name: the static name 2 params is null? false params length is 2 [blah, blue] name: the static name 3 with blah=null params is null? false params length is 1 [null] Arrays.asList(blah)? [null] {code} -- This message was sent by Atlassian Jira (v8.3.4#803005)