jrubyc won't compile a file with method in outer scope (NoMethodError: undefined method `new_method' for nil:NilClass) #3599

Closed
ruskotron opened this Issue Jan 13, 2016 · 11 comments

Comments

Projects
None yet
3 participants
@ruskotron

Looking at the example at https://github.com/jruby/jruby/wiki/JRubyCompiler

This should be possible i.e.

def my_method
  puts 'splat'
end

my_method

results in a error like this from jrubyc:

 $ java -jar tools/jruby-complete-9.0.4.0.jar -S jrubyc --verbose --javac outer.rb
NoMethodError: undefined method `new_method' for nil:NilClass
                           new_method at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler/java_class.rb:115
             block in ClassNodeWalker at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler/java_class.rb:239
 .. and so on ...

This works fine when executed as a script e.g.

 $ java -jar tools/jruby-complete-9.0.4.0.jar  outer.rb
splat

@enebo enebo added this to the JRuby 9.0.5.0 milestone Jan 13, 2016

@enebo enebo added the ir label Jan 13, 2016

@enebo

This comment has been minimized.

Show comment
Hide comment
@enebo

enebo Jan 13, 2016

Member

I just tried this and it works now. We had a serious problem with ir persistence in 9.0.4.0 which has since been fixed. Since I do not see an issue for this problem. I am using yours so we can display something in our release notes. You can try 9.0.5.0-SNAPSHOT and verify all is well here: http://ci.jruby.org/

Member

enebo commented Jan 13, 2016

I just tried this and it works now. We had a serious problem with ir persistence in 9.0.4.0 which has since been fixed. Since I do not see an issue for this problem. I am using yours so we can display something in our release notes. You can try 9.0.5.0-SNAPSHOT and verify all is well here: http://ci.jruby.org/

@enebo enebo closed this Jan 13, 2016

@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 13, 2016

much thanks

much thanks

@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 13, 2016

I'm still getting this same problem with jruby-complete-9.0.5.0-SNAPSHOT.jar ...

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT.jar -S jrubyc --verbose --javac outer.rb
NoMethodError: undefined method `new_method' for nil:NilClass
                       new_method at uri:classloader:/META-INF/jruby.home/li

