Details
-
Type:
Wish
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: JRuby 1.1.2
-
Fix Version/s: JRuby 1.7.0.pre1
-
Component/s: Interpreter
-
Labels:None
Description
Occasionally, when loading classes that have been compiled ahead-of-time, references such as require './foo.rb' is encountered by the runtime. The runtime is already smart enough to also look for foo.class when loading 'foo.rb'. It would be nice if it will also automatically consider foo.class if "./foo.rb" or ".\foo.rb" is requested.
-
- jruby_load_class_without_relative_prefix.patch
- 02/Jul/08 4:34 PM
- 0.9 kB
- Peter K Chan
-
- test_jruby_load_relative_path_from_jar_2.rb
- 16/Jul/08 12:18 PM
- 0.6 kB
- Peter K Chan
-
- test_jruby_load_relative_path_from_jar.rb
- 03/Jul/08 11:20 AM
- 0.7 kB
- Peter K Chan
Activity
The attached patch will try loading class "foo/bar" when asked to load "./foo/bar".
Ok, I'm a little confused. Can you provide a test case for this or something that shows what you need it to do? Removing the relative path seems like an odd thing to do, if not a dangerous one, since it would then start trying to load things from elsewhere in the load path. If this is intended to just have "load" gain the behavior to look for .class as well as .rb, I can handle that. Test case please?
Here is an example from an older version of Active Support that I still use:
On the very last line of active_support/inflector.rb, there is a line that uses the _FILE_ reference:
require File.dirname(__FILE__) + '/inflections'
Which is translated to "./active_support/inflections" at run time. However, if /active_support/inflections.rb has been pre-compiled as a Java class, then you have to load it with the name "active_support/inflections" and not "./active_support/inflections" Otherwise, even if active_support/inflections.class is available, the following error result:
no such file to load -- ./active_support/inflections
The attached patch only changes the very specific case of locating a Java class, and it does not affect other cases, such as loading .rb files or any other such ways.
require File.dirname(__FILE__) + '/inflections'
no such file to load -- ./active_support/inflections
Hmm, are you sure this isn't working? Can you provide a reproducible case that doesn't depend on Rails? For example, I tried precompiling optparse into my current dir, and then renamed the .class file to make sure it wasn't loading optparse.rb from anywhere. This works:
~/NetBeansProjects/jruby ➔ ls -l optparse2.class -rw-r--r-- 1 headius headius 161848 Jul 3 09:30 optparse2.class ~/NetBeansProjects/jruby ➔ jruby -e "require './optparse2'; p OptionParser" OptionParser
What's more, even if it's in a subdir it seems to work ok. What am I doing differently than your app?
~/NetBeansProjects/jruby ➔ mv optparse2.class tmp ~/NetBeansProjects/jruby ➔ jruby -e "require './tmp/optparse2'; p OptionParser" OptionParser
~/NetBeansProjects/jruby ➔ ls -l optparse2.class -rw-r--r-- 1 headius headius 161848 Jul 3 09:30 optparse2.class ~/NetBeansProjects/jruby ➔ jruby -e "require './optparse2'; p OptionParser" OptionParser
~/NetBeansProjects/jruby ➔ mv optparse2.class tmp ~/NetBeansProjects/jruby ➔ jruby -e "require './tmp/optparse2'; p OptionParser" OptionParser
It looks like the problem only occurs in the specific case of when you load AOT compiled classes from a JAR.
I have a half working test script attached. I am in a hurry to step out now, so I don't have time to debug it, but give it a try (it acts weird on Windows, but may actually runs fine on Linux). Even if it doesn't work, you should be able to reproduce the problem manually by emulating what the script does.
Ok, I was able to confirm the problem:
~/NetBeansProjects/jruby ➔ cat test.rb require './foo.rb' ~/NetBeansProjects/jruby ➔ cat foo.rb puts 'here' ~/NetBeansProjects/jruby ➔ jruby test.rb here ~/NetBeansProjects/jruby ➔ jrubyc test.rb foo.rb Compiling test.rb to class test Compiling foo.rb to class foo ~/NetBeansProjects/jruby ➔ jar cf blah.jar foo.class test.class ~/NetBeansProjects/jruby ➔ java -cp blah.jar:lib/jruby.jar test here ~/NetBeansProjects/jruby ➔ rm foo.rb foo.class ~/NetBeansProjects/jruby ➔ java -cp blah.jar:lib/jruby.jar test Exception in thread "main" test.rb:1:in `require': no such file to load -- ./foo (LoadError) from test.rb:1 ...internal jruby stack elided...
Refreshing my memory about the patch, but I'll probably go ahead and apply it.
~/NetBeansProjects/jruby ➔ cat test.rb require './foo.rb' ~/NetBeansProjects/jruby ➔ cat foo.rb puts 'here' ~/NetBeansProjects/jruby ➔ jruby test.rb here ~/NetBeansProjects/jruby ➔ jrubyc test.rb foo.rb Compiling test.rb to class test Compiling foo.rb to class foo ~/NetBeansProjects/jruby ➔ jar cf blah.jar foo.class test.class ~/NetBeansProjects/jruby ➔ java -cp blah.jar:lib/jruby.jar test here ~/NetBeansProjects/jruby ➔ rm foo.rb foo.class ~/NetBeansProjects/jruby ➔ java -cp blah.jar:lib/jruby.jar test Exception in thread "main" test.rb:1:in `require': no such file to load -- ./foo (LoadError) from test.rb:1 ...internal jruby stack elided...
Ok, I've gone ahead and applied the fix, because I was able to reproduce the problem and see that it fixed it, and it didn't appear to break anything with normal filesystem-based requiring of .class files. No problem. But the test you provided didn't seem to pass, or at least didn't seem to test what I fixed. Can you try to clean it up for 1.1.3 and we'll mark this as resolved? Or if there's still an issue on your end, let me know?
I can verify that the relative reference is now automatically handled when trying to load for class. I no longer have to hook the require statement to strip the initial "./"
I have worked on a simpler test script, but I am having a hard time polishing it up (I want to delete the JAR for test teardown, but I can't delete it, if it is loaded into the JVM by JRuby for AOT loading). I will try, I may not be able to get the test case in in time for 1.1.3.
Looks like in my effort to write the test script, I have uncovered a bug/incompleteness in the handling of relative path loading. The attached script fails when the ruby class is loaded dynamically using require('test.jar'), as opposed to when test.jar was included on the JVM classpath at launch.
The patch that I previously submitted only deals with classloading with compiled ruby files that are already in the JVM initial classpath, but not when the JAR is later added to the loadpath.
Charles, I probably won't have time to come up with a new patch in time for JRuby 1.1.3 release. Unless you see a quick way to patch for the other use case, feel free to postpone this bug for after 1.1.3.
Ok Peter, thanks for responding. I'll leave your fix in place for this for 1.1.3, since it doesn't hurt anything and it does fix at least one instance of loading from a jar file. We'll bump the remaining work off 1.1.3 and continue to find and fix cases, for complete resolution as part of a future release.
Peter: Can you give this another try with current JRuby? We've done a couple passes at making require logic more logical, and there's a strong chance we've got all cases covered now.
I am out of town right now. I will look at this before the end of this week.
I have verified that JRuby 1.2.0 trunk fails the test to require "./foo.rb" when loading from a JAR file.
IRB transcript below, where tmp_test.jar contains a 'foo.class' on the top level:
c:\work>c:\tmp\jruby\bin\jruby c:\tmp\jruby\bin\jirb
irb(main):001:0> require 'tmp_test.jar'
=> true
irb(main):002:0> require './foo.rb'
LoadError: no such file to load – ./foo
from (irb):3:in `require'
from (irb):3
irb(main):003:0> require 'foo.rb'
Foo loading...
=> true
Note how the "foo.rb" reference works, but "./foo.rb" does not.
This failure only pertains to loading "./foo.rb" from a packaged JAR file. If foo.class lies on the file system without any package, then require "./foo.rb" also works.
This fails because of a call to ClassLoader.getResource() with the resource "././foo.rb". This occurs because the load path being tested is ".", and it has a slash appended to it.
Actually the previous comment is a complete lie. The answer lies in this comment at the end of findFileInClassPath():
// Try to load from classpath without prefix. "A/b.rb" will not load as "./A/b.rb" in a jar file.
Assuming that comment's correct, the only solution I can see is stripping off the ./ prefix.
Hmmm, sounds like there's a patch in here somewhere. Someone want to try fixing it?
Charlie,
After 2.5 years, this issue seems to be fixed. Do you agree?
$ java -cp blah.jar:../lib/jruby.jar test here
$ java -cp blah.jar:../lib/jruby.jar test here
Sorry, the name of this issue is incomplete. It should be "JRuby should load relative path reference to AOT classes"