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
@@ -3,7 +3,7 @@ module Furnace::AVM2::ABC
3
3
  instruction 0x24
4
4
 
5
5
  body do
6
- uint8 :value
6
+ int8 :value
7
7
  end
8
8
 
9
9
  type :integer
@@ -3,7 +3,7 @@ module Furnace::AVM2::ABC
3
3
  instruction 0x25
4
4
 
5
5
  body do
6
- vuint30 :value
6
+ vint32 :value
7
7
  end
8
8
 
9
9
  type :integer
@@ -1,5 +1,11 @@
1
1
  module Furnace::AVM2::Binary
2
2
  class Record
3
+ # This jruby compatibility hack allows descendants to use type
4
+ # as a name for the field.
5
+ if defined?(type)
6
+ undef :type
7
+ end
8
+
3
9
  class << self
4
10
  attr_reader :format, :codebase
5
11
 
@@ -180,6 +186,20 @@ module Furnace::AVM2::Binary
180
186
  1
181
187
  end
182
188
 
189
+ # int8
190
+
191
+ def read_int8(io, options)
192
+ io.read(1).unpack("c").at(0)
193
+ end
194
+
195
+ def write_int8(io, value, options)
196
+ io.write([value].pack("c"))
197
+ end
198
+
199
+ def length_int8(value, options)
200
+ 1
201
+ end
202
+
183
203
  # uint16
184
204
 
185
205
  def read_uint16(io, options)
@@ -275,7 +295,7 @@ module Furnace::AVM2::Binary
275
295
 
276
296
  def read_vstring(io, options)
277
297
  length = read_vuint32(io, {})
278
- io.read(length)
298
+ io.read(length).force_encoding('UTF-8')
279
299
  end
280
300
 
281
301
  def write_vstring(io, value, options)
