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,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,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,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
|