Jochen, > On 14 Aug 2018, at 6:34 PM, Jochen Theodorou <blackd...@gmx.org> wrote: > [...] >> For the moment, the best solution — far as I have been able to ascertain — >> consists of [*] >> (a) at launch, setting own DelegatingMetaClass subclass for a >> Null.metaclass; it essentially would return null from invokeMethod, but >> still needs to process special cases (like e.g., null.is >> <http://null.is>(foo)) explicitly; > > so for general Groovy... how are we supposed to recognized in a transform if > "is" has been replaced?
It is definitely highly arguable, but in the very specific case of “null?.is(null)“, which currently returns an absurd (though technically understandable) false value, I would rather suggest to break the backward compatibility. I can't really see anyone whose code would actually use "?.is", and moreover, rely on that “null?.is(null)“ is valid and returns null (instead of true). Is there such a person in the sweet world? Anyway, the point is, I'd say, rather at the unimportant side, for I believe is() is being won't be widely used anyway, given we got === and !=== now. Thus, even if we decide that breaking backward compatibility even in this slightly insane case is a big no-no, we just can keep the current behaviour of “null?.is(null)==null“ unchanged. There's no real harm; new code would simply use === instead, and old one would work as before. >> (b) since the above for some godforsaken reason does not work for property >> access at all, still implement an ASTT with a ClassCodeExpressionTransformer >> to set expression.safe=true for get/setProperty, guarding explicitly against >> the known problematic cases (e.g., super.getProperty). > > are you saying x?.foo will NPE if x is null? Or that x?.getFoo() will NPE in > that case? Not sure how to read your comment. Provided only (a) “the Null.metaclass; returning null from invokeMethod” is used and no ASTT, then yes, it would NPE. Easiest thing on earth to try: === 262 /tmp> <q.groovy class q { static main(args) { // ExpandoMetaClass.enableGlobally() // I thought this is needed; seems not (though my application use it anyway) def mc=new OCSNMC(org.codehaus.groovy.runtime.NullObject) mc.initialize() org.codehaus.groovy.runtime.NullObject.metaClass=mc println "null.foo() is OK: ${null.foo()==null}" println "null.foo we won't see: ${null.foo==null}" } } class OCSNMC extends DelegatingMetaClass { OCSNMC(Class clazz){ super(clazz) } Object invokeMethod(Object object, String methodName, Object[] arguments) { null } } 263 /tmp> /usr/local/groovy-2.4.15/bin/groovy q null.foo() is OK: true Caught: java.lang.NullPointerException: Cannot get property 'foo' on null object java.lang.NullPointerException: Cannot get property 'foo' on null object at q.main(q.groovy:9) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 264 /tmp> === >> For all I know, this probably would not work properly with @CompileStatic >> (which I do not use at all myself, but others do frequently). > > the result type could be a problem... Worth to check. Definitely :) >> Trust me, been there, done that. I am pretty darn sure it would be >> /infinitely/ easier and, what's important, more reliable in the core with an >> explicit compiler support. > > How about making a small github project to dump the current state there? Not that easy, for my code is mixed up with other ASTT and runtime stuff; but I'll try to make some simple proof-of-concept ASAP and send here. Thanks and all the best, OC