furnace-avm2 0.0.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +4 -3
  3. data/LICENSE +20 -0
  4. data/bin/furnace-avm2 +36 -6
  5. data/bin/furnace-avm2-decompiler +141 -0
  6. data/furnace-avm2.gemspec +2 -2
  7. data/lib/furnace-avm2.rb +1 -0
  8. data/lib/furnace-avm2/abc.rb +24 -7
  9. data/lib/furnace-avm2/abc/metadata/{option_detail.rb → default_value.rb} +5 -1
  10. data/lib/furnace-avm2/abc/metadata/initializer_body.rb +8 -0
  11. data/lib/furnace-avm2/abc/metadata/instance_info.rb +19 -4
  12. data/lib/furnace-avm2/abc/metadata/klass_info.rb +1 -4
  13. data/lib/furnace-avm2/abc/metadata/method_body_info.rb +27 -10
  14. data/lib/furnace-avm2/abc/metadata/method_info.rb +20 -13
  15. data/lib/furnace-avm2/abc/metadata/multiname_info.rb +6 -0
  16. data/lib/furnace-avm2/abc/metadata/multiname_kind_genericname.rb +8 -5
  17. data/lib/furnace-avm2/abc/metadata/multiname_kind_multiname.rb +4 -0
  18. data/lib/furnace-avm2/abc/metadata/multiname_kind_qname.rb +5 -1
  19. data/lib/furnace-avm2/abc/metadata/record_with_value.rb +90 -0
  20. data/lib/furnace-avm2/abc/metadata/script_info.rb +25 -1
  21. data/lib/furnace-avm2/abc/metadata/trait_class.rb +7 -3
  22. data/lib/furnace-avm2/abc/metadata/trait_info.rb +8 -4
  23. data/lib/furnace-avm2/abc/metadata/trait_method.rb +4 -0
  24. data/lib/furnace-avm2/abc/metadata/trait_slot.rb +5 -51
  25. data/lib/furnace-avm2/abc/opcodes/bitwise/as3_lshift.rb +1 -1
  26. data/lib/furnace-avm2/abc/opcodes/bitwise/as3_rshift.rb +1 -1
  27. data/lib/furnace-avm2/abc/opcodes/bitwise/as3_urshift.rb +1 -1
  28. data/lib/furnace-avm2/abc/opcodes/debug/as3_debug.rb +15 -0
  29. data/lib/furnace-avm2/abc/opcodes/debug/as3_debugfile.rb +12 -0
  30. data/lib/furnace-avm2/abc/opcodes/debug/as3_debugline.rb +12 -0
  31. data/lib/furnace-avm2/abc/opcodes/function_invocation/as3_callproplex.rb +21 -0
  32. data/lib/furnace-avm2/abc/opcodes/push_literal/as3_pushbyte.rb +1 -1
  33. data/lib/furnace-avm2/abc/opcodes/push_literal/as3_pushshort.rb +1 -1
  34. data/lib/furnace-avm2/binary/record.rb +21 -1
  35. data/lib/furnace-avm2/source/declaration_tokens/argument_declaration_token.rb +4 -0
  36. data/lib/furnace-avm2/source/declaration_tokens/arguments_token.rb +15 -0
  37. data/lib/furnace-avm2/source/declaration_tokens/callee_token.rb +72 -0
  38. data/lib/furnace-avm2/source/declaration_tokens/class_implementations_token.rb +21 -0
  39. data/lib/furnace-avm2/source/declaration_tokens/class_inheritance_token.rb +17 -0
  40. data/lib/furnace-avm2/source/declaration_tokens/class_name_token.rb +21 -0
  41. data/lib/furnace-avm2/source/declaration_tokens/class_specifiers_token.rb +12 -0
  42. data/lib/furnace-avm2/source/declaration_tokens/class_token.rb +21 -0
  43. data/lib/furnace-avm2/source/declaration_tokens/comment_token.rb +22 -0
  44. data/lib/furnace-avm2/source/declaration_tokens/constructor_specifiers_token.rb +12 -0
  45. data/lib/furnace-avm2/source/declaration_tokens/constructor_token.rb +15 -0
  46. data/lib/furnace-avm2/source/declaration_tokens/function_name_token.rb +14 -0
  47. data/lib/furnace-avm2/source/declaration_tokens/import_token.rb +17 -0
  48. data/lib/furnace-avm2/source/declaration_tokens/initialization_token.rb +7 -0
  49. data/lib/furnace-avm2/source/declaration_tokens/method_specifiers_token.rb +13 -0
  50. data/lib/furnace-avm2/source/declaration_tokens/method_token.rb +15 -0
  51. data/lib/furnace-avm2/source/declaration_tokens/multiname_token.rb +41 -0
  52. data/lib/furnace-avm2/source/declaration_tokens/namespace_name_token.rb +16 -0
  53. data/lib/furnace-avm2/source/declaration_tokens/package_name_token.rb +17 -0
  54. data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +29 -0
  55. data/lib/furnace-avm2/source/declaration_tokens/rest_argument_token.rb +12 -0
  56. data/lib/furnace-avm2/source/declaration_tokens/scope_token.rb +23 -0
  57. data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +14 -0
  58. data/lib/furnace-avm2/source/declaration_tokens/slot_name_token.rb +17 -0
  59. data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +23 -0
  60. data/lib/furnace-avm2/source/declaration_tokens/specifiers_token.rb +32 -0
  61. data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +51 -0
  62. data/lib/furnace-avm2/source/declaration_tokens/type_token.rb +7 -0
  63. data/lib/furnace-avm2/source/decompiler.rb +669 -0
  64. data/lib/furnace-avm2/source/implementation_tokens/access_token.rb +9 -0
  65. data/lib/furnace-avm2/source/implementation_tokens/array_token.rb +17 -0
  66. data/lib/furnace-avm2/source/implementation_tokens/as_token.rb +9 -0
  67. data/lib/furnace-avm2/source/implementation_tokens/assignment_token.rb +9 -0
  68. data/lib/furnace-avm2/source/implementation_tokens/binary_operator_token.rb +14 -0
  69. data/lib/furnace-avm2/source/implementation_tokens/break_token.rb +9 -0
  70. data/lib/furnace-avm2/source/implementation_tokens/call_token.rb +5 -0
  71. data/lib/furnace-avm2/source/implementation_tokens/continue_token.rb +9 -0
  72. data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +26 -0
  73. data/lib/furnace-avm2/source/implementation_tokens/control_transfer_token.rb +20 -0
  74. data/lib/furnace-avm2/source/implementation_tokens/delete_token.rb +9 -0
  75. data/lib/furnace-avm2/source/implementation_tokens/do_token.rb +9 -0
  76. data/lib/furnace-avm2/source/implementation_tokens/else_if_token.rb +9 -0
  77. data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +22 -0
  78. data/lib/furnace-avm2/source/implementation_tokens/for_each_token.rb +9 -0
  79. data/lib/furnace-avm2/source/implementation_tokens/for_token.rb +9 -0
  80. data/lib/furnace-avm2/source/implementation_tokens/generic_specializers_token.rb +17 -0
  81. data/lib/furnace-avm2/source/implementation_tokens/generic_type_token.rb +9 -0
  82. data/lib/furnace-avm2/source/implementation_tokens/if_token.rb +9 -0
  83. data/lib/furnace-avm2/source/implementation_tokens/immediate_token.rb +14 -0
  84. data/lib/furnace-avm2/source/implementation_tokens/immediate_typename_token.rb +14 -0
  85. data/lib/furnace-avm2/source/implementation_tokens/in_token.rb +9 -0
  86. data/lib/furnace-avm2/source/implementation_tokens/index_token.rb +9 -0
  87. data/lib/furnace-avm2/source/implementation_tokens/is_complex.rb +7 -0
  88. data/lib/furnace-avm2/source/implementation_tokens/is_simple.rb +7 -0
  89. data/lib/furnace-avm2/source/implementation_tokens/is_token.rb +9 -0
  90. data/lib/furnace-avm2/source/implementation_tokens/label_declaration_token.rb +12 -0
  91. data/lib/furnace-avm2/source/implementation_tokens/label_token.rb +12 -0
  92. data/lib/furnace-avm2/source/implementation_tokens/local_variable_token.rb +7 -0
  93. data/lib/furnace-avm2/source/implementation_tokens/new_token.rb +9 -0
  94. data/lib/furnace-avm2/source/implementation_tokens/object_pair_token.rb +9 -0
  95. data/lib/furnace-avm2/source/implementation_tokens/object_token.rb +17 -0
  96. data/lib/furnace-avm2/source/implementation_tokens/parentheses_token.rb +11 -0
  97. data/lib/furnace-avm2/source/implementation_tokens/property_name_token.rb +14 -0
  98. data/lib/furnace-avm2/source/implementation_tokens/return_token.rb +16 -0
  99. data/lib/furnace-avm2/source/implementation_tokens/rtname_token.rb +9 -0
  100. data/lib/furnace-avm2/source/implementation_tokens/statement_token.rb +8 -0
  101. data/lib/furnace-avm2/source/implementation_tokens/super_token.rb +9 -0
  102. data/lib/furnace-avm2/source/implementation_tokens/switch_token.rb +9 -0
  103. data/lib/furnace-avm2/source/implementation_tokens/ternary_operator_token.rb +13 -0
  104. data/lib/furnace-avm2/source/implementation_tokens/throw_token.rb +12 -0
  105. data/lib/furnace-avm2/source/implementation_tokens/typeof_token.rb +13 -0
  106. data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +14 -0
  107. data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +14 -0
  108. data/lib/furnace-avm2/source/implementation_tokens/variable_name_token.rb +14 -0
  109. data/lib/furnace-avm2/source/implementation_tokens/while_token.rb +9 -0
  110. data/lib/furnace-avm2/transform.rb +5 -1
  111. data/lib/furnace-avm2/transform/ast_build.rb +22 -7
  112. data/lib/furnace-avm2/transform/ast_normalize.rb +61 -20
  113. data/lib/furnace-avm2/transform/cfg_build.rb +86 -0
  114. data/lib/furnace-avm2/transform/cfg_reduce.rb +249 -0
  115. data/lib/furnace-avm2/transform/nf_normalize.rb +130 -0
  116. data/lib/furnace-avm2/version.rb +4 -2
  117. data/test/basic.as +239 -0
  118. metadata +110 -38
  119. data/lib/furnace-avm2/abc/metadata/option_info.rb +0 -5
  120. data/test/exception.as +0 -29
  121. data/test/literal.as +0 -5
  122. data/test/logic.as +0 -23
  123. data/test/loops.as +0 -30
  124. data/test/number.as +0 -14
  125. data/test/switch.as +0 -38
  126. data/test/ternary.as +0 -22
