Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

return problem when decompiling jruby class

jode-users
2009-11-30
2013-05-02
  • Eric Caspole
    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