Skip to content

Commit

Permalink
WIP import aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
diamondburned committed May 14, 2021
1 parent 6b4520e commit b0dc0cf
Show file tree
Hide file tree
Showing 16 changed files with 4,727 additions and 43 deletions.
58 changes: 42 additions & 16 deletions gir/girgen/gen_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@ package girgen

import (
"github.com/diamondburned/gotk4/gir"
"github.com/diamondburned/gotk4/internal/pen"
)

var classTmpl = newGoTemplate(`
{{ GoDoc .Doc 0 .GoName }}
type {{ .GoName }} struct {
{{ index .TypeTree 0 }}
{{ $.Ng.GoType (index .TypeTree 0) }}
}
func wrap{{ .GoName }}(obj *glib.Object) *{{ .GoName }} {
return {{ .Wrap "obj" }}
}
func marshal{{ .GoName }}(p uintptr) (interface{}, error) {
val := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := glib.Take(unsafe.Pointer(val))
return wrapWidget(obj), nil
}
`)

type classGenerator struct {
gir.Class
GoName string
TypeTree []string
TypeTree []*ResolvedType

ng *NamespaceGenerator
Ng *NamespaceGenerator
}

func (cg *classGenerator) Use(class gir.Class) bool {
Expand All @@ -27,25 +38,19 @@ func (cg *classGenerator) Use(class gir.Class) bool {
if class.Parent != "" {
parent := class.Parent
for {
parentType := cg.ng.ResolveTypeName(parent)
parentType := cg.Ng.ResolveTypeName(parent)
if parentType == nil {
return false
}

goType := parentType.GoType(parentType.NeedsNamespace(cg.ng.current))
cg.TypeTree = append(cg.TypeTree, goType)
cg.TypeTree = append(cg.TypeTree, parentType)

// We've resolved as deep as we can, so bail. This check works
// because non-class types don't have parent classes.
if parentType.Extern == nil || parentType.Extern.Result.Class == nil {
if parentType.Parent == "" {
break
}

// Use the parent class' parent type.
parent = parentType.Extern.Result.Class.Parent
if parent == "" {
break
}
parent = parentType.Parent
}
} else {
// TODO: check what happens if a class has no parent. It should have a
Expand All @@ -59,17 +64,38 @@ func (cg *classGenerator) Use(class gir.Class) bool {
return true
}

// Wrap returns the wrap string around the given variable name of type
// *glib.Object.
func (cg *classGenerator) Wrap(objName string) string {
var p pen.Piece
p.Char('&').Write(cg.GoName).Char('{')

for _, typ := range cg.TypeTree {
p.Writef("%s{", typ.GoType(false))
}

p.Write(objName)

for range cg.TypeTree {
p.Char('}')
}

p.Char('}')

return p.String()
}

func (ng *NamespaceGenerator) generateClasses() {
cg := classGenerator{
TypeTree: make([]string, 15),
ng: ng,
TypeTree: make([]*ResolvedType, 15),
Ng: ng,
}

for _, class := range ng.current.Namespace.Classes {
if !cg.Use(class) {
continue
}

ng.pen.BlockTmpl(classTmpl, cg)
ng.pen.BlockTmpl(classTmpl, &cg)
}
}
21 changes: 15 additions & 6 deletions gir/girgen/girgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"log"
"path"
"strconv"
"text/template"

Expand Down Expand Up @@ -128,7 +129,7 @@ func (g *Generator) UseNamespace(namespace string) *NamespaceGenerator {
pen: pen.New(&buf),
body: &buf,

imports: map[string]struct{}{},
imports: map[string]string{},
gen: g,
current: res,
}
Expand All @@ -139,16 +140,14 @@ type NamespaceGenerator struct {
pen *pen.Pen
body *bytes.Buffer

imports map[string]struct{}
imports map[string]string // optional alias value

gen *Generator
current *gir.NamespaceFindResult
}

// Generate generates the current namespace into the given writer.
func (ng *NamespaceGenerator) Generate(w io.Writer) error {
ng.addImport("unsafe")
ng.addImport("github.com/gotk3/gotk3/glib")
ng.addImport("github.com/diamondburned/gotk4/internal/gextras")
ng.addImport("github.com/diamondburned/gotk4/internal/callback")

Expand Down Expand Up @@ -178,7 +177,13 @@ func (ng *NamespaceGenerator) Generate(w io.Writer) error {

if len(ng.imports) > 0 {
pen.Words("import (")
for imp := range ng.imports {
for imp, alias := range ng.imports {
// Only use the import alias if it's provided and does not match the
// base name of the import path for idiomaticity.
if alias != "" && alias != path.Base(imp) {
pen.Words(alias, "")
}

pen.Words(strconv.Quote(imp))
}
pen.Block(")")
Expand Down Expand Up @@ -235,10 +240,14 @@ func (ng *NamespaceGenerator) warnUnknownType(typ string) {
}

func (ng *NamespaceGenerator) addImport(pkgPath string) {
ng.addImportAlias(pkgPath, "")
}

func (ng *NamespaceGenerator) addImportAlias(pkgPath, alias string) {
_, ok := ng.imports[pkgPath]
if ok {
return
}

ng.imports[pkgPath] = struct{}{}
ng.imports[pkgPath] = alias
}
90 changes: 69 additions & 21 deletions gir/girgen/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package girgen
import (
"fmt"
"log"
"path"
"strings"
"sync"

Expand All @@ -17,14 +18,17 @@ type ResolvedType struct {
Extern *ExternType // optional
Builtin *string // optional

CType string
Ptr uint8
Import string // Full import path.
Package string // Package name, also import alias.

Parent string // GIR Type, optional
CType string
Ptr uint8
}

// ExternType is an externally resolved type.
type ExternType struct {
Package string
Result *gir.TypeFindResult
Result *gir.TypeFindResult
}

var (
Expand All @@ -50,23 +54,50 @@ func countPtrs(typ gir.Type, result *gir.TypeFindResult) uint8 {
}

// builtinType is a convenient function to make a new resolvedType.
func builtinType(goType string, typ gir.Type) *ResolvedType {
func builtinType(imp, typ string, girType gir.Type) *ResolvedType {
// Create the actual type.
typ = path.Base(imp) + "." + typ

return &ResolvedType{
Builtin: &typ,
Import: imp,
Package: path.Base(imp),
CType: girType.CType,
Ptr: countPtrs(girType, nil),
}
}

// externGLibType returns an external GLib type from gotk3.
func externGLibType(glibType string, typ gir.Type) *ResolvedType {
glibType = "externglib." + glibType

return &ResolvedType{
Builtin: &goType,
Builtin: &glibType,
Import: "github.com/gotk3/gotk3/glib",
Package: "externglib",
CType: typ.CType,
Ptr: countPtrs(typ, nil),
}
}

// typeFromResult creates a resolved type from the given type result.
func typeFromResult(typ gir.Type, result *gir.TypeFindResult) *ResolvedType {
func typeFromResult(gen *Generator, typ gir.Type, result *gir.TypeFindResult) *ResolvedType {
var parent string
if result.Class != nil {
parent = result.Class.Parent
}

pkg := gir.GoNamespace(result.Namespace)

return &ResolvedType{
Extern: &ExternType{
Package: gir.GoNamespace(result.Namespace),
Result: result,
Result: result,
},
CType: typ.CType,
Ptr: countPtrs(typ, result),
Import: gen.ImportPath(pkg),
Package: pkg,
Parent: parent,
CType: typ.CType,
Ptr: countPtrs(typ, result),
}
}

Expand Down Expand Up @@ -94,7 +125,7 @@ func (typ *ResolvedType) GoType(needsNamespace bool) string {
if !needsNamespace {
return ptr + name
}
return ptr + typ.Extern.Package + "." + name
return ptr + typ.Package + "." + name
}

// CGoType returns the CGo type.
Expand All @@ -105,6 +136,18 @@ func (typ *ResolvedType) CGoType() string {
return strings.Repeat("*", ptr) + "C." + val
}

// movePtr moves the same number of pointers from the given orig string into
// another string.
func movePtr(orig, into string) string {
ptr := strings.Count(orig, "*")
return strings.Repeat("*", ptr) + into
}

// GoType returns the generated Go type of the given resolved type.
func (ng *NamespaceGenerator) GoType(resolved *ResolvedType) string {
return resolved.GoType(resolved.NeedsNamespace(ng.current))
}

// arrayType generates the Go type signature for the given array.
func (ng *NamespaceGenerator) resolveArrayType(array gir.Array) (string, bool) {
arrayPrefix := "[]"
Expand Down Expand Up @@ -144,7 +187,7 @@ func (ng *NamespaceGenerator) ResolveToGoType(typ gir.Type) (string, bool) {
return "", false
}

return resolved.GoType(resolved.NeedsNamespace(ng.current)), true
return ng.GoType(resolved), true
}

// ResolveTypeName resolves the given GIR type name. The resolved type will
Expand Down Expand Up @@ -191,7 +234,7 @@ func (ng *NamespaceGenerator) ResolveType(typ gir.Type) *ResolvedType {
// Add the import in the same singleflight callback, but only if the
// namespace is not the current one.
if resolved.Extern != nil && !resolved.Extern.Result.Eq(ng.current) {
ng.addImport(ng.gen.ImportPath(resolved.Extern.Package))
ng.addImportAlias(resolved.Import, resolved.Package)
}
}

Expand Down Expand Up @@ -231,10 +274,6 @@ var girPrimitiveGo = map[string]string{
"guint64": "uint64",
"utf8": "string",
"filename": "string",

// TODO: ignore field
// TODO: aaaaaaaaaaaaaaaaaaaaaaa
"gpointer": "unsafe.Pointer",
}

func (ng *NamespaceGenerator) resolveTypeUncached(typ gir.Type) *ResolvedType {
Expand All @@ -244,11 +283,15 @@ func (ng *NamespaceGenerator) resolveTypeUncached(typ gir.Type) *ResolvedType {
}

if prim, ok := girPrimitiveGo[typ.Name]; ok {
return builtinType(prim, typ)
return builtinType("", prim, typ)
}

// Resolve the unknown namespace that is GLib and primitive types.
switch typ.Name {
// TODO: ignore field
// TODO: aaaaaaaaaaaaaaaaaaaaaaa
case "gpointer":
return builtinType("unsafe", "Pointer", typ)
case "GLib.DestroyNotify", "DestroyNotify": // This should be handled externally.
return builtinType("unsafe.Pointer", typ)
case "GType":
Expand All @@ -259,14 +302,17 @@ func (ng *NamespaceGenerator) resolveTypeUncached(typ gir.Type) *ResolvedType {
return builtinType("*glib.Object", typ)
case "GObject.Closure":
return builtinType("*glib.Closure", typ)
case "GObject.InitiallyUnowned":
return builtinType("glib.InitiallyUnowned", typ)
case "GObject.Callback":
// Callback is a special func(Any) Any type, so we treat it as
// interface{} similarly to object.Connect(). We can use glib's Closure
// APIs to parse this interface{}.
return builtinType("interface{}", typ)

case "GObject.InitiallyUnowned":
resolved := builtinType("glib.InitiallyUnowned", typ)
// resolved.Parent = "GObject.Object"
return resolved

case "va_list":
// CGo cannot handle variadic argument lists.
return nil
Expand Down Expand Up @@ -421,6 +467,8 @@ func (ng *NamespaceGenerator) _typeConverter(value, target string, typ gir.Type,

// Resolve special-case GLib types.
switch typ.Name {
case "gpointer":
return directCallOrCreate(value, target, "unsafe.Pointer", create)
case "GLib.DestroyNotify", "DestroyNotify":
return ""
case "GType":
Expand Down
Loading

0 comments on commit b0dc0cf

Please sign in to comment.