furnace-avm2 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +2 -2
- data/bin/furnace-avm2 +5 -1
- data/bin/furnace-avm2-decompiler +80 -13
- data/bin/furnace-avm2-shell +32 -0
- data/furnace-avm2.gemspec +1 -1
- data/lib/furnace-avm2/abc.rb +1 -7
- data/lib/furnace-avm2/abc/metadata/file.rb +65 -0
- data/lib/furnace-avm2/abc/metadata/instance_info.rb +17 -11
- data/lib/furnace-avm2/abc/metadata/metadata_info.rb +14 -4
- data/lib/furnace-avm2/abc/metadata/method_body_info.rb +11 -3
- data/lib/furnace-avm2/abc/metadata/method_info.rb +3 -4
- data/lib/furnace-avm2/abc/metadata/multiname_kind_genericname.rb +3 -2
- data/lib/furnace-avm2/abc/metadata/multiname_kind_multiname.rb +11 -2
- data/lib/furnace-avm2/abc/metadata/multiname_kind_multinamel.rb +4 -0
- data/lib/furnace-avm2/abc/metadata/multiname_kind_qname.rb +10 -2
- data/lib/furnace-avm2/abc/metadata/multiname_kind_rtqname.rb +4 -0
- data/lib/furnace-avm2/abc/metadata/multiname_kind_rtqnamel.rb +4 -0
- data/lib/furnace-avm2/abc/metadata/script_info.rb +11 -7
- data/lib/furnace-avm2/abc/metadata/trait_class.rb +2 -2
- data/lib/furnace-avm2/abc/metadata/trait_info.rb +1 -1
- data/lib/furnace-avm2/abc/metadata/trait_method.rb +4 -3
- data/lib/furnace-avm2/abc/metadata/trait_slot.rb +2 -2
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_extend_1.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_extend_16.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_extend_8.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_load_float32.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_load_float64.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_load_int16.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_load_int32.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_load_int8.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_store_float32.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_store_float64.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_store_int16.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_store_int32.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/alchemy/alchemy_store_int8.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_divide.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_equals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_modulo.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_not.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_strictequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_bitand.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_bitnot.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_bitor.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/bitwise/as3_bitxor.rb +1 -1
- 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/control_transfer/as3_lookupswitch.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/control_transfer_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/exception/as3_newcatch.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_invocation/as3_callproplex.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/function_invocation_opcode.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvalue.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvoid.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/generic/as3_escxattr.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/generic/as3_escxelem.rb +8 -0
- data/lib/furnace-avm2/abc/opcodes/generic/as3_getlex.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/object_manipulation/as3_newfunction.rb +5 -1
- data/lib/furnace-avm2/abc/opcodes/property/as3_constructprop.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/property_opcode.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/push_literal/as3_pushuint.rb +11 -0
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_applytype.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce.rb +5 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_d.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_i.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_o.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_s.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_u.rb +1 -1
- data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +35 -23
- data/lib/furnace-avm2/abc/primitives/record.rb +11 -0
- data/lib/furnace-avm2/binary/record.rb +9 -7
- data/lib/furnace-avm2/source/declaration_tokens/callee_token.rb +8 -5
- data/lib/furnace-avm2/source/declaration_tokens/class_implementations_token.rb +5 -1
- data/lib/furnace-avm2/source/declaration_tokens/class_specifiers_token.rb +2 -1
- data/lib/furnace-avm2/source/declaration_tokens/class_token.rb +5 -1
- data/lib/furnace-avm2/source/declaration_tokens/metadata_token.rb +25 -0
- data/lib/furnace-avm2/source/declaration_tokens/method_specifiers_token.rb +4 -3
- data/lib/furnace-avm2/source/declaration_tokens/method_token.rb +1 -0
- data/lib/furnace-avm2/source/declaration_tokens/multiname_token.rb +28 -13
- data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +9 -8
- data/lib/furnace-avm2/source/declaration_tokens/scope_token.rb +2 -0
- data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +4 -1
- data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +1 -0
- data/lib/furnace-avm2/source/declaration_tokens/specifiers_token.rb +11 -9
- data/lib/furnace-avm2/source/decompiler.rb +372 -101
- data/lib/furnace-avm2/source/implementation_tokens/closure_name_token.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/closure_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/instance_of_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/supplementary_comment_token.rb +16 -0
- data/lib/furnace-avm2/source/implementation_tokens/xml_literal_token.rb +14 -0
- data/lib/furnace-avm2/transform/ast_build.rb +3 -2
- data/lib/furnace-avm2/transform/ast_normalize.rb +39 -7
- data/lib/furnace-avm2/transform/cfg_build.rb +16 -6
- data/lib/furnace-avm2/transform/cfg_reduce.rb +4 -3
- data/lib/furnace-avm2/transform/nf_normalize.rb +15 -7
- data/lib/furnace-avm2/version.rb +1 -1
- data/test/basic.as +15 -0
- data/test/global.as +11 -0
- metadata +46 -29
- data/bin/furnace-avm2-benchmark +0 -38
- data/lib/furnace-avm2/abc/opcodes/arithmetic_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/bitwise_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/exception_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/function_return_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/object_manipulation_opcode.rb +0 -4
- data/lib/furnace-avm2/abc/opcodes/type_conversion_opcode.rb +0 -4
@@ -6,11 +6,13 @@ module Furnace::AVM2::Tokens
|
|
6
6
|
super(origin, [
|
7
7
|
*header,
|
8
8
|
*declaration(origin, options),
|
9
|
-
(Furnace::AVM2::Decompiler.new(@body,
|
10
|
-
|
9
|
+
(Furnace::AVM2::Decompiler.new(@body,
|
10
|
+
options).decompile if @body),
|
11
|
+
(Furnace::Code::NewlineToken.new(origin,
|
12
|
+
options) if @body && !options[:closure])
|
11
13
|
], options)
|
12
14
|
|
13
|
-
if options[:debug_funids]
|
15
|
+
if options[:debug_funids] && !options[:closure]
|
14
16
|
@children.unshift \
|
15
17
|
CommentToken.new(origin,
|
16
18
|
"Function ##{options[:index]}",
|
@@ -53,14 +55,15 @@ module Furnace::AVM2::Tokens
|
|
53
55
|
|
54
56
|
if @method.needs_rest?
|
55
57
|
args << ArgumentDeclarationToken.new(origin, [
|
56
|
-
RestArgumentToken.new(origin, "
|
58
|
+
RestArgumentToken.new(origin, "local0", options)
|
57
59
|
], options)
|
58
60
|
end
|
59
61
|
|
60
62
|
tokens = []
|
61
63
|
|
62
64
|
tokens << ArgumentsToken.new(origin, args, options)
|
63
|
-
|
65
|
+
|
66
|
+
if @method.return_type && !options[:closure]
|
64
67
|
tokens << TypeToken.new(origin, [
|
65
68
|
MultinameToken.new(origin, @method.return_type, options)
|
66
69
|
], options)
|
@@ -3,9 +3,10 @@ require_relative 'specifiers_token'
|
|
3
3
|
module Furnace::AVM2::Tokens
|
4
4
|
class ClassSpecifiersToken < SpecifiersToken
|
5
5
|
def specifiers
|
6
|
-
list =
|
6
|
+
list = []
|
7
7
|
list << "final" if @origin.final?
|
8
8
|
list << "dynamic" unless @origin.sealed?
|
9
|
+
list << "public"
|
9
10
|
list
|
10
11
|
end
|
11
12
|
end
|
@@ -3,7 +3,11 @@ module Furnace::AVM2::Tokens
|
|
3
3
|
include TokenWithTraits
|
4
4
|
|
5
5
|
def initialize(origin, options={})
|
6
|
-
|
6
|
+
if origin.interface?
|
7
|
+
options = options.merge(environment: :interface)
|
8
|
+
else
|
9
|
+
options = options.merge(environment: :class)
|
10
|
+
end
|
7
11
|
|
8
12
|
super(origin, [
|
9
13
|
ClassSpecifiersToken.new(origin, options),
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Furnace::AVM2::Tokens
|
2
|
+
class MetadataToken < Furnace::Code::TerminalToken
|
3
|
+
def initialize(origin, options={})
|
4
|
+
super(origin, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_text
|
8
|
+
if @origin.metadata?
|
9
|
+
elements = []
|
10
|
+
|
11
|
+
@origin.metadata.each do |datum|
|
12
|
+
values = datum.to_hash.map do |key, value|
|
13
|
+
%Q[#{key || '*'}="#{value}"]
|
14
|
+
end
|
15
|
+
|
16
|
+
elements << "#{datum.name}(#{values.join(",")})"
|
17
|
+
end
|
18
|
+
|
19
|
+
"[#{elements.join(",")}]\n"
|
20
|
+
else
|
21
|
+
""
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,10 +3,11 @@ require_relative 'specifiers_token'
|
|
3
3
|
module Furnace::AVM2::Tokens
|
4
4
|
class MethodSpecifiersToken < SpecifiersToken
|
5
5
|
def specifiers
|
6
|
-
list = []
|
6
|
+
list, super_list = [], super
|
7
7
|
list << "final" if @origin.final? && !@options[:static]
|
8
|
-
list << "override" if @origin.override?
|
9
|
-
list.concat
|
8
|
+
list << "override" if @origin.override? && !super_list.include?("private")
|
9
|
+
list.concat super_list
|
10
|
+
list << "native" if @origin.body.nil? && @options[:environment] != :interface
|
10
11
|
list
|
11
12
|
end
|
12
13
|
end
|
@@ -4,6 +4,7 @@ module Furnace::AVM2::Tokens
|
|
4
4
|
class MethodToken < CalleeToken
|
5
5
|
def initialize(origin, options={})
|
6
6
|
super(origin, [
|
7
|
+
MetadataToken.new(origin, options),
|
7
8
|
MethodSpecifiersToken.new(origin, options),
|
8
9
|
FunctionNameToken.new(origin, [
|
9
10
|
MultinameToken.new(origin, origin.name, options.merge(omit_ns: true))
|
@@ -6,32 +6,47 @@ module Furnace::AVM2::Tokens
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_text
|
9
|
-
|
9
|
+
name_to_text(@multiname)
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def name_to_text(multiname)
|
15
|
+
if multiname
|
10
16
|
if @options[:debug_names]
|
11
|
-
debug = "/* #{
|
17
|
+
debug = "/* #{multiname.to_astlet.to_sexp} */ "
|
12
18
|
end
|
13
19
|
|
14
|
-
qualified_name = ->(ns) {
|
15
|
-
if @options[:ns].include?(ns) ||
|
16
|
-
|
20
|
+
qualified_name = ->(ns, name) {
|
21
|
+
if @options[:ns].include?(ns) ||
|
22
|
+
["*", ""].include?(ns.name) ||
|
23
|
+
@options[:omit_ns]
|
24
|
+
"#{debug}#{name}"
|
17
25
|
else
|
18
|
-
"#{debug}#{ns.name}.#{
|
26
|
+
"#{debug}#{ns.name}.#{name}"
|
19
27
|
end
|
20
28
|
}
|
21
29
|
|
22
|
-
case
|
30
|
+
case multiname.kind
|
23
31
|
when :QName, :QNameA
|
24
|
-
qualified_name.(
|
32
|
+
qualified_name.(multiname.ns, multiname.name)
|
25
33
|
when :GenericName
|
26
|
-
|
34
|
+
parameters = multiname.parameters.map do |param|
|
35
|
+
subname = name_to_text(param)
|
36
|
+
# Fuck you.
|
37
|
+
subname = "#{subname} " if subname.end_with? ">"
|
38
|
+
subname
|
39
|
+
end
|
40
|
+
"#{debug}#{multiname.name.name}.<#{parameters.join(", ")}>"
|
27
41
|
when :Multiname
|
28
|
-
if
|
29
|
-
qualified_name.(
|
42
|
+
if multiname.ns_set.ns.count == 1
|
43
|
+
qualified_name.(multiname.ns_set.ns[0], multiname.name)
|
30
44
|
else
|
31
|
-
"#{
|
45
|
+
"/* #{multiname.to_astlet.to_sexp} */ " +
|
46
|
+
qualified_name.(multiname.ns_set.ns[0], multiname.name)
|
32
47
|
end
|
33
48
|
else
|
34
|
-
"#{debug}%%#{
|
49
|
+
"#{debug}%%#{multiname.kind}"
|
35
50
|
end
|
36
51
|
else
|
37
52
|
"%%nil"
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module Furnace::AVM2::Tokens
|
2
2
|
class PackageToken < Furnace::Code::NonterminalToken
|
3
3
|
def initialize(origin, options={})
|
4
|
-
options[:ns] = options[:ns].
|
4
|
+
options[:ns] = options[:ns].reject { |ns|
|
5
5
|
ns.name == "" || ns.name == "*"
|
6
6
|
}
|
7
7
|
|
8
|
-
import_ns = options[:ns]
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
import_ns = options[:ns].reject { |ns|
|
9
|
+
ns.name == options[:package_name].ns.name ||
|
10
|
+
ns.name == "__AS3__.vec" ||
|
11
|
+
ns.name !~ /^[A-Za-z0-9_$.]+$/
|
12
|
+
}
|
13
13
|
|
14
14
|
super(origin, [
|
15
15
|
(PackageNameToken.new(origin, options[:package_name].ns.name, options) if options[:package_name]),
|
@@ -19,8 +19,9 @@ module Furnace::AVM2::Tokens
|
|
19
19
|
},
|
20
20
|
(Furnace::Code::NewlineToken.new(origin, options) if import_ns.any?),
|
21
21
|
(case options[:package_type]
|
22
|
-
when :class;
|
23
|
-
when :
|
22
|
+
when :class; ClassToken.new(origin, options)
|
23
|
+
when :interface; ClassToken.new(origin, options)
|
24
|
+
when :script; ScriptToken.new(origin, options)
|
24
25
|
end)
|
25
26
|
], options)
|
26
27
|
], options)
|
@@ -5,9 +5,12 @@ module Furnace::AVM2::Tokens
|
|
5
5
|
def initialize(origin, options={})
|
6
6
|
options = options.merge(environment: :script)
|
7
7
|
|
8
|
+
global_code = Furnace::AVM2::Decompiler.new(origin.initializer_body,
|
9
|
+
options.merge(global_code: true)).decompile
|
10
|
+
|
8
11
|
super(origin, [
|
9
12
|
*transform_traits(origin, options.merge(static: false)),
|
10
|
-
|
13
|
+
(global_code if global_code.children.any?)
|
11
14
|
], options)
|
12
15
|
end
|
13
16
|
end
|
@@ -2,6 +2,7 @@ module Furnace::AVM2::Tokens
|
|
2
2
|
class SlotToken < Furnace::Code::SurroundedToken
|
3
3
|
def initialize(origin, options={})
|
4
4
|
super(origin, [
|
5
|
+
MetadataToken.new(origin, options),
|
5
6
|
SpecifiersToken.new(origin, options),
|
6
7
|
SlotNameToken.new(origin, options),
|
7
8
|
(TypeToken.new(origin, [
|
@@ -4,15 +4,17 @@ module Furnace::AVM2::Tokens
|
|
4
4
|
list = []
|
5
5
|
list << "static" if @options[:static]
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
unless @options[:environment] == :interface
|
8
|
+
if @origin.name.ns.name == ""
|
9
|
+
list << "public"
|
10
|
+
elsif @options[:instance]
|
11
|
+
if @options[:instance].protected_ns? &&
|
12
|
+
# protected_ns is not singleton. Why? Fuck me if I know.
|
13
|
+
@origin.name.ns.name == @options[:instance].protected_ns.name
|
14
|
+
list << "protected"
|
15
|
+
elsif @origin.name.ns != @options[:instance].name.ns
|
16
|
+
list << "private"
|
17
|
+
end
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -13,17 +13,65 @@ module Furnace::AVM2
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def initialize(body, options)
|
16
|
-
@body, @method, @options = body, body.method, options
|
16
|
+
@body, @method, @options = body, body.method, options.dup
|
17
|
+
@closure = @options.delete(:closure)
|
18
|
+
end
|
19
|
+
|
20
|
+
ActivationPrologue = Matcher.new do
|
21
|
+
[:begin,
|
22
|
+
[:push_scope,
|
23
|
+
[:get_local, 0]],
|
24
|
+
[:set_local, -1,
|
25
|
+
[:new_activation]],
|
26
|
+
[:set_local, capture(:activation_local),
|
27
|
+
[:get_local, -1]],
|
28
|
+
[:push_scope,
|
29
|
+
[:get_local, -1]],
|
30
|
+
skip
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
RegularPrologue = Matcher.new do
|
35
|
+
[:begin,
|
36
|
+
[:push_scope,
|
37
|
+
[:get_local, 0]],
|
38
|
+
skip
|
39
|
+
]
|
17
40
|
end
|
18
41
|
|
19
42
|
def decompile
|
20
43
|
begin
|
21
44
|
@locals = Set.new([0]) + (1..@method.param_count).to_a
|
45
|
+
@scopes = []
|
22
46
|
|
23
47
|
@nf = @body.code_to_nf
|
24
48
|
|
25
|
-
|
49
|
+
if captures = ActivationPrologue.match(@nf)
|
50
|
+
@closure_slots = {}
|
51
|
+
@body.slot_traits.each do |trait|
|
52
|
+
@closure_slots[trait.idx] = trait
|
53
|
+
end
|
54
|
+
|
55
|
+
@closure_locals = Set.new
|
56
|
+
|
57
|
+
@nf.children.slice! 0...4
|
58
|
+
else
|
59
|
+
if RegularPrologue.match @nf
|
60
|
+
@nf.children.slice! 0...1
|
61
|
+
else
|
62
|
+
# No prologue; probably a closure.
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@global_slots = @options[:global_slots]
|
67
|
+
|
68
|
+
stmt_block @nf,
|
69
|
+
function: !@options[:global_code],
|
70
|
+
closure: @closure
|
71
|
+
|
26
72
|
rescue Exception => e
|
73
|
+
@failed = true
|
74
|
+
|
27
75
|
comment = "'Ouch!' cried I, then died.\n" +
|
28
76
|
"#{e.class}: #{e.message}\n" +
|
29
77
|
"#{e.backtrace[0..5].map { |l| " #{l}\n"}.join}" +
|
@@ -31,103 +79,144 @@ module Furnace::AVM2
|
|
31
79
|
|
32
80
|
token(ScopeToken, [
|
33
81
|
token(CommentToken, comment)
|
34
|
-
], function:
|
82
|
+
], function: !@options[:global_code],
|
83
|
+
closure: @closure)
|
84
|
+
|
85
|
+
ensure
|
86
|
+
if stat = @options[:stat]
|
87
|
+
stat[:total] += 1
|
88
|
+
|
89
|
+
if @failed
|
90
|
+
stat[:failed] += 1
|
91
|
+
elsif @partial
|
92
|
+
stat[:partial] += 1
|
93
|
+
else
|
94
|
+
stat[:success] += 1
|
95
|
+
end
|
96
|
+
end
|
35
97
|
end
|
36
98
|
end
|
37
99
|
|
38
100
|
# Statements
|
39
101
|
|
40
|
-
Prologue = Matcher.new do
|
41
|
-
[:push_scope,
|
42
|
-
[:get_local, 0]]
|
43
|
-
end
|
44
|
-
|
45
102
|
def stmt_block(block, options={})
|
46
103
|
nodes = []
|
47
104
|
|
48
105
|
block.children.each do |opcode|
|
49
|
-
if (opcode.type
|
50
|
-
#
|
51
|
-
|
106
|
+
if respond_to?(:"stmt_#{opcode.type}")
|
107
|
+
send :"stmt_#{opcode.type}", opcode, nodes
|
108
|
+
else
|
109
|
+
catch(:skip) do
|
110
|
+
nodes << token(StatementToken, [
|
111
|
+
handle_expression(opcode)
|
112
|
+
])
|
113
|
+
end
|
52
114
|
end
|
115
|
+
end
|
53
116
|
|
54
|
-
|
55
|
-
|
56
|
-
condition, if_true, if_false = opcode.children
|
117
|
+
rescue ExpressionNotRecognized => e
|
118
|
+
@partial = true
|
57
119
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
stmt_block(if_false)) if if_false
|
120
|
+
comment = "Well, this is embarassing.\n\n" +
|
121
|
+
"Expression recognizer failed at:\n" +
|
122
|
+
"#{e.opcode.inspect}\n"
|
62
123
|
|
63
|
-
|
64
|
-
|
124
|
+
if e.context != e.opcode
|
125
|
+
comment << "\nOpcode at the top of stack:\n" +
|
126
|
+
"#{e.context.inspect}\n"
|
127
|
+
end
|
65
128
|
|
66
|
-
|
129
|
+
nodes << CommentToken.new(@body, comment, @options)
|
67
130
|
|
68
|
-
|
69
|
-
|
131
|
+
ensure
|
132
|
+
if $!.nil? || $!.is_a?(ExpressionNotRecognized)
|
133
|
+
return token(ScopeToken, nodes, options)
|
134
|
+
end
|
135
|
+
end
|
70
136
|
|
71
|
-
|
72
|
-
|
137
|
+
def stmt_if(opcode, nodes)
|
138
|
+
condition, if_true, if_false = opcode.children
|
73
139
|
|
74
|
-
|
75
|
-
|
140
|
+
nodes << token(IfToken, handle_expression(condition),
|
141
|
+
stmt_block(if_true, continuation: !if_false.nil?))
|
142
|
+
nodes << token(ElseToken,
|
143
|
+
stmt_block(if_false)) if if_false
|
144
|
+
end
|
76
145
|
|
77
|
-
|
146
|
+
def stmt_label(opcode, nodes)
|
147
|
+
name, = opcode.children
|
78
148
|
|
79
|
-
|
80
|
-
|
81
|
-
elsif opcode.type == :for_each_in
|
82
|
-
klass = ForEachToken
|
83
|
-
end
|
149
|
+
nodes << token(LabelDeclarationToken, name)
|
150
|
+
end
|
84
151
|
|
85
|
-
|
86
|
-
|
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))
|
152
|
+
def stmt_while(opcode, nodes)
|
153
|
+
condition, body = opcode.children
|
94
154
|
|
95
|
-
|
96
|
-
|
155
|
+
nodes << token(WhileToken, handle_expression(condition),
|
156
|
+
stmt_block(body))
|
157
|
+
end
|
97
158
|
|
98
|
-
|
99
|
-
|
159
|
+
def stmt_for(opcode, nodes)
|
160
|
+
value_reg, value_type, object_reg, body = opcode.children
|
100
161
|
|
101
|
-
|
102
|
-
nodes << token(ThrowToken, exprs(opcode.children))
|
162
|
+
@locals.add(value_reg)
|
103
163
|
|
104
|
-
|
105
|
-
|
164
|
+
if opcode.type == :for_in
|
165
|
+
klass = ForToken
|
166
|
+
elsif opcode.type == :for_each_in
|
167
|
+
klass = ForEachToken
|
168
|
+
end
|
106
169
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
170
|
+
if @closure_slots
|
171
|
+
name = token(VariableNameToken, @closure_slots[value_reg].name.name)
|
172
|
+
else
|
173
|
+
name = token(VariableNameToken, local_name(value_reg))
|
112
174
|
end
|
113
175
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
176
|
+
nodes << token(klass,
|
177
|
+
token(InToken, [
|
178
|
+
token(LocalVariableToken, [
|
179
|
+
name,
|
180
|
+
type_token(value_type)
|
181
|
+
]),
|
182
|
+
token(VariableNameToken, local_name(object_reg)),
|
183
|
+
]),
|
184
|
+
stmt_block(body))
|
185
|
+
end
|
186
|
+
alias :stmt_for_in :stmt_for
|
187
|
+
alias :stmt_for_each_in :stmt_for
|
118
188
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
189
|
+
def stmt_break(opcode, nodes)
|
190
|
+
nodes << token(ReturnToken, exprs(opcode.children))
|
191
|
+
end
|
123
192
|
|
124
|
-
|
193
|
+
def stmt_continue(opcode, nodes)
|
194
|
+
nodes << token(ContinueToken, exprs(opcode.children))
|
195
|
+
end
|
125
196
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
197
|
+
def stmt_throw(opcode, nodes)
|
198
|
+
nodes << token(ThrowToken, exprs(opcode.children))
|
199
|
+
end
|
200
|
+
|
201
|
+
def stmt_return(opcode, nodes)
|
202
|
+
nodes << token(ReturnToken, exprs(opcode.children))
|
203
|
+
end
|
204
|
+
alias :stmt_return_value :stmt_return
|
205
|
+
alias :stmt_return_void :stmt_return
|
206
|
+
|
207
|
+
def stmt_push_scope(opcode, nodes)
|
208
|
+
if @options[:global_code]
|
209
|
+
@scopes.push opcode.children.first
|
210
|
+
else
|
211
|
+
raise "pushscope in nonglobal code"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def stmt_pop_scope(opcode, nodes)
|
216
|
+
if @options[:global_code]
|
217
|
+
@scopes.pop
|
218
|
+
else
|
219
|
+
raise "popscope in nonglobal code"
|
131
220
|
end
|
132
221
|
end
|
133
222
|
|
@@ -202,7 +291,11 @@ module Furnace::AVM2
|
|
202
291
|
if index < 0
|
203
292
|
"sp#{-index}"
|
204
293
|
elsif index == 0
|
205
|
-
|
294
|
+
if @options[:static]
|
295
|
+
@options[:instance].name.name
|
296
|
+
else
|
297
|
+
"this"
|
298
|
+
end
|
206
299
|
elsif index <= @method.param_count
|
207
300
|
if @method.has_param_names?
|
208
301
|
@method.param_names[index - 1]
|
@@ -219,6 +312,38 @@ module Furnace::AVM2
|
|
219
312
|
token(VariableNameToken, local_name(index))
|
220
313
|
end
|
221
314
|
|
315
|
+
ActivationGetSlot = Matcher.new do
|
316
|
+
[:get_slot,
|
317
|
+
capture(:index),
|
318
|
+
[:get_scope_object, 1]]
|
319
|
+
end
|
320
|
+
|
321
|
+
GlobalGetSlot = Matcher.new do
|
322
|
+
[:get_slot,
|
323
|
+
capture(:index),
|
324
|
+
either[
|
325
|
+
[:get_global_scope], # normalized form
|
326
|
+
[:get_scope_object, 0] # not emitted by ASC
|
327
|
+
]]
|
328
|
+
end
|
329
|
+
|
330
|
+
def expr_get_slot(opcode)
|
331
|
+
if @closure_slots && captures = ActivationGetSlot.match(opcode)
|
332
|
+
# treat as a local variable
|
333
|
+
slot = @closure_slots[captures[:index]]
|
334
|
+
token(VariableNameToken, slot.name.name)
|
335
|
+
elsif captures = GlobalGetSlot.match(opcode)
|
336
|
+
# treat as a global property
|
337
|
+
if @global_slots
|
338
|
+
slot = @global_slots[captures[:index]]
|
339
|
+
get_name(nil, slot.name.to_astlet)
|
340
|
+
else
|
341
|
+
token(PropertyNameToken,
|
342
|
+
"$__GLOBAL_#{captures[:index]}")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
222
347
|
IMMEDIATE_TYPE_MAP = {
|
223
348
|
:any => '*',
|
224
349
|
:integer => 'int',
|
@@ -243,42 +368,94 @@ module Furnace::AVM2
|
|
243
368
|
end
|
244
369
|
end
|
245
370
|
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
371
|
+
XmlLiteralPreMatcher = Matcher.new do
|
372
|
+
[:coerce, [:q, "XML"], any]
|
373
|
+
end
|
259
374
|
|
375
|
+
def expr_set_var(name, value, type, declare)
|
260
376
|
if declare
|
261
377
|
token(LocalVariableToken, [
|
262
378
|
token(VariableNameToken, name),
|
263
379
|
type,
|
264
380
|
token(InitializationToken, [
|
265
|
-
expr(
|
381
|
+
expr(value)
|
266
382
|
])
|
267
383
|
])
|
268
384
|
else
|
269
385
|
token(AssignmentToken, [
|
270
386
|
token(VariableNameToken, name),
|
271
|
-
expr(
|
387
|
+
expr(value)
|
272
388
|
])
|
273
389
|
end
|
274
390
|
end
|
275
391
|
|
276
392
|
def expr_set_local(opcode)
|
277
393
|
index, value = opcode.children
|
394
|
+
if IMMEDIATE_TYPE_MAP.include?(value.type)
|
395
|
+
type = token(TypeToken, [
|
396
|
+
token(ImmediateTypenameToken, IMMEDIATE_TYPE_MAP[value.type])
|
397
|
+
])
|
398
|
+
elsif XmlLiteralPreMatcher.match value
|
399
|
+
# XML literals work through expr_coerce
|
400
|
+
type = type_token(value.children.first)
|
401
|
+
value = value
|
402
|
+
elsif value.type == :coerce || value.type == :convert
|
403
|
+
# Don't emit spurious coercion; typed variables already
|
404
|
+
# imply it
|
405
|
+
type = type_token(value.children.first)
|
406
|
+
value = value.children.last
|
407
|
+
end
|
278
408
|
|
279
|
-
expr_set_var(local_name(index), value, !@locals.include?(index))
|
409
|
+
expr_set_var(local_name(index), value, type, !@locals.include?(index))
|
280
410
|
ensure
|
281
|
-
@locals
|
411
|
+
@locals.add index if index
|
412
|
+
end
|
413
|
+
|
414
|
+
ActivationSetSlot = Matcher.new do
|
415
|
+
[:set_slot,
|
416
|
+
capture(:index),
|
417
|
+
[:get_scope_object, 1],
|
418
|
+
capture(:value)]
|
419
|
+
end
|
420
|
+
|
421
|
+
GlobalSetSlot = Matcher.new do
|
422
|
+
[:set_slot,
|
423
|
+
capture(:index),
|
424
|
+
either[
|
425
|
+
[:get_global_scope], # normalized form
|
426
|
+
[:get_scope_object, 0] # not emitted by ASC
|
427
|
+
],
|
428
|
+
capture(:value)]
|
429
|
+
end
|
430
|
+
|
431
|
+
def expr_set_slot(opcode)
|
432
|
+
if @closure_slots && captures = ActivationSetSlot.match(opcode)
|
433
|
+
# treat as a local variable
|
434
|
+
index, value = captures.values_at(:index, :value)
|
435
|
+
slot = @closure_slots[index]
|
436
|
+
|
437
|
+
type = type_token(slot.type.to_astlet) if slot.type
|
438
|
+
expr = expr_set_var(slot.name.name, value, type,
|
439
|
+
!@closure_locals.include?(index))
|
440
|
+
@closure_locals.add index
|
441
|
+
|
442
|
+
expr
|
443
|
+
elsif captures = GlobalSetSlot.match(opcode)
|
444
|
+
# treat as a global property
|
445
|
+
index, value = captures.values_at(:index, :value)
|
446
|
+
|
447
|
+
if @global_slots
|
448
|
+
slot = @global_slots[index]
|
449
|
+
name = get_name(nil, slot.name.to_astlet)
|
450
|
+
else
|
451
|
+
token(PropertyNameToken, "$__GLOBAL_#{index}")
|
452
|
+
end
|
453
|
+
|
454
|
+
token(AssignmentToken, [
|
455
|
+
name,
|
456
|
+
expr(value)
|
457
|
+
])
|
458
|
+
end
|
282
459
|
end
|
283
460
|
|
284
461
|
## Arithmetics
|
@@ -416,9 +593,22 @@ module Furnace::AVM2
|
|
416
593
|
|
417
594
|
PropertyGlobal = Matcher.new do
|
418
595
|
[any,
|
419
|
-
[
|
420
|
-
|
421
|
-
|
596
|
+
either_multi[
|
597
|
+
[
|
598
|
+
[ either[:find_property, :find_property_strict],
|
599
|
+
capture(:multiname)],
|
600
|
+
backref(:multiname),
|
601
|
+
],
|
602
|
+
[
|
603
|
+
[:find_property_strict,
|
604
|
+
[:m, [:set, "*"], any]],
|
605
|
+
capture(:multiname)
|
606
|
+
],
|
607
|
+
[
|
608
|
+
[:get_scope_object, 0],
|
609
|
+
capture(:multiname)
|
610
|
+
],
|
611
|
+
],
|
422
612
|
capture_rest(:arguments)]
|
423
613
|
end
|
424
614
|
|
@@ -438,9 +628,16 @@ module Furnace::AVM2
|
|
438
628
|
|
439
629
|
def expr_get_super(opcode)
|
440
630
|
subject, multiname, = opcode.children
|
441
|
-
|
442
|
-
|
631
|
+
|
632
|
+
stmt = get_name(token(SuperToken), multiname)
|
633
|
+
|
634
|
+
unless This.match subject
|
635
|
+
stmt = token(SupplementaryCommentToken,
|
636
|
+
"subject != this: #{subject.inspect}",
|
637
|
+
[ stmt ])
|
443
638
|
end
|
639
|
+
|
640
|
+
stmt
|
444
641
|
end
|
445
642
|
|
446
643
|
def expr_set_property(opcode)
|
@@ -461,12 +658,19 @@ module Furnace::AVM2
|
|
461
658
|
|
462
659
|
def expr_set_super(opcode)
|
463
660
|
subject, multiname, value = opcode.children
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
661
|
+
|
662
|
+
stmt = token(AssignmentToken, [
|
663
|
+
get_name(token(SuperToken), multiname),
|
664
|
+
expr(value)
|
665
|
+
])
|
666
|
+
|
667
|
+
unless This.match subject
|
668
|
+
stmt = token(SupplementaryCommentToken,
|
669
|
+
"subject != this: #{subject.inspect}",
|
670
|
+
[ stmt ])
|
469
671
|
end
|
672
|
+
|
673
|
+
stmt
|
470
674
|
end
|
471
675
|
|
472
676
|
def expr_delete_property(opcode)
|
@@ -492,6 +696,7 @@ module Furnace::AVM2
|
|
492
696
|
def expr_call_property(opcode)
|
493
697
|
expr_do_property(opcode, CallToken)
|
494
698
|
end
|
699
|
+
alias :expr_call_property_lex :expr_call_property
|
495
700
|
|
496
701
|
def expr_call_super(opcode)
|
497
702
|
subject, multiname, *args = opcode.children
|
@@ -556,7 +761,7 @@ module Furnace::AVM2
|
|
556
761
|
token(CallToken, [
|
557
762
|
subject_token,
|
558
763
|
token(ArgumentsToken, [
|
559
|
-
token(VariableNameToken,
|
764
|
+
token(VariableNameToken, local_name(0)),
|
560
765
|
*exprs(args)
|
561
766
|
])
|
562
767
|
])
|
@@ -581,6 +786,10 @@ module Furnace::AVM2
|
|
581
786
|
token(IsToken, parenthesize_each(exprs(opcode.children)))
|
582
787
|
end
|
583
788
|
|
789
|
+
def expr_instance_of(opcode)
|
790
|
+
token(InstanceOfToken, parenthesize_each(exprs(opcode.children)))
|
791
|
+
end
|
792
|
+
|
584
793
|
def expr_type_of(opcode)
|
585
794
|
token(TypeOfToken, exprs(opcode.children))
|
586
795
|
end
|
@@ -594,9 +803,8 @@ module Furnace::AVM2
|
|
594
803
|
])
|
595
804
|
end
|
596
805
|
|
597
|
-
def
|
598
|
-
|
599
|
-
expr(subject)
|
806
|
+
def expr_new_class(opcode)
|
807
|
+
throw :skip
|
600
808
|
end
|
601
809
|
|
602
810
|
def expr_convert(opcode)
|
@@ -609,6 +817,69 @@ module Furnace::AVM2
|
|
609
817
|
])
|
610
818
|
end
|
611
819
|
|
820
|
+
## Closures
|
821
|
+
|
822
|
+
def expr_new_function(opcode)
|
823
|
+
index, = opcode.children
|
824
|
+
body = @method.root.method_body_at(index)
|
825
|
+
|
826
|
+
token(ClosureToken,
|
827
|
+
body)
|
828
|
+
end
|
829
|
+
|
830
|
+
## XML literals
|
831
|
+
|
832
|
+
# FFFUUUUUUUUUU~~~
|
833
|
+
XmlLiteralMatcher = Matcher.new do
|
834
|
+
[:coerce, [:q, "XML"],
|
835
|
+
[:construct,
|
836
|
+
either[
|
837
|
+
[:get_lex, [:q, "XML"]],
|
838
|
+
[:get_property,
|
839
|
+
[:find_property_strict, [:q, "XML"]], [:q, "XML"]]
|
840
|
+
],
|
841
|
+
capture(:body),
|
842
|
+
]
|
843
|
+
]
|
844
|
+
end
|
845
|
+
|
846
|
+
def expr_coerce(opcode)
|
847
|
+
if captures = XmlLiteralMatcher.match(opcode)
|
848
|
+
# Oh, shit...
|
849
|
+
token(XmlLiteralToken,
|
850
|
+
xml_expr(captures[:body]))
|
851
|
+
else
|
852
|
+
typename, subject, = opcode.children
|
853
|
+
expr(subject)
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
def xml_expr(node)
|
858
|
+
if respond_to?(:"xml_#{node.type}")
|
859
|
+
send :"xml_#{node.type}", node
|
860
|
+
else
|
861
|
+
"{#{expr(node).to_text}}"
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
def xml_string(node)
|
866
|
+
node.children.first
|
867
|
+
end
|
868
|
+
|
869
|
+
def xml_add(node)
|
870
|
+
node.children.map do |child|
|
871
|
+
xml_expr child
|
872
|
+
end.join
|
873
|
+
end
|
874
|
+
|
875
|
+
def xml_esc_xattr(node)
|
876
|
+
xml_expr(node.children.first)
|
877
|
+
end
|
878
|
+
|
879
|
+
def xml_esc_xelem(node)
|
880
|
+
xml_expr(node.children.first)
|
881
|
+
end
|
882
|
+
|
612
883
|
private
|
613
884
|
|
614
885
|
def token(klass, *args)
|