From 92b24fd02d14ebc028c1b2b8d09bfc7b970ead39 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Wed, 6 Sep 2023 12:29:07 +1000 Subject: [PATCH] Further improve on NSBlock * Correct setting the isa of the NSBlock * Make global blocks managed by the odin runtime * Make local blocks copy to a malloc block that is managed by the objc runtime * Add method to create blocks that take a paramater --- vendor/darwin/Foundation/NSBlock.odin | 105 +++++++++++++++++--------- 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/vendor/darwin/Foundation/NSBlock.odin b/vendor/darwin/Foundation/NSBlock.odin index 9cc58da71..ecb31bcfb 100644 --- a/vendor/darwin/Foundation/NSBlock.odin +++ b/vendor/darwin/Foundation/NSBlock.odin @@ -1,27 +1,37 @@ package objc_Foundation import "core:intrinsics" +import "core:builtin" +import "core:mem" -@(objc_class="NSConcreteGlobalBlock") +@(objc_class="NSBlock") Block :: struct {using _: Object} @(objc_type=Block, objc_name="createGlobal", objc_is_class_method=true) -Block_createGlobal :: proc "c" (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { - return Block_createInternal(true, user_data, user_proc) +Block_createGlobal :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr), allocator := context.allocator) -> (^Block, mem.Allocator_Error) #optional_allocator_error { + return Block_createInternal(true, user_data, user_proc, allocator) } - @(objc_type=Block, objc_name="createLocal", objc_is_class_method=true) -Block_createLocal :: proc "c" (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { - return Block_createInternal(false, user_data, user_proc) +Block_createLocal :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { + b, _ := Block_createInternal(false, user_data, user_proc, {}) + return b +} +@(objc_type=Block, objc_name="createGlobalWithParam", objc_is_class_method=true) +Block_createGlobalWithParam :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr, t: $T), allocator := context.allocator) -> (^Block, mem.Allocator_Error) #optional_allocator_error { + return Block_createInternalWithParam(true, user_data, user_proc, allocator) +} +@(objc_type=Block, objc_name="createLocalWithParam", objc_is_class_method=true) +Block_createLocalWithParam :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr, t: $T)) -> ^Block { + b, _ := Block_createInternalWithParam(false, user_data, user_proc, {}) + return b } - @(private) Internal_Block_Literal_Base :: struct { isa: ^intrinsics.objc_class, flags: u32, reserved: u32, - invoke: proc "c" (^Internal_Block_Literal), + invoke: rawptr, // contains a pointer to a proc "c" (^Internal_Block_Literal, ...) descriptor: ^Block_Descriptor, } @@ -29,7 +39,7 @@ Internal_Block_Literal_Base :: struct { Internal_Block_Literal :: struct { using base: Internal_Block_Literal_Base, // Imported Variables - user_proc: proc "c" (user_data: rawptr), + user_proc: rawptr, // contains a pointer to a proc "c" (user_data: rawptr, ...) user_data: rawptr, } @@ -50,36 +60,61 @@ global_block_descriptor := Block_Descriptor{ foreign import libSystem "system:System.framework" foreign libSystem { - _NSConcreteGlobalBlock: ^intrinsics.objc_class + _NSConcreteGlobalBlock: intrinsics.objc_class + _NSConcreteStackBlock: intrinsics.objc_class } @(private="file") -Block_createInternal :: proc "c" (is_global: bool, user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { - // Set to true on blocks that have captures (and thus are not true - // global blocks) but are known not to escape for various other - // reasons. For backward compatibility with old runtimes, whenever - // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a - // non-escaping block returns the original block and releasing such a - // block is a no-op, which is exactly how global blocks are handled. - BLOCK_IS_NOESCAPE :: (1 << 23)|BLOCK_IS_GLOBAL +internal_block_literal_make :: proc (is_global: bool, user_data: rawptr, user_proc: rawptr, invoke: rawptr, allocator: mem.Allocator) -> (b: ^Block, err: mem.Allocator_Error) { + _init :: proc(bl: ^Internal_Block_Literal, is_global: bool, user_data: rawptr, user_proc: rawptr, invoke: rawptr) { + // Set to true on blocks that have captures (and thus are not true + // global blocks) but are known not to escape for various other + // reasons. For backward compatibility with old runtimes, whenever + // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a + // non-escaping block returns the original block and releasing such a + // block is a no-op, which is exactly how global blocks are handled. + BLOCK_IS_NOESCAPE :: (1 << 23)|BLOCK_IS_GLOBAL - BLOCK_HAS_COPY_DISPOSE :: 1 << 25 - BLOCK_HAS_CTOR :: 1 << 26 // helpers have C++ code - BLOCK_IS_GLOBAL :: 1 << 28 - BLOCK_HAS_STRET :: 1 << 29 // IFF BLOCK_HAS_SIGNATURE - BLOCK_HAS_SIGNATURE :: 1 << 30 + BLOCK_HAS_COPY_DISPOSE :: 1 << 25 + BLOCK_HAS_CTOR :: 1 << 26 // helpers have C++ code + BLOCK_IS_GLOBAL :: 1 << 28 + BLOCK_HAS_STRET :: 1 << 29 // IFF BLOCK_HAS_SIGNATURE + BLOCK_HAS_SIGNATURE :: 1 << 30 - extraBytes :: size_of(Internal_Block_Literal) - size_of(Internal_Block_Literal_Base) - - bl := (^Internal_Block_Literal)(AllocateObject(_NSConcreteGlobalBlock, extraBytes, nil)) - bl.isa = _NSConcreteGlobalBlock - bl.flags = BLOCK_IS_GLOBAL if is_global else 0 - bl.invoke = proc "c" (bl: ^Internal_Block_Literal) { - bl.user_proc(bl.user_data) + bl.isa = is_global ? &_NSConcreteGlobalBlock : &_NSConcreteStackBlock + bl.flags = BLOCK_IS_GLOBAL if is_global else 0 + bl.invoke = invoke + bl.descriptor = &global_block_descriptor + bl.user_proc = auto_cast user_proc + bl.user_data = user_data + } + if is_global { + bl := builtin.new (Internal_Block_Literal, allocator) or_return + _init(bl, true, user_data, user_proc, invoke) + return auto_cast bl, .None + } else { + // malloc blocks are created by calling 'copy' on a stack block + bl: Internal_Block_Literal + _init(&bl, false, user_data, user_proc, invoke) + return auto_cast copy(cast(^Copying(Block))(&bl)), .None } - bl.descriptor = &global_block_descriptor - bl.user_proc = user_proc - bl.user_data = user_data - - return auto_cast bl } + +@(private="file") +Block_createInternal :: proc (is_global: bool, user_data: rawptr, user_proc: proc "c" (user_data: rawptr), allocator: mem.Allocator) -> (b: ^Block, err: mem.Allocator_Error) { + invoke :: proc "c" (bl: ^Internal_Block_Literal) { + user_proc := (proc "c" (rawptr))(bl.user_proc) + user_proc(bl.user_data) + } + return internal_block_literal_make(is_global, user_data, auto_cast user_proc, auto_cast invoke, allocator) +} + +@(private="file") +Block_createInternalWithParam :: proc (is_global: bool, user_data: rawptr, user_proc: proc "c" (user_data: rawptr, t: $T), allocator: mem.Allocator) -> (b: ^Block, err: mem.Allocator_Error) { + invoke :: proc "c" (bl: ^Internal_Block_Literal, t: T) { + user_proc := (proc "c" (rawptr, T))(bl.user_proc) + user_proc(bl.user_data, t) + } + return internal_block_literal_make(is_global, user_data, auto_cast user_proc, auto_cast invoke, allocator) +} +