Eric Caspole - 2009-11-30

Hello jode users,
I don't know if anyone still reads this, but perhaps someone more familiar with the code could comment. I am working with JRuby 1.4.0 and wished to decompile a simple ruby script that I had compiled into a .class file by jrubyc:

    % more simple.rb
   
    begin
   
        puts "hello jode"
   
    end

Jruby adds all the stuff to boot the ruby runtime into the class as shown in the javap:

    % javap -c  -classpath . simple
    Compiled from "simple.rb"
    public class simple extends org.jruby.ast.executable.AbstractScript{
    public simple();
      Code:
       0:   aload_0
       1:   invokespecial   #18; //Method org/jruby/ast/executable/AbstractScript."<init>":()V
       4:   aload_0
       5:   ldc     #21; //String simple
       7:   invokestatic    #27; //Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
       10:  putfield        #29; //Field $class:Ljava/lang/Class;
       13:  aload_0
       14:  ldc     #8; //String simple.rb
       16:  putfield        #33; //Field filename:Ljava/lang/String;
       19:  aload_0
       20:  new     #125; //class org/jruby/ast/executable/AbstractScript$RuntimeCache
       23:  dup
       24:  invokespecial   #126; //Method org/jruby/ast/executable/AbstractScript$RuntimeCache."<init>":()V
       27:  putfield        #130; //Field org/jruby/ast/executable/AbstractScript.runtimeCache:Lorg/jruby/ast/executable/AbstractScript$RuntimeCache;
       30:  aload_0
       31:  getfield        #130; //Field org/jruby/ast/executable/AbstractScript.runtimeCache:Lorg/jruby/ast/executable/AbstractScript$RuntimeCache;
       34:  dup
       35:  iconst_1
       36:  invokevirtual   #134; //Method org/jruby/ast/executable/AbstractScript$RuntimeCache.initCallSites:(I)V
       39:  getfield        #138; //Field org/jruby/ast/executable/AbstractScript$RuntimeCache.callSites:[Lorg/jruby/runtime/CallSite;
       42:  iconst_0
       43:  ldc     #140; //String puts
       45:  invokestatic    #144; //Method setFunctionalCallSite:([Lorg/jruby/runtime/CallSite;ILjava/lang/String;)[Lorg/jruby/runtime/CallSite;
       48:  pop
       49:  aload_0
       50:  getfield        #130; //Field org/jruby/ast/executable/AbstractScript.runtimeCache:Lorg/jruby/ast/executable/AbstractScript$RuntimeCache;
       53:  iconst_1
       54:  invokevirtual   #148; //Method org/jruby/ast/executable/AbstractScript$RuntimeCache.initStrings:(I)[Lorg/jruby/util/ByteList;
       57:  ldc     #70; //int 0
       59:  ldc     #150; //String hello jode
       61:  invokestatic    #154; //Method org/jruby/ast/executable/AbstractScript.createByteList:([Lorg/jruby/util/ByteList;ILjava/lang/String;)[Lorg/jruby/util/ByteList;
       64:  pop
       65:  return
   
    public static org.jruby.runtime.builtin.IRubyObject __file__(simple, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.Block);
      Code:
       0:   aload_1
       1:   invokevirtual   #39; //Method org/jruby/runtime/ThreadContext.getRuntime:()Lorg/jruby/Ruby;
       4:   astore  6
       6:   aload   6
       8:   invokevirtual   #45; //Method org/jruby/Ruby.getNil:()Lorg/jruby/runtime/builtin/IRubyObject;
       11:  astore  8
       13:  aload_1
       14:  iconst_2
       15:  invokestatic    #50; //Method setPosition:(Lorg/jruby/runtime/ThreadContext;I)V
       18:  aload_1
       19:  iconst_4
       20:  invokestatic    #50; //Method setPosition:(Lorg/jruby/runtime/ThreadContext;I)V
       23:  aload_0
       24:  invokevirtual   #54; //Method getCallSite0:()Lorg/jruby/runtime/CallSite;
       27:  aload_1
       28:  aload_2
       29:  aload_2
       30:  aload_0
       31:  aload   6
       33:  invokevirtual   #58; //Method getString0:(Lorg/jruby/Ruby;)Lorg/jruby/RubyString;
       36:  invokevirtual   #64; //Method org/jruby/runtime/CallSite.call:(Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject;
       39:  areturn
   
    public org.jruby.runtime.builtin.IRubyObject __file__(org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.Block);
      Code:
       0:   aload_0
       1:   aload_1
       2:   aload_2
       3:   aload_3
       4:   aload   4
       6:   invokestatic    #48; //Method __file__:(Lsimple;Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;[Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/Block;)Lorg/jruby/runtime/builtin/IRubyObject;
       9:   areturn
   
    public org.jruby.runtime.builtin.IRubyObject load(org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.Block);
      Code:
       0:   aload_1
       1:   iconst_0
       2:   anewarray       #76; //class java/lang/String
       5:   invokestatic    #82; //Method org/jruby/javasupport/util/RuntimeHelpers.preLoad:(Lorg/jruby/runtime/ThreadContext;[Ljava/lang/String;)V
       8:   aload_0
       9:   aload_1
       10:  aload_2
       11:  aload_3
       12:  aload   4
       14:  invokestatic    #48; //Method __file__:(Lsimple;Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;[Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/Block;)Lorg/jruby/runtime/builtin/IRubyObject;
       17:  aload_1
       18:  invokestatic    #86; //Method org/jruby/javasupport/util/RuntimeHelpers.postLoad:(Lorg/jruby/runtime/ThreadContext;)V
       21:  areturn
       22:  aload_1
       23:  invokestatic    #86; //Method org/jruby/javasupport/util/RuntimeHelpers.postLoad:(Lorg/jruby/runtime/ThreadContext;)V
       26:  athrow
      Exception table:
       from   to  target type
         0    22    22   any
   
    public static void main(java.lang.String);
      Code:
       0:   new     #2; //class simple
       3:   dup
       4:   invokespecial   #97; //Method "<init>":()V
       7:   new     #99; //class org/jruby/RubyInstanceConfig
       10:  dup
       11:  invokespecial   #100; //Method org/jruby/RubyInstanceConfig."<init>":()V
       14:  dup
       15:  aload_0
       16:  invokevirtual   #103; //Method org/jruby/RubyInstanceConfig.setArgv:([Ljava/lang/String;)V
       19:  invokestatic    #107; //Method org/jruby/Ruby.newInstance:(Lorg/jruby/RubyInstanceConfig;)Lorg/jruby/Ruby;
       22:  dup
       23:  invokevirtual   #111; //Method org/jruby/Ruby.getCurrentContext:()Lorg/jruby/runtime/ThreadContext;
       26:  swap
       27:  invokevirtual   #114; //Method org/jruby/Ruby.getTopSelf:()Lorg/jruby/runtime/builtin/IRubyObject;
       30:  getstatic       #117; //Field org/jruby/runtime/builtin/IRubyObject.NULL_ARRAY:[Lorg/jruby/runtime/builtin/IRubyObject;
       33:  getstatic       #121; //Field org/jruby/runtime/Block.NULL_BLOCK:Lorg/jruby/runtime/Block;
       36:  invokevirtual   #123; //Method load:(Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;[Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/Block;)Lorg/jruby/runtime/builtin/IRubyObject;
       39:  return
   
    }

