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.
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -3
- data/LICENSE +20 -0
- data/bin/furnace-avm2 +36 -6
- data/bin/furnace-avm2-decompiler +141 -0
- data/furnace-avm2.gemspec +2 -2
- data/lib/furnace-avm2.rb +1 -0
- data/lib/furnace-avm2/abc.rb +24 -7
- data/lib/furnace-avm2/abc/metadata/{option_detail.rb → default_value.rb} +5 -1
- data/lib/furnace-avm2/abc/metadata/initializer_body.rb +8 -0
- data/lib/furnace-avm2/abc/metadata/instance_info.rb +19 -4
- data/lib/furnace-avm2/abc/metadata/klass_info.rb +1 -4
- data/lib/furnace-avm2/abc/metadata/method_body_info.rb +27 -10
- data/lib/furnace-avm2/abc/metadata/method_info.rb +20 -13
- data/lib/furnace-avm2/abc/metadata/multiname_info.rb +6 -0
- data/lib/furnace-avm2/abc/metadata/multiname_kind_genericname.rb +8 -5
- data/lib/furnace-avm2/abc/metadata/multiname_kind_multiname.rb +4 -0
- data/lib/furnace-avm2/abc/metadata/multiname_kind_qname.rb +5 -1
- data/lib/furnace-avm2/abc/metadata/record_with_value.rb +90 -0
- data/lib/furnace-avm2/abc/metadata/script_info.rb +25 -1
- data/lib/furnace-avm2/abc/metadata/trait_class.rb +7 -3
- data/lib/furnace-avm2/abc/metadata/trait_info.rb +8 -4
- data/lib/furnace-avm2/abc/metadata/trait_method.rb +4 -0
- data/lib/furnace-avm2/abc/metadata/trait_slot.rb +5 -51
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_lshift.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_rshift.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_urshift.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/debug/as3_debug.rb +15 -0
- data/lib/furnace-avm2/abc/opcodes/debug/as3_debugfile.rb +12 -0
- data/lib/furnace-avm2/abc/opcodes/debug/as3_debugline.rb +12 -0
- data/lib/furnace-avm2/abc/opcodes/function_invocation/as3_callproplex.rb +21 -0
- data/lib/furnace-avm2/abc/opcodes/push_literal/as3_pushbyte.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/push_literal/as3_pushshort.rb +1 -1
- data/lib/furnace-avm2/binary/record.rb +21 -1
- data/lib/furnace-avm2/source/declaration_tokens/argument_declaration_token.rb +4 -0
- data/lib/furnace-avm2/source/declaration_tokens/arguments_token.rb +15 -0
- data/lib/furnace-avm2/source/declaration_tokens/callee_token.rb +72 -0
- data/lib/furnace-avm2/source/declaration_tokens/class_implementations_token.rb +21 -0
- data/lib/furnace-avm2/source/declaration_tokens/class_inheritance_token.rb +17 -0
- data/lib/furnace-avm2/source/declaration_tokens/class_name_token.rb +21 -0
- data/lib/furnace-avm2/source/declaration_tokens/class_specifiers_token.rb +12 -0
- data/lib/furnace-avm2/source/declaration_tokens/class_token.rb +21 -0
- data/lib/furnace-avm2/source/declaration_tokens/comment_token.rb +22 -0
- data/lib/furnace-avm2/source/declaration_tokens/constructor_specifiers_token.rb +12 -0
- data/lib/furnace-avm2/source/declaration_tokens/constructor_token.rb +15 -0
- data/lib/furnace-avm2/source/declaration_tokens/function_name_token.rb +14 -0
- data/lib/furnace-avm2/source/declaration_tokens/import_token.rb +17 -0
- data/lib/furnace-avm2/source/declaration_tokens/initialization_token.rb +7 -0
- data/lib/furnace-avm2/source/declaration_tokens/method_specifiers_token.rb +13 -0
- data/lib/furnace-avm2/source/declaration_tokens/method_token.rb +15 -0
- data/lib/furnace-avm2/source/declaration_tokens/multiname_token.rb +41 -0
- data/lib/furnace-avm2/source/declaration_tokens/namespace_name_token.rb +16 -0
- data/lib/furnace-avm2/source/declaration_tokens/package_name_token.rb +17 -0
- data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +29 -0
- data/lib/furnace-avm2/source/declaration_tokens/rest_argument_token.rb +12 -0
- data/lib/furnace-avm2/source/declaration_tokens/scope_token.rb +23 -0
- data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +14 -0
- data/lib/furnace-avm2/source/declaration_tokens/slot_name_token.rb +17 -0
- data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +23 -0
- data/lib/furnace-avm2/source/declaration_tokens/specifiers_token.rb +32 -0
- data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +51 -0
- data/lib/furnace-avm2/source/declaration_tokens/type_token.rb +7 -0
- data/lib/furnace-avm2/source/decompiler.rb +669 -0
- data/lib/furnace-avm2/source/implementation_tokens/access_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/array_token.rb +17 -0
- data/lib/furnace-avm2/source/implementation_tokens/as_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/assignment_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/binary_operator_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/break_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/call_token.rb +5 -0
- data/lib/furnace-avm2/source/implementation_tokens/continue_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +26 -0
- data/lib/furnace-avm2/source/implementation_tokens/control_transfer_token.rb +20 -0
- data/lib/furnace-avm2/source/implementation_tokens/delete_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/do_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/else_if_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +22 -0
- data/lib/furnace-avm2/source/implementation_tokens/for_each_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/for_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/generic_specializers_token.rb +17 -0
- data/lib/furnace-avm2/source/implementation_tokens/generic_type_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/if_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/immediate_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/immediate_typename_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/in_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/index_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/is_complex.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/is_simple.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/is_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/label_declaration_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/label_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/local_variable_token.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/new_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/object_pair_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/object_token.rb +17 -0
- data/lib/furnace-avm2/source/implementation_tokens/parentheses_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/property_name_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/return_token.rb +16 -0
- data/lib/furnace-avm2/source/implementation_tokens/rtname_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/statement_token.rb +8 -0
- data/lib/furnace-avm2/source/implementation_tokens/super_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/switch_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/ternary_operator_token.rb +13 -0
- data/lib/furnace-avm2/source/implementation_tokens/throw_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/typeof_token.rb +13 -0
- data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/variable_name_token.rb +14 -0
- data/lib/furnace-avm2/source/implementation_tokens/while_token.rb +9 -0
- data/lib/furnace-avm2/transform.rb +5 -1
- data/lib/furnace-avm2/transform/ast_build.rb +22 -7
- data/lib/furnace-avm2/transform/ast_normalize.rb +61 -20
- data/lib/furnace-avm2/transform/cfg_build.rb +86 -0
- data/lib/furnace-avm2/transform/cfg_reduce.rb +249 -0
- data/lib/furnace-avm2/transform/nf_normalize.rb +130 -0
- data/lib/furnace-avm2/version.rb +4 -2
- data/test/basic.as +239 -0
- metadata +110 -38
- data/lib/furnace-avm2/abc/metadata/option_info.rb +0 -5
- data/test/exception.as +0 -29
- data/test/literal.as +0 -5
- data/test/logic.as +0 -23
- data/test/loops.as +0 -30
- data/test/number.as +0 -14
- data/test/switch.as +0 -38
- data/test/ternary.as +0 -22
@@ -0,0 +1,249 @@
|
|
1
|
+
module Furnace::AVM2
|
2
|
+
module Transform
|
3
|
+
class CFGReduce
|
4
|
+
def transform(cfg)
|
5
|
+
@cfg = cfg
|
6
|
+
|
7
|
+
@dom = @cfg.dominators
|
8
|
+
@loops = @cfg.identify_loops
|
9
|
+
|
10
|
+
@visited = Set.new
|
11
|
+
@loop_tails = {}
|
12
|
+
@loop_nonlocal = Set.new
|
13
|
+
|
14
|
+
ast, = extended_block(@cfg.entry)
|
15
|
+
|
16
|
+
ast
|
17
|
+
end
|
18
|
+
|
19
|
+
def extended_block(block, stopgap=nil, loop_stack=[])
|
20
|
+
nodes = []
|
21
|
+
|
22
|
+
while block
|
23
|
+
if @loops.include?(block) && loop_stack.include?(block)
|
24
|
+
# We have just arrived to loop head. Insert `continue'
|
25
|
+
# and exit.
|
26
|
+
check_nonlocal_loop(loop_stack, block) do |params|
|
27
|
+
nodes << AST::Node.new(:continue, params)
|
28
|
+
end
|
29
|
+
break
|
30
|
+
elsif @loop_tails.include?(block) &&
|
31
|
+
loop_stack.include?(@loop_tails[block])
|
32
|
+
# We have just arrived to loop tail. Insert `break'
|
33
|
+
# and exit.
|
34
|
+
loop = @loop_tails[block]
|
35
|
+
check_nonlocal_loop(loop_stack, loop) do |params|
|
36
|
+
nodes << AST::Node.new(:break, params)
|
37
|
+
end
|
38
|
+
break
|
39
|
+
elsif block == stopgap
|
40
|
+
# We have just arrived to a merge point of `if'
|
41
|
+
# contidional. Exit.
|
42
|
+
break
|
43
|
+
end
|
44
|
+
|
45
|
+
if @visited.include? block
|
46
|
+
raise "failsafe: block #{block.label} already visited"
|
47
|
+
elsif block != @cfg.exit
|
48
|
+
@visited.add block
|
49
|
+
end
|
50
|
+
|
51
|
+
block.insns.each do |insn|
|
52
|
+
next if insn == block.cti
|
53
|
+
nodes << insn
|
54
|
+
end
|
55
|
+
|
56
|
+
if block.cti
|
57
|
+
if @loops.include?(block)
|
58
|
+
# we're trapped in a strange loop
|
59
|
+
reverse = !block.cti.children[0]
|
60
|
+
in_root, out_root = block.targets
|
61
|
+
|
62
|
+
# One of the branch targets should reside within
|
63
|
+
# the loop.
|
64
|
+
if !@loops[block].include?(in_root)
|
65
|
+
in_root, out_root = out_root, in_root
|
66
|
+
reverse = !reverse
|
67
|
+
end
|
68
|
+
|
69
|
+
# Mark the loop tail so we could detect `break' and
|
70
|
+
# `continue' statements.
|
71
|
+
@loop_tails[out_root] = block
|
72
|
+
|
73
|
+
# If we reversed the roots or it was a (jump-if false),
|
74
|
+
# then reverse the condition.
|
75
|
+
expr = normalize_cti_expr(block, reverse)
|
76
|
+
|
77
|
+
body = extended_block(in_root, nil, [ block, *loop_stack ])
|
78
|
+
|
79
|
+
# [(label name)]
|
80
|
+
# We first parse the body and then add the label before
|
81
|
+
# the loop body if anything in the body requires that label
|
82
|
+
# to be present.
|
83
|
+
if @loop_nonlocal.include?(block)
|
84
|
+
nodes << AST::Node.new(:label, [ loop_label(block) ])
|
85
|
+
end
|
86
|
+
|
87
|
+
# (while (condition)
|
88
|
+
# (body ...))
|
89
|
+
# (for-in '(var name) # to be done
|
90
|
+
# (body ...))
|
91
|
+
nodes << AST::Node.new(:while, [
|
92
|
+
expr,
|
93
|
+
body
|
94
|
+
])
|
95
|
+
|
96
|
+
block = out_root
|
97
|
+
else
|
98
|
+
# this is an `if', `break' or `continue'
|
99
|
+
reverse = !block.cti.children[0]
|
100
|
+
left_root, right_root = block.targets
|
101
|
+
|
102
|
+
# (if (condition)
|
103
|
+
# (if-true ...)
|
104
|
+
# [(if-false ...)])
|
105
|
+
# Note that you cannot reach expression if-true nor
|
106
|
+
# expression if-false without evaluating condition.
|
107
|
+
# Thus, to go inside the if body, a root has to be
|
108
|
+
# completely dominated by this block--that is, does
|
109
|
+
# not have edges coming to it even from other blocks
|
110
|
+
# dominated by this block.
|
111
|
+
|
112
|
+
# If the left root isn't dominated by block,
|
113
|
+
# then it can't be `if' branch.
|
114
|
+
if !completely_dominated?(left_root, block)
|
115
|
+
left_root, right_root = right_root, left_root
|
116
|
+
reverse = !reverse
|
117
|
+
end
|
118
|
+
|
119
|
+
# If the left root still isn't dominated by block,
|
120
|
+
# then this is not a proper conditional.
|
121
|
+
unless completely_dominated?(left_root, block)
|
122
|
+
raise "not-well-formed if"
|
123
|
+
end
|
124
|
+
|
125
|
+
# If the right root is dominated by this block, which
|
126
|
+
# means that we have an `else' part, and if the condition
|
127
|
+
# is reversed, turn that back. This serves purely aesthetical
|
128
|
+
# purposes and depends on behavior of ASC code generator.
|
129
|
+
if completely_dominated?(right_root, block) && reverse
|
130
|
+
left_root, right_root = right_root, left_root
|
131
|
+
reverse = false
|
132
|
+
end
|
133
|
+
|
134
|
+
# If we reversed the roots or it was a (jump-if false),
|
135
|
+
# then reverse the condition.
|
136
|
+
expr = normalize_cti_expr(block, reverse)
|
137
|
+
|
138
|
+
# Does this conditional have an `else' block?
|
139
|
+
if completely_dominated?(right_root, block)
|
140
|
+
# Yes. Find a merge point.
|
141
|
+
merge = find_merge_point(block, left_root, right_root, loop_stack)
|
142
|
+
|
143
|
+
# If the merge search did not yield a valid node, use
|
144
|
+
# stopgap for the current block to avoid runaway code
|
145
|
+
# synthesis.
|
146
|
+
#
|
147
|
+
# The stopgap block is actually an innermost block from
|
148
|
+
# a stopgap block set implicitly represented by a set of
|
149
|
+
# objects contained in arguments of recursive calls. As
|
150
|
+
# the `if's are fully nested when well-formed, we can only
|
151
|
+
# check for collision with innermost stopgap block.
|
152
|
+
nodes << AST::Node.new(:if, [
|
153
|
+
expr,
|
154
|
+
extended_block(left_root, merge || stopgap, loop_stack),
|
155
|
+
extended_block(right_root, merge || stopgap, loop_stack)
|
156
|
+
])
|
157
|
+
|
158
|
+
block = merge
|
159
|
+
else
|
160
|
+
# No. The "right root" is actually post-if code.
|
161
|
+
nodes << AST::Node.new(:if, [
|
162
|
+
expr,
|
163
|
+
extended_block(left_root, right_root, loop_stack)
|
164
|
+
])
|
165
|
+
|
166
|
+
block = right_root
|
167
|
+
end
|
168
|
+
end
|
169
|
+
elsif block.targets.count == 1
|
170
|
+
block = block.targets.first
|
171
|
+
elsif block == @cfg.exit
|
172
|
+
break
|
173
|
+
else
|
174
|
+
raise "invalid target count (#{block.targets.count})"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
AST::Node.new(:begin, nodes)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Block B is completely dominated by another block D if
|
182
|
+
# it is dominated by D and no edges ever lead to block B
|
183
|
+
# from any other block, including those dominated by D,
|
184
|
+
# but excluding any back edges.
|
185
|
+
def completely_dominated?(block, dominator)
|
186
|
+
if @loops.include?(block)
|
187
|
+
(block.sources - @loops[block].to_a) == [dominator]
|
188
|
+
else
|
189
|
+
block.sources == [dominator]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# A merge point for blocks R (root), L (left) and D (right)
|
194
|
+
# is first block found with BFS starting at {L,D} so that
|
195
|
+
# it is dominated by R, but not L or D.
|
196
|
+
def find_merge_point(root, left, right, loop_stack)
|
197
|
+
worklist = Set[left, right]
|
198
|
+
visited = Set[root, left, right]
|
199
|
+
|
200
|
+
while worklist.any?
|
201
|
+
node = worklist.first
|
202
|
+
worklist.delete node
|
203
|
+
|
204
|
+
visited.add node
|
205
|
+
|
206
|
+
if (@dom[node].include?(root) &&
|
207
|
+
!(@dom[node].include?(left) ||
|
208
|
+
@dom[node].include?(right))) ||
|
209
|
+
loop_stack.include?(node)
|
210
|
+
return node
|
211
|
+
end
|
212
|
+
|
213
|
+
node.targets.each do |target|
|
214
|
+
next if visited.include? target
|
215
|
+
worklist.add target
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# The paths have diverged.
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
|
223
|
+
# Check if the control transfer is nonlocal according to the
|
224
|
+
# innermost loop and adjust @loop_nonlocal accordingly for labels
|
225
|
+
# to be inserted where appropriate.
|
226
|
+
def check_nonlocal_loop(loop_stack, block)
|
227
|
+
if loop_stack.first != block
|
228
|
+
@loop_nonlocal.add block
|
229
|
+
|
230
|
+
yield [loop_label(block)]
|
231
|
+
else
|
232
|
+
yield []
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def loop_label(block)
|
237
|
+
"label#{block.label}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def normalize_cti_expr(block, negate)
|
241
|
+
if negate
|
242
|
+
AST::Node.new(:!, [ block.cti.children[1] ])
|
243
|
+
else
|
244
|
+
block.cti.children[1]
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Furnace::AVM2
|
2
|
+
module Transform
|
3
|
+
class NFNormalize
|
4
|
+
include AST::Visitor
|
5
|
+
|
6
|
+
def transform(nf)
|
7
|
+
@nf = nf.normalize_hierarchy!
|
8
|
+
|
9
|
+
remove_useless_return
|
10
|
+
visit @nf
|
11
|
+
|
12
|
+
@nf
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_useless_return
|
16
|
+
if @nf.children.last.type == :return_void
|
17
|
+
@nf.children.slice! -1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
COERCE_MAP = {
|
22
|
+
:coerce_a => :any,
|
23
|
+
:coerce_b => :bool,
|
24
|
+
:coerce_s => :string,
|
25
|
+
}
|
26
|
+
|
27
|
+
def on_coerce_imm(node)
|
28
|
+
expr, = node.children
|
29
|
+
node.update(:coerce, [
|
30
|
+
COERCE_MAP[node.type],
|
31
|
+
expr
|
32
|
+
])
|
33
|
+
end
|
34
|
+
alias :on_coerce_a :on_coerce_imm
|
35
|
+
alias :on_coerce_b :on_coerce_imm
|
36
|
+
alias :on_coerce_s :on_coerce_imm
|
37
|
+
|
38
|
+
CONVERT_MAP = {
|
39
|
+
:convert_i => :integer,
|
40
|
+
:convert_u => :unsigned,
|
41
|
+
:convert_d => :double,
|
42
|
+
:convert_s => :string,
|
43
|
+
:convert_o => :object,
|
44
|
+
}
|
45
|
+
|
46
|
+
def on_convert_imm(node)
|
47
|
+
expr, = node.children
|
48
|
+
node.update(:convert, [
|
49
|
+
CONVERT_MAP[node.type],
|
50
|
+
expr
|
51
|
+
])
|
52
|
+
end
|
53
|
+
alias :on_convert_i :on_convert_imm
|
54
|
+
alias :on_convert_u :on_convert_imm
|
55
|
+
alias :on_convert_d :on_convert_imm
|
56
|
+
alias :on_convert_s :on_convert_imm
|
57
|
+
alias :on_convert_o :on_convert_imm
|
58
|
+
|
59
|
+
ForInMatcher = AST::Matcher.new do
|
60
|
+
[:while,
|
61
|
+
[:has_next2, capture(:object_reg), capture(:index_reg)],
|
62
|
+
[:begin,
|
63
|
+
[:set_local, capture(:value_reg),
|
64
|
+
[either[:coerce, :convert], capture(:value_type),
|
65
|
+
[capture(:iterator),
|
66
|
+
[:get_local, backref(:object_reg)],
|
67
|
+
[:get_local, backref(:index_reg)]]]],
|
68
|
+
capture_rest(:body)]]
|
69
|
+
end
|
70
|
+
|
71
|
+
ForInIndexMatcher = AST::Matcher.new do
|
72
|
+
[:set_local, backref(:index_reg), [:integer, 0]]
|
73
|
+
end
|
74
|
+
|
75
|
+
ForInObjectMatcher = AST::Matcher.new do
|
76
|
+
[:set_local, backref(:object_reg),
|
77
|
+
[:coerce, :any,
|
78
|
+
capture(:root)]]
|
79
|
+
end
|
80
|
+
|
81
|
+
SuperfluousContinueMatcher = AST::Matcher.new do
|
82
|
+
[:continue]
|
83
|
+
end
|
84
|
+
|
85
|
+
def on_while(node)
|
86
|
+
if captures = ForInMatcher.match(node)
|
87
|
+
parent = node.parent
|
88
|
+
|
89
|
+
case captures[:iterator]
|
90
|
+
when :next_name
|
91
|
+
type = :for_in
|
92
|
+
when :next_value
|
93
|
+
type = :for_each_in
|
94
|
+
else
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
index_node = object_node = nil
|
99
|
+
|
100
|
+
loop_index = parent.children.index(node)
|
101
|
+
parent.children[0..loop_index].reverse_each do |parent_node|
|
102
|
+
if ForInIndexMatcher.match(parent_node, captures)
|
103
|
+
index_node = parent_node
|
104
|
+
elsif ForInObjectMatcher.match(parent_node, captures)
|
105
|
+
object_node = parent_node
|
106
|
+
end
|
107
|
+
|
108
|
+
break if index_node && object_node
|
109
|
+
end
|
110
|
+
|
111
|
+
return unless index_node && object_node
|
112
|
+
|
113
|
+
index_node.update(:remove)
|
114
|
+
object_node.update(:remove)
|
115
|
+
|
116
|
+
if SuperfluousContinueMatcher.match captures[:body].last
|
117
|
+
captures[:body].slice! -1
|
118
|
+
end
|
119
|
+
|
120
|
+
node.update(type, [
|
121
|
+
captures[:value_reg],
|
122
|
+
captures[:value_type],
|
123
|
+
captures[:object_reg],
|
124
|
+
AST::Node.new(:begin, captures[:body])
|
125
|
+
])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/furnace-avm2/version.rb
CHANGED
data/test/basic.as
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
package test {
|
2
|
+
class Literal {
|
3
|
+
function test() {
|
4
|
+
call(1, 1, 200, 200, -1, -50, 32767, 32768, -32760, -500000);
|
5
|
+
}
|
6
|
+
}
|
7
|
+
|
8
|
+
class Arithmetics {
|
9
|
+
function b() {
|
10
|
+
var a:int = 0;
|
11
|
+
var b:int;
|
12
|
+
b = (a += 1);
|
13
|
+
return (b -= (a += 1));
|
14
|
+
}
|
15
|
+
|
16
|
+
function a() {
|
17
|
+
var a:int = 0;
|
18
|
+
var b:int = ++a;
|
19
|
+
var c:int = a++;
|
20
|
+
return a;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
class Logic {
|
25
|
+
function P_a_or_b_p_and_P_c_and_d_p_or_e(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
26
|
+
var v:Boolean = (a || b) && (c && d) || e;
|
27
|
+
away();
|
28
|
+
return v;
|
29
|
+
}
|
30
|
+
|
31
|
+
function a_or_b(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
32
|
+
if(a || b) {
|
33
|
+
yes();
|
34
|
+
} else {
|
35
|
+
no();
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
function a_and_b_and_c(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
40
|
+
return a && (b && c);
|
41
|
+
}
|
42
|
+
|
43
|
+
function P_a_and_b_p_and_c(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
44
|
+
return (a && b) && c;
|
45
|
+
}
|
46
|
+
|
47
|
+
function a_and_b(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
48
|
+
return a && b;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
class Ternary {
|
53
|
+
function hardcore(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
54
|
+
if(((a && b) ? (c || d) && e : b) && (a || b)) {
|
55
|
+
return pow();
|
56
|
+
} else {
|
57
|
+
weeee();
|
58
|
+
}
|
59
|
+
duh();
|
60
|
+
}
|
61
|
+
function q(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
62
|
+
return ((a ? b : c) ? (b ? c : d) : (c ? d : e));
|
63
|
+
}
|
64
|
+
function a_I_b_E_c_I_d_E_e(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
65
|
+
return a ? b : (c ? d : e);
|
66
|
+
}
|
67
|
+
function a_I_b_I_c_E_d_E_e(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
68
|
+
return a ? (b ? c : d) : e;
|
69
|
+
}
|
70
|
+
function a_I_b_E_c(a: Boolean, b:Boolean, c:Boolean, d:Boolean, e:Boolean) : Boolean {
|
71
|
+
return (1 > 2) ? b : c;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
class Conditionals {
|
76
|
+
function els(param1:Object, param2:String, param3:String) : Boolean {
|
77
|
+
var _loc_4:Object = null;
|
78
|
+
if (param1 != null)
|
79
|
+
{
|
80
|
+
_loc_4 = param1[param2];
|
81
|
+
if (_loc_4 != null)
|
82
|
+
{
|
83
|
+
if (_loc_4[param3] != null)
|
84
|
+
{
|
85
|
+
return true;
|
86
|
+
}
|
87
|
+
a();
|
88
|
+
}
|
89
|
+
b();
|
90
|
+
}
|
91
|
+
heys();
|
92
|
+
return false;
|
93
|
+
}
|
94
|
+
|
95
|
+
function els2(param1:Object, param2:String, param3:String) : Boolean {
|
96
|
+
var _loc_4:Object = null;
|
97
|
+
if (param1 != null)
|
98
|
+
{
|
99
|
+
_loc_4 = param1[param2];
|
100
|
+
if (_loc_4 != null)
|
101
|
+
{
|
102
|
+
if (_loc_4[param3] != null)
|
103
|
+
{
|
104
|
+
return true;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
heys();
|
109
|
+
return false;
|
110
|
+
}
|
111
|
+
|
112
|
+
function b(a:Boolean) {
|
113
|
+
if(a)
|
114
|
+
return foo();
|
115
|
+
else
|
116
|
+
return 1;
|
117
|
+
}
|
118
|
+
|
119
|
+
function q(a:Boolean, b:Boolean) {
|
120
|
+
if(b) {
|
121
|
+
if(a) {
|
122
|
+
foo();
|
123
|
+
}
|
124
|
+
}
|
125
|
+
baz();
|
126
|
+
}
|
127
|
+
|
128
|
+
function a(a:Boolean) {
|
129
|
+
baz();
|
130
|
+
if(a) {
|
131
|
+
foge();
|
132
|
+
} else {
|
133
|
+
huga();
|
134
|
+
}
|
135
|
+
piyo();
|
136
|
+
}
|
137
|
+
|
138
|
+
function nested(a:Boolean, b:Boolean) {
|
139
|
+
if(a) {
|
140
|
+
foo();
|
141
|
+
} else {
|
142
|
+
if(b) {
|
143
|
+
bar();
|
144
|
+
} else {
|
145
|
+
baz();
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
class Loops {
|
152
|
+
function e(f:Boolean) {
|
153
|
+
do { f = tttest();
|
154
|
+
} while(f);
|
155
|
+
}
|
156
|
+
|
157
|
+
function d() {
|
158
|
+
while(true) {
|
159
|
+
pow();
|
160
|
+
if(frak()) { a(); break; b(); }
|
161
|
+
weee();
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
function b() {
|
166
|
+
weee();
|
167
|
+
for(var q:int = 1; q > 0; q++) {
|
168
|
+
frak();
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
class Switch {
|
174
|
+
function proper_switch(q:int):Boolean {
|
175
|
+
switch(q) {
|
176
|
+
case 0x10:
|
177
|
+
hoge();
|
178
|
+
break;
|
179
|
+
case "test":
|
180
|
+
fuga();
|
181
|
+
break;
|
182
|
+
case 0x30:
|
183
|
+
piyo();
|
184
|
+
break;
|
185
|
+
case 0x40:
|
186
|
+
fuck();
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
return false;
|
190
|
+
}
|
191
|
+
|
192
|
+
function improper_switch(q:int):Boolean {
|
193
|
+
switch(q) {
|
194
|
+
case 0x10:
|
195
|
+
hoge();
|
196
|
+
case 0x20:
|
197
|
+
fuga();
|
198
|
+
break;
|
199
|
+
case 0x30:
|
200
|
+
piyo();
|
201
|
+
break;
|
202
|
+
default:
|
203
|
+
baz();
|
204
|
+
break;
|
205
|
+
}
|
206
|
+
return false;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
class Exceptions {
|
211
|
+
function c() {
|
212
|
+
try {
|
213
|
+
hoge();
|
214
|
+
} finally {
|
215
|
+
piyo();
|
216
|
+
}
|
217
|
+
}
|
218
|
+
function b() {
|
219
|
+
try {
|
220
|
+
hoge();
|
221
|
+
throw 1;
|
222
|
+
fuga();
|
223
|
+
} catch(e: SecurityError) {
|
224
|
+
piyo(e);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
function a() {
|
228
|
+
try {
|
229
|
+
hoge();
|
230
|
+
throw 1;
|
231
|
+
fuga();
|
232
|
+
} catch(e: SecurityError) {
|
233
|
+
piyo(e);
|
234
|
+
} catch(e: Error) {
|
235
|
+
throw e;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|