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.
@@ -1,12 +1,24 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
- begin
5
- require "bundler/setup"
6
- rescue LoadError
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
- $: << File.join(File.dirname(__FILE__), '..', 'lib')
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: #{__FILE__} [options]
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
- "catch(#{exception.exception.to_s} #{exception.variable.to_s}) #{exception.target_offset}"
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
@@ -1,12 +1,24 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
- begin
5
- require "bundler/setup"
6
- rescue LoadError
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
- $: << File.join(File.dirname(__FILE__), '..', 'lib')
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: #{__FILE__} [options]
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
@@ -1,21 +1,33 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
- begin
5
- require "bundler/setup"
6
- rescue LoadError
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
- $: << File.join(File.dirname(__FILE__), '..', 'lib')
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
- puts "$ gem install pry"
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
@@ -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("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
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
- self.from_offset = @from.offset
26
- self.to_offset = @to.offset
27
- self.target_offset = @target.offset
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 @name_set.include? indexed_name
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.gsub(/^[^a-zA-Z_$]+/, '').
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.gsub(/^[^a-zA-Z_$]+/, '').
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
- variant :QName, :nested, :class => MultinameKindQName
22
- variant :QNameA, :nested, :class => MultinameKindQName
23
- variant :RTQName, :nested, :class => MultinameKindRTQName
24
- variant :RTQNameA, :nested, :class => MultinameKindRTQName
25
- variant :RTQNameL, :nested, :class => MultinameKindRTQNameL
26
- variant :RTQNameLA, :nested, :class => MultinameKindRTQNameL
27
- variant :Multiname, :nested, :class => MultinameKindMultiname
28
- variant :MultinameA, :nested, :class => MultinameKindMultiname
29
- variant :MultinameL, :nested, :class => MultinameKindMultinameL
30
- variant :MultinameLA, :nested, :class => MultinameKindMultinameL
31
- variant :GenericName, :nested, :class => MultinameKindGenericName
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
- variant :Slot, :nested, :class => TraitSlot
45
- variant :Method, :nested, :class => TraitMethod
46
- variant :Getter, :nested, :class => TraitMethod
47
- variant :Setter, :nested, :class => TraitMethod
48
- variant :Class, :nested, :class => TraitClass
49
- variant :Function, :nested, :class => TraitFunction
50
- variant :Const, :nested, :class => TraitSlot
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?
@@ -45,6 +45,8 @@ module Furnace::AVM2::ABC
45
45
 
46
46
  define_property :instruction do |encoding|
47
47
  MAP[encoding] = self
48
+
49
+ define_singleton_method(:opcode) { encoding }
48
50
  end
49
51
 
50
52
  define_property :ast_type
@@ -74,7 +74,11 @@ module Furnace::AVM2::ABC
74
74
  end
75
75
 
76
76
  def byte_length
77
- map(&:byte_length).reduce(0, :+)
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? ControlTransferOpcode
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? AS3LookupSwitch
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
- cfg = build_cfg
148
- dead_opcodes = []
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
- worklist = cfg.nodes.dup
170
+ livelist = Set[]
151
171
  while worklist.any?
152
172
  node = worklist.first
153
173
  worklist.delete node
154
174
 
155
- next if node == cfg.entry
175
+ livelist.add node
156
176
 
157
- if node.sources.count == 0 ||
158
- node.sources == [node]
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
- dead_opcodes.each do |opcode|
164
- delete opcode
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
- dead_opcodes.any?
192
+ livelist != cfg.nodes
170
193
  end
171
194
 
172
- protected
195
+ private
173
196
 
174
197
  def parse
175
198
  sub_io = StringIO.new(@raw_code)
@@ -15,7 +15,7 @@ module Furnace::AVM2::Tokens
15
15
  if options[:debug_funids] && !options[:closure]
16
16
  @children.unshift \
17
17
  CommentToken.new(origin,
18
- "Function ##{options[:index]}",
18
+ "Method ##{options[:index]}",
19
19
  options)
20
20
  end
21
21
  end
@@ -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
- properties = initializer_decompiler.decompose_static_initializer
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)
@@ -0,0 +1,13 @@
1
+ module Furnace::AVM2::Tokens
2
+ class AsmOpToken < Furnace::Code::TerminalToken
3
+
4
+ def initialize(origin, opcode, options={})
5
+ super(origin, options)
6
+ @opcode = opcode
7
+ end
8
+
9
+ def to_text
10
+ "op(0x#{@opcode.opcode})"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Furnace::AVM2::Tokens
2
+ class AsmPushToken < Furnace::Code::SurroundedToken
3
+
4
+ def text_before
5
+ 'push('
6
+ end
7
+
8
+ def text_after
9
+ ')'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Furnace::AVM2::Tokens
2
+ class AsmToken < Furnace::Code::TerminalToken
3
+
4
+ def to_text
5
+ 'asm'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module Furnace::AVM2::Tokens
2
+ class XAsmToken < Furnace::Code::SurroundedToken
3
+
4
+ def text_before
5
+ '__xasm<'
6
+ end
7
+
8
+ def text_after
9
+ '>'
10
+ end
11
+ end
12
+ end
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Furnace
2
2
  module AVM2
3
- VERSION = "1.0.2"
3
+ VERSION = "1.0.3"
4
4
  end
5
5
  end
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.2
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-07 00:00:00.000000000 Z
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: '0'
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