@@ -0,0 +1,4 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ArgumentDeclarationToken < Furnace::Code::NonterminalToken
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ArgumentsToken < Furnace::Code::SeparatedToken
3
+ def text_before
4
+ "("
5
+ end
6
+
7
+ def text_after
8
+ ")"
9
+ end
10
+
11
+ def text_between
12
+ ", "
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ module Furnace::AVM2::Tokens
2
+ class CalleeToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, header, method, body, options={})
4
+ @method, @body = method, body
5
+
6
+ super(origin, [
7
+ *header,
8
+ *declaration(origin, options),
9
+ (Furnace::AVM2::Decompiler.new(@body, options).decompile if @body),
10
+ Furnace::Code::NewlineToken.new(origin, options)
11
+ ], options)
12
+
13
+ if options[:debug_funids]
14
+ @children.unshift \
15
+ CommentToken.new(origin,
16
+ "Function ##{options[:index]}",
17
+ options)
18
+ end
19
+ end
20
+
21
+ def text_after
22
+ if @body.nil?
23
+ ";\n" # no bodies
24
+ end
25
+ end
26
+
27
+ def declaration(origin, options)
28
+ if @method.has_defaults?
29
+ defaults = [nil] * (@method.param_count - @method.default_count) + @method.defaults
30
+ end
31
+
32
+ args = @method.param_count.times.map do |num|
33
+ if @method.has_param_names?
34
+ name = @method.param_names[num]
35
+ else
36
+ name = "param#{num}"
37
+ end
38
+
39
+ if defaults
40
+ default = defaults[num]
41
+ end
42
+
43
+ ArgumentDeclarationToken.new(@origin, [
44
+ VariableNameToken.new(origin, name, options),
45
+ (TypeToken.new(origin, [
46
+ MultinameToken.new(origin, @method.param_types[num], options)
47
+ ], options) if @method.param_types[num]),
48
+ (InitializationToken.new(origin, [
49
+ ImmediateToken.new(origin, default.printable_value, options)
50
+ ], options) if default && default.printable_value)
51
+ ], options)
52
+ end
53
+
54
+ if @method.needs_rest?
55
+ args << ArgumentDeclarationToken.new(origin, [
56
+ RestArgumentToken.new(origin, "rest", options)
57
+ ], options)
58
+ end
59
+
60
+ tokens = []
61
+
62
+ tokens << ArgumentsToken.new(origin, args, options)
63
+ if @method.return_type
64
+ tokens << TypeToken.new(origin, [
65
+ MultinameToken.new(origin, @method.return_type, options)
66
+ ], options)
67
+ end
68
+
69
+ tokens
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,21 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ClassImplementationsToken < Furnace::Code::SeparatedToken
3
+ def initialize(origin, options={})
4
+ super(origin, origin.interfaces.map { |iface|
5
+ MultinameToken.new(origin, iface, options)
6
+ }, options)
7
+ end
8
+
9
+ def text_before
10
+ "implements "
11
+ end
12
+
13
+ def text_between
14
+ ", "
15
+ end
16
+
17
+ def text_after
18
+ " "
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ClassInheritanceToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, options={})
4
+ super(origin, [
5
+ MultinameToken.new(origin, origin.super_name, options)
6
+ ], options)
7
+ end
8
+
9
+ def text_before
10
+ "extends "
11
+ end
12
+
13
+ def text_after
14
+ " "
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ClassNameToken < Furnace::Code::SurroundedToken
3
+ def initialize(origin, options={})
4
+ super(origin, [
5
+ MultinameToken.new(origin, origin.name, options)
6
+ ], options)
7
+ end
8
+
9
+ def text_before
10
+ if origin.interface?
11
+ "interface "
12
+ else
13
+ "class "
14
+ end
15
+ end
16
+
17
+ def text_after
18
+ " "
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'specifiers_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class ClassSpecifiersToken < SpecifiersToken
5
+ def specifiers
6
+ list = super
7
+ list << "final" if @origin.final?
8
+ list << "dynamic" unless @origin.sealed?
9
+ list
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ClassToken < Furnace::Code::NonterminalToken
3
+ include TokenWithTraits
4
+
5
+ def initialize(origin, options={})
6
+ options = options.merge(environment: :class)
7
+
8
+ super(origin, [
9
+ ClassSpecifiersToken.new(origin, options),
10
+ ClassNameToken.new(origin, options),
11
+ (ClassInheritanceToken.new(origin, options) if origin.super_name),
12
+ (ClassImplementationsToken.new(origin, options) if origin.interfaces.any?),
13
+ ScopeToken.new(origin, [
14
+ *transform_traits(origin.klass, options.merge(static: true, instance: origin)),
15
+ (Furnace::Code::NewlineToken.new(origin, options) if origin.klass.traits.any?),
16
+ *transform_traits(origin, options.merge(static: false, instance: origin)),
17
+ ], options)
18
+ ], options)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module Furnace::AVM2::Tokens
2
+ class CommentToken < Furnace::Code::TerminalToken
3
+ def initialize(origin, content, options={})
4
+ super(origin, options)
5
+ @content = content
6
+ end
7
+
8
+ def to_text
9
+ if @options[:commented]
10
+ " #{@content}\n"
11
+ elsif @content.is_a? Furnace::Code::Token
12
+ "/*\n#{@content.to_text.rstrip}\n */\n\n"
13
+ else
14
+ "/* #{@content} */\n"
15
+ end
16
+ end
17
+
18
+ def to_structure(options={})
19
+ structurize "/* ... */", options
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'specifiers_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class ConstructorSpecifiersToken < SpecifiersToken
5
+ def specifiers
6
+ list = []
7
+ list << "static" if @options[:static]
8
+ list << "public"
9
+ list
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'callee_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class ConstructorToken < CalleeToken
5
+ def initialize(origin, options={})
6
+ super(origin, [
7
+ ConstructorSpecifiersToken.new(origin, options),
8
+ FunctionNameToken.new(origin, [
9
+ MultinameToken.new(origin, options[:instance].name, options.merge(omit_ns: true))
10
+ ], options),
11
+ ], origin.initializer, origin.initializer_body,
12
+ options.merge({ index: origin.initializer_idx }))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Furnace::AVM2::Tokens
2
+ class FunctionNameToken < Furnace::Code::SurroundedToken
3
+ def text_before
4
+ case @options[:type]
5
+ when :getter
6
+ "function get "
7
+ when :setter
8
+ "function set "
9
+ else
10
+ "function "
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Furnace::AVM2::Tokens
2
+ class ImportToken < 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
+ "import "
11
+ end
12
+
13
+ def text_after
14
+ ".*;\n"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Furnace::AVM2::Tokens
2
+ class InitializationToken < Furnace::Code::SurroundedToken
3
+ def text_before
4
+ " = "
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'specifiers_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class MethodSpecifiersToken < SpecifiersToken
5
+ def specifiers
6
+ list = []
7
+ list << "final" if @origin.final? && !@options[:static]
8
+ list << "override" if @origin.override?
9
+ list.concat super
10
+ list
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'callee_token'
2
+
3
+ module Furnace::AVM2::Tokens
4
+ class MethodToken < CalleeToken
5
+ def initialize(origin, options={})
6
+ super(origin, [
7
+ MethodSpecifiersToken.new(origin, options),
8
+ FunctionNameToken.new(origin, [
9
+ MultinameToken.new(origin, origin.name, options.merge(omit_ns: true))
10
+ ], options),
11
+ ], origin.data.method, origin.body,
12
+ options.merge({ index: origin.data.method_idx }))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ module Furnace::AVM2::Tokens
2
+ class MultinameToken < Furnace::Code::TerminalToken
3
+ def initialize(origin, multiname, options={})
4
+ super(origin, options)
5
+ @multiname = multiname
6
+ end
7
+
8
+ def to_text
9
+ if @multiname
10
+ if @options[:debug_names]
11
+ debug = "/* #{@multiname.to_astlet.to_sexp} */ "
12
+ end
13
+
14
+ qualified_name = ->(ns) {
15
+ if @options[:ns].include?(ns) || ["*", ""].include?(ns.name.to_s) || @options[:omit_ns]
16
+ "#{debug}#{@multiname.name}"
17
+ else
18
+ "#{debug}#{ns.name}.#{@multiname.name}"
19
+ end
20
+ }
21
+
22
+ case @multiname.kind
23
+ when :QName, :QNameA
24
+ qualified_name.(@multiname.ns)
25
+ when :GenericName
26
+ "#{debug}#{@multiname.name.name}.<#{@multiname.parameters.map(&:name).join}>"
27
+ when :Multiname
28
+ if @multiname.ns_set.ns.count == 1
29
+ qualified_name.(@multiname.ns_set.ns[0])
30
+ else
31
+ "#{debug}%%Multiname"
32
+ end
33
+ else
34
+ "#{debug}%%#{@multiname.kind}"
35
+ end
36
+ else
37
+ "%%nil"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ module Furnace::AVM2::Tokens
2
+ class NamespaceNameToken < Furnace::Code::TerminalToken
3
+ def initialize(origin, name, options={})
4
+ super(origin, options)
5
+ @name = name
6
+ end
7
+
8
+ def to_text
9
+ if @name == "*"
10
+ nil
11
+ else
12
+ @name
13
+ end
14
+ end
15
+ end
16
+ end