furnace-avm2 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +3 -1
- data/bin/furnace-avm2 +27 -13
- data/bin/furnace-avm2-decompiler +14 -4
- data/bin/furnace-avm2-shell +4 -0
- data/furnace-avm2.gemspec +1 -1
- data/lib/furnace-avm2.rb +1 -1
- data/lib/furnace-avm2/abc.rb +3 -0
- data/lib/furnace-avm2/abc/metadata/exception_info.rb +7 -2
- data/lib/furnace-avm2/abc/metadata/method_body_info.rb +4 -2
- data/lib/furnace-avm2/abc/metadata/script_info.rb +8 -3
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_divide.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_equals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_modulo.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_not.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_strictequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic_opcode.rb +5 -0
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvalue.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvoid.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return_opcode.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/opcode.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +4 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_d.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_i.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_o.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_u.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion_opcode.rb +10 -0
- data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +4 -0
- data/lib/furnace-avm2/abc/primitives/record.rb +21 -1
- data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +16 -8
- data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +7 -0
- data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +12 -4
- data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +10 -9
- data/lib/furnace-avm2/source/decompiler.rb +363 -166
- data/lib/furnace-avm2/source/implementation_tokens/case_token.rb +20 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_filter_token.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/closure_token.rb +2 -0
- data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +0 -1
- data/lib/furnace-avm2/source/implementation_tokens/do_while_token.rb +16 -0
- data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +3 -1
- data/lib/furnace-avm2/source/implementation_tokens/finally_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/label_name_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/this_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/try_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +2 -2
- data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +2 -2
- data/lib/furnace-avm2/transform.rb +2 -0
- data/lib/furnace-avm2/transform/ast_build.rb +234 -92
- data/lib/furnace-avm2/transform/ast_normalize.rb +4 -13
- data/lib/furnace-avm2/transform/cfg_build.rb +74 -33
- data/lib/furnace-avm2/transform/cfg_reduce.rb +457 -75
- data/lib/furnace-avm2/transform/nf_normalize.rb +69 -40
- data/lib/furnace-avm2/transform/propagate_constants.rb +49 -0
- data/lib/furnace-avm2/transform/propagate_labels.rb +31 -0
- data/lib/furnace-avm2/version.rb +1 -1
- data/test/basic.as +111 -3
- data/test/switch.as +27 -0
- metadata +907 -313
- data/Gemfile.lock +0 -19
@@ -0,0 +1,20 @@
|
|
1
|
+
module Furnace::AVM2::Tokens
|
2
|
+
class CaseToken < Furnace::Code::SurroundedToken
|
3
|
+
def initialize(origin, value, options)
|
4
|
+
super(origin, [ value ], options)
|
5
|
+
@value = value
|
6
|
+
end
|
7
|
+
|
8
|
+
def text_before
|
9
|
+
if @value
|
10
|
+
"case "
|
11
|
+
else
|
12
|
+
"default"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def text_after
|
17
|
+
":\n"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Furnace::AVM2::Tokens
|
2
|
+
class DoWhileToken < Furnace::Code::NonterminalToken
|
3
|
+
def initialize(origin, body, condition, options={})
|
4
|
+
super(origin, [body, condition], options)
|
5
|
+
@condition, @body = condition, body
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_text
|
9
|
+
"do #{@body.to_text}while(#{@condition.to_text});\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_structure(options={})
|
13
|
+
structurize "do ... while(...)", options
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -2,8 +2,8 @@ module Furnace::AVM2::Tokens
|
|
2
2
|
class UnaryOperatorToken < Furnace::Code::SurroundedToken
|
3
3
|
include IsSimple
|
4
4
|
|
5
|
-
def initialize(origin,
|
6
|
-
super(origin,
|
5
|
+
def initialize(origin, child, operator, options={})
|
6
|
+
super(origin, [ child ], options)
|
7
7
|
@operator = operator
|
8
8
|
end
|
9
9
|
|
@@ -2,8 +2,8 @@ module Furnace::AVM2::Tokens
|
|
2
2
|
class UnaryPostOperatorToken < Furnace::Code::SurroundedToken
|
3
3
|
include IsSimple
|
4
4
|
|
5
|
-
def initialize(origin,
|
6
|
-
super(origin,
|
5
|
+
def initialize(origin, child, operator, options={})
|
6
|
+
super(origin, [ child ], options)
|
7
7
|
@operator = operator
|
8
8
|
end
|
9
9
|
|
@@ -5,6 +5,8 @@ end
|
|
5
5
|
|
6
6
|
require_relative "transform/ast_build"
|
7
7
|
require_relative "transform/ast_normalize"
|
8
|
+
require_relative "transform/propagate_labels"
|
9
|
+
require_relative "transform/propagate_constants"
|
8
10
|
require_relative "transform/cfg_build"
|
9
11
|
require_relative "transform/cfg_reduce"
|
10
12
|
require_relative "transform/nf_normalize"
|
@@ -7,7 +7,15 @@ module Furnace::AVM2
|
|
7
7
|
:if_le, :if_lt, :if_ne, :if_nge, :if_ngt,
|
8
8
|
:if_nle, :if_nlt, :if_strict_eq, :if_strict_ne, :if_true ]
|
9
9
|
|
10
|
-
|
10
|
+
PURE_OPERATORS = [ :integer, :double, :string, :false, :true, :nan, :undefined, :null,
|
11
|
+
:find_property_strict ]
|
12
|
+
|
13
|
+
PRE_POST_OPERATORS = [ :increment, :increment_i, :decrement, :decrement_i ]
|
14
|
+
|
15
|
+
SHORT_ASSIGN_OPERATORS = [ :add, :add_i, :subtract, :subtract_i, :multiply, :multiply_i,
|
16
|
+
:divide, :modulo,
|
17
|
+
:set_local, :set_local_0, :set_local_1, :set_local_2, :set_local_3,
|
18
|
+
:new_catch, :new_activation ]
|
11
19
|
|
12
20
|
def initialize(options)
|
13
21
|
@validate = options[:validate] || false
|
@@ -16,94 +24,196 @@ module Furnace::AVM2
|
|
16
24
|
#@verbose = true
|
17
25
|
end
|
18
26
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
27
|
+
def consume(count)
|
28
|
+
if count == 0
|
29
|
+
[]
|
30
|
+
elsif count <= @stack.size
|
31
|
+
@stack.slice!(-count..-1)
|
32
|
+
else
|
33
|
+
raise "cannot consume #{count}: stack underflow with #{@stack.size}"
|
34
|
+
end
|
35
|
+
end
|
22
36
|
|
23
|
-
|
24
|
-
|
37
|
+
def produce(what)
|
38
|
+
@stack.push what
|
39
|
+
end
|
25
40
|
|
26
|
-
|
27
|
-
|
28
|
-
|
41
|
+
def emit(node)
|
42
|
+
if @verbose
|
43
|
+
puts "emitted:"
|
44
|
+
puts node.to_sexp(1)
|
45
|
+
end
|
29
46
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
47
|
+
@ast.children.push node
|
48
|
+
end
|
49
|
+
|
50
|
+
def unemit
|
51
|
+
@ast.children.delete(-1)
|
52
|
+
end
|
53
|
+
|
54
|
+
def extend_complex_expr(valid_types, expected_depth=nil)
|
55
|
+
expr, current = consume(2)
|
56
|
+
|
57
|
+
if @validate
|
58
|
+
if !valid_types.include?(expr.type)
|
59
|
+
raise "invalid complex expr: #{expr.type} not in #{valid_types}"
|
60
|
+
elsif expected_depth && expr.children.count != expected_depth
|
61
|
+
raise "invalid complex expr: depth #{expr.children.count} != #{expected_depth}"
|
37
62
|
end
|
38
63
|
end
|
39
64
|
|
40
|
-
|
41
|
-
|
42
|
-
|
65
|
+
expr.children << current
|
66
|
+
produce(expr)
|
67
|
+
end
|
43
68
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
69
|
+
def finalize_complex_expr(opcode, worklist, valid_types, expected_depth=nil, wrap_to=nil)
|
70
|
+
while worklist.last == opcode.offset
|
71
|
+
extend_complex_expr(valid_types, expected_depth)
|
72
|
+
|
73
|
+
if wrap_to
|
74
|
+
node, *prepend = *wrap_to
|
75
|
+
|
76
|
+
expr, = consume(1)
|
77
|
+
expr = AST::Node.new(node, [*prepend, expr])
|
78
|
+
produce(expr)
|
48
79
|
end
|
49
80
|
|
50
|
-
|
81
|
+
worklist.pop
|
51
82
|
end
|
83
|
+
end
|
52
84
|
|
53
|
-
|
54
|
-
|
85
|
+
def expand_conditionals
|
86
|
+
expressions = []
|
55
87
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
88
|
+
while @stack.any? && CONDITIONAL_OPERATORS.include?(@stack.last.type)
|
89
|
+
conditional, = consume(1)
|
90
|
+
|
91
|
+
jump_node = AST::Node.new(:jump_if, [
|
92
|
+
true,
|
93
|
+
conditional.metadata[:offset],
|
94
|
+
conditional
|
95
|
+
], label: conditional.metadata[:label])
|
96
|
+
|
97
|
+
expressions.unshift jump_node
|
98
|
+
end
|
63
99
|
|
64
|
-
|
65
|
-
|
100
|
+
expressions.each do |expr|
|
101
|
+
emit(expr)
|
66
102
|
end
|
103
|
+
end
|
67
104
|
|
68
|
-
|
69
|
-
|
70
|
-
|
105
|
+
LocalIncDecInnerMatcher = AST::Matcher.new do
|
106
|
+
[ either[*PRE_POST_OPERATORS],
|
107
|
+
either[
|
108
|
+
[:convert, any,
|
109
|
+
[:get_local, any]],
|
110
|
+
[:get_local, any],
|
111
|
+
]
|
112
|
+
]
|
113
|
+
end
|
71
114
|
|
72
|
-
|
73
|
-
|
115
|
+
def transform(code, body)
|
116
|
+
@stack = []
|
117
|
+
@ast = AST::Node.new(:root)
|
74
118
|
|
75
|
-
|
76
|
-
|
77
|
-
produce.(expr)
|
78
|
-
end
|
119
|
+
dup = nil
|
120
|
+
spurious = 0
|
79
121
|
|
80
|
-
|
81
|
-
|
82
|
-
|
122
|
+
in_shortcut = false
|
123
|
+
shortjump = []
|
124
|
+
ternary = []
|
83
125
|
|
84
|
-
|
85
|
-
|
126
|
+
current_handler = nil
|
127
|
+
current_finally = nil
|
128
|
+
|
129
|
+
finallies = {}
|
130
|
+
body.exceptions.each_with_index do |exception, index|
|
131
|
+
first, second = exception, body.exceptions[index + 1]
|
132
|
+
next unless second
|
133
|
+
|
134
|
+
if first.from_offset == second.from_offset &&
|
135
|
+
second.to_offset > first.to_offset &&
|
136
|
+
first.target_offset > second.from_offset &&
|
137
|
+
first.target_offset < second.to_offset &&
|
138
|
+
first.to.is_a?(ABC::AS3Jump) &&
|
139
|
+
first.to.target.is_a?(ABC::AS3PushByte)
|
140
|
+
entry = first.to.target.next.target
|
141
|
+
epilogue = nil
|
142
|
+
|
143
|
+
cursor = entry.next
|
144
|
+
while cursor
|
145
|
+
if cursor.is_a?(ABC::AS3LookupSwitch) &&
|
146
|
+
cursor.body.default_offset == 8 &&
|
147
|
+
cursor.body.case_count == 0
|
148
|
+
epilogue = cursor
|
149
|
+
break
|
150
|
+
end
|
86
151
|
|
87
|
-
|
88
|
-
|
152
|
+
cursor = cursor.next
|
153
|
+
end
|
89
154
|
|
90
|
-
|
91
|
-
|
155
|
+
raise "cannot find finally epilogue" if epilogue.nil?
|
156
|
+
|
157
|
+
finallies[first.to_offset] = {
|
158
|
+
first_catch: first,
|
159
|
+
second_catch: second,
|
160
|
+
skip_intervals: [ first.target_offset...second.target_offset,
|
161
|
+
(second.target_offset + 4)...entry.offset ],
|
162
|
+
begin_offset: first.to.offset,
|
163
|
+
entry_offset: entry.offset,
|
164
|
+
epilogue_offset: epilogue.offset,
|
165
|
+
end_offset: epilogue.offset + epilogue.byte_length,
|
166
|
+
}
|
92
167
|
end
|
168
|
+
end
|
93
169
|
|
94
|
-
|
170
|
+
exceptions = {}
|
171
|
+
body.exceptions.each_with_index do |exception, index|
|
172
|
+
next if finallies.find { |k,f| f[:first_catch] == exception }
|
173
|
+
exceptions[exception.target_offset] = exception
|
95
174
|
end
|
96
175
|
|
97
176
|
code.each do |opcode|
|
98
177
|
if @verbose
|
99
178
|
puts "================================"
|
100
|
-
puts "stack: #{stack.inspect}"
|
179
|
+
puts "stack: #{@stack.inspect}"
|
101
180
|
puts "shortjump: #{shortjump.inspect} ternary: #{ternary.inspect}"
|
102
181
|
puts "opcode: #{opcode.inspect}"
|
103
182
|
end
|
104
183
|
|
105
|
-
finalize_complex_expr
|
106
|
-
finalize_complex_expr
|
184
|
+
finalize_complex_expr(opcode, ternary, CONDITIONAL_OPERATORS, nil, [:ternary_if, false])
|
185
|
+
finalize_complex_expr(opcode, shortjump, [ :and, :or ], 1)
|
186
|
+
|
187
|
+
if finallies.has_key? opcode.offset
|
188
|
+
current_finally = finallies[opcode.offset]
|
189
|
+
end
|
190
|
+
|
191
|
+
if current_finally
|
192
|
+
if current_finally[:begin_offset] == opcode.offset
|
193
|
+
puts "FINALLY: begin" if @verbose
|
194
|
+
emit(AST::Node.new(:jump, [ current_finally[:end_offset] ], label: opcode.offset))
|
195
|
+
next
|
196
|
+
elsif current_finally[:skip_intervals].find { |si| si.include? opcode.offset }
|
197
|
+
puts "FINALLY: skip" if @verbose
|
198
|
+
next
|
199
|
+
elsif current_finally[:epilogue_offset] == opcode.offset
|
200
|
+
puts "FINALLY: epilogue" if @verbose
|
201
|
+
emit(AST::Node.new(:nop, [], label: opcode.offset))
|
202
|
+
@stack.clear
|
203
|
+
next
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
if exceptions.has_key? opcode.offset
|
208
|
+
exception = exceptions[opcode.offset]
|
209
|
+
|
210
|
+
current_handler = exception
|
211
|
+
if exception.variable
|
212
|
+
produce(AST::Node.new(:exception_variable, [ exception.variable.to_astlet ]))
|
213
|
+
else
|
214
|
+
produce(AST::Node.new(:exception_variable))
|
215
|
+
end
|
216
|
+
end
|
107
217
|
|
108
218
|
if dup == 1 && (opcode.is_a?(ABC::AS3CoerceB) ||
|
109
219
|
opcode.is_a?(ABC::AS3IfTrue) || opcode.is_a?(ABC::AS3IfFalse))
|
@@ -129,21 +239,21 @@ module Furnace::AVM2
|
|
129
239
|
end
|
130
240
|
|
131
241
|
node = AST::Node.new(type, [], label: opcode.offset)
|
132
|
-
node.children = consume
|
133
|
-
produce
|
242
|
+
node.children = consume(1)
|
243
|
+
produce(node)
|
134
244
|
|
135
245
|
shortjump.push opcode.target_offset
|
136
246
|
elsif opcode.is_a?(ABC::AS3Swap)
|
137
|
-
first, second = stack.pop, stack.pop
|
138
|
-
stack.push first, second
|
247
|
+
first, second = @stack.pop, @stack.pop
|
248
|
+
@stack.push first, second
|
139
249
|
elsif opcode.is_a?(ABC::AS3Dup)
|
140
|
-
node = stack.last
|
250
|
+
node = @stack.last
|
141
251
|
|
142
|
-
if
|
252
|
+
if PURE_OPERATORS.include?(node.type) ||
|
143
253
|
(node.type == :get_local && node.children.first == 0)
|
144
254
|
dup_node = node.dup
|
145
255
|
dup_node.metadata[:label] = opcode.offset
|
146
|
-
produce
|
256
|
+
produce(dup_node)
|
147
257
|
else
|
148
258
|
dup ||= 0
|
149
259
|
dup += 1
|
@@ -151,44 +261,68 @@ module Furnace::AVM2
|
|
151
261
|
elsif opcode.is_a?(ABC::AS3Jump)
|
152
262
|
if opcode.body.jump_offset == 0
|
153
263
|
node = AST::Node.new(:nop, [], label: opcode.offset)
|
154
|
-
emit
|
155
|
-
elsif
|
156
|
-
|
264
|
+
emit(node)
|
265
|
+
elsif opcode.target.is_a? ABC::AS3Throw
|
266
|
+
node = AST::Node.new(:throw, [ consume(1) ], label: opcode.offset)
|
267
|
+
emit(node)
|
268
|
+
elsif @stack.any? && !CONDITIONAL_OPERATORS.include?(@stack.last.type)
|
269
|
+
extend_complex_expr(CONDITIONAL_OPERATORS)
|
157
270
|
|
158
271
|
ternary.push opcode.target_offset
|
159
272
|
else
|
160
|
-
expand_conditionals
|
273
|
+
expand_conditionals()
|
161
274
|
|
162
275
|
node = AST::Node.new(opcode.ast_type, opcode.parameters, label: opcode.offset)
|
163
|
-
emit
|
276
|
+
emit(node)
|
164
277
|
end
|
165
278
|
elsif opcode.is_a?(ABC::ControlTransferOpcode)
|
166
279
|
node = AST::Node.new(opcode.ast_type, [], label: opcode.offset)
|
167
280
|
node.metadata[:offset] = opcode.target_offset
|
168
|
-
node.children = consume
|
169
|
-
produce
|
170
|
-
elsif opcode.is_a?(ABC::AS3Kill)
|
171
|
-
# Ignore. SSA will handle that.
|
281
|
+
node.children = consume(opcode.consumes)
|
282
|
+
produce(node)
|
172
283
|
else
|
284
|
+
node = AST::Node.new(opcode.ast_type, [], label: opcode.offset)
|
285
|
+
|
286
|
+
if dup == 1
|
287
|
+
top_opcode, = consume(1)
|
288
|
+
found = true
|
289
|
+
|
290
|
+
if PRE_POST_OPERATORS.include?(top_opcode.type)
|
291
|
+
top_opcode.update(:"pre_#{top_opcode.type}")
|
292
|
+
elsif PRE_POST_OPERATORS.include?(node.type)
|
293
|
+
node.update(:"post_#{node.type}")
|
294
|
+
elsif SHORT_ASSIGN_OPERATORS.include? top_opcode.type
|
295
|
+
# just push it through
|
296
|
+
else
|
297
|
+
found = false
|
298
|
+
end
|
299
|
+
|
300
|
+
if found
|
301
|
+
produce(AST::Node.new(:unemit))
|
302
|
+
dup = false
|
303
|
+
end
|
304
|
+
|
305
|
+
produce(top_opcode)
|
306
|
+
end
|
307
|
+
|
173
308
|
if dup
|
174
309
|
spurious += 1
|
175
310
|
|
176
|
-
save_node = AST::Node.new(:set_local, [ -spurious, *consume
|
177
|
-
|
311
|
+
save_node = AST::Node.new(:set_local, [ -spurious, *consume(1) ])
|
312
|
+
expand_conditionals()
|
313
|
+
emit(save_node)
|
178
314
|
|
179
315
|
(1 + dup).times do
|
180
316
|
load_node = AST::Node.new(:get_local, [ -spurious ])
|
181
|
-
produce
|
317
|
+
produce(load_node)
|
182
318
|
end
|
183
319
|
|
184
320
|
dup = false
|
185
321
|
end
|
186
322
|
|
187
|
-
|
188
|
-
|
189
|
-
parameters = consume.(opcode.consumes)
|
323
|
+
parameters = consume(opcode.consumes)
|
190
324
|
if opcode.consumes_context
|
191
|
-
context = opcode.context(consume
|
325
|
+
context = opcode.context(consume(opcode.consumes_context))
|
192
326
|
end
|
193
327
|
|
194
328
|
node.children.concat context if context
|
@@ -198,23 +332,31 @@ module Furnace::AVM2
|
|
198
332
|
# All opcodes which produce 2 results--that is, dup and swap--
|
199
333
|
# are already handled at the top.
|
200
334
|
if opcode.produces == 0
|
201
|
-
|
202
|
-
|
335
|
+
# This was a fallthrough assignment.
|
336
|
+
if @stack.any? && @stack.last.type == :unemit
|
337
|
+
consume(1) # dump unemit
|
338
|
+
produce(node)
|
339
|
+
next
|
340
|
+
end
|
341
|
+
|
342
|
+
expand_conditionals()
|
343
|
+
|
344
|
+
# Spec does not require stack to be empty upon encountering return.
|
345
|
+
# If there's something left, it should have been here.
|
346
|
+
if opcode.is_a? ABC::FunctionReturnOpcode
|
347
|
+
while @stack.any?
|
348
|
+
emit(*consume(1))
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
emit(node)
|
203
353
|
elsif opcode.produces == 1
|
204
|
-
produce
|
354
|
+
produce(node)
|
205
355
|
end
|
206
356
|
end
|
207
357
|
end
|
208
358
|
|
209
|
-
|
210
|
-
extracted_opcodes = []
|
211
|
-
extract = lambda do |tree|
|
212
|
-
extracted_opcodes.push tree.metadata[:label]
|
213
|
-
tree.select { |c| c.is_a? Node }.each { |c| extract.(c) }
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
ast.normalize_hierarchy!
|
359
|
+
[ @ast.normalize_hierarchy!, body, finallies.values ]
|
218
360
|
end
|
219
361
|
end
|
220
362
|
end
|