Skip to content

Commit 537f468

Browse files
[[ libscript ]] Add Objective-C block support
This patch adds support for lcb scripts to use Objective-C blocks. Two handlers have been added to the Objective-C library, allowing for the creation and deletion of block pointers.
1 parent 22e97b3 commit 537f468

2 files changed

Lines changed: 209 additions & 1 deletion

File tree

libscript/src/objc.lcb

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,212 @@ public handler CreateObjcInformalDelegateWithContext(in pProtocol as List, in pH
610610
end unsafe
611611
end handler
612612

613+
/* Block handling */
614+
615+
private variable mNSConcreteGlobalBlock as optional Pointer
616+
private constant kRTLD_LAZY is 1
617+
private type LibraryHandle is Pointer
618+
619+
private foreign handler OpenLibrary(in pPath as ZStringUTF8, in pFlag as CSInt) \
620+
returns optional LibraryHandle \
621+
binds to "dlopen"
622+
623+
private foreign handler GetSymbolPointer(in pHandle as LibraryHandle, in pSymbol as ZStringUTF8) \
624+
returns optional Pointer \
625+
binds to "dlsym"
626+
627+
private foreign handler CloseLibrary(in pHandle as LibraryHandle) \
628+
returns CSInt \
629+
binds to "dlclose"
630+
631+
/* Fetch the `isa` pointer for the (internal) Obj-C concreate block class */
632+
private unsafe handler EnsureNSConcreteGlobalBlock() returns nothing
633+
if mNSConcreteGlobalBlock is not nothing then
634+
return
635+
end if
636+
637+
/* As we cannot bind to the pointer to a symbol as a variable, we use
638+
* dynamic library loading and resolution functions to do so directly. */
639+
variable tSystem as LibraryHandle
640+
put OpenLibrary("libSystem.dylib", kRTLD_LAZY) into tSystem
641+
if tSystem is not nothing then
642+
put GetSymbolPointer(tSystem, "_NSConcreteGlobalBlock") \
643+
into mNSConcreteGlobalBlock
644+
CloseLibrary(tSystem)
645+
end if
646+
end handler
647+
648+
private constant kBlockSize is 32
649+
private constant kBlockDescriptorSize is 40
650+
651+
public foreign type _ObjcBlockDescriptor binds to "MCAggregateTypeInfo:ffrrr"
652+
public foreign type _ObjcBlock binds to "MCAggregateTypeInfo:rEErr"
653+
public type ObjcBlockPointer is Pointer
654+
655+
foreign handler MCHandlerGetFunctionPtr(in pHandler as any, out rPtr as Pointer) \
656+
returns CBool \
657+
binds to "<builtin>"
658+
659+
private foreign handler CopyBlockDescriptor(inout pBlock as _ObjcBlockDescriptor, in pSize as UIntSize, out rNewBlock as Pointer) \
660+
returns CBool \
661+
binds to "MCMemoryAllocateCopy"
662+
663+
private foreign handler CopyBlock(inout pBlock as _ObjcBlock, in pSize as UIntSize, out rNewBlock as Pointer) \
664+
returns CBool \
665+
binds to "MCMemoryAllocateCopy"
666+
667+
private foreign handler MCMemoryDelete(in pBlock as Pointer) \
668+
returns nothing \
669+
binds to "<builtin>"
670+
671+
private foreign handler BlockPointerToBlock(out rBlock as _ObjcBlock, in pPointer as ObjcBlockPointer, in pSize as UIntSize) \
672+
returns nothing \
673+
binds to "c:memcpy"
674+
675+
/**
676+
Create an Objective-C block pointer that wraps an LCB handler.
677+
678+
Example:
679+
private variable sRequestPermissionsCompletionHandler as optional ObjcBlockPointer
680+
private variable sTarget as ScriptObject
681+
682+
public handler AudioLibraryInitialize() returns Boolean
683+
if not CreateObjcBlockPointerFromHandler(RequestPermissionsCompletionHandler, sRequestPermissionsCompletionHandler) then
684+
put nothing into sRequestPermissionsCompletionHandler
685+
return false
686+
end if
687+
put the caller into sTarget
688+
return true
689+
end handler
690+
691+
private foreign handler ObjC_AVCaptureDeviceRequestAccessForMediaType(in pMediaType as ObjcId, in pCompletionHandler as ObjcBlockPointer) \
692+
returns nothing \
693+
binds to "objc:AVCaptureDevice.+requestAccessForMediaType:completionHandler:"
694+
695+
public handler AudioLibraryRequestPermissions()
696+
unsafe
697+
ObjC_AVCaptureDeviceRequestAccessForMediaType(StringToNSString("soun"), sRequestPermissionsCompletionHandler)
698+
end unsafe
699+
end handler
700+
701+
public handler RequestPermissionsCompletionHandler(in pBlock as ObjcBlockPointer, in pGranted as CBool)
702+
post "AudioLibraryRequestPermissionsCallback" to sTarget with [pGranted]
703+
end handler
704+
705+
Parameters:
706+
pHandler: The handler the block pointer should wrap.
707+
rBlockPtr: The variable into which the block pointer should be returned.
708+
709+
Returns:
710+
True if the block pointer was successfully created, false otherwise.
711+
712+
Description:
713+
Use the <CreateObjcBlockPointerFromHandler> handler to create a pointer to an
714+
Objective-C block that wraps an LCB handler. The block pointer can be used in
715+
calls to Objective-C foreign functions that expect a block as a parameter.
716+
717+
The wrapped handler will be called whenever the block is invoked, with the first
718+
parameter of its signature being the block pointer. The remaining parameters
719+
should match those of the Objective-C block.
720+
721+
The lifetime of a created `ObjcBlockPointer` is not automatically managed. When
722+
such a value has no more references to it and it is no longer going to be used,
723+
<DeleteObjcBlockPointer> should be used to free the resources used by it.
724+
725+
References:
726+
DeleteObjcBlockPointer (handler)
727+
728+
*/
729+
public handler CreateObjcBlockPointerFromHandler(in pHandler as any, out rBlockPtr as optional ObjcBlockPointer) returns Boolean
730+
variable tBlockPtr as ObjcBlockPointer
731+
unsafe
732+
/* Make sure the `isa` pointer has been initialized */
733+
EnsureNSConcreteGlobalBlock()
734+
735+
/* Fetch the (C) function pointer for the given handler - this is used
736+
* later when constructing the block's record and is called when the
737+
* block is invoked. */
738+
variable tFunctionPtr as Pointer
739+
if not MCHandlerGetFunctionPtr(pHandler, tFunctionPtr) then
740+
return false
741+
end if
742+
743+
/* Initialize the list of block descriptor aggregate members. The first
744+
* field is the flags where 0 means global block; the second field is
745+
* the size of the block record; the other members are not used (for our
746+
* purposes). */
747+
variable tBlockDescriptor as _ObjcBlockDescriptor
748+
put [0, kBlockSize, nothing, nothing, nothing] into tBlockDescriptor
749+
750+
/* Copy the block descriptor after being (implicitly) converted to an
751+
* aggregate into a heap allocated memory block. */
752+
variable tBlockDescriptorPointer as Pointer
753+
if not CopyBlockDescriptor(tBlockDescriptor, \
754+
kBlockDescriptorSize, \
755+
tBlockDescriptorPointer) then
756+
return false
757+
end if
758+
759+
/* Initialize the list of block members. The first field is the obj-c
760+
* objects 'isa' pointer; the second is the flags word; the third is
761+
* unused for our purposes; the fourth is the (C)function pointer it
762+
* wraps; and the fifth is the block descriptor (which mainly defines
763+
* the actual size of the block - as we don't add any block variables,
764+
* this is a fixed size. */
765+
variable tBlock as _ObjcBlock
766+
put [mNSConcreteGlobalBlock, \
767+
0x30000000, \
768+
0, \
769+
tFunctionPtr, \
770+
tBlockDescriptorPointer] into tBlock
771+
772+
/* Copy the block after being (implicitly) converted to an aggregate
773+
* into a heap allocated memory block. */
774+
if not CopyBlock(tBlock, kBlockSize, tBlockPtr) then
775+
MCMemoryDelete(tBlockDescriptorPointer)
776+
return false
777+
end if
778+
end unsafe
779+
780+
put tBlockPtr into rBlockPtr
781+
return true
782+
end handler
783+
784+
/**
785+
Delete an Objective-C block pointer.
786+
787+
Example:
788+
public handler AudioLibraryFinalize()
789+
if sRequestPermissionsCompletionHandler is not nothing then
790+
DeleteObjcBlockPointer(sRequestPermissionsCompletionHandler)
791+
put nothing into sRequestPermissionsCompletionHandler
792+
end if
793+
end handler
794+
795+
Parameters:
796+
pBlockPointer: An Objective-C block pointer created with
797+
<CreateObjcBlockPointerFromHandler>
798+
799+
Description:
800+
Use the <DeleteObjcBlockPointer> handler to delete an Objective-C block pointer
801+
created with <CreateObjcBlockPointerFromHandler>
802+
803+
References:
804+
CreateObjcBlockPointerFromHandler (handler)
805+
806+
*/
807+
public handler DeleteObjcBlockPointer(in pBlockPointer as ObjcBlockPointer)
808+
unsafe
809+
/* Unpack the pointer to block aggregate as an aggregate. */
810+
variable tBlock as _ObjcBlock
811+
BlockPointerToBlock(tBlock, pBlockPointer, kBlockSize)
812+
813+
/* Delete the block's descriptor (fifth field). */
814+
MCMemoryDelete(tBlock[5])
815+
816+
/* Delete the block itself. */
817+
MCMemoryDelete(pBlockPointer)
818+
end unsafe
819+
end handler
820+
613821
end module

libscript/stdscript-sources.gypi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
'src/stream.lcb',
2929
'src/string.lcb',
3030
'src/system.lcb',
31-
'src/objc.lcb',
3231
'src/type.lcb',
3332
'src/type-convert.lcb',
3433
'src/unittest.lcb',
@@ -37,6 +36,7 @@
3736

3837
'stdscript_other_lcb_files':
3938
[
39+
'src/objc.lcb',
4040
'src/unittest-impl.lcb',
4141
],
4242

0 commit comments

Comments
 (0)