I'm still getting this same problem with jruby-complete-9.0.5.0-SNAPSHOT.jar ...

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT.jar -S jrubyc --verbose --javac outer.rb
NoMethodError: undefined method `new_method' for nil:NilClass
                       new_method at uri:classloader:/META-INF/jruby.home/li
@enebo

This comment has been minimized.

Show comment
Hide comment
@enebo

enebo Jan 13, 2016

Member

AHA... I was not supplying --javac (but jut generating a .class file directly). I can reproduce this now. Thanks for checking so quickly. Different issue!

Member

enebo commented Jan 13, 2016

AHA... I was not supplying --javac (but jut generating a .class file directly). I can reproduce this now. Thanks for checking so quickly. Different issue!

@enebo enebo reopened this Jan 13, 2016

@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 13, 2016

thanks again :-)

thanks again :-)

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Jan 19, 2016

Member

So I'm confused by this. I can fix the error, so that a file with toplevel method devs and a java_class compiles, but your example has nothing that --java or --javac can generate. And without a java_class in the script, there's no place to put the execution of the script.

I feel like maybe this isn't the right use of the jrubyc java/javac features...

Member

headius commented Jan 19, 2016

So I'm confused by this. I can fix the error, so that a file with toplevel method devs and a java_class compiles, but your example has nothing that --java or --javac can generate. And without a java_class in the script, there's no place to put the execution of the script.

I feel like maybe this isn't the right use of the jrubyc java/javac features...

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Jan 19, 2016

Member

Ok, so here's what I'm going to do.

I'm going to make this work when there's a class present in the target file, because that's the only way this makes sense to me:

[] ~/projects/jruby $ cat outer2.rb 
def foo
  'hello'
end

class Bar
  java_signature 'static void main(String[] args)'
  def self.main(args)
    puts foo
  end
end

[] ~/projects/jruby $ jrubyc --javac outer2.rb

[] ~/projects/jruby $ java -Djruby.home=`pwd` -cp lib/jruby.jar:. Bar
hello

It will error if the file does not contain any classes, since the whole purpose of the --java and --javac flags is to generate Java class stubs based on a Ruby script that contains Ruby class definitions:

[] ~/projects/jruby $ cat outer.rb
def my_method
  puts 'splat'
end

my_method

[] ~/projects/jruby $ jrubyc --java outer.rb
RuntimeError: No classes found in target script: outer.rb
  block in compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:143
  block in compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:294
                                 each at org/jruby/RubyArray.java:1555
           compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:278
                         compile_argv at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:94
                                <top> at /Users/headius/projects/jruby/bin/jrubyc:5

And I will update the --help docs to more clearly indicate that these are specialized flags only for generating Java class stubs based on Ruby class definitions:

[] ~/projects/jruby $ jrubyc --help
jrubyc [options] (FILE|DIRECTORY)

  -d, --dir DIR            Use DIR as the base path
  -p, --prefix PREFIX      Prepend PREFIX to the file path and package. Default is no prefix.
  -t, --target TARGET      Output files to TARGET directory
  -J OPTION                Pass OPTION to javac for javac compiles
  -5                        --jdk5
                           Generate JDK 5 classes (version 49)
      --java               Generate Java classes (.java) for a script containing Ruby class definitions
      --javac              Generate Java classes (.java and .class) for a script containing Ruby class definitions
  -c CLASSPATH,            Add a jar to the classpath for building
      --classpath
      --sha1               Compile to a class named using the SHA1 hash of the source file
      --handles            Also generate all direct handle classes for the source file
      --verbose            Log verbose output while compile
Member

headius commented Jan 19, 2016

Ok, so here's what I'm going to do.

I'm going to make this work when there's a class present in the target file, because that's the only way this makes sense to me:

[] ~/projects/jruby $ cat outer2.rb 
def foo
  'hello'
end

class Bar
  java_signature 'static void main(String[] args)'
  def self.main(args)
    puts foo
  end
end

[] ~/projects/jruby $ jrubyc --javac outer2.rb

[] ~/projects/jruby $ java -Djruby.home=`pwd` -cp lib/jruby.jar:. Bar
hello

It will error if the file does not contain any classes, since the whole purpose of the --java and --javac flags is to generate Java class stubs based on a Ruby script that contains Ruby class definitions:

[] ~/projects/jruby $ cat outer.rb
def my_method
  puts 'splat'
end

my_method

[] ~/projects/jruby $ jrubyc --java outer.rb
RuntimeError: No classes found in target script: outer.rb
  block in compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:143
  block in compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:294
                                 each at org/jruby/RubyArray.java:1555
           compile_files_with_options at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:278
                         compile_argv at /Users/headius/projects/jruby/lib/ruby/stdlib/jruby/compiler.rb:94
                                <top> at /Users/headius/projects/jruby/bin/jrubyc:5

And I will update the --help docs to more clearly indicate that these are specialized flags only for generating Java class stubs based on Ruby class definitions:

[] ~/projects/jruby $ jrubyc --help
jrubyc [options] (FILE|DIRECTORY)

  -d, --dir DIR            Use DIR as the base path
  -p, --prefix PREFIX      Prepend PREFIX to the file path and package. Default is no prefix.
  -t, --target TARGET      Output files to TARGET directory
  -J OPTION                Pass OPTION to javac for javac compiles
  -5                        --jdk5
                           Generate JDK 5 classes (version 49)
      --java               Generate Java classes (.java) for a script containing Ruby class definitions
      --javac              Generate Java classes (.java and .class) for a script containing Ruby class definitions
  -c CLASSPATH,            Add a jar to the classpath for building
      --classpath
      --sha1               Compile to a class named using the SHA1 hash of the source file
      --handles            Also generate all direct handle classes for the source file
      --verbose            Log verbose output while compile

headius added a commit that referenced this issue Jan 19, 2016

Fixes for jrubyc --java(c) for #3599.
* Don't error when target script has root level method defs.
* Do error when target script does not contain class defs.
* Update --help doco to clarify usage.

headius added a commit that referenced this issue Jan 19, 2016

Fixes for jrubyc --java(c) for #3599.
* Don't error when target script has root level method defs.
* Do error when target script does not contain class defs.
* Update --help doco to clarify usage.

@headius headius closed this Jan 19, 2016

@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 20, 2016

I'm sorry, I gave a bad example. I know why that shouldn't work, and thanks for the advice that using java_class would in fact make it work (I'll try that out in a second). My original problem that I thought I had abstracted in the example was where a function in a class was calling a method in outer scope (as in the example on the linked wiki page) and hopefully that will work now. Will let you know when I check the new release.

I'm sorry, I gave a bad example. I know why that shouldn't work, and thanks for the advice that using java_class would in fact make it work (I'll try that out in a second). My original problem that I thought I had abstracted in the example was where a function in a class was calling a method in outer scope (as in the example on the linked wiki page) and hopefully that will work now. Will let you know when I check the new release.

@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 20, 2016

Okay I have verified that the latest version of jruby-complete-9.0.5.0-SNAPSHOT.jar now compiles the script from the examples (https://github.com/jruby/jruby/wiki/JRubyCompiler):

 $ cat outer3.rb
require 'foo'

# normal code in the method body
puts 'here we go'

# upon encountering a method def, a new method is started in the class
def bar
 # this is a simple method body, and would use stack-based vars
 puts 'hello'
end
# once the method has been compiled, binding code is added to __file__

# class definitions become methods as well, building the class
class MyClass
 # this is code in the body of the class
 puts 'here'

 # a method in the class is compiled like any other method body
 def something(a, b = 2, *c, &block)
     # this method has all four param types:
     # normal, optional, "rest" or varargs, and block argument
     # the compiler generates code to assign these from an incoming
     # IRubyObject[]

     # this method has a closure, so it would use heap-based vars
     # ... but the closure would use stack vars, since it's a simple leaf
     1.times { puts 'in closure' }
 end
 # method is completed, bound into the class we're building
end
# end of class definition; __file__ code invokes the class body directly

# any begin block or method body with a rescue/ensure attached will
# be compiled as a synthetic method. This also necessarily means that
# method bodies containing rescue/ensure must be heap-based.
begin
 puts 'rescue me'
rescue
 puts 'rescued!'
ensure
 puts 'ensured!'
end

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar outer3.rb
Generating Java class MyClass to C:/Users/robert.rusk/Documents/cvs/ip_probe/MyClass.java
javac  -d C:/Users/robert.rusk/Documents/cvs/ip_probe -cp /lib/;tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar C:/Users/robert.rusk/Documents/cvs/ip_probe/MyClass.java

Whereas the previous version did not:

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT.jar outer3.rb
NoMethodError: undefined method `new_method' for nil:NilClass
                           new_method at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler/java_class.rb:115
             block in ClassNodeWalker at uri:classloader:/META-INF/jruby.home/li
