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.
@@ -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