Skip to content

Instantly share code, notes, and snippets.

@shreeve
Last active August 29, 2015 14:22
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 shreeve/bffc05490347e8b62575 to your computer and use it in GitHub Desktop.
Save shreeve/bffc05490347e8b62575 to your computer and use it in GitHub Desktop.
binding_of_caller, pure Fiddle version (no compiler needed)
#!/usr/bin/env ruby
# =============================================================================
# UPDATE as of June 15, 2015: See the bindings gem at:
#
# https://rubygems.org/gems/bindings
# =============================================================================
# =============================================================================
# Quick proof-of-concept of an Fiddle-based binding_of_caller (no compiling!)
#
# Author: Steve Shreeve <steve.shreeve@gmail.com>
# Basis: binding_of_caller and ffi gems, along with the standard ruby fiddle source code
# Date: 29 May 2015
#
# Legal: MIT License
# =============================================================================
# NOTE: Fiddle requires a dynamic library, it cannot be a static library
#
# To install a shared libruby using ruby-install, use this:
#
# ruby-install ruby 2.2.2 -- --enable-shared --disable-install-doc
#
# To nuke everything if you're using ruby-install, use this:
#
# rm -rf ~/.gem/ruby/2.2.2 ~/.rubies/ruby-2.2.2 ~/src/ruby-2.2.2
require 'fiddle/import'
module Fiddle
module Binding
extend Fiddle::Importer
dlload Fiddle.dlopen(nil)
extern "void* rb_debug_inspector_open(void*, void*)"
bind("void* callback(void*, void*)") {|ptr, _| DebugStruct.new(ptr).contexts }
DebugStruct = struct [
"void* thread",
"void* frame",
"void* backtrace",
"void* contexts",
"long backtrace_size"
]
class << self
def callers
list = rb_debug_inspector_open(self['callback'], nil).to_value
list.drop(4).map {|ary| ary[2]} # grab proper bindings
end
end
end
end
class ::Binding
def of_caller(n)
c = ::Fiddle::Binding.callers
n < c.size or raise "No such frame, gone beyond end of stack!"
c[n]
end
end
# ==[ Show that it works... ]==
outer = 10
class Z
def z
u = 10
A.new.a
end
end
class A
def a
y = 10
B.new.b
end
end
class B
def b
x = 10
puts binding.of_caller(0).eval('local_variables')
puts binding.of_caller(1).eval('local_variables')
puts binding.of_caller(2).eval('local_variables')
puts binding.of_caller(3).eval('local_variables')
# puts binding.of_caller(400).eval('local_variables') # should throw an Exception
end
end
Z.new.z
__END__
# output:
# => x
# => y
# => u
# => outer
# Exception
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment