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) }