etc. etc.

Modified "outer2.rb" also works.

Raw "outer.rb" from the original example fails with a sensible error message:

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar outer.rb
RuntimeError: No classes found in target script: outer.rb
  block in compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:143
                                 call at org/jruby/RubyProc.java:318
  block in compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:294
                                 each at org/jruby/RubyArray.java:1560
           compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:278
                         compile_argv at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:94
                                <top> at uri:classloader:/META-INF/jruby.home/bin/jrubyc:5

Okay I have verified that the latest version of jruby-complete-9.0.5.0-SNAPSHOT.jar now compiles the script from the examples (https://github.com/jruby/jruby/wiki/JRubyCompiler):

 $ cat outer3.rb
require 'foo'

# normal code in the method body
puts 'here we go'

# upon encountering a method def, a new method is started in the class
def bar
 # this is a simple method body, and would use stack-based vars
 puts 'hello'
end
# once the method has been compiled, binding code is added to __file__

# class definitions become methods as well, building the class
class MyClass
 # this is code in the body of the class
 puts 'here'

 # a method in the class is compiled like any other method body
 def something(a, b = 2, *c, &block)
     # this method has all four param types:
     # normal, optional, "rest" or varargs, and block argument
     # the compiler generates code to assign these from an incoming
     # IRubyObject[]

     # this method has a closure, so it would use heap-based vars
     # ... but the closure would use stack vars, since it's a simple leaf
     1.times { puts 'in closure' }
 end
 # method is completed, bound into the class we're building
end
# end of class definition; __file__ code invokes the class body directly

# any begin block or method body with a rescue/ensure attached will
# be compiled as a synthetic method. This also necessarily means that
# method bodies containing rescue/ensure must be heap-based.
begin
 puts 'rescue me'
rescue
 puts 'rescued!'
ensure
 puts 'ensured!'
end

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar outer3.rb
Generating Java class MyClass to C:/Users/robert.rusk/Documents/cvs/ip_probe/MyClass.java
javac  -d C:/Users/robert.rusk/Documents/cvs/ip_probe -cp /lib/;tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar C:/Users/robert.rusk/Documents/cvs/ip_probe/MyClass.java

Whereas the previous version did not:

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT.jar outer3.rb
NoMethodError: undefined method `new_method' for nil:NilClass
                           new_method at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler/java_class.rb:115
             block in ClassNodeWalker at uri:classloader:/META-INF/jruby.home/li
etc. etc.

Modified "outer2.rb" also works.

Raw "outer.rb" from the original example fails with a sensible error message:

 $ java -jar tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar -S jrubyc --verbose --javac -c tools/jruby-complete-9.0.5.0-SNAPSHOT-2.jar outer.rb
RuntimeError: No classes found in target script: outer.rb
  block in compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:143
                                 call at org/jruby/RubyProc.java:318
  block in compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:294
                                 each at org/jruby/RubyArray.java:1560
           compile_files_with_options at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:278
                         compile_argv at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/jruby/compiler.rb:94
                                <top> at uri:classloader:/META-INF/jruby.home/bin/jrubyc:5
@ruskotron

This comment has been minimized.

Show comment
Hide comment
@ruskotron

ruskotron Jan 20, 2016

@headius I have one final question, you mentioned in your comment above that

without a java_class in the script, there's no place to put the execution of the script

I can't find any documentation for java_class in the wiki or elsewhere. Have I got the wrong end of this? I've tried adding java_class "Splat" to outer.rb but this obviously doesn't work :)

@headius I have one final question, you mentioned in your comment above that

without a java_class in the script, there's no place to put the execution of the script

I can't find any documentation for java_class in the wiki or elsewhere. Have I got the wrong end of this? I've tried adding java_class "Splat" to outer.rb but this obviously doesn't work :)

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Feb 14, 2016

Member

@ruskotron Oh, I meant without there being some class to emit, there's not really any output from the script.

Member

headius commented Feb 14, 2016

@ruskotron Oh, I meant without there being some class to emit, there's not really any output from the script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment