objc: remove unsafe usage from test (#57)
Add support for NewIMP to take a pointer to an Objective-C struct. This allows the removal of all uses of package unsafe from the tests by creating a struct that matches the structure of the Objective-C object.
- commit
- fbeb60f11c9695c15c8d6a15a5b22bc185161b80
- author
- TotallyGamerJet · · 2022-10-31 03:44 UTC
- committer
- GitHub · · 2022-10-31 03:44 UTC
- parent
- 63d5487
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
2 files · +20 -12
M objc/objc_runtime_darwin.go +9 -3
| @@ -168,8 +168,10 @@ func GetProtocol(name string) *Protocol { | |||
| 168 | 168 | // IMP is a function pointer that can be called by Objective-C code. | |
| 169 | 169 | type IMP uintptr | |
| 170 | 170 | ||
| 171 | - | // NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. It returns an IMP function | |
| 172 | - | // pointer that can be called by Objective-C code. The function pointer is never deallocated. | |
| 171 | + | // NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. | |
| 172 | + | // ID may instead be a pointer to a struct whose first field has type Class. | |
| 173 | + | // It returns an IMP function pointer that can be called by Objective-C code. | |
| 174 | + | // The function pointer is never deallocated. | |
| 173 | 175 | func NewIMP(fn interface{}) IMP { | |
| 174 | 176 | ty := reflect.TypeOf(fn) | |
| 175 | 177 | if ty.Kind() != reflect.Func { | |
| @@ -180,7 +182,11 @@ func NewIMP(fn interface{}) IMP { | |||
| 180 | 182 | switch { | |
| 181 | 183 | case ty.NumIn() < 2: | |
| 182 | 184 | fallthrough | |
| 183 | - | case ty.In(0).Kind() != reflect.Uintptr: | |
| 185 | + | case ty.In(0).Kind() != reflect.Uintptr && // checks if it's objc.ID | |
| 186 | + | // or that it's a pointer to a struct | |
| 187 | + | (ty.In(0).Kind() != reflect.Pointer || ty.In(0).Elem().Kind() != reflect.Struct || | |
| 188 | + | // and that the structs first field is an objc.Class | |
| 189 | + | ty.In(0).Elem().Field(0).Type != reflect.TypeOf(Class(0))): | |
| 184 | 190 | fallthrough | |
| 185 | 191 | case ty.In(1).Kind() != reflect.Uintptr: | |
| 186 | 192 | panic("objc: NewIMP must take a (id, SEL) as its first two arguments") | |
| @@ -168,8 +168,10 @@ func GetProtocol(name string) *Protocol { | |||||
| 168 | // IMP is a function pointer that can be called by Objective-C code. | 168 | // IMP is a function pointer that can be called by Objective-C code. | ||
| 169 | type IMP uintptr | 169 | type IMP uintptr | ||
| 170 | 170 | ||||
| 171 | - | // NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. It returns an IMP function | 171 | + | // NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. |
| 172 | - | // pointer that can be called by Objective-C code. The function pointer is never deallocated. | 172 | + | // ID may instead be a pointer to a struct whose first field has type Class. |
| 173 | + | // It returns an IMP function pointer that can be called by Objective-C code. | |||
| 174 | + | // The function pointer is never deallocated. | |||
| 173 | func NewIMP(fn interface{}) IMP { | 175 | func NewIMP(fn interface{}) IMP { | ||
| 174 | ty := reflect.TypeOf(fn) | 176 | ty := reflect.TypeOf(fn) | ||
| 175 | if ty.Kind() != reflect.Func { | 177 | if ty.Kind() != reflect.Func { | ||
| @@ -180,7 +182,11 @@ func NewIMP(fn interface{}) IMP { | |||||
| 180 | switch { | 182 | switch { | ||
| 181 | case ty.NumIn() < 2: | 183 | case ty.NumIn() < 2: | ||
| 182 | fallthrough | 184 | fallthrough | ||
| 183 | - | case ty.In(0).Kind() != reflect.Uintptr: | 185 | + | case ty.In(0).Kind() != reflect.Uintptr && // checks if it's objc.ID |
| 186 | + | // or that it's a pointer to a struct | |||
| 187 | + | (ty.In(0).Kind() != reflect.Pointer || ty.In(0).Elem().Kind() != reflect.Struct || | |||
| 188 | + | // and that the structs first field is an objc.Class | |||
| 189 | + | ty.In(0).Elem().Field(0).Type != reflect.TypeOf(Class(0))): | |||
| 184 | fallthrough | 190 | fallthrough | ||
| 185 | case ty.In(1).Kind() != reflect.Uintptr: | 191 | case ty.In(1).Kind() != reflect.Uintptr: | ||
| 186 | panic("objc: NewIMP must take a (id, SEL) as its first two arguments") | 192 | panic("objc: NewIMP must take a (id, SEL) as its first two arguments") | ||
M objc/objc_runtime_darwin_test.go +11 -9
| @@ -5,7 +5,6 @@ package objc_test | |||
| 5 | 5 | ||
| 6 | 6 | import ( | |
| 7 | 7 | "fmt" | |
| 8 | - | "unsafe" | |
| 9 | 8 | ||
| 10 | 9 | "github.com/ebitengine/purego" | |
| 11 | 10 | "github.com/ebitengine/purego/objc" | |
| @@ -24,20 +23,23 @@ func ExampleAllocateClassPair() { | |||
| 24 | 23 | } | |
| 25 | 24 | ||
| 26 | 25 | func ExampleClass_AddIvar() { | |
| 26 | + | type barObject struct { | |
| 27 | + | isa objc.Class | |
| 28 | + | bar int | |
| 29 | + | } | |
| 27 | 30 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "BarObject", 0) | |
| 28 | 31 | class.AddIvar("bar", int(0), "q") | |
| 29 | - | var barOffset = class.InstanceVariable("bar").Offset() | |
| 30 | - | class.AddMethod(objc.RegisterName("bar"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL) int { | |
| 31 | - | return *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + barOffset)) | |
| 32 | + | class.AddMethod(objc.RegisterName("bar"), objc.NewIMP(func(self *barObject, _cmd objc.SEL) int { | |
| 33 | + | return self.bar | |
| 32 | 34 | }), "q@:") | |
| 33 | - | class.AddMethod(objc.RegisterName("setBar:"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL, bar int) { | |
| 34 | - | *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + barOffset)) = bar | |
| 35 | + | class.AddMethod(objc.RegisterName("setBar:"), objc.NewIMP(func(self *barObject, _cmd objc.SEL, bar int) { | |
| 36 | + | self.bar = bar | |
| 35 | 37 | }), "v@:q") | |
| 36 | 38 | class.Register() | |
| 37 | 39 | ||
| 38 | - | var barObject = objc.ID(class).Send(objc.RegisterName("new")) | |
| 39 | - | barObject.Send(objc.RegisterName("setBar:"), 123) | |
| 40 | - | var bar = int(barObject.Send(objc.RegisterName("bar"))) | |
| 40 | + | var object = objc.ID(class).Send(objc.RegisterName("new")) | |
| 41 | + | object.Send(objc.RegisterName("setBar:"), 123) | |
| 42 | + | var bar = int(object.Send(objc.RegisterName("bar"))) | |
| 41 | 43 | fmt.Println(bar) | |
| 42 | 44 | // Output: 123 | |
| 43 | 45 | } | |
| @@ -5,7 +5,6 @@ package objc_test | |||||
| 5 | 5 | ||||
| 6 | import ( | 6 | import ( | ||
| 7 | "fmt" | 7 | "fmt" | ||
| 8 | - | "unsafe" | |||
| 9 | 8 | ||||
| 10 | "github.com/ebitengine/purego" | 9 | "github.com/ebitengine/purego" | ||
| 11 | "github.com/ebitengine/purego/objc" | 10 | "github.com/ebitengine/purego/objc" | ||
| @@ -24,20 +23,23 @@ func ExampleAllocateClassPair() { | |||||
| 24 | } | 23 | } | ||
| 25 | 24 | ||||
| 26 | func ExampleClass_AddIvar() { | 25 | func ExampleClass_AddIvar() { | ||
| 26 | + | type barObject struct { | |||
| 27 | + | isa objc.Class | |||
| 28 | + | bar int | |||
| 29 | + | } | |||
| 27 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "BarObject", 0) | 30 | var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "BarObject", 0) | ||
| 28 | class.AddIvar("bar", int(0), "q") | 31 | class.AddIvar("bar", int(0), "q") | ||
| 29 | - | var barOffset = class.InstanceVariable("bar").Offset() | 32 | + | class.AddMethod(objc.RegisterName("bar"), objc.NewIMP(func(self *barObject, _cmd objc.SEL) int { |
| 30 | - | class.AddMethod(objc.RegisterName("bar"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL) int { | 33 | + | return self.bar |
| 31 | - | return *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + barOffset)) | |||
| 32 | }), "q@:") | 34 | }), "q@:") | ||
| 33 | - | class.AddMethod(objc.RegisterName("setBar:"), objc.NewIMP(func(self objc.ID, _cmd objc.SEL, bar int) { | 35 | + | class.AddMethod(objc.RegisterName("setBar:"), objc.NewIMP(func(self *barObject, _cmd objc.SEL, bar int) { |
| 34 | - | *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + barOffset)) = bar | 36 | + | self.bar = bar |
| 35 | }), "v@:q") | 37 | }), "v@:q") | ||
| 36 | class.Register() | 38 | class.Register() | ||
| 37 | 39 | ||||
| 38 | - | var barObject = objc.ID(class).Send(objc.RegisterName("new")) | 40 | + | var object = objc.ID(class).Send(objc.RegisterName("new")) |
| 39 | - | barObject.Send(objc.RegisterName("setBar:"), 123) | 41 | + | object.Send(objc.RegisterName("setBar:"), 123) |
| 40 | - | var bar = int(barObject.Send(objc.RegisterName("bar"))) | 42 | + | var bar = int(object.Send(objc.RegisterName("bar"))) |
| 41 | fmt.Println(bar) | 43 | fmt.Println(bar) | ||
| 42 | // Output: 123 | 44 | // Output: 123 | ||
| 43 | } | 45 | } | ||