furnace-avm2 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|