I got the jode svn trunk branch and tried to decompile simple.class and got the error shown below, it was failing on the return bytecode at the end of main():

    % java -cp /junk/tmp/jode/jode/trunk/jode/release/jode-1.90-CVS/jode.jar:/junk/tmp/jode/jode/trunk/jode/release/jode-1.90-CVS/java-getopt-1.0.8.jar net.sf.jode.decompiler.Main -classpath .:/junk/jruby-1.4.0/lib/jruby.jar  simple
    Jode (C) 1998-2004 Jochen Hoenicke <jochen@gnu.org>
    simple
    Warning: Can't get public information of java.lang.Object to detect name conflicts.
    java.io.FileNotFoundException: java.lang.Object
    Can't get full class hierarchy for java.lang.Class types may be incorrect.
    java.io.FileNotFoundException: java.lang.Class
    Can't get full class hierarchy for java.lang.String types may be incorrect.
    java.io.FileNotFoundException: java.lang.String
    Can't get full class hierarchy for java.lang.Cloneable types may be incorrect.
    java.io.FileNotFoundException: java.lang.Cloneable
    Can't get full class hierarchy for java.io.Serializable types may be incorrect.
    java.io.FileNotFoundException: java.io.Serializable
    Can't get full class hierarchy for java.lang.Comparable types may be incorrect.
    java.io.FileNotFoundException: java.lang.Comparable
    Internal error whilst decompiling simple.
    java.lang.InternalError: opc_return no longer allowed
            at net.sf.jode.decompiler.Opcodes.addOpcode(Opcodes.java:349)
            at net.sf.jode.decompiler.MethodAnalyzer.analyzeCode(MethodAnalyzer.java:489)
            at net.sf.jode.decompiler.MethodAnalyzer.analyze(MethodAnalyzer.java:611)
            at net.sf.jode.decompiler.ClassAnalyzer.analyze(ClassAnalyzer.java:390)
            at net.sf.jode.decompiler.ClassAnalyzer.dumpJavaFile(ClassAnalyzer.java:665)
            at net.sf.jode.decompiler.ClassAnalyzer.dumpJavaFile(ClassAnalyzer.java:654)
            at net.sf.jode.decompiler.Main.decompileClass(Main.java:169)
            at net.sf.jode.decompiler.Main.decompile(Main.java:426)
            at net.sf.jode.decompiler.Main.main(Main.java:220)

