From c168035853fe8ee9b06a22e5890414e5efa61776 Mon Sep 17 00:00:00 2001 From: RX14 Date: Wed, 29 Nov 2017 13:58:10 +0000 Subject: [PATCH] Stub things out for windows --- src/callstack.cr | 10 + src/crystal/hasher.cr | 4 +- src/crystal/main.cr | 12 +- src/exception.cr | 6 +- src/gc/boehm.cr | 49 +++-- src/gc/none.cr | 42 ++-- src/io.cr | 94 +++++---- src/io/console.cr | 2 + src/io/encoding.cr | 412 +++++++++++++++++++++----------------- src/io/file_descriptor.cr | 2 +- src/io/syscall.cr | 2 + src/kernel.cr | 34 ++-- src/prelude.cr | 24 ++- src/raise.cr | 70 +++++-- src/time.cr | 14 +- 15 files changed, 458 insertions(+), 319 deletions(-) diff --git a/src/callstack.cr b/src/callstack.cr index ec291a6e4129..329571a9ee1c 100644 --- a/src/callstack.cr +++ b/src/callstack.cr @@ -1,3 +1,13 @@ +{% if flag?(:win32) %} + struct CallStack + def self.skip(*args) + # do nothing + end + end + + {% skip_file() %} +{% end %} + require "c/dlfcn" require "c/stdio" require "c/string" diff --git a/src/crystal/hasher.cr b/src/crystal/hasher.cr index 92de7044b1ff..c6aee96f347e 100644 --- a/src/crystal/hasher.cr +++ b/src/crystal/hasher.cr @@ -82,7 +82,9 @@ struct Crystal::Hasher private HASH_INF_MINUS = (-314159_i64).unsafe_as(UInt64) @@seed = uninitialized UInt64[2] - Random::Secure.random_bytes(Slice.new(pointerof(@@seed).as(UInt8*), sizeof(typeof(@@seed)))) + {% unless flag?(:win32) %} + Random::Secure.random_bytes(Slice.new(pointerof(@@seed).as(UInt8*), sizeof(typeof(@@seed)))) + {% end %} def initialize(@a : UInt64 = @@seed[0], @b : UInt64 = @@seed[1]) end diff --git a/src/crystal/main.cr b/src/crystal/main.cr index b7bdb4561f6c..06a802562aad 100644 --- a/src/crystal/main.cr +++ b/src/crystal/main.cr @@ -114,9 +114,15 @@ module Crystal # :nodoc: def self.remember_blocking_state - @@stdin_is_blocking = IO::FileDescriptor.fcntl(0, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 - @@stdout_is_blocking = IO::FileDescriptor.fcntl(1, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 - @@stderr_is_blocking = IO::FileDescriptor.fcntl(2, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + {% if flag?(:win32) %} + @@stdin_is_blocking = true + @@stdout_is_blocking = true + @@stderr_is_blocking = true + {% else %} + @@stdin_is_blocking = IO::FileDescriptor.fcntl(0, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + @@stdout_is_blocking = IO::FileDescriptor.fcntl(1, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + @@stderr_is_blocking = IO::FileDescriptor.fcntl(2, LibC::F_GETFL) & LibC::O_NONBLOCK == 0 + {% end %} end # :nodoc: diff --git a/src/exception.cr b/src/exception.cr index 2dfccda4987f..c440f89bac64 100644 --- a/src/exception.cr +++ b/src/exception.cr @@ -32,7 +32,11 @@ class Exception # The backtrace is an array of strings, each containing # “0xAddress: Function at File Line Column”. def backtrace? - @callstack.try &.printable_backtrace + {% if flag?(:win32) %} + nil + {% else %} + @callstack.try &.printable_backtrace + {% end %} end def to_s(io : IO) diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr index 311a03e281cd..bf37a5f607c7 100644 --- a/src/gc/boehm.cr +++ b/src/gc/boehm.cr @@ -1,4 +1,7 @@ -@[Link("pthread")] +{% unless flag?(:win32) %} + @[Link("pthread")] +{% end %} + {% if flag?(:freebsd) %} @[Link("gc-threaded")] {% else %} @@ -54,10 +57,12 @@ lib LibGC fun size = GC_size(addr : Void*) : LibC::SizeT - # Boehm GC requires to use GC_pthread_create and GC_pthread_join instead of pthread_create and pthread_join - fun pthread_create = GC_pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) : LibC::Int - fun pthread_join = GC_pthread_join(thread : LibC::PthreadT, value : Void**) : LibC::Int - fun pthread_detach = GC_pthread_detach(thread : LibC::PthreadT) : LibC::Int + {% unless flag?(:win32) %} + # Boehm GC requires to use GC_pthread_create and GC_pthread_join instead of pthread_create and pthread_join + fun pthread_create = GC_pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) : LibC::Int + fun pthread_join = GC_pthread_join(thread : LibC::PthreadT, value : Void**) : LibC::Int + fun pthread_detach = GC_pthread_detach(thread : LibC::PthreadT) : LibC::Int + {% end %} end module GC @@ -77,7 +82,9 @@ module GC end def self.init - LibGC.set_handle_fork(1) + {% unless flag?(:win32) %} + LibGC.set_handle_fork(1) + {% end %} LibGC.init end @@ -146,22 +153,24 @@ module GC ) end - # :nodoc: - def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) - LibGC.pthread_create(thread, attr, start, arg) - end + {% unless flag?(:win32) %} + # :nodoc: + def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) + LibGC.pthread_create(thread, attr, start, arg) + end - # :nodoc: - def self.pthread_join(thread : LibC::PthreadT) : Void* - ret = LibGC.pthread_join(thread, out value) - raise Errno.new("pthread_join") unless ret == 0 - value - end + # :nodoc: + def self.pthread_join(thread : LibC::PthreadT) : Void* + ret = LibGC.pthread_join(thread, out value) + raise Errno.new("pthread_join") unless ret == 0 + value + end - # :nodoc: - def self.pthread_detach(thread : LibC::PthreadT) - LibGC.pthread_detach(thread) - end + # :nodoc: + def self.pthread_detach(thread : LibC::PthreadT) + LibGC.pthread_detach(thread) + end + {% end %} # :nodoc: def self.stack_bottom diff --git a/src/gc/none.cr b/src/gc/none.cr index 39216c72401e..100d5799cd2e 100644 --- a/src/gc/none.cr +++ b/src/gc/none.cr @@ -1,6 +1,8 @@ -@[Link("pthread")] -lib LibC -end +{% unless flag?(:win32) %} + @[Link("pthread")] + lib LibC + end +{% end %} module GC def self.init @@ -46,22 +48,24 @@ module GC Stats.new(zero, zero, zero, zero, zero) end - # :nodoc: - def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) - LibC.pthread_create(thread, attr, start, arg) - end - - # :nodoc: - def self.pthread_join(thread : LibC::PthreadT) : Void* - ret = LibC.pthread_join(thread, out value) - raise Errno.new("pthread_join") unless ret == 0 - value - end - - # :nodoc: - def self.pthread_detach(thread : LibC::PthreadT) - LibC.pthread_detach(thread) - end + {% unless flag?(:win32) %} + # :nodoc: + def self.pthread_create(thread : LibC::PthreadT*, attr : LibC::PthreadAttrT*, start : Void* -> Void*, arg : Void*) + LibC.pthread_create(thread, attr, start, arg) + end + + # :nodoc: + def self.pthread_join(thread : LibC::PthreadT) : Void* + ret = LibC.pthread_join(thread, out value) + raise Errno.new("pthread_join") unless ret == 0 + value + end + + # :nodoc: + def self.pthread_detach(thread : LibC::PthreadT) + LibC.pthread_detach(thread) + end + {% end %} @@stack_bottom = Pointer(Void).null diff --git a/src/io.cr b/src/io.cr index a1598edb9fa0..98b46392880f 100644 --- a/src/io.cr +++ b/src/io.cr @@ -1,6 +1,8 @@ require "c/stdio" require "c/errno" -require "c/unistd" +{% unless flag?(:win32) %} + require "c/unistd" +{% end %} # The `IO` class is the basis for all input and output in Crystal. # @@ -71,6 +73,10 @@ abstract class IO End = 2 end + @encoding : EncodingOptions? + @encoder : Encoder? + @decoder : Decoder? + # Raised when an `IO` operation times out. # # ``` @@ -128,52 +134,54 @@ abstract class IO def flush end - # Creates a pair of pipe endpoints (connected to each other) - # and returns them as a two-element `Tuple`. - # - # ``` - # reader, writer = IO.pipe - # writer.puts "hello" - # writer.puts "world" - # reader.gets # => "hello" - # reader.gets # => "world" - # ``` - def self.pipe(read_blocking = false, write_blocking = false) - pipe_fds = uninitialized StaticArray(LibC::Int, 2) - if LibC.pipe(pipe_fds) != 0 - raise Errno.new("Could not create pipe") - end + {% unless flag?(:win32) %} + # Creates a pair of pipe endpoints (connected to each other) + # and returns them as a two-element `Tuple`. + # + # ``` + # reader, writer = IO.pipe + # writer.puts "hello" + # writer.puts "world" + # reader.gets # => "hello" + # reader.gets # => "world" + # ``` + def self.pipe(read_blocking = false, write_blocking = false) + pipe_fds = uninitialized StaticArray(LibC::Int, 2) + if LibC.pipe(pipe_fds) != 0 + raise Errno.new("Could not create pipe") + end - r = IO::FileDescriptor.new(pipe_fds[0], read_blocking) - w = IO::FileDescriptor.new(pipe_fds[1], write_blocking) - r.close_on_exec = true - w.close_on_exec = true - w.sync = true + r = IO::FileDescriptor.new(pipe_fds[0], read_blocking) + w = IO::FileDescriptor.new(pipe_fds[1], write_blocking) + r.close_on_exec = true + w.close_on_exec = true + w.sync = true - {r, w} - end + {r, w} + end - # Creates a pair of pipe endpoints (connected to each other) and passes them - # to the given block. Both endpoints are closed after the block. - # - # ``` - # IO.pipe do |reader, writer| - # writer.puts "hello" - # writer.puts "world" - # reader.gets # => "hello" - # reader.gets # => "world" - # end - # ``` - def self.pipe(read_blocking = false, write_blocking = false) - r, w = IO.pipe(read_blocking, write_blocking) - begin - yield r, w - ensure - w.flush - r.close - w.close + # Creates a pair of pipe endpoints (connected to each other) and passes them + # to the given block. Both endpoints are closed after the block. + # + # ``` + # IO.pipe do |reader, writer| + # writer.puts "hello" + # writer.puts "world" + # reader.gets # => "hello" + # reader.gets # => "world" + # end + # ``` + def self.pipe(read_blocking = false, write_blocking = false) + r, w = IO.pipe(read_blocking, write_blocking) + begin + yield r, w + ensure + w.flush + r.close + w.close + end end - end + {% end %} # Writes the given object into this `IO`. # This ends up calling `to_s(io)` on the object. diff --git a/src/io/console.cr b/src/io/console.cr index 236c0a47c6f5..da639da274d2 100644 --- a/src/io/console.cr +++ b/src/io/console.cr @@ -1,3 +1,5 @@ +{% skip_file() if flag?(:win32) %} + require "termios" class IO::FileDescriptor < IO diff --git a/src/io/encoding.cr b/src/io/encoding.cr index b4c90fa471c4..f3d0c72be5f9 100644 --- a/src/io/encoding.cr +++ b/src/io/encoding.cr @@ -15,238 +15,288 @@ class IO end end - private class Encoder - def initialize(@encoding_options : EncodingOptions) - @iconv = Iconv.new("UTF-8", encoding_options.name, encoding_options.invalid) - @closed = false - end + {% if flag?(:win32) %} + private class Encoder + def initialize(@encoding_options : EncodingOptions) + raise NotImplementedError.new("IO::Encoder.new") + end - def write(io, slice : Bytes) - inbuf_ptr = slice.to_unsafe - inbytesleft = LibC::SizeT.new(slice.size) - outbuf = uninitialized UInt8[1024] - while inbytesleft > 0 - outbuf_ptr = outbuf.to_unsafe - outbytesleft = LibC::SizeT.new(outbuf.size) - err = @iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft)) - if err == -1 - @iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft)) - end - io.write(outbuf.to_slice[0, outbuf.size - outbytesleft]) + def write(io, slice : Bytes) + raise NotImplementedError.new("IO::Encoder#write") end - end - def close - return if @closed - @closed = true - @iconv.close + def close + raise NotImplementedError.new("IO::Encoder#close") + end end - def finalize - close - end - end + private class Decoder + def initialize(@encoding_options : EncodingOptions) + raise NotImplementedError.new("IO::Decoder.new") + end - private class Decoder - BUFFER_SIZE = 4 * 1024 - OUT_BUFFER_SIZE = 4 * 1024 + def read(io) + raise NotImplementedError.new("IO::Decoder#read") + end - property out_slice : Bytes + def read_byte(io) + raise NotImplementedError.new("IO::Decoder#read_byte") + end - @in_buffer : Pointer(UInt8) + def read_utf8(io, slice) + raise NotImplementedError.new("IO::Decoder#read_utf8") + end - def initialize(@encoding_options : EncodingOptions) - @iconv = Iconv.new(encoding_options.name, "UTF-8", encoding_options.invalid) - @buffer = Bytes.new((GC.malloc_atomic(BUFFER_SIZE).as(UInt8*)), BUFFER_SIZE) - @in_buffer = @buffer.to_unsafe - @in_buffer_left = LibC::SizeT.new(0) - @out_buffer = Bytes.new((GC.malloc_atomic(OUT_BUFFER_SIZE).as(UInt8*)), OUT_BUFFER_SIZE) - @out_slice = Bytes.empty - @closed = false - end + def gets(io, delimiter : UInt8, limit : Int, chomp) + raise NotImplementedError.new("IO::Decoder#gets") + end - def read(io) - loop do - return unless @out_slice.empty? + def write(io) + raise NotImplementedError.new("IO::Decoder#write") + end - if @in_buffer_left == 0 - @in_buffer = @buffer.to_unsafe - @in_buffer_left = LibC::SizeT.new(io.read(@buffer)) - end + def write(io, numbytes) + raise NotImplementedError.new("IO::Decoder#write") + end - # If we just have a few bytes to decode, read more, just in case these don't produce a character - if @in_buffer_left < 16 - buffer_remaining = BUFFER_SIZE - @in_buffer_left - (@in_buffer - @buffer.to_unsafe) - @buffer.copy_from(@in_buffer, @in_buffer_left) - @in_buffer = @buffer.to_unsafe - @in_buffer_left += LibC::SizeT.new(io.read(Slice.new(@in_buffer + @in_buffer_left, buffer_remaining))) - end + def close + raise NotImplementedError.new("IO::Decoder#close") + end + end + {% else %} + private class Encoder + def initialize(@encoding_options : EncodingOptions) + @iconv = Iconv.new("UTF-8", encoding_options.name, encoding_options.invalid) + @closed = false + end - # If, after refilling the buffer, we couldn't read new bytes - # it means we reached the end - break if @in_buffer_left == 0 - - # Convert bytes using iconv - out_buffer = @out_buffer.to_unsafe - out_buffer_left = LibC::SizeT.new(OUT_BUFFER_SIZE) - result = @iconv.convert(pointerof(@in_buffer), pointerof(@in_buffer_left), pointerof(out_buffer), pointerof(out_buffer_left)) - @out_slice = @out_buffer[0, OUT_BUFFER_SIZE - out_buffer_left] - - # Check for errors - if result == -1 - case Errno.value - when Errno::EILSEQ - # For an illegal sequence we just skip one byte and we'll continue next - @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left)) - when Errno::EINVAL - # EINVAL means "An incomplete multibyte sequence has been encountered in the input." - old_in_buffer_left = @in_buffer_left - - # On invalid multibyte sequence we try to read more bytes - # to see if they complete the sequence - refill_in_buffer(io) - - # If we couldn't read anything new, we raise or skip - if old_in_buffer_left == @in_buffer_left - @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left)) - end + def write(io, slice : Bytes) + inbuf_ptr = slice.to_unsafe + inbytesleft = LibC::SizeT.new(slice.size) + outbuf = uninitialized UInt8[1024] + while inbytesleft > 0 + outbuf_ptr = outbuf.to_unsafe + outbytesleft = LibC::SizeT.new(outbuf.size) + err = @iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft)) + if err == -1 + @iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft)) end - - # Continue decoding after an error - next + io.write(outbuf.to_slice[0, outbuf.size - outbytesleft]) end - - break end - end - private def refill_in_buffer(io) - buffer_remaining = BUFFER_SIZE - @in_buffer_left - (@in_buffer - @buffer.to_unsafe) - if buffer_remaining < 64 - @buffer.copy_from(@in_buffer, @in_buffer_left) - @in_buffer = @buffer.to_unsafe - buffer_remaining = BUFFER_SIZE - @in_buffer_left + def close + return if @closed + @closed = true + @iconv.close end - @in_buffer_left += LibC::SizeT.new(io.read(Slice.new(@in_buffer + @in_buffer_left, buffer_remaining))) - end - def read_byte(io) - read(io) - if out_slice.empty? - nil - else - byte = out_slice.to_unsafe.value - advance 1 - byte + def finalize + close end end - def read_utf8(io, slice) - count = 0 - until slice.empty? - read(io) - break if out_slice.empty? + private class Decoder + BUFFER_SIZE = 4 * 1024 + OUT_BUFFER_SIZE = 4 * 1024 + + property out_slice : Bytes + + @in_buffer : Pointer(UInt8) - available = Math.min(out_slice.size, slice.size) - out_slice[0, available].copy_to(slice.to_unsafe, available) - advance(available) - count += available - slice += available + def initialize(@encoding_options : EncodingOptions) + @iconv = Iconv.new(encoding_options.name, "UTF-8", encoding_options.invalid) + @buffer = Bytes.new((GC.malloc_atomic(BUFFER_SIZE).as(UInt8*)), BUFFER_SIZE) + @in_buffer = @buffer.to_unsafe + @in_buffer_left = LibC::SizeT.new(0) + @out_buffer = Bytes.new((GC.malloc_atomic(OUT_BUFFER_SIZE).as(UInt8*)), OUT_BUFFER_SIZE) + @out_slice = Bytes.empty + @closed = false end - count - end - def gets(io, delimiter : UInt8, limit : Int, chomp) - read(io) - return nil if @out_slice.empty? + def read(io) + loop do + return unless @out_slice.empty? - index = @out_slice.index(delimiter) - if index - # If we find it past the limit, limit the result - if index >= limit - index = limit - else - index += 1 - end + if @in_buffer_left == 0 + @in_buffer = @buffer.to_unsafe + @in_buffer_left = LibC::SizeT.new(io.read(@buffer)) + end + + # If we just have a few bytes to decode, read more, just in case these don't produce a character + if @in_buffer_left < 16 + buffer_remaining = BUFFER_SIZE - @in_buffer_left - (@in_buffer - @buffer.to_unsafe) + @buffer.copy_from(@in_buffer, @in_buffer_left) + @in_buffer = @buffer.to_unsafe + @in_buffer_left += LibC::SizeT.new(io.read(Slice.new(@in_buffer + @in_buffer_left, buffer_remaining))) + end - return gets_index(index, delimiter, chomp) + # If, after refilling the buffer, we couldn't read new bytes + # it means we reached the end + break if @in_buffer_left == 0 + + # Convert bytes using iconv + out_buffer = @out_buffer.to_unsafe + out_buffer_left = LibC::SizeT.new(OUT_BUFFER_SIZE) + result = @iconv.convert(pointerof(@in_buffer), pointerof(@in_buffer_left), pointerof(out_buffer), pointerof(out_buffer_left)) + @out_slice = @out_buffer[0, OUT_BUFFER_SIZE - out_buffer_left] + + # Check for errors + if result == -1 + case Errno.value + when Errno::EILSEQ + # For an illegal sequence we just skip one byte and we'll continue next + @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left)) + when Errno::EINVAL + # EINVAL means "An incomplete multibyte sequence has been encountered in the input." + old_in_buffer_left = @in_buffer_left + + # On invalid multibyte sequence we try to read more bytes + # to see if they complete the sequence + refill_in_buffer(io) + + # If we couldn't read anything new, we raise or skip + if old_in_buffer_left == @in_buffer_left + @iconv.handle_invalid(pointerof(@in_buffer), pointerof(@in_buffer_left)) + end + end + + # Continue decoding after an error + next + end + + break + end end - # Check if there's limit bytes in the out slice - if @out_slice.size >= limit - return gets_index(limit, delimiter, chomp) + private def refill_in_buffer(io) + buffer_remaining = BUFFER_SIZE - @in_buffer_left - (@in_buffer - @buffer.to_unsafe) + if buffer_remaining < 64 + @buffer.copy_from(@in_buffer, @in_buffer_left) + @in_buffer = @buffer.to_unsafe + buffer_remaining = BUFFER_SIZE - @in_buffer_left + end + @in_buffer_left += LibC::SizeT.new(io.read(Slice.new(@in_buffer + @in_buffer_left, buffer_remaining))) end - # We need to read from the out_slice into a String until we find that byte, - # or until we consumed limit bytes - String.build do |str| - loop do - limit -= @out_slice.size - write str + def read_byte(io) + read(io) + if out_slice.empty? + nil + else + byte = out_slice.to_unsafe.value + advance 1 + byte + end + end + def read_utf8(io, slice) + count = 0 + until slice.empty? read(io) + break if out_slice.empty? - break if @out_slice.empty? + available = Math.min(out_slice.size, slice.size) + out_slice[0, available].copy_to(slice.to_unsafe, available) + advance(available) + count += available + slice += available + end + count + end - index = @out_slice.index(delimiter) - if index - if index >= limit - index = limit - else - index += 1 - end - write str, index - break + def gets(io, delimiter : UInt8, limit : Int, chomp) + read(io) + return nil if @out_slice.empty? + + index = @out_slice.index(delimiter) + if index + # If we find it past the limit, limit the result + if index >= limit + index = limit else - if limit < @out_slice.size - write(str, limit) + index += 1 + end + + return gets_index(index, delimiter, chomp) + end + + # Check if there's limit bytes in the out slice + if @out_slice.size >= limit + return gets_index(limit, delimiter, chomp) + end + + # We need to read from the out_slice into a String until we find that byte, + # or until we consumed limit bytes + String.build do |str| + loop do + limit -= @out_slice.size + write str + + read(io) + + break if @out_slice.empty? + + index = @out_slice.index(delimiter) + if index + if index >= limit + index = limit + else + index += 1 + end + write str, index break + else + if limit < @out_slice.size + write(str, limit) + break + end end end + str.chomp!(delimiter) if chomp end - str.chomp!(delimiter) if chomp end - end - - private def gets_index(index, delimiter, chomp) - advance_increment = index - if chomp && index > 0 && @out_slice[index - 1] === delimiter - index -= 1 + private def gets_index(index, delimiter, chomp) + advance_increment = index - if delimiter === '\n' && index > 0 && @out_slice[index - 1] === '\r' + if chomp && index > 0 && @out_slice[index - 1] === delimiter index -= 1 + + if delimiter === '\n' && index > 0 && @out_slice[index - 1] === '\r' + index -= 1 + end end - end - string = String.new(@out_slice[0, index]) - advance(advance_increment) - string - end + string = String.new(@out_slice[0, index]) + advance(advance_increment) + string + end - def write(io) - io.write @out_slice - @out_slice = Bytes.empty - end + def write(io) + io.write @out_slice + @out_slice = Bytes.empty + end - def write(io, numbytes) - io.write @out_slice[0, numbytes] - @out_slice += numbytes - end + def write(io, numbytes) + io.write @out_slice[0, numbytes] + @out_slice += numbytes + end - def advance(numbytes) - @out_slice += numbytes - end + def advance(numbytes) + @out_slice += numbytes + end - def close - return if @closed - @closed = true + def close + return if @closed + @closed = true - @iconv.close - end + @iconv.close + end - def finalize - close + def finalize + close + end end - end + {% end %} end diff --git a/src/io/file_descriptor.cr b/src/io/file_descriptor.cr index c501c050e6fd..2c8e6b3266b0 100644 --- a/src/io/file_descriptor.cr +++ b/src/io/file_descriptor.cr @@ -13,7 +13,7 @@ class IO::FileDescriptor < IO def initialize(@fd, blocking = false) @closed = false - unless blocking + unless blocking || {{flag?(:win32)}} self.blocking = false end end diff --git a/src/io/syscall.cr b/src/io/syscall.cr index 65c6826b5842..f7e9f0b7651c 100644 --- a/src/io/syscall.cr +++ b/src/io/syscall.cr @@ -1,3 +1,5 @@ +{% skip_file() if flag?(:win32) %} + module IO::Syscall @read_timed_out = false @write_timed_out = false diff --git a/src/kernel.cr b/src/kernel.cr index 47067d7b2e4d..89031da4d4df 100644 --- a/src/kernel.cr +++ b/src/kernel.cr @@ -1,8 +1,14 @@ -require "c/unistd" - -STDIN = IO::FileDescriptor.new(0, blocking: LibC.isatty(0) == 0) -STDOUT = (IO::FileDescriptor.new(1, blocking: LibC.isatty(1) == 0)).tap { |f| f.flush_on_newline = true } -STDERR = (IO::FileDescriptor.new(2, blocking: LibC.isatty(2) == 0)).tap { |f| f.flush_on_newline = true } +{% if flag?(:win32) %} + STDIN = IO::FileDescriptor.new(0) + STDOUT = (IO::FileDescriptor.new(1)).tap { |f| f.flush_on_newline = true } + STDERR = (IO::FileDescriptor.new(2)).tap { |f| f.flush_on_newline = true } +{% else %} + require "c/unistd" + + STDIN = IO::FileDescriptor.new(0, blocking: LibC.isatty(0) == 0) + STDOUT = (IO::FileDescriptor.new(1, blocking: LibC.isatty(1) == 0)).tap { |f| f.flush_on_newline = true } + STDERR = (IO::FileDescriptor.new(2, blocking: LibC.isatty(2) == 0)).tap { |f| f.flush_on_newline = true } +{% end %} PROGRAM_NAME = String.new(ARGV_UNSAFE.value) ARGV = Array.new(ARGC_UNSAFE - 1) { |i| String.new(ARGV_UNSAFE[1 + i]) } @@ -190,14 +196,16 @@ class Process end end -Signal.setup_default_handlers +{% unless flag?(:win32) %} + Signal.setup_default_handlers -at_exit { Event::SignalHandler.close } + at_exit { Event::SignalHandler.close } -# Background loop to cleanup unused fiber stacks. -spawn do - loop do - sleep 5 - Fiber.stack_pool_collect + # Background loop to cleanup unused fiber stacks. + spawn do + loop do + sleep 5 + Fiber.stack_pool_collect + end end -end +{% end %} diff --git a/src/prelude.cr b/src/prelude.cr index 88a80bb33e65..7189fb603fd3 100644 --- a/src/prelude.cr +++ b/src/prelude.cr @@ -7,6 +7,12 @@ # to also add them to `docs_main.cr` if their content need to # appear in the API docs. +private macro no_win(stmt) + {% unless flag?(:win32) %} + {{stmt}} + {% end %} +end + # This list requires ordered statements require "lib_c" require "macros" @@ -26,26 +32,26 @@ require "box" require "char" require "char/reader" require "class" -require "concurrent" +no_win require "concurrent" require "crystal/main" require "deque" -require "dir" +no_win require "dir" require "enum" require "enumerable" require "env" require "errno" require "ext" -require "file" +no_win require "file" require "float" require "gc" require "hash" -require "iconv" +no_win require "iconv" require "int" require "intrinsics" require "io" require "kernel" require "math/math" -require "mutex" +no_win require "mutex" require "named_tuple" require "nil" require "number" @@ -53,7 +59,7 @@ require "pointer" require "pretty_print" require "primitives" require "proc" -require "process" +no_win require "process" require "raise" require "random" require "range" @@ -61,13 +67,13 @@ require "reference" require "reflect" require "regex" require "set" -require "signal" +no_win require "signal" require "slice" require "static_array" require "struct" require "symbol" -require "system" -require "thread" +no_win require "system" +no_win require "thread" require "time" require "tuple" require "unicode" diff --git a/src/raise.cr b/src/raise.cr index b501ceb606a4..15cf406e7001 100644 --- a/src/raise.cr +++ b/src/raise.cr @@ -37,7 +37,29 @@ private struct LEBReader end end -{% if flag?(:arm) %} +{% if flag?(:win32) %} + require "callstack/lib_unwind" + + # 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 + end + + # :nodoc: + @[Raises] + fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn + LibC.printf("EXITING: __crystal_raise called") + LibC.exit(1) + end +{% elsif flag?(:arm) %} # On ARM EHABI the personality routine is responsible for actually # unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). private macro __crystal_continue_unwind @@ -161,33 +183,37 @@ end end {% end %} -# :nodoc: -@[Raises] -fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn - ret = LibUnwind.raise_exception(unwind_ex) - LibC.dprintf 2, "Failed to raise an exception: %s\n", ret.to_s - CallStack.print_backtrace - LibC.exit(ret) -end +{% unless flag?(:win32) %} + # :nodoc: + @[Raises] + fun __crystal_raise(unwind_ex : LibUnwind::Exception*) : NoReturn + ret = LibUnwind.raise_exception(unwind_ex) + LibC.dprintf 2, "Failed to raise an exception: %s\n", ret.to_s + CallStack.print_backtrace + LibC.exit(ret) + end +{% end %} # :nodoc: fun __crystal_get_exception(unwind_ex : LibUnwind::Exception*) : UInt64 unwind_ex.value.exception_object end -# 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.callstack ||= CallStack.new - unwind_ex = Pointer(LibUnwind::Exception).malloc - unwind_ex.value.exception_class = LibC::SizeT.zero - unwind_ex.value.exception_cleanup = LibC::SizeT.zero - unwind_ex.value.exception_object = exception.object_id - unwind_ex.value.exception_type_id = exception.crystal_type_id - __crystal_raise(unwind_ex) -end +{% unless flag?(:win32) %} + # 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.callstack ||= CallStack.new + unwind_ex = Pointer(LibUnwind::Exception).malloc + unwind_ex.value.exception_class = LibC::SizeT.zero + unwind_ex.value.exception_cleanup = LibC::SizeT.zero + unwind_ex.value.exception_object = exception.object_id + unwind_ex.value.exception_type_id = exception.crystal_type_id + __crystal_raise(unwind_ex) + end +{% end %} # Raises an Exception with the *message*. def raise(message : String) : NoReturn diff --git a/src/time.cr b/src/time.cr index ff09bc3e7d94..79ee7ba971b6 100644 --- a/src/time.cr +++ b/src/time.cr @@ -208,12 +208,14 @@ struct Time new(seconds: seconds, nanoseconds: nanosecond.to_i, kind: kind) end - # :nodoc: - def self.new(time : LibC::Timespec, kind = Kind::Unspecified) - seconds = UNIX_SECONDS + time.tv_sec - nanoseconds = time.tv_nsec.to_i - new(seconds: seconds, nanoseconds: nanoseconds, kind: kind) - end + {% unless flag?(:win32) %} + # :nodoc: + def self.new(time : LibC::Timespec, kind = Kind::Unspecified) + seconds = UNIX_SECONDS + time.tv_sec + nanoseconds = time.tv_nsec.to_i + new(seconds: seconds, nanoseconds: nanoseconds, kind: kind) + end + {% end %} def initialize(*, @seconds : Int64, @nanoseconds : Int32, @kind : Kind) unless 0 <= @nanoseconds < NANOSECONDS_PER_SECOND