Merge pull request #7 from bnema/fix/gerror-signal-callbacks
fix(gen): emit *glib.Error instead of uintptr for GError signal params
3 files · +56 -5
M internal/gir/types/template.go +52 -1
| @@ -47,6 +47,16 @@ type NullableStringParam struct { | |||
| 47 | 47 | Name string | |
| 48 | 48 | } | |
| 49 | 49 | ||
| 50 | + | // GErrorParam tracks a signal/callback parameter that is a GLib.Error record. | |
| 51 | + | // The Pure type stays uintptr (purego ABI), but the API type is *glib.Error | |
| 52 | + | // and the signal closure converts via unsafe.Pointer. | |
| 53 | + | type GErrorParam struct { | |
| 54 | + | // Index is the position in the Pure args slice. | |
| 55 | + | Index int | |
| 56 | + | // GoType is the qualified Go type for the cast (e.g., "glib.Error" or "Error"). | |
| 57 | + | GoType string | |
| 58 | + | } | |
| 59 | + | ||
| 50 | 60 | type funcArgsTemplate struct { | |
| 51 | 61 | // Pure are the arguments as passed directly to PureGo | |
| 52 | 62 | // The pure Call is a special case that contains the arguments for a callback call | |
| @@ -61,6 +71,10 @@ type funcArgsTemplate struct { | |||
| 61 | 71 | // NullableStrings tracks nullable string parameters that need temporary C strings | |
| 62 | 72 | NullableStrings []NullableStringParam | |
| 63 | 73 | ||
| 74 | + | // GErrors tracks GError record parameters in signal/callback contexts | |
| 75 | + | // so PuregoSignalCall can emit unsafe.Pointer casts. | |
| 76 | + | GErrors []GErrorParam | |
| 77 | + | ||
| 64 | 78 | // UsesNullableHelper indicates nullable string handling that needs core import. | |
| 65 | 79 | UsesNullableHelper bool | |
| 66 | 80 | ||
| @@ -86,6 +100,12 @@ func isStringType(t string) bool { | |||
| 86 | 100 | return strings.TrimLeft(t, "*") == "string" | |
| 87 | 101 | } | |
| 88 | 102 | ||
| 103 | + | // isGErrorType reports whether a GIR type name refers to GLib.Error. | |
| 104 | + | // Within the GLib namespace the name is just "Error"; elsewhere it is "GLib.Error". | |
| 105 | + | func isGErrorType(girName string) bool { | |
| 106 | + | return girName == "GLib.Error" || girName == "Error" | |
| 107 | + | } | |
| 108 | + | ||
| 89 | 109 | // NeedsCore reports whether this argument set requires core helpers. | |
| 90 | 110 | func (f funcArgsTemplate) NeedsCore() bool { | |
| 91 | 111 | return f.UsesNullableHelper || f.UsesGStrdup | |
| @@ -117,7 +137,8 @@ func (f funcArgsTemplate) PuregoSignalFull() []string { | |||
| 117 | 137 | } | |
| 118 | 138 | ||
| 119 | 139 | // PuregoSignalCall returns Pure.Call with string variables wrapped in | |
| 120 | - | // core.GoString() for C char* → Go string conversion in signal closures. | |
| 140 | + | // core.GoString() for C char* → Go string conversion in signal closures, | |
| 141 | + | // and GError parameters wrapped in (*glib.Error)(unsafe.Pointer(...)). | |
| 121 | 142 | func (f funcArgsTemplate) PuregoSignalCall() []string { | |
| 122 | 143 | out := make([]string, len(f.Pure.Call)) | |
| 123 | 144 | copy(out, f.Pure.Call) | |
| @@ -126,9 +147,18 @@ func (f funcArgsTemplate) PuregoSignalCall() []string { | |||
| 126 | 147 | out[i] = "core.GoString(" + f.Pure.Names[i] + ")" | |
| 127 | 148 | } | |
| 128 | 149 | } | |
| 150 | + | for _, ge := range f.GErrors { | |
| 151 | + | out[ge.Index] = "(*" + ge.GoType + ")(unsafe.Pointer(" + f.Pure.Names[ge.Index] + "))" | |
| 152 | + | } | |
| 129 | 153 | return out | |
| 130 | 154 | } | |
| 131 | 155 | ||
| 156 | + | // HasGErrors reports whether any parameter is a GError that needs | |
| 157 | + | // an unsafe.Pointer cast in the signal closure. | |
| 158 | + | func (f funcArgsTemplate) HasGErrors() bool { | |
| 159 | + | return len(f.GErrors) > 0 | |
| 160 | + | } | |
| 161 | + | ||
| 132 | 162 | func qualifyCallbackType(t string, callbackNS string, currentNS string) string { | |
| 133 | 163 | if t == "" || callbackNS == "" || strings.EqualFold(callbackNS, currentNS) { | |
| 134 | 164 | return t | |
| @@ -377,6 +407,27 @@ func (f *funcArgsTemplate) Add(p Parameter, ins string, ns string, kinds KindMap | |||
| 377 | 407 | f.AddAPI(goType, varName, kind, ns, p.Nullable, isOut, ctx, transferFull) | |
| 378 | 408 | f.AddPure(goType, varName, kind, isOut, p.Nullable, ctx, transferFull) | |
| 379 | 409 | ||
| 410 | + | // For GError record parameters in signal/callback contexts (C→Go), the type | |
| 411 | + | // is collapsed to uintptr by Type.Template(). Override the API type to | |
| 412 | + | // *glib.Error so consumers get a proper Go error, and track the Pure index | |
| 413 | + | // so PuregoSignalCall() can emit the unsafe.Pointer cast. | |
| 414 | + | if ctx == ArgsFromCToGo && !isOut && goType == "uintptr" && p.Type != nil && isGErrorType(p.Type.Name) { | |
| 415 | + | gerrorGoType := "glib.Error" | |
| 416 | + | if strings.ToLower(ns) == "glib" { | |
| 417 | + | gerrorGoType = "Error" | |
| 418 | + | } | |
| 419 | + | // Override API type from uintptr to *glib.Error | |
| 420 | + | apiIdx := len(f.API.Types) - 1 | |
| 421 | + | f.API.Types[apiIdx] = "*" + gerrorGoType | |
| 422 | + | f.API.Full[apiIdx] = varName + " *" + gerrorGoType | |
| 423 | + | // Pure stays uintptr; record the index for signal call casting | |
| 424 | + | pureIdx := len(f.Pure.Types) - 1 | |
| 425 | + | f.GErrors = append(f.GErrors, GErrorParam{ | |
| 426 | + | Index: pureIdx, | |
| 427 | + | GoType: gerrorGoType, | |
| 428 | + | }) | |
| 429 | + | } | |
| 430 | + | ||
| 380 | 431 | // Upstream purego does not support direct array parameters (reflect.Array kind). | |
| 381 | 432 | // Fixed-size C arrays like "int fds[2]" are pointer params at the ABI level. | |
| 382 | 433 | // For non-out fixed-size array params, change the pure signature to *[N]T and | |
| @@ -47,6 +47,16 @@ type NullableStringParam struct { | |||||
| 47 | Name string | 47 | Name string | ||
| 48 | } | 48 | } | ||
| 49 | 49 | ||||
| 50 | + | // GErrorParam tracks a signal/callback parameter that is a GLib.Error record. | |||
| 51 | + | // The Pure type stays uintptr (purego ABI), but the API type is *glib.Error | |||
| 52 | + | // and the signal closure converts via unsafe.Pointer. | |||
| 53 | + | type GErrorParam struct { | |||
| 54 | + | // Index is the position in the Pure args slice. | |||
| 55 | + | Index int | |||
| 56 | + | // GoType is the qualified Go type for the cast (e.g., "glib.Error" or "Error"). | |||
| 57 | + | GoType string | |||
| 58 | + | } | |||
| 59 | + | ||||
| 50 | type funcArgsTemplate struct { | 60 | type funcArgsTemplate struct { | ||
| 51 | // Pure are the arguments as passed directly to PureGo | 61 | // Pure are the arguments as passed directly to PureGo | ||
| 52 | // The pure Call is a special case that contains the arguments for a callback call | 62 | // The pure Call is a special case that contains the arguments for a callback call | ||
| @@ -61,6 +71,10 @@ type funcArgsTemplate struct { | |||||
| 61 | // NullableStrings tracks nullable string parameters that need temporary C strings | 71 | // NullableStrings tracks nullable string parameters that need temporary C strings | ||
| 62 | NullableStrings []NullableStringParam | 72 | NullableStrings []NullableStringParam | ||
| 63 | 73 | ||||
| 74 | + | // GErrors tracks GError record parameters in signal/callback contexts | |||
| 75 | + | // so PuregoSignalCall can emit unsafe.Pointer casts. | |||
| 76 | + | GErrors []GErrorParam | |||
| 77 | + | ||||
| 64 | // UsesNullableHelper indicates nullable string handling that needs core import. | 78 | // UsesNullableHelper indicates nullable string handling that needs core import. | ||
| 65 | UsesNullableHelper bool | 79 | UsesNullableHelper bool | ||
| 66 | 80 | ||||
| @@ -86,6 +100,12 @@ func isStringType(t string) bool { | |||||
| 86 | return strings.TrimLeft(t, "*") == "string" | 100 | return strings.TrimLeft(t, "*") == "string" | ||
| 87 | } | 101 | } | ||
| 88 | 102 | ||||
| 103 | + | // isGErrorType reports whether a GIR type name refers to GLib.Error. | |||
| 104 | + | // Within the GLib namespace the name is just "Error"; elsewhere it is "GLib.Error". | |||
| 105 | + | func isGErrorType(girName string) bool { | |||
| 106 | + | return girName == "GLib.Error" || girName == "Error" | |||
| 107 | + | } | |||
| 108 | + | ||||
| 89 | // NeedsCore reports whether this argument set requires core helpers. | 109 | // NeedsCore reports whether this argument set requires core helpers. | ||
| 90 | func (f funcArgsTemplate) NeedsCore() bool { | 110 | func (f funcArgsTemplate) NeedsCore() bool { | ||
| 91 | return f.UsesNullableHelper || f.UsesGStrdup | 111 | return f.UsesNullableHelper || f.UsesGStrdup | ||
| @@ -117,7 +137,8 @@ func (f funcArgsTemplate) PuregoSignalFull() []string { | |||||
| 117 | } | 137 | } | ||
| 118 | 138 | ||||
| 119 | // PuregoSignalCall returns Pure.Call with string variables wrapped in | 139 | // PuregoSignalCall returns Pure.Call with string variables wrapped in | ||
| 120 | - | // core.GoString() for C char* → Go string conversion in signal closures. | 140 | + | // core.GoString() for C char* → Go string conversion in signal closures, |
| 141 | + | // and GError parameters wrapped in (*glib.Error)(unsafe.Pointer(...)). | |||
| 121 | func (f funcArgsTemplate) PuregoSignalCall() []string { | 142 | func (f funcArgsTemplate) PuregoSignalCall() []string { | ||
| 122 | out := make([]string, len(f.Pure.Call)) | 143 | out := make([]string, len(f.Pure.Call)) | ||
| 123 | copy(out, f.Pure.Call) | 144 | copy(out, f.Pure.Call) | ||
| @@ -126,9 +147,18 @@ func (f funcArgsTemplate) PuregoSignalCall() []string { | |||||
| 126 | out[i] = "core.GoString(" + f.Pure.Names[i] + ")" | 147 | out[i] = "core.GoString(" + f.Pure.Names[i] + ")" | ||
| 127 | } | 148 | } | ||
| 128 | } | 149 | } | ||
| 150 | + | for _, ge := range f.GErrors { | |||
| 151 | + | out[ge.Index] = "(*" + ge.GoType + ")(unsafe.Pointer(" + f.Pure.Names[ge.Index] + "))" | |||
| 152 | + | } | |||
| 129 | return out | 153 | return out | ||
| 130 | } | 154 | } | ||
| 131 | 155 | ||||
| 156 | + | // HasGErrors reports whether any parameter is a GError that needs | |||
| 157 | + | // an unsafe.Pointer cast in the signal closure. | |||
| 158 | + | func (f funcArgsTemplate) HasGErrors() bool { | |||
| 159 | + | return len(f.GErrors) > 0 | |||
| 160 | + | } | |||
| 161 | + | ||||
| 132 | func qualifyCallbackType(t string, callbackNS string, currentNS string) string { | 162 | func qualifyCallbackType(t string, callbackNS string, currentNS string) string { | ||
| 133 | if t == "" || callbackNS == "" || strings.EqualFold(callbackNS, currentNS) { | 163 | if t == "" || callbackNS == "" || strings.EqualFold(callbackNS, currentNS) { | ||
| 134 | return t | 164 | return t | ||
| @@ -377,6 +407,27 @@ func (f *funcArgsTemplate) Add(p Parameter, ins string, ns string, kinds KindMap | |||||
| 377 | f.AddAPI(goType, varName, kind, ns, p.Nullable, isOut, ctx, transferFull) | 407 | f.AddAPI(goType, varName, kind, ns, p.Nullable, isOut, ctx, transferFull) | ||
| 378 | f.AddPure(goType, varName, kind, isOut, p.Nullable, ctx, transferFull) | 408 | f.AddPure(goType, varName, kind, isOut, p.Nullable, ctx, transferFull) | ||
| 379 | 409 | ||||
| 410 | + | // For GError record parameters in signal/callback contexts (C→Go), the type | |||
| 411 | + | // is collapsed to uintptr by Type.Template(). Override the API type to | |||
| 412 | + | // *glib.Error so consumers get a proper Go error, and track the Pure index | |||
| 413 | + | // so PuregoSignalCall() can emit the unsafe.Pointer cast. | |||
| 414 | + | if ctx == ArgsFromCToGo && !isOut && goType == "uintptr" && p.Type != nil && isGErrorType(p.Type.Name) { | |||
| 415 | + | gerrorGoType := "glib.Error" | |||
| 416 | + | if strings.ToLower(ns) == "glib" { | |||
| 417 | + | gerrorGoType = "Error" | |||
| 418 | + | } | |||
| 419 | + | // Override API type from uintptr to *glib.Error | |||
| 420 | + | apiIdx := len(f.API.Types) - 1 | |||
| 421 | + | f.API.Types[apiIdx] = "*" + gerrorGoType | |||
| 422 | + | f.API.Full[apiIdx] = varName + " *" + gerrorGoType | |||
| 423 | + | // Pure stays uintptr; record the index for signal call casting | |||
| 424 | + | pureIdx := len(f.Pure.Types) - 1 | |||
| 425 | + | f.GErrors = append(f.GErrors, GErrorParam{ | |||
| 426 | + | Index: pureIdx, | |||
| 427 | + | GoType: gerrorGoType, | |||
| 428 | + | }) | |||
| 429 | + | } | |||
| 430 | + | ||||
| 380 | // Upstream purego does not support direct array parameters (reflect.Array kind). | 431 | // Upstream purego does not support direct array parameters (reflect.Array kind). | ||
| 381 | // Fixed-size C arrays like "int fds[2]" are pointer params at the ABI level. | 432 | // Fixed-size C arrays like "int fds[2]" are pointer params at the ABI level. | ||
| 382 | // For non-out fixed-size array params, change the pure signature to *[N]T and | 433 | // For non-out fixed-size array params, change the pure signature to *[N]T and | ||
M v4/gio/gdbusconnection.go +2 -2
| @@ -1841,7 +1841,7 @@ func (x *DBusConnection) GetPropertyUniqueName() string { | |||
| 1841 | 1841 | // Upon receiving this signal, you should give up your reference to | |
| 1842 | 1842 | // @connection. You are guaranteed that this signal is emitted only | |
| 1843 | 1843 | // once. | |
| 1844 | - | func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, uintptr)) uint { | |
| 1844 | + | func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, *glib.Error)) uint { | |
| 1845 | 1845 | cbPtr := uintptr(unsafe.Pointer(cb)) | |
| 1846 | 1846 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |
| 1847 | 1847 | handlerID := gobject.SignalConnect(x.GoPointer(), "closed", cbRefPtr) | |
| @@ -1854,7 +1854,7 @@ func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, uintptr)) | |||
| 1854 | 1854 | fa.Ptr = clsPtr | |
| 1855 | 1855 | cbFn := *cb | |
| 1856 | 1856 | ||
| 1857 | - | cbFn(fa, RemotePeerVanishedVarp, ErrorVarp) | |
| 1857 | + | cbFn(fa, RemotePeerVanishedVarp, (*glib.Error)(unsafe.Pointer(ErrorVarp))) | |
| 1858 | 1858 | ||
| 1859 | 1859 | } | |
| 1860 | 1860 | cbRefPtr := purego.NewCallback(fcb) | |
| @@ -1841,7 +1841,7 @@ func (x *DBusConnection) GetPropertyUniqueName() string { | |||||
| 1841 | // Upon receiving this signal, you should give up your reference to | 1841 | // Upon receiving this signal, you should give up your reference to | ||
| 1842 | // @connection. You are guaranteed that this signal is emitted only | 1842 | // @connection. You are guaranteed that this signal is emitted only | ||
| 1843 | // once. | 1843 | // once. | ||
| 1844 | - | func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, uintptr)) uint { | 1844 | + | func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, *glib.Error)) uint { |
| 1845 | cbPtr := uintptr(unsafe.Pointer(cb)) | 1845 | cbPtr := uintptr(unsafe.Pointer(cb)) | ||
| 1846 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | 1846 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | ||
| 1847 | handlerID := gobject.SignalConnect(x.GoPointer(), "closed", cbRefPtr) | 1847 | handlerID := gobject.SignalConnect(x.GoPointer(), "closed", cbRefPtr) | ||
| @@ -1854,7 +1854,7 @@ func (x *DBusConnection) ConnectClosed(cb *func(DBusConnection, bool, uintptr)) | |||||
| 1854 | fa.Ptr = clsPtr | 1854 | fa.Ptr = clsPtr | ||
| 1855 | cbFn := *cb | 1855 | cbFn := *cb | ||
| 1856 | 1856 | ||||
| 1857 | - | cbFn(fa, RemotePeerVanishedVarp, ErrorVarp) | 1857 | + | cbFn(fa, RemotePeerVanishedVarp, (*glib.Error)(unsafe.Pointer(ErrorVarp))) |
| 1858 | 1858 | ||||
| 1859 | } | 1859 | } | ||
| 1860 | cbRefPtr := purego.NewCallback(fcb) | 1860 | cbRefPtr := purego.NewCallback(fcb) | ||
M v4/gtk/gtkcssprovider.go +2 -2
| @@ -218,7 +218,7 @@ func (c *CssProvider) SetGoPointer(ptr uintptr) { | |||
| 218 | 218 | // Note that this signal may be emitted at any time as the css provider | |
| 219 | 219 | // may opt to defer parsing parts or all of the input to a later time | |
| 220 | 220 | // than when a loading function was called. | |
| 221 | - | func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, uintptr)) uint { | |
| 221 | + | func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, *glib.Error)) uint { | |
| 222 | 222 | cbPtr := uintptr(unsafe.Pointer(cb)) | |
| 223 | 223 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | |
| 224 | 224 | handlerID := gobject.SignalConnect(x.GoPointer(), "parsing-error", cbRefPtr) | |
| @@ -231,7 +231,7 @@ func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, uintptr | |||
| 231 | 231 | fa.Ptr = clsPtr | |
| 232 | 232 | cbFn := *cb | |
| 233 | 233 | ||
| 234 | - | cbFn(fa, SectionVarp, ErrorVarp) | |
| 234 | + | cbFn(fa, SectionVarp, (*glib.Error)(unsafe.Pointer(ErrorVarp))) | |
| 235 | 235 | ||
| 236 | 236 | } | |
| 237 | 237 | cbRefPtr := purego.NewCallback(fcb) | |
| @@ -218,7 +218,7 @@ func (c *CssProvider) SetGoPointer(ptr uintptr) { | |||||
| 218 | // Note that this signal may be emitted at any time as the css provider | 218 | // Note that this signal may be emitted at any time as the css provider | ||
| 219 | // may opt to defer parsing parts or all of the input to a later time | 219 | // may opt to defer parsing parts or all of the input to a later time | ||
| 220 | // than when a loading function was called. | 220 | // than when a loading function was called. | ||
| 221 | - | func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, uintptr)) uint { | 221 | + | func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, *glib.Error)) uint { |
| 222 | cbPtr := uintptr(unsafe.Pointer(cb)) | 222 | cbPtr := uintptr(unsafe.Pointer(cb)) | ||
| 223 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | 223 | if cbRefPtr, ok := glib.GetCallback(cbPtr); ok { | ||
| 224 | handlerID := gobject.SignalConnect(x.GoPointer(), "parsing-error", cbRefPtr) | 224 | handlerID := gobject.SignalConnect(x.GoPointer(), "parsing-error", cbRefPtr) | ||
| @@ -231,7 +231,7 @@ func (x *CssProvider) ConnectParsingError(cb *func(CssProvider, uintptr, uintptr | |||||
| 231 | fa.Ptr = clsPtr | 231 | fa.Ptr = clsPtr | ||
| 232 | cbFn := *cb | 232 | cbFn := *cb | ||
| 233 | 233 | ||||
| 234 | - | cbFn(fa, SectionVarp, ErrorVarp) | 234 | + | cbFn(fa, SectionVarp, (*glib.Error)(unsafe.Pointer(ErrorVarp))) |
| 235 | 235 | ||||
| 236 | } | 236 | } | ||
| 237 | cbRefPtr := purego.NewCallback(fcb) | 237 | cbRefPtr := purego.NewCallback(fcb) | ||