diff --git a/spec/compiler/semantic/simple_rescues_spec.cr b/spec/compiler/semantic/simple_rescues_spec.cr new file mode 100644 index 000000000000..bf28d10033a9 --- /dev/null +++ b/spec/compiler/semantic/simple_rescues_spec.cr @@ -0,0 +1,264 @@ +require "../../spec_helper" + +private def assert_transform(from, to, flags = nil) + program = Program.new + program.flags = flags if flags + from_nodes = Parser.parse(from) + to_nodes = from_nodes.transform SimpleRescues.new + to_nodes.to_s.strip.should eq(to.strip) +end + +describe "Semantic: SimpleRescues" do + it "can do noop" do + assert_transform( + <<-CR + a = 1 + CR + , + <<-CR + a = 1 + CR + ) + end + + it "ensures variables on rescue blocks" do + assert_transform( + <<-CR + begin + a = 1 + rescue + a = 2 + end + CR + , + <<-CR + begin + a = 1 + rescue ___e1 + a = 2 + end + CR + ) + end + + it "ensures translate single type restrictions to conditionals" do + assert_transform( + <<-CR + begin + a = 1 + rescue e : A + e.foo + a = 2 + end + CR + , + <<-CR + begin + a = 1 + rescue e + if e.is_a?(A) + e.foo + a = 2 + else + ::raise(e) + end + end + CR + ) + end + + it "ensures translate multiple type restrictions to conditionals" do + assert_transform( + <<-CR + begin + a = 1 + rescue e : A | B + e.foo + a = 2 + end + CR + , + <<-CR + begin + a = 1 + rescue e + if e.is_a?(A | B) + e.foo + a = 2 + else + ::raise(e) + end + end + CR + ) + end + + it "ensures translate multiple rescues with type restrictions" do + assert_transform( + <<-CR + begin + lorem + rescue a : A + a.foo + rescue b : B + b.bar + end + CR + , + <<-CR + begin + lorem + rescue ___e1 + if (a = ___e1) && (a.is_a?(A)) + a.foo + else + if (b = ___e1) && (b.is_a?(B)) + b.bar + else + ::raise(___e1) + end + end + end + CR + ) + end + + it "ensures translate multiple rescues with type some restrictions" do + assert_transform( + <<-CR + begin + lorem + rescue a : A + a.foo + rescue b + b.bar + end + CR + , + <<-CR + begin + lorem + rescue ___e1 + if (a = ___e1) && (a.is_a?(A)) + a.foo + else + b = ___e1 + b.bar + end + end + CR + ) + end + + it "translate ensure with a wrapping rescue" do + assert_transform( + <<-CR + begin + lorem + rescue + foo + else + bar + ensure + qux + end + CR + , + <<-CR + (begin + begin + lorem + rescue ___e2 + foo + else + bar + end + rescue ___e1 + qux + ::raise(___e1) + end).tap do + qux + end + CR + ) + end + + it "translate ensure without rescue with a simlre rescue" do + assert_transform( + <<-CR + begin + lorem + ensure + qux + end + CR + , + <<-CR + (begin + lorem + rescue ___e1 + qux + ::raise(___e1) + end).tap do + qux + end + CR + ) + end + + it "ensures nested transforms in body" do + assert_transform( + <<-CR + begin + begin + a = 1 + rescue + a = 2 + end + rescue + a = 3 + end + CR + , + <<-CR + begin + begin + a = 1 + rescue ___e2 + a = 2 + end + rescue ___e1 + a = 3 + end + CR + ) + end + + it "ensures nested transforms in rescue" do + assert_transform( + <<-CR + begin + a = 0 + rescue + begin + a = 1 + rescue + a = 2 + end + end + CR + , + <<-CR + begin + a = 0 + rescue ___e1 + begin + a = 1 + rescue ___e2 + a = 2 + end + end + CR + ) + end +end diff --git a/src/compiler/crystal/codegen/call.cr b/src/compiler/crystal/codegen/call.cr index 8277ae58c212..c852b30741c0 100644 --- a/src/compiler/crystal/codegen/call.cr +++ b/src/compiler/crystal/codegen/call.cr @@ -457,12 +457,18 @@ class Crystal::CodeGenVisitor def codegen_call_or_invoke(node, target_def, self_type, func, call_args, raises, type, is_closure = false, fun_type = nil) set_current_debug_location node if @debug.line_numbers? + funclet = if (catch_pad = @catch_pad) + builder.build_operand_bundle_def("funclet", [catch_pad]) + else + LLVM::OperandBundleDef.null + end + if raises && (rescue_block = @rescue_block) invoke_out_block = new_block "invoke_out" - @last = builder.invoke func, call_args, invoke_out_block, rescue_block + @last = builder.invoke func, call_args, invoke_out_block, rescue_block, bundle: funclet position_at_end invoke_out_block else - @last = call func, call_args + @last = call func, call_args, bundle: funclet end if target_def.is_a?(External) && (call_convention = target_def.call_convention) diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index 25287dad1723..dcef192e31e0 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -11,7 +11,6 @@ module Crystal MALLOC_NAME = "__crystal_malloc64" MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64" REALLOC_NAME = "__crystal_realloc64" - PERSONALITY_NAME = "__crystal_personality" GET_EXCEPTION_NAME = "__crystal_get_exception" class Program @@ -97,6 +96,7 @@ module Crystal getter llvm_typer : LLVMTyper getter alloca_block : LLVM::BasicBlock getter entry_block : LLVM::BasicBlock + getter personality_name : String property last : LLVM::Value class LLVMVar @@ -129,6 +129,7 @@ module Crystal @argv : LLVM::Value @empty_md_list : LLVM::Value @rescue_block : LLVM::BasicBlock? + @catch_pad : LLVM::Value? @malloc_fun : LLVM::Function? @malloc_atomic_fun : LLVM::Function? @c_malloc_fun : LLVM::Function? @@ -156,6 +157,16 @@ module Crystal ret_type = @llvm_typer.llvm_return_type(@main_ret_type) @main = @llvm_mod.functions.add(MAIN_NAME, [llvm_context.int32, llvm_context.void_pointer.pointer], ret_type) + @personality_name = if @program.has_flag?("windows") + "__CxxFrameHandler3" + else + "__crystal_personality" + end + + if @program.has_flag?("windows") + @main.personality_function = @llvm_mod.functions.add(@personality_name, ([] of LLVM::Type), llvm_context.int32, true) + end + emit_main_def_debug_metadata(@main, "??") unless @debug.none? @context = Context.new @main, @program @@ -280,7 +291,7 @@ module Crystal def visit(node : FunDef) case node.name - when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, PERSONALITY_NAME, GET_EXCEPTION_NAME + when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, @codegen.personality_name, GET_EXCEPTION_NAME @codegen.accept node end false diff --git a/src/compiler/crystal/codegen/crystal_llvm_builder.cr b/src/compiler/crystal/codegen/crystal_llvm_builder.cr index 73e1c3f7a8a3..e0bef92227f0 100644 --- a/src/compiler/crystal/codegen/crystal_llvm_builder.cr +++ b/src/compiler/crystal/codegen/crystal_llvm_builder.cr @@ -54,6 +54,10 @@ module Crystal @builder.insert_block end + def build_operand_bundle_def(name, values : Array(LLVM::Value)) + @builder.build_operand_bundle_def(name, values) + end + def to_unsafe @builder.to_unsafe end diff --git a/src/compiler/crystal/codegen/exception.cr b/src/compiler/crystal/codegen/exception.cr index ca1eb6ef3bf6..2b5bc04503f0 100644 --- a/src/compiler/crystal/codegen/exception.cr +++ b/src/compiler/crystal/codegen/exception.cr @@ -4,6 +4,134 @@ class Crystal::CodeGenVisitor @node_ensure_exception_handlers = {} of UInt64 => Handler def visit(node : ExceptionHandler) + if @program.has_flag?("windows") + windows_runtime_exception_handling(node) + else + landing_pad(node) + end + end + + private def windows_runtime_exception_handling(node : ExceptionHandler) + context.fun.personality_function = @llvm_mod.functions[@personality_name] + + # http://llvm.org/docs/ExceptionHandling.html#overview + rescue_block = new_block "rescue" + + node_rescues = node.rescues + node_ensure = node.ensure + node_else = node.else + rescue_ensure_block = nil + + Phi.open(self, node, @needs_value) do |phi| + phi.force_exit_block = !!node_ensure + + # 1) + old_rescue_block = @rescue_block + @rescue_block = rescue_block + accept node.body + @rescue_block = old_rescue_block + + # 2) + # If there's an else, we take the value from it. + # Otherwise, the value is taken from the body. + if node_else + accept node_else + phi.add @last, node_else.type? + else + phi.add @last, node.body.type? + end + + position_at_end rescue_block + + catch_body = new_block "catch.body" + # if @catch_pad is not nil then this rescue block is inner + # http://llvm.org/docs/ExceptionHandling.html#funclet-parent-tokens + if c = @catch_pad + cs = builder.catch_switch(c, old_rescue_block || LLVM::BasicBlock.null, 1) + else + cs = builder.catch_switch(nil, old_rescue_block || LLVM::BasicBlock.null, 1) + end + builder.add_handler cs, catch_body + position_at_end catch_body + + image_base = external_constant(llvm_context.int8, "__ImageBase") + base_type_descriptor = external_constant(llvm_context.void_pointer, "\u{1}??_7type_info@@6B@") + + # .PEAX is void* + void_ptr_type_descriptor = @llvm_mod.globals.add( + llvm_context.struct([ + llvm_context.void_pointer.pointer, + llvm_context.void_pointer, + llvm_context.int8.array(6), + ]), "\u{1}??_R0PEAX@8") + void_ptr_type_descriptor.initializer = llvm_context.const_struct [ + base_type_descriptor, + llvm_context.void_pointer.null, + llvm_context.const_string(".PEAX"), + ] + + catchable_type = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32]) + void_ptr_catchable_type = @llvm_mod.globals.add( + catchable_type, "_CT??_R0PEAX@88") + void_ptr_catchable_type.initializer = llvm_context.const_struct [ + int32(1), + sub_image_base(void_ptr_type_descriptor), + int32(0), + int32(-1), + int32(0), + int32(8), + int32(0), + ] + + catchable_type_array = llvm_context.struct([llvm_context.int32, llvm_context.int32.array(1)]) + catchable_void_ptr = @llvm_mod.globals.add( + catchable_type_array, "_CTA1PEAX") + catchable_void_ptr.initializer = llvm_context.const_struct [ + int32(1), + llvm_context.int32.const_array([sub_image_base(void_ptr_catchable_type)]), + ] + + eh_throwinfo = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32]) + void_ptr_throwinfo = @llvm_mod.globals.add( + eh_throwinfo, "_TI1PEAX") + void_ptr_throwinfo.initializer = llvm_context.const_struct [ + int32(0), + int32(0), + int32(0), + sub_image_base(catchable_void_ptr), + ] + + if node_rescues + # 3) + # Make sure the rescue knows about the current ensure + # and the previous catch block + old_rescue_block = @rescue_block + @rescue_block = rescue_ensure_block || @rescue_block + + a_rescue = node_rescues[0] + var = context.vars[a_rescue.name] + old_catch_pad = @catch_pad + @catch_pad = catch_pad = builder.catch_pad cs, [void_ptr_type_descriptor, int32(0), var.pointer] + caught = new_block "caught" + accept a_rescue.body + builder.build_catch_ret catch_pad, caught + @catch_pad = old_catch_pad + + position_at_end caught + + phi.add @last, a_rescue.body.type? + end + end + + old_last = @last + builder_end = @builder.end + + @last = old_last + + false + end + + private def landing_pad(node : ExceptionHandler) rescue_block = new_block "rescue" node_rescues = node.rescues @@ -99,7 +227,7 @@ class Crystal::CodeGenVisitor position_at_end rescue_block lp_ret_type = llvm_typer.landing_pad_type - lp = builder.landing_pad lp_ret_type, main_fun(PERSONALITY_NAME), [] of LLVM::Value + lp = builder.landing_pad lp_ret_type, main_fun(self.personality_name), [] of LLVM::Value unwind_ex_obj = extract_value lp, 0 ex_type_id = extract_value lp, 1 @@ -177,7 +305,7 @@ class Crystal::CodeGenVisitor old_block = insert_block position_at_end rescue_ensure_block lp_ret_type = llvm_typer.landing_pad_type - lp = builder.landing_pad lp_ret_type, main_fun(PERSONALITY_NAME), [] of LLVM::Value + lp = builder.landing_pad lp_ret_type, main_fun(self.personality_name), [] of LLVM::Value unwind_ex_obj = extract_value lp, 0 accept node_ensure @@ -215,4 +343,22 @@ class Crystal::CodeGenVisitor @node_ensure_exception_handlers[node.object_id] = eh end end + + def external_constant(type, name) + @llvm_mod.globals[name]? || begin + c = @llvm_mod.globals.add(type, name) + c.global_constant = true + c + end + end + + def sub_image_base(value) + image_base = external_constant(llvm_context.int8, "__ImageBase") + + @builder.trunc( + @builder.sub( + @builder.ptr2int(value, llvm_context.int64), + @builder.ptr2int(image_base, llvm_context.int64)), + llvm_context.int32) + end end diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr index cf5e8be7ebf1..25a3bfa2f567 100644 --- a/src/compiler/crystal/codegen/fun.cr +++ b/src/compiler/crystal/codegen/fun.cr @@ -54,6 +54,7 @@ class Crystal::CodeGenVisitor old_alloca_block = @alloca_block old_ensure_exception_handlers = @ensure_exception_handlers old_rescue_block = @rescue_block + old_catch_pad = @catch_pad old_llvm_mod = @llvm_mod old_llvm_context = @llvm_context old_llvm_typer = @llvm_typer @@ -75,6 +76,7 @@ class Crystal::CodeGenVisitor @ensure_exception_handlers = nil @rescue_block = nil + @catch_pad = nil @needs_value = true args = codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure) @@ -152,6 +154,7 @@ class Crystal::CodeGenVisitor @ensure_exception_handlers = old_ensure_exception_handlers @rescue_block = old_rescue_block + @catch_pad = old_catch_pad @entry_block = old_entry_block @alloca_block = old_alloca_block @needs_value = old_needs_value diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index ff87cf55230f..93d0343c2b55 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -69,6 +69,8 @@ class Crystal::CodeGenVisitor codegen_primitive_load_atomic call, node, target_def, call_args when "store_atomic" codegen_primitive_store_atomic call, node, target_def, call_args + when "throw_info" + @llvm_mod.globals["_TI1PEAX"] else raise "BUG: unhandled primitive in codegen: #{node.name}" end diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index b3deb7523403..44bc2598a053 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -2292,6 +2292,8 @@ module Crystal # Nothing to do when "enum_new" # Nothing to do + when "throw_info" + node.type = program.pointer_of(program.void) else node.raise "BUG: unhandled primitive in MainVisitor: #{node.name}" end diff --git a/src/compiler/crystal/semantic/normalizer.cr b/src/compiler/crystal/semantic/normalizer.cr index a65e55986136..e82220a53536 100644 --- a/src/compiler/crystal/semantic/normalizer.cr +++ b/src/compiler/crystal/semantic/normalizer.cr @@ -5,6 +5,9 @@ require "../syntax/transformer" module Crystal class Program def normalize(node, inside_exp = false) + if self.flags.includes?("windows") + node = node.transform SimpleRescues.new + end node.transform Normalizer.new(self) end end diff --git a/src/compiler/crystal/semantic/simple_rescues.cr b/src/compiler/crystal/semantic/simple_rescues.cr new file mode 100644 index 000000000000..2f2ba8ede0a7 --- /dev/null +++ b/src/compiler/crystal/semantic/simple_rescues.cr @@ -0,0 +1,100 @@ +require "set" +require "../program" +require "../syntax/transformer" + +module Crystal + class SimpleRescues < Transformer + @i = 0 + + def transform(node : ExceptionHandler) + ensure_body = node.ensure + rescues = node.rescues + + if ensure_body + # if there is an ensure wrap it in another begin/rescue + # add the original ensure at the end of the outer rescue and + # also following the outer rescue + node.ensure = nil + var_name = next_var + + ensure_body = ensure_body.transform(self) + + body = if rescues || node.else + node + else + node.body + end + new_handler = ExceptionHandler.new( + body.transform(self), + [Rescue.new(Expressions.from([ + ensure_body, + Call.global("raise", Var.new(var_name)).at(node), + ] of ASTNode), nil, var_name)]) + tap_block = Block.new(@args = [] of Var, ensure_body.clone) + return Expressions.from [Call.new(new_handler, "tap", [] of ASTNode, tap_block)] of ASTNode + end + + if rescues + if rescues.size > 1 + var_name = next_var + rescue_body = Call.global("raise", Var.new(var_name)).at(node) + + rescues.reverse_each do |rescue_node| + typed_var_name, restriction_type, body = split_rescue rescue_node + + if restriction_type + rescue_body = type_restricted_rescue_body(var_name, + typed_var_name, restriction_type, body, rescue_body) + else + rescue_body = Expressions.from [ + Assign.new(Var.new(typed_var_name), Var.new(var_name)), + body, + ] of ASTNode + end + end + else + typed_var_name, restriction_type, body = split_rescue rescues.first + var_name = typed_var_name + if restriction_type + rescue_body = If.new( + IsA.new(Var.new(typed_var_name), restriction_type), + body, Call.global("raise", Var.new(var_name)).at(node)) + else + rescue_body = body + end + end + + node.body = node.body.transform(self) + node.rescues = [Rescue.new(rescue_body.transform(self), nil, var_name)] + end + + node + end + + private def split_rescue(node : Rescue) + types = node.types + + if types + restriction_type = types.size == 1 ? types[0] : Union.new(types) + else + restriction_type = nil + end + + {node.name || next_var, restriction_type, node.body} + end + + private def type_restricted_rescue_body(var_name : String, + typed_var_name : String, restriction_type : ASTNode, + body : ASTNode, cont : ASTNode) + If.new( + And.new(Assign.new(Var.new(typed_var_name), Var.new(var_name)), + IsA.new(Var.new(typed_var_name), restriction_type)), + body, cont) + end + + private def next_var + @i += 1 + "___e#{@i}" + end + end +end diff --git a/src/lib_c/x86_64-windows-msvc/c/throw.cr b/src/lib_c/x86_64-windows-msvc/c/throw.cr new file mode 100644 index 000000000000..8fe9429ebe5f --- /dev/null +++ b/src/lib_c/x86_64-windows-msvc/c/throw.cr @@ -0,0 +1,9 @@ +lib LibC + fun _CxxThrowException = _CxxThrowException(pExceptionObject : Void*, _ThrowInfo : Void*) : NoReturn +end + +module WindowsExt + @[Primitive(:throw_info)] + def self.throw_info : Void* + end +end diff --git a/src/llvm/function.cr b/src/llvm/function.cr index cf75d689af88..c957d91d58d8 100644 --- a/src/llvm/function.cr +++ b/src/llvm/function.cr @@ -75,4 +75,8 @@ struct LLVM::Function def params ParameterCollection.new self end + + def personality_function=(fn) + LibLLVM.set_personality_fn(self, fn) + end end diff --git a/src/llvm/lib_llvm.cr b/src/llvm/lib_llvm.cr index 7c6fc5c333ef..f15b2cf24be3 100644 --- a/src/llvm/lib_llvm.cr +++ b/src/llvm/lib_llvm.cr @@ -223,6 +223,7 @@ lib LibLLVM fun set_thread_local = LLVMSetThreadLocal(global_var : ValueRef, is_thread_local : Int32) fun is_thread_local = LLVMIsThreadLocal(global_var : ValueRef) : Int32 fun set_value_name = LLVMSetValueName(val : ValueRef, name : UInt8*) + fun set_personality_fn = LLVMSetPersonalityFn(fn : ValueRef, personality_fn : ValueRef) fun size_of = LLVMSizeOf(ty : TypeRef) : ValueRef fun size_of_type_in_bits = LLVMSizeOfTypeInBits(ref : TargetDataRef, ty : TypeRef) : UInt64 fun struct_create_named = LLVMStructCreateNamed(c : ContextRef, name : UInt8*) : TypeRef diff --git a/src/llvm/lib_llvm_ext.cr b/src/llvm/lib_llvm_ext.cr index b5e9bc418314..62c5d475050b 100644 --- a/src/llvm/lib_llvm_ext.cr +++ b/src/llvm/lib_llvm_ext.cr @@ -99,26 +99,24 @@ lib LibLLVMExt fun build_cmpxchg = LLVMExtBuildCmpxchg(builder : LibLLVM::BuilderRef, pointer : LibLLVM::ValueRef, cmp : LibLLVM::ValueRef, new : LibLLVM::ValueRef, success_ordering : LLVM::AtomicOrdering, failure_ordering : LLVM::AtomicOrdering) : LibLLVM::ValueRef fun set_ordering = LLVMExtSetOrdering(value : LibLLVM::ValueRef, ordering : LLVM::AtomicOrdering) - {% if LibLLVM::IS_38 || LibLLVM::IS_39 %} - fun build_catch_pad = LLVMExtBuildCatchPad(builder : LibLLVM::BuilderRef, - parent_pad : LibLLVM::ValueRef, - arg_count : LibC::UInt, - args : LibLLVM::ValueRef*, - name : LibC::Char*) : LibLLVM::ValueRef - - fun build_catch_ret = LLVMExtBuildCatchRet(builder : LibLLVM::BuilderRef, - pad : LibLLVM::ValueRef, - basic_block : LibLLVM::BasicBlockRef) : LibLLVM::ValueRef - - fun build_catch_switch = LLVMExtBuildCatchSwitch(builder : LibLLVM::BuilderRef, - parent_pad : LibLLVM::ValueRef, - basic_block : LibLLVM::BasicBlockRef, - num_handlers : LibC::UInt, - name : LibC::Char*) : LibLLVM::ValueRef - - fun add_handler = LLVMExtAddHandler(catch_switch_ref : LibLLVM::ValueRef, - handler : LibLLVM::BasicBlockRef) : Void - {% end %} + fun build_catch_pad = LLVMExtBuildCatchPad(builder : LibLLVM::BuilderRef, + parent_pad : LibLLVM::ValueRef, + arg_count : LibC::UInt, + args : LibLLVM::ValueRef*, + name : LibC::Char*) : LibLLVM::ValueRef + + fun build_catch_ret = LLVMExtBuildCatchRet(builder : LibLLVM::BuilderRef, + pad : LibLLVM::ValueRef, + basic_block : LibLLVM::BasicBlockRef) : LibLLVM::ValueRef + + fun build_catch_switch = LLVMExtBuildCatchSwitch(builder : LibLLVM::BuilderRef, + parent_pad : LibLLVM::ValueRef, + basic_block : LibLLVM::BasicBlockRef, + num_handlers : LibC::UInt, + name : LibC::Char*) : LibLLVM::ValueRef + + fun add_handler = LLVMExtAddHandler(catch_switch_ref : LibLLVM::ValueRef, + handler : LibLLVM::BasicBlockRef) : Void fun build_operand_bundle_def = LLVMExtBuildOperandBundleDef(name : LibC::Char*, input : LibLLVM::ValueRef*, diff --git a/src/raise.cr b/src/raise.cr index 15cf406e7001..0335b0bc54c5 100644 --- a/src/raise.cr +++ b/src/raise.cr @@ -39,24 +39,22 @@ end {% if flag?(:win32) %} require "callstack/lib_unwind" + require "c/throw" # Raises the *exception*. # # This will set the exception's callstack if it hasn't been already. # Re-raising a previously catched exception won't replace the callstack. def raise(exception : Exception) : NoReturn - exception.inspect_with_backtrace(STDERR) - LibC.exit(1) - end - - fun __crystal_personality(version : Int32, actions : LibUnwind::Action, exception_class : UInt64, exception_object : LibUnwind::Exception*, context : Void*) : LibUnwind::ReasonCode - LibUnwind::ReasonCode::NO_REASON + __crystal_raise(pointerof(exception).as(Void*)) end # :nodoc: @[Raises] - fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn - LibC.printf("EXITING: __crystal_raise called") + fun __crystal_raise(unwind_ex : Void*) : NoReturn + ti = WindowsExt.throw_info.as(Pointer({Int32, Int32, Int32, Int32})) + LibC._CxxThrowException(unwind_ex, ti) + LibC.printf "Failed to raise an exception: \n" LibC.exit(1) end {% elsif flag?(:arm) %}