jira.codehaus.org

  • Log In Access more options
    • Online Help
    • Keyboard Shortcuts
    • About JIRA
    • JIRA Credits
    • What?s New
  • Dashboards Access more options (Alt+d)
  • Projects Access more options (Alt+p)
  • Issues Access more options (Alt+i)
  • JRuby
  • JRUBY-780

JavaUtilities.extend_proxy methods that call super() can't find super's method if it is implemented in an ancestor's class

  • Log In
  • Views
    • XML
    • Word
    • Printable

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: JRuby 0.9.8
  • Fix Version/s: JRuby 0.9.9
  • Component/s: Java Integration
  • Labels:
    None
  • Environment:
    OSX recent svn checkout as of 4/5/07

Description

Given 2 java classes, Parent and Child. Parent implements who() method, child does not.

JavaUtilities.extend_proxy('Child') {
def who()
"I'm a big baby: " + super()
end
}

claims that
MissingMethod.rb:#:in `who': super: no superclass method 'who' (NameError)
from MissingMethod.rb:#

And it's right: Child does not have that method - but it's parent class sure does!

Attached is a trivial test case.

  • Options
    • Sort By Name
    • Sort By Date
    • Ascending
    • Descending
    • Download All

Attachments

  1. GZip Archive
    MissingMethod.tar.gz
    05/Apr/07 5:26 PM
    0.5 kB
    Kurt Werle

Issue Links

is duplicated by

Bug - A problem which impairs or prevents the functions of the product. JRUBY-782 Adding methods to a java class results in an access error when calling on a java subclass

  • Minor - Minor loss of function, or other problem where easy workaround is present.
  • Closed - The issue is considered finished, the resolution is correct. Issues which are not closed can be reopened.
is superceded by

Bug - A problem which impairs or prevents the functions of the product. JRUBY-814 Multiple improvements to Java integration (was: Java method get lost.)

  • Major - Major loss of function.
  • Closed - The issue is considered finished, the resolution is correct. Issues which are not closed can be reopened.

Activity

Ascending order - Click to sort in descending order
  • All
  • Comments
  • Work Log
  • History
  • Activity
Hide
Permalink
Kurt Werle added a comment - 06/Apr/07 11:42 AM

Oops - it's worse than that. It looks like super() does not work at all for methods. If you extend the Parent class, it can not get the original who() method, either.

Show
Kurt Werle added a comment - 06/Apr/07 11:42 AM Oops - it's worse than that. It looks like super() does not work at all for methods. If you extend the Parent class, it can not get the original who() method, either.
Hide
Permalink
Kurt Werle added a comment - 06/Apr/07 12:11 PM

And you can't seem to get a handle on the previous implementation by using alias, either.

Show
Kurt Werle added a comment - 06/Apr/07 12:11 PM And you can't seem to get a handle on the previous implementation by using alias, either.
Hide
Permalink
Charles Oliver Nutter added a comment - 06/Apr/07 3:32 PM

This may be more of a systemic issue in JRuby that doesn't have an immediate solution. The basic problem is that Java types entering JRuby do not map their hierarchies appropriately to the Java hierarchy. An instance of HashMap, for example, extends a proxy class named HashMap, the super class of that proxy class is ConcreteJavaProxy instead of the usual Java hierarchy you'd expect. This leads to problems for methods like super() that try to go up the Ruby hierarchy instead of the Java hierarchy.

A possible workaround for this would be to provide a call_super method that knows about the Java hierarchy. I believe that JavaSupport needs an internal overhaul post-1.0, but this would at least give you a way to call Java superclass method appropriately from within Ruby code.

Show
Charles Oliver Nutter added a comment - 06/Apr/07 3:32 PM This may be more of a systemic issue in JRuby that doesn't have an immediate solution. The basic problem is that Java types entering JRuby do not map their hierarchies appropriately to the Java hierarchy. An instance of HashMap, for example, extends a proxy class named HashMap, the super class of that proxy class is ConcreteJavaProxy instead of the usual Java hierarchy you'd expect. This leads to problems for methods like super() that try to go up the Ruby hierarchy instead of the Java hierarchy. A possible workaround for this would be to provide a call_super method that knows about the Java hierarchy. I believe that JavaSupport needs an internal overhaul post-1.0, but this would at least give you a way to call Java superclass method appropriately from within Ruby code.
Hide
Permalink
Charles Oliver Nutter added a comment - 18/Apr/07 4:00 AM

From my email about this issue, and why it won't be fixed before 1.0:

Well, I've stalled looking at JavaSupport for a long time since it basically works ok except for various cases (that people are now reporting in force). I've now been having a look at it for several hours, and I've come to a determination:

