Skip to content

Instantly share code, notes, and snippets.

@miura1729
Created July 31, 2010 07:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miura1729/501900 to your computer and use it in GitHub Desktop.
Save miura1729/501900 to your computer and use it in GitHub Desktop.
# Unused Variable Checker
#
# Usage: ruby varcheck.rb test-src.rb
#
#
module VarCheck
class InstSeqTree
Headers = %w(magic major_version minor_version format_type
misc name filename filepath line type locals args
exception_table)
# call-seq:
# VMLib::InstSeqTree.new(parent, iseq)
# parent Partent of InstSeqTree
# For example, when you will construct InstSeqTree of
# the method body, you must 'parent' is InstSeqTree of definition
# code of the method.
# If parent is none, 'parent' is nil.
# iseq Instruction Sequence, Normally the result of
# VM::InstructionSequence.compile(...) or
# VM::InstructionSequence.compile_file(...)
def initialize(parent = nil, iseq = nil)
@lblock = {}
@lblock_list = [nil]
@header = {}
@body = nil
@parent = parent
Headers.each do |name|
@header[name] = nil
end
if iseq then
init_from_ary(iseq.to_a)
end
end
attr :header
attr_accessor :body
attr :parent
def init_from_ary(ary)
i = 0
Headers.each do |name|
@header[name] = ary[i]
i = i + 1
end
@body = ary[i]
end
def to_a
res = []
Headers.each do |name|
res.push @header[name]
end
res.push @body
res
end
end
class VarChecker
def initialize
@sttop = nil
@methodpos = [0]
end
def unused_check(envst, klass, method)
a = 0
envst.each do |v|
if v then
printf "Unused variable '#{v}' in #{klass}##{method}\n"
end
end
end
def traverse_one(iseq, ins, klass, method, envst)
if ins.is_a?(Array) then
case ins[0]
when :putobject
@sttop = ins[1]
when :putiseq
body = InstSeqTree.new(iseq, ins[1])
if body.header['type'] == :method then
@methodpos.push envst.size
envst.push body.header['locals'].dup
nmethod = @sttop
traverse(body, klass, nmethod, envst)
@methodpos.pop
unused_check(envst.pop, klass, nmethod)
else
envst.push body.header['locals'].dup
traverse(body, klass, method, envst)
unused_check(envst.pop, klass, method)
end
when :defineclass
body = InstSeqTree.new(iseq, ins[2])
envst.push body.header['locals'].dup
nklass = ins[1]
traverse(body, nklass, nil, envst)
unused_check(envst.pop, nklass, method)
when :send
if ins[3] then
body = InstSeqTree.new(iseq, ins[3])
envst.push body.header['locals'].dup
traverse(body, klass, method, envst)
unused_check(envst.pop, klass, method)
end
when :getdynamic
off = ins[1]
dep = ins[2]
envst[-dep - 1][-off + 1] = nil
when :getlocal
off = ins[1]
envst[@methodpos.last][-off + 1] = nil
end
end
end
def traverse(iseq, klass, method, envst)
iseq.body.each do |ins|
traverse_one(iseq, ins, klass, method, envst)
end
end
end
end
is = RubyVM::InstructionSequence.compile_file(ARGV[0])
iseq = VarCheck::InstSeqTree.new(nil, is)
VarCheck::VarChecker.new.traverse(iseq, nil, nil, [iseq.header['locals']])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment