github.com/jwijenbergh/purego@v0.0.0-20240126093400-70ff3a61df13/objc/objc_runtime_darwin.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2022 The Ebitengine Authors
     3  
     4  // Package objc is a low-level pure Go objective-c runtime. This package is easy to use incorrectly, so it is best
     5  // to use a wrapper that provides the functionality you need in a safer way.
     6  package objc
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"math"
    12  	"reflect"
    13  	"regexp"
    14  	"unicode"
    15  	"unsafe"
    16  
    17  	"github.com/jwijenbergh/purego"
    18  )
    19  
    20  // TODO: support try/catch?
    21  // https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime
    22  var (
    23  	objc_msgSend_fn           uintptr
    24  	objc_msgSend              func(obj ID, cmd SEL, args ...interface{}) ID
    25  	objc_msgSendSuper2_fn     uintptr
    26  	objc_msgSendSuper2        func(super *objc_super, cmd SEL, args ...interface{}) ID
    27  	objc_getClass             func(name string) Class
    28  	objc_getProtocol          func(name string) *Protocol
    29  	objc_allocateClassPair    func(super Class, name string, extraBytes uintptr) Class
    30  	objc_registerClassPair    func(class Class)
    31  	sel_registerName          func(name string) SEL
    32  	class_getSuperclass       func(class Class) Class
    33  	class_getInstanceVariable func(class Class, name string) Ivar
    34  	class_getInstanceSize     func(class Class) uintptr
    35  	class_addMethod           func(class Class, name SEL, imp IMP, types string) bool
    36  	class_addIvar             func(class Class, name string, size uintptr, alignment uint8, types string) bool
    37  	class_addProtocol         func(class Class, protocol *Protocol) bool
    38  	ivar_getOffset            func(ivar Ivar) uintptr
    39  	ivar_getName              func(ivar Ivar) string
    40  	object_getClass           func(obj ID) Class
    41  	object_getIvar            func(obj ID, ivar Ivar) ID
    42  	object_setIvar            func(obj ID, ivar Ivar, value ID)
    43  	protocol_getName          func(protocol *Protocol) string
    44  	protocol_isEqual          func(p *Protocol, p2 *Protocol) bool
    45  )
    46  
    47  func init() {
    48  	objc, err := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL)
    49  	if err != nil {
    50  		panic(fmt.Errorf("objc: %w", err))
    51  	}
    52  	objc_msgSend_fn, err = purego.Dlsym(objc, "objc_msgSend")
    53  	if err != nil {
    54  		panic(fmt.Errorf("objc: %w", err))
    55  	}
    56  	purego.RegisterFunc(&objc_msgSend, objc_msgSend_fn)
    57  	objc_msgSendSuper2_fn, err = purego.Dlsym(objc, "objc_msgSendSuper2")
    58  	if err != nil {
    59  		panic(fmt.Errorf("objc: %w", err))
    60  	}
    61  	purego.RegisterFunc(&objc_msgSendSuper2, objc_msgSendSuper2_fn)
    62  	purego.RegisterLibFunc(&object_getClass, objc, "object_getClass")
    63  	purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass")
    64  	purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol")
    65  	purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair")
    66  	purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair")
    67  	purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName")
    68  	purego.RegisterLibFunc(&class_getSuperclass, objc, "class_getSuperclass")
    69  	purego.RegisterLibFunc(&class_getInstanceVariable, objc, "class_getInstanceVariable")
    70  	purego.RegisterLibFunc(&class_addMethod, objc, "class_addMethod")
    71  	purego.RegisterLibFunc(&class_addIvar, objc, "class_addIvar")
    72  	purego.RegisterLibFunc(&class_addProtocol, objc, "class_addProtocol")
    73  	purego.RegisterLibFunc(&class_getInstanceSize, objc, "class_getInstanceSize")
    74  	purego.RegisterLibFunc(&ivar_getOffset, objc, "ivar_getOffset")
    75  	purego.RegisterLibFunc(&ivar_getName, objc, "ivar_getName")
    76  	purego.RegisterLibFunc(&protocol_getName, objc, "protocol_getName")
    77  	purego.RegisterLibFunc(&protocol_isEqual, objc, "protocol_isEqual")
    78  	purego.RegisterLibFunc(&object_getIvar, objc, "object_getIvar")
    79  	purego.RegisterLibFunc(&object_setIvar, objc, "object_setIvar")
    80  }
    81  
    82  // ID is an opaque pointer to some Objective-C object
    83  type ID uintptr
    84  
    85  // Class returns the class of the object.
    86  func (id ID) Class() Class {
    87  	return object_getClass(id)
    88  }
    89  
    90  // Send is a convenience method for sending messages to objects. This function takes a SEL
    91  // instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
    92  // of RegisterName.
    93  func (id ID) Send(sel SEL, args ...interface{}) ID {
    94  	return objc_msgSend(id, sel, args...)
    95  }
    96  
    97  // GetIvar reads the value of an instance variable in an object.
    98  func (id ID) GetIvar(ivar Ivar) ID {
    99  	return object_getIvar(id, ivar)
   100  }
   101  
   102  // SetIvar sets the value of an instance variable in an object.
   103  func (id ID) SetIvar(ivar Ivar, value ID) {
   104  	object_setIvar(id, ivar, value)
   105  }
   106  
   107  // Send is a convenience method for sending messages to objects that can return any type.
   108  // This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock.
   109  // It is best to cache the result of RegisterName.
   110  func Send[T any](id ID, sel SEL, args ...any) T {
   111  	var fn func(id ID, sel SEL, args ...any) T
   112  	purego.RegisterFunc(&fn, objc_msgSend_fn)
   113  	return fn(id, sel, args...)
   114  }
   115  
   116  // objc_super data structure is generated by the Objective-C compiler when it encounters the super keyword
   117  // as the receiver of a message. It specifies the class definition of the particular superclass that should
   118  // be messaged.
   119  type objc_super struct {
   120  	receiver   ID
   121  	superClass Class
   122  }
   123  
   124  // SendSuper is a convenience method for sending message to object's super. This function takes a SEL
   125  // instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result
   126  // of RegisterName.
   127  func (id ID) SendSuper(sel SEL, args ...interface{}) ID {
   128  	super := &objc_super{
   129  		receiver:   id,
   130  		superClass: id.Class(),
   131  	}
   132  	return objc_msgSendSuper2(super, sel, args...)
   133  }
   134  
   135  // SendSuper is a convenience method for sending message to object's super that can return any type.
   136  // This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock.
   137  // It is best to cache the result of RegisterName.
   138  func SendSuper[T any](id ID, sel SEL, args ...any) T {
   139  	super := &objc_super{
   140  		receiver:   id,
   141  		superClass: id.Class(),
   142  	}
   143  	var fn func(objcSuper *objc_super, sel SEL, args ...any) T
   144  	purego.RegisterFunc(&fn, objc_msgSendSuper2_fn)
   145  	return fn(super, sel, args...)
   146  }
   147  
   148  // SEL is an opaque type that represents a method selector
   149  type SEL uintptr
   150  
   151  // RegisterName registers a method with the Objective-C runtime system, maps the method name to a selector,
   152  // and returns the selector value. This function grabs the global Objective-c lock. It is best the cache the
   153  // result of this function.
   154  func RegisterName(name string) SEL {
   155  	return sel_registerName(name)
   156  }
   157  
   158  // Class is an opaque type that represents an Objective-C class.
   159  type Class uintptr
   160  
   161  // GetClass returns the Class object for the named class, or nil if the class is not registered with the Objective-C runtime.
   162  func GetClass(name string) Class {
   163  	return objc_getClass(name)
   164  }
   165  
   166  // AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created
   167  //
   168  // Deprecated: use RegisterClass instead
   169  func AllocateClassPair(super Class, name string, extraBytes uintptr) Class {
   170  	return objc_allocateClassPair(super, name, extraBytes)
   171  }
   172  
   173  // MethodDef represents the Go function and the selector that ObjC uses to access that function.
   174  type MethodDef struct {
   175  	Cmd SEL
   176  	Fn  any
   177  }
   178  
   179  // IvarAttrib is the attribute that an ivar has. It affects if and which methods are automatically
   180  // generated when creating a class with RegisterClass. See [Apple Docs] for an understanding of these attributes.
   181  // The fields are still accessible using objc.GetIvar and objc.SetIvar regardless of the value of IvarAttrib.
   182  //
   183  // Take for example this Objective-C code:
   184  //
   185  //	@property (readwrite) float value;
   186  //
   187  // In Go, the functions can be accessed as followed:
   188  //
   189  //	var value = purego.Send[float32](id, purego.RegisterName("value"))
   190  //	id.Send(purego.RegisterName("setValue:"), 3.46)
   191  //
   192  // [Apple Docs]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html
   193  type IvarAttrib int
   194  
   195  const (
   196  	ReadOnly IvarAttrib = 1 << iota
   197  	ReadWrite
   198  )
   199  
   200  // FieldDef is a definition of a field to add to an Objective-C class.
   201  // The name of the field is what will be used to access it through the Ivar. If the type is bool
   202  // the name cannot start with `is` since a getter will be generated with the name `isBoolName`.
   203  // The name also cannot contain any spaces.
   204  // The type is the Go equivalent type of the Ivar.
   205  // Attribute determines if a getter and or setter method is generated for this field.
   206  type FieldDef struct {
   207  	Name      string
   208  	Type      reflect.Type
   209  	Attribute IvarAttrib
   210  }
   211  
   212  // ivarRegex checks to make sure the Ivar is correctly formatted
   213  var ivarRegex = regexp.MustCompile("[a-z_][a-zA-Z0-9_]*")
   214  
   215  // RegisterClass takes the name of the class to create, the superclass, a list of protocols this class
   216  // implements, a list of fields this class has and a list of methods. It returns the created class or an error
   217  // describing what went wrong.
   218  func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars []FieldDef, methods []MethodDef) (Class, error) {
   219  	class := objc_allocateClassPair(superClass, name, 0)
   220  	if class == 0 {
   221  		return 0, fmt.Errorf("objc: failed to create class with name '%s'", name)
   222  	}
   223  	// Add Protocols
   224  	for _, p := range protocols {
   225  		if !class.AddProtocol(p) {
   226  			return 0, fmt.Errorf("objc: couldn't add Protocol %s", protocol_getName(p))
   227  		}
   228  	}
   229  	// Add exported methods based on the selectors returned from ClassDef(string) SEL
   230  	for idx, def := range methods {
   231  		imp, err := func() (imp IMP, err error) {
   232  			defer func() {
   233  				if r := recover(); r != nil {
   234  					err = fmt.Errorf("objc: failed to create IMP: %s", r)
   235  				}
   236  			}()
   237  			return NewIMP(def.Fn), nil
   238  		}()
   239  		if err != nil {
   240  			return 0, fmt.Errorf("objc: couldn't add Method at index %d: %w", idx, err)
   241  		}
   242  		encoding, err := encodeFunc(def.Fn)
   243  		if err != nil {
   244  			return 0, fmt.Errorf("objc: couldn't add Method at index %d: %w", idx, err)
   245  		}
   246  		if !class.AddMethod(def.Cmd, imp, encoding) {
   247  			return 0, fmt.Errorf("objc: couldn't add Method at index %d", idx)
   248  		}
   249  	}
   250  	// Add Ivars
   251  	for _, instVar := range ivars {
   252  		ivar := instVar
   253  		if !ivarRegex.MatchString(ivar.Name) {
   254  			return 0, fmt.Errorf("objc: Ivar must start with a lowercase letter and only contain ASCII letters and numbers: '%s'", ivar.Name)
   255  		}
   256  		size := ivar.Type.Size()
   257  		alignment := uint8(math.Log2(float64(ivar.Type.Align())))
   258  		enc, err := encodeType(ivar.Type, false)
   259  		if err != nil {
   260  			return 0, fmt.Errorf("objc: couldn't add Ivar %s: %w", ivar.Name, err)
   261  		}
   262  		if !class_addIvar(class, ivar.Name, size, alignment, enc) {
   263  			return 0, fmt.Errorf("objc: couldn't add Ivar %s", ivar.Name)
   264  		}
   265  		offset := class.InstanceVariable(ivar.Name).Offset()
   266  		switch ivar.Attribute {
   267  		case ReadWrite:
   268  			ty := reflect.FuncOf(
   269  				[]reflect.Type{
   270  					reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)), ivar.Type,
   271  				},
   272  				nil, false,
   273  			)
   274  			var encoding string
   275  			if encoding, err = encodeFunc(reflect.New(ty).Elem().Interface()); err != nil {
   276  				return 0, fmt.Errorf("objc: failed to create read method for '%s': %w", ivar.Name, err)
   277  			}
   278  			val := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
   279  				// on entry the first and second arguments are ID and SEL followed by the value
   280  				if len(args) != 3 {
   281  					panic(fmt.Sprintf("objc: incorrect number of args. expected 3 got %d", len(args)))
   282  				}
   283  				// The following reflect code does the equivalent of this:
   284  				//
   285  				//	((*struct {
   286  				//		Padding [offset]byte
   287  				//		Value int
   288  				//	})(unsafe.Pointer(args[0].Interface().(ID)))).v = 123
   289  				//
   290  				// However, since the type of the variable is unknown reflection is used to actually assign the value
   291  				id := args[0].Interface().(ID)
   292  				ptr := *(*unsafe.Pointer)(unsafe.Pointer(&id)) // circumvent go vet
   293  				reflect.NewAt(ivar.Type, unsafe.Add(ptr, offset)).Elem().Set(args[2])
   294  				return nil
   295  			}).Interface()
   296  			// this code only works for ascii but that shouldn't be a problem
   297  			selector := "set" + string(unicode.ToUpper(rune(ivar.Name[0]))) + ivar.Name[1:] + ":\x00"
   298  			class.AddMethod(RegisterName(selector), NewIMP(val), encoding)
   299  			fallthrough // also implement the read method
   300  		case ReadOnly:
   301  			ty := reflect.FuncOf(
   302  				[]reflect.Type{
   303  					reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)),
   304  				},
   305  				[]reflect.Type{ivar.Type}, false,
   306  			)
   307  			var encoding string
   308  			if encoding, err = encodeFunc(reflect.New(ty).Elem().Interface()); err != nil {
   309  				return 0, fmt.Errorf("objc: failed to create read method for '%s': %w", ivar.Name, err)
   310  			}
   311  			val := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
   312  				// on entry the first and second arguments are ID and SEL
   313  				if len(args) != 2 {
   314  					panic(fmt.Sprintf("objc: incorrect number of args. expected 2 got %d", len(args)))
   315  				}
   316  				id := args[0].Interface().(ID)
   317  				ptr := *(*unsafe.Pointer)(unsafe.Pointer(&id)) // circumvent go vet
   318  				// the variable is located at an offset from the id
   319  				return []reflect.Value{reflect.NewAt(ivar.Type, unsafe.Add(ptr, offset)).Elem()}
   320  			}).Interface()
   321  			if ivar.Type.Kind() == reflect.Bool {
   322  				// this code only works for ascii but that shouldn't be a problem
   323  				ivar.Name = "is" + string(unicode.ToUpper(rune(ivar.Name[0]))) + ivar.Name[1:]
   324  			}
   325  			class.AddMethod(RegisterName(ivar.Name), NewIMP(val), encoding)
   326  		default:
   327  			return 0, fmt.Errorf("objc: unknown Ivar Attribute (%d)", ivar.Attribute)
   328  		}
   329  	}
   330  	objc_registerClassPair(class)
   331  	return class, nil
   332  }
   333  
   334  const (
   335  	encId          = "@"
   336  	encClass       = "#"
   337  	encSelector    = ":"
   338  	encChar        = "c"
   339  	encUChar       = "C"
   340  	encShort       = "s"
   341  	encUShort      = "S"
   342  	encInt         = "i"
   343  	encUInt        = "I"
   344  	encLong        = "l"
   345  	encULong       = "L"
   346  	encFloat       = "f"
   347  	encDouble      = "d"
   348  	encBool        = "B"
   349  	encVoid        = "v"
   350  	encPtr         = "^"
   351  	encCharPtr     = "*"
   352  	encStructBegin = "{"
   353  	encStructEnd   = "}"
   354  	encUnsafePtr   = "^v"
   355  )
   356  
   357  // encodeType returns a string representing a type as if it was given to @encode(typ)
   358  // Source: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
   359  func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
   360  	switch typ {
   361  	case reflect.TypeOf(Class(0)):
   362  		return encClass, nil
   363  	case reflect.TypeOf(ID(0)):
   364  		return encId, nil
   365  	case reflect.TypeOf(SEL(0)):
   366  		return encSelector, nil
   367  	}
   368  
   369  	kind := typ.Kind()
   370  	switch kind {
   371  	case reflect.Bool:
   372  		return encBool, nil
   373  	case reflect.Int:
   374  		return encLong, nil
   375  	case reflect.Int8:
   376  		return encChar, nil
   377  	case reflect.Int16:
   378  		return encShort, nil
   379  	case reflect.Int32:
   380  		return encInt, nil
   381  	case reflect.Int64:
   382  		return encULong, nil
   383  	case reflect.Uint:
   384  		return encULong, nil
   385  	case reflect.Uint8:
   386  		return encUChar, nil
   387  	case reflect.Uint16:
   388  		return encUShort, nil
   389  	case reflect.Uint32:
   390  		return encUInt, nil
   391  	case reflect.Uint64:
   392  		return encULong, nil
   393  	case reflect.Uintptr:
   394  		return encPtr, nil
   395  	case reflect.Float32:
   396  		return encFloat, nil
   397  	case reflect.Float64:
   398  		return encDouble, nil
   399  	case reflect.Ptr:
   400  		enc, err := encodeType(typ.Elem(), true)
   401  		return encPtr + enc, err
   402  	case reflect.Struct:
   403  		if insidePtr {
   404  			return encStructBegin + typ.Name() + encStructEnd, nil
   405  		}
   406  		encoding := encStructBegin
   407  		encoding += typ.Name()
   408  		encoding += "="
   409  		for i := 0; i < typ.NumField(); i++ {
   410  			f := typ.Field(i)
   411  			tmp, err := encodeType(f.Type, false)
   412  			if err != nil {
   413  				return "", err
   414  			}
   415  			encoding += tmp
   416  		}
   417  		encoding = encStructEnd
   418  		return encoding, nil
   419  	case reflect.UnsafePointer:
   420  		return encUnsafePtr, nil
   421  	case reflect.String:
   422  		return encCharPtr, nil
   423  	}
   424  
   425  	return "", errors.New(fmt.Sprintf("unhandled/invalid kind %v typed %v", kind, typ))
   426  }
   427  
   428  // encodeFunc returns a functions type as if it was given to @encode(fn)
   429  func encodeFunc(fn interface{}) (string, error) {
   430  	typ := reflect.TypeOf(fn)
   431  	if typ.Kind() != reflect.Func {
   432  		return "", errors.New("not a func")
   433  	}
   434  
   435  	encoding := ""
   436  	switch typ.NumOut() {
   437  	case 0:
   438  		encoding += encVoid
   439  	case 1:
   440  		tmp, err := encodeType(typ.Out(0), false)
   441  		if err != nil {
   442  			return "", err
   443  		}
   444  		encoding += tmp
   445  	default:
   446  		return "", errors.New("too many output parameters")
   447  	}
   448  
   449  	if typ.NumIn() < 2 {
   450  		return "", errors.New("func doesn't take ID and SEL as its first two parameters")
   451  	}
   452  
   453  	encoding += encId
   454  
   455  	for i := 1; i < typ.NumIn(); i++ {
   456  		tmp, err := encodeType(typ.In(i), false)
   457  		if err != nil {
   458  			return "", err
   459  		}
   460  		encoding += tmp
   461  	}
   462  	return encoding, nil
   463  }
   464  
   465  // SuperClass returns the superclass of a class.
   466  // You should usually use NSObject‘s superclass method instead of this function.
   467  func (c Class) SuperClass() Class {
   468  	return class_getSuperclass(c)
   469  }
   470  
   471  // AddMethod adds a new method to a class with a given name and implementation.
   472  // The types argument is a string containing the mapping of parameters and return type.
   473  // Since the function must take at least two arguments—self and _cmd, the second and third
   474  // characters must be “@:” (the first character is the return type).
   475  func (c Class) AddMethod(name SEL, imp IMP, types string) bool {
   476  	return class_addMethod(c, name, imp, types)
   477  }
   478  
   479  // AddIvar adds a new instance variable to a class.
   480  // It may only be called after AllocateClassPair and before Register.
   481  // Adding an instance variable to an existing class is not supported.
   482  // The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
   483  // It takes the instance of the type of the Ivar and a string representing the type.
   484  //
   485  // Deprecated: use RegisterClass instead
   486  func (c Class) AddIvar(name string, ty interface{}, types string) bool {
   487  	typeOf := reflect.TypeOf(ty)
   488  	size := typeOf.Size()
   489  	alignment := uint8(math.Log2(float64(typeOf.Align())))
   490  	return class_addIvar(c, name, size, alignment, types)
   491  }
   492  
   493  // AddProtocol adds a protocol to a class.
   494  // Returns true if the protocol was added successfully, otherwise false (for example,
   495  // the class already conforms to that protocol).
   496  func (c Class) AddProtocol(protocol *Protocol) bool {
   497  	return class_addProtocol(c, protocol)
   498  }
   499  
   500  // InstanceSize returns the size in bytes of instances of the class or 0 if cls is nil
   501  func (c Class) InstanceSize() uintptr {
   502  	return class_getInstanceSize(c)
   503  }
   504  
   505  // InstanceVariable returns an Ivar data structure containing information about the instance variable specified by name.
   506  func (c Class) InstanceVariable(name string) Ivar {
   507  	return class_getInstanceVariable(c, name)
   508  }
   509  
   510  // Register registers a class that was allocated using AllocateClassPair.
   511  // It can now be used to make objects by sending it either alloc and init or new.
   512  //
   513  // Deprecated: use RegisterClass instead
   514  func (c Class) Register() {
   515  	objc_registerClassPair(c)
   516  }
   517  
   518  // Ivar an opaque type that represents an instance variable.
   519  type Ivar uintptr
   520  
   521  // Offset returns the offset of an instance variable that can be used to assign and read the Ivar's value.
   522  //
   523  // For instance variables of type ID or other object types, call Ivar and SetIvar instead
   524  // of using this offset to access the instance variable data directly.
   525  func (i Ivar) Offset() uintptr {
   526  	return ivar_getOffset(i)
   527  }
   528  
   529  func (i Ivar) Name() string {
   530  	return ivar_getName(i)
   531  }
   532  
   533  // Protocol is a type that declares methods that can be implemented by any class.
   534  type Protocol [0]func()
   535  
   536  // GetProtocol returns the protocol for the given name or nil if there is no protocol by that name.
   537  func GetProtocol(name string) *Protocol {
   538  	return objc_getProtocol(name)
   539  }
   540  
   541  // Equals return true if the two protocols are the same.
   542  func (p *Protocol) Equals(p2 *Protocol) bool {
   543  	return protocol_isEqual(p, p2)
   544  }
   545  
   546  // IMP is a function pointer that can be called by Objective-C code.
   547  type IMP uintptr
   548  
   549  // NewIMP takes a Go function that takes (ID, SEL) as its first two arguments.
   550  // It returns an IMP function pointer that can be called by Objective-C code.
   551  // The function panics if an error occurs.
   552  // The function pointer is never deallocated.
   553  func NewIMP(fn interface{}) IMP {
   554  	ty := reflect.TypeOf(fn)
   555  	if ty.Kind() != reflect.Func {
   556  		panic("objc: not a function")
   557  	}
   558  	// IMP is stricter than a normal callback
   559  	// id (*IMP)(id, SEL, ...)
   560  	switch {
   561  	case ty.NumIn() < 2:
   562  		fallthrough
   563  	case ty.In(0) != reflect.TypeOf(ID(0)):
   564  		fallthrough
   565  	case ty.In(1) != reflect.TypeOf(SEL(0)):
   566  		panic("objc: NewIMP must take a (id, SEL) as its first two arguments; got " + ty.String())
   567  	}
   568  	return IMP(purego.NewCallback(fn))
   569  }