-
-
Notifications
You must be signed in to change notification settings - Fork 953
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
Grails 6.2.0 - Cast Exception if Action takes Command #13486
Comments
That is interesting! Could you also post the generated code when the variable is renamed? |
This behavior wasn't present in our project for Grails 5.x. We first noticed it when we updated to Grails 6.x. |
FYI: I ran into this too. I found that if I use the method parameter name 'cmd' for the command object, then it fails:
produces the org.codehaus.groovy.runtime.typehandling.GroovyCastException when I call save(). But when changing the parameter named to 'acmd', everything works as expected:
|
I've learned a bit more... it is not JUST the name. I have another case where the name of the parameter is 'cmd' and it works just fine. |
I am investigating this issue. At https://github.com/osscontributor/issue13486 I have a simple sample that demonstrates the issue: grails-app/controllers/issue13486/DemoController.groovy package issue13486
class DemoController {
def list(DemoCommand x) {
}
// commenting out this method or the variable declaration in it
// will allow the unit test to pass
private void placeToDeclareLocalVariable() {
String x;
}
}
class DemoCommand {
} src/test/groovy/issue13486/DemoControllerSpec.groovy package issue13486
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {
void "test index action"() {
when: 'the controller action is invoked'
controller.list()
then:
noExceptionThrown()
}
} I will update soon. |
Will investigate whether or not c46d2cd is relevant. (looks unlikely right now) |
FYI... The expression at https://github.com/grails/grails-core/blob/v6.0.0/grails-plugin-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java#L853 does appear to be relevant, details still not entirely clear. |
FYI, For an update, this is what I am seeing. If I declare a method in the controller like this… private void placeToDeclareLocalVariable() {
String x
} The ast generated no-arg list() method will contain the following: String x = null;
x = __$stMC || BytecodeInterface8.disabledStandardMetaClass() ?
ShortTypeHandling.castToString((Object)arrcallSite[43].callCurrent((GroovyObject)this, DemoCommand.class, (Object)"x")) :
ShortTypeHandling.castToString((Object)this.initializeCommandObject(DemoCommand.class, (String)"x")); If I change that method to look like this… private void placeToDeclareLocalVariable() {
java.sql.Connection x
} The ast generated no-arg list() method will contain the following: Connection x = null;
x = __$stMC || BytecodeInterface8.disabledStandardMetaClass() ?
(Connection)ScriptBytecodeAdapter.castToType((Object)arrcallSite[43].callCurrent((GroovyObject)this, DemoCommand.class, (Object)"x"), Connection.class) :
(Connection)ScriptBytecodeAdapter.castToType((Object)this.initializeCommandObject(DemoCommand.class, (String)"x"), Connection.class); If I remove that method altogether, the following is generated (which looks right): DemoCommand x = null;
x = __$stMC || BytecodeInterface8.disabledStandardMetaClass() ?
(DemoCommand)ScriptBytecodeAdapter.castToType((Object)arrcallSite[43].callCurrent((GroovyObject)this, DemoCommand.class, (Object)"x"), DemoCommand.class) :
(DemoCommand)ScriptBytecodeAdapter.castToType((Object)this.initializeCommandObject(DemoCommand.class, (String)"x"), DemoCommand.class); It is not yet clear how the type in that local variable is affecting the code that gets generated for that list() method. |
This should set accessedVariable to itself
@codeconsole Are there particular types of constructor args that are not working for you? I am building up some automated tests around this using inheritance and constructor args and am trying to identify a scenario that doesn't work. Thank you for your input! |
The info I have right now is the following:
I have built up tests that involve extending controller and passing super args and will put those in a PR soon. If you can help me know what the problematic scenarios are I can get those addressed and I appreciate your help describing the issue. |
@codeconsole I got some info this afternoon that indicates that changes you have recently made to scaffolding may demonstrate the issue. I am going to test with the latest scaffolding code and that may lead to where I need to be. In the meantime, if you can share any info here describing the problem you found that might prove helpful. Thank you! |
@osscontributor I don't think you will get it with the scaffolding specifically. I will create an example for you. It's an edge case if you reuse a parent controller and pass a parameter to the parent. A compilation error occurs with the change. The error did not occur prior to the change. The parameter does not implement Validateable. @Artefact("Controller")
class GenericController<T> {
Class<T> resource
GenericController(Class<T> resource) {
this.resource = resource
}
def show(Map model) {
if (!model) {
model = [:]
}
respond resource.get(params.id), model: model
}
}
class UserController extends GenericController<User> {
public UserController() {
super(User)
}
def show() {
super.show([hello:'world'])
}
}
|
Thank you! This is helpful. I will work up some tests and a solution. |
Thanks @osscontributor, also be aware that there can be multiple parameters to the method. You probably don't even need generics to replicate and can just extend a normal controller. def show(Long id, String name, Map model) {
if (!model) {
model = [:]
}
respond resource.get(id), model: model
} I know this is kind of edgy, but it really adds flexibility to the codebase and eliminates a lot of duplicate generated code. |
If you have a We added code a long time ago to prevent overloading controller actions and this is bumping up against some assumptions that are made in the code. Thank you for clarification! |
@codeconsole If you can share a small simple sample 6.2.1-SNAPSHOT project which demonstrates code that doesn't compile with the change that was proposed and later reverted, I am happy to resolve that. Your help would be appreciated. |
You would want |
I will create you an example app that replicates the issue with the previous commit. |
Thank you! |
When |
It looks like the |
What is it that you would expect to be in the |
There isn't that but FYI there is a |
Hi @osscontributor, I appreciate your help figuring this one out.
So I over generalized the problem. I can not replicate this issue specifically with a
null, which has been the case
yeah, I was aware, but in this particular case I am only interested in passing a model. |
I misunderstood and thought you wanted to avoid passing the model.
That is good info. I spent yesterday trying to do that and found the same. Thank you for the confirmation!
That is perfect. I will investigate that repo today. I really appreciate your help. |
Is it possible for the problem to manifest in a Grails application? I know I wasn't explicit about asking for the app to be a Grails app, but that what would be helpful. Thank you for your feedback! |
Locally here when I copy the GenericController and ModelResolver class into a Grails app, the controller is compiling fine. I expect I am missing a build dependency but I can't tell what it is. If I can reproduce the problem in a Grails app, I am sure I can resolve it. |
Interesting is that if I remove org.springframework.boot:spring-boot-autoconfigure from your app, the code seems to compile. That same dependency is in the Grails app environment I am trying to test with and the code does compile with that dependency. I don't yet know what that means, but is a point of interest. |
I think in your project the the build will fail with the following dependencies:
And will compile successfully with the following (only difference is order):
I see the same thing in the grails app. If I declare the org.springframework.boot:spring-boot-autoconfigure dependency first, then the compile error emerges. |
@codeconsole Is it the case that as far as we know this issue has nothing to do with super args and nothing to do with inheritance? |
I am not 100% certain but it looks like as long as some dependency declared before org.springframework.boot:spring-boot-autoconfigure is pulling in Groovy, then the code compiles, including explicitly adding |
@paulk-asert - does this behavior make sense to you?...
|
I was suggesting perhaps in the future it would be nice to always have access to a bound default
This was a Grails plugin that previously also ran using This was the initial dependency block before I stripped it down to replicate the issue:
I really don't know exactly because the issue goes away when commit 355eccfc1804ca0b6cfe912a1f8309e8735b0740 is removed. This is what makes it baffling. All you have to do is remove |
Thank you. I am trying to chase down what role the inheritance and super args play. As far as I can tell so far they aren't relevant. I will report back. |
It might be safe to assume this has nothing to do with inheritance at this point considering compilation still fails without it. |
Understanding why adding a dependency like org.codehaus.groovy:groovy:3.0.21 to the top of the dependencies list makes this particular error go away may prove helpful. I am investigating and have asked for help. |
Another way to make that failing build mentioned above compile successfully is by removing the I am not suggesting that is a solution. It is just a data point while looking at the role the dependencies are playing. |
Is the artifact still being properly processed when those changes are made or does making those changes prevent the artifact from being processed which is why the compilation ends up being successful? |
In the tests I am looking at now, they are being processed. |
@osscontributor I am not sure I understand the full behavior expected for that AST transform but does this achieve the desired behaviors:
|
@paulk-asert That fixes the "The current scope already contains a variable of the name..." issue but brings back the root issue that this issue was originally about. This is a good pointer. I will move forward and find a solution that addresses both of those. I have a better idea now of what the problematic scenarios are. Thank you for the input! |
This change should assign a value to the existing variable instead of declaring a new variable. This commit breaks some tests and this branch is currently a WIP.
I believe #13698 addresses this so this can be closed? @osscontributor can you please confirm? |
Expected Behavior
After upgrading to Grails 6.2.0, one of our controllers started to error. I was able to reproduce this issue in a sample application. Visiting http://localhost:8080/example/list should render the list action renders without issue. Instead an exception is thrown.
Changing a variable name in an unrelated action fixes the issue.
Actual Behaviour
A class cast exception occurs:
Cannot cast object 'brokenBinding.ExampleSearchCommand@116e5496' with class 'brokenBinding.ExampleSearchCommand' to class 'brokenBinding.ExampleCommand'
Steps To Reproduce
Details in README.md
Environment Information
Example Application
https://github.com/jdaugherty/grails-broken-binding
Version
6.2.0
The text was updated successfully, but these errors were encountered: