New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GROOVY-8096 setScriptBaseClass with Java class breaks @Field Binding init due to call to super() instead of super(Binding) #502
base: master
Are you sure you want to change the base?
Conversation
…ass with a Java class causes ModuleNode to generate a constructor that prevents Field initialization from Binding context.
| * A Script which requires a Binding passed in the constructor and disallows calling the default constructor. | ||
| */ | ||
| public abstract class BindingScript extends Script { | ||
| protected BindingScript() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about making the default constructor private? Developers can get compilation error other than runtime error, then the program will be more robust IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the exact same super() call is generated, because classNode.getSuperClass().getDeclaredConstructor(SCRIPT_CONTEXT_CTOR) incorrectly returns null in ModuleNode.createStatementsClass() even though that constructor exists.
If i make the default constructor private i get IllegalAccessError:
java.lang.reflect.InvocationTargetException
at TestBindingsInFieldInitializersWithConfigJavaBaseScript.main(TestBindingsInFieldInitializersWithConfigJavaBaseScript.groovy)
Caused by: java.lang.IllegalAccessError: tried to access method groovy.lang.BindingScript.<init>()V from class TestBindingsInFieldInitializersWithConfigJavaBaseScript
at TestBindingsInFieldInitializersWithConfigJavaBaseScript.<init>(TestBindingsInFieldInitializersWithConfigJavaBaseScript.groovy)
If i remove the default constructor i get NoSuchMethodError:
java.lang.reflect.InvocationTargetException
at TestBindingsInFieldInitializersWithConfigJavaBaseScript.main(TestBindingsInFieldInitializersWithConfigJavaBaseScript.groovy)
Caused by: java.lang.NoSuchMethodError: groovy.lang.BindingScript: method <init>()V not found
at TestBindingsInFieldInitializersWithConfigJavaBaseScript.<init>(TestBindingsInFieldInitializersWithConfigJavaBaseScript.groovy)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If i remove both constructors the super() call just goes to the default constructor in groovy.lang.Script, giving MissingPropertyException on trying to access the wrong Binding context on field initalization:
groovy.lang.MissingPropertyException: No such property: args for class: TestBindingsInFieldInitializersWithConfigJavaBaseScript
at TestBindingsInFieldInitializersWithConfigJavaBaseScript.<init>(TestBindingsInFieldInitializersWithConfigJavaBaseScript.groovy:2)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:271)
|
Created JIRA ticket at https://issues.apache.org/jira/browse/GROOVY-8096 |
|
Just to clarify - this isn't a PR designed to fix a problem (although BindingScript might prove useful in a solution) but to illustrate a problem. Correct? |
|
@paulk-asert Correct, this just adds a couple of unit tests to illustrate the problem. I hope you agree that it's better than just describing it. :) I was able to make a patch myself where i use Class.forName() to look up the class, but i highly doubt it is correct. |


This test fails because
ClassNode.getSuperClass().getDeclaredConstructor(SCRIPT_CONTEXT_CTOR)doesn't see the constructors in the Java base class.ModuleNode.setScriptBaseClassFromConfig(ClassNode)calls
.setSuperClass(ClassHelper.make(baseClassName))on thescriptDummyClassNode.The
ClassNodecreated for this script's base class has.lazyInitDone = trueand.constructors = nullso when.getSuperClass().getDeclaredConstructor(SCRIPT_CONTEXT_CTOR)is called byModuleNode.createStatementsClass(), thenClassNode.constructorsis set to an empty ArrayList inClassNode.getDeclaredConstructors()The script constructor is then generated as
instead of
Fields are initialized between the call to super() and the setBinding(context)
which means Field initializers don't have access to the Binding context.
This leads to
MissingPropertyExceptionbecause we're trying to look up variables from thenew Binding()created in the default constructor, instead of the binding we passed in.