@@ -0,0 +1,16 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ReturnToken < Furnace::Code::SurroundedToken
3
+
4
+ def text_before
5
+ if @children.any?
6
+ "return "
7
+ else
8
+ "return"
9
+ end
10
+ end
11
+
12
+ def text_after
13
+ ";\n"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Furnace::AVM2::Tokens
2
+ class RTNameToken < Furnace::Code::SeparatedToken
3
+ include IsSimple
4
+
5
+ def text_between
6
+ "::"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Furnace::AVM2::Tokens
2
+ class StatementToken < Furnace::Code::SurroundedToken
3
+
4
+ def text_after
5
+ ";\n"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Furnace::AVM2::Tokens
2
+ class SuperToken < Furnace::Code::TerminalToken
3
+ include IsSimple
4
+
5
+ def to_text
6
+ "super"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'control_flow_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class SwitchToken < ControlFlowToken
5
+ def keyword
6
+ 'switch'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Furnace::AVM2::Tokens
2
+ class TernaryOperatorToken < Furnace::Code::NonterminalToken
3
+ include IsComplex
4
+
5
+ def to_text
6
+ "#{@children[0].to_text} ? #{@children[1].to_text} : #{@children[2].to_text}"
7
+ end
8
+
9
+ def to_structure
10
+ structurize "... ? ... : ..."
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ThrowToken < Furnace::Code::SurroundedToken
3
+
4
+ def text_before
5
+ "throw "
6
+ end
7
+
8
+ def text_after
9
+ ";\n"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Furnace::AVM2::Tokens
2
+ class TypeOfToken < Furnace::Code::SurroundedToken
3
+ include IsSimple
4
+
5
+ def text_before
6
+ "typeof("
7
+ end
8
+
9
+ def text_after
10
+ ")"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Furnace::AVM2::Tokens
2
+ class UnaryOperatorToken < Furnace::Code::SurroundedToken
3
+ include IsSimple
4
+
5
+ def initialize(origin, children, operator, options={})
6
+ super(origin, children, options)
7
+ @operator = operator
8
+ end
9
+
10
+ def text_before
11
+ @operator.to_s
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Furnace::AVM2::Tokens
2
+ class UnaryPostOperatorToken < Furnace::Code::SurroundedToken
3
+ include IsSimple
4
+
5
+ def initialize(origin, children, operator, options={})
6
+ super(origin, children, options)
7
+ @operator = operator
8
+ end
9
+
10
+ def text_after
11
+ @operator.to_s
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Furnace::AVM2::Tokens
2
+ class VariableNameToken < Furnace::Code::TerminalToken
3
+ include IsSimple
4
+
5
+ def initialize(origin, name, options={})
6
+ super(origin, options)
7
+ @name = name
8
+ end
9
+
10
+ def to_text
11
+ @name
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'control_flow_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class WhileToken < ControlFlowToken
5
+ def keyword
6
+ 'while'
7
+ end
8
+ end
9
+ end
@@ -1,6 +1,10 @@
1
1
  module Furnace::AVM2::Transform