It needs a redesign to carry us to the future (probably post 1.0).

Much of this is legacy code, largely unchanged from years gone by. We've done what we could to clean it up, improve the code, add in concrete extension support, but the bottom line appears to be that this design may not be right for the long-term future of JRuby.

Now don't get me wrong...it still works, and in general it works really well. But people have started to hit the places where it doesn't (and may never) work correctly, like calling super when adding methods to a proxied class (super goes up the Ruby hierarchy instead of the Java hierarchy), not necessarily seeing the methods you'd expect) or extending classes and looking for the same behavior as in Java.

The basic problem extends from the fact that we don't actually duplicate the Java hierarchy in Ruby. We fake it.

Instances of HashMap, in Ruby, do extend what looks like a HashMap class. You can even reopen that class, add utility methods and so on. But what if you call super?

The proxied HashMap's super class is not AbstractMap, as you might hope, it's ConcreteJavaProxy. And here is where the troubles begin.

x = HashMap
puts "#

Unknown macro: {x}
" while x = x.super

ConcreteJavaProxy
JavaProxy
Object

So for example, you can't do this directly:

class Map
def foo; puts 'in Map'; end
end
class HashMap
def foo; puts 'in HashMap'; super; end
end

...because there is no foo method on ConcreteJavaProxy, and it does not have smarts to look at the Map proxy (nor could it really dispatch to id, since HashMap and Map are in separate hierarchies.

The proxy logic usually works fine...it allows most Ruby code to call Java code without problems, and largely the interfaces and "feel" of calling Java code won't ever have to change. But for the two worlds to truly work well together, there's a rework needed at the core of JavaSupport.

I believe the hierarchy for HashMap should look like this:

HashMap
AbstractMap
Object # and it's debateable whether this ought to be Ruby's "Object" or a magical JavaObject; the effect is the same

And I also believe that HashMap should mix in the following "modules" to emulate interface behavior:

Cloneable
Map
Serializable

Now the purpose of these interfaces being modules (when we have previously said this is not an appropriate way to represent interfaces) is that we want methods from those interfaces to appear on HashMap, and we want kind_of to work without goofy tricks. Both are almost exactly the same behavior that interfaces give you in Java.

Now this wouldn't mean that interfaces are general-purpose mixins, since we couldn't really do that. Java's Map interface will expect that a given invocation target really implements Map, and we can't change that after the class has been created. But it would mean that if you implement the Map interface in Ruby code, we'll create a new class. Call it MyMap:

MyMap < Object
MyMap would mix in, or appear to mix in Map

This would again give us the correct appearance for MyMap without the proxy hierarchies.

So then the magic of all this is that we need a different way to represent metaclasses. I propose that Java objects within Ruby would not use RubyModule, as the proxies do today, but would use a new implementation of "MetaClass" that knows how to proxy calls to those objects. This would be similar to how Groovy does Java integration, but of course we'd continue to look and feel like Ruby code throughout.

This requires the following changes in the long term:

We need to pull off an interface for what a MetaClass is. RubyClass would implement this. We may want two interfaces, one for RubyModule and one for RubyClass, that represent class and interface-like behavior, but that's not clear yet.

When extending a Java class or implementing an interface, your new type will also implement IRubyObject, allowing it to be passed around directly throughout JRuby without a wrapper object. This will improve performance of extended types tremendously.

Long term, we should start passing Object around instead of IRubyObject, and use either Java-based or Ruby-based dispatching depending on what the object's actual type is.

These changes, and changes to the core runtime to accommodate them, would eventually eliminate almost all the javasupport code we have today, improve performance of java support in general, and allow Java and Ruby types to intermingle almost seamlessly, without impacting Ruby performance in any way.

I think this is the future.

Show
Charles Oliver Nutter added a comment - 18/Apr/07 4:00 AM From my email about this issue, and why it won't be fixed before 1.0:
Well, I've stalled looking at JavaSupport for a long time since it basically works ok except for various cases (that people are now reporting in force). I've now been having a look at it for several hours, and I've come to a determination: It needs a redesign to carry us to the future (probably post 1.0). Much of this is legacy code, largely unchanged from years gone by. We've done what we could to clean it up, improve the code, add in concrete extension support, but the bottom line appears to be that this design may not be right for the long-term future of JRuby. Now don't get me wrong...it still works, and in general it works really well. But people have started to hit the places where it doesn't (and may never) work correctly, like calling super when adding methods to a proxied class (super goes up the Ruby hierarchy instead of the Java hierarchy), not necessarily seeing the methods you'd expect) or extending classes and looking for the same behavior as in Java. The basic problem extends from the fact that we don't actually duplicate the Java hierarchy in Ruby. We fake it. Instances of HashMap, in Ruby, do extend what looks like a HashMap class. You can even reopen that class, add utility methods and so on. But what if you call super? The proxied HashMap's super class is not AbstractMap, as you might hope, it's ConcreteJavaProxy. And here is where the troubles begin. x = HashMap puts "#
Unknown macro: {x}
" while x = x.super ConcreteJavaProxy JavaProxy Object So for example, you can't do this directly: class Map def foo; puts 'in Map'; end end class HashMap def foo; puts 'in HashMap'; super; end end ...because there is no foo method on ConcreteJavaProxy, and it does not have smarts to look at the Map proxy (nor could it really dispatch to id, since HashMap and Map are in separate hierarchies. The proxy logic usually works fine...it allows most Ruby code to call Java code without problems, and largely the interfaces and "feel" of calling Java code won't ever have to change. But for the two worlds to truly work well together, there's a rework needed at the core of JavaSupport. I believe the hierarchy for HashMap should look like this: HashMap AbstractMap Object # and it's debateable whether this ought to be Ruby's "Object" or a magical JavaObject; the effect is the same And I also believe that HashMap should mix in the following "modules" to emulate interface behavior: Cloneable Map Serializable Now the purpose of these interfaces being modules (when we have previously said this is not an appropriate way to represent interfaces) is that we want methods from those interfaces to appear on HashMap, and we want kind_of to work without goofy tricks. Both are almost exactly the same behavior that interfaces give you in Java. Now this wouldn't mean that interfaces are general-purpose mixins, since we couldn't really do that. Java's Map interface will expect that a given invocation target really implements Map, and we can't change that after the class has been created. But it would mean that if you implement the Map interface in Ruby code, we'll create a new class. Call it MyMap: MyMap < Object MyMap would mix in, or appear to mix in Map This would again give us the correct appearance for MyMap without the proxy hierarchies. So then the magic of all this is that we need a different way to represent metaclasses. I propose that Java objects within Ruby would not use RubyModule, as the proxies do today, but would use a new implementation of "MetaClass" that knows how to proxy calls to those objects. This would be similar to how Groovy does Java integration, but of course we'd continue to look and feel like Ruby code throughout. This requires the following changes in the long term: We need to pull off an interface for what a MetaClass is. RubyClass would implement this. We may want two interfaces, one for RubyModule and one for RubyClass, that represent class and interface-like behavior, but that's not clear yet. When extending a Java class or implementing an interface, your new type will also implement IRubyObject, allowing it to be passed around directly throughout JRuby without a wrapper object. This will improve performance of extended types tremendously. Long term, we should start passing Object around instead of IRubyObject, and use either Java-based or Ruby-based dispatching depending on what the object's actual type is. These changes, and changes to the core runtime to accommodate them, would eventually eliminate almost all the javasupport code we have today, improve performance of java support in general, and allow Java and Ruby types to intermingle almost seamlessly, without impacting Ruby performance in any way. I think this is the future.
Hide
Permalink
Kurt Werle added a comment - 18/Apr/07 10:23 AM

All I'd really like to see is some way to call the original method. If I have to do something like

proxyObjectFor(self).getJavaObject().callJavaMethod("ThisMethodName", params)

that'd be just fine.

Show
Kurt Werle added a comment - 18/Apr/07 10:23 AM All I'd really like to see is some way to call the original method. If I have to do something like proxyObjectFor(self).getJavaObject().callJavaMethod("ThisMethodName", params) that'd be just fine.
Hide
Permalink
Bill Dortch added a comment - 20/Apr/07 6:25 AM

This is resolved by JRUBY-814 (once applied).

Show
Bill Dortch added a comment - 20/Apr/07 6:25 AM This is resolved by JRUBY-814 (once applied).
Hide
Permalink
Charles Oliver Nutter added a comment - 20/Apr/07 1:19 PM

This will be fixed when we commit the JRUBY-814 patch some time today. Watch that bug for updates.

Show
Charles Oliver Nutter added a comment - 20/Apr/07 1:19 PM This will be fixed when we commit the JRUBY-814 patch some time today. Watch that bug for updates.

People

  • Assignee:
    Charles Oliver Nutter
    Reporter:
    Kurt Werle
Vote (0)
Watch (1)

Dates

  • Created:
    05/Apr/07 5:26 PM
    Updated:
    30/Apr/07 3:12 AM
    Resolved:
    20/Apr/07 1:19 PM
  • Atlassian JIRA (v5.0.4#731-sha1:3aa7374)
  • Report a problem
  • Powered by a free Atlassian JIRA open source license for Codehaus. Try JIRA - bug tracking software for your team.