flaw_detector 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +18 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +4 -0
- data/Rakefile +4 -0
- data/bin/flaw_detector +38 -0
- data/ext/insns_ext/extconf.rb +2 -0
- data/ext/insns_ext/insn_ext.rb +7 -0
- data/ext/insns_ext/insns.inc +179 -0
- data/ext/insns_ext/insns_ext.c +36 -0
- data/ext/insns_ext/insns_info.inc +695 -0
- data/flaw_detector.gemspec +26 -0
- data/lib/flaw_detector/code_model/cfg_node.rb +190 -0
- data/lib/flaw_detector/code_model/code_document.rb +63 -0
- data/lib/flaw_detector/code_model/insns_frame.rb +341 -0
- data/lib/flaw_detector/controller.rb +27 -0
- data/lib/flaw_detector/detector/abstract_detector.rb +10 -0
- data/lib/flaw_detector/detector/nil_false_path_flow.rb +179 -0
- data/lib/flaw_detector/formatter/csv_formatter.rb +24 -0
- data/lib/flaw_detector/message.rb +45 -0
- data/lib/flaw_detector/version.rb +3 -0
- data/lib/flaw_detector.rb +169 -0
- data/sample/flaw_in_code.rb +9 -0
- data/spec/lib/flaw_detector_spec.rb +526 -0
- data/spec/spec_helper.rb +7 -0
- metadata +92 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module FlawDetector
|
2
|
+
module Detector
|
3
|
+
class NilFalsePathFlow
|
4
|
+
NIL_CHECK_METHODS = [:nil?]
|
5
|
+
VAR_BRANCH_KLASS = {:klass => [NilClass, FalseClass], :message => {:branchif => :last, :branchunless => :first}}
|
6
|
+
# if methods return true, RCEV type is decided
|
7
|
+
RCEV_KLASS_OF_METHODS = {
|
8
|
+
:nil? =>
|
9
|
+
{:klass => [NilClass],
|
10
|
+
:message => {:branchif => :first, :branchunless => :last}}
|
11
|
+
}
|
12
|
+
REVERSE_EDGE = {:first => :last, :last => :first}
|
13
|
+
NIL_METHODS = nil.methods
|
14
|
+
FALSE_CLASS_METHODS = FalseClass.methods
|
15
|
+
CALC_CODE = {:opt_plus => :+, :opt_minus => :-, :opt_mult => :*, :opt_div => :/, :opt_mod => :%,
|
16
|
+
:opt_eq => :==, :opt_neq => :!=, :opt_lt => :<, :opt_le => :<=, :opt_gt => :>, :opt_ge => :>=,
|
17
|
+
:opt_ltlt => :<<}
|
18
|
+
def initialize
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# == Arguments & Return
|
23
|
+
# _dom_ ::
|
24
|
+
# *Return* :: [Array] array of hash. each hash key must be [:msgid,:file,:line,:short_desc,:long_desc,:details]
|
25
|
+
def analyze(dom)
|
26
|
+
result = []
|
27
|
+
|
28
|
+
search_frames = dom.select_methods + [dom.top] + dom.select_classes
|
29
|
+
search_frames.each do |frame|
|
30
|
+
klass_specified_variable_list = []
|
31
|
+
# *search
|
32
|
+
frame.insns.each_with_index do |insn, index|
|
33
|
+
next unless [:getlocal, :getdynamic].include?(insn[0])
|
34
|
+
varframe, varname = frame.insn2variable(insn)
|
35
|
+
obj_index,argnum = frame.find_use_insn_index_of_ret(index, insn)
|
36
|
+
unless obj_index
|
37
|
+
# @todo support using variable at multiple branchif ;ex) case-when syntax
|
38
|
+
next
|
39
|
+
end
|
40
|
+
obj_insn = frame.raw_block_data[:body][:insns][obj_index]
|
41
|
+
p_cfg = frame.find_cfg_node_by_insn_index(obj_index)
|
42
|
+
klass_specified_edge = nil
|
43
|
+
case obj_insn[0]
|
44
|
+
when :send
|
45
|
+
if argnum == 0 && RCEV_KLASS_OF_METHODS.has_key?(obj_insn[1]) #varnum is the receiver and method is a nil check
|
46
|
+
next_obj_index,next_argnum = frame.find_use_insn_index_of_ret(obj_index, obj_insn)
|
47
|
+
next_obj_insn = frame.raw_block_data[:body][:insns][next_obj_index]
|
48
|
+
case next_obj_insn[0]
|
49
|
+
when :branchif, :branchunless
|
50
|
+
klass_specified_edge = RCEV_KLASS_OF_METHODS[obj_insn[1]][:message][next_obj_insn[0]]
|
51
|
+
klass = RCEV_KLASS_OF_METHODS[obj_insn[1]][:klass]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
when :branchif, :branchunless
|
55
|
+
klass_specified_edge = VAR_BRANCH_KLASS[:message][obj_insn[0]]
|
56
|
+
klass = VAR_BRANCH_KLASS[:klass]
|
57
|
+
end
|
58
|
+
|
59
|
+
next unless klass_specified_edge
|
60
|
+
ks_variable = {:specified_edge => klass_specified_edge, :branch_node => p_cfg, :variable => {:frame => varframe, :name => varname, :klass => klass}, :already_checked => false}
|
61
|
+
ks_variable[:complement_edge] = REVERSE_EDGE[ks_variable[:specified_edge]]
|
62
|
+
nn = ks_variable[:branch_node].next_nodes
|
63
|
+
tmperase = []
|
64
|
+
tmperase << :complement_edge if nn.send(ks_variable[:specified_edge]).is_dominance_frontier_of?(nn.send(ks_variable[:complement_edge]))
|
65
|
+
tmperase << :specified_edge if nn.send(ks_variable[:complement_edge]).is_dominance_frontier_of?(nn.send(ks_variable[:specified_edge]))
|
66
|
+
tmperase.each{|k| ks_variable.delete(k)}
|
67
|
+
|
68
|
+
klass_specified_variable_list << ks_variable
|
69
|
+
end
|
70
|
+
|
71
|
+
# *detection
|
72
|
+
klass_specified_variable_list.each do |ks_variable|
|
73
|
+
next if ks_variable[:already_checked]
|
74
|
+
if ks_variable[:specified_edge]
|
75
|
+
cfg = ks_variable[:branch_node].next_nodes.send(ks_variable[:specified_edge])
|
76
|
+
cfg.visit_specified_dominators do |node|
|
77
|
+
check_end, ng_list = check_nil_var_ref(node, ks_variable[:variable])
|
78
|
+
ng_list.each do |elem|
|
79
|
+
line = elem[:frame].index2line(elem[:index])
|
80
|
+
result << FlawDetector::make_info(:file => dom.filepath, :line => line, :msgid => "NP_ALWAYS_FALSE", :params => [elem[:variable][:name]])
|
81
|
+
end
|
82
|
+
if check_end
|
83
|
+
next :none
|
84
|
+
else
|
85
|
+
redundant = find_redundant_node(klass_specified_variable_list, node, ks_variable)
|
86
|
+
if redundant
|
87
|
+
redundant[:already_checked] = true
|
88
|
+
line = redundant[:branch_node].last_line
|
89
|
+
result << FlawDetector::make_info(:file => dom.filepath, :line => line, :msgid => "RCN_REDUNDANT_FALSECHECK_OF_FALSE_VALUE", :params => [redundant[:variable][:name], ks_variable[:branch_node].last_line])
|
90
|
+
next redundant[:specified_edge]
|
91
|
+
else
|
92
|
+
next :all
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if ks_variable[:complement_edge]
|
99
|
+
cfg = ks_variable[:branch_node].next_nodes.send(ks_variable[:complement_edge])
|
100
|
+
cfg.visit_specified_dominators do |node|
|
101
|
+
redundant = find_redundant_node(klass_specified_variable_list, node, ks_variable)
|
102
|
+
if redundant
|
103
|
+
redundant[:already_checked] = true
|
104
|
+
line = redundant[:branch_node].last_line
|
105
|
+
result << FlawDetector::make_info(:file => dom.filepath, :line => line, :msgid => "RCN_REDUNDANT_FALSECHECK_OF_TRUE_VALUE", :params => [redundant[:variable][:name], ks_variable[:branch_node].last_line])
|
106
|
+
next redundant[:complement_edge]
|
107
|
+
else
|
108
|
+
next :all
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
# [p_cfg's dominators - s_cfg's dominators] check send method except nil methods
|
113
|
+
#TODO: add warning list if get localvar and nil checked. # deadcode
|
114
|
+
#TODO: implements
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#* remove duplicate result
|
119
|
+
#NOTE: it be caused that input code to analyzer has deadcode.
|
120
|
+
return result
|
121
|
+
end
|
122
|
+
|
123
|
+
# _variable_ :: nil variable on node
|
124
|
+
# return: check_end?, detected ngs
|
125
|
+
def check_nil_var_ref(node, variable)
|
126
|
+
ng_list = [] # each element has keys: {:variable, :index, :frame}
|
127
|
+
unless node.prev_nodes.first == node.prev_nodes.last
|
128
|
+
# check setlocal opcode at dominance frontier
|
129
|
+
pfpl = node.prev_nodes.first.dominators - node.prev_nodes.last.dominators
|
130
|
+
plpf = node.prev_nodes.last.dominators - node.prev_nodes.first.dominators
|
131
|
+
#TODO: return if pfpl/plpf have set localvar
|
132
|
+
end
|
133
|
+
|
134
|
+
frame = node.insns_frame
|
135
|
+
|
136
|
+
node.bb[0].each do |index|
|
137
|
+
insn = frame.insns[index]
|
138
|
+
case insn[0]
|
139
|
+
when :setlocal, :setdynamic
|
140
|
+
varframe,varname = frame.insn2variable(insn)
|
141
|
+
# return if insn of index set localvar
|
142
|
+
return true, ng_list if variable[:name] == varname && variable[:frame] == varframe
|
143
|
+
when :getlocal, :getdynamic
|
144
|
+
varframe,varname = frame.insn2variable(insn)
|
145
|
+
if variable[:name] == varname && variable[:frame] == varframe
|
146
|
+
use_index, use_argnum = frame.find_use_insn_index_of_ret(index, insn)
|
147
|
+
use_insn = frame.insns[use_index]
|
148
|
+
case use_insn[0]
|
149
|
+
when :send
|
150
|
+
use_insn[1]
|
151
|
+
if use_argnum == 0 && !NIL_METHODS.include?(use_insn[1])
|
152
|
+
ng_list << {:variable => variable, :index => use_index, :frame => frame}
|
153
|
+
end
|
154
|
+
when *(CALC_CODE.keys)
|
155
|
+
# ASSERT: nil not operand method
|
156
|
+
unless NIL_METHODS.include?(CALC_CODE[use_insn[0]])
|
157
|
+
ng_list << {:variable => variable, :index => use_index, :frame => frame}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
return false, ng_list
|
164
|
+
end
|
165
|
+
|
166
|
+
def find_redundant_node(klass_specified_variable_list, node, ks_variable)
|
167
|
+
redundant = klass_specified_variable_list.find do |other_info| # @todo sort by tree parent first
|
168
|
+
next if other_info[:branch_node] == ks_variable[:branch_node]
|
169
|
+
next unless other_info[:variable][:name] == ks_variable[:variable][:name] &&
|
170
|
+
other_info[:variable][:frame] == ks_variable[:variable][:frame]
|
171
|
+
next unless other_info[:branch_node] == node
|
172
|
+
next unless (ks_variable[:variable][:klass] - other_info[:variable][:klass]).empty?
|
173
|
+
true
|
174
|
+
end
|
175
|
+
return redundant
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module FlawDetector
|
4
|
+
module Formatter
|
5
|
+
class CsvFormatter
|
6
|
+
def initialize(io = STDOUT)
|
7
|
+
@io = io
|
8
|
+
end
|
9
|
+
|
10
|
+
# == Arguments & Return
|
11
|
+
# _result_ :: [Array] array of hash. each hash key must be [:msgid,:file,:line,:summary,:description]
|
12
|
+
# *Return* :: [String]
|
13
|
+
def render(result)
|
14
|
+
headers = [:msgid,:file,:line,:short_desc,:long_desc,:details]
|
15
|
+
data = CSV.generate("", :row_sep => "\r\n", :headers => headers, :write_headers => true) do |csv|
|
16
|
+
result.each do |row|
|
17
|
+
csv << row.values_at(*headers)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@io << data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module FlawDetector
|
2
|
+
MESSAGE_IDS = {
|
3
|
+
"RCN_REDUNDANT_FALSECHECK_WOULD_HAVE_BEEN_A_NPE" => { # @todo rewrite it to meaningful message
|
4
|
+
:short_desc => "Falsecheck of value previously dereferenced",
|
5
|
+
:long_desc => "Falsecheck of %{0} at %{1} of value previously dereferenced in %{2}",
|
6
|
+
:details => "A value is checked here to see whether it is false, but this value can't be false because it was previously dereferenced and if it were false a false pointer exception would have occurred at the earlier dereference. Essentially, this code and the previous dereference disagree as to whether this value is allowed to be false. Either the check is redundant or the previous dereference is erroneous."
|
7
|
+
},
|
8
|
+
"RCN_REDUNDANT_FALSECHECK_OF_FALSE_VALUE" => {
|
9
|
+
:short_desc => "Redundant falsecheck of value known to be false",
|
10
|
+
:long_desc => "Redundant falsecheck of %{0} which is known to be false in LINE:%{1}",
|
11
|
+
:details => "This method contains a redundant check of a known false value against the constant false."
|
12
|
+
},
|
13
|
+
"RCN_REDUNDANT_FALSECHECK_OF_TRUE_VALUE" => {
|
14
|
+
:short_desc => "Redundant falsecheck of value known to be false",
|
15
|
+
:long_desc => "Redundant falsecheck of %{0} which is known to be false in LINE:%{1}",
|
16
|
+
:details => "This method contains a redundant check of a known false value against the constant false."
|
17
|
+
},
|
18
|
+
"NP_ALWAYS_FALSE" => {
|
19
|
+
:short_desc => "False value missing method received",
|
20
|
+
:long_desc => "False value missing method received in %{0}",
|
21
|
+
:details => "A false value, which is NilClass or FalseClass, is received missing method here. This will lead to a NoMethodError when the code is executed."
|
22
|
+
},
|
23
|
+
"NP_FALSE_ON_SOME_PATH" =>{ # @todo rewrite it to meaningful message
|
24
|
+
:short_desc => "Possible false pointer dereference",
|
25
|
+
:long_desc => "Possible false pointer dereference in %{0}",
|
26
|
+
:details => "There is a branch of statement that, if executed, guarantees that a false value will be dereferenced, which would generate a RuntimeError when the code is executed. Of course, the problem might be that the branch or statement is infeasible and that the false pointer exception can't ever be executed; deciding that is beyond the ability of FlawDetector."
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
def make_info(params={})
|
31
|
+
info = {}
|
32
|
+
info[:msgid] = params[:msgid]
|
33
|
+
info[:file] = params[:file]
|
34
|
+
info[:line] = params[:line]
|
35
|
+
hash = {}
|
36
|
+
params[:params].each_with_index do |elem, index|
|
37
|
+
hash["%{#{index}}"] = elem
|
38
|
+
end
|
39
|
+
pattern = Regexp.new(hash.keys.map{|n| Regexp.escape(n)}.join("|"))
|
40
|
+
info[:short_desc] = MESSAGE_IDS[info[:msgid]][:short_desc].gsub(pattern, hash)
|
41
|
+
info[:long_desc] = MESSAGE_IDS[info[:msgid]][:long_desc].gsub(pattern, hash)
|
42
|
+
info[:details] = MESSAGE_IDS[info[:msgid]][:details].gsub(pattern, hash)
|
43
|
+
return info
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require "flaw_detector/version"
|
2
|
+
require "flaw_detector/controller"
|
3
|
+
require "flaw_detector/message"
|
4
|
+
require "flaw_detector/formatter/csv_formatter"
|
5
|
+
require "flaw_detector/detector/nil_false_path_flow"
|
6
|
+
require "flaw_detector/code_model/code_document"
|
7
|
+
require "flaw_detector/code_model/insns_frame"
|
8
|
+
require "flaw_detector/code_model/cfg_node"
|
9
|
+
require File.expand_path("../../ext/insns_ext/insn_ext.rb", __FILE__)
|
10
|
+
|
11
|
+
module FlawDetector
|
12
|
+
OPERAND_0 = 1
|
13
|
+
OPERAND_1 = 2
|
14
|
+
OPERAND_2 = 3
|
15
|
+
OPERAND_3 = 4
|
16
|
+
|
17
|
+
module ISeqHeader
|
18
|
+
TYPE_TOP = :top
|
19
|
+
TYPE_CLASS = :class
|
20
|
+
TYPE_METHOD = :method
|
21
|
+
TYPE_BLOCK = :block
|
22
|
+
end
|
23
|
+
|
24
|
+
include CodeModel
|
25
|
+
|
26
|
+
# (key,val) = (opcode, operand number from 0)
|
27
|
+
INSNS_HAVE_ISEQ_IN_OPERAND = {"putiseq" => OPERAND_0, "defineclass" => OPERAND_1, "send" => OPERAND_2, "invokesuper" => OPERAND_1}
|
28
|
+
# ASSERT: operand number 0 is offset
|
29
|
+
INSNS_OF_BRANCH = ["branchif", "branchunless"]
|
30
|
+
|
31
|
+
def parse(code_str, filepath="<compiled>")
|
32
|
+
isqns = RubyVM::InstructionSequence.new(code_str)
|
33
|
+
data = iseq_parse(isqns.to_a)
|
34
|
+
CodeDocument.create(data, filepath)
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_file(file)
|
38
|
+
case file
|
39
|
+
when String
|
40
|
+
code_str = File.open(file, "r"){|f| f.read}
|
41
|
+
filepath = file
|
42
|
+
when File
|
43
|
+
code_str = file.read
|
44
|
+
filepath = file.path
|
45
|
+
when IO
|
46
|
+
code_str = file.read
|
47
|
+
filepath = "<compiled>"
|
48
|
+
else
|
49
|
+
raise "not support class#{file.class}"
|
50
|
+
end
|
51
|
+
parse(code_str, filepath)
|
52
|
+
end
|
53
|
+
|
54
|
+
def iseq_parse(iseq)
|
55
|
+
return nil unless iseq
|
56
|
+
|
57
|
+
header = {}
|
58
|
+
header[:magic] = iseq[0]
|
59
|
+
header[:major] = iseq[1]
|
60
|
+
header[:minor] = iseq[2]
|
61
|
+
header[:format_type] = iseq[3]
|
62
|
+
header[:misc] = iseq[4]
|
63
|
+
header[:name] = iseq[5]
|
64
|
+
header[:filename] = iseq[6]
|
65
|
+
header[:filepath] = iseq[7]
|
66
|
+
header[:line_no] = iseq[8]
|
67
|
+
header[:type] = iseq[9]
|
68
|
+
header[:locals] = iseq[10]
|
69
|
+
header[:args] = iseq[11]
|
70
|
+
header[:exceptions] = iseq[12]
|
71
|
+
|
72
|
+
insns = []
|
73
|
+
insns_pos_to_lineno = []
|
74
|
+
label_pos = {}
|
75
|
+
lineno_and_insns_pos = [nil,nil]
|
76
|
+
insns_pos_to_operand_iseq = {}
|
77
|
+
branch_info = [] # element = {:pos =>, :label =>}
|
78
|
+
basic_block = [] # element type is (Range, Array). It is guaranteed that each element Range doesn't overlap
|
79
|
+
tmp_bb_start_pos = 0
|
80
|
+
|
81
|
+
iseq[13].each do |mixed|
|
82
|
+
case mixed
|
83
|
+
when Array # instruction
|
84
|
+
insns << mixed
|
85
|
+
opcode = mixed.first.to_s
|
86
|
+
if INSNS_HAVE_ISEQ_IN_OPERAND.keys.include?(opcode)
|
87
|
+
operand_num = INSNS_HAVE_ISEQ_IN_OPERAND[opcode]
|
88
|
+
iseq_in_operand = iseq_parse(mixed[operand_num])
|
89
|
+
insn_pos = insns.count-1
|
90
|
+
insns_pos_to_operand_iseq[insn_pos] = iseq_in_operand
|
91
|
+
|
92
|
+
#replace to link
|
93
|
+
mixed[operand_num] = insn_pos
|
94
|
+
|
95
|
+
if insns_pos_to_operand_iseq[insn_pos] &&
|
96
|
+
insns_pos_to_operand_iseq[insn_pos][:header][:type] == ISeqHeader::TYPE_BLOCK
|
97
|
+
# NOTE: bb is changed becase iseq of block type may have break, raise, or etc...
|
98
|
+
if tmp_bb_start_pos < insn_pos
|
99
|
+
basic_block << [tmp_bb_start_pos...insn_pos, insn_pos]
|
100
|
+
end
|
101
|
+
basic_block << [insn_pos...(insn_pos+1), insn_pos+1, :exception]
|
102
|
+
tmp_bb_start_pos = insns.count
|
103
|
+
end
|
104
|
+
elsif INSNS_OF_BRANCH.include?(opcode)
|
105
|
+
basic_block << [tmp_bb_start_pos...insns.count, mixed[OPERAND_0], insns.count]
|
106
|
+
tmp_bb_start_pos = insns.count
|
107
|
+
elsif opcode == "jump"
|
108
|
+
basic_block << [tmp_bb_start_pos...insns.count, mixed[OPERAND_0]]
|
109
|
+
tmp_bb_start_pos = insns.count
|
110
|
+
elsif opcode == "leave"
|
111
|
+
basic_block << [tmp_bb_start_pos...insns.count, :leave]
|
112
|
+
tmp_bb_start_pos = insns.count
|
113
|
+
end
|
114
|
+
when Fixnum # lineno
|
115
|
+
if lineno_and_insns_pos[0]
|
116
|
+
range = lineno_and_insns_pos[1]...(insns.count)
|
117
|
+
insns_pos_to_lineno << [range, lineno_and_insns_pos[0]]
|
118
|
+
end
|
119
|
+
lineno_and_insns_pos[0] = mixed
|
120
|
+
lineno_and_insns_pos[1] = insns.count
|
121
|
+
when Symbol # label
|
122
|
+
label_pos[mixed] = insns.count
|
123
|
+
basic_block << [tmp_bb_start_pos...insns.count, insns.count]
|
124
|
+
tmp_bb_start_pos = insns.count
|
125
|
+
end
|
126
|
+
end
|
127
|
+
if lineno_and_insns_pos[0]
|
128
|
+
if insns_pos_to_lineno.empty? || !insns_pos_to_lineno.last.include?(insns.count-1)
|
129
|
+
range = lineno_and_insns_pos[1]...(insns.count)
|
130
|
+
insns_pos_to_lineno << [range, lineno_and_insns_pos[0]]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
# remove redundant bb
|
134
|
+
basic_block.reject! {|bb| bb[0].to_a.size == 0}
|
135
|
+
basic_block.each do |bb|
|
136
|
+
bb.map! do |elem|
|
137
|
+
if elem.to_s =~ /label_.+/
|
138
|
+
label_pos[elem]
|
139
|
+
else
|
140
|
+
elem
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
#TODO: add remove redundant bb code
|
145
|
+
|
146
|
+
#set name
|
147
|
+
cnt = 0
|
148
|
+
bs = {}
|
149
|
+
basic_block.each do |bb|
|
150
|
+
sym = "bb_#{bs.count}".to_sym
|
151
|
+
bs[sym] = bb
|
152
|
+
end
|
153
|
+
bs[:entry] = [0...0,0]
|
154
|
+
bs[:leave] = [0...0]
|
155
|
+
|
156
|
+
body = {}
|
157
|
+
body[:insns] = insns
|
158
|
+
|
159
|
+
extra = {}
|
160
|
+
extra[:insns_pos_to_lineno] = insns_pos_to_lineno
|
161
|
+
extra[:label_pos] = label_pos
|
162
|
+
extra[:insns_pos_to_operand_iseq] = insns_pos_to_operand_iseq
|
163
|
+
extra[:basic_blocks] = bs
|
164
|
+
|
165
|
+
return {:header => header, :body => body, :extra => extra}
|
166
|
+
end
|
167
|
+
|
168
|
+
module_function :iseq_parse, :parse, :make_info, :parse_file
|
169
|
+
end
|