2
2
  AST = Furnace::AST
3
+ CFG = Furnace::CFG
3
4
  end
4
5
 
5
6
  require_relative "transform/ast_build"
6
- require_relative "transform/ast_normalize"
7
+ require_relative "transform/ast_normalize"
8
+ require_relative "transform/cfg_build"
9
+ require_relative "transform/cfg_reduce"
10
+ require_relative "transform/nf_normalize"
@@ -1,5 +1,7 @@
1
1
  module Furnace::AVM2
2
2
  module Transform
3
+ # I'm not exactly proud of this code, but it works... for now. I really should
4
+ # rework it if I want to expect it to work good.
3
5
  class ASTBuild
4
6
  CONDITIONAL_OPERATORS = [ :if_eq, :if_false, :if_true, :if_ge, :if_gt,
5
7
  :if_le, :if_lt, :if_ne, :if_nge, :if_ngt,
@@ -63,20 +65,33 @@ module Furnace::AVM2
63
65
  produce.(expr)
64
66
  end
65
67
 
66
- finalize_complex_expr = lambda do |opcode, worklist, valid_types, expected_depth=nil|
68
+ finalize_complex_expr = lambda do |opcode, worklist, valid_types, expected_depth=nil, wrap_to=nil|
67
69
  while worklist.last == opcode.offset
68
70
  extend_complex_expr.(valid_types, expected_depth)
71
+
72
+ if wrap_to
73
+ node, *prepend = *wrap_to
74
+
75
+ expr, = consume.(1)
76
+ expr = AST::Node.new(node, [*prepend, expr])
77
+ produce.(expr)
78
+ end
79
+
69
80
  worklist.pop
70
81
  end
71
82
  end
72
83
 
73
84
  expand_conditionals = lambda do
85
+ expressions = []
86
+
74
87
  while stack.any? && CONDITIONAL_OPERATORS.include?(stack.last.type)
75
88
  conditional, = consume.(1)
76
89
 
77
90
  jump_node = AST::Node.new(:jump_if, [ true, conditional.metadata[:offset], conditional ])
78
- emit.(jump_node)
91
+ expressions.unshift jump_node
79
92
  end
93
+
94
+ ast.children.concat expressions
80
95
  end
81
96
 
82
97
  code.each do |opcode|
@@ -88,7 +103,7 @@ module Furnace::AVM2
88
103
  end
89
104
 
90
105
  finalize_complex_expr.(opcode, shortjump, [ :and, :or ], 1)
91
- finalize_complex_expr.(opcode, ternary, CONDITIONAL_OPERATORS)
106
+ finalize_complex_expr.(opcode, ternary, CONDITIONAL_OPERATORS, nil, [:ternary_if, false])
92
107
 
93
108
  if dup == 1 && (opcode.is_a?(ABC::AS3CoerceB) ||
94
109
  opcode.is_a?(ABC::AS3IfTrue) || opcode.is_a?(ABC::AS3IfFalse))
@@ -134,7 +149,7 @@ module Furnace::AVM2
134
149
  end
135
150
  elsif opcode.is_a?(ABC::AS3Jump)
136
151
  if opcode.body.jump_offset == 0
137
- node = AST::Node.new(:jump_target, [], label: opcode.offset)
152
+ node = AST::Node.new(:nop, [], label: opcode.offset)
138
153
  emit.(node)
139
154
  elsif stack.any? && !CONDITIONAL_OPERATORS.include?(stack.last.type)
140
155
  extend_complex_expr.(CONDITIONAL_OPERATORS)
@@ -157,11 +172,11 @@ module Furnace::AVM2
157
172
  if dup
158
173
  spurious += 1
159
174
 
160
- save_node = AST::Node.new(:set_spurious, [ spurious, *consume.(1) ])
175
+ save_node = AST::Node.new(:set_local, [ -spurious, *consume.(1) ])
161
176
  emit.(save_node)
162
177
 
163
178
  (1 + dup).times do
164
- load_node = AST::Node.new(:get_spurious, [ spurious ])
179
+ load_node = AST::Node.new(:get_local, [ -spurious ])
165
180
  produce.(load_node)
166
181
  end
167
182
 
@@ -198,7 +213,7 @@ module Furnace::AVM2
198
213
  end
199
214
  end
200
215
 
201
- [ ast.normalize_hierarchy!, method ]
216
+ ast.normalize_hierarchy!
202
217
  end
203
218
  end
204
219
  end
@@ -3,10 +3,10 @@ module Furnace::AVM2
3
3
  class ASTNormalize
4
4
  include AST::Visitor
5
5
 
6
- def transform(ast, method)
6
+ def transform(ast)
7
7
  visit ast
8
8
 
9
- [ ast, method ]
9
+ ast
10
10
  end
11
11
 
12
12
  # (pop x) -> (jump-target) x
@@ -15,7 +15,7 @@ module Furnace::AVM2
15
15
 
16
16
  node.update(:expand, [
17
17
  child,
18
- AST::Node.new(:jump_target, [], node.metadata)
18
+ AST::Node.new(:nop, [], node.metadata)
19
19
  ], nil)
20
20
  end
21
21
 
@@ -31,32 +31,73 @@ module Furnace::AVM2
31
31
 
32
32
  # (if-* a b) -> (*' a b)
33
33
  IF_MAPPING = {
34
- :eq => [false, :==],
35
- :ne => [true, :==],
36
- :ge => [false, :>=],
37
- :nge => [true, :>=],
38
- :gt => [false, :>],
39
- :ngt => [true, :>],
40
- :le => [false, :<=],
41
- :nle => [true, :<=],
42
- :lt => [false, :<],
43
- :nlt => [true, :<],
44
- :stricteq => [false, :===],
45
- :strictne => [true, :===],
46
- :true => [false, :expand],
47
- :false => [true, :expand],
34
+ :eq => [false, :==],
35
+ :ne => [false, :!=],
36
+ :ge => [false, :>=],
37
+ :nge => [true, :>=],
38
+ :gt => [false, :>],
39
+ :ngt => [true, :>],
40
+ :le => [false, :<=],
41
+ :nle => [true, :<=],
42
+ :lt => [false, :<],
43
+ :nlt => [true, :<],
44
+ :strict_eq => [false, :===],
45
+ :strict_ne => [false, :"!=="],
46
+ :true => [false, :expand],
47
+ :false => [true, :expand]
48
48
  }
49
49
  IF_MAPPING.each do |cond, (reverse, comp)|
50
50
  define_method :"on_if_#{cond}" do |node|
51
51
  node.update(comp)
52
52
  node.parent.children[0] = !node.parent.children[0] if reverse
53
+
54
+ if node.parent.type == :ternary_if && comp == :expand
55
+ node.parent.update(:ternary_if_boolean)
56
+ end
57
+ end
58
+ end
59
+
60
+ # (ternary-if * (op a b x y)) -> (ternary-if-boolean * (op a b) x y)
61
+ def on_ternary_if(node)
62
+ comparsion, op = node.children
63
+ node.children.concat op.children.slice!(2..-1)
64
+
65
+ on_ternary_if_boolean(node)
66
+ end
67
+
68
+ # (ternary-if-boolean true (op a b) x y) -> (ternary (op a b) x y)
69
+ # (ternary-if-boolean false (op a b) x y) -> (ternary (op a b) y x)
70
+ def on_ternary_if_boolean(node)
71
+ comparsion, condition, if_true, if_false = node.children
72
+ if comparsion
73
+ node.update(:ternary, [ condition, if_true, if_false ])
74
+ else
75
+ node.update(:ternary, [ condition, if_false, if_true ])
76
+ end
77
+ end
78
+
79
+ # (&& (coerce-b ...) (coerce-b ...)) -> (&& ... ...)
80
+ def fix_boolean(node)
81
+ node.children.map! do |child|
82
+ if child.is_a?(AST::Node) &&
83
+ child.type == :coerce_b
84
+ child.children.first
85
+ else
86
+ child
87
+ end
53
88
  end
54
89
  end
90
+ alias :on_and :fix_boolean
91
+ alias :on_or :fix_boolean
92
+ alias :on_jump_if :fix_boolean
55
93
 
56
- # TEMPORARY: (jump-target) -> x
57
- def on_jump_target(node)
58
- node.update(:remove)
94
+ def replace_with_nop(node)
95
+ node.update(:nop)
59
96
  end
97
+ alias :on_label :replace_with_nop
98
+ alias :on_debug :replace_with_nop
99
+ alias :on_debug_file :replace_with_nop
100
+ alias :on_debug_line :replace_with_nop
60
101
  end
61
102
  end
62
103
  end
@@ -0,0 +1,86 @@
1
+ module Furnace::AVM2
2
+ module Transform
3
+ class CFGBuild
4
+ include AST::Visitor
5
+
6
+ def transform(ast)
7
+ @ast = ast
8
+ visit @ast
9
+
10
+ @cfg = CFG::Graph.new
11
+
12
+ @pending_label = nil
13
+ @pending_queue = []
14
+
15
+ @jumps = []
16
+
17
+ @ast.children.each_with_index do |node, index|
18
+ @pending_label ||= node.metadata[:label]
19
+ @pending_queue << node if ![:nop, :jump].include? node.type
20
+
21
+ next_node = @ast.children[index + 1]
22
+ next_label = next_node.metadata[:label] if next_node
23
+
24
+ case node.type
25
+ when :return_value, :return_void
26
+ cutoff(nil, [nil])
27
+
28
+ when :jump
29
+ @jumps << node.children[0]
30
+ cutoff(nil, [node.children.delete_at(0)])
31
+
32
+ when :jump_if
33
+ @jumps << node.children[1]
34
+ cutoff(node, [node.children.delete_at(1), next_label])
35
+
36
+ else
37
+ if @jumps.include? next_label
38
+ cutoff(nil, [next_label])
39
+ end
40
+ end
41
+ end
42
+
43
+ exit_node = CFG::Node.new(@cfg)
44
+ @cfg.nodes.add exit_node
45
+ @cfg.exit = exit_node
46
+
47
+ @cfg.eliminate_unreachable!
48
+ @cfg.merge_redundant!
49
+
50
+ @cfg
51
+ end
52
+
53
+ # propagate labels
54
+ def on_any(node)
55
+ return if node == @ast
56
+
57
+ label = nil
58
+
59
+ node.children.each do |child|
60
+ if child.is_a?(AST::Node) && child.metadata[:label]
61
+ if label.nil? || child.metadata[:label] < label
62
+ label = child.metadata[:label]
63
+ end
64
+
65
+ child.metadata.delete :label
66
+ end
67
+ end
68
+
69
+ node.metadata[:label] = label if label
70
+ end
71
+
72
+ def cutoff(cti, targets)
73
+ node = CFG::Node.new(@cfg, @pending_label, @pending_queue, cti, targets)
74
+
75
+ if @cfg.nodes.empty?
76
+ @cfg.entry = node
77
+ end
78
+
79
+ @cfg.nodes.add node
80
+
81
+ @pending_label = nil
82
+ @pending_queue = []
83
+ end
84
+ end
85
+ end
86
+ end