After looking at the code, and comparing the trunk code to older tags, I made a slight change to try to avoid choking on the return:

    % svn diff
    Index: src/net/sf/jode/decompiler/Opcodes.java
    ===================================================================
    -- src/net/sf/jode/decompiler/Opcodes.java     (revision 1409)
    +++ src/net/sf/jode/decompiler/Opcodes.java     (working copy)
    @@ -346,7 +346,9 @@
                break;
             }
             case opc_return:
    -           throw new InternalError("opc_return no longer allowed");
    +           //throw new InternalError("opc_return no longer allowed");
    +            flow.appendBlock(new ReturnBlock( null));
    +            break;
    
             case opc_getstatic:
             case opc_getfield: {

Now I can decompile the jruby class main() with a "return;" at the end:

    % java -cp /junk/views/jode/jode/trunk/jode/release/jode-1.90-CVS/jode.jar:/junk/views/jode/jode/trunk/jode/release/jode-1.90-CVS/java-getopt-1.0.8.jar net.sf.jode.decompiler.Main -classpath .:/junk/jruby-1.4.0/lib/jruby.jar  simple
    Jode (C) 1998-2004 Jochen Hoenicke <jochen@gnu.org>
    simple
    Warning: Can't get public information of java.lang.Object to detect name conflicts.
    java.io.FileNotFoundException: java.lang.Object
    Can't get full class hierarchy for java.lang.Class types may be incorrect.
    java.io.FileNotFoundException: java.lang.Class
    Can't get full class hierarchy for java.lang.String types may be incorrect.
    java.io.FileNotFoundException: java.lang.String
    Can't get full class hierarchy for java.lang.Cloneable types may be incorrect.
    java.io.FileNotFoundException: java.lang.Cloneable
    Can't get full class hierarchy for java.io.Serializable types may be incorrect.
    java.io.FileNotFoundException: java.io.Serializable
    Can't get full class hierarchy for java.lang.Comparable types may be incorrect.
    java.io.FileNotFoundException: java.lang.Comparable
    /* simple - Decompiled by JODE
     * Visit http://jode.sourceforge.net/
     */
    import org.jruby.Ruby;
    import org.jruby.RubyInstanceConfig;
    import org.jruby.ast.executable.AbstractScript;
    import org.jruby.javasupport.util.RuntimeHelpers;
    import org.jruby.runtime.Block;
    import org.jruby.runtime.ThreadContext;
    import org.jruby.runtime.builtin.IRubyObject;
   
    public class simple extends AbstractScript
    {
        private final Class $class = Class.forName("simple");
       
        /*synthetic*/ private static void setPosition(ThreadContext threadcontext,
                                                      int i) {
            threadcontext.setFileAndLine("simple.rb", i);
        }
       
        public simple() {
            filename = "simple.rb";
            runtimeCache = new AbstractScript.RuntimeCache();
            AbstractScript.RuntimeCache runtimecache = runtimeCache;
            runtimecache.initCallSites(1);
            setFunctionalCallSite(runtimecache.callSites, 0, "puts");
            AbstractScript.createByteList(runtimeCache.initStrings(1), 0,
                                          "hello jode");
        }
       
        public static IRubyObject __file__(simple var_simple,
                                           ThreadContext threadcontext,
                                           IRubyObject irubyobject,
                                           IRubyObject irubyobjects,
                                           Block block) {
            Ruby ruby = threadcontext.getRuntime();
            IRubyObject irubyobject_0_ = ruby.getNil();
            setPosition(threadcontext, 2);
            setPosition(threadcontext, 4);
            return var_simple.getCallSite0().call(threadcontext, irubyobject,
                                                  irubyobject,
                                                  var_simple.getString0(ruby));
        }
       
        public IRubyObject __file__(ThreadContext threadcontext,
                                    IRubyObject irubyobject,
                                    IRubyObject irubyobjects, Block block) {
            return __file__(this, threadcontext, irubyobject, irubyobjects, block);
        }
       
        public IRubyObject load(ThreadContext threadcontext,
                                IRubyObject irubyobject,
                                IRubyObject irubyobjects, Block block) {
            try {
                RuntimeHelpers.preLoad(threadcontext, new String);
                IRubyObject irubyobject_1_
                    = __file__(this, threadcontext, irubyobject, irubyobjects,
                               block);
                RuntimeHelpers.postLoad(threadcontext);
                return irubyobject_1_;
            } catch (Object object) {
                RuntimeHelpers.postLoad(threadcontext);
                throw object;
            }
        }
       
        public static void main(String strings) {
            simple var_simple = new simple();
            RubyInstanceConfig rubyinstanceconfig = new RubyInstanceConfig();
            rubyinstanceconfig.setArgv(strings);
            Ruby ruby = Ruby.newInstance(rubyinstanceconfig);
            ThreadContext threadcontext = ruby.getCurrentContext();
            IRubyObject irubyobject
                = var_simple.load(threadcontext, ruby.getTopSelf(),
                                  IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
            return;
        }
    }

I don't know if this is the right fix, but there it is if anyone else sees this problem. It is working for me.
Regards,
Eric