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