furnace-avm2 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/furnace-avm2 +28 -11
- data/bin/furnace-avm2-decompiler +18 -6
- data/bin/furnace-avm2-shell +20 -8
- data/furnace-avm2.gemspec +5 -3
- data/lib/furnace-avm2/abc/metadata/exception_info.rb +4 -3
- data/lib/furnace-avm2/abc/metadata/file.rb +25 -6
- data/lib/furnace-avm2/abc/metadata/multiname_info.rb +12 -11
- data/lib/furnace-avm2/abc/metadata/multiname_kind_multiname.rb +2 -2
- data/lib/furnace-avm2/abc/metadata/multiname_kind_qname.rb +2 -2
- data/lib/furnace-avm2/abc/metadata/trait_info.rb +8 -7
- data/lib/furnace-avm2/abc/opcodes/opcode.rb +2 -0
- data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +37 -14
- data/lib/furnace-avm2/source/declaration_tokens/callee_token.rb +1 -1
- data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +11 -1
- data/lib/furnace-avm2/source/decompiler.rb +91 -0
- data/lib/furnace-avm2/source/implementation_tokens/asm_op_token.rb +13 -0
- data/lib/furnace-avm2/source/implementation_tokens/asm_push_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/asm_token.rb +8 -0
- data/lib/furnace-avm2/source/implementation_tokens/xasm_token.rb +12 -0
- data/lib/furnace-avm2/transform/ast_build.rb +6 -10
- data/lib/furnace-avm2/version.rb +1 -1
- metadata +11 -4
data/bin/furnace-avm2
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
6
|
+
if File.directory?(File.join(root_dir,'.git'))
|
7
|
+
Dir.chdir(root_dir) do |path|
|
8
|
+
require 'bundler'
|
9
|
+
|
10
|
+
begin
|
11
|
+
Bundler.setup(:default)
|
12
|
+
rescue Bundler::BundlerError => e
|
13
|
+
warn e.message
|
14
|
+
warn "Run `bundle install` to install missing gems"
|
15
|
+
exit e.status_code
|
16
|
+
end
|
17
|
+
end
|
7
18
|
end
|
8
19
|
|
9
|
-
|
20
|
+
lib_dir = File.join(root_dir,'lib')
|
21
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
10
22
|
|
11
23
|
require "trollop"
|
12
24
|
require "furnace-avm2"
|
@@ -24,12 +36,13 @@ opts = Trollop::options do
|
|
24
36
|
|
25
37
|
Supported graphing formats: #{GRAPH_FORMATS.join(", ")}.
|
26
38
|
|
27
|
-
Usage: #{
|
39
|
+
Usage: #{$0} [options]
|
28
40
|
EOS
|
29
41
|
|
30
42
|
opt :input, "Input file", :type => :string, :required => true
|
31
43
|
opt :output, "Output file", :type => :string
|
32
44
|
opt :verbose, "Be verbose", :default => false
|
45
|
+
opt :verify, "Do extra verification passes", :default => false, :short => '-V'
|
33
46
|
|
34
47
|
opt :threads, "Use <i> threads for processing", :default => 1
|
35
48
|
|
@@ -74,11 +87,11 @@ disasm = lambda do |body, after|
|
|
74
87
|
puts body.code.disassemble
|
75
88
|
|
76
89
|
if body.exceptions.any?
|
77
|
-
puts
|
78
|
-
puts "Exceptions"
|
90
|
+
$stderr.puts
|
91
|
+
$stderr.puts "Exceptions"
|
79
92
|
body.exceptions.each do |exception|
|
80
|
-
puts " #{exception.from_offset} -> #{exception.to_offset}: " <<
|
81
|
-
|
93
|
+
$stderr.puts " #{exception.from_offset} -> #{exception.to_offset}: " <<
|
94
|
+
"catch(#{exception.exception.to_s} #{exception.variable.to_s}) #{exception.target_offset}"
|
82
95
|
end
|
83
96
|
end
|
84
97
|
|
@@ -145,6 +158,10 @@ opts[:threads].times do
|
|
145
158
|
|
146
159
|
if opts[:dce]
|
147
160
|
dced << body.method_idx if body.code.eliminate_dead!
|
161
|
+
if opts[:verify]
|
162
|
+
temp_io = StringIO.new
|
163
|
+
body.write temp_io
|
164
|
+
end
|
148
165
|
end
|
149
166
|
|
150
167
|
disasm[body, true] if opts[:disasm_after]
|
@@ -201,7 +218,7 @@ opts[:threads].times do
|
|
201
218
|
end
|
202
219
|
rescue Exception => e
|
203
220
|
if opts[:collect]
|
204
|
-
puts "Failure at method body idx=#{body.method_idx}: #{e.class} (#{e.message}) at #{e.backtrace.first}."
|
221
|
+
$stderr.puts "Failure at method body idx=#{body.method_idx}: #{e.class} (#{e.message}) at #{e.backtrace.first}."
|
205
222
|
failed << body.method_idx
|
206
223
|
else
|
207
224
|
raise e
|
@@ -238,4 +255,4 @@ if opts[:output]
|
|
238
255
|
File.open(opts[:output], "w") do |file|
|
239
256
|
abc.write(file)
|
240
257
|
end
|
241
|
-
end
|
258
|
+
end
|
data/bin/furnace-avm2-decompiler
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
6
|
+
if File.directory?(File.join(root_dir,'.git'))
|
7
|
+
Dir.chdir(root_dir) do |path|
|
8
|
+
require 'bundler'
|
9
|
+
|
10
|
+
begin
|
11
|
+
Bundler.setup(:default)
|
12
|
+
rescue Bundler::BundlerError => e
|
13
|
+
warn e.message
|
14
|
+
warn "Run `bundle install` to install missing gems"
|
15
|
+
exit e.status_code
|
16
|
+
end
|
17
|
+
end
|
7
18
|
end
|
8
19
|
|
9
|
-
|
20
|
+
lib_dir = File.join(root_dir,'lib')
|
21
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
10
22
|
|
11
23
|
require "trollop"
|
12
24
|
require "furnace-avm2"
|
@@ -23,7 +35,7 @@ opts = Trollop::options do
|
|
23
35
|
|
24
36
|
Debugging information classes: #{DEBUG_INFO.join(", ")}.
|
25
37
|
|
26
|
-
Usage: #{
|
38
|
+
Usage: #{$0} [options]
|
27
39
|
EOS
|
28
40
|
|
29
41
|
opt :input, "Input file", :type => :string, :required => true
|
@@ -215,4 +227,4 @@ unless opts[:no_output]
|
|
215
227
|
puts code.gsub(/ +$/, '')
|
216
228
|
puts
|
217
229
|
end
|
218
|
-
end
|
230
|
+
end
|
data/bin/furnace-avm2-shell
CHANGED
@@ -1,21 +1,33 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
6
|
+
if File.directory?(File.join(root_dir,'.git'))
|
7
|
+
Dir.chdir(root_dir) do |path|
|
8
|
+
require 'bundler'
|
9
|
+
|
10
|
+
begin
|
11
|
+
Bundler.setup(:default)
|
12
|
+
rescue Bundler::BundlerError => e
|
13
|
+
warn e.message
|
14
|
+
warn "Run `bundle install` to install missing gems"
|
15
|
+
exit e.status_code
|
16
|
+
end
|
17
|
+
end
|
7
18
|
end
|
8
19
|
|
9
|
-
|
20
|
+
lib_dir = File.join(root_dir,'lib')
|
21
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
10
22
|
|
11
23
|
require "furnace-avm2"
|
12
24
|
|
13
25
|
begin
|
14
26
|
gem "pry"
|
15
27
|
require "pry"
|
16
|
-
rescue LoadError
|
17
|
-
|
18
|
-
exit 1
|
28
|
+
rescue LoadError => e
|
29
|
+
warn "Run `gem install pry` to install Pry"
|
30
|
+
exit -1
|
19
31
|
end
|
20
32
|
|
21
33
|
include Furnace
|
@@ -33,4 +45,4 @@ end
|
|
33
45
|
# > abc = load_file("filename.abc")
|
34
46
|
# Records can be examined via #to_hash.
|
35
47
|
|
36
|
-
binding.pry
|
48
|
+
binding.pry
|
data/furnace-avm2.gemspec
CHANGED
@@ -12,11 +12,13 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = %q{furnace-avm2 allows one to load, modify and write back } <<
|
13
13
|
%q{Flash ActionScript3 bytecode. It can also decompile it.}
|
14
14
|
|
15
|
-
s.files = `git ls-files`.split(
|
16
|
-
s.
|
17
|
-
s.
|
15
|
+
s.files = `git ls-files`.split($/)
|
16
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
+
s.required_ruby_version = '>= 1.9.1'
|
21
|
+
|
20
22
|
s.add_runtime_dependency "furnace", '= 0.2.5'
|
21
23
|
s.add_runtime_dependency "trollop"
|
22
24
|
end
|
@@ -22,9 +22,10 @@ module Furnace::AVM2::ABC
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def lookup!
|
25
|
-
|
26
|
-
self.
|
27
|
-
self.
|
25
|
+
# HACK ALERT this fixes improper DCE implementation failures
|
26
|
+
self.from_offset = @from.offset || 0
|
27
|
+
self.to_offset = @to.offset || 0
|
28
|
+
self.target_offset = @target.offset || 0
|
28
29
|
end
|
29
30
|
|
30
31
|
def range
|
@@ -44,16 +44,32 @@ module Furnace::AVM2::ABC
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
AS3_KEYWORDS = \
|
48
|
+
# Lexical keywords
|
49
|
+
%w(as break case catch class const continue default delete
|
50
|
+
do else extends false finally for function if implements
|
51
|
+
import in instanceof interface internal is native new
|
52
|
+
null package private protected public return super switch
|
53
|
+
this throw to true try typeof use var void while with) +
|
54
|
+
# Syntactical keywords
|
55
|
+
%w(each get set namespace include dynamic final native
|
56
|
+
override static) +
|
57
|
+
# Future reserved words
|
58
|
+
%w(abstract boolean byte cast char debugger double enum export
|
59
|
+
float goto intrinsic long prototype short synchronized throws
|
60
|
+
to transient type virtual volatile)
|
61
|
+
|
47
62
|
def fix_name!(name_idx, options={})
|
48
63
|
old_name = constant_pool.strings[name_idx - 1]
|
49
64
|
return if ["", "*"].include? old_name
|
50
65
|
|
51
66
|
fixed_name = sanitize_name(old_name, options)
|
52
67
|
|
53
|
-
if old_name != fixed_name
|
68
|
+
if old_name != fixed_name || AS3_KEYWORDS.include?(fixed_name)
|
54
69
|
index = 0
|
55
70
|
indexed_name = fixed_name
|
56
|
-
while
|
71
|
+
while AS3_KEYWORDS.include?(indexed_name) ||
|
72
|
+
@name_set.include?(indexed_name)
|
57
73
|
indexed_name = "#{fixed_name}_i#{index}"
|
58
74
|
index += 1
|
59
75
|
end
|
@@ -69,17 +85,20 @@ module Furnace::AVM2::ABC
|
|
69
85
|
return name if name.start_with? "http://"
|
70
86
|
|
71
87
|
name.split('.').map do |part|
|
72
|
-
part
|
73
|
-
gsub(/[^a-zA-Z_$0-9:]+/, '')
|
88
|
+
clean_name_part(part)
|
74
89
|
end.reject do |part|
|
75
90
|
part.empty?
|
76
91
|
end.join('.')
|
77
92
|
else
|
78
|
-
name
|
79
|
-
gsub(/[^a-zA-Z_$0-9:]+/, '')
|
93
|
+
clean_name_part(name)
|
80
94
|
end
|
81
95
|
end
|
82
96
|
|
97
|
+
def clean_name_part(part)
|
98
|
+
part = part.gsub(/^[^a-zA-Z_$]+/, '').
|
99
|
+
gsub(/[^a-zA-Z_$0-9:]+/, '')
|
100
|
+
end
|
101
|
+
|
83
102
|
protected
|
84
103
|
|
85
104
|
def after_read(io)
|
@@ -18,17 +18,18 @@ module Furnace::AVM2::ABC
|
|
18
18
|
xlat_field :kind
|
19
19
|
|
20
20
|
choice :data, :selection => :kind do
|
21
|
-
|
22
|
-
variant :
|
23
|
-
variant :
|
24
|
-
variant :
|
25
|
-
variant :
|
26
|
-
variant :
|
27
|
-
variant :
|
28
|
-
variant :
|
29
|
-
variant :
|
30
|
-
variant :
|
31
|
-
variant :
|
21
|
+
# Fully qualified names required by 1.9.1 compat.
|
22
|
+
variant :QName, :nested, :class => Furnace::AVM2::ABC::MultinameKindQName
|
23
|
+
variant :QNameA, :nested, :class => Furnace::AVM2::ABC::MultinameKindQName
|
24
|
+
variant :RTQName, :nested, :class => Furnace::AVM2::ABC::MultinameKindRTQName
|
25
|
+
variant :RTQNameA, :nested, :class => Furnace::AVM2::ABC::MultinameKindRTQName
|
26
|
+
variant :RTQNameL, :nested, :class => Furnace::AVM2::ABC::MultinameKindRTQNameL
|
27
|
+
variant :RTQNameLA, :nested, :class => Furnace::AVM2::ABC::MultinameKindRTQNameL
|
28
|
+
variant :Multiname, :nested, :class => Furnace::AVM2::ABC::MultinameKindMultiname
|
29
|
+
variant :MultinameA, :nested, :class => Furnace::AVM2::ABC::MultinameKindMultiname
|
30
|
+
variant :MultinameL, :nested, :class => Furnace::AVM2::ABC::MultinameKindMultinameL
|
31
|
+
variant :MultinameLA, :nested, :class => Furnace::AVM2::ABC::MultinameKindMultinameL
|
32
|
+
variant :GenericName, :nested, :class => Furnace::AVM2::ABC::MultinameKindGenericName
|
32
33
|
end
|
33
34
|
|
34
35
|
def method_missing(method, *args, &block)
|
@@ -17,10 +17,10 @@ module Furnace::AVM2::ABC
|
|
17
17
|
|
18
18
|
names = options[:names]
|
19
19
|
if names[name].nil?
|
20
|
-
options[:ns].add ns
|
20
|
+
options[:ns].add ns if ns
|
21
21
|
names[name] = ns
|
22
22
|
elsif names[name] != ns
|
23
|
-
options[:no_ns].add ns
|
23
|
+
options[:no_ns].add ns if ns
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -42,10 +42,10 @@ module Furnace::AVM2::ABC
|
|
42
42
|
|
43
43
|
names = options[:names]
|
44
44
|
if names[name].nil?
|
45
|
-
options[:ns].add ns
|
45
|
+
options[:ns].add ns if ns
|
46
46
|
names[name] = ns
|
47
47
|
elsif names[name] != ns
|
48
|
-
options[:no_ns].add ns
|
48
|
+
options[:no_ns].add ns if ns
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -41,13 +41,14 @@ module Furnace::AVM2::ABC
|
|
41
41
|
xlat_field :kind
|
42
42
|
|
43
43
|
choice :data, :selection => :kind do
|
44
|
-
|
45
|
-
variant :
|
46
|
-
variant :
|
47
|
-
variant :
|
48
|
-
variant :
|
49
|
-
variant :
|
50
|
-
variant :
|
44
|
+
# Fully qualified names required by 1.9.1 compat.
|
45
|
+
variant :Slot, :nested, :class => Furnace::AVM2::ABC::TraitSlot
|
46
|
+
variant :Method, :nested, :class => Furnace::AVM2::ABC::TraitMethod
|
47
|
+
variant :Getter, :nested, :class => Furnace::AVM2::ABC::TraitMethod
|
48
|
+
variant :Setter, :nested, :class => Furnace::AVM2::ABC::TraitMethod
|
49
|
+
variant :Class, :nested, :class => Furnace::AVM2::ABC::TraitClass
|
50
|
+
variant :Function, :nested, :class => Furnace::AVM2::ABC::TraitFunction
|
51
|
+
variant :Const, :nested, :class => Furnace::AVM2::ABC::TraitSlot
|
51
52
|
end
|
52
53
|
|
53
54
|
root_array_of :metadata, :metadata, :plural => :metadata, :if => :metadata?
|
@@ -74,7 +74,11 @@ module Furnace::AVM2::ABC
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def byte_length
|
77
|
-
|
77
|
+
if @raw_code
|
78
|
+
@raw_code.length
|
79
|
+
else
|
80
|
+
map(&:byte_length).reduce(0, :+)
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
# Transformations
|
@@ -97,6 +101,10 @@ module Furnace::AVM2::ABC
|
|
97
101
|
end
|
98
102
|
end
|
99
103
|
|
104
|
+
@parent.exceptions.each do |exc|
|
105
|
+
targets << exc.target
|
106
|
+
end
|
107
|
+
|
100
108
|
pending_label = nil
|
101
109
|
pending_queue = []
|
102
110
|
|
@@ -121,14 +129,17 @@ module Furnace::AVM2::ABC
|
|
121
129
|
pending_label = opcode.offset if pending_label.nil?
|
122
130
|
pending_queue << opcode
|
123
131
|
|
124
|
-
if opcode.is_a?
|
132
|
+
if opcode.is_a?(ControlTransferOpcode)
|
125
133
|
if opcode.conditional
|
126
134
|
cutoff.([ opcode.target.offset, opcode.offset + opcode.byte_length ])
|
127
135
|
else
|
128
136
|
cutoff.([ opcode.target.offset ])
|
129
137
|
end
|
130
|
-
elsif opcode.is_a?
|
138
|
+
elsif opcode.is_a?(AS3LookupSwitch)
|
131
139
|
cutoff.(opcode.parameters.flatten)
|
140
|
+
elsif opcode.is_a?(FunctionReturnOpcode) ||
|
141
|
+
opcode.is_a?(AS3Throw)
|
142
|
+
cutoff.([])
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
@@ -144,32 +155,44 @@ module Furnace::AVM2::ABC
|
|
144
155
|
end
|
145
156
|
|
146
157
|
def eliminate_dead!
|
147
|
-
|
148
|
-
|
158
|
+
begin
|
159
|
+
cfg = build_cfg
|
160
|
+
rescue Exception => e
|
161
|
+
# Ignore. This will fail at later stages.
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
|
165
|
+
worklist = Set[cfg.entry]
|
166
|
+
@parent.exceptions.each do |exc|
|
167
|
+
worklist.add cfg.find_node(exc.target_offset)
|
168
|
+
end
|
149
169
|
|
150
|
-
|
170
|
+
livelist = Set[]
|
151
171
|
while worklist.any?
|
152
172
|
node = worklist.first
|
153
173
|
worklist.delete node
|
154
174
|
|
155
|
-
|
175
|
+
livelist.add node
|
156
176
|
|
157
|
-
|
158
|
-
|
159
|
-
dead_opcodes.concat node.insns
|
177
|
+
node.targets.each do |target|
|
178
|
+
worklist.add target unless livelist.include? target
|
160
179
|
end
|
161
180
|
end
|
162
181
|
|
163
|
-
|
164
|
-
|
182
|
+
cfg.nodes.each do |node|
|
183
|
+
unless livelist.include? node
|
184
|
+
node.insns.each do |opcode|
|
185
|
+
delete opcode
|
186
|
+
end
|
187
|
+
end
|
165
188
|
end
|
166
189
|
|
167
190
|
recache!
|
168
191
|
|
169
|
-
|
192
|
+
livelist != cfg.nodes
|
170
193
|
end
|
171
194
|
|
172
|
-
|
195
|
+
private
|
173
196
|
|
174
197
|
def parse
|
175
198
|
sub_io = StringIO.new(@raw_code)
|
@@ -11,7 +11,14 @@ module Furnace::AVM2::Tokens
|
|
11
11
|
if origin.initializer_body
|
12
12
|
initializer_decompiler = Furnace::AVM2::Decompiler.new(
|
13
13
|
origin.initializer_body, options.merge(global_code: true))
|
14
|
-
|
14
|
+
|
15
|
+
begin
|
16
|
+
properties = initializer_decompiler.decompose_static_initializer
|
17
|
+
rescue Exception => e
|
18
|
+
# This error will be caught when decompiling the rest of the
|
19
|
+
# static initializer code. Ignore it here.
|
20
|
+
end
|
21
|
+
|
15
22
|
static_initialization = initializer_decompiler.decompile
|
16
23
|
|
17
24
|
options = options.merge(property_values: properties)
|
@@ -25,6 +32,9 @@ module Furnace::AVM2::Tokens
|
|
25
32
|
end
|
26
33
|
|
27
34
|
if static_initialization && static_initialization.children.any?
|
35
|
+
tokens << CommentToken.new(origin,
|
36
|
+
"Method ##{origin.initializer_body.method_idx}",
|
37
|
+
options) if options[:debug_funids]
|
28
38
|
tokens << static_initialization
|
29
39
|
tokens << Furnace::Code::NewlineToken.new(origin, options)
|
30
40
|
end
|
@@ -1129,6 +1129,97 @@ module Furnace::AVM2
|
|
1129
1129
|
expr(content)
|
1130
1130
|
end
|
1131
1131
|
|
1132
|
+
## Alchemy opcodes
|
1133
|
+
|
1134
|
+
ALCHEMY_BINARY_MAP = {
|
1135
|
+
:alchemy_store_int8 => AS3AlchemyStoreInt8,
|
1136
|
+
:alchemy_store_int16 => AS3AlchemyStoreInt16,
|
1137
|
+
:alchemy_store_int32 => AS3AlchemyStoreInt32,
|
1138
|
+
:alchemy_store_float32 => AS3AlchemyStoreFloat32,
|
1139
|
+
:alchemy_store_float64 => AS3AlchemyStoreFloat64
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
def expr_alchemy_binary_asm(node)
|
1143
|
+
value, address = node.children
|
1144
|
+
opcode = ALCHEMY_BINARY_MAP[node.type]
|
1145
|
+
|
1146
|
+
token(CallToken, [
|
1147
|
+
token(AsmToken),
|
1148
|
+
token(ArgumentsToken, [
|
1149
|
+
token(AsmPushToken, [ expr(value) ]),
|
1150
|
+
token(AsmPushToken, [ expr(address) ]),
|
1151
|
+
token(SupplementaryCommentToken, node.type.to_s, [
|
1152
|
+
token(AsmOpToken, opcode)
|
1153
|
+
])
|
1154
|
+
])
|
1155
|
+
])
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
alias :expr_alchemy_store_int8 :expr_alchemy_binary_asm
|
1159
|
+
alias :expr_alchemy_store_int16 :expr_alchemy_binary_asm
|
1160
|
+
alias :expr_alchemy_store_int32 :expr_alchemy_binary_asm
|
1161
|
+
alias :expr_alchemy_store_float32 :expr_alchemy_binary_asm
|
1162
|
+
alias :expr_alchemy_store_float64 :expr_alchemy_binary_asm
|
1163
|
+
|
1164
|
+
ALCHEMY_UNARY_MAP = {
|
1165
|
+
:alchemy_load_int8 => AS3AlchemyLoadInt8,
|
1166
|
+
:alchemy_load_int16 => AS3AlchemyLoadInt16,
|
1167
|
+
:alchemy_load_int32 => AS3AlchemyLoadInt32,
|
1168
|
+
|
1169
|
+
:alchemy_extend1 => AS3AlchemyExtend1,
|
1170
|
+
:alchemy_extend8 => AS3AlchemyExtend8,
|
1171
|
+
:alchemy_extend16 => AS3AlchemyExtend16
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
def expr_alchemy_unary_asm(node)
|
1175
|
+
value, = node.children
|
1176
|
+
opcode = ALCHEMY_UNARY_MAP[node.type]
|
1177
|
+
|
1178
|
+
token(CallToken, [
|
1179
|
+
token(XAsmToken, [
|
1180
|
+
token(ImmediateTypenameToken, "uint")
|
1181
|
+
]),
|
1182
|
+
token(ArgumentsToken, [
|
1183
|
+
token(AsmPushToken, [ expr(value) ]),
|
1184
|
+
token(SupplementaryCommentToken, node.type.to_s, [
|
1185
|
+
token(AsmOpToken, opcode)
|
1186
|
+
])
|
1187
|
+
])
|
1188
|
+
])
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
alias :expr_alchemy_load_int8 :expr_alchemy_unary_asm
|
1192
|
+
alias :expr_alchemy_load_int16 :expr_alchemy_unary_asm
|
1193
|
+
alias :expr_alchemy_load_int32 :expr_alchemy_unary_asm
|
1194
|
+
alias :expr_alchemy_extend1 :expr_alchemy_unary_asm
|
1195
|
+
alias :expr_alchemy_extend8 :expr_alchemy_unary_asm
|
1196
|
+
alias :expr_alchemy_extend16 :expr_alchemy_unary_asm
|
1197
|
+
|
1198
|
+
ALCHEMY_FLOAT_MAP = {
|
1199
|
+
:alchemy_load_float32 => AS3AlchemyLoadFloat32,
|
1200
|
+
:alchemy_load_float64 => AS3AlchemyLoadFloat64
|
1201
|
+
}
|
1202
|
+
|
1203
|
+
def expr_alchemy_float_asm(node)
|
1204
|
+
value, = node.children
|
1205
|
+
opcode = ALCHEMY_FLOAT_MAP[node.type]
|
1206
|
+
|
1207
|
+
token(CallToken, [
|
1208
|
+
token(XAsmToken, [
|
1209
|
+
token(ImmediateTypenameToken, "Number")
|
1210
|
+
]),
|
1211
|
+
token(ArgumentsToken, [
|
1212
|
+
token(AsmPushToken, [ expr(value) ]),
|
1213
|
+
token(SupplementaryCommentToken, node.type.to_s, [
|
1214
|
+
token(AsmOpToken, opcode)
|
1215
|
+
])
|
1216
|
+
])
|
1217
|
+
])
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
alias :expr_alchemy_load_float32 :expr_alchemy_float_asm
|
1221
|
+
alias :expr_alchemy_load_float64 :expr_alchemy_float_asm
|
1222
|
+
|
1132
1223
|
private
|
1133
1224
|
|
1134
1225
|
def token(klass, *args)
|
@@ -104,7 +104,7 @@ module Furnace::AVM2
|
|
104
104
|
end
|
105
105
|
|
106
106
|
LocalIncDecInnerMatcher = AST::Matcher.new do
|
107
|
-
[ either[*PRE_POST_OPERATORS],
|
107
|
+
[ either[*Furnace::AVM2::Transform::ASTBuild::PRE_POST_OPERATORS],
|
108
108
|
either[
|
109
109
|
[:convert, any,
|
110
110
|
[:get_local, any]],
|
@@ -342,14 +342,6 @@ module Furnace::AVM2
|
|
342
342
|
|
343
343
|
expand_conditionals()
|
344
344
|
|
345
|
-
# Spec does not require stack to be empty upon encountering return.
|
346
|
-
# If there's something left, it should have been here.
|
347
|
-
if opcode.is_a? ABC::FunctionReturnOpcode
|
348
|
-
while @stack.any?
|
349
|
-
emit(*consume(1))
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
345
|
emit(node)
|
354
346
|
elsif opcode.produces == 1
|
355
347
|
produce(node)
|
@@ -357,8 +349,12 @@ module Furnace::AVM2
|
|
357
349
|
end
|
358
350
|
end
|
359
351
|
|
352
|
+
if @stack.any?
|
353
|
+
raise "nonempty stack on exit"
|
354
|
+
end
|
355
|
+
|
360
356
|
[ @ast.normalize_hierarchy!, body, finallies.values ]
|
361
357
|
end
|
362
358
|
end
|
363
359
|
end
|
364
|
-
end
|
360
|
+
end
|
data/lib/furnace-avm2/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: furnace-avm2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: furnace
|
@@ -289,6 +289,9 @@ files:
|
|
289
289
|
- lib/furnace-avm2/source/implementation_tokens/access_token.rb
|
290
290
|
- lib/furnace-avm2/source/implementation_tokens/array_token.rb
|
291
291
|
- lib/furnace-avm2/source/implementation_tokens/as_token.rb
|
292
|
+
- lib/furnace-avm2/source/implementation_tokens/asm_op_token.rb
|
293
|
+
- lib/furnace-avm2/source/implementation_tokens/asm_push_token.rb
|
294
|
+
- lib/furnace-avm2/source/implementation_tokens/asm_token.rb
|
292
295
|
- lib/furnace-avm2/source/implementation_tokens/assignment_token.rb
|
293
296
|
- lib/furnace-avm2/source/implementation_tokens/binary_operator_token.rb
|
294
297
|
- lib/furnace-avm2/source/implementation_tokens/break_token.rb
|
@@ -345,6 +348,7 @@ files:
|
|
345
348
|
- lib/furnace-avm2/source/implementation_tokens/variable_name_token.rb
|
346
349
|
- lib/furnace-avm2/source/implementation_tokens/while_token.rb
|
347
350
|
- lib/furnace-avm2/source/implementation_tokens/with_token.rb
|
351
|
+
- lib/furnace-avm2/source/implementation_tokens/xasm_token.rb
|
348
352
|
- lib/furnace-avm2/source/implementation_tokens/xml_literal_token.rb
|
349
353
|
- lib/furnace-avm2/transform.rb
|
350
354
|
- lib/furnace-avm2/transform/ast_build.rb
|
@@ -369,7 +373,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
369
373
|
requirements:
|
370
374
|
- - ! '>='
|
371
375
|
- !ruby/object:Gem::Version
|
372
|
-
version:
|
376
|
+
version: 1.9.1
|
373
377
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
374
378
|
none: false
|
375
379
|
requirements:
|
@@ -382,4 +386,7 @@ rubygems_version: 1.8.24
|
|
382
386
|
signing_key:
|
383
387
|
specification_version: 3
|
384
388
|
summary: AVM2 analysis framework based on Furnace
|
385
|
-
test_files:
|
389
|
+
test_files:
|
390
|
+
- test/basic.as
|
391
|
+
- test/global.as
|
392
|
+
- test/switch.as
|