From 5e1b28caa8d81dee0e9ac7b29fe5621259e15e50 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Wed, 11 Mar 2020 00:00:22 +0100 Subject: Correct macOS >= 10.13, Go >= 1.10 CoreFoundation pointer type errors When building the project on macOS 10.15 with Go 1.13, I ended up with the following errors: # github.com/keybase/go-notifier ./corefoundation.go:25:9: cannot convert nil to type _Ctype_CFTypeRef ./corefoundation.go:34:3: cannot use nil as type _Ctype_CFDataRef in return argument ./corefoundation.go:40:26: cannot use nil as type _Ctype_CFAllocatorRef in argument to _Cfunc_CFDataCreate ./corefoundation.go:41:12: cannot convert nil to type _Ctype_CFDataRef ./corefoundation.go:42:3: cannot use nil as type _Ctype_CFDataRef in return argument ./corefoundation.go:66:47: cannot use nil as type _Ctype_CFAllocatorRef in assignment ./corefoundation.go:67:12: cannot convert nil to type _Ctype_CFDictionaryRef ./corefoundation.go:68:3: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:79:59: cannot convert &(*_cgoIndex1)[0] (type *_Ctype_CFTypeRef) to type *unsafe.Pointer ./corefoundation.go:79:88: cannot convert &(*_cgoIndex2)[0] (type *_Ctype_CFTypeRef) to type *unsafe.Pointer ./corefoundation.go:92:3: cannot use nil as type _Ctype_CFStringRef in return argument ./corefoundation.go:95:3: cannot use nil as type _Ctype_CFStringRef in return argument ./corefoundation.go:103:34: cannot use nil as type _Ctype_CFAllocatorRef in argument to _Cfunc_CFStringCreateWithBytes ./corefoundation.go:138:39: cannot use nil as type _Ctype_CFAllocatorRef in assignment ./corefoundation.go:146:69: cannot convert &(*_cgoIndex2)[0] (type *_Ctype_CFTypeRef) to type *unsafe.Pointer ./corefoundation.go:157:4: cannot use nil as type _Ctype_CFArrayRef in return argument ./corefoundation.go:178:4: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:190:5: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:197:5: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:204:5: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:211:4: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./corefoundation.go:218:3: cannot use nil as type _Ctype_CFDictionaryRef in return argument ./notifier_darwin.go:57:38: cannot use nil as type _Ctype_CFStringRef in assignment ./notifier_darwin.go:58:3: cannot use nil as type _Ctype_CFStringRef in assignment Found similar errors reported on the 'fsevents' project: https://github.com/fsnotify/fsevents/issues/33 , https://github.com/golang/go/issues/23317 It turns out that Cgo handles CoreFoundation types differently starting in Go 1.10: > Cgo now translates some C types that would normally map to a pointer > type in Go, to a uintptr instead. These types include the CFTypeRef > hierarchy in Darwin's CoreFoundation framework and the jobject > hierarchy in Java's JNI interface. > > These types must be uintptr on the Go side because they would > otherwise confuse the Go garbage collector; they are sometimes not > really pointers but data structures encoded in a pointer-sized > integer. Pointers to Go memory must not be stored in these uintptr > values. > > Because of this change, values of the affected types need to be > zero-initialized with the constant 0 instead of the constant nil. https://tip.golang.org/doc/go1.10#cgo For the "cannot convert" errors, use xlab's (https://github.com/xlab) suggestion: > The workaround is to pass it as ptr := > (*unsafe.Pointer)(unsafe.Pointer(arg)) which looks strange but works. https://github.com/golang/go/issues/13830#issuecomment-169139332 --- corefoundation.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'corefoundation.go') diff --git a/corefoundation.go b/corefoundation.go index ebec7a4..9f0bbff 100644 --- a/corefoundation.go +++ b/corefoundation.go @@ -22,7 +22,7 @@ import ( // Release releases a TypeRef func Release(ref C.CFTypeRef) { - if ref != nil { + if ref != 0 { C.CFRelease(ref) } } @@ -31,15 +31,15 @@ func Release(ref C.CFTypeRef) { // Release(ref). func BytesToCFData(b []byte) (C.CFDataRef, error) { if uint64(len(b)) > math.MaxUint32 { - return nil, fmt.Errorf("Data is too large") + return 0, fmt.Errorf("Data is too large") } var p *C.UInt8 if len(b) > 0 { p = (*C.UInt8)(&b[0]) } - cfData := C.CFDataCreate(nil, p, C.CFIndex(len(b))) - if cfData == nil { - return nil, fmt.Errorf("CFDataCreate failed") + cfData := C.CFDataCreate(0, p, C.CFIndex(len(b))) + if cfData == 0 { + return 0, fmt.Errorf("CFDataCreate failed") } return cfData, nil } @@ -63,9 +63,9 @@ func MapToCFDictionary(m map[C.CFTypeRef]C.CFTypeRef) (C.CFDictionaryRef, error) keysPointer = &keys[0] valuesPointer = &values[0] } - cfDict := C.CFDictionaryCreate(nil, keysPointer, valuesPointer, C.CFIndex(numValues), &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) - if cfDict == nil { - return nil, fmt.Errorf("CFDictionaryCreate failed") + cfDict := C.CFDictionaryCreate(0, keysPointer, valuesPointer, C.CFIndex(numValues), &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) + if cfDict == 0 { + return 0, fmt.Errorf("CFDictionaryCreate failed") } return cfDict, nil } @@ -76,7 +76,7 @@ func CFDictionaryToMap(cfDict C.CFDictionaryRef) (m map[C.CFTypeRef]C.CFTypeRef) if count > 0 { keys := make([]C.CFTypeRef, count) values := make([]C.CFTypeRef, count) - C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(&keys[0]), (*unsafe.Pointer)(&values[0])) + C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(unsafe.Pointer(keys[0])), (*unsafe.Pointer)(unsafe.Pointer(values[0]))) m = make(map[C.CFTypeRef]C.CFTypeRef, count) for i := C.CFIndex(0); i < count; i++ { m[keys[i]] = values[i] @@ -89,10 +89,10 @@ func CFDictionaryToMap(cfDict C.CFDictionaryRef) (m map[C.CFTypeRef]C.CFTypeRef) // Release(ref). func StringToCFString(s string) (C.CFStringRef, error) { if !utf8.ValidString(s) { - return nil, errors.New("Invalid UTF-8 string") + return 0, errors.New("Invalid UTF-8 string") } if uint64(len(s)) > math.MaxUint32 { - return nil, errors.New("String is too large") + return 0, errors.New("String is too large") } bytes := []byte(s) @@ -100,7 +100,7 @@ func StringToCFString(s string) (C.CFStringRef, error) { if len(bytes) > 0 { p = (*C.UInt8)(&bytes[0]) } - return C.CFStringCreateWithBytes(nil, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil + return C.CFStringCreateWithBytes(0, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil } // CFStringToString converts a CFStringRef to a string. @@ -135,7 +135,7 @@ func ArrayToCFArray(a []C.CFTypeRef) C.CFArrayRef { if numValues > 0 { valuesPointer = &values[0] } - return C.CFArrayCreate(nil, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) + return C.CFArrayCreate(0, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) } // CFArrayToArray converts a CFArrayRef to an array of CFTypes. @@ -143,7 +143,7 @@ func CFArrayToArray(cfArray C.CFArrayRef) (a []C.CFTypeRef) { count := C.CFArrayGetCount(cfArray) if count > 0 { a = make([]C.CFTypeRef, count) - C.CFArrayGetValues(cfArray, C.CFRange{0, count}, (*unsafe.Pointer)(&a[0])) + C.CFArrayGetValues(cfArray, C.CFRange{0, count}, (*unsafe.Pointer)(unsafe.Pointer(a[0]))) } return } @@ -154,7 +154,7 @@ func StringsToCFArray(strs []string) C.CFArrayRef { for _, s := range strs { strRef, err := StringToCFString(s) if err != nil { - return nil + return 0 } defer Release(C.CFTypeRef(strRef)) strRefs = append(strRefs, C.CFTypeRef(strRef)) @@ -175,7 +175,7 @@ func ConvertMapToCFDictionary(attr map[string]interface{}) (C.CFDictionaryRef, e var valueRef C.CFTypeRef switch i.(type) { default: - return nil, fmt.Errorf("Unsupported value type: %v", reflect.TypeOf(i)) + return 0, fmt.Errorf("Unsupported value type: %v", reflect.TypeOf(i)) case C.CFTypeRef: valueRef = i.(C.CFTypeRef) case bool: @@ -187,35 +187,35 @@ func ConvertMapToCFDictionary(attr map[string]interface{}) (C.CFDictionaryRef, e case []byte: bytesRef, err := BytesToCFData(i.([]byte)) if err != nil { - return nil, err + return 0, err } valueRef = C.CFTypeRef(bytesRef) defer Release(valueRef) case string: stringRef, err := StringToCFString(i.(string)) if err != nil { - return nil, err + return 0, err } valueRef = C.CFTypeRef(stringRef) defer Release(valueRef) case Convertable: convertedRef, err := (i.(Convertable)).Convert() if err != nil { - return nil, err + return 0, err } valueRef = C.CFTypeRef(convertedRef) defer Release(valueRef) } keyRef, err := StringToCFString(key) if err != nil { - return nil, err + return 0, err } m[C.CFTypeRef(keyRef)] = valueRef } cfDict, err := MapToCFDictionary(m) if err != nil { - return nil, err + return 0, err } return cfDict, nil } -- cgit v1.2.3