detached 15b4fd9

Gir: Do a better job at converting array types

Needs dep updates for arrays in Purego
commit
15b4fd9ec240e10643205cb4abfa63f89b6332d4
author
Jeroen Wijenbergh · · 2024-12-10 14:38 UTC
committer
Jeroen Wijenbergh · · 2025-04-07 12:23 UTC
parent
6bd409b
signature
unsigned
7 files · +119 -28
M go.mod +2 -2
@@ -2,6 +2,6 @@ module github.com/jwijenbergh/puregotk
22
33 go 1.20
44
5-require github.com/jwijenbergh/purego v0.0.0-20240527152122-d7876464a49c
5+require github.com/jwijenbergh/purego v0.0.0-20241210143217-aeaa0bfe09e0
66
7-require golang.org/x/sys v0.20.0 // indirect
7+require golang.org/x/sys v0.28.0 // indirect
@@ -2,6 +2,6 @@ module github.com/jwijenbergh/puregotk
2 2
3 go 1.203 go 1.20
4 4
5-require github.com/jwijenbergh/purego v0.0.0-20240527152122-d7876464a49c5+require github.com/jwijenbergh/purego v0.0.0-20241210143217-aeaa0bfe09e0
6 6
7-require golang.org/x/sys v0.20.0 // indirect7+require golang.org/x/sys v0.28.0 // indirect
M go.sum +4 -0 generated file: collapsed
M internal/gir/pass/pass.go +1 -1
@@ -204,7 +204,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
204204 for _, alias := range ns.Aliases {
205205 fn := alias.FilenameSafe()
206206 files = append(files, fn)
207- typeName := alias.Type.Template(ns.Name, p.Types)
207+ typeName := alias.Type.Template(ns.Name, p.Types, false)
208208 if typeName == "" {
209209 typeName = "uintptr"
210210 }
@@ -204,7 +204,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
204 for _, alias := range ns.Aliases {204 for _, alias := range ns.Aliases {
205 fn := alias.FilenameSafe()205 fn := alias.FilenameSafe()
206 files = append(files, fn)206 files = append(files, fn)
207- typeName := alias.Type.Template(ns.Name, p.Types)207+ typeName := alias.Type.Template(ns.Name, p.Types, false)
208 if typeName == "" {208 if typeName == "" {
209 typeName = "uintptr"209 typeName = "uintptr"
210 }210 }
M internal/gir/types/kind.go +9 -1
@@ -1,6 +1,10 @@
11 package types
22
3-import "github.com/jwijenbergh/puregotk/internal/gir/util"
3+import (
4+ "strings"
5+
6+ "github.com/jwijenbergh/puregotk/internal/gir/util"
7+)
48
59 type Kind int8
610
@@ -11,6 +15,7 @@ const (
1115 ClassesType
1216 InterfacesType
1317 RecordsType
18+ SliceType
1419 OtherType
1520 )
1621
@@ -34,6 +39,9 @@ func (km KindMap) pair(ns string, name string) KindPair {
3439 }
3540
3641 func (km KindMap) Kind(ns string, name string) Kind {
42+ if strings.HasPrefix(name, "[") {
43+ return SliceType
44+ }
3745 return km.pair(ns, name).K
3846 }
3947
@@ -1,6 +1,10 @@
1 package types1 package types
2 2
3-import "github.com/jwijenbergh/puregotk/internal/gir/util"3+import (
4+ "strings"
5+
6+ "github.com/jwijenbergh/puregotk/internal/gir/util"
7+)
4 8
5 type Kind int89 type Kind int8
6 10
@@ -11,6 +15,7 @@ const (
11 ClassesType15 ClassesType
12 InterfacesType16 InterfacesType
13 RecordsType17 RecordsType
18+ SliceType
14 OtherType19 OtherType
15 )20 )
16 21
@@ -34,6 +39,9 @@ func (km KindMap) pair(ns string, name string) KindPair {
34 }39 }
35 40
36 func (km KindMap) Kind(ns string, name string) Kind {41 func (km KindMap) Kind(ns string, name string) Kind {
42+ if strings.HasPrefix(name, "[") {
43+ return SliceType
44+ }
37 return km.pair(ns, name).K45 return km.pair(ns, name).K
38 }46 }
39 47
M internal/gir/types/template.go +4 -4
@@ -119,15 +119,15 @@ func (f *funcArgsTemplate) Add(p Parameter, ins string, ns string, kinds KindMap
119119 }
120120 goType := p.Translate(lns, kinds)
121121 kind := kinds.Kind(lns, goType)
122+
122123 stars := strings.Count(goType, "*")
123124 goType = util.NormalizeNamespace(ns, goType, true)
125+
124126 if kind != OtherType && kind != UnknownType {
125- if ins != "" && strings.Count(goType, ".") < 1 {
126- goType = ins + "." + goType
127- }
127+ goType = util.AddNamespace(goType, ins)
128128 }
129129 if stars > 0 {
130- goType = strings.Repeat("*", stars) + strings.ReplaceAll(goType, "*", "")
130+ goType = util.StarsInFront(strings.ReplaceAll(goType, "*", ""), stars)
131131 }
132132
133133 // Get a suitable variable name
@@ -119,15 +119,15 @@ func (f *funcArgsTemplate) Add(p Parameter, ins string, ns string, kinds KindMap
119 }119 }
120 goType := p.Translate(lns, kinds)120 goType := p.Translate(lns, kinds)
121 kind := kinds.Kind(lns, goType)121 kind := kinds.Kind(lns, goType)
122+
122 stars := strings.Count(goType, "*")123 stars := strings.Count(goType, "*")
123 goType = util.NormalizeNamespace(ns, goType, true)124 goType = util.NormalizeNamespace(ns, goType, true)
125+
124 if kind != OtherType && kind != UnknownType {126 if kind != OtherType && kind != UnknownType {
125- if ins != "" && strings.Count(goType, ".") < 1 {127+ goType = util.AddNamespace(goType, ins)
126- goType = ins + "." + goType
127- }
128 }128 }
129 if stars > 0 {129 if stars > 0 {
130- goType = strings.Repeat("*", stars) + strings.ReplaceAll(goType, "*", "")130+ goType = util.StarsInFront(strings.ReplaceAll(goType, "*", ""), stars)
131 }131 }
132 132
133 // Get a suitable variable name133 // Get a suitable variable name
M internal/gir/types/types.go +68 -20
@@ -44,15 +44,16 @@ var convList = map[string]string{
4444 "GType": "types.GType",
4545
4646 // these are probably not correct but needed to compile
47- "long double": "float64",
48- "va_list": "[]interface{}",
49- "gpointer": "uintptr",
50- "GType": "[]interface{}",
51- "const char*": "string",
52- "const char**": "[]string",
53- "char**": "[]string",
47+ "_Value__data__union": "uint64",
48+ "long double": "float64",
49+ "va_list": "[]interface{}",
50+ "gpointer": "uintptr",
51+ "const char*": "string",
52+ "const char**": "[]string",
53+ "char**": "[]string",
5454 // not exported
55- "HarfBuzz.font_t": "uintptr",
55+ "HarfBuzz.feature_t": "uintptr",
56+ "HarfBuzz.font_t": "uintptr",
5657 //"GLib.Quark": "byte",
5758 //"Allocation": "uintptr",
5859 }
@@ -99,14 +100,12 @@ func (a *AnyType) Translate(ns string, kinds KindMap) string {
99100 t := a.Type
100101 if t == nil {
101102 if a.Array == nil {
103+ // no type information available
102104 return ""
103105 }
104- if v, ok := convList[a.Array.CType]; ok {
105- return v
106- }
107- return "uintptr"
106+ return a.Array.Template(ns, kinds)
108107 }
109- return a.Type.Template(ns, kinds)
108+ return a.Type.Template(ns, kinds, false)
110109 }
111110
112111 type Annotation struct {
@@ -127,6 +126,48 @@ type Array struct {
127126 Type *Type `xml:"http://www.gtk.org/introspection/core/1.0 type"`
128127 }
129128
129+func (a *Array) Size() int {
130+ // there is a Length property
131+ // but it seems that GTK doesn't really use this
132+ // see e.g. gtk_css_provider_load_from_data
133+ //if a.Length != nil {
134+ // return *a.Length
135+ //}
136+ return a.FixedSize
137+}
138+
139+func ReplaceSize(str string, n int) string {
140+ trimmed := strings.TrimPrefix(str, "*")
141+ if trimmed == str {
142+ trimmed = strings.TrimPrefix(str, "[]")
143+ }
144+ return fmt.Sprintf("[%d]%s", n, trimmed)
145+}
146+
147+func (a *Array) Template(ns string, kinds KindMap) string {
148+ // Does the c type exist in our little hacky conversion rule?
149+ if v, ok := convList[a.CType]; ok {
150+ return v
151+ }
152+ // default case of an array is just a pointer
153+ ret := "uintptr"
154+
155+ if a.Type != nil {
156+ t := a.Type.Template(ns, kinds, true)
157+ if strings.Count(t, "*") > 0 {
158+ return "uintptr"
159+ }
160+ ret = "[]" + t
161+ }
162+
163+ // Check if there is a fixed size
164+ n := a.Size()
165+ if n > 0 {
166+ return ReplaceSize(ret, n)
167+ }
168+ return ret
169+}
170+
130171 // IsZeroTerminated returns true if the Array is zero-terminated. It accounts
131172 // for edge cases of the structure.
132173 func (a Array) IsZeroTerminated() bool {
@@ -217,7 +258,7 @@ func (typ *Type) IsIntrospectable() bool {
217258 return typ.Introspectable == nil || *typ.Introspectable
218259 }
219260
220-func (t *Type) Template(ns string, kinds KindMap) string {
261+func (t *Type) Template(ns string, kinds KindMap, array bool) string {
221262 if v, ok := convList[t.Name]; ok {
222263 return v
223264 }
@@ -235,6 +276,9 @@ func (t *Type) Template(ns string, kinds KindMap) string {
235276 if count > 0 && kind != CallbackType {
236277 _type = strings.Repeat("*", count) + _type
237278 } else if kind == RecordsType {
279+ if array {
280+ return _type
281+ }
238282 // if it's not a pointer
239283 // then purego doesn't support it (struct by value is not supported
240284 // so return uintptr
@@ -336,7 +380,7 @@ type Constant struct {
336380 }
337381
338382 func (c *Constant) Template(ns string, kinds KindMap) ConstantTemplate {
339- t := c.Type.Template(ns, kinds)
383+ t := c.Type.Template(ns, kinds, false)
340384 v := c.Value
341385
342386 switch t {
@@ -692,18 +736,22 @@ func (r *ReturnValue) Template(ns string, ins string, kinds KindMap, throws bool
692736 }
693737 kind := kinds.Kind(lns, raw)
694738 stars := strings.Count(val, "*")
695- if kind != OtherType && kind != UnknownType {
696- if ins != "" && strings.Count(val, ".") < 1 {
697- val = ins + "." + val
698- }
739+
740+ if kind != OtherType && kind != UnknownType && kind != SliceType {
741+ val = util.AddNamespace(val, ins)
699742 }
700743 if stars > 0 {
701- val = strings.Repeat("*", stars) + strings.ReplaceAll(val, "*", "")
744+ val = util.StarsInFront(strings.ReplaceAll(val, "*", ""), stars)
702745 }
703746 switch kind {
704747 case ClassesType:
705748 raw = "uintptr"
706749 class = true
750+ // TODO: why does this happen?
751+ if strings.HasPrefix(val, "[]") {
752+ class = false
753+ val = "uintptr"
754+ }
707755 case InterfacesType:
708756 raw = "uintptr"
709757 val += "Base"
@@ -44,15 +44,16 @@ var convList = map[string]string{
44 "GType": "types.GType",44 "GType": "types.GType",
45 45
46 // these are probably not correct but needed to compile46 // these are probably not correct but needed to compile
47- "long double": "float64",47+ "_Value__data__union": "uint64",
48- "va_list": "[]interface{}",48+ "long double": "float64",
49- "gpointer": "uintptr",49+ "va_list": "[]interface{}",
50- "GType": "[]interface{}",50+ "gpointer": "uintptr",
51- "const char*": "string",51+ "const char*": "string",
52- "const char**": "[]string",52+ "const char**": "[]string",
53- "char**": "[]string",53+ "char**": "[]string",
54 // not exported54 // not exported
55- "HarfBuzz.font_t": "uintptr",55+ "HarfBuzz.feature_t": "uintptr",
56+ "HarfBuzz.font_t": "uintptr",
56 //"GLib.Quark": "byte",57 //"GLib.Quark": "byte",
57 //"Allocation": "uintptr",58 //"Allocation": "uintptr",
58 }59 }
@@ -99,14 +100,12 @@ func (a *AnyType) Translate(ns string, kinds KindMap) string {
99 t := a.Type100 t := a.Type
100 if t == nil {101 if t == nil {
101 if a.Array == nil {102 if a.Array == nil {
103+ // no type information available
102 return ""104 return ""
103 }105 }
104- if v, ok := convList[a.Array.CType]; ok {106+ return a.Array.Template(ns, kinds)
105- return v
106- }
107- return "uintptr"
108 }107 }
109- return a.Type.Template(ns, kinds)108+ return a.Type.Template(ns, kinds, false)
110 }109 }
111 110
112 type Annotation struct {111 type Annotation struct {
@@ -127,6 +126,48 @@ type Array struct {
127 Type *Type `xml:"http://www.gtk.org/introspection/core/1.0 type"`126 Type *Type `xml:"http://www.gtk.org/introspection/core/1.0 type"`
128 }127 }
129 128
129+func (a *Array) Size() int {
130+ // there is a Length property
131+ // but it seems that GTK doesn't really use this
132+ // see e.g. gtk_css_provider_load_from_data
133+ //if a.Length != nil {
134+ // return *a.Length
135+ //}
136+ return a.FixedSize
137+}
138+
139+func ReplaceSize(str string, n int) string {
140+ trimmed := strings.TrimPrefix(str, "*")
141+ if trimmed == str {
142+ trimmed = strings.TrimPrefix(str, "[]")
143+ }
144+ return fmt.Sprintf("[%d]%s", n, trimmed)
145+}
146+
147+func (a *Array) Template(ns string, kinds KindMap) string {
148+ // Does the c type exist in our little hacky conversion rule?
149+ if v, ok := convList[a.CType]; ok {
150+ return v
151+ }
152+ // default case of an array is just a pointer
153+ ret := "uintptr"
154+
155+ if a.Type != nil {
156+ t := a.Type.Template(ns, kinds, true)
157+ if strings.Count(t, "*") > 0 {
158+ return "uintptr"
159+ }
160+ ret = "[]" + t
161+ }
162+
163+ // Check if there is a fixed size
164+ n := a.Size()
165+ if n > 0 {
166+ return ReplaceSize(ret, n)
167+ }
168+ return ret
169+}
170+
130 // IsZeroTerminated returns true if the Array is zero-terminated. It accounts171 // IsZeroTerminated returns true if the Array is zero-terminated. It accounts
131 // for edge cases of the structure.172 // for edge cases of the structure.
132 func (a Array) IsZeroTerminated() bool {173 func (a Array) IsZeroTerminated() bool {
@@ -217,7 +258,7 @@ func (typ *Type) IsIntrospectable() bool {
217 return typ.Introspectable == nil || *typ.Introspectable258 return typ.Introspectable == nil || *typ.Introspectable
218 }259 }
219 260
220-func (t *Type) Template(ns string, kinds KindMap) string {261+func (t *Type) Template(ns string, kinds KindMap, array bool) string {
221 if v, ok := convList[t.Name]; ok {262 if v, ok := convList[t.Name]; ok {
222 return v263 return v
223 }264 }
@@ -235,6 +276,9 @@ func (t *Type) Template(ns string, kinds KindMap) string {
235 if count > 0 && kind != CallbackType {276 if count > 0 && kind != CallbackType {
236 _type = strings.Repeat("*", count) + _type277 _type = strings.Repeat("*", count) + _type
237 } else if kind == RecordsType {278 } else if kind == RecordsType {
279+ if array {
280+ return _type
281+ }
238 // if it's not a pointer282 // if it's not a pointer
239 // then purego doesn't support it (struct by value is not supported283 // then purego doesn't support it (struct by value is not supported
240 // so return uintptr284 // so return uintptr
@@ -336,7 +380,7 @@ type Constant struct {
336 }380 }
337 381
338 func (c *Constant) Template(ns string, kinds KindMap) ConstantTemplate {382 func (c *Constant) Template(ns string, kinds KindMap) ConstantTemplate {
339- t := c.Type.Template(ns, kinds)383+ t := c.Type.Template(ns, kinds, false)
340 v := c.Value384 v := c.Value
341 385
342 switch t {386 switch t {
@@ -692,18 +736,22 @@ func (r *ReturnValue) Template(ns string, ins string, kinds KindMap, throws bool
692 }736 }
693 kind := kinds.Kind(lns, raw)737 kind := kinds.Kind(lns, raw)
694 stars := strings.Count(val, "*")738 stars := strings.Count(val, "*")
695- if kind != OtherType && kind != UnknownType {739+
696- if ins != "" && strings.Count(val, ".") < 1 {740+ if kind != OtherType && kind != UnknownType && kind != SliceType {
697- val = ins + "." + val741+ val = util.AddNamespace(val, ins)
698- }
699 }742 }
700 if stars > 0 {743 if stars > 0 {
701- val = strings.Repeat("*", stars) + strings.ReplaceAll(val, "*", "")744+ val = util.StarsInFront(strings.ReplaceAll(val, "*", ""), stars)
702 }745 }
703 switch kind {746 switch kind {
704 case ClassesType:747 case ClassesType:
705 raw = "uintptr"748 raw = "uintptr"
706 class = true749 class = true
750+ // TODO: why does this happen?
751+ if strings.HasPrefix(val, "[]") {
752+ class = false
753+ val = "uintptr"
754+ }
707 case InterfacesType:755 case InterfacesType:
708 raw = "uintptr"756 raw = "uintptr"
709 val += "Base"757 val += "Base"
M internal/gir/util/util.go +31 -0
@@ -19,6 +19,20 @@ func delimToCamel(s string, delim string) string {
1919 return sb.String()
2020 }
2121
22+// StarsInFront adds pointer characters (*, stars) in front of the type
23+// if there is a slice in front
24+// we need to add the slice and then afterwards the stars
25+// e.g. [2]foo becomes [2]*foo with n=1
26+func StarsInFront(str string, n int) string {
27+ b := strings.Index(str, "[")
28+ e := strings.Index(str, "]")
29+ stars := strings.Repeat("*", n)
30+ if b == 0 && e != -1 {
31+ return str[b:e+1] + stars + str[e+1:len(str)]
32+ }
33+ return stars + str
34+}
35+
2236 // SnakeToCamel converts hello_world to HelloWorld
2337 func SnakeToCamel(s string) string {
2438 return delimToCamel(s, "_")
@@ -53,6 +67,23 @@ func ReplaceExtension(filename string, ext string) string {
5367 return strings.Join(splt, ".")
5468 }
5569
70+func PrefixValue(val, prefix string) string {
71+ // if it's a slice, it has to come first
72+ b := strings.Index(val, "[")
73+ e := strings.Index(val, "]")
74+ if b == 0 && e != -1 {
75+ return val[b:e+1] + prefix + val[e+1:]
76+ }
77+ return prefix + val
78+}
79+
80+func AddNamespace(val, ns string) string {
81+ if ns == "" || strings.Count(val, ".") >= 1 {
82+ return val
83+ }
84+ return PrefixValue(val, ns+".")
85+}
86+
5687 // NormalizeNamespace converts a type to one that always includes a lowercase namespace
5788 // if no namespace is found, it adds `ns`, unless if strip is True then namespaces always equaling `ns` will be removed
5889 func NormalizeNamespace(ns string, gotype string, strip bool) string {
@@ -19,6 +19,20 @@ func delimToCamel(s string, delim string) string {
19 return sb.String()19 return sb.String()
20 }20 }
21 21
22+// StarsInFront adds pointer characters (*, stars) in front of the type
23+// if there is a slice in front
24+// we need to add the slice and then afterwards the stars
25+// e.g. [2]foo becomes [2]*foo with n=1
26+func StarsInFront(str string, n int) string {
27+ b := strings.Index(str, "[")
28+ e := strings.Index(str, "]")
29+ stars := strings.Repeat("*", n)
30+ if b == 0 && e != -1 {
31+ return str[b:e+1] + stars + str[e+1:len(str)]
32+ }
33+ return stars + str
34+}
35+
22 // SnakeToCamel converts hello_world to HelloWorld36 // SnakeToCamel converts hello_world to HelloWorld
23 func SnakeToCamel(s string) string {37 func SnakeToCamel(s string) string {
24 return delimToCamel(s, "_")38 return delimToCamel(s, "_")
@@ -53,6 +67,23 @@ func ReplaceExtension(filename string, ext string) string {
53 return strings.Join(splt, ".")67 return strings.Join(splt, ".")
54 }68 }
55 69
70+func PrefixValue(val, prefix string) string {
71+ // if it's a slice, it has to come first
72+ b := strings.Index(val, "[")
73+ e := strings.Index(val, "]")
74+ if b == 0 && e != -1 {
75+ return val[b:e+1] + prefix + val[e+1:]
76+ }
77+ return prefix + val
78+}
79+
80+func AddNamespace(val, ns string) string {
81+ if ns == "" || strings.Count(val, ".") >= 1 {
82+ return val
83+ }
84+ return PrefixValue(val, ns+".")
85+}
86+
56 // NormalizeNamespace converts a type to one that always includes a lowercase namespace87 // NormalizeNamespace converts a type to one that always includes a lowercase namespace
57 // if no namespace is found, it adds `ns`, unless if strip is True then namespaces always equaling `ns` will be removed88 // if no namespace is found, it adds `ns`, unless if strip is True then namespaces always equaling `ns` will be removed
58 func NormalizeNamespace(ns string, gotype string, strip bool) string {89 func NormalizeNamespace(ns string, gotype string, strip bool) string {

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