detached fbeb60f

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 Unverified4AEE18F83AFDEB23 The key is unknown
2 files · +20 -12
M objc/objc_runtime_darwin.go +9 -3
@@ -168,8 +168,10 @@ func GetProtocol(name string) *Protocol {
168168 // IMP is a function pointer that can be called by Objective-C code.
169169 type IMP uintptr
170170
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.
173175 func NewIMP(fn interface{}) IMP {
174176 ty := reflect.TypeOf(fn)
175177 if ty.Kind() != reflect.Func {
@@ -180,7 +182,11 @@ func NewIMP(fn interface{}) IMP {
180182 switch {
181183 case ty.NumIn() < 2:
182184 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))):
184190 fallthrough
185191 case ty.In(1).Kind() != reflect.Uintptr:
186192 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 uintptr169 type IMP uintptr
170 170
171-// NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. It returns an IMP function171+// 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 fallthrough184 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 fallthrough190 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
55
66 import (
77 "fmt"
8- "unsafe"
98
109 "github.com/ebitengine/purego"
1110 "github.com/ebitengine/purego/objc"
@@ -24,20 +23,23 @@ func ExampleAllocateClassPair() {
2423 }
2524
2625 func ExampleClass_AddIvar() {
26+ type barObject struct {
27+ isa objc.Class
28+ bar int
29+ }
2730 var class = objc.AllocateClassPair(objc.GetClass("NSObject"), "BarObject", 0)
2831 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
3234 }), "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
3537 }), "v@:q")
3638 class.Register()
3739
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")))
4143 fmt.Println(bar)
4244 // Output: 123
4345 }
@@ -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)) = bar36+ 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: 12344 // Output: 123
43 }45 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close