genbindings: track all typedefs, apply in a fixup AST pass

This commit is contained in:
mappu 2024-08-26 22:49:33 +12:00
parent 85cf3b84d8
commit 0aa312c424
6 changed files with 132 additions and 3 deletions

View File

@ -548,7 +548,7 @@ func parseMethod(node map[string]interface{}, mm *CppMethod) error {
return nil return nil
} }
// parseTypeString converts a string like // parseTypeString converts a function/method type string such as
// - `QString (const char *, const char *, int)` // - `QString (const char *, const char *, int)`
// - `void (const QKeySequence \u0026)` // - `void (const QKeySequence \u0026)`
// into its (A) return type and (B) separate parameter types. // into its (A) return type and (B) separate parameter types.

View File

@ -7,10 +7,12 @@ import (
var ( var (
KnownClassnames map[string]struct{} // Entries of the form QFoo::Bar if it is an inner class KnownClassnames map[string]struct{} // Entries of the form QFoo::Bar if it is an inner class
KnownTypedefs map[string]CppTypedef
) )
func init() { func init() {
KnownClassnames = make(map[string]struct{}) KnownClassnames = make(map[string]struct{})
KnownTypedefs = make(map[string]CppTypedef)
} }
type CppParameter struct { type CppParameter struct {
@ -23,6 +25,20 @@ type CppParameter struct {
Optional bool Optional bool
} }
func (p *CppParameter) AssignAlias(newType string) {
if p.TypeAlias == "" {
p.TypeAlias = p.ParameterType // Overwrite once only, at the earliest base type
}
p.ParameterType = newType
}
func (p *CppParameter) CopyWithAlias(alias CppParameter) CppParameter {
ret := *p // copy
ret.ParameterName = alias.ParameterName
ret.TypeAlias = alias.ParameterType
return ret
}
func (p *CppParameter) UnderlyingType() string { func (p *CppParameter) UnderlyingType() string {
if p.TypeAlias != "" { if p.TypeAlias != "" {
return p.TypeAlias return p.TypeAlias
@ -221,7 +237,7 @@ type CppClass struct {
type CppTypedef struct { type CppTypedef struct {
Alias string Alias string
UnderlyingType string UnderlyingType CppParameter
} }
type CppParsedHeader struct { type CppParsedHeader struct {

View File

@ -141,6 +141,10 @@ func main() {
for _, c := range parsed.Classes { for _, c := range parsed.Classes {
KnownClassnames[c.ClassName] = struct{}{} KnownClassnames[c.ClassName] = struct{}{}
} }
for _, td := range parsed.Typedefs {
KnownTypedefs[td.Alias] = td // copy
}
processHeaders = append(processHeaders, parsed) processHeaders = append(processHeaders, parsed)
} }
@ -151,6 +155,10 @@ func main() {
for _, parsed := range processHeaders { for _, parsed := range processHeaders {
log.Printf("Processing %q...", parsed.Filename) log.Printf("Processing %q...", parsed.Filename)
// More AST transforms on our IL
astTransformTypedefs(parsed)
{ {
// Save the IL file for debug inspection // Save the IL file for debug inspection
jb, err := json.MarshalIndent(parsed, "", "\t") jb, err := json.MarshalIndent(parsed, "", "\t")

View File

@ -23,9 +23,14 @@ func takeChildren(c *CppClass) []CppClass {
func astTransformChildClasses(parsed *CppParsedHeader) { func astTransformChildClasses(parsed *CppParsedHeader) {
var taken []CppClass var taken []CppClass
for i, _ := range parsed.Classes { for i, c := range parsed.Classes {
taken = append(taken, takeChildren(&parsed.Classes[i])...) taken = append(taken, takeChildren(&parsed.Classes[i])...)
// Also lift all child typedefs and enums
parsed.Typedefs = append(parsed.Typedefs, c.ChildTypedefs...)
parsed.Enums = append(parsed.Enums, c.ChildEnums...)
} }
parsed.Classes = append(parsed.Classes, taken...) parsed.Classes = append(parsed.Classes, taken...)
} }

View File

@ -0,0 +1,55 @@
package main
/*
func typedefUnderlyingOrInt(td CppTypedef) string {
if strings.HasPrefix(td.UnderlyingType.ParameterType, "QFlag<") {
return "int"
}
if strings.HasPrefix(td.UnderlyingType.ParameterType, "signed ") {
return td.UnderlyingType.ParameterType[7:]
}
if strings.Contains(td.UnderlyingType.ParameterType, "(*)") {
return "uintptr" // Function pointer, nonrepresentible
}
return td.UnderlyingType.ParameterType
}
*/
// astTransformTypedefs replaces the ParameterType with any known typedef value.
func astTransformTypedefs(parsed *CppParsedHeader) {
for i, c := range parsed.Classes {
for j, m := range c.Methods {
for k, p := range m.Parameters {
if td, ok := KnownTypedefs[p.ParameterType]; ok {
p = td.UnderlyingType.CopyWithAlias(p)
}
m.Parameters[k] = p
}
if td, ok := KnownTypedefs[m.ReturnType.ParameterType]; ok {
m.ReturnType = td.UnderlyingType.CopyWithAlias(m.ReturnType)
//m.ReturnType.AssignAlias(typedefUnderlyingOrInt(td))
}
c.Methods[j] = m
}
for j, m := range c.Ctors {
for k, p := range m.Parameters {
if td, ok := KnownTypedefs[p.ParameterType]; ok {
p = td.UnderlyingType.CopyWithAlias(p) // .AssignAlias(typedefUnderlyingOrInt(td))
}
m.Parameters[k] = p
}
c.Ctors[j] = m
}
parsed.Classes[i] = c
}
}

View File

@ -0,0 +1,45 @@
package main
import (
"testing"
)
func TestTransformTypedefs(t *testing.T) {
// Test that the static typedefs are applied
makeTest := func(typeName string) CppParsedHeader {
return CppParsedHeader{
Classes: []CppClass{
CppClass{
ClassName: "QTestClass",
Ctors: []CppMethod{
CppMethod{
Parameters: []CppParameter{
CppParameter{
ParameterName: "foo",
ParameterType: typeName,
},
},
IsStatic: true,
},
},
},
},
}
}
// t.Logf("Existing typedefs: %#v\n", KnownTypedefs)
parsed := makeTest("WId")
astTransformTypedefs(&parsed)
got := parsed.Classes[0].Ctors[0].Parameters[0].ParameterType
expect := "uintptr_t"
if got != expect {
t.Errorf("Transform of WId got %q, expected %q", got, expect)
}
}