aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base/runtime/core_builtin.odin13
-rw-r--r--base/runtime/core_builtin_soa.odin5
-rw-r--r--base/runtime/internal.odin6
-rw-r--r--base/runtime/print.odin13
-rw-r--r--core/io/util.odin8
-rw-r--r--core/net/errors.odin8
-rw-r--r--core/net/socket.odin19
-rw-r--r--core/os/os2/file_windows.odin36
-rw-r--r--core/reflect/reflect.odin12
-rw-r--r--core/strings/builder.odin37
-rw-r--r--core/sys/darwin/Foundation/NSApplication.odin19
-rw-r--r--core/sys/darwin/Foundation/NSCursor.odin26
-rw-r--r--core/sys/darwin/Foundation/NSEvent.odin5
-rw-r--r--core/sys/darwin/Foundation/NSObject.odin8
-rw-r--r--core/sys/darwin/Foundation/NSRunLoop.odin27
-rw-r--r--core/sys/darwin/Foundation/NSTimer.odin15
-rw-r--r--core/sys/darwin/Foundation/NSWindow.odin8
-rw-r--r--core/sys/linux/sys.odin27
-rw-r--r--core/sys/posix/poll.odin2
-rw-r--r--core/sys/wasm/js/odin.js2
-rw-r--r--src/checker.cpp6
-rw-r--r--src/llvm_backend.cpp2
-rw-r--r--src/llvm_backend_utility.cpp6
-rw-r--r--tests/core/net/test_core_net.odin35
-rw-r--r--tests/core/sys/posix/structs/structs.c1
-rw-r--r--tests/core/sys/posix/structs/structs.odin2
-rw-r--r--vendor/darwin/QuartzCore/QuartzCore.odin22
27 files changed, 304 insertions, 66 deletions
diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin
index 9f3fcbf9a..6fecbaad9 100644
--- a/base/runtime/core_builtin.odin
+++ b/base/runtime/core_builtin.odin
@@ -267,7 +267,10 @@ non_zero_resize :: proc{
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
@builtin
-shrink :: proc{shrink_dynamic_array, shrink_map}
+shrink :: proc{
+ shrink_dynamic_array,
+ shrink_map,
+}
// `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
@builtin
@@ -794,7 +797,11 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
}
// `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across"
-@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
+@builtin inject_at :: proc{
+ inject_at_elem,
+ inject_at_elems,
+ inject_at_elem_string,
+}
@@ -861,7 +868,6 @@ assign_at :: proc{
-
// `clear_dynamic_array` will set the length of a passed dynamic array to `0`
//
// Note: Prefer the procedure group `clear`.
@@ -1000,6 +1006,7 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
// If `len(array) < new_cap`, then `len(array)` will be left unchanged.
//
// Note: Prefer the procedure group `shrink`
+@builtin
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
}
diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin
index 0ccc8fd9b..5078b85d0 100644
--- a/base/runtime/core_builtin_soa.odin
+++ b/base/runtime/core_builtin_soa.odin
@@ -615,7 +615,7 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no
// `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across"
@builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa}
-
+@builtin
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
@@ -626,6 +626,7 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
return nil
}
+@builtin
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
@@ -644,7 +645,7 @@ delete_soa :: proc{
delete_soa_dynamic_array,
}
-
+@builtin
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin
index 8956ae303..cf96098b8 100644
--- a/base/runtime/internal.odin
+++ b/base/runtime/internal.odin
@@ -148,6 +148,7 @@ mem_alloc_non_zeroed :: #force_no_inline proc(size: int, alignment: int = DEFAUL
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
}
+@builtin
mem_free :: #force_no_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
@@ -172,7 +173,7 @@ mem_free_bytes :: #force_no_inline proc(bytes: []byte, allocator := context.allo
return err
}
-
+@builtin
mem_free_all :: #force_no_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
if allocator.procedure != nil {
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
@@ -341,7 +342,7 @@ memory_compare :: proc "contextless" (x, y: rawptr, n: int) -> int #no_bounds_ch
case y == nil: return +1
}
a, b := cast([^]byte)x, cast([^]byte)y
-
+
n := uint(n)
i := uint(0)
m := uint(0)
@@ -1409,4 +1410,3 @@ when .Address in ODIN_SANITIZER_FLAGS {
__asan_unpoison_memory_region :: proc "system" (address: rawptr, size: uint) ---
}
}
-
diff --git a/base/runtime/print.odin b/base/runtime/print.odin
index 90119c699..4ef142037 100644
--- a/base/runtime/print.odin
+++ b/base/runtime/print.odin
@@ -184,10 +184,11 @@ print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_ch
print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
+ b :: u64(10)
+ u := x
+
a: [129]byte
i := len(a)
- b := u64(10)
- u := x
for u >= b {
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
@@ -199,11 +200,9 @@ print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check {
- b :: i64(10)
-
- u := x
- neg := u < 0
- u = abs(u)
+ b :: u64(10)
+ u := u64(abs(x))
+ neg := x < 0
a: [129]byte
i := len(a)
diff --git a/core/io/util.odin b/core/io/util.odin
index 86a89cfb1..a82a80be0 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -21,12 +21,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri
}
write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
- buf: [32]byte
+ buf: [64]byte
s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(w, s, n_written)
}
write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
- buf: [32]byte
+ buf: [65]byte
s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(w, s, n_written)
}
@@ -39,12 +39,12 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> (
}
write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
- buf: [39]byte
+ buf: [128]byte
s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
return write_string(w, s, n_written)
}
write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
- buf: [40]byte
+ buf: [129]byte
s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
return write_string(w, s, n_written)
}
diff --git a/core/net/errors.odin b/core/net/errors.odin
index 4853327b0..de53640fc 100644
--- a/core/net/errors.odin
+++ b/core/net/errors.odin
@@ -149,7 +149,8 @@ TCP_Recv_Error :: enum i32 {
Invalid_Argument,
// The socket is not connected.
Not_Connected,
- // Connection was closed/broken/shutdown while receiving data.
+ // Connection was closed due to an error or shutdown.
+ // NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return.
Connection_Closed,
// Timed out before being able to receive any data.
Timeout,
@@ -170,7 +171,8 @@ UDP_Recv_Error :: enum i32 {
Insufficient_Resources,
// Invalid socket or buffer given.
Invalid_Argument,
- // "Connection" was refused by remote, or closed/broken/shutdown while receiving data.
+ // "Connection" was refused, or closed due to an error.
+ // NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return.
Connection_Refused,
// Timed out before being able to receive any data.
Timeout,
@@ -193,7 +195,7 @@ TCP_Send_Error :: enum i32 {
Insufficient_Resources,
// Invalid socket or buffer given.
Invalid_Argument,
- // Connection was closed/broken/shutdown while receiving data.
+ // Connection was closed/broken/shutdown while sending data.
Connection_Closed,
// The socket is not connected.
Not_Connected,
diff --git a/core/net/socket.odin b/core/net/socket.odin
index 7e96ba2b2..edb47cd0b 100644
--- a/core/net/socket.odin
+++ b/core/net/socket.odin
@@ -193,21 +193,36 @@ close :: proc(socket: Any_Socket) {
_close(socket)
}
+/*
+ Receive data into a buffer from a TCP socket.
+
+ If no error occurs, `recv_tcp` returns the number of bytes received and `buf` will contain this data received.
+ If the connection has been gracefully closed, the return value is `0, nil` (0 bytes read and no error).
+*/
recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
return _recv_tcp(socket, buf)
}
+/*
+ Receive data into a buffer from a UDP socket.
+
+ If no error occurs, `recv_udp` returns the number of bytes received and `buf` will contain this data received.
+ If the "connection" has been gracefully closed, the return value is `0, nil` (0 bytes read and no error).
+*/
recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
return _recv_udp(socket, buf)
}
/*
- Receive data from into a buffer from any socket.
+ Receive data into a buffer from any socket.
Note: `remote_endpoint` parameter is non-nil only if the socket type is UDP. On TCP sockets it
will always return `nil`.
- Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`
+ Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`.
+
+ If no error occurs, `recv_any` returns the number of bytes received and `buf` will contain this data received.
+ If the connection has been gracefully closed, the return value is `0, nil, nil` (0 bytes read and no error).
*/
recv_any :: proc(socket: Any_Socket, buf: []byte) -> (
bytes_read: int,
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index 0d2f28642..9a969f07e 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -364,33 +364,33 @@ _read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
handle := _handle(&f.file)
- single_read_length: win32.DWORD
total_read: int
sync.shared_guard(&f.rw_mutex) // multiple readers
if sync.guard(&f.p_mutex) {
to_read := min(win32.DWORD(length), MAX_RW)
- ok: win32.BOOL
- if f.kind == .Console {
- n, cerr := read_console(handle, p[total_read:][:to_read])
- total_read += n
- if cerr != nil {
- return i64(total_read), cerr
+ switch f.kind {
+ case .Console:
+ // NOTE(laytan): at least for now, just use ReadFile, it seems to work fine,
+ // but, there may be issues with certain situations that we need to get reproductions for.
+ // total_read, err = read_console(handle, p[total_read:][:to_read])
+ fallthrough
+ case .Pipe, .File:
+ single_read_length: win32.DWORD
+ ok := win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
+ if ok {
+ total_read += int(single_read_length)
+ } else {
+ err = _get_platform_error()
}
- } else {
- ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
}
+ }
- if single_read_length > 0 && ok {
- total_read += int(single_read_length)
- } else if single_read_length == 0 && ok {
- // ok and 0 bytes means EOF:
- // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
- err = .EOF
- } else {
- err = _get_platform_error()
- }
+ if total_read == 0 && err == nil {
+ // ok and 0 bytes means EOF:
+ // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
+ err = .EOF
}
return i64(total_read), err
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 5fbff08c2..b39c6ac6b 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -2003,4 +2003,16 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
runtime.print_typeid(a.id)
runtime.print_string("\n")
return true
+}
+
+
+@(require_results)
+default_map_hash_by_ptr :: proc(ptr: ^$T, seed: uintptr = 0) -> uintptr where intrinsics.type_is_comparable(T) {
+ assert(ptr != nil)
+ info := intrinsics.type_map_info(map[T]struct{})
+
+ h := u64(seed) + runtime.INITIAL_HASH_SEED
+ actual_seed := uintptr(h) | uintptr(uintptr(h) == 0)
+
+ return info.key_hasher(ptr, actual_seed)
} \ No newline at end of file
diff --git a/core/strings/builder.odin b/core/strings/builder.odin
index da9b6df27..ce636a2a1 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -880,6 +880,20 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo
}
if len(old) == 0 {
+ // NOTE(bill): reserve the necessary memory
+ found := 0
+ for i := 0; i <= len(b.buf); i += len(new)+1 {
+ if n > 0 && found == n {
+ break
+ }
+ found += 1
+ }
+ if found == 0 {
+ return
+ }
+ reserve(&b.buf, len(b.buf) + len(new)*found) or_return
+
+
for i := 0; i <= len(b.buf); i += len(new)+1 {
if n > 0 && replaced == n {
break
@@ -891,6 +905,27 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo
replaced += 1
}
} else {
+ if len(new) > len(old) {
+ // NOTE(bill): reserve the necessary memory
+ found := 0
+ for i := 0; i < len(b.buf); /**/ {
+ if n > 0 && found == n {
+ break
+ }
+
+ j := index(string(b.buf[i:]), old)
+ if j < 0 {
+ break
+ }
+ i += j+len(old)
+ found += 1
+ }
+ if found == 0 {
+ return
+ }
+ reserve(&b.buf, len(b.buf) + (len(new)-len(old))*found) or_return
+ }
+
for i := 0; i < len(b.buf); /**/ {
if n > 0 && replaced == n {
break
@@ -901,7 +936,7 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo
break
}
- if len(new) >= len(old) {
+ if len(new) > len(old) {
resize(&b.buf, len(b.buf) + len(new)-len(old)) or_return
}
diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin
index 2295e46b8..cdda7b3e4 100644
--- a/core/sys/darwin/Foundation/NSApplication.odin
+++ b/core/sys/darwin/Foundation/NSApplication.odin
@@ -1,21 +1,9 @@
package objc_Foundation
-foreign import "system:Foundation.framework"
-
import "base:intrinsics"
import "base:runtime"
import "core:strings"
-RunLoopMode :: ^String
-
-@(link_prefix="NS")
-foreign Foundation {
- RunLoopCommonModes: RunLoopMode
- DefaultRunLoopMode: RunLoopMode
- EventTrackingRunLoopMode: RunLoopMode
- ModalPanelRunLoopMode: RunLoopMode
-}
-
ActivationPolicy :: enum UInteger {
Regular = 0,
Accessory = 1,
@@ -206,6 +194,13 @@ Application_updateWindows :: proc "c" (self: ^Application) {
msgSend(nil, self, "updateWindows")
}
+@(objc_type=Application, objc_name="sendAction")
+Application_sendAction :: proc "c" (self: ^Application, action: SEL, to: id, from: id) {
+ msgSend(nil, self, "sendAction:to:from:", action, to, from)
+}
+
+
+
@(objc_class="NSRunningApplication")
RunningApplication :: struct {using _: Object}
diff --git a/core/sys/darwin/Foundation/NSCursor.odin b/core/sys/darwin/Foundation/NSCursor.odin
new file mode 100644
index 000000000..ad2decdca
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSCursor.odin
@@ -0,0 +1,26 @@
+package objc_Foundation
+
+@(objc_class="NSCursor")
+Cursor :: struct {using _: Object}
+
+@(objc_type=Cursor, objc_name="set")
+Cursor_set :: proc(self: ^Cursor) {
+ msgSend(EventType, self, "set")
+}
+@(objc_type=Cursor, objc_name="currentCursor", objc_is_class_method=true)
+Cursor_currentCursor :: proc "c" () -> ^Cursor {
+ return msgSend(^Cursor, Cursor, "currentCursor")
+}
+@(objc_type=Cursor, objc_name="IBeamCursor", objc_is_class_method=true)
+Cursor_IBeamCursor :: proc "c" () -> ^Cursor {
+ return msgSend(^Cursor, Cursor, "IBeamCursor")
+}
+@(objc_type=Cursor, objc_name="arrowCursor", objc_is_class_method=true)
+Cursor_arrowCursor :: proc "c" () -> ^Cursor {
+ return msgSend(^Cursor, Cursor, "arrowCursor")
+}
+@(objc_type=Cursor, objc_name="pointingHandCursor", objc_is_class_method=true)
+Cursor_pointingHandCursor :: proc "c" () -> ^Cursor {
+ return msgSend(^Cursor, Cursor, "pointingHandCursor")
+}
+
diff --git a/core/sys/darwin/Foundation/NSEvent.odin b/core/sys/darwin/Foundation/NSEvent.odin
index 3bd0c1879..952a1726c 100644
--- a/core/sys/darwin/Foundation/NSEvent.odin
+++ b/core/sys/darwin/Foundation/NSEvent.odin
@@ -334,6 +334,11 @@ Event_locationInWindow :: proc "c" (self: ^Event) -> Point {
return msgSend(Point, self, "locationInWindow")
}
+@(objc_type=Event, objc_name="mouseLocation", objc_is_class_method=true)
+Event_mouseLocation :: proc "c" () -> Point {
+ return msgSend(Point, Event, "mouseLocation")
+}
+
@(objc_type=Event, objc_name="deltaX")
Event_deltaX :: proc "c" (self: ^Event) -> Float {
diff --git a/core/sys/darwin/Foundation/NSObject.odin b/core/sys/darwin/Foundation/NSObject.odin
index 31ece47a1..7a423e757 100644
--- a/core/sys/darwin/Foundation/NSObject.odin
+++ b/core/sys/darwin/Foundation/NSObject.odin
@@ -81,6 +81,12 @@ debugDescription :: proc "c" (self: ^Object) -> ^String {
return nil
}
+
+@(objc_type=Object, objc_name="performSelectorOnMainThread")
+performSelectorOnMainThread :: proc "c" (self: ^Object, aSelector: SEL, arg: id, wait: BOOL) {
+ msgSend(^String, self, "performSelectorOnMainThread:withObject:waitUntilDone:", aSelector, arg, wait)
+}
+
bridgingCast :: proc "c" ($T: typeid, obj: ^Object) where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) {
return (T)(obj)
}
@@ -88,4 +94,4 @@ bridgingCast :: proc "c" ($T: typeid, obj: ^Object) where intrinsics.type_is_poi
@(objc_class="NSCoder")
Coder :: struct {using _: Object}
-// TODO(bill): Implement all the methods for this massive type \ No newline at end of file
+// TODO(bill): Implement all the methods for this massive type
diff --git a/core/sys/darwin/Foundation/NSRunLoop.odin b/core/sys/darwin/Foundation/NSRunLoop.odin
new file mode 100644
index 000000000..a9c47bd0e
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSRunLoop.odin
@@ -0,0 +1,27 @@
+package objc_Foundation
+
+foreign import "system:Foundation.framework"
+
+RunLoopMode :: ^String
+
+@(link_prefix="NS")
+foreign Foundation {
+ RunLoopCommonModes: RunLoopMode
+ DefaultRunLoopMode: RunLoopMode
+ EventTrackingRunLoopMode: RunLoopMode
+ ModalPanelRunLoopMode: RunLoopMode
+}
+
+@(objc_class="NSRunLoop")
+RunLoop :: struct { using _: Object }
+
+@(objc_type=RunLoop, objc_name="mainRunLoop", objc_is_class_method=true)
+RunLoop_mainRunLoop :: proc() -> ^RunLoop {
+ return msgSend(^RunLoop, RunLoop, "mainRunLoop")
+}
+
+@(objc_type=RunLoop, objc_name="addTimerForMode")
+RunLoop_addTimerForMode :: proc(self: ^RunLoop, timer: ^Timer, forMode: RunLoopMode) {
+ msgSend(nil, self, "addTimer:forMode:", timer, forMode)
+}
+
diff --git a/core/sys/darwin/Foundation/NSTimer.odin b/core/sys/darwin/Foundation/NSTimer.odin
new file mode 100644
index 000000000..2edcf34c3
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSTimer.odin
@@ -0,0 +1,15 @@
+package objc_Foundation
+
+@(objc_class="NSTimer")
+Timer :: struct { using _: Object }
+
+@(objc_type=Timer, objc_name="scheduledTimerWithTimeIntervalRepeatsBlock", objc_is_class_method=true)
+Timer_scheduledTimerWithTimeIntervalRepeatsBlock :: proc(interval: TimeInterval, repeats: BOOL, block: ^Block) -> ^Timer {
+ return msgSend(^Timer, Timer, "scheduledTimerWithTimeInterval:repeats:block:")
+}
+
+@(objc_type=Timer, objc_name="scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeat", objc_is_class_method=true)
+Timer_scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeat :: proc(interval: TimeInterval, aTarget: id, aSelector: SEL, userInfo: id, repeats: BOOL) -> ^Timer {
+ return msgSend(^Timer, Timer, "scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:", interval, aTarget, aSelector, userInfo, repeats)
+}
+
diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin
index 8317c7bb1..f39faca0a 100644
--- a/core/sys/darwin/Foundation/NSWindow.odin
+++ b/core/sys/darwin/Foundation/NSWindow.odin
@@ -699,6 +699,14 @@ View_convertPointFromView :: proc "c" (self: ^View, point: Point, view: ^View) -
View_addSubview :: proc "c" (self: ^View, view: ^View) {
msgSend(nil, self, "addSubview:", view)
}
+@(objc_type=View, objc_name="isFlipped")
+View_isFlipped :: proc "c" (self: ^View) -> BOOL {
+ return msgSend(BOOL, self, "isFlipped")
+}
+@(objc_type=View, objc_name="setIsFlipped")
+View_setIsFlipped :: proc "c" (self: ^View, flipped: BOOL) {
+ msgSend(nil, self, "setIsFlipped:", flipped)
+}
@(objc_class="NSWindow")
Window :: struct {using _: Responder}
diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin
index 04305ece1..faeda6f43 100644
--- a/core/sys/linux/sys.odin
+++ b/core/sys/linux/sys.odin
@@ -2992,15 +2992,36 @@ epoll_pwait :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: i32,
// TODO(flysand): signalfd
-// TODO(flysand): timerfd_create
+/*
+ Create Linux file descriptor based timer.
+ Available since Linux 2.6.25
+*/
+timerfd_create :: proc "contextless" (clock_id: Clock_Id, flags: Open_Flags) -> (Fd, Errno) {
+ ret := syscall(SYS_timerfd_create, clock_id, transmute(u32)flags)
+ return errno_unwrap2(ret, Fd)
+}
// TODO(flysand): eventfd
// TODO(flysand): fallocate
-// TODO(flysand): timerfd_settime
+/*
+ Arm/disarm the state of the Linux file descriptor based timer.
+ Available since Linux 2.6.25
+*/
+timerfd_settime :: proc "contextless" (fd: Fd, flags: ITimer_Flags, new_value: ^ITimer_Spec, old_value: ^ITimer_Spec) -> Errno {
+ ret := syscall(SYS_timerfd_settime, fd, transmute(u32)flags, new_value, old_value)
+ return Errno(-ret)
+}
-// TODO(flysand): timerfd_gettime
+/*
+ Get the state of the Linux file descriptor based timer.
+ Available since Linux 2.6.25
+*/
+timerfd_gettime :: proc "contextless" (fd: Fd, curr_value: ^ITimer_Spec) -> Errno {
+ ret := syscall(SYS_timerfd_gettime, fd, curr_value)
+ return Errno(-ret)
+}
// TODO(flysand): accept4
diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin
index a9e582b51..bb400c5a9 100644
--- a/core/sys/posix/poll.odin
+++ b/core/sys/posix/poll.odin
@@ -25,7 +25,7 @@ foreign lib {
poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int ---
}
-when ODIN_OS == .Haiku {
+when ODIN_OS == .Haiku || ODIN_OS == .Linux {
nfds_t :: c.ulong
} else {
nfds_t :: c.uint
diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js
index 43a6b002a..ef203e24d 100644
--- a/core/sys/wasm/js/odin.js
+++ b/core/sys/wasm/js/odin.js
@@ -2107,7 +2107,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory
if (exports.memory) {
if (wasmMemoryInterface.memory) {
- console.warn("WASM module exports memory, but `runWasm` was given an interface with existing memory too");
+ console.warn('WASM module exports memory, but `runWasm` was given an interface with existing memory too. Did you mean to use `-extra-linker-flags:"--import-memory"` to tell the compiler not to export memory?');
}
wasmMemoryInterface.setMemory(exports.memory);
}
diff --git a/src/checker.cpp b/src/checker.cpp
index 4d5482933..33121b453 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -3581,7 +3581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
return true;
} else if (name == "test") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->test = true;
return true;
@@ -3629,13 +3629,13 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
return true;
} else if (name == "init") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->init = true;
return true;
} else if (name == "fini") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->fini = true;
return true;
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index c38150e31..1cde65640 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -2085,7 +2085,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur
continue;
}
- if (type_size_of(e->type) > 8) {
+ if (false && type_size_of(e->type) > 8) {
String ename = lb_get_entity_name(m, e);
gbString name = gb_string_make(permanent_allocator(), "");
name = gb_string_appendc(name, "__$startup$");
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 33ad2ee8d..9ddbd1f9c 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -2868,9 +2868,13 @@ gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lb
GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
objc_class = entity_from_expr(se->expr);
-
GB_ASSERT(objc_class);
GB_ASSERT(objc_class->kind == Entity_TypeName);
+
+ if (objc_class->TypeName.is_type_alias) {
+ objc_class = objc_class->type->Named.type_name;
+ }
+
GB_ASSERT(objc_class->TypeName.objc_class_name != "");
}
diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin
index ec45744f3..9b3973a60 100644
--- a/tests/core/net/test_core_net.odin
+++ b/tests/core/net/test_core_net.odin
@@ -624,6 +624,41 @@ test_nonblocking_option :: proc(t: ^testing.T) {
}
}
+// Test that when the server closes it's connection, the client's next receive is `0, nil` to indicate a correct close.
+@(test)
+test_connection_close :: proc(t: ^testing.T) {
+ server, listen_err := net.listen_tcp({address=net.IP4_Address{127, 0, 0, 1}, port=0})
+ testing.expect_value(t, listen_err, nil)
+ defer net.close(server)
+
+ server_ep, bound_endpoint_err := net.bound_endpoint(server)
+ testing.expect_value(t, bound_endpoint_err, nil)
+
+ client, dial_err := net.dial_tcp(server_ep)
+ testing.expect_value(t, dial_err, nil)
+ defer net.close(client)
+
+ server_client, _, accept_err := net.accept_tcp(server)
+ testing.expect_value(t, accept_err, nil)
+
+ send_buf: [512]byte = 1
+ sent, send_err := net.send(server_client, send_buf[:])
+ testing.expect_value(t, sent, 512)
+ testing.expect_value(t, send_err, nil)
+ net.close(server_client)
+
+ recv_buf: [512]byte = ---
+ received, recv_err := net.recv(client, recv_buf[:])
+ testing.expect_value(t, received, 512)
+ testing.expect_value(t, recv_err, nil)
+
+ testing.expect_value(t, recv_buf, send_buf)
+
+ received, recv_err = net.recv(client, recv_buf[:])
+ testing.expect_value(t, received, 0)
+ testing.expect_value(t, recv_err, nil)
+}
+
@(private)
address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
switch t in address {
diff --git a/tests/core/sys/posix/structs/structs.c b/tests/core/sys/posix/structs/structs.c
index ac771057e..6bb7df29e 100644
--- a/tests/core/sys/posix/structs/structs.c
+++ b/tests/core/sys/posix/structs/structs.c
@@ -74,6 +74,7 @@ int main(int argc, char *argv[])
printf("ai_canonname %zu\n", offsetof(struct addrinfo, ai_canonname));
printf("pollfd %zu %zu\n", sizeof(struct pollfd), _Alignof(struct pollfd));
+ printf("nfds_t %zu %zu\n", sizeof(nfds_t), _Alignof(nfds_t));
printf("passwd %zu %zu\n", sizeof(struct passwd), _Alignof(struct passwd));
diff --git a/tests/core/sys/posix/structs/structs.odin b/tests/core/sys/posix/structs/structs.odin
index 55369e386..64833c437 100644
--- a/tests/core/sys/posix/structs/structs.odin
+++ b/tests/core/sys/posix/structs/structs.odin
@@ -41,6 +41,8 @@ main :: proc() {
fmt.println("ai_canonname", offset_of(posix.addrinfo, ai_canonname))
fmt.println("pollfd", size_of(posix.pollfd), align_of(posix.pollfd))
+ fmt.println("nfds_t", size_of(posix.nfds_t), align_of(posix.nfds_t))
+
fmt.println("passwd", size_of(posix.passwd), align_of(posix.passwd))
when ODIN_OS != .Haiku {
diff --git a/vendor/darwin/QuartzCore/QuartzCore.odin b/vendor/darwin/QuartzCore/QuartzCore.odin
index 0e54b3d30..64e8e66d3 100644
--- a/vendor/darwin/QuartzCore/QuartzCore.odin
+++ b/vendor/darwin/QuartzCore/QuartzCore.odin
@@ -47,6 +47,10 @@ MetalLayer_pixelFormat :: proc "c" (self: ^MetalLayer) -> MTL.PixelFormat {
MetalLayer_setPixelFormat :: proc "c" (self: ^MetalLayer, pixelFormat: MTL.PixelFormat) {
msgSend(nil, self, "setPixelFormat:", pixelFormat)
}
+@(objc_type=MetalLayer, objc_name="setColorSpace")
+MetalLayer_setColorSpace :: proc "c" (self: ^MetalLayer, colorspace: NS.id) {
+ msgSend(nil, self, "setColorspace:", colorspace)
+}
@(objc_type=MetalLayer, objc_name="framebufferOnly")
MetalLayer_framebufferOnly :: proc "c" (self: ^MetalLayer) -> NS.BOOL {
@@ -126,3 +130,21 @@ MetalDrawable_addPresentedHandler :: proc "c" (self: ^MetalDrawable, block: Draw
msgSend(nil, self, "addPresentedHandler:", block)
}
+@(objc_class="CATransaction")
+Transaction :: struct { using _: NS.Object }
+
+@(objc_type=Transaction, objc_name="begin", objc_is_class_method=true)
+transaction_begin :: proc() {
+ msgSend(nil, Transaction, "begin")
+}
+
+@(objc_type=Transaction, objc_name="commit", objc_is_class_method=true)
+transaction_commit :: proc() {
+ msgSend(nil, Transaction, "commit")
+}
+
+@(objc_type=Transaction, objc_name="flush", objc_is_class_method=true)
+transaction_flush :: proc() {
+ msgSend(nil, Transaction, "flush")
+}
+