purego: new mechanism for calling C functions (#49)
This commit adds purego.RegisterLibFunc and purego.RegisterFunc which combines C functions with their Go counterpart. This is a better way to call into C because it is type safe from the caller's perspective. It will also eventually allow for float and struct arguments and returns.
- commit
- 7128aea44c774c29b29ab5e079938f36d88264c6
- author
- TotallyGamerJet · · 2022-10-19 15:39 UTC
- committer
- GitHub · · 2022-10-19 15:39 UTC
- parent
- 09060e5
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
4 files · +222 -142
M dlfcn.go +1 -21
| @@ -7,7 +7,6 @@ | |||
| 7 | 7 | package purego | |
| 8 | 8 | ||
| 9 | 9 | import ( | |
| 10 | - | "reflect" | |
| 11 | 10 | "runtime" | |
| 12 | 11 | "unsafe" | |
| 13 | 12 | ||
| @@ -52,26 +51,7 @@ func Dlerror() string { | |||
| 52 | 51 | // msg is only valid until the next call to Dlerror | |
| 53 | 52 | // which is why it gets copied into a Go string | |
| 54 | 53 | msg, _, _ := SyscallN(dlerrorABI0) | |
| 55 | - | if msg == 0 { | |
| 56 | - | return "" | |
| 57 | - | } | |
| 58 | - | var length int | |
| 59 | - | for { | |
| 60 | - | // use unsafe.Add once we reach 1.17 | |
| 61 | - | if *(*byte)(unsafe.Pointer(msg + uintptr(length))) == '\x00' { | |
| 62 | - | break | |
| 63 | - | } | |
| 64 | - | length++ | |
| 65 | - | } | |
| 66 | - | // use unsafe.Slice once we reach 1.17 | |
| 67 | - | s := make([]byte, length) | |
| 68 | - | var src []byte | |
| 69 | - | h := (*reflect.SliceHeader)(unsafe.Pointer(&src)) | |
| 70 | - | h.Data = msg | |
| 71 | - | h.Len = length | |
| 72 | - | h.Cap = length | |
| 73 | - | copy(s, src) | |
| 74 | - | return string(s) | |
| 54 | + | return strings.GoString(msg) | |
| 75 | 55 | } | |
| 76 | 56 | ||
| 77 | 57 | // Dlclose decrements the reference count on the dynamic library handle. | |
| @@ -7,7 +7,6 @@ | |||||
| 7 | package purego | 7 | package purego | ||
| 8 | 8 | ||||
| 9 | import ( | 9 | import ( | ||
| 10 | - | "reflect" | |||
| 11 | "runtime" | 10 | "runtime" | ||
| 12 | "unsafe" | 11 | "unsafe" | ||
| 13 | 12 | ||||
| @@ -52,26 +51,7 @@ func Dlerror() string { | |||||
| 52 | // msg is only valid until the next call to Dlerror | 51 | // msg is only valid until the next call to Dlerror | ||
| 53 | // which is why it gets copied into a Go string | 52 | // which is why it gets copied into a Go string | ||
| 54 | msg, _, _ := SyscallN(dlerrorABI0) | 53 | msg, _, _ := SyscallN(dlerrorABI0) | ||
| 55 | - | if msg == 0 { | 54 | + | return strings.GoString(msg) |
| 56 | - | return "" | |||
| 57 | - | } | |||
| 58 | - | var length int | |||
| 59 | - | for { | |||
| 60 | - | // use unsafe.Add once we reach 1.17 | |||
| 61 | - | if *(*byte)(unsafe.Pointer(msg + uintptr(length))) == '\x00' { | |||
| 62 | - | break | |||
| 63 | - | } | |||
| 64 | - | length++ | |||
| 65 | - | } | |||
| 66 | - | // use unsafe.Slice once we reach 1.17 | |||
| 67 | - | s := make([]byte, length) | |||
| 68 | - | var src []byte | |||
| 69 | - | h := (*reflect.SliceHeader)(unsafe.Pointer(&src)) | |||
| 70 | - | h.Data = msg | |||
| 71 | - | h.Len = length | |||
| 72 | - | h.Cap = length | |||
| 73 | - | copy(s, src) | |||
| 74 | - | return string(s) | |||
| 75 | } | 55 | } | ||
| 76 | 56 | ||||
| 77 | // Dlclose decrements the reference count on the dynamic library handle. | 57 | // Dlclose decrements the reference count on the dynamic library handle. | ||
A func_darwin.go +143 -0
| @@ -0,0 +1,143 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package purego | |
| 5 | + | ||
| 6 | + | import ( | |
| 7 | + | "reflect" | |
| 8 | + | "runtime" | |
| 9 | + | "unsafe" | |
| 10 | + | ||
| 11 | + | "github.com/ebitengine/purego/internal/strings" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | |
| 15 | + | // It panics if Dlsym fails. | |
| 16 | + | func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |
| 17 | + | sym := Dlsym(handle, name) | |
| 18 | + | if sym == 0 { | |
| 19 | + | panic("purego: couldn't find symbol: " + Dlerror()) | |
| 20 | + | } | |
| 21 | + | RegisterFunc(fptr, sym) | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // RegisterFunc takes a pointer to a Go function representing the calling convention of the C function. | |
| 25 | + | // fptr will be set to a function that when called will call the C function given by cfn with the | |
| 26 | + | // parameters passed in the correct registers and stack. | |
| 27 | + | // | |
| 28 | + | // A panic is produced if the type is not a function pointer or if the function returns more than 1 value. | |
| 29 | + | // | |
| 30 | + | // These conversions describe how a Go type in the fptr will be used to call | |
| 31 | + | // the C function. It is important to note that there is no way to verify that fptr | |
| 32 | + | // matches the C function. This also holds true for struct types where the padding | |
| 33 | + | // needs to be ensured to match that of C; RegisterFunc does not verify this. | |
| 34 | + | // | |
| 35 | + | // Type Conversions (Go => C) | |
| 36 | + | // | |
| 37 | + | // string <=> char* | |
| 38 | + | // bool <=> _Bool | |
| 39 | + | // uintptr <=> uintptr_t | |
| 40 | + | // uint <=> uint32_t or uint64_t | |
| 41 | + | // uint8 <=> uint8_t | |
| 42 | + | // uint16 <=> uint16_t | |
| 43 | + | // uint32 <=> uint32_t | |
| 44 | + | // uint64 <=> uint64_t | |
| 45 | + | // int <=> int32_t or int64_t | |
| 46 | + | // int8 <=> int8_t | |
| 47 | + | // int16 <=> int16_t | |
| 48 | + | // int32 <=> int32_t | |
| 49 | + | // int64 <=> int64_t | |
| 50 | + | // float32 <=> float (WIP) | |
| 51 | + | // float64 <=> double (WIP) | |
| 52 | + | // struct <=> struct (WIP) | |
| 53 | + | // func <=> C function | |
| 54 | + | // unsafe.Pointer, *T <=> void* | |
| 55 | + | // []T => void* | |
| 56 | + | // | |
| 57 | + | // There is a special case when the last argument of fptr is a variadic interface (or []interface} | |
| 58 | + | // it will be expanded into a call to the C function as if it had the arguments in that slice. | |
| 59 | + | // This means that using arg ...interface{} is like a cast to the function with the arguments inside arg. | |
| 60 | + | // This is not the same as C variadic. | |
| 61 | + | func RegisterFunc(fptr interface{}, cfn uintptr) { | |
| 62 | + | fn := reflect.ValueOf(fptr).Elem() | |
| 63 | + | ty := fn.Type() | |
| 64 | + | if ty.Kind() != reflect.Func { | |
| 65 | + | panic("purego: fptr must be a function pointer") | |
| 66 | + | } | |
| 67 | + | if ty.NumOut() > 1 { | |
| 68 | + | panic("purego: function can only return zero or one values") | |
| 69 | + | } | |
| 70 | + | v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { | |
| 71 | + | if len(args) > 0 { | |
| 72 | + | if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok { | |
| 73 | + | // subtract one from args bc the last argument in args is []interface{} | |
| 74 | + | // which we are currently expanding | |
| 75 | + | tmp := make([]reflect.Value, len(args)-1+len(variadic)) | |
| 76 | + | n := copy(tmp, args[:len(args)-1]) | |
| 77 | + | for i, v := range variadic { | |
| 78 | + | tmp[n+i] = reflect.ValueOf(v) | |
| 79 | + | } | |
| 80 | + | args = tmp | |
| 81 | + | } | |
| 82 | + | } | |
| 83 | + | var sysargs = make([]uintptr, len(args)) | |
| 84 | + | var keepAlive []interface{} | |
| 85 | + | defer func() { | |
| 86 | + | runtime.KeepAlive(keepAlive) | |
| 87 | + | }() | |
| 88 | + | for i, v := range args { | |
| 89 | + | switch v.Kind() { | |
| 90 | + | case reflect.String: | |
| 91 | + | ptr := strings.CString(v.String()) | |
| 92 | + | keepAlive = append(keepAlive, ptr) | |
| 93 | + | sysargs[i] = uintptr(unsafe.Pointer(ptr)) | |
| 94 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 95 | + | sysargs[i] = uintptr(v.Uint()) | |
| 96 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 97 | + | sysargs[i] = uintptr(v.Int()) | |
| 98 | + | case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: | |
| 99 | + | keepAlive = append(keepAlive, v.Pointer()) | |
| 100 | + | sysargs[i] = v.Pointer() | |
| 101 | + | case reflect.Func: | |
| 102 | + | sysargs[i] = NewCallback(v.Interface()) | |
| 103 | + | case reflect.Bool: | |
| 104 | + | if v.Bool() { | |
| 105 | + | sysargs[i] = 1 | |
| 106 | + | } else { | |
| 107 | + | sysargs[i] = 0 | |
| 108 | + | } | |
| 109 | + | default: | |
| 110 | + | panic("purego: unsupported kind: " + v.Kind().String()) | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | r1, _, _ := SyscallN(cfn, sysargs...) //TODO: handle float32/64 and struct types | |
| 114 | + | if ty.NumOut() == 0 { | |
| 115 | + | return nil | |
| 116 | + | } | |
| 117 | + | outType := ty.Out(0) | |
| 118 | + | v := reflect.New(outType).Elem() | |
| 119 | + | switch outType.Kind() { | |
| 120 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 121 | + | v.SetUint(uint64(r1)) | |
| 122 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 123 | + | v.SetInt(int64(r1)) | |
| 124 | + | case reflect.Bool: | |
| 125 | + | v.SetBool(r1 != 0) | |
| 126 | + | case reflect.UnsafePointer: | |
| 127 | + | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |
| 128 | + | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1))) | |
| 129 | + | case reflect.Ptr: | |
| 130 | + | v = reflect.NewAt(outType, unsafe.Pointer(&r1)).Elem() | |
| 131 | + | case reflect.Func: | |
| 132 | + | // wrap this C function in a nicely typed Go function | |
| 133 | + | v = reflect.New(outType) | |
| 134 | + | RegisterFunc(v.Interface(), r1) | |
| 135 | + | case reflect.String: | |
| 136 | + | v.SetString(strings.GoString(r1)) | |
| 137 | + | default: | |
| 138 | + | panic("purego: unsupported return kind: " + outType.Kind().String()) | |
| 139 | + | } | |
| 140 | + | return []reflect.Value{v} | |
| 141 | + | }) | |
| 142 | + | fn.Set(v) | |
| 143 | + | } | |
| @@ -0,0 +1,143 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package purego | |||
| 5 | + | ||||
| 6 | + | import ( | |||
| 7 | + | "reflect" | |||
| 8 | + | "runtime" | |||
| 9 | + | "unsafe" | |||
| 10 | + | ||||
| 11 | + | "github.com/ebitengine/purego/internal/strings" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 14 | + | // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name). | |||
| 15 | + | // It panics if Dlsym fails. | |||
| 16 | + | func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |||
| 17 | + | sym := Dlsym(handle, name) | |||
| 18 | + | if sym == 0 { | |||
| 19 | + | panic("purego: couldn't find symbol: " + Dlerror()) | |||
| 20 | + | } | |||
| 21 | + | RegisterFunc(fptr, sym) | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // RegisterFunc takes a pointer to a Go function representing the calling convention of the C function. | |||
| 25 | + | // fptr will be set to a function that when called will call the C function given by cfn with the | |||
| 26 | + | // parameters passed in the correct registers and stack. | |||
| 27 | + | // | |||
| 28 | + | // A panic is produced if the type is not a function pointer or if the function returns more than 1 value. | |||
| 29 | + | // | |||
| 30 | + | // These conversions describe how a Go type in the fptr will be used to call | |||
| 31 | + | // the C function. It is important to note that there is no way to verify that fptr | |||
| 32 | + | // matches the C function. This also holds true for struct types where the padding | |||
| 33 | + | // needs to be ensured to match that of C; RegisterFunc does not verify this. | |||
| 34 | + | // | |||
| 35 | + | // Type Conversions (Go => C) | |||
| 36 | + | // | |||
| 37 | + | // string <=> char* | |||
| 38 | + | // bool <=> _Bool | |||
| 39 | + | // uintptr <=> uintptr_t | |||
| 40 | + | // uint <=> uint32_t or uint64_t | |||
| 41 | + | // uint8 <=> uint8_t | |||
| 42 | + | // uint16 <=> uint16_t | |||
| 43 | + | // uint32 <=> uint32_t | |||
| 44 | + | // uint64 <=> uint64_t | |||
| 45 | + | // int <=> int32_t or int64_t | |||
| 46 | + | // int8 <=> int8_t | |||
| 47 | + | // int16 <=> int16_t | |||
| 48 | + | // int32 <=> int32_t | |||
| 49 | + | // int64 <=> int64_t | |||
| 50 | + | // float32 <=> float (WIP) | |||
| 51 | + | // float64 <=> double (WIP) | |||
| 52 | + | // struct <=> struct (WIP) | |||
| 53 | + | // func <=> C function | |||
| 54 | + | // unsafe.Pointer, *T <=> void* | |||
| 55 | + | // []T => void* | |||
| 56 | + | // | |||
| 57 | + | // There is a special case when the last argument of fptr is a variadic interface (or []interface} | |||
| 58 | + | // it will be expanded into a call to the C function as if it had the arguments in that slice. | |||
| 59 | + | // This means that using arg ...interface{} is like a cast to the function with the arguments inside arg. | |||
| 60 | + | // This is not the same as C variadic. | |||
| 61 | + | func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 62 | + | fn := reflect.ValueOf(fptr).Elem() | |||
| 63 | + | ty := fn.Type() | |||
| 64 | + | if ty.Kind() != reflect.Func { | |||
| 65 | + | panic("purego: fptr must be a function pointer") | |||
| 66 | + | } | |||
| 67 | + | if ty.NumOut() > 1 { | |||
| 68 | + | panic("purego: function can only return zero or one values") | |||
| 69 | + | } | |||
| 70 | + | v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) { | |||
| 71 | + | if len(args) > 0 { | |||
| 72 | + | if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok { | |||
| 73 | + | // subtract one from args bc the last argument in args is []interface{} | |||
| 74 | + | // which we are currently expanding | |||
| 75 | + | tmp := make([]reflect.Value, len(args)-1+len(variadic)) | |||
| 76 | + | n := copy(tmp, args[:len(args)-1]) | |||
| 77 | + | for i, v := range variadic { | |||
| 78 | + | tmp[n+i] = reflect.ValueOf(v) | |||
| 79 | + | } | |||
| 80 | + | args = tmp | |||
| 81 | + | } | |||
| 82 | + | } | |||
| 83 | + | var sysargs = make([]uintptr, len(args)) | |||
| 84 | + | var keepAlive []interface{} | |||
| 85 | + | defer func() { | |||
| 86 | + | runtime.KeepAlive(keepAlive) | |||
| 87 | + | }() | |||
| 88 | + | for i, v := range args { | |||
| 89 | + | switch v.Kind() { | |||
| 90 | + | case reflect.String: | |||
| 91 | + | ptr := strings.CString(v.String()) | |||
| 92 | + | keepAlive = append(keepAlive, ptr) | |||
| 93 | + | sysargs[i] = uintptr(unsafe.Pointer(ptr)) | |||
| 94 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |||
| 95 | + | sysargs[i] = uintptr(v.Uint()) | |||
| 96 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| 97 | + | sysargs[i] = uintptr(v.Int()) | |||
| 98 | + | case reflect.Ptr, reflect.UnsafePointer, reflect.Slice: | |||
| 99 | + | keepAlive = append(keepAlive, v.Pointer()) | |||
| 100 | + | sysargs[i] = v.Pointer() | |||
| 101 | + | case reflect.Func: | |||
| 102 | + | sysargs[i] = NewCallback(v.Interface()) | |||
| 103 | + | case reflect.Bool: | |||
| 104 | + | if v.Bool() { | |||
| 105 | + | sysargs[i] = 1 | |||
| 106 | + | } else { | |||
| 107 | + | sysargs[i] = 0 | |||
| 108 | + | } | |||
| 109 | + | default: | |||
| 110 | + | panic("purego: unsupported kind: " + v.Kind().String()) | |||
| 111 | + | } | |||
| 112 | + | } | |||
| 113 | + | r1, _, _ := SyscallN(cfn, sysargs...) //TODO: handle float32/64 and struct types | |||
| 114 | + | if ty.NumOut() == 0 { | |||
| 115 | + | return nil | |||
| 116 | + | } | |||
| 117 | + | outType := ty.Out(0) | |||
| 118 | + | v := reflect.New(outType).Elem() | |||
| 119 | + | switch outType.Kind() { | |||
| 120 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |||
| 121 | + | v.SetUint(uint64(r1)) | |||
| 122 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| 123 | + | v.SetInt(int64(r1)) | |||
| 124 | + | case reflect.Bool: | |||
| 125 | + | v.SetBool(r1 != 0) | |||
| 126 | + | case reflect.UnsafePointer: | |||
| 127 | + | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |||
| 128 | + | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1))) | |||
| 129 | + | case reflect.Ptr: | |||
| 130 | + | v = reflect.NewAt(outType, unsafe.Pointer(&r1)).Elem() | |||
| 131 | + | case reflect.Func: | |||
| 132 | + | // wrap this C function in a nicely typed Go function | |||
| 133 | + | v = reflect.New(outType) | |||
| 134 | + | RegisterFunc(v.Interface(), r1) | |||
| 135 | + | case reflect.String: | |||
| 136 | + | v.SetString(strings.GoString(r1)) | |||
| 137 | + | default: | |||
| 138 | + | panic("purego: unsupported return kind: " + outType.Kind().String()) | |||
| 139 | + | } | |||
| 140 | + | return []reflect.Value{v} | |||
| 141 | + | }) | |||
| 142 | + | fn.Set(v) | |||
| 143 | + | } | |||
M internal/strings/strings.go +30 -1
| @@ -3,7 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | 4 | package strings | |
| 5 | 5 | ||
| 6 | - | import "unsafe" | |
| 6 | + | import ( | |
| 7 | + | "reflect" | |
| 8 | + | "unsafe" | |
| 9 | + | ) | |
| 7 | 10 | ||
| 8 | 11 | // hasSuffix tests whether the string s ends with suffix. | |
| 9 | 12 | func hasSuffix(s, suffix string) bool { | |
| @@ -19,3 +22,29 @@ func CString(name string) *byte { | |||
| 19 | 22 | copy(b, name) | |
| 20 | 23 | return &b[0] | |
| 21 | 24 | } | |
| 25 | + | ||
| 26 | + | // GoString copies a char* to a Go string. | |
| 27 | + | func GoString(c uintptr) string { | |
| 28 | + | // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer | |
| 29 | + | ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c)) | |
| 30 | + | if ptr == nil { | |
| 31 | + | return "" | |
| 32 | + | } | |
| 33 | + | var length int | |
| 34 | + | for { | |
| 35 | + | // use unsafe.Add once we reach 1.17 | |
| 36 | + | if *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(length))) == '\x00' { | |
| 37 | + | break | |
| 38 | + | } | |
| 39 | + | length++ | |
| 40 | + | } | |
| 41 | + | // use unsafe.Slice once we reach 1.17 | |
| 42 | + | s := make([]byte, length) | |
| 43 | + | var src []byte | |
| 44 | + | h := (*reflect.SliceHeader)(unsafe.Pointer(&src)) | |
| 45 | + | h.Data = uintptr(ptr) | |
| 46 | + | h.Len = length | |
| 47 | + | h.Cap = length | |
| 48 | + | copy(s, src) | |
| 49 | + | return string(s) | |
| 50 | + | } | |
| @@ -3,7 +3,10 @@ | |||||
| 3 | 3 | ||||
| 4 | package strings | 4 | package strings | ||
| 5 | 5 | ||||
| 6 | - | import "unsafe" | 6 | + | import ( |
| 7 | + | "reflect" | |||
| 8 | + | "unsafe" | |||
| 9 | + | ) | |||
| 7 | 10 | ||||
| 8 | // hasSuffix tests whether the string s ends with suffix. | 11 | // hasSuffix tests whether the string s ends with suffix. | ||
| 9 | func hasSuffix(s, suffix string) bool { | 12 | func hasSuffix(s, suffix string) bool { | ||
| @@ -19,3 +22,29 @@ func CString(name string) *byte { | |||||
| 19 | copy(b, name) | 22 | copy(b, name) | ||
| 20 | return &b[0] | 23 | return &b[0] | ||
| 21 | } | 24 | } | ||
| 25 | + | ||||
| 26 | + | // GoString copies a char* to a Go string. | |||
| 27 | + | func GoString(c uintptr) string { | |||
| 28 | + | // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer | |||
| 29 | + | ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c)) | |||
| 30 | + | if ptr == nil { | |||
| 31 | + | return "" | |||
| 32 | + | } | |||
| 33 | + | var length int | |||
| 34 | + | for { | |||
| 35 | + | // use unsafe.Add once we reach 1.17 | |||
| 36 | + | if *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(length))) == '\x00' { | |||
| 37 | + | break | |||
| 38 | + | } | |||
| 39 | + | length++ | |||
| 40 | + | } | |||
| 41 | + | // use unsafe.Slice once we reach 1.17 | |||
| 42 | + | s := make([]byte, length) | |||
| 43 | + | var src []byte | |||
| 44 | + | h := (*reflect.SliceHeader)(unsafe.Pointer(&src)) | |||
| 45 | + | h.Data = uintptr(ptr) | |||
| 46 | + | h.Len = length | |||
| 47 | + | h.Cap = length | |||
| 48 | + | copy(s, src) | |||
| 49 | + | return string(s) | |||
| 50 | + | } | |||
M objc/objc_runtime_darwin.go +48 -120
| @@ -6,53 +6,61 @@ | |||
| 6 | 6 | package objc | |
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | - | "fmt" | |
| 9 | + | "github.com/ebitengine/purego" | |
| 10 | 10 | "math" | |
| 11 | 11 | "reflect" | |
| 12 | - | "runtime" | |
| 13 | - | "unsafe" | |
| 14 | - | ||
| 15 | - | "github.com/ebitengine/purego" | |
| 16 | - | "github.com/ebitengine/purego/internal/strings" | |
| 17 | 12 | ) | |
| 18 | 13 | ||
| 19 | 14 | //TODO: support try/catch? | |
| 20 | 15 | //https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime | |
| 21 | 16 | ||
| 22 | 17 | var ( | |
| 23 | - | objc = purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL) | |
| 24 | - | ||
| 25 | - | objc_msgSend = purego.Dlsym(objc, "objc_msgSend") | |
| 26 | - | objc_msgSendSuper2 = purego.Dlsym(objc, "objc_msgSendSuper2") | |
| 27 | - | objc_getClass = purego.Dlsym(objc, "objc_getClass") | |
| 28 | - | objc_getProtocol = purego.Dlsym(objc, "objc_getProtocol") | |
| 29 | - | objc_allocateClassPair = purego.Dlsym(objc, "objc_allocateClassPair") | |
| 30 | - | objc_registerClassPair = purego.Dlsym(objc, "objc_registerClassPair") | |
| 31 | - | sel_registerName = purego.Dlsym(objc, "sel_registerName") | |
| 32 | - | class_getSuperclass = purego.Dlsym(objc, "class_getSuperclass") | |
| 33 | - | class_getInstanceVariable = purego.Dlsym(objc, "class_getInstanceVariable") | |
| 34 | - | class_addMethod = purego.Dlsym(objc, "class_addMethod") | |
| 35 | - | class_addIvar = purego.Dlsym(objc, "class_addIvar") | |
| 36 | - | class_addProtocol = purego.Dlsym(objc, "class_addProtocol") | |
| 37 | - | ivar_getOffset = purego.Dlsym(objc, "ivar_getOffset") | |
| 38 | - | object_getClass = purego.Dlsym(objc, "object_getClass") | |
| 18 | + | objc_msgSend func(obj ID, cmd SEL, args ...interface{}) ID | |
| 19 | + | objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...interface{}) ID | |
| 20 | + | objc_getClass func(name string) Class | |
| 21 | + | objc_getProtocol func(name string) *Protocol | |
| 22 | + | objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class | |
| 23 | + | objc_registerClassPair func(class Class) | |
| 24 | + | sel_registerName func(name string) SEL | |
| 25 | + | class_getSuperclass func(class Class) Class | |
| 26 | + | class_getInstanceVariable func(class Class, name string) Ivar | |
| 27 | + | class_addMethod func(class Class, name SEL, imp IMP, types string) bool | |
| 28 | + | class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool | |
| 29 | + | class_addProtocol func(class Class, protocol *Protocol) bool | |
| 30 | + | ivar_getOffset func(ivar Ivar) uintptr | |
| 31 | + | object_getClass func(obj ID) Class | |
| 39 | 32 | ) | |
| 40 | 33 | ||
| 34 | + | func init() { | |
| 35 | + | objc := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL) | |
| 36 | + | ||
| 37 | + | purego.RegisterLibFunc(&objc_msgSend, objc, "objc_msgSend") | |
| 38 | + | purego.RegisterLibFunc(&objc_msgSendSuper2, objc, "objc_msgSendSuper2") | |
| 39 | + | purego.RegisterLibFunc(&object_getClass, objc, "object_getClass") | |
| 40 | + | purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass") | |
| 41 | + | purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol") | |
| 42 | + | purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair") | |
| 43 | + | purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair") | |
| 44 | + | purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName") | |
| 45 | + | purego.RegisterLibFunc(&class_getSuperclass, objc, "class_getSuperclass") | |
| 46 | + | purego.RegisterLibFunc(&class_getInstanceVariable, objc, "class_getInstanceVariable") | |
| 47 | + | purego.RegisterLibFunc(&class_addMethod, objc, "class_addMethod") | |
| 48 | + | purego.RegisterLibFunc(&class_addIvar, objc, "class_addIvar") | |
| 49 | + | purego.RegisterLibFunc(&class_addProtocol, objc, "class_addProtocol") | |
| 50 | + | purego.RegisterLibFunc(&ivar_getOffset, objc, "ivar_getOffset") | |
| 51 | + | } | |
| 52 | + | ||
| 41 | 53 | // ID is an opaque pointer to some Objective-C object | |
| 42 | 54 | type ID uintptr | |
| 43 | 55 | ||
| 44 | 56 | // Class returns the class of the object. | |
| 45 | 57 | func (id ID) Class() Class { | |
| 46 | - | ret, _, _ := purego.SyscallN(object_getClass, uintptr(id)) | |
| 47 | - | return Class(ret) | |
| 58 | + | return object_getClass(id) | |
| 48 | 59 | } | |
| 49 | 60 | ||
| 50 | 61 | // Send is a convenience method for sending messages to objects. | |
| 51 | 62 | func (id ID) Send(sel SEL, args ...interface{}) ID { | |
| 52 | - | tmp, keepAlive := createArgs(id, sel, args...) | |
| 53 | - | defer keepAlive() | |
| 54 | - | ret, _, _ := purego.SyscallN(objc_msgSend, tmp...) | |
| 55 | - | return ID(ret) | |
| 63 | + | return objc_msgSend(id, sel, args...) | |
| 56 | 64 | } | |
| 57 | 65 | ||
| 58 | 66 | // objc_super data structure is generated by the Objective-C compiler when it encounters the super keyword | |
| @@ -69,60 +77,7 @@ func (id ID) SendSuper(sel SEL, args ...interface{}) ID { | |||
| 69 | 77 | receiver: id, | |
| 70 | 78 | superClass: id.Class(), | |
| 71 | 79 | } | |
| 72 | - | tmp, keepAlive := createArgs(0, sel, args...) | |
| 73 | - | defer keepAlive() | |
| 74 | - | tmp[0] = uintptr(unsafe.Pointer(super)) // if createArgs splits the stack the pointer would be wrong | |
| 75 | - | ret, _, _ := purego.SyscallN(objc_msgSendSuper2, tmp...) | |
| 76 | - | return ID(ret) | |
| 77 | - | } | |
| 78 | - | ||
| 79 | - | func createArgs(cls ID, sel SEL, args ...interface{}) (out []uintptr, keepAlive func()) { | |
| 80 | - | out = make([]uintptr, 2, len(args)+2) | |
| 81 | - | out[0] = uintptr(cls) | |
| 82 | - | out[1] = uintptr(sel) | |
| 83 | - | var alive []interface{} | |
| 84 | - | for _, a := range args { | |
| 85 | - | switch v := a.(type) { | |
| 86 | - | case ID: | |
| 87 | - | out = append(out, uintptr(v)) | |
| 88 | - | case Class: | |
| 89 | - | out = append(out, uintptr(v)) | |
| 90 | - | case SEL: | |
| 91 | - | out = append(out, uintptr(v)) | |
| 92 | - | case IMP: | |
| 93 | - | out = append(out, uintptr(v)) | |
| 94 | - | case bool: | |
| 95 | - | if v { | |
| 96 | - | out = append(out, uintptr(1)) | |
| 97 | - | } else { | |
| 98 | - | out = append(out, uintptr(0)) | |
| 99 | - | } | |
| 100 | - | case string: | |
| 101 | - | c := strings.CString(v) | |
| 102 | - | alive = append(alive, c) | |
| 103 | - | // NOTE: that although the pointer will be kept alive | |
| 104 | - | // if there are any stack splits before this uintptr | |
| 105 | - | // is no longer needed the pointer will point to random memory. | |
| 106 | - | out = append(out, uintptr(unsafe.Pointer(c))) | |
| 107 | - | case unsafe.Pointer: | |
| 108 | - | alive = append(alive, v) | |
| 109 | - | // NOTE: that although the pointer will be kept alive | |
| 110 | - | // if there are any stack splits before this uintptr | |
| 111 | - | // is no longer needed the pointer will point to random memory. | |
| 112 | - | out = append(out, uintptr(v)) | |
| 113 | - | case uintptr: | |
| 114 | - | out = append(out, v) | |
| 115 | - | case int: | |
| 116 | - | out = append(out, uintptr(v)) | |
| 117 | - | case uint: | |
| 118 | - | out = append(out, uintptr(v)) | |
| 119 | - | default: | |
| 120 | - | panic(fmt.Sprintf("objc: unknown type %T", v)) | |
| 121 | - | } | |
| 122 | - | } | |
| 123 | - | return out, func() { | |
| 124 | - | runtime.KeepAlive(alive) | |
| 125 | - | } | |
| 80 | + | return objc_msgSendSuper2(super, sel, args...) | |
| 126 | 81 | } | |
| 127 | 82 | ||
| 128 | 83 | // SEL is an opaque type that represents a method selector | |
| @@ -131,10 +86,7 @@ type SEL uintptr | |||
| 131 | 86 | // RegisterName registers a method with the Objective-C runtime system, maps the method name to a selector, | |
| 132 | 87 | // and returns the selector value. | |
| 133 | 88 | func RegisterName(name string) SEL { | |
| 134 | - | n := strings.CString(name) | |
| 135 | - | ret, _, _ := purego.SyscallN(sel_registerName, uintptr(unsafe.Pointer(n))) | |
| 136 | - | runtime.KeepAlive(n) | |
| 137 | - | return SEL(ret) | |
| 89 | + | return sel_registerName(name) | |
| 138 | 90 | } | |
| 139 | 91 | ||
| 140 | 92 | // Class is an opaque type that represents an Objective-C class. | |
| @@ -142,25 +94,18 @@ type Class uintptr | |||
| 142 | 94 | ||
| 143 | 95 | // GetClass returns the Class object for the named class, or nil if the class is not registered with the Objective-C runtime. | |
| 144 | 96 | func GetClass(name string) Class { | |
| 145 | - | n := strings.CString(name) | |
| 146 | - | ret, _, _ := purego.SyscallN(objc_getClass, uintptr(unsafe.Pointer(n))) | |
| 147 | - | runtime.KeepAlive(n) | |
| 148 | - | return Class(ret) | |
| 97 | + | return objc_getClass(name) | |
| 149 | 98 | } | |
| 150 | 99 | ||
| 151 | 100 | // AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created | |
| 152 | 101 | func AllocateClassPair(super Class, name string, extraBytes uintptr) Class { | |
| 153 | - | n := strings.CString(name) | |
| 154 | - | ret, _, _ := purego.SyscallN(objc_allocateClassPair, uintptr(super), uintptr(unsafe.Pointer(n)), extraBytes) | |
| 155 | - | runtime.KeepAlive(n) | |
| 156 | - | return Class(ret) | |
| 102 | + | return objc_allocateClassPair(super, name, extraBytes) | |
| 157 | 103 | } | |
| 158 | 104 | ||
| 159 | 105 | // SuperClass returns the superclass of a class. | |
| 160 | 106 | // You should usually use NSObject‘s superclass method instead of this function. | |
| 161 | 107 | func (c Class) SuperClass() Class { | |
| 162 | - | ret, _, _ := purego.SyscallN(class_getSuperclass, uintptr(c)) | |
| 163 | - | return Class(ret) | |
| 108 | + | return class_getSuperclass(c) | |
| 164 | 109 | } | |
| 165 | 110 | ||
| 166 | 111 | // AddMethod adds a new method to a class with a given name and implementation. | |
| @@ -168,10 +113,7 @@ func (c Class) SuperClass() Class { | |||
| 168 | 113 | // Since the function must take at least two arguments—self and _cmd, the second and third | |
| 169 | 114 | // characters must be “@:” (the first character is the return type). | |
| 170 | 115 | func (c Class) AddMethod(name SEL, imp IMP, types string) bool { | |
| 171 | - | t := strings.CString(types) | |
| 172 | - | ret, _, _ := purego.SyscallN(class_addMethod, uintptr(c), uintptr(name), uintptr(imp), uintptr(unsafe.Pointer(t))) | |
| 173 | - | runtime.KeepAlive(t) | |
| 174 | - | return byte(ret) != 0 | |
| 116 | + | return class_addMethod(c, name, imp, types) | |
| 175 | 117 | } | |
| 176 | 118 | ||
| 177 | 119 | // AddIvar adds a new instance variable to a class. | |
| @@ -180,37 +122,28 @@ func (c Class) AddMethod(name SEL, imp IMP, types string) bool { | |||
| 180 | 122 | // The class must not be a metaclass. Adding an instance variable to a metaclass is not supported. | |
| 181 | 123 | // It takes the instance of the type of the Ivar and a string representing the type. | |
| 182 | 124 | func (c Class) AddIvar(name string, ty interface{}, types string) bool { | |
| 183 | - | n := strings.CString(name) | |
| 184 | - | t := strings.CString(types) | |
| 185 | 125 | typeOf := reflect.TypeOf(ty) | |
| 186 | 126 | size := typeOf.Size() | |
| 187 | 127 | alignment := uint8(math.Log2(float64(typeOf.Align()))) | |
| 188 | - | ret, _, _ := purego.SyscallN(class_addIvar, uintptr(c), uintptr(unsafe.Pointer(n)), size, uintptr(alignment), uintptr(unsafe.Pointer(t))) | |
| 189 | - | runtime.KeepAlive(n) | |
| 190 | - | runtime.KeepAlive(t) | |
| 191 | - | return byte(ret) != 0 | |
| 128 | + | return class_addIvar(c, name, size, alignment, types) | |
| 192 | 129 | } | |
| 193 | 130 | ||
| 194 | 131 | // AddProtocol adds a protocol to a class. | |
| 195 | 132 | // Returns true if the protocol was added successfully, otherwise false (for example, | |
| 196 | 133 | // the class already conforms to that protocol). | |
| 197 | 134 | func (c Class) AddProtocol(protocol *Protocol) bool { | |
| 198 | - | ret, _, _ := purego.SyscallN(class_addProtocol, uintptr(c), uintptr(unsafe.Pointer(protocol))) | |
| 199 | - | return byte(ret) != 0 | |
| 135 | + | return class_addProtocol(c, protocol) | |
| 200 | 136 | } | |
| 201 | 137 | ||
| 202 | 138 | // InstanceVariable returns an Ivar data structure containing information about the instance variable specified by name. | |
| 203 | 139 | func (c Class) InstanceVariable(name string) Ivar { | |
| 204 | - | n := strings.CString(name) | |
| 205 | - | ret, _, _ := purego.SyscallN(class_getInstanceVariable, uintptr(c), uintptr(unsafe.Pointer(n))) | |
| 206 | - | runtime.KeepAlive(n) | |
| 207 | - | return Ivar(ret) | |
| 140 | + | return class_getInstanceVariable(c, name) | |
| 208 | 141 | } | |
| 209 | 142 | ||
| 210 | 143 | // Register registers a class that was allocated using AllocateClassPair. | |
| 211 | 144 | // It can now be used to make objects by sending it either alloc and init or new. | |
| 212 | 145 | func (c Class) Register() { | |
| 213 | - | purego.SyscallN(objc_registerClassPair, uintptr(c)) | |
| 146 | + | objc_registerClassPair(c) | |
| 214 | 147 | } | |
| 215 | 148 | ||
| 216 | 149 | // Ivar an opaque type that represents an instance variable. | |
| @@ -221,8 +154,7 @@ type Ivar uintptr | |||
| 221 | 154 | // For instance variables of type ID or other object types, call Ivar and SetIvar instead | |
| 222 | 155 | // of using this offset to access the instance variable data directly. | |
| 223 | 156 | func (i Ivar) Offset() uintptr { | |
| 224 | - | ret, _, _ := purego.SyscallN(ivar_getOffset, uintptr(i)) | |
| 225 | - | return ret | |
| 157 | + | return ivar_getOffset(i) | |
| 226 | 158 | } | |
| 227 | 159 | ||
| 228 | 160 | // Protocol is a type that declares methods that can be implemented by any class. | |
| @@ -230,11 +162,7 @@ type Protocol uintptr | |||
| 230 | 162 | ||
| 231 | 163 | // GetProtocol returns the protocol for the given name or nil if there is no protocol by that name. | |
| 232 | 164 | func GetProtocol(name string) *Protocol { | |
| 233 | - | n := strings.CString(name) | |
| 234 | - | p, _, _ := purego.SyscallN(objc_getProtocol, uintptr(unsafe.Pointer(n))) | |
| 235 | - | runtime.KeepAlive(n) | |
| 236 | - | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |
| 237 | - | return *(**Protocol)(unsafe.Pointer(&p)) | |
| 165 | + | return objc_getProtocol(name) | |
| 238 | 166 | } | |
| 239 | 167 | ||
| 240 | 168 | // IMP is a function pointer that can be called by Objective-C code. | |
| @@ -6,53 +6,61 @@ | |||||
| 6 | package objc | 6 | package objc | ||
| 7 | 7 | ||||
| 8 | import ( | 8 | import ( | ||
| 9 | - | "fmt" | 9 | + | "github.com/ebitengine/purego" |
| 10 | "math" | 10 | "math" | ||
| 11 | "reflect" | 11 | "reflect" | ||
| 12 | - | "runtime" | |||
| 13 | - | "unsafe" | |||
| 14 | - | ||||
| 15 | - | "github.com/ebitengine/purego" | |||
| 16 | - | "github.com/ebitengine/purego/internal/strings" | |||
| 17 | ) | 12 | ) | ||
| 18 | 13 | ||||
| 19 | //TODO: support try/catch? | 14 | //TODO: support try/catch? | ||
| 20 | //https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime | 15 | //https://stackoverflow.com/questions/7062599/example-of-how-objective-cs-try-catch-implementation-is-executed-at-runtime | ||
| 21 | 16 | ||||
| 22 | var ( | 17 | var ( | ||
| 23 | - | objc = purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL) | 18 | + | objc_msgSend func(obj ID, cmd SEL, args ...interface{}) ID |
| 24 | - | 19 | + | objc_msgSendSuper2 func(super *objc_super, cmd SEL, args ...interface{}) ID | |
| 25 | - | objc_msgSend = purego.Dlsym(objc, "objc_msgSend") | 20 | + | objc_getClass func(name string) Class |
| 26 | - | objc_msgSendSuper2 = purego.Dlsym(objc, "objc_msgSendSuper2") | 21 | + | objc_getProtocol func(name string) *Protocol |
| 27 | - | objc_getClass = purego.Dlsym(objc, "objc_getClass") | 22 | + | objc_allocateClassPair func(super Class, name string, extraBytes uintptr) Class |
| 28 | - | objc_getProtocol = purego.Dlsym(objc, "objc_getProtocol") | 23 | + | objc_registerClassPair func(class Class) |
| 29 | - | objc_allocateClassPair = purego.Dlsym(objc, "objc_allocateClassPair") | 24 | + | sel_registerName func(name string) SEL |
| 30 | - | objc_registerClassPair = purego.Dlsym(objc, "objc_registerClassPair") | 25 | + | class_getSuperclass func(class Class) Class |
| 31 | - | sel_registerName = purego.Dlsym(objc, "sel_registerName") | 26 | + | class_getInstanceVariable func(class Class, name string) Ivar |
| 32 | - | class_getSuperclass = purego.Dlsym(objc, "class_getSuperclass") | 27 | + | class_addMethod func(class Class, name SEL, imp IMP, types string) bool |
| 33 | - | class_getInstanceVariable = purego.Dlsym(objc, "class_getInstanceVariable") | 28 | + | class_addIvar func(class Class, name string, size uintptr, alignment uint8, types string) bool |
| 34 | - | class_addMethod = purego.Dlsym(objc, "class_addMethod") | 29 | + | class_addProtocol func(class Class, protocol *Protocol) bool |
| 35 | - | class_addIvar = purego.Dlsym(objc, "class_addIvar") | 30 | + | ivar_getOffset func(ivar Ivar) uintptr |
| 36 | - | class_addProtocol = purego.Dlsym(objc, "class_addProtocol") | 31 | + | object_getClass func(obj ID) Class |
| 37 | - | ivar_getOffset = purego.Dlsym(objc, "ivar_getOffset") | |||
| 38 | - | object_getClass = purego.Dlsym(objc, "object_getClass") | |||
| 39 | ) | 32 | ) | ||
| 40 | 33 | ||||
| 34 | + | func init() { | |||
| 35 | + | objc := purego.Dlopen("/usr/lib/libobjc.A.dylib", purego.RTLD_GLOBAL) | |||
| 36 | + | ||||
| 37 | + | purego.RegisterLibFunc(&objc_msgSend, objc, "objc_msgSend") | |||
| 38 | + | purego.RegisterLibFunc(&objc_msgSendSuper2, objc, "objc_msgSendSuper2") | |||
| 39 | + | purego.RegisterLibFunc(&object_getClass, objc, "object_getClass") | |||
| 40 | + | purego.RegisterLibFunc(&objc_getClass, objc, "objc_getClass") | |||
| 41 | + | purego.RegisterLibFunc(&objc_getProtocol, objc, "objc_getProtocol") | |||
| 42 | + | purego.RegisterLibFunc(&objc_allocateClassPair, objc, "objc_allocateClassPair") | |||
| 43 | + | purego.RegisterLibFunc(&objc_registerClassPair, objc, "objc_registerClassPair") | |||
| 44 | + | purego.RegisterLibFunc(&sel_registerName, objc, "sel_registerName") | |||
| 45 | + | purego.RegisterLibFunc(&class_getSuperclass, objc, "class_getSuperclass") | |||
| 46 | + | purego.RegisterLibFunc(&class_getInstanceVariable, objc, "class_getInstanceVariable") | |||
| 47 | + | purego.RegisterLibFunc(&class_addMethod, objc, "class_addMethod") | |||
| 48 | + | purego.RegisterLibFunc(&class_addIvar, objc, "class_addIvar") | |||
| 49 | + | purego.RegisterLibFunc(&class_addProtocol, objc, "class_addProtocol") | |||
| 50 | + | purego.RegisterLibFunc(&ivar_getOffset, objc, "ivar_getOffset") | |||
| 51 | + | } | |||
| 52 | + | ||||
| 41 | // ID is an opaque pointer to some Objective-C object | 53 | // ID is an opaque pointer to some Objective-C object | ||
| 42 | type ID uintptr | 54 | type ID uintptr | ||
| 43 | 55 | ||||
| 44 | // Class returns the class of the object. | 56 | // Class returns the class of the object. | ||
| 45 | func (id ID) Class() Class { | 57 | func (id ID) Class() Class { | ||
| 46 | - | ret, _, _ := purego.SyscallN(object_getClass, uintptr(id)) | 58 | + | return object_getClass(id) |
| 47 | - | return Class(ret) | |||
| 48 | } | 59 | } | ||
| 49 | 60 | ||||
| 50 | // Send is a convenience method for sending messages to objects. | 61 | // Send is a convenience method for sending messages to objects. | ||
| 51 | func (id ID) Send(sel SEL, args ...interface{}) ID { | 62 | func (id ID) Send(sel SEL, args ...interface{}) ID { | ||
| 52 | - | tmp, keepAlive := createArgs(id, sel, args...) | 63 | + | return objc_msgSend(id, sel, args...) |
| 53 | - | defer keepAlive() | |||
| 54 | - | ret, _, _ := purego.SyscallN(objc_msgSend, tmp...) | |||
| 55 | - | return ID(ret) | |||
| 56 | } | 64 | } | ||
| 57 | 65 | ||||
| 58 | // objc_super data structure is generated by the Objective-C compiler when it encounters the super keyword | 66 | // objc_super data structure is generated by the Objective-C compiler when it encounters the super keyword | ||
| @@ -69,60 +77,7 @@ func (id ID) SendSuper(sel SEL, args ...interface{}) ID { | |||||
| 69 | receiver: id, | 77 | receiver: id, | ||
| 70 | superClass: id.Class(), | 78 | superClass: id.Class(), | ||
| 71 | } | 79 | } | ||
| 72 | - | tmp, keepAlive := createArgs(0, sel, args...) | 80 | + | return objc_msgSendSuper2(super, sel, args...) |
| 73 | - | defer keepAlive() | |||
| 74 | - | tmp[0] = uintptr(unsafe.Pointer(super)) // if createArgs splits the stack the pointer would be wrong | |||
| 75 | - | ret, _, _ := purego.SyscallN(objc_msgSendSuper2, tmp...) | |||
| 76 | - | return ID(ret) | |||
| 77 | - | } | |||
| 78 | - | ||||
| 79 | - | func createArgs(cls ID, sel SEL, args ...interface{}) (out []uintptr, keepAlive func()) { | |||
| 80 | - | out = make([]uintptr, 2, len(args)+2) | |||
| 81 | - | out[0] = uintptr(cls) | |||
| 82 | - | out[1] = uintptr(sel) | |||
| 83 | - | var alive []interface{} | |||
| 84 | - | for _, a := range args { | |||
| 85 | - | switch v := a.(type) { | |||
| 86 | - | case ID: | |||
| 87 | - | out = append(out, uintptr(v)) | |||
| 88 | - | case Class: | |||
| 89 | - | out = append(out, uintptr(v)) | |||
| 90 | - | case SEL: | |||
| 91 | - | out = append(out, uintptr(v)) | |||
| 92 | - | case IMP: | |||
| 93 | - | out = append(out, uintptr(v)) | |||
| 94 | - | case bool: | |||
| 95 | - | if v { | |||
| 96 | - | out = append(out, uintptr(1)) | |||
| 97 | - | } else { | |||
| 98 | - | out = append(out, uintptr(0)) | |||
| 99 | - | } | |||
| 100 | - | case string: | |||
| 101 | - | c := strings.CString(v) | |||
| 102 | - | alive = append(alive, c) | |||
| 103 | - | // NOTE: that although the pointer will be kept alive | |||
| 104 | - | // if there are any stack splits before this uintptr | |||
| 105 | - | // is no longer needed the pointer will point to random memory. | |||
| 106 | - | out = append(out, uintptr(unsafe.Pointer(c))) | |||
| 107 | - | case unsafe.Pointer: | |||
| 108 | - | alive = append(alive, v) | |||
| 109 | - | // NOTE: that although the pointer will be kept alive | |||
| 110 | - | // if there are any stack splits before this uintptr | |||
| 111 | - | // is no longer needed the pointer will point to random memory. | |||
| 112 | - | out = append(out, uintptr(v)) | |||
| 113 | - | case uintptr: | |||
| 114 | - | out = append(out, v) | |||
| 115 | - | case int: | |||
| 116 | - | out = append(out, uintptr(v)) | |||
| 117 | - | case uint: | |||
| 118 | - | out = append(out, uintptr(v)) | |||
| 119 | - | default: | |||
| 120 | - | panic(fmt.Sprintf("objc: unknown type %T", v)) | |||
| 121 | - | } | |||
| 122 | - | } | |||
| 123 | - | return out, func() { | |||
| 124 | - | runtime.KeepAlive(alive) | |||
| 125 | - | } | |||
| 126 | } | 81 | } | ||
| 127 | 82 | ||||
| 128 | // SEL is an opaque type that represents a method selector | 83 | // SEL is an opaque type that represents a method selector | ||
| @@ -131,10 +86,7 @@ type SEL uintptr | |||||
| 131 | // RegisterName registers a method with the Objective-C runtime system, maps the method name to a selector, | 86 | // RegisterName registers a method with the Objective-C runtime system, maps the method name to a selector, | ||
| 132 | // and returns the selector value. | 87 | // and returns the selector value. | ||
| 133 | func RegisterName(name string) SEL { | 88 | func RegisterName(name string) SEL { | ||
| 134 | - | n := strings.CString(name) | 89 | + | return sel_registerName(name) |
| 135 | - | ret, _, _ := purego.SyscallN(sel_registerName, uintptr(unsafe.Pointer(n))) | |||
| 136 | - | runtime.KeepAlive(n) | |||
| 137 | - | return SEL(ret) | |||
| 138 | } | 90 | } | ||
| 139 | 91 | ||||
| 140 | // Class is an opaque type that represents an Objective-C class. | 92 | // Class is an opaque type that represents an Objective-C class. | ||
| @@ -142,25 +94,18 @@ type Class uintptr | |||||
| 142 | 94 | ||||
| 143 | // GetClass returns the Class object for the named class, or nil if the class is not registered with the Objective-C runtime. | 95 | // GetClass returns the Class object for the named class, or nil if the class is not registered with the Objective-C runtime. | ||
| 144 | func GetClass(name string) Class { | 96 | func GetClass(name string) Class { | ||
| 145 | - | n := strings.CString(name) | 97 | + | return objc_getClass(name) |
| 146 | - | ret, _, _ := purego.SyscallN(objc_getClass, uintptr(unsafe.Pointer(n))) | |||
| 147 | - | runtime.KeepAlive(n) | |||
| 148 | - | return Class(ret) | |||
| 149 | } | 98 | } | ||
| 150 | 99 | ||||
| 151 | // AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created | 100 | // AllocateClassPair creates a new class and metaclass. Then returns the new class, or Nil if the class could not be created | ||
| 152 | func AllocateClassPair(super Class, name string, extraBytes uintptr) Class { | 101 | func AllocateClassPair(super Class, name string, extraBytes uintptr) Class { | ||
| 153 | - | n := strings.CString(name) | 102 | + | return objc_allocateClassPair(super, name, extraBytes) |
| 154 | - | ret, _, _ := purego.SyscallN(objc_allocateClassPair, uintptr(super), uintptr(unsafe.Pointer(n)), extraBytes) | |||
| 155 | - | runtime.KeepAlive(n) | |||
| 156 | - | return Class(ret) | |||
| 157 | } | 103 | } | ||
| 158 | 104 | ||||
| 159 | // SuperClass returns the superclass of a class. | 105 | // SuperClass returns the superclass of a class. | ||
| 160 | // You should usually use NSObject‘s superclass method instead of this function. | 106 | // You should usually use NSObject‘s superclass method instead of this function. | ||
| 161 | func (c Class) SuperClass() Class { | 107 | func (c Class) SuperClass() Class { | ||
| 162 | - | ret, _, _ := purego.SyscallN(class_getSuperclass, uintptr(c)) | 108 | + | return class_getSuperclass(c) |
| 163 | - | return Class(ret) | |||
| 164 | } | 109 | } | ||
| 165 | 110 | ||||
| 166 | // AddMethod adds a new method to a class with a given name and implementation. | 111 | // AddMethod adds a new method to a class with a given name and implementation. | ||
| @@ -168,10 +113,7 @@ func (c Class) SuperClass() Class { | |||||
| 168 | // Since the function must take at least two arguments—self and _cmd, the second and third | 113 | // Since the function must take at least two arguments—self and _cmd, the second and third | ||
| 169 | // characters must be “@:” (the first character is the return type). | 114 | // characters must be “@:” (the first character is the return type). | ||
| 170 | func (c Class) AddMethod(name SEL, imp IMP, types string) bool { | 115 | func (c Class) AddMethod(name SEL, imp IMP, types string) bool { | ||
| 171 | - | t := strings.CString(types) | 116 | + | return class_addMethod(c, name, imp, types) |
| 172 | - | ret, _, _ := purego.SyscallN(class_addMethod, uintptr(c), uintptr(name), uintptr(imp), uintptr(unsafe.Pointer(t))) | |||
| 173 | - | runtime.KeepAlive(t) | |||
| 174 | - | return byte(ret) != 0 | |||
| 175 | } | 117 | } | ||
| 176 | 118 | ||||
| 177 | // AddIvar adds a new instance variable to a class. | 119 | // AddIvar adds a new instance variable to a class. | ||
| @@ -180,37 +122,28 @@ func (c Class) AddMethod(name SEL, imp IMP, types string) bool { | |||||
| 180 | // The class must not be a metaclass. Adding an instance variable to a metaclass is not supported. | 122 | // The class must not be a metaclass. Adding an instance variable to a metaclass is not supported. | ||
| 181 | // It takes the instance of the type of the Ivar and a string representing the type. | 123 | // It takes the instance of the type of the Ivar and a string representing the type. | ||
| 182 | func (c Class) AddIvar(name string, ty interface{}, types string) bool { | 124 | func (c Class) AddIvar(name string, ty interface{}, types string) bool { | ||
| 183 | - | n := strings.CString(name) | |||
| 184 | - | t := strings.CString(types) | |||
| 185 | typeOf := reflect.TypeOf(ty) | 125 | typeOf := reflect.TypeOf(ty) | ||
| 186 | size := typeOf.Size() | 126 | size := typeOf.Size() | ||
| 187 | alignment := uint8(math.Log2(float64(typeOf.Align()))) | 127 | alignment := uint8(math.Log2(float64(typeOf.Align()))) | ||
| 188 | - | ret, _, _ := purego.SyscallN(class_addIvar, uintptr(c), uintptr(unsafe.Pointer(n)), size, uintptr(alignment), uintptr(unsafe.Pointer(t))) | 128 | + | return class_addIvar(c, name, size, alignment, types) |
| 189 | - | runtime.KeepAlive(n) | |||
| 190 | - | runtime.KeepAlive(t) | |||
| 191 | - | return byte(ret) != 0 | |||
| 192 | } | 129 | } | ||
| 193 | 130 | ||||
| 194 | // AddProtocol adds a protocol to a class. | 131 | // AddProtocol adds a protocol to a class. | ||
| 195 | // Returns true if the protocol was added successfully, otherwise false (for example, | 132 | // Returns true if the protocol was added successfully, otherwise false (for example, | ||
| 196 | // the class already conforms to that protocol). | 133 | // the class already conforms to that protocol). | ||
| 197 | func (c Class) AddProtocol(protocol *Protocol) bool { | 134 | func (c Class) AddProtocol(protocol *Protocol) bool { | ||
| 198 | - | ret, _, _ := purego.SyscallN(class_addProtocol, uintptr(c), uintptr(unsafe.Pointer(protocol))) | 135 | + | return class_addProtocol(c, protocol) |
| 199 | - | return byte(ret) != 0 | |||
| 200 | } | 136 | } | ||
| 201 | 137 | ||||
| 202 | // InstanceVariable returns an Ivar data structure containing information about the instance variable specified by name. | 138 | // InstanceVariable returns an Ivar data structure containing information about the instance variable specified by name. | ||
| 203 | func (c Class) InstanceVariable(name string) Ivar { | 139 | func (c Class) InstanceVariable(name string) Ivar { | ||
| 204 | - | n := strings.CString(name) | 140 | + | return class_getInstanceVariable(c, name) |
| 205 | - | ret, _, _ := purego.SyscallN(class_getInstanceVariable, uintptr(c), uintptr(unsafe.Pointer(n))) | |||
| 206 | - | runtime.KeepAlive(n) | |||
| 207 | - | return Ivar(ret) | |||
| 208 | } | 141 | } | ||
| 209 | 142 | ||||
| 210 | // Register registers a class that was allocated using AllocateClassPair. | 143 | // Register registers a class that was allocated using AllocateClassPair. | ||
| 211 | // It can now be used to make objects by sending it either alloc and init or new. | 144 | // It can now be used to make objects by sending it either alloc and init or new. | ||
| 212 | func (c Class) Register() { | 145 | func (c Class) Register() { | ||
| 213 | - | purego.SyscallN(objc_registerClassPair, uintptr(c)) | 146 | + | objc_registerClassPair(c) |
| 214 | } | 147 | } | ||
| 215 | 148 | ||||
| 216 | // Ivar an opaque type that represents an instance variable. | 149 | // Ivar an opaque type that represents an instance variable. | ||
| @@ -221,8 +154,7 @@ type Ivar uintptr | |||||
| 221 | // For instance variables of type ID or other object types, call Ivar and SetIvar instead | 154 | // For instance variables of type ID or other object types, call Ivar and SetIvar instead | ||
| 222 | // of using this offset to access the instance variable data directly. | 155 | // of using this offset to access the instance variable data directly. | ||
| 223 | func (i Ivar) Offset() uintptr { | 156 | func (i Ivar) Offset() uintptr { | ||
| 224 | - | ret, _, _ := purego.SyscallN(ivar_getOffset, uintptr(i)) | 157 | + | return ivar_getOffset(i) |
| 225 | - | return ret | |||
| 226 | } | 158 | } | ||
| 227 | 159 | ||||
| 228 | // Protocol is a type that declares methods that can be implemented by any class. | 160 | // Protocol is a type that declares methods that can be implemented by any class. | ||
| @@ -230,11 +162,7 @@ type Protocol uintptr | |||||
| 230 | 162 | ||||
| 231 | // GetProtocol returns the protocol for the given name or nil if there is no protocol by that name. | 163 | // GetProtocol returns the protocol for the given name or nil if there is no protocol by that name. | ||
| 232 | func GetProtocol(name string) *Protocol { | 164 | func GetProtocol(name string) *Protocol { | ||
| 233 | - | n := strings.CString(name) | 165 | + | return objc_getProtocol(name) |
| 234 | - | p, _, _ := purego.SyscallN(objc_getProtocol, uintptr(unsafe.Pointer(n))) | |||
| 235 | - | runtime.KeepAlive(n) | |||
| 236 | - | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |||
| 237 | - | return *(**Protocol)(unsafe.Pointer(&p)) | |||
| 238 | } | 166 | } | ||
| 239 | 167 | ||||
| 240 | // 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. | ||