/reversal

Decompiler for YARV bytecode.

Primary LanguageRubyMIT LicenseMIT

reversal

Best result so far:

This is the best test so far for a complicated decompilation. Reversal turns this:

module Hello
  include SomeModule
  class World < Universe
    attr_accessor :name
    self.attr_writer :moons
    def initialize(some_thing, silly_arg = some_thing.to_s, *args)
      super
      @name = some_thing.to_s
    end

    def orbit!(optional_arg = 10, required_arg, &blk)
      @moons.each do |moon|
        if moon.spherical(:not_cube => true)
          moon.rotate
        else
          moon.cubify
        end
        moon.revolve! :twice
        class << moon
          def crash_into(other_planet)
            other_planet.go_boom!
          end
        end
        yield
      end
    end
  end
end

into this (indentation is generated by the gem):

module Hello
  include(SomeModule)
  class World < Universe
    attr_accessor(:name)
    self.attr_writer(:moons)
    def initialize(some_thing, silly_arg = some_thing.to_s, *args)
      super
      @name = some_thing.to_s
    end
    def orbit!(optional_arg = 10, required_arg, &blk)
      @moons.each do |moon|
        if moon.spherical({:not_cube => true})
          moon.rotate
        else
          moon.cubify
        end
        moon.revolve!(:twice)
        class << moon
          def crash_into(other_planet)
            other_planet.go_boom!
          end
        end
        yield
      end
    end
  end
end

You will need a tiny patch to your interpreter for this to work. I will be petitioning for this to be added to Ruby 1.9.2. The patch should be applied to iseq.c.

Index: iseq.c
===================================================================
--- iseq.c	(revision 26813)
+++ iseq.c	(working copy)
@@ -1004,6 +1004,31 @@
 }
 
 static VALUE
+iseq_s_get_from_method(VALUE klass, VALUE body)
+{
+    VALUE ret = Qnil;
+    rb_iseq_t *iseq;
+    extern rb_iseq_t *rb_method_get_iseq(VALUE body);
+
+    rb_secure(1);
+
+    if (rb_obj_is_proc(body)) {
+	rb_proc_t *proc;
+	VALUE iseqval;
+	GetProcPtr(body, proc);
+	iseqval = proc->block.iseq->self;
+	if (RUBY_VM_NORMAL_ISEQ_P(iseqval)) {
+        ret = iseqval;
+	}
+    }
+    else if ((iseq = rb_method_get_iseq(body)) != 0) {
+	ret = iseq->self;
+    }
+
+    return ret;
+}
+
+static VALUE
 iseq_s_disasm(VALUE klass, VALUE body)
 {
     VALUE ret = Qnil;
@@ -1498,5 +1523,6 @@
     rb_define_singleton_method(rb_cISeq, "compile_option=", iseq_s_compile_option_set, 1);
     rb_define_singleton_method(rb_cISeq, "disasm", iseq_s_disasm, 1);
     rb_define_singleton_method(rb_cISeq, "disassemble", iseq_s_disasm, 1);
+    rb_define_singleton_method(rb_cISeq, "from_method", iseq_s_get_from_method, 1);
 }

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright (c) 2010 Michael Edgar. See LICENSE for details.