Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
#!ruby
if false
class C
def m
xxxx
end
end
def mm
yyyy
end
class << self
private
def mmm
zzzz
end
end
end
def_methods = {}
call_methods = {}
ObjectSpace.each_object {|o|
methods = []
if Module === o
methods.concat o.instance_methods(false).map {|msym| o.instance_method(msym) }
methods.concat o.private_instance_methods(false).map {|msym| o.instance_method(msym) }
end
unless Bignum === o || Float === o || Symbol === o || String === o
methods.concat o.singleton_methods(false).map {|msym| o.method(msym) }
begin
methods.concat o.singleton_class.private_instance_methods(false).map {|msym| o.method(msym) }
rescue
p o
raise
end
end
methods.each {|m|
def_methods[m.name] = true
ops = RubyVM::InstructionSequence.of(m).to_a[13]
next unless ops
ops.select{|a|a[0] == :opt_send_simple}.each{|op|
call_methods[ op[1][:mid] ] ||= []
call_methods[ op[1][:mid] ] << m
}
}
}
a = call_methods.keys - def_methods.keys
if !a.empty?
a.sort.each {|m|
n = call_methods[m].first
puts "`#{m}` is called from #{n.inspect[/[\w:]+[#.]\w+/]} at " \
"#{n.source_location.join(":").sub(RbConfig::CONFIG["rubylibdir"],"lib")}"
}
p a.size
end
require "pp"
def can_skip_to(insns, beg, label)
labels = []
cnt = nil
insns[beg..-1].each_with_index do |insn, i|
case insn
when Symbol
if insn == label
cnt = i
break
else
labels << insn
end
end
end
return -1 unless cnt
is = insns.dup
is.slice!(beg, cnt)
is.each do |insn|
case insn
when Array
case insn[0]
when :branchif, :branchunless, :jump
if labels.include?(insn[1])
#p [:cannot_skip, insn, labels]
return -1
end
end
end
end
beg+cnt
end
def called_methods(m)
p m
iseq = RubyVM::InstructionSequence.of(m)
#puts iseq.disasm
stack = []
ics = []
unk = BasicObject.new
def unk.inspect;"unk" end
def unk.===(o);o.equal? self end
def unk.!;true end
insns = iseq.to_a[13]
skip_to = -1
calls = []
idx = 0
insns.each_with_index do |insn, i|
if Array === insn
#p [idx, insn]
idx += insn.size
end
next if i <= skip_to
case insn
when Integer
# line number
when Symbol
# label
when Array
# instruction
case op = insn[0]
when :trace
when :getinlinecache
stack << nil
when :setinlinecache
when :getinstancevariable
stack << unk
when :setinstancevariable
stack.pop
when :defined
v = stack.pop
case insn[1]
when 6 # constant
if v == unk
stack << unk
#p [insn, v]
next
elsif v.nil?
v = m.owner
elsif Module === v
else
v = v.class
end
stack << (v.const_defined?(insn[2]) ? true : false)
else
stack << unk
end
#p [insn, stack.last]
when :getconstant
klass = stack.pop # klass
if klass == unk
stack << unk
next
elsif klass.nil?
klass = m.owner
elsif Module === klass
else
klass = klass.class
end
stack << (klass.const_get(insn[1]) rescue unk)
when :getlocal_OP__WC__0
stack << unk
when :setlocal_OP__WC__0
stack.pop
when :branchif, :branchunless
v = stack.pop
#p [insn, v]
if !v == (op == :branchunless)
skip_to = can_skip_to(insns, i, insn[1])
#p [:PREDICT, insn, v, i, skip_to]
#skip_to = -1
end
when :jump
skip_to = can_skip_to(insns, i, insn[1])
when :leave
when :opt_aref
stack.pop(2)
stack << unk
when :nop
when :putnil
stack << nil
when :putstring
stack << insn[1]
when :putself
stack << unk
when :putobject
stack << insn[1]
when :pop
stack.pop
when :dup
stack << stack.last
when :swap
stack.concat stack.pop(2).reverse
when :opt_send_simple, :opt_send_without_block
calls << insn[1][:mid]
when :opt_neq
stack.pop(2)
stack << unk
else
# p insn
end
else
# [bug]
raise insn.inspect
end
end
calls
end
if !a.empty?
eliminated_iseqs = {}
b = a.select {|m|
p m: m
call_methods[m].any? {|n|
ms = eliminated_iseqs[n]
unless ms
ms = eliminated_iseqs[n] = called_methods( n )
end
p ms: ms
ms.include?(m)
}
}
p b
p b.size
end
m = Gem.method(:latest_spec_for)
puts RubyVM::InstructionSequence.of(m).disasm
p called_methods( m )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment