diff options
Diffstat (limited to 'core/sys/darwin/Foundation/NSBlock.odin')
| -rw-r--r-- | core/sys/darwin/Foundation/NSBlock.odin | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/core/sys/darwin/Foundation/NSBlock.odin b/core/sys/darwin/Foundation/NSBlock.odin new file mode 100644 index 000000000..b9d94bfee --- /dev/null +++ b/core/sys/darwin/Foundation/NSBlock.odin @@ -0,0 +1,120 @@ +package objc_Foundation + +import "base:intrinsics" +import "base:builtin" +import "core:mem" + +@(objc_class="NSBlock") +Block :: struct {using _: Object} + +@(objc_type=Block, objc_name="createGlobal", objc_is_class_method=true) +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 (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: rawptr, // contains a pointer to a proc "c" (^Internal_Block_Literal, ...) + descriptor: ^Block_Descriptor, +} + +@(private) +Internal_Block_Literal :: struct { + using base: Internal_Block_Literal_Base, + // Imported Variables + user_proc: rawptr, // contains a pointer to a proc "c" (user_data: rawptr, ...) + user_data: rawptr, +} + +@(private) +Block_Descriptor :: struct { + reserved: uint, + size: uint, + copy_helper: proc "c" (dst, src: rawptr), + dispose_helper: proc "c" (src: rawptr), + signature: cstring, +} + +@(private) +global_block_descriptor := Block_Descriptor{ + reserved = 0, + size = size_of(Internal_Block_Literal), +} + +foreign import libSystem "system:System.framework" +foreign libSystem { + _NSConcreteGlobalBlock: intrinsics.objc_class + _NSConcreteStackBlock: intrinsics.objc_class +} + +@(private="file") +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 + + 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 + } +} + +@(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) +} + |