Problem inheriting LayoutFocusTraversalPolicy and other Swing classes with Java 17
Environment Information
JRuby versions tested: 9.2.20.1, 9.3.4.0 Java version: openjdk version "17.0.3" 2022-04-19 System: Linux bree 5.17.0-1-amd64 #1 SMP PREEMPT Debian 5.17.3-1 (2022-04-18) x86_64 GNU/Linux
Expected Behavior
I am trying to inherit LayoutFocusTraversalPolicy class from Java. This has worked until now with Java 8. I include a little script for reproducing the problem. Expected behaviour is that when yo use tab to change focus between components, all works.
Actual Behavior
The reality is worse. When using tab to change focus I get an error:
Exception in thread "AWT-EventQueue-0" org.jruby.exceptions.NoMethodError: (NoMethodError) super: no superclass method `accept' for #<FocusTraversalManager:0x1de5cc88 @potential_searching=false> at coso.accept(coso.rb:38)
As I said before, with Java 8 this code works well and it seems that should work in any case.
Problem seems general enough (I get errors too with AbstractListModel). Perhaps something related with protected superclass methods?...
Has anybody tested this problem?
It seems an important error and it happens in different JRuby versions. It you need more info, please, tell me. When I inherit some Java classes in Java 17 I can't use super for overwritting superclass methods.
Thank you for the report! This could be due to us locking down access to protected methods when they are not "accessible" or "open", which would be the case on all Java versions after 9 (which is when the Java module system was introduced). I suspect this would also fail on Java versions 9 through 16. The case here is that accept is protected on FocusTraversalManager, so we don't bind it, and then we fail to call it.
I can confirm this is still broken on 9.3 HEAD.
We can try to come up with a workaround, probably via invoking the method directly, but this is more of a general problem since we need to be able to call a protected super method from child classes even if we do not bind that method to the Ruby proxy class.
cc @enebo @byteit101 for thoughts on how to go forward with fixing this. Marking for 9.3.7.0 but this may be a 9.4 feature.
Does using -add-opens for the enclosing package make this issue go away? I know it can resolve some of these issues, though the error message is different than I expect
On Mon, Aug 1, 2022, 12:59 PM Charles Oliver Nutter < @.***> wrote:
Thank you for the report! This could be due to us locking down access to protected methods when they are not "accessible" or "open", which would be the case on all Java versions after 9 (which is when the Java module system was introduced). I suspect this would also fail on Java versions 9 through 16. The case here is that accept is protected on FocusTraversalManager, so we don't bind it, and then we fail to call it.
I can confirm this is still broken on 9.3 HEAD.
We can try to come up with a workaround, probably via invoking the method directly, but this is more of a general problem since we need to be able to call a protected super method from child classes even if we do not bind that method to the Ruby proxy class.
cc @enebo https://github.com/enebo @byteit101 https://github.com/byteit101 for thoughts on how to go forward with fixing this. Marking for 9.3.7.0 but this may be a 9.4 feature.
— Reply to this email directly, view it on GitHub https://github.com/jruby/jruby/issues/7220#issuecomment-1201468334, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEM6QDRXILGF2AOVNH4HT3VW767FANCNFSM5WLQBFDA . You are receiving this because you were mentioned.Message ID: @.***>
@byteit101 I'm not sure this case would be helped... we do not bind protected methods to the Ruby proxies, since they should not be callable as public methods. That in turn triggers this simple super to not find the method and fail to invoke it.
Can you confirm whether this form of super would get rewritten by the work you did?
I don't understand reason for not binding protected methods. They should be callable from inherited classes, shouldn't they?
As a data point this will make the script run in Java 9+:
jruby -J--add-opens=java.desktop/javax.swing=org.jruby.dist example.rb
Java 8 does work so this is exclusively in the domain of module issue. I half wonder if we should just add-opens all of core jdk?
I half wonder if we should just add-opens all of core jdk
I have been trying to avoid doing this since it subverts the module system globally, but that means people will keep running into this. In another issue I can't remember, I proposed that perhaps we should bind all methods -- even protected and private ones -- but make them error with a tip on how to avoid the issue. That would at least point people in the right direction.
These module flags are also a big part of why I added the .jruby.java_opts support, so that users can wire up appropriate add-opens and other flags for their app without having to create a custom launcher script. We have the tools but perhaps have not publicized them well enough.
@headius Is it possible to know what module a class is in at runtime?
@enebo Yes, it is posslble to know the target class's module as well as the calling class's module (e.g. org.jruby.dist or org.jruby.complete or whatever). We can tailor the error to explain exactly what flag is needed for the method to be callable.
We should also consider a fix that does not need add-opens, so that super methods that should be callable are callable without opening up the entire package.
This has a workaround and we are discussing other ways to improve so punting this to next release.
@brometeo We do not bind them because they are not callable from other classes, but would look callable to Ruby users. That said, perhaps we should try to find a way to better unify Ruby visibility and Java visibility so that the methods are callable from appropriate contexts (subclasses).
I have a few thoughts on how to move forward with this, but using @enebo's add-opens flag above (possibly added to .jruby.java_opts files) should work around the issue for now.
Great. Thank you very much :)
Revisiting this since it came up on the JRuby mailiing list:
There's currently no planned resolution because this is a tricky issue to solve. The workaround of using --add-opens is pretty good, and ultimately some form of that will probably be necessary given Java 17 locking down modules completely.
I do believe we should take another look at this, but it would not happen in a JRuby 9.3.x release most likely. We do have an opportunity with the new "real" subclass work that @byteit101 did to dispatch these super calls to the actual protected superclass method, similar to how we use core rewriting to allow initialize to call the superclass's constructor. Such dispatches would get routed directly to the Java superclass method rather than through Ruby method dispatch in the cases where we see there's a Java method available to call (even if it is not bound in Ruby). The other option would be to start binding all these methods but mark them with appropriate visibility (e.g. Ruby protected for Java protected) and handle dispatching via super and Ruby method lookup by using invokedynamic to call the right method from the right source (the "real" subclass).
@byteit101 You have any thoughts here? We might start by re-adding protected methods into the Ruby method tables and see how hard it is to get them callable using invokedynamic. The super-splitting might not be necessary in that case, as long as we can do the dispatch with a real subclass in hand.
I think we're going to have to accept this is unlikely to be fixed in 9.3 given the invasiveness of any fix. It's still problematic to fix in 9.4+ but we have more freedom there and in 9.5 to push forward with better modularization. That should in turn make it easier to expose protected superclass methods.
I'm sorry about the breakage here folks, but it's really the newer Java modularity to blame since it forces us to be more strict about allowing access to methods that are not public. The --add-opens workaround is fairly simple and can be put into .jruby.java_opts files either in your app, your home dir, or your JRuby installation (see https://github.com/jruby/jruby/wiki/JRuby-Java-Options-Files).
I'm punting to 9.4ish, but it also won't be fixed in 9.4.4.
JRuby 9.5 is the big modularity push, so rehoming there.