From 968e6406b42d573141d425e9dffb6ad0f984f1ea Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 17 Sep 2017 15:18:46 -0300 Subject: [PATCH 1/2] Codegen: always expose `__crystal_main` This makes it possible to invoke `__crystal_main` from inside types and modules. --- src/compiler/crystal/codegen/codegen.cr | 7 +++---- src/compiler/crystal/compiler.cr | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index 06c5017b0f0d..f7e2af791ebc 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -60,8 +60,8 @@ module Crystal end end - def codegen(node, single_module = false, debug = Debug::Default, expose_crystal_main = true) - visitor = CodeGenVisitor.new self, node, single_module: single_module, debug: debug, expose_crystal_main: expose_crystal_main + def codegen(node, single_module = false, debug = Debug::Default) + visitor = CodeGenVisitor.new self, node, single_module: single_module, debug: debug node.accept visitor visitor.process_finished_hooks visitor.finish @@ -141,7 +141,7 @@ module Crystal @main_module_info : ModuleInfo @main_builder : CrystalLLVMBuilder - def initialize(@program : Program, @node : ASTNode, single_module = false, @debug = Debug::Default, expose_crystal_main = true) + def initialize(@program : Program, @node : ASTNode, single_module = false, @debug = Debug::Default) @single_module = !!single_module @abi = @program.target_machine.abi @llvm_context = LLVM::Context.new @@ -155,7 +155,6 @@ module Crystal @main_ret_type = node.type? || @program.nil_type 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) - @main.linkage = LLVM::Linkage::Internal unless expose_crystal_main emit_main_def_debug_metadata(@main, "??") unless @debug.none? diff --git a/src/compiler/crystal/compiler.cr b/src/compiler/crystal/compiler.cr index 6d93c5f30add..1855a2188370 100644 --- a/src/compiler/crystal/compiler.cr +++ b/src/compiler/crystal/compiler.cr @@ -232,7 +232,7 @@ module Crystal private def codegen(program, node : ASTNode, sources, output_filename) llvm_modules = @progress_tracker.stage("Codegen (crystal)") do - program.codegen node, debug: debug, single_module: @single_module || @release || @cross_compile || @emit, expose_crystal_main: false + program.codegen node, debug: debug, single_module: @single_module || @release || @cross_compile || @emit end if @cross_compile From 5f0fe1c09a93a8b5f53f3cb8db99af2f03644096 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Sun, 17 Sep 2017 15:19:14 -0300 Subject: [PATCH 2/2] Add Crystal.main This makes it simpler to redefine main with custom logic. --- samples/sdl/sdl/lib_sdl.cr | 6 -- src/crystal/main.cr | 112 +++++++++++++++++++++++++++++++++++++ src/main.cr | 29 ---------- src/prelude.cr | 2 +- 4 files changed, 113 insertions(+), 36 deletions(-) create mode 100644 src/crystal/main.cr delete mode 100644 src/main.cr diff --git a/samples/sdl/sdl/lib_sdl.cr b/samples/sdl/sdl/lib_sdl.cr index 8db5dda75bbe..9ee4ef32e726 100644 --- a/samples/sdl/sdl/lib_sdl.cr +++ b/samples/sdl/sdl/lib_sdl.cr @@ -150,9 +150,3 @@ lib LibSDL fun get_ticks = SDL_GetTicks : UInt32 fun flip = SDL_Flip(screen : Surface*) : Int32 end - -{% if flag?(:darwin) %} - redefine_main(SDL_main) do |main| - \{{main}} - end -{% end %} diff --git a/src/crystal/main.cr b/src/crystal/main.cr new file mode 100644 index 000000000000..3fa0586148ec --- /dev/null +++ b/src/crystal/main.cr @@ -0,0 +1,112 @@ +lib LibCrystalMain + @[Raises] + fun __crystal_main(argc : Int32, argv : UInt8**) +end + +# :nodoc: +def _crystal_main(argc : Int32, argv : UInt8**) + # TODO: remove this method and embed this inside + # Crystal.main. A bug in Crystal 0.23.1 prevents invoking + # __crystal_main from anywhere except the top level. + LibCrystalMain.__crystal_main(argc, argv) +end + +module Crystal + # Defines the main routine run by normal Crystal programs: + # + # - Initializes the GC + # - Invokes the given *block* + # - Handles unhandled exceptions + # - Invokes `at_exit` handlers + # - Flushes `STDOUT` and `STDERR` + # + # This method can be invoked if you need to define a custom + # main (as in C main) function, doing all the above steps. + # + # For example: + # + # ``` + # fun main(argc : Int32, argv : UInt8**) : Int32 + # Crystal.main do + # time = Time.now + # Crystal.main_user_code(argc, argv) + # puts "Time to execute program: #{Time.now - time}" + # end + # end + # ``` + # + # Note that the above is really just an example, almost the + # same can be accomplished with `at_exit`. But in some cases + # redefinition of C's main is needed. + def self.main(&block) + GC.init + + status = + begin + yield + 0 + rescue ex + 1 + end + + AtExitHandlers.run status + ex.inspect_with_backtrace STDERR if ex + STDOUT.flush + STDERR.flush + status + end + + # Main method run by all Crystal programs at startup. + # + # This setups up the GC, invokes your program, rescuing + # any handled exception, and then runs `at_exit` handlers. + # + # This method is automatically invoked for you, so you + # don't need to invoke it. + # + # However, if you need to define a special main C function, + # you can redefine main and invoke `Crystal.main` from it: + # + # ``` + # fun main(argc : Int32, argv : UInt8**) : Int32 + # # some setup before Crystal main + # Crystal.main(argc, argv) + # # some cleanup logic after Crystal main + # end + # ``` + # + # The `Crystal.main` can also be passed as a callback: + # + # ``` + # fun main(argc : Int32, argv : UInt8**) : Int32 + # LibFoo.init_foo_and_invoke_main(argc, argv, ->Crystal.main) + # end + # ``` + # + # Note that before `Crystal.main` is invoked the GC + # is not setup yet, so nothing that allocates memory + # in Crystal (like `new` for classes) can be used. + def self.main(argc : Int32, argv : UInt8**) + main do + main_user_code(argc, argv) + end + end + + # Executes the main user code. This normally is executed + # after initializing the GC and before executing `at_exit` handlers. + # + # You should never invoke this method unless you need to + # redefine C's main function. See `Crystal.main` for + # more details. + def self.main_user_code(argc : Int32, argv : UInt8**) + _crystal_main(argc, argv) + end +end + +# Main function that acts as C's main function. +# Invokes `Crystal.main`. +# +# Can be redefined. See `Crystal.main` for examples. +fun main(argc : Int32, argv : UInt8**) : Int32 + Crystal.main(argc, argv) +end diff --git a/src/main.cr b/src/main.cr deleted file mode 100644 index 4a2483c42c85..000000000000 --- a/src/main.cr +++ /dev/null @@ -1,29 +0,0 @@ -lib LibCrystalMain - @[Raises] - fun __crystal_main(argc : Int32, argv : UInt8**) -end - -macro redefine_main(name = main) - # :nodoc: - fun main = {{name}}(argc : Int32, argv : UInt8**) : Int32 - %ex = nil - %status = begin - GC.init - {{yield LibCrystalMain.__crystal_main(argc, argv)}} - 0 - rescue ex - %ex = ex - 1 - end - - AtExitHandlers.run %status - %ex.inspect_with_backtrace STDERR if %ex - STDOUT.flush - STDERR.flush - %status - end -end - -redefine_main do |main| - {{main}} -end diff --git a/src/prelude.cr b/src/prelude.cr index 4aa5e5954b57..2bcde1819f42 100644 --- a/src/prelude.cr +++ b/src/prelude.cr @@ -27,6 +27,7 @@ require "char" require "char/reader" require "class" require "concurrent" +require "crystal/main" require "deque" require "dir" require "enum" @@ -45,7 +46,6 @@ require "int" require "intrinsics" require "io" require "kernel" -require "main" require "math/math" require "mutex" require "named_tuple"