Browse Source

Merge pull request #2790 from Lperlind/master

Further improve on NSBlock
gingerBill 1 year ago
parent
commit
ed3a9aed12
1 changed files with 68 additions and 33 deletions
  1. 68 33
      vendor/darwin/Foundation/NSBlock.odin

+ 68 - 33
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.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 := (^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)
+@(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)
 	}
-	bl.descriptor = &global_block_descriptor
-	bl.user_proc = user_proc
-	bl.user_data = user_data
+	return internal_block_literal_make(is_global, user_data, auto_cast user_proc, auto_cast invoke, allocator)
+}
 
-	return auto_cast bl
+@(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)
 }
+