updated ebiten version from 2.7.9 to 2.9.9

This commit is contained in:
2026-06-15 19:06:55 +02:00
parent 21edbc41c4
commit db1b625069
405 changed files with 31913 additions and 12595 deletions
+267
View File
@@ -0,0 +1,267 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
package objc
import (
"fmt"
"reflect"
"sync"
"unsafe"
"github.com/ebitengine/purego"
)
const (
// The end-goal of these defaults is to get an Objective-C memory-managed block object
// that won't try to free() a Go pointer, but will call our custom blockFunctionCache.Delete()
// when the reference count drops to zero, so the associated function is also unreferenced.
// blockBaseClass is the name of the class that block objects will be initialized with.
blockBaseClass = "__NSMallocBlock__"
// blockFlags is the set of flags that block objects will be initialized with.
blockFlags = blockHasCopyDispose | blockHasSignature
// blockHasCopyDispose is a flag that tells the Objective-C runtime the block exports Copy and/or Dispose helpers.
blockHasCopyDispose = 1 << 25
// blockHasSignature is a flag that tells the Objective-C runtime the block exports a function signature.
blockHasSignature = 1 << 30
)
// blockDescriptor is the Go representation of an Objective-C block descriptor.
// It is a component to be referenced by blockDescriptor.
//
// The layout of this struct matches Block_literal_1 described in https://clang.llvm.org/docs/Block-ABI-Apple.html#high-level
type blockDescriptor struct {
_ uintptr
size uintptr
_ uintptr
dispose uintptr
signature *uint8
}
// blockLayout is the Go representation of the structure abstracted by a block pointer.
// From the Objective-C point of view, a pointer to this struct is equivalent to an ID that
// references a block.
//
// The layout of this struct matches __block_literal_1 described in https://clang.llvm.org/docs/Block-ABI-Apple.html#high-level
type blockLayout struct {
isa Class
flags uint32
_ uint32
invoke uintptr
descriptor *blockDescriptor
}
// blockFunctionCache is a thread safe cache of block layouts.
//
// The function closures themselves are kept alive by caching them internally until the Objective-C runtime indicates that
// they can be released (presumably when the reference count reaches zero). This approach is used instead of appending the function
// object to the block allocation, where it is out of the visible domain of Go's GC.
type blockFunctionCache struct {
mutex sync.RWMutex
functions map[Block]reflect.Value
}
// Load retrieves a function (in the form of a reflect.Value, so Call can be invoked) associated with the key Block.
func (b *blockFunctionCache) Load(key Block) reflect.Value {
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.functions[key]
}
// Store associates a function (in the form of a reflect.Value) with the key Block.
func (b *blockFunctionCache) Store(key Block, value reflect.Value) Block {
b.mutex.Lock()
defer b.mutex.Unlock()
b.functions[key] = value
return key
}
// Delete removed the function associated with the key Block.
func (b *blockFunctionCache) Delete(key Block) {
b.mutex.Lock()
defer b.mutex.Unlock()
delete(b.functions, key)
}
// newBlockFunctionCache initializes a new blockFunctionCache
func newBlockFunctionCache() *blockFunctionCache {
return &blockFunctionCache{functions: map[Block]reflect.Value{}}
}
// blockCache is a thread safe cache of block layouts.
//
// It takes advantage of the block being the first argument of a block call being the block closure,
// only invoking [github.com/ebitengine/purego.NewCallback] when it encounters a new function type (rather than on for every block creation).
// This should mitigate block creations putting pressure on the callback limit.
type blockCache struct {
sync.Mutex
descriptorTemplate blockDescriptor
layoutTemplate blockLayout
layouts map[reflect.Type]blockLayout
Functions *blockFunctionCache
}
// encode returns a blocks type as if it was given to @encode(typ)
func (*blockCache) encode(typ reflect.Type) *uint8 {
// this algorithm was copied from encodeFunc,
// but altered to panic on error, and to only accept a block-type signature.
if typ == nil || typ.Kind() != reflect.Func {
panic("objc: not a function")
}
var encoding string
switch typ.NumOut() {
case 0:
encoding = encVoid
default:
returnType, err := encodeType(typ.Out(0), false)
if err != nil {
panic(fmt.Sprintf("objc: %v", err))
}
encoding = returnType
}
if typ.NumIn() == 0 || typ.In(0) != reflect.TypeOf(Block(0)) {
panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))
}
encoding += encId
for i := 1; i < typ.NumIn(); i++ {
argType, err := encodeType(typ.In(i), false)
if err != nil {
panic(fmt.Sprintf("objc: %v", err))
}
encoding = fmt.Sprint(encoding, argType)
}
// return the encoding as a C-style string.
return &append([]uint8(encoding), 0)[0]
}
// getLayout retrieves a blockLayout VALUE constructed with the supplied function type.
// It will panic if the type is not a valid block function.
func (b *blockCache) getLayout(typ reflect.Type) blockLayout {
b.Lock()
defer b.Unlock()
// return the cached layout, if it exists.
if layout, ok := b.layouts[typ]; ok {
return layout
}
// otherwise: create a layout, and populate it with the default templates
layout := b.layoutTemplate
layout.descriptor = &blockDescriptor{}
*layout.descriptor = b.descriptorTemplate
// getting the signature now will panic on invalid types before we invest in creating a callback.
layout.descriptor.signature = b.encode(typ)
// create a global callback.
// this single callback can dispatch to any function with the same signature,
// since the user-provided functions are associated with the actual block allocations.
layout.invoke = purego.NewCallback(
reflect.MakeFunc(
typ,
func(args []reflect.Value) (results []reflect.Value) {
return b.Functions.Load(args[0].Interface().(Block)).Call(args)
},
).Interface(),
)
// store it and return it
b.layouts[typ] = layout
return layout
}
// newBlockCache initializes a block cache.
// It should not be called until AFTER libobjc is fully initialized.
func newBlockCache() *blockCache {
cache := &blockCache{
descriptorTemplate: blockDescriptor{
size: unsafe.Sizeof(blockLayout{}),
},
layoutTemplate: blockLayout{
isa: GetClass(blockBaseClass),
flags: blockFlags,
},
layouts: map[reflect.Type]blockLayout{},
Functions: newBlockFunctionCache(),
}
cache.descriptorTemplate.dispose = purego.NewCallback(cache.Functions.Delete)
return cache
}
// theBlocksCache is the global block cache
var theBlocksCache *blockCache
// Block is an opaque pointer to an Objective-C object containing a function with its associated closure.
type Block ID
// Copy creates a copy of a block on the Objective-C heap (or increments the reference count if already on the heap).
// Use [Block.Release] to free the copy when it is no longer in use.
func (b Block) Copy() Block {
return _Block_copy(b)
}
// Invoke calls the implementation of a block.
func (b Block) Invoke(args ...any) {
fn := theBlocksCache.Functions.Load(b)
reflectedArgs := make([]reflect.Value, len(args)+1)
reflectedArgs[0] = reflect.ValueOf(b)
for i := range args {
reflectedArgs[i+1] = reflect.ValueOf(args[i])
}
fn.Call(reflectedArgs)
}
// Release decrements the Block's reference count, and if it is the last reference, frees it.
func (b Block) Release() {
_Block_release(b)
}
// NewBlock takes a Go function that takes a Block as its first argument.
// It returns an Block that can be called by Objective-C code.
// The function panics if an error occurs.
// Use [Block.Release] to free this block when it is no longer in use.
func NewBlock(fn any) Block {
// get or create a block layout for the callback.
layout := theBlocksCache.getLayout(reflect.TypeOf(fn))
// we created the layout in Go memory, so we'll copy it to a newly-created Objective-C object.
block := Block(unsafe.Pointer(&layout)).Copy()
// associate the fn with the block we created before returning it.
return theBlocksCache.Functions.Store(block, reflect.ValueOf(fn))
}
// InvokeBlock is a convenience method for calling the implementation of a block.
// The block implementation must return 1 value.
func InvokeBlock[T any](block Block, args ...any) (result T, err error) {
block = block.Copy()
defer block.Release()
fn := theBlocksCache.Functions.Load(block)
if fn.Type().NumIn() != len(args)+1 {
return result, fmt.Errorf("objc: block callback expects %d arguments, got %d", fn.Type().NumIn()-1, len(args))
}
reflectedArgs := make([]reflect.Value, len(args)+1)
reflectedArgs[0] = reflect.ValueOf(block)
for i := range args {
reflectedArgs[i+1] = reflect.ValueOf(args[i])
}
callResult := fn.Call(reflectedArgs)
var ok bool
result, ok = callResult[0].Interface().(T)
if !ok {
return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)
}
return result, nil
}
+193 -60
View File
@@ -11,41 +11,59 @@ import (
"math"
"reflect"
"regexp"
"runtime"
"unicode"
"unsafe"
"github.com/ebitengine/purego"
"github.com/ebitengine/purego/internal/strings"
)
// TODO: support try/catch?
// https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime
var (
objc_msgSend_fn uintptr
objc_msgSend func(obj ID, cmd SEL, args ...interface{}) ID
objc_msgSendSuper2_fn uintptr
objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...interface{}) ID
objc_getClass func(name string) Class
objc_getProtocol func(name string) *Protocol
objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class
objc_registerClassPair func(class Class)
sel_registerName func(name string) SEL
class_getSuperclass func(class Class) Class
class_getInstanceVariable func(class Class, name string) Ivar
class_getInstanceSize func(class Class) uintptr
class_addMethod func(class Class, name SEL, imp IMP, types string) bool
class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool
class_addProtocol func(class Class, protocol *Protocol) bool
ivar_getOffset func(ivar Ivar) uintptr
ivar_getName func(ivar Ivar) string
object_getClass func(obj ID) Class
object_getIvar func(obj ID, ivar Ivar) ID
object_setIvar func(obj ID, ivar Ivar, value ID)
protocol_getName func(protocol *Protocol) string
protocol_isEqual func(p *Protocol, p2 *Protocol) bool
objc_msgSend_fn uintptr
objc_msgSend_stret_fn uintptr
objc_msgSend func(obj ID, cmd SEL, args ...any) ID
objc_msgSendSuper2_fn uintptr
objc_msgSendSuper2_stret_fn uintptr
objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...any) ID
objc_getClass func(name string) Class
objc_getProtocol func(name string) *Protocol
objc_allocateProtocol func(name string) *Protocol
objc_registerProtocol func(protocol *Protocol)
objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class
objc_registerClassPair func(class Class)
sel_registerName func(name string) SEL
class_getSuperclass func(class Class) Class
class_getInstanceVariable func(class Class, name string) Ivar
class_getInstanceSize func(class Class) uintptr
class_addMethod func(class Class, name SEL, imp IMP, types string) bool
class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool
class_addProtocol func(class Class, protocol *Protocol) bool
ivar_getOffset func(ivar Ivar) uintptr
ivar_getName func(ivar Ivar) string
object_getClass func(obj ID) Class
object_getIvar func(obj ID, ivar Ivar) ID
object_setIvar func(obj ID, ivar Ivar, value ID)
protocol_getName func(protocol *Protocol) string
protocol_isEqual func(p *Protocol, p2 *Protocol) bool
protocol_addMethodDescription func(p *Protocol, name SEL, types string, isRequiredMethod bool, isInstanceMethod bool)
protocol_copyMethodDescriptionList func(p *Protocol, isRequiredMethod bool, isInstanceMethod bool, outCount *uint32) *MethodDescription
protocol_copyProtocolList func(p *Protocol, outCount *uint32) **Protocol
protocol_copyPropertyList2 func(p *Protocol, outCount *uint32, isRequiredProperty, isInstanceProperty bool) *Property
protocol_addProtocol func(p *Protocol, p2 *Protocol)
protocol_addProperty func(p *Protocol, name string, attributes []PropertyAttribute, attributeCount uint32, isRequiredProperty bool, isInstanceProperty bool)
property_getName func(p Property) string
property_getAttributes func(p Property) string
free func(ptr unsafe.Pointer)
_Block_copy func(Block) Block
_Block_release func(Block)
)
func init() {
objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL|purego.RTLD_NOW)
if err != nil {
panic(fmt.Errorf("objc: %w", err))
}
@@ -53,6 +71,16 @@ func init() {
if err != nil {
panic(fmt.Errorf("objc: %w", err))
}
if runtime.GOARCH == "amd64" {
objc_msgSend_stret_fn, err = purego.Dlsym(objc, "objc_msgSend_stret")
if err != nil {
panic(fmt.Errorf("objc: %w", err))
}
objc_msgSendSuper2_stret_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2_stret")
if err != nil {
panic(fmt.Errorf("objc: %w", err))
}
}
purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
if err != nil {
@@ -62,6 +90,8 @@ func init() {
purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol")
purego.RegisterLibFunc(&objc_allocateProtocol, objc, "objc_allocateProtocol")
purego.RegisterLibFunc(&objc_registerProtocol, objc, "objc_registerProtocol")
purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair")
purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair")
purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName")
@@ -75,8 +105,21 @@ func init() {
purego.RegisterLibFunc(&ivar_getName, objc, "ivar_getName")
purego.RegisterLibFunc(&protocol_getName, objc, "protocol_getName")
purego.RegisterLibFunc(&protocol_isEqual, objc, "protocol_isEqual")
purego.RegisterLibFunc(&protocol_addMethodDescription, objc, "protocol_addMethodDescription")
purego.RegisterLibFunc(&protocol_copyMethodDescriptionList, objc, "protocol_copyMethodDescriptionList")
purego.RegisterLibFunc(&protocol_copyProtocolList, objc, "protocol_copyProtocolList")
purego.RegisterLibFunc(&protocol_addProtocol, objc, "protocol_addProtocol")
purego.RegisterLibFunc(&protocol_addProperty, objc, "protocol_addProperty")
purego.RegisterLibFunc(&protocol_copyPropertyList2, objc, "protocol_copyPropertyList2")
purego.RegisterLibFunc(&property_getName, objc, "property_getName")
purego.RegisterLibFunc(&property_getAttributes, objc, "property_getAttributes")
purego.RegisterLibFunc(&object_getIvar, objc, "object_getIvar")
purego.RegisterLibFunc(&object_setIvar, objc, "object_setIvar")
purego.RegisterLibFunc(&free, purego.RTLD_DEFAULT, "free")
purego.RegisterLibFunc(&_Block_copy, objc, "_Block_copy")
purego.RegisterLibFunc(&_Block_release, objc, "_Block_release")
theBlocksCache = newBlockCache()
}
// ID is an opaque pointer to some Objective-C object
@@ -90,7 +133,7 @@ func (id ID) Class() Class {
// Send is a convenience method for sending messages to objects. This function takes a SEL
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
// of RegisterName.
func (id ID) Send(sel SEL, args ...interface{}) ID {
func (id ID) Send(sel SEL, args ...any) ID {
return objc_msgSend(id, sel, args...)
}
@@ -104,12 +147,22 @@ func (id ID) SetIvar(ivar Ivar, value ID) {
object_setIvar(id, ivar, value)
}
// keep in sync with func.go
const maxRegAllocStructSize = 16
// Send is a convenience method for sending messages to objects that can return any type.
// This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock.
// It is best to cache the result of RegisterName.
func Send[T any](id ID, sel SEL, args ...any) T {
var fn func(id ID, sel SEL, args ...any) T
purego.RegisterFunc(&fn, objc_msgSend_fn)
var zero T
if runtime.GOARCH == "amd64" &&
reflect.ValueOf(zero).Kind() == reflect.Struct &&
reflect.ValueOf(zero).Type().Size() > maxRegAllocStructSize {
purego.RegisterFunc(&fn, objc_msgSend_stret_fn)
} else {
purego.RegisterFunc(&fn, objc_msgSend_fn)
}
return fn(id, sel, args...)
}
@@ -124,7 +177,7 @@ type objc_super struct {
// SendSuper is a convenience method for sending message to object's super. This function takes a SEL
// instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
// of RegisterName.
func (id ID) SendSuper(sel SEL, args ...interface{}) ID {
func (id ID) SendSuper(sel SEL, args ...any) ID {
super := &objc_super{
receiver: id,
superClass: id.Class(),
@@ -141,7 +194,14 @@ func SendSuper[T any](id ID, sel SEL, args ...any) T {
superClass: id.Class(),
}
var fn func(objcSuper *objc_super, sel SEL, args ...any) T
purego.RegisterFunc(&fn, objc_msgSendSuper2_fn)
var zero T
if runtime.GOARCH == "amd64" &&
reflect.ValueOf(zero).Kind() == reflect.Struct &&
reflect.ValueOf(zero).Type().Size() > maxRegAllocStructSize {
purego.RegisterFunc(&fn, objc_msgSendSuper2_stret_fn)
} else {
purego.RegisterFunc(&fn, objc_msgSendSuper2_fn)
}
return fn(super, sel, args...)
}
@@ -163,13 +223,6 @@ func GetClass(name string) Class {
return objc_getClass(name)
}
// AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created
//
// Deprecated: use RegisterClass instead
func AllocateClassPair(super Class, name string, extraBytes uintptr) Class {
return objc_allocateClassPair(super, name, extraBytes)
}
// MethodDef represents the Go function and the selector that ObjC uses to access that function.
type MethodDef struct {
Cmd SEL
@@ -360,7 +413,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
switch typ {
case reflect.TypeOf(Class(0)):
return encClass, nil
case reflect.TypeOf(ID(0)):
case reflect.TypeOf(ID(0)), reflect.TypeOf(Block(0)):
return encId, nil
case reflect.TypeOf(SEL(0)):
return encSelector, nil
@@ -414,7 +467,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
}
encoding += tmp
}
encoding = encStructEnd
encoding += encStructEnd
return encoding, nil
case reflect.UnsafePointer:
return encUnsafePtr, nil
@@ -426,7 +479,7 @@ func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
}
// encodeFunc returns a functions type as if it was given to @encode(fn)
func encodeFunc(fn interface{}) (string, error) {
func encodeFunc(fn any) (string, error) {
typ := reflect.TypeOf(fn)
if typ.Kind() != reflect.Func {
return "", errors.New("not a func")
@@ -476,20 +529,6 @@ func (c Class) AddMethod(name SEL, imp IMP, types string) bool {
return class_addMethod(c, name, imp, types)
}
// AddIvar adds a new instance variable to a class.
// It may only be called after AllocateClassPair and before Register.
// Adding an instance variable to an existing class is not supported.
// The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
// It takes the instance of the type of the Ivar and a string representing the type.
//
// Deprecated: use RegisterClass instead
func (c Class) AddIvar(name string, ty interface{}, types string) bool {
typeOf := reflect.TypeOf(ty)
size := typeOf.Size()
alignment := uint8(math.Log2(float64(typeOf.Align())))
return class_addIvar(c, name, size, alignment, types)
}
// AddProtocol adds a protocol to a class.
// Returns true if the protocol was added successfully, otherwise false (for example,
// the class already conforms to that protocol).
@@ -507,14 +546,6 @@ func (c Class) InstanceVariable(name string) Ivar {
return class_getInstanceVariable(c, name)
}
// Register registers a class that was allocated using AllocateClassPair.
// It can now be used to make objects by sending it either alloc and init or new.
//
// Deprecated: use RegisterClass instead
func (c Class) Register() {
objc_registerClassPair(c)
}
// Ivar an opaque type that represents an instance variable.
type Ivar uintptr
@@ -530,6 +561,39 @@ func (i Ivar) Name() string {
return ivar_getName(i)
}
// MethodDescription holds the name and type definition of a method.
type MethodDescription struct {
name, types uintptr
}
// Name returns the name of this method.
func (m MethodDescription) Name() string {
return strings.GoString(m.name)
}
// Types returns the OBJC runtime encoded type description.
func (m MethodDescription) Types() string {
return strings.GoString(m.types)
}
// PropertyAttribute contains the null-terminated Name and Value pair of a Properties internal description.
type PropertyAttribute struct {
Name, Value *byte
}
// Property is an opaque type for Objective-C property metadata.
type Property uintptr
// Name returns the name of this property.
func (p Property) Name() string {
return property_getName(p)
}
// Attributes returns a comma separated list of PropertyAttribute
func (p Property) Attributes() string {
return property_getAttributes(p)
}
// Protocol is a type that declares methods that can be implemented by any class.
type Protocol [0]func()
@@ -538,11 +602,69 @@ func GetProtocol(name string) *Protocol {
return objc_getProtocol(name)
}
// AllocateProtocol creates a new protocol in the OBJC runtime or nil if the protocol already exists.
func AllocateProtocol(name string) *Protocol {
return objc_allocateProtocol(name)
}
// Register registers the protocol created using AllocateProtocol with the runtime. This must be done
// before it is used anywhere and can only be called once.
func (p *Protocol) Register() {
objc_registerProtocol(p)
}
// CopyMethodDescriptionList returns a list of methods that this protocol has given it isRequiredMethod and isInstanceMethod.
func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {
count := uint32(0)
desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)
methods := clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return methods
}
// CopyProtocolList returns a list of the protocols that this protocol inherits from.
func (p *Protocol) CopyProtocolList() []*Protocol {
count := uint32(0)
desc := protocol_copyProtocolList(p, &count)
protocols := clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return protocols
}
// CopyPropertyList returns a list of properties that this protocol has given it isRequiredProperty and isInstanceProperty.
func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {
count := uint32(0)
desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)
protocols := clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return protocols
}
// Name returns the name of this protocol.
func (p *Protocol) Name() string {
return protocol_getName(p)
}
// Equals return true if the two protocols are the same.
func (p *Protocol) Equals(p2 *Protocol) bool {
return protocol_isEqual(p, p2)
}
// AddMethodDescription adds a method to a protocol. This can only be called between AllocateProtocol and Protocol.Register.
func (p *Protocol) AddMethodDescription(name SEL, types string, isRequiredMethod, isInstanceMethod bool) {
protocol_addMethodDescription(p, name, types, isRequiredMethod, isInstanceMethod)
}
// AddProtocol marks the protocol as inheriting from another. This can only be called between AllocateProtocol and Protocol.Register.
func (p *Protocol) AddProtocol(protocol *Protocol) {
protocol_addProtocol(p, protocol)
}
// AddProperty adds a property to the protocol. This can only be called between AllocateProtocol and Protocol.Register.
func (p *Protocol) AddProperty(name string, attributes []PropertyAttribute, isRequiredProperty, isInstanceProperty bool) {
protocol_addProperty(p, name, attributes, uint32(len(attributes)), isRequiredProperty, isInstanceProperty)
}
// IMP is a function pointer that can be called by Objective-C code.
type IMP uintptr
@@ -550,7 +672,7 @@ type IMP uintptr
// It returns an IMP function pointer that can be called by Objective-C code.
// The function panics if an error occurs.
// The function pointer is never deallocated.
func NewIMP(fn interface{}) IMP {
func NewIMP(fn any) IMP {
ty := reflect.TypeOf(fn)
if ty.Kind() != reflect.Func {
panic("objc: not a function")
@@ -567,3 +689,14 @@ func NewIMP(fn interface{}) IMP {
}
return IMP(purego.NewCallback(fn))
}
// TODO: remove and use slices.Clone when minimum version for purego is 1.21
func clone[S ~[]E, E any](s S) S {
// Preserve nilness in case it matters.
if s == nil {
return nil
}
// Avoid s[:0:0] as it leads to unwanted liveness when cloning a
// zero-length slice of a large array; see https://go.dev/issue/68488.
return append(S{}, s...)
}