Last active
August 29, 2015 14:22
-
-
Save shreeve/bffc05490347e8b62575 to your computer and use it in GitHub Desktop.
binding_of_caller, pure Fiddle version (no compiler needed)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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