Skip to content

Instantly share code, notes, and snippets.

@rocky
Created August 28, 2010 19:14
Show Gist options
  • Save rocky/555467 to your computer and use it in GitHub Desktop.
Save rocky/555467 to your computer and use it in GitHub Desktop.
# How to use.
# $ irb
# >> load 'irb-hack-no-tf.rb' # Substitute file path. NOTE: 'load', not 'require'
# >> # work, work, work... look at CLASSES, METHODS and MODULES
require 'irb'
$IRBHACK_DEBUG = true
# irb-hack.rb without threadframe and trace modules. However since
# there is still a bug in the way Ruby handles c-call events (it calls the
# hook before pushing the the frame), this doesn't catch "define-method"
# calls properly.
CLASSES = {}
METHODS = {}
MODULES = {}
CHECK_METHODS = %w(define_method method_added singleton_method_added CLASS)
def capture_hook(event, file, line, id, binding, classname)
return unless CHECK_METHODS.member?(id.to_s)
# Work around what is probably a bug in the way classname
# is set inside the Ruby 1.9 callback hook.
klass = eval('self.to_s', binding)
p [event, file, id, klass, classname] if $IRBHACK_DEBUG
lines = $irb_stmts.split(/\n/)
return unless lines.size + $irb_firstline >= line
loc = line
first_line = lines[loc - $irb_firstline]
puts "checking #{first_line}..." if $IRBHACK_DEBUG
if 'class' == event
if first_line =~ /^\s*class\s+(\S+)/
puts "adding #{klass}" if $IRBHACK_DEBUG
CLASSES[klass] = $irb_stmts
return
end
elsif first_line =~ /^\s*def\s+([^(; \t\n]+)(:?[ \t\n(;])?/
puts "adding #{klass}::#{$1}" if $IRBHACK_DEBUG
METHODS["#{klass}::#{$1}"] = $irb_stmts
end
MODULES[$1] = $irb_stmts if $irb_stmts =~ /^\s*module\s+(\S+)\s+/
end
# Monkeypatch to save the current IRB statement to be run.
# Possibly not needed.
class IRB::Context
alias original_evaluate evaluate
def evaluate(line, line_no)
$irb_stmts = line
$irb_firstline = line_no
original_evaluate(line, line_no)
end
end
workspace = IRB::WorkSpace.new(binding)
irb = IRB::Irb.new(workspace)
set_trace_func(method(:capture_hook).to_proc)
irb.eval_input
# How to use.
# $ irb
# >> load 'irb-hack.rb' # Substitute file path. NOTE: 'load', not 'require
# >> # work, work, work... look at CLASSES, METHODS and MODULES
require 'irb'
require 'trace'
include Trace
CLASSES = {}
METHODS = {}
MODULES = {}
CHECK_METHODS = %w(define_method method_added singleton_method_added CLASS)
def capture_hook(event, frame, arg=nil)
return unless CHECK_METHODS.member?(frame.method)
klass = eval('self.to_s', frame.binding)
lines = $irb_stmts.split(/\n/)
return unless frame.source_container[0] == 'string' &&
lines.size + $irb_firstline >= frame.source_location[0]
loc = frame.source_location[0]
first_line = lines[loc - $irb_firstline]
puts "checking #{first_line}..." if $IRBHACK_DEBUG
if 'class' == event
if first_line =~ /^\s*class\s+(\S+)/
puts "adding #{klass}" if $IRBHACK_DEBUG
CLASSES[klass] = $irb_stmts
return
end
elsif first_line =~ /^\s*def\s+([^(; \t\n]+)(:?[ \t\n(;])?/
puts "adding #{klass}::#{$1}" if $IRBHACK_DEBUG
METHODS["#{klass}::#{$1}"] = $irb_stmts
end
MODULES[$1] = $irb_stmts if $irb_stmts =~ /^\s*module\s+(\S+)\s+/
end
# Monkeypatch to save the current IRB statement to be run.
# Possibly not needed.
class IRB::Context
alias original_evaluate evaluate
def evaluate(line, line_no)
$irb_stmts = line
$irb_firstline = line_no
original_evaluate(line, line_no)
end
end
workspace = IRB::WorkSpace.new(binding)
irb = IRB::Irb.new(workspace)
trace_filter = Trace::Filter.new
trace_filter.add_trace_func(method(:capture_hook).to_proc,
C_CALL_EVENT_MASK | CLASS_EVENT_MASK)
irb.eval_input
@rocky
Copy link
Author

rocky commented Apr 23, 2012

I think so for MRI YARV. rubinius doesn't implement trace hooks but JRuby does. So why not try it and get back to me in a year or so if it doesn't work?

@nusco
Copy link

nusco commented Apr 23, 2012

Will do! This should take even less than a year.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment