Permalink
Browse files

Implement finally{}.

  • Loading branch information...
1 parent ac84c27 commit 5cc880affa5c6f3ca6d1622f1f0fd02a2230afe6 @whitequark committed Jun 17, 2012
@@ -37,7 +37,7 @@ def code_to_cfg
Furnace::AVM2::Transform::CFGBuild.new
])
- pipeline.run(code_to_ast({}), self)
+ pipeline.run(*code_to_ast({}))
end
def code_to_nf
@@ -62,10 +62,14 @@ def flush!
end
def opcode_at(position)
+ parse if @raw_code
+
@pos_cache[position]
end
def offset_of(opcode)
+ parse if @raw_code
+
@opcode_cache[opcode]
end
@@ -232,16 +232,24 @@ def stmt_try(opcode, nodes)
])
handlers.each_with_index do |handler, index|
- type, variable, body = handler.children
- nodes << token(CatchToken,
- token(CatchFilterToken, [
- token(MultinameToken, variable.metadata[:origin]),
- token(MultinameToken, type.metadata[:origin])
- ]),
- within_meta_scope {
- stmt_block(body, continuation: index < handlers.size - 1)
- }
- )
+ block = within_meta_scope do
+ stmt_block(handler.children.last, continuation: index < handlers.size - 1)
+ end
+
+ if handler.type == :catch
+ type, variable, = handler.children
+ nodes << token(CatchToken,
+ token(CatchFilterToken, [
+ token(MultinameToken, variable.metadata[:origin]),
+ token(MultinameToken, type.metadata[:origin])
+ ]),
+ block
+ )
+ elsif handler.type == :finally
+ nodes << token(FinallyToken, block)
+ else
+ raise "unknown handler type #{handler.type}"
+ end
end
end
@@ -0,0 +1,11 @@
+module Furnace::AVM2::Tokens
+ class FinallyToken < Furnace::Code::SeparatedToken
+ def initialize(origin, body, options={})
+ super(origin, [ body ], options)
+ end
+
+ def text_before
+ "finally "
+ end
+ end
+end
@@ -117,10 +117,53 @@ def transform(code, body)
ternary = []
current_handler = nil
+ current_finally = nil
+
+ finallies = {}
+ body.exceptions.each_with_index do |exception, index|
+ first, second = exception, body.exceptions[index + 1]
+ next unless second
+
+ if first.from_offset == second.from_offset &&
+ second.to_offset > first.to_offset &&
+ first.target_offset > second.from_offset &&
+ first.target_offset < second.to_offset &&
+ first.to.is_a?(ABC::AS3Jump) &&
+ first.to.target.is_a?(ABC::AS3PushByte)
+ entry = first.to.target.next.target
+ epilogue = nil
+
+ cursor = entry.next
+ while cursor
+ if cursor.is_a?(ABC::AS3LookupSwitch) &&
+ cursor.body.default_offset == 8 &&
+ cursor.body.case_count == 0
+ epilogue = cursor
+ break
+ end
+
+ cursor = cursor.next
+ end
+
+ raise "cannot find finally epilogue" if epilogue.nil?
+
+ finallies[first.to_offset] = {
+ first_catch: first,
+ second_catch: second,
+ skip_intervals: [ first.target_offset...second.target_offset,
+ (second.target_offset + 4)...entry.offset ],
+ begin_offset: first.to.offset,
+ entry_offset: entry.offset,
+ epilogue_offset: epilogue.offset,
+ end_offset: epilogue.offset + epilogue.byte_length,
+ }
+ end
+ end
exceptions = {}
body.exceptions.each_with_index do |exception, index|
- exceptions[exception.target_offset] = [ index, exception ]
+ next if finallies.find { |k,f| f[:first_catch] == exception }
+ exceptions[exception.target_offset] = exception
end
code.each do |opcode|
@@ -134,8 +177,28 @@ def transform(code, body)
finalize_complex_expr(opcode, ternary, CONDITIONAL_OPERATORS, nil, [:ternary_if, false])
finalize_complex_expr(opcode, shortjump, [ :and, :or ], 1)
+ if finallies.has_key? opcode.offset
+ current_finally = finallies[opcode.offset]
+ end
+
+ if current_finally
+ if current_finally[:begin_offset] == opcode.offset
+ puts "FINALLY: begin" if @verbose
+ emit(AST::Node.new(:jump, [ current_finally[:end_offset] ], label: opcode.offset))
+ next
+ elsif current_finally[:skip_intervals].find { |si| si.include? opcode.offset }
+ puts "FINALLY: skip" if @verbose
+ next
+ elsif current_finally[:epilogue_offset] == opcode.offset
+ puts "FINALLY: epilogue" if @verbose
+ emit(AST::Node.new(:nop, [], label: opcode.offset))
+ @stack.clear
+ next
+ end
+ end
+
if exceptions.has_key? opcode.offset
- index, exception = exceptions[opcode.offset]
+ exception = exceptions[opcode.offset]
current_handler = exception
if exception.variable
@@ -285,7 +348,7 @@ def transform(code, body)
end
end
- [ @ast.normalize_hierarchy!, body ]
+ [ @ast.normalize_hierarchy!, body, finallies.values ]
end
end
end
@@ -7,10 +7,10 @@ def initialize(options={})
@options = options
end
- def transform(ast, body)
+ def transform(ast, *stuff)
visit ast
- ast
+ [ ast, *stuff ]
end
# (pop x) -> (jump-target) x
@@ -1,13 +1,18 @@
module Furnace::AVM2
module Transform
class CFGBuild
- def transform(ast, body)
+ def transform(ast, body, finallies)
@jumps = Set.new
@exceptions = {}
@cfg = CFG::Graph.new
body.exceptions.each_with_index do |exc, index|
+ if finallies.find { |f| f[:first_catch] == exc }
+ # The first catch in finally is a no-op
+ next
+ end
+
unless exc_block = @exceptions[exc.range]
exc_block = CFG::Node.new(@cfg, "exc_#{index}")
@@ -19,11 +24,18 @@ def transform(ast, body)
end
exc_block.target_labels << exc.target_offset
- exc_block.cti.children <<
- AST::Node.new(:catch,
- [ (exc.exception.to_astlet if exc.exception),
- exc.variable.to_astlet,
- exc.target_offset ])
+
+ if finallies.find { |f| f[:second_catch] == exc }
+ exc_block.cti.children <<
+ AST::Node.new(:finally,
+ [ exc.target_offset ])
+ else
+ exc_block.cti.children <<
+ AST::Node.new(:catch,
+ [ (exc.exception.to_astlet if exc.exception),
+ exc.variable.to_astlet,
+ exc.target_offset ])
+ end
end
@pending_label = nil
@@ -48,15 +48,23 @@ def possibly_wrap_eh(block, nodes, exception, loop_stack, nesting)
root, *tails = find_merge_point([ block ] + exception.targets)
exception.targets.zip(tails).each_with_index do |(target, tail), index|
- exception, var = catches[index].children
-
- log nesting, "catch #{exception.inspect} #{var.inspect}"
-
+ log nesting, "handler #{catches[index].inspect}"
handler = extended_block(target, tail, loop_stack, nesting + 1, nil)
- handlers.push AST::Node.new(:catch, [
- exception, var,
- handler
- ], catches[index].metadata)
+
+ node = catches[index]
+ if node.type == :catch
+ exception, var = node.children
+ handlers.push AST::Node.new(:catch, [
+ exception, var,
+ handler
+ ], node.metadata)
+ elsif node.type == :finally
+ handlers.push AST::Node.new(:finally, [
+ handler
+ ], node.metadata)
+ else
+ raise "unknown handler type #{node.type}"
+ end
end
[ AST::Node.new(:try, [
@@ -5,7 +5,7 @@ module Transform
class PropagateConstants
include AST::Visitor
- def transform(ast)
+ def transform(ast, *stuff)
@local_nonconst = Set.new
@local_sets = {}
@local_gets = Hash.new { |h,k| h[k] = [] }
@@ -26,7 +26,7 @@ def transform(ast)
end
end
- ast
+ [ ast, *stuff ]
end
def on_set_local(node)
@@ -3,10 +3,10 @@ module Transform
class PropagateLabels
include AST::Visitor
- def transform(ast, body)
+ def transform(ast, *stuff)
visit ast # propagate labels
- [ast, body]
+ [ ast, *stuff ]
end
def on_any(node)
View
@@ -294,6 +294,20 @@ package test {
}
class Exceptions {
+ function f() {
+ if(a) {
+ try {
+ try {
+ a();
+ } catch(e) {
+ b();
+ }
+ hoge();
+ } catch(q: SecurityError) {
+ piyo();
+ }
+ }
+ }
function e() {
if(a) {
try {

0 comments on commit 5cc880a

Please sign in to comment.