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,17 @@
1
+ module Furnace::AVM2::Tokens
2
+ class PackageNameToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, name, options={})
4
+ super(origin, [
5
+ NamespaceNameToken.new(origin, name, options)
6
+ ], options)
7
+ end
8
+
9
+ def text_before
10
+ "package "
11
+ end
12
+
13
+ def text_after
14
+ " "
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module Furnace::AVM2::Tokens
2
+ class PackageToken < Furnace::Code::NonterminalToken
3
+ def initialize(origin, options={})
4
+ options[:ns] = options[:ns].uniq.reject { |ns|
5
+ ns.name == "" || ns.name == "*"
6
+ }
7
+
8
+ import_ns = options[:ns]
9
+
10
+ if options[:package_name]
11
+ options[:ns] = (options[:ns] + [ options[:package_name].ns ]).uniq
12
+ end
13
+
14
+ super(origin, [
15
+ (PackageNameToken.new(origin, options[:package_name].ns.name, options) if options[:package_name]),
16
+ ScopeToken.new(origin, [
17
+ *import_ns.map { |ns|
18
+ ImportToken.new(origin, ns.name, options)
19
+ },
20
+ (Furnace::Code::NewlineToken.new(origin, options) if import_ns.any?),
21
+ (case options[:package_type]
22
+ when :class; ClassToken.new(origin, options)
23
+ when :script; ScriptToken.new(origin, options)
24
+ end)
25
+ ], options)
26
+ ], options)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ module Furnace::AVM2::Tokens
2
+ class RestArgumentToken < Furnace::Code::TerminalToken
3
+ def initialize(origin, name, options={})
4
+ super(origin, options)
5
+ @name = name
6
+ end
7
+
8
+ def to_text
9
+ "...#{@name}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ScopeToken < Furnace::Code::SurroundedToken
3
+ def text_before
4
+ if @options[:function]
5
+ " {\n"
6
+ else
7
+ "{\n"
8
+ end
9
+ end
10
+
11
+ def text_after
12
+ if @options[:continuation]
13
+ "} "
14
+ else
15
+ "}\n"
16
+ end
17
+ end
18
+
19
+ def to_text
20
+ "#{text_before}#{indent(children.map(&:to_text).join, @options)}#{text_after}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ScriptToken < Furnace::Code::NonterminalToken
3
+ include TokenWithTraits
4
+
5
+ def initialize(origin, options={})
6
+ options = options.merge(environment: :script)
7
+
8
+ super(origin, [
9
+ *transform_traits(origin, options.merge(static: false)),
10
+ Furnace::AVM2::Decompiler.new(origin.initializer_body, options).decompile
11
+ ], options)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Furnace::AVM2::Tokens
2
+ class SlotNameToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, options={})
4
+ super(origin, [
5
+ MultinameToken.new(origin, origin.name, options.merge(omit_ns: true))
6
+ ], options)
7
+ end
8
+
9
+ def text_before
10
+ if @options[:const]
11
+ "const "
12
+ else
13
+ "var "
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module Furnace::AVM2::Tokens
2
+ class SlotToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, options={})
4
+ super(origin, [
5
+ SpecifiersToken.new(origin, options),
6
+ SlotNameToken.new(origin, options),
7
+ (TypeToken.new(origin, [
8
+ MultinameToken.new(origin, origin.type, options)
9
+ ], options) if origin.type)
10
+ ], options)
11
+
12
+ if origin.printable_value
13
+ @children << InitializationToken.new(origin, [
14
+ ImmediateToken.new(origin, origin.printable_value, options)
15
+ ], @options)
16
+ end
17
+ end
18
+
19
+ def text_after
20
+ ";\n"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module Furnace::AVM2::Tokens
2
+ class SpecifiersToken < Furnace::Code::TerminalToken
3
+ def specifiers
4
+ list = []
5
+ list << "static" if @options[:static]
6
+
7
+ if @origin.name.ns.name == ""
8
+ list << "public"
9
+ elsif @options[:instance]
10
+ if @options[:instance].protected_ns? &&
11
+ # protected_ns is not singleton. Why? Fuck me if I know.
12
+ @origin.name.ns.name == @options[:instance].protected_ns.name
13
+ list << "protected"
14
+ elsif @origin.name.ns != @options[:instance].name.ns
15
+ list << "private"
16
+ end
17
+ end
18
+
19
+ list
20
+ end
21
+
22
+ def to_text
23
+ list = specifiers
24
+
25
+ if list.any?
26
+ "#{list.join(" ")} "
27
+ else
28
+ ""
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,51 @@
1
+ module Furnace::AVM2::Tokens
2
+ module TokenWithTraits
3
+ def transform_traits(origin, options)
4
+ tokens = []
5
+
6
+ vars, methods = origin.traits.partition do |trait|
7
+ [:Class, :Slot, :Const].include? trait.kind
8
+ end
9
+
10
+ tokens += vars.map { |trait| transform_trait trait, options }
11
+
12
+ if tokens.any?
13
+ tokens << Furnace::Code::NewlineToken.new(origin, options)
14
+ end
15
+
16
+ if options[:environment] == :class
17
+ if options[:static]
18
+ tokens << CommentToken.new(origin, "Static initializer", options)
19
+ tokens << CommentToken.new(origin,
20
+ ConstructorToken.new(origin, options.merge(commented: true)),
21
+ options)
22
+ else
23
+ tokens << ConstructorToken.new(origin, options)
24
+ end
25
+ end
26
+
27
+ tokens += methods.map { |trait| transform_trait trait, options }
28
+
29
+ tokens
30
+ end
31
+
32
+ def transform_trait(trait, options)
33
+ case trait.kind
34
+ when :Method
35
+ MethodToken.new(trait, options)
36
+ when :Getter
37
+ MethodToken.new(trait, options.merge(type: :getter))
38
+ when :Setter
39
+ MethodToken.new(trait, options.merge(type: :setter))
40
+ when :Slot
41
+ SlotToken.new(trait, options.merge(const: false))
42
+ when :Const
43
+ SlotToken.new(trait, options.merge(const: true))
44
+ when :Class
45
+ # nothing
46
+ else
47
+ CommentToken.new(trait, "%%#{trait.kind}", options)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ module Furnace::AVM2::Tokens
2
+ class TypeToken < Furnace::Code::SurroundedToken
3
+ def text_before
4
+ ":"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,669 @@
1
+ module Furnace::AVM2
2
+ class Decompiler
3
+ include Furnace::AST
4
+ include Furnace::AVM2::ABC
5
+ include Furnace::AVM2::Tokens
6
+
7
+ class ExpressionNotRecognized < StandardError
8
+ attr_reader :context, :opcode
9
+
10
+ def initialize(context, opcode)
11
+ @context, @opcode = context, opcode
12
+ end
13
+ end
14
+
15
+ def initialize(body, options)
16
+ @body, @method, @options = body, body.method, options
17
+ end
18
+
19
+ def decompile
20
+ begin
21
+ @locals = Set.new([0]) + (1..@method.param_count).to_a
22
+
23
+ @nf = @body.code_to_nf
24
+
25
+ stmt_block @nf, function: true
26
+ rescue Exception => e
27
+ comment = "'Ouch!' cried I, then died.\n" +
28
+ "#{e.class}: #{e.message}\n" +
29
+ "#{e.backtrace[0..5].map { |l| " #{l}\n"}.join}" +
30
+ " ... et cetera\n"
31
+
32
+ token(ScopeToken, [
33
+ token(CommentToken, comment)
34
+ ], function: true)
35
+ end
36
+ end
37
+
38
+ # Statements
39
+
40
+ Prologue = Matcher.new do
41
+ [:push_scope,
42
+ [:get_local, 0]]
43
+ end
44
+
45
+ def stmt_block(block, options={})
46
+ nodes = []
47
+
48
+ block.children.each do |opcode|
49
+ if (opcode.type == :push_scope && Prologue.match(opcode))
50
+ # Ignore these
51
+ next
52
+ end
53
+
54
+ case opcode.type
55
+ when :if
56
+ condition, if_true, if_false = opcode.children
57
+
58
+ nodes << token(IfToken, handle_expression(condition),
59
+ stmt_block(if_true, continuation: !if_false.nil?))
60
+ nodes << token(ElseToken,
61
+ stmt_block(if_false)) if if_false
62
+
63
+ when :label
64
+ name, = opcode.children
65
+
66
+ nodes << token(LabelDeclarationToken, name)
67
+
68
+ when :while
69
+ condition, body = opcode.children
70
+
71
+ nodes << token(WhileToken, handle_expression(condition),
72
+ stmt_block(body))
73
+
74
+ when :for_in, :for_each_in
75
+ value_reg, value_type, object_reg, body = opcode.children
76
+
77
+ @locals.add(value_reg)
78
+
79
+ if opcode.type == :for_in
80
+ klass = ForToken
81
+ elsif opcode.type == :for_each_in
82
+ klass = ForEachToken
83
+ end
84
+
85
+ nodes << token(klass,
86
+ token(InToken, [
87
+ token(LocalVariableToken, [
88
+ token(VariableNameToken, local_name(value_reg)),
89
+ type_token(value_type)
90
+ ]),
91
+ token(VariableNameToken, local_name(object_reg)),
92
+ ]),
93
+ stmt_block(body))
94
+
95
+ when :break
96
+ nodes << token(ReturnToken, exprs(opcode.children))
97
+
98
+ when :continue
99
+ nodes << token(ContinueToken, exprs(opcode.children))
100
+
101
+ when :throw
102
+ nodes << token(ThrowToken, exprs(opcode.children))
103
+
104
+ when :return_value, :return_void
105
+ nodes << token(ReturnToken, exprs(opcode.children))
106
+
107
+ else
108
+ node = handle_expression(opcode)
109
+ node = token(StatementToken, [node])
110
+ nodes << node
111
+ end
112
+ end
113
+
114
+ rescue ExpressionNotRecognized => e
115
+ comment = "Well, this is embarassing.\n\n" +
116
+ "Expression recognizer failed at:\n" +
117
+ "#{e.opcode.inspect}\n"
118
+
119
+ if e.context != e.opcode
120
+ comment << "\nOpcode at the top of stack:\n" +
121
+ "#{e.context.inspect}\n"
122
+ end
123
+
124
+ nodes << CommentToken.new(@body, comment, @options)
125
+
126
+ ensure
127
+ if $!.nil? || $!.is_a?(ExpressionNotRecognized)
128
+ return token(ScopeToken, nodes,
129
+ continuation: options[:continuation],
130
+ function: options[:function])
131
+ end
132
+ end
133
+
134
+ # Expressions
135
+
136
+ def handle_expression(opcode)
137
+ expression(opcode)
138
+ rescue ExpressionNotRecognized => e
139
+ raise ExpressionNotRecognized.new(opcode, e.opcode)
140
+ end
141
+
142
+ def expression(opcode)
143
+ handler = :"expr_#{opcode.type}"
144
+ if respond_to?(handler) && node = send(handler, opcode)
145
+ node
146
+ else
147
+ raise ExpressionNotRecognized.new(nil, opcode)
148
+ end
149
+ end
150
+ alias :expr :expression
151
+
152
+ def expressions(opcodes)
153
+ opcodes.map do |opcode|
154
+ expression opcode
155
+ end
156
+ end
157
+ alias :exprs :expressions
158
+
159
+ ## Immediates
160
+
161
+ def expr_imm(opcode)
162
+ case opcode.type
163
+ when :integer, :double
164
+ token(ImmediateToken, *opcode.children)
165
+ when :null, :undefined, :true, :false
166
+ token(ImmediateToken, opcode.type)
167
+ end
168
+ end
169
+ alias :expr_integer :expr_imm
170
+ alias :expr_double :expr_imm
171
+ alias :expr_null :expr_imm
172
+ alias :expr_undefined :expr_imm
173
+ alias :expr_true :expr_imm
174
+ alias :expr_false :expr_imm
175
+
176
+ def expr_string(opcode)
177
+ string, = opcode.children
178
+ token(ImmediateToken, string.inspect)
179
+ end
180
+
181
+ def expr_nan(opcode)
182
+ token(ImmediateToken, "NaN")
183
+ end
184
+
185
+ def expr_new_array(opcode)
186
+ token(ArrayToken, exprs(opcode.children))
187
+ end
188
+
189
+ def expr_new_object(opcode)
190
+ token(ObjectToken, opcode.children.
191
+ each_slice(2).map do |key, value|
192
+ token(ObjectPairToken, [
193
+ expr(key),
194
+ expr(value)
195
+ ])
196
+ end)
197
+ end
198
+
199
+ ## Locals
200
+
201
+ def local_name(index)
202
+ if index < 0
203
+ "sp#{-index}"
204
+ elsif index == 0
205
+ "this"
206
+ elsif index <= @method.param_count
207
+ if @method.has_param_names?
208
+ @method.param_names[index - 1]
209
+ else
210
+ "param#{index - 1}"
211
+ end
212
+ else
213
+ "local#{index - @method.param_count - 1}"
214
+ end
215
+ end
216
+
217
+ def expr_get_local(opcode)
218
+ index, = opcode.children
219
+ token(VariableNameToken, local_name(index))
220
+ end
221
+
222
+ IMMEDIATE_TYPE_MAP = {
223
+ :any => '*',
224
+ :integer => 'int',
225
+ :unsigned => 'uint',
226
+ :string => 'String',
227
+ :double => 'Number',
228
+ :object => 'Object',
229
+ :bool => 'Boolean',
230
+ :true => 'Boolean',
231
+ :false => 'Boolean',
232
+ }
233
+
234
+ def type_token(type)
235
+ if IMMEDIATE_TYPE_MAP.include?(type)
236
+ token(TypeToken, [
237
+ token(ImmediateTypenameToken, IMMEDIATE_TYPE_MAP[type])
238
+ ])
239
+ else
240
+ token(TypeToken, [
241
+ token(MultinameToken, type.metadata[:origin])
242
+ ])
243
+ end
244
+ end
245
+
246
+ def expr_set_var(name, value, declare)
247
+ if IMMEDIATE_TYPE_MAP.include?(value.type)
248
+ inside = value
249
+ type = token(TypeToken, [
250
+ token(ImmediateTypenameToken, IMMEDIATE_TYPE_MAP[value.type])
251
+ ])
252
+ elsif value.type == :coerce || value.type == :convert
253
+ inside_type, inside = value.children
254
+ type = type_token(inside_type)
255
+ else
256
+ inside = value
257
+ type = nil
258
+ end
259
+
260
+ if declare
261
+ token(LocalVariableToken, [
262
+ token(VariableNameToken, name),
263
+ type,
264
+ token(InitializationToken, [
265
+ expr(inside)
266
+ ])
267
+ ])
268
+ else
269
+ token(AssignmentToken, [
270
+ token(VariableNameToken, name),
271
+ expr(inside)
272
+ ])
273
+ end
274
+ end
275
+
276
+ def expr_set_local(opcode)
277
+ index, value = opcode.children
278
+
279
+ expr_set_var(local_name(index), value, !@locals.include?(index))
280
+ ensure
281
+ @locals << index
282
+ end
283
+
284
+ ## Arithmetics
285
+
286
+ INPLACE_OPERATOR_MAP = {
287
+ :inc_local => :"++",
288
+ :inc_local_i => :"++",
289
+ :dec_local => :"--",
290
+ :dec_local_i => :"--",
291
+ }
292
+
293
+ def expr_inplace_arithmetic(opcode)
294
+ token(UnaryPostOperatorToken, [
295
+ token(VariableNameToken, local_name(*opcode.children)),
296
+ ], INPLACE_OPERATOR_MAP[opcode.type])
297
+ end
298
+
299
+ alias :expr_inc_local :expr_inplace_arithmetic
300
+ alias :expr_inc_local_i :expr_inplace_arithmetic
301
+ alias :expr_dec_local :expr_inplace_arithmetic
302
+ alias :expr_dec_local_i :expr_inplace_arithmetic
303
+
304
+ PSEUDO_OPERATOR_MAP = {
305
+ :increment => [:"+", 1],
306
+ :increment_i => [:"+", 1],
307
+ :decrement => [:"-", 1],
308
+ :decrement_i => [:"-", 1],
309
+ }
310
+
311
+ def expr_pseudo_arithmetic(opcode)
312
+ inside = expr(*opcode.children)
313
+ inside = token(ParenthesesToken, [inside]) if inside.complex?
314
+
315
+ operator, other_value = PSEUDO_OPERATOR_MAP[opcode.type]
316
+ token(BinaryOperatorToken, [
317
+ inside,
318
+ token(ImmediateToken, other_value)
319
+ ], operator)
320
+ end
321
+
322
+ alias :expr_increment :expr_pseudo_arithmetic
323
+ alias :expr_increment_i :expr_pseudo_arithmetic
324
+ alias :expr_decrement :expr_pseudo_arithmetic
325
+ alias :expr_decrement_i :expr_pseudo_arithmetic
326
+
327
+ OPERATOR_MAP = {
328
+ :and => :"&&",
329
+ :or => :"||",
330
+
331
+ :add => :"+",
332
+ :add_i => :"+",
333
+ :subtract => :"-",
334
+ :subtract_i => :"-",
335
+ :multiply => :"*",
336
+ :multiply_i => :"*",
337
+ :divide => :"/",
338
+ :modulo => :"%",
339
+ :multiply => :"*",
340
+ :negate => :"-",
341
+ :negate_i => :"-",
342
+
343
+ :! => :!,
344
+ :> => :>,
345
+ :>= => :>=,
346
+ :< => :<,
347
+ :<= => :<=,
348
+ :== => :==,
349
+ :=== => :===,
350
+ :!= => :!=,
351
+ :"!==" => :"!==",
352
+
353
+ :bit_and => :"&",
354
+ :bit_or => :"|",
355
+ :bit_xor => :"^",
356
+ :bit_not => :"~",
357
+ :lshift => :"<<",
358
+ :rshift => :">>",
359
+ :urshift => :">>>",
360
+ }
361
+
362
+ def expr_arithmetic(opcode)
363
+ if OPERATOR_MAP.include?(opcode.type)
364
+ insides = parenthesize_each(exprs(opcode.children))
365
+
366
+ if insides.count == 1
367
+ token(UnaryOperatorToken, insides, OPERATOR_MAP[opcode.type])
368
+ elsif insides.count == 2
369
+ token(BinaryOperatorToken, insides, OPERATOR_MAP[opcode.type])
370
+ else
371
+ token(CommentToken, "Unexpected #{insides.count}-ary operator")
372
+ end
373
+ end
374
+ end
375
+
376
+ alias :expr_and :expr_arithmetic
377
+ alias :expr_or :expr_arithmetic
378
+
379
+ alias :expr_add :expr_arithmetic
380
+ alias :expr_add_i :expr_arithmetic
381
+ alias :expr_subtract :expr_arithmetic
382
+ alias :expr_subtract_i :expr_arithmetic
383
+ alias :expr_multiply :expr_arithmetic
384
+ alias :expr_multiply_i :expr_arithmetic
385
+ alias :expr_divide :expr_arithmetic
386
+ alias :expr_modulo :expr_arithmetic
387
+ alias :expr_negate :expr_arithmetic
388
+
389
+ alias :expr_! :expr_arithmetic
390
+ alias :"expr_>" :expr_arithmetic
391
+ alias :"expr_>=" :expr_arithmetic
392
+ alias :"expr_<" :expr_arithmetic
393
+ alias :"expr_<=" :expr_arithmetic
394
+ alias :"expr_==" :expr_arithmetic
395
+ alias :"expr_===" :expr_arithmetic
396
+ alias :"expr_!=" :expr_arithmetic
397
+ alias :"expr_!==" :expr_arithmetic
398
+
399
+ alias :expr_bit_and :expr_arithmetic
400
+ alias :expr_bit_or :expr_arithmetic
401
+ alias :expr_bit_xor :expr_arithmetic
402
+ alias :expr_bit_not :expr_arithmetic
403
+ alias :expr_lshift :expr_arithmetic
404
+ alias :expr_rshift :expr_arithmetic
405
+ alias :expr_urshift :expr_arithmetic
406
+
407
+ def expr_in(opcode)
408
+ token(InToken, parenthesize_each(exprs(opcode.children)))
409
+ end
410
+
411
+ ## Properties and objects
412
+
413
+ This = Matcher.new do
414
+ [:get_local, 0]
415
+ end
416
+
417
+ PropertyGlobal = Matcher.new do
418
+ [any,
419
+ [either[:find_property, :find_property_strict],
420
+ capture(:multiname)],
421
+ backref(:multiname),
422
+ capture_rest(:arguments)]
423
+ end
424
+
425
+ def expr_get_lex(opcode)
426
+ multiname, = opcode.children
427
+ get_name(nil, multiname)
428
+ end
429
+
430
+ def expr_get_property(opcode)
431
+ if captures = PropertyGlobal.match(opcode)
432
+ get_name(nil, captures[:multiname])
433
+ else
434
+ subject, multiname, = opcode.children
435
+ get_name(expr(subject), multiname)
436
+ end
437
+ end
438
+
439
+ def expr_get_super(opcode)
440
+ subject, multiname, = opcode.children
441
+ if This.match subject
442
+ get_name(token(SuperToken), multiname)
443
+ end
444
+ end
445
+
446
+ def expr_set_property(opcode)
447
+ if captures = PropertyGlobal.match(opcode)
448
+ token(AssignmentToken, [
449
+ get_name(nil, captures[:multiname]),
450
+ expr(*captures[:arguments])
451
+ ])
452
+ else
453
+ subject, multiname, value, = opcode.children
454
+ token(AssignmentToken, [
455
+ get_name(expr(subject), multiname),
456
+ expr(value)
457
+ ])
458
+ end
459
+ end
460
+ alias :expr_init_property :expr_set_property
461
+
462
+ def expr_set_super(opcode)
463
+ subject, multiname, value = opcode.children
464
+ if This.match subject
465
+ token(AssignmentToken, [
466
+ get_name(token(SuperToken), multiname),
467
+ expr(value)
468
+ ])
469
+ end
470
+ end
471
+
472
+ def expr_delete_property(opcode)
473
+ subject, multiname, = opcode.children
474
+ token(DeleteToken, [get_name(expr(subject), multiname)])
475
+ end
476
+
477
+ def expr_do_property(opcode, klass)
478
+ if captures = PropertyGlobal.match(opcode)
479
+ token(klass, [
480
+ get_name(nil, captures[:multiname]),
481
+ token(ArgumentsToken, exprs(captures[:arguments]))
482
+ ])
483
+ else
484
+ subject, multiname, *args = opcode.children
485
+ token(klass, [
486
+ get_name(expr(subject), multiname),
487
+ token(ArgumentsToken, exprs(args))
488
+ ])
489
+ end
490
+ end
491
+
492
+ def expr_call_property(opcode)
493
+ expr_do_property(opcode, CallToken)
494
+ end
495
+
496
+ def expr_call_super(opcode)
497
+ subject, multiname, *args = opcode.children
498
+ if This.match subject
499
+ token(CallToken, [
500
+ get_name(token(SuperToken), multiname),
501
+ token(ArgumentsToken, exprs(args))
502
+ ])
503
+ end
504
+ end
505
+
506
+ ## Object creation
507
+
508
+ def expr_construct_property(opcode)
509
+ expr_do_property(opcode, NewToken)
510
+ end
511
+
512
+ def expr_construct(opcode)
513
+ type, *args = opcode.children
514
+ token(NewToken, [
515
+ parenthesize(expr(type)),
516
+ token(ArgumentsToken, exprs(args))
517
+ ])
518
+ end
519
+
520
+ def expr_construct_super(opcode)
521
+ subject, *args = opcode.children
522
+ if This.match subject
523
+ token(CallToken, [
524
+ token(SuperToken),
525
+ token(ArgumentsToken, exprs(args))
526
+ ])
527
+ end
528
+ end
529
+
530
+ ## Control flow
531
+
532
+ def expr_ternary(opcode)
533
+ token(TernaryOperatorToken, parenthesize_each(exprs(opcode.children)))
534
+ end
535
+
536
+ # See /src/java/macromedia/asc/semantics/CodeGenerator.java
537
+ # If this looks stupid to you, that's because it IS stupid.
538
+ CallThisGlobal = Matcher.new do
539
+ [:get_global_scope]
540
+ end
541
+
542
+ CallThisLocal = Matcher.new do
543
+ [ either[:get_scope_object, :get_local], 0 ]
544
+ end
545
+
546
+ def expr_call(opcode)
547
+ subject, this, *args = opcode.children
548
+
549
+ subject_token = token(AccessToken, [
550
+ parenthesize(expr(subject)),
551
+ token(PropertyNameToken, "call")
552
+ ])
553
+
554
+ if CallThisGlobal.match(this) || CallThisLocal.match(this)
555
+ # FUCK YOU!
556
+ token(CallToken, [
557
+ subject_token,
558
+ token(ArgumentsToken, [
559
+ token(VariableNameToken, "this"),
560
+ *exprs(args)
561
+ ])
562
+ ])
563
+ else
564
+ token(CallToken, [
565
+ subject_token,
566
+ token(ArgumentsToken, exprs([
567
+ this,
568
+ *args
569
+ ]))
570
+ ])
571
+ end
572
+ end
573
+
574
+ ## Types
575
+
576
+ def expr_as_type_late(opcode)
577
+ token(AsToken, parenthesize_each(exprs(opcode.children)))
578
+ end
579
+
580
+ def expr_is_type_late(opcode)
581
+ token(IsToken, parenthesize_each(exprs(opcode.children)))
582
+ end
583
+
584
+ def expr_type_of(opcode)
585
+ token(TypeOfToken, exprs(opcode.children))
586
+ end
587
+
588
+ def expr_apply_type(opcode)
589
+ base, *args = opcode.children
590
+ token(GenericTypeToken, [
591
+ expr(base),
592
+ token(GenericSpecializersToken,
593
+ exprs(args))
594
+ ])
595
+ end
596
+
597
+ def expr_coerce(opcode)
598
+ typename, subject, = opcode.children
599
+ expr(subject)
600
+ end
601
+
602
+ def expr_convert(opcode)
603
+ type, subject = opcode.children
604
+ token(CallToken, [
605
+ token(ImmediateTypenameToken, IMMEDIATE_TYPE_MAP[type]),
606
+ token(ArgumentsToken, [
607
+ expr(subject)
608
+ ])
609
+ ])
610
+ end
611
+
612
+ private
613
+
614
+ def token(klass, *args)
615
+ if args.last.is_a? Hash
616
+ options = @options.merge(args.pop)
617
+ else
618
+ options = @options
619
+ end
620
+
621
+ klass.new(@body, *args, options)
622
+ end
623
+
624
+ def get_name(subject, multiname)
625
+ origin = multiname.metadata[:origin]
626
+ case origin.kind
627
+ when :QName, :QNameA, :Multiname, :MultinameA
628
+ if subject
629
+ token(AccessToken, [
630
+ parenthesize(subject),
631
+ token(PropertyNameToken, origin.name)
632
+ ])
633
+ else
634
+ token(PropertyNameToken, origin.name)
635
+ end
636
+ when :MultinameL, :MultinameLA
637
+ if subject
638
+ token(IndexToken, [
639
+ parenthesize(subject),
640
+ expr(multiname.children.last)
641
+ ])
642
+ else
643
+ token(CommentToken, "%%type #{origin} with no subject")
644
+ end
645
+ when :RTQName, :RTQNameA
646
+ token(RTNameToken, [
647
+ parenthesize(expr(multiname.children.first)),
648
+ token(PropertyNameToken, origin.name)
649
+ ])
650
+ else
651
+ token(CommentToken, "%%type #{origin}")
652
+ end
653
+ end
654
+
655
+ def parenthesize(what)
656
+ if what.complex?
657
+ token(ParenthesesToken, [what])
658
+ else
659
+ what
660
+ end
661
+ end
662
+
663
+ def parenthesize_each(what)
664
+ what.map do |inside|
665
+ parenthesize(inside)
666
+ end
667
+ end
668
+ end
669
+ end