Skip to content

Commit

Permalink
Starting with exception handling
Browse files Browse the repository at this point in the history
  • Loading branch information
waj committed Aug 9, 2013
1 parent ebb01c3 commit a04b1e5
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 9 deletions.
28 changes: 27 additions & 1 deletion lib/crystal/codegen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def initialize(mod, node, return_type, filename = nil, debug = false, llvm_mod =
@const_block_entry = @const_block

@vars = {}
@exception_handlers = []
@block_context = []
@type = @mod

Expand Down Expand Up @@ -1108,6 +1109,21 @@ def codegen_yield(node, scope)
end
end

def visit_exception_handler(node)

catch_block = new_block "catch"

@exception_handlers << { node: node, catch_block: catch_block }
accept(node.body)
@exception_handlers.pop

@builder.position_at_end catch_block
@builder.landingpad LLVM::Struct(LLVM::Pointer(LLVM::Int8), LLVM::Int32), @llvm_mod.functions['__crystal_personality'], []
accept(node.rescues.first)

false
end

def codegen_call(node, self_type, call_args)
target_def = node.target_def
mangled_name = target_def.mangled_name(self_type)
Expand Down Expand Up @@ -1136,7 +1152,14 @@ def codegen_call(node, self_type, call_args)
end
end

@last = @builder.call fun, *call_args
if @exception_handlers.empty?
@last = @builder.call fun, *call_args
else
handler = @exception_handlers.last
invoke_out_block = new_block "invoke_out"
@last = @builder.invoke fun, call_args, invoke_out_block, handler[:catch_block]
@builder.position_at_end invoke_out_block
end

if has_struct_or_union_out_args
call_args.each_with_index do |call_arg, i|
Expand Down Expand Up @@ -1164,8 +1187,10 @@ def codegen_fun(mangled_name, target_def, self_type)
old_type = @type
old_entry_block = @entry_block
old_alloca_block = @alloca_block
old_exception_handlers = @exception_handlers

@vars = {}
@exception_handlers = []

args = []
if self_type && self_type.passed_as_self?
Expand Down Expand Up @@ -1245,6 +1270,7 @@ def codegen_fun(mangled_name, target_def, self_type)
end

@vars = old_vars
@exception_handlers = old_exception_handlers
@type = old_type
@entry_block = old_entry_block
@alloca_block = old_alloca_block
Expand Down
8 changes: 8 additions & 0 deletions lib/crystal/codegen/crystal_llvm_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ def initialize(builder, codegen)
@codegen = codegen
end

def landingpad(type, personality, clauses, name = "")
lpad = LLVM::C.build_landing_pad @builder, type, personality, clauses.length, name
LLVM::C.set_cleanup lpad, 1
clauses.each do |clause|
LLVM::C.add_clause lpad, clause
end
end

def ret(*args)
return if @end
@builder.ret *args
Expand Down
9 changes: 9 additions & 0 deletions lib/crystal/type_inference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,15 @@ def visit_pointer_cast(node)
end
end

def visit_exception_handler(node)
node.bind_to node.body
if node.rescues
node.rescues.each do |a_rescue|
node.bind_to a_rescue.body
end
end
end

def lookup_var(name)
var = @vars[name]
unless var
Expand Down
27 changes: 27 additions & 0 deletions spec/codegen/exception_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'spec_helper'

describe 'Codegen: exceptin' do

it "executes normally the main block" do
run(%q(
begin
1
rescue
2
end
)).to_i.should eq(1)
end

it "executes rescue all block" do
run(%q(
require "prelude"
begin
raise 1
1
rescue
2
end
)).to_i.should eq(2)
end

end
13 changes: 13 additions & 0 deletions spec/type_inference/exception_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'spec_helper'

describe 'Type inference: exception' do
it "type is union of main and rescue blocks" do
assert_type(%(
begin
1
rescue
'a'
end
)) { union_of(int32, char) }
end
end
9 changes: 1 addition & 8 deletions std/prelude.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,4 @@ require "string_builder"
require "symbol"
require "argv"
require "time"

def raise(msg)
print "ERROR: "
print msg.to_s
puts

exit 1
end
require "raise"
53 changes: 53 additions & 0 deletions std/raise.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
lib ABI
struct UnwindException
exception_class : UInt64
exception_cleanup : Void*
private1 : UInt64
private2 : UInt64
end

UA_SEARCH_PHASE = 1
UA_CLEANUP_PHASE = 2
UA_HANDLER_FRAME = 4
UA_FORCE_UNWIND = 8

URC_NO_REASON = 0
URC_FOREIGN_EXCEPTION_CAUGHT = 1
URC_FATAL_PHASE2_ERROR = 2
URC_FATAL_PHASE1_ERROR = 3
URC_NORMAL_STOP = 4
URC_END_OF_STACK = 5
URC_HANDLER_FOUND = 6
URC_INSTALL_CONTEXT = 7
URC_CONTINUE_UNWIND = 8

fun unwind_raise_exception = _Unwind_RaiseException(ex : UnwindException*) : Int32
end


fun __crystal_personality(version : Int32, actions : Int32, exception_class : UInt64, exception_object : ABI::UnwindException*, context : Void*) : Int32
puts "PERSONALITY: version: #{version}, actions: #{actions}, exception_class: #{exception_class}, exception_object: #{exception_object.address}"
if (actions & ABI::UA_SEARCH_PHASE) > 0
return ABI::URC_HANDLER_FOUND
elsif (actions & ABI::UA_HANDLER_FRAME) > 0
puts "??"
return ABI::URC_INSTALL_CONTEXT
else
ABI::URC_NO_REASON
end
end


# u = ABI::UnwindException.new
# personality(1, 1, 0_u64, u.ptr, Pointer(Void).malloc(1))


def raise(msg)
puts "Raising..."
ex = ABI::UnwindException.new
ex.exception_class = 0_u64
# ex.exception_cleanup = nil
ABI.unwind_raise_exception(ex.ptr)

exit 1
end

0 comments on commit a04b1e5

Please sign in to comment.