Skip to content

Instantly share code, notes, and snippets.

@benolee
Last active April 13, 2023 23:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benolee/9d2b80b488464a900d6d92b34f518d76 to your computer and use it in GitHub Desktop.
Save benolee/9d2b80b488464a900d6d92b34f518d76 to your computer and use it in GitHub Desktop.

What is this?

This is a ruby C extension that defines a global method named current_iseq. It returns the instruction sequence of the context it was called from.

I couldn't find any other way to get a reference to the method being called from within the method itself. The closest you can get is probably __callee__ which just gives you the name of the method. There's no guarantee that getting a method object using something like method(__callee__) will actually give you the method it's being called from.

For example, suppose I want to get a reference to a method that's temporarily bound to another object:

# Note: I do not actually care about or want a reference to C#foo()
class C
  def foo
    :original
  end
end

# Note: I DO want to get a reference to M#foo() from within the method itself.
# I will attempt to use `method(__callee__)` which is the same as using
# `method(:foo)` in this case.
module M
  def foo
    method(__callee__)
  end
end

But what if I only temporarily bind M#foo() to an instance of C?

method_object = M.instance_method(:foo).bind(C.new)
method_object.call
#=> #<Method: C#foo() foo.rb:2>

It gives me C#foo()! That's not what I wanted :(

Now I can do this instead and at least get a reference to the instruction sequence of M#foo()

module M
  def foo
    current_iseq
  end
end

puts M.instance_method(:foo).bind(C.new).call.disasm

Output:

== disasm: #<ISeq:foo@foo.rb:10 (10,2)-(12,5)> (catch: FALSE)
0000 putself                                                          (  11)[LiCa]
0001 opt_send_without_block                 <calldata!mid:current_iseq, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0003 leave                                                            (  12)[Re]

How do I use it?

For now this is just a quick test. You'll have to actually generate the makefile and run make.

$ ruby extconf.rb && make
creating Makefile
linking shared-object current_iseq.bundle

Then require 'current_iseq' from that directory.

#include "ruby/ruby.h"
#include "ruby/debug.h"
static VALUE
callback(const rb_debug_inspector_t *dbg_context, void *data)
{
return rb_debug_inspector_frame_iseq_get(dbg_context, 1);
}
static VALUE
current_iseq(VALUE self)
{
return rb_debug_inspector_open(callback, NULL);
}
void
Init_current_iseq(void)
{
rb_define_global_function("current_iseq", current_iseq, 0);
}
require './current_iseq'
def some_method
current_iseq
end
p some_method
require 'mkmf'
create_makefile 'current_iseq'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment