NSBlock.odin 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package objc_Foundation
  2. import "base:intrinsics"
  3. import "base:builtin"
  4. import "core:mem"
  5. @(objc_class="NSBlock")
  6. Block :: struct {using _: Object}
  7. @(objc_type=Block, objc_name="createGlobal", objc_is_class_method=true)
  8. Block_createGlobal :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr), allocator := context.allocator) -> (^Block, mem.Allocator_Error) #optional_allocator_error {
  9. return Block_createInternal(true, user_data, user_proc, allocator)
  10. }
  11. @(objc_type=Block, objc_name="createLocal", objc_is_class_method=true)
  12. Block_createLocal :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block {
  13. b, _ := Block_createInternal(false, user_data, user_proc, {})
  14. return b
  15. }
  16. @(objc_type=Block, objc_name="createGlobalWithParam", objc_is_class_method=true)
  17. 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 {
  18. return Block_createInternalWithParam(true, user_data, user_proc, allocator)
  19. }
  20. @(objc_type=Block, objc_name="createLocalWithParam", objc_is_class_method=true)
  21. Block_createLocalWithParam :: proc (user_data: rawptr, user_proc: proc "c" (user_data: rawptr, t: $T)) -> ^Block {
  22. b, _ := Block_createInternalWithParam(false, user_data, user_proc, {})
  23. return b
  24. }
  25. @(objc_type=Block, objc_name="invoke")
  26. Block_invoke :: proc "c" (self: ^Block, args: ..any) -> ^Object {
  27. return msgSend(^Object, self, "invoke:", ..args)
  28. }
  29. @(private)
  30. Internal_Block_Literal_Base :: struct {
  31. isa: ^intrinsics.objc_class,
  32. flags: u32,
  33. reserved: u32,
  34. invoke: rawptr, // contains a pointer to a proc "c" (^Internal_Block_Literal, ...)
  35. descriptor: ^Block_Descriptor,
  36. }
  37. @(private)
  38. Internal_Block_Literal :: struct {
  39. using base: Internal_Block_Literal_Base,
  40. // Imported Variables
  41. user_proc: rawptr, // contains a pointer to a proc "c" (user_data: rawptr, ...)
  42. user_data: rawptr,
  43. }
  44. @(private)
  45. Block_Descriptor :: struct {
  46. reserved: uint,
  47. size: uint,
  48. copy_helper: proc "c" (dst, src: rawptr),
  49. dispose_helper: proc "c" (src: rawptr),
  50. signature: cstring,
  51. }
  52. @(private)
  53. global_block_descriptor := Block_Descriptor{
  54. reserved = 0,
  55. size = size_of(Internal_Block_Literal),
  56. }
  57. foreign import libSystem "system:System"
  58. foreign libSystem {
  59. _NSConcreteGlobalBlock: intrinsics.objc_class
  60. _NSConcreteStackBlock: intrinsics.objc_class
  61. }
  62. @(private="file")
  63. 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) {
  64. _init :: proc(bl: ^Internal_Block_Literal, is_global: bool, user_data: rawptr, user_proc: rawptr, invoke: rawptr) {
  65. // Set to true on blocks that have captures (and thus are not true
  66. // global blocks) but are known not to escape for various other
  67. // reasons. For backward compatibility with old runtimes, whenever
  68. // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
  69. // non-escaping block returns the original block and releasing such a
  70. // block is a no-op, which is exactly how global blocks are handled.
  71. BLOCK_IS_NOESCAPE :: (1 << 23)|BLOCK_IS_GLOBAL
  72. BLOCK_HAS_COPY_DISPOSE :: 1 << 25
  73. BLOCK_HAS_CTOR :: 1 << 26 // helpers have C++ code
  74. BLOCK_IS_GLOBAL :: 1 << 28
  75. BLOCK_HAS_STRET :: 1 << 29 // IFF BLOCK_HAS_SIGNATURE
  76. BLOCK_HAS_SIGNATURE :: 1 << 30
  77. bl.isa = is_global ? &_NSConcreteGlobalBlock : &_NSConcreteStackBlock
  78. bl.flags = BLOCK_IS_GLOBAL if is_global else 0
  79. bl.invoke = invoke
  80. bl.descriptor = &global_block_descriptor
  81. bl.user_proc = auto_cast user_proc
  82. bl.user_data = user_data
  83. }
  84. if is_global {
  85. bl := builtin.new (Internal_Block_Literal, allocator) or_return
  86. _init(bl, true, user_data, user_proc, invoke)
  87. return auto_cast bl, .None
  88. } else {
  89. // malloc blocks are created by calling 'copy' on a stack block
  90. bl: Internal_Block_Literal
  91. _init(&bl, false, user_data, user_proc, invoke)
  92. return auto_cast copy(cast(^Copying(Block))(&bl)), .None
  93. }
  94. }
  95. @(private="file")
  96. 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) {
  97. invoke :: proc "c" (bl: ^Internal_Block_Literal) {
  98. user_proc := (proc "c" (rawptr))(bl.user_proc)
  99. user_proc(bl.user_data)
  100. }
  101. return internal_block_literal_make(is_global, user_data, auto_cast user_proc, auto_cast invoke, allocator)
  102. }
  103. @(private="file")
  104. 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) {
  105. invoke :: proc "c" (bl: ^Internal_Block_Literal, t: T) {
  106. user_proc := (proc "c" (rawptr, T))(bl.user_proc)
  107. user_proc(bl.user_data, t)
  108. }
  109. return internal_block_literal_make(is_global, user_data, auto_cast user_proc, auto_cast invoke, allocator)
  110. }