nswrap/types/convert.go
2019-06-19 14:07:33 -04:00

523 lines
11 KiB
Go

package types
import (
"fmt"
"os"
"regexp"
"strings"
)
//super is a map recording which class is the parent of each other class
var super map[string]string
//wrapped is a map recording whether a given GoType is to be "wrapped" in a
//go struct.
var wrapped map[string]bool
var tdptr map[string]bool
func ShouldWrap(gt string) bool {
return wrapped[gt]
}
func PtrShouldWrap(gt string) bool {
return gt != "" && gt[0] == '*' && wrapped[gt[1:]]
}
func TypedefShouldWrap(gt string) bool {
return tdptr[gt]
}
//goInterfaces records the names of top level Go interfaces.
var goInterfaces map[string]bool
func IsGoInterface(gt string) bool {
return goInterfaces[gt]
}
func PtrIsGoInterface(gt string) bool {
return gt != "" && gt[0] == '*' && goInterfaces[gt[1:]]
}
//TypeParameters maps, for each class, a TypedefName to a type, representing
//the Objective-C type parameters for that class
var TypeParameters map[string]map[string]string
//Typedefs maps from C types to the Type of a typedef with that name.
var typedefs map[string]*Type
func (t *Type) Typedef() *Type {
return typedefs[t.CType()]
}
var (
r_id *regexp.Regexp
r_instancename *regexp.Regexp
r_instancetype *regexp.Regexp
)
func init() {
super = make(map[string]string)
wrapped = make(map[string]bool)
tdptr = make(map[string]bool)
goInterfaces = make(map[string]bool)
TypeParameters = make(map[string]map[string]string)
typedefs = make(map[string]*Type)
r_id = regexp.MustCompile(`\bid\b`)
r_instancename = regexp.MustCompile(`\binstancename\b`)
r_instancetype = regexp.MustCompile(`\binstancetype\b`)
}
func Super(c string) string {
return super[c]
}
func SetSuper(c, p string) {
super[c] = p
}
func SetTypeParam(c, n, t string) {
if TypeParameters[c] == nil {
TypeParameters[c] = make(map[string]string)
}
TypeParameters[c][n] = t
}
func AddTypedef(n string, tp *Type) {
//fmt.Printf("AddTypedef(): %s -> %s\n",n,t)
typedefs[n] = tp
}
type Type struct {
Node *Node
Class string
Variadic bool
}
func (t *Type) CloneToClass(c string) *Type {
return &Type{
Node: t.Node,
Class: c,
Variadic: t.Variadic,
}
}
func clean(n *Node, c string) (*Node, bool) {
if n == nil {
return nil, false
}
ret := NewNode(n.Kind, n.Content)
ret.Children = n.Children
//fmt.Printf("clean(%s,%s)\n",n.CType(),c)
recur := false
if TypeParameters[c] != nil {
for k, v := range TypeParameters[c] {
recur = ret.renameTypedefs(k, v)
}
}
if recur {
clean(n, c)
return ret, true
}
return n, false
}
func NewType(n *Node, c string) *Type {
n2, _ := clean(n, c)
return &Type{
Node: n2,
Class: c,
}
}
func NewTypeFromString(t, c string) *Type {
//fmt.Printf("t/c: %s/%s\n",t,c)
n, err := Parse(t)
//fmt.Printf("%p %s",n,n.String())
if err != nil {
return &Type{}
}
if n2, ok := clean(n, c); ok {
//found type parameters, re-parse
return NewTypeFromString(n2.CType(), c)
}
return &Type{
Node: n,
Class: c,
}
}
func (t *Type) String() string {
return t.Node.String()
}
func (t *Type) PointsTo() *Type {
if td := t.Typedef(); td != nil {
return td.PointsTo()
}
if pt := t.Node.PointsTo(); pt != nil {
return NewType(t.Node.PointsTo(), t.Class)
} else {
return nil
}
}
func Wrap(s string) {
//fmt.Printf("wrapped[%s] = true\n",s)
wrapped[s] = true
}
func (t *Type) BaseType() *Type {
if t == nil {
return nil
}
ret := NewType(
t.Node.BaseType(),
t.Class,
)
return ret
}
func swapstars(s string) string {
for i := len(s) - 1; i > 0 && s[i] == '*'; {
s = "*" + s[:i]
}
return strings.TrimSpace(s)
}
func (t *Type) CGoType() string {
ct := swapstars("C." + t.CType())
ct = strings.ReplaceAll(ct, "unsigned ", "u")
ct = strings.ReplaceAll(ct, "signed ", "u")
ct = strings.ReplaceAll(ct, "long ", "long")
ct = strings.ReplaceAll(ct, "complex ", "complex")
ct = strings.ReplaceAll(ct, " ", "_")
return ct
}
func (t *Type) GoType() string {
return _goType(t.CType())
}
func _goType(ct string) string {
ct = strings.Title(ct)
ct = strings.ReplaceAll(ct, " ", "")
ct = strings.TrimPrefix(ct, "Struct")
ct = swapstars(ct)
if len(ct) > 0 && ct[0] == '*' && IsGoInterface(ct[1:]) {
return ct
}
if ct == "Id" {
ct = "*Id"
}
if ShouldWrap(ct) {
return ct
}
if len(ct) > 4 && ct[len(ct)-4:len(ct)] == "Void" {
ct = ct[:len(ct)-5] + "unsafe.Pointer"
}
return ct
}
func (t *Type) CType() string {
return t._CType(false)
}
func (t *Type) CTypeAttrib() string {
return t._CType(true)
}
func (t *Type) _CType(attrib bool) string {
if t == nil {
//fmt.Println("nil sent to _CType()")
return ""
}
var ct string
if attrib {
ignore := map[string]bool{"GenericList": true}
ct = t.Node._CType(ignore)
} else {
ct = t.Node.CTypeSimplified()
}
ct = r_id.ReplaceAllString(ct, "NSObject*")
ct = r_instancename.ReplaceAllString(ct, t.Class)
ct = r_instancetype.ReplaceAllString(ct, t.Class+"*")
return ct
}
func (t *Type) GoTypeDecl(fin bool) string {
gt := t.GoType()
if wrapped[gt] {
return t.GoInterfaceDecl(fin)
}
if t.Node.IsId() {
return ""
}
if td := t.Typedef(); td != nil {
tdgt := td.GoType()
if ShouldWrap(tdgt) || PtrShouldWrap(tdgt) { // type alias
if PtrIsGoInterface(tdgt) {
tdgt = "*Id"
}
if IsGoInterface(tdgt) {
tdgt = "Id"
}
Wrap(gt)
tdptr[gt] = true
return fmt.Sprintf(`
type %s = %s
`, gt, tdgt)
}
return fmt.Sprintf(`
type %s %s
`, gt, td.CGoType())
}
if Debug {
fmt.Printf(" writing GoTypeDecl for %s\n",gt)
}
switch gt {
case "", "Void":
return ""
default:
return fmt.Sprintf(`
type %s %s
`, gt, t.CGoType())
}
}
func (t *Type) GoInterfaceDecl(fin bool) string {
ct := t.CType()
gt := t.GoType()
if Debug {
fmt.Printf(" writing GoInterfaceDecl for %s\n",gt)
}
if gt[0] == '*' {
gt = gt[1:] // dereference wrapped types
ct = ct[:len(ct)-1]
fmt.Printf(" dereferenced %s\n",gt)
}
super := Super(ct)
if super == "" {
//fmt.Printf("goInterfaces[%s] = true\n",gt)
goInterfaces[gt] = true
return fmt.Sprintf(`
type %s interface {
Ptr() unsafe.Pointer
}
`, gt)
}
if IsGoInterface(super) {
super = "Id"
}
return fmt.Sprintf(`
type %s struct { %s }
func (o *%s) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
func (o *Id) %s() *%s {
return (*%s)(unsafe.Pointer(o))
}
`, gt, super, gt, gt, gt, gt)
}
func (t *Type) IsFunctionPtr() bool {
if t == nil {
return false
}
if td := t.Typedef(); td != nil {
return td.IsFunctionPtr()
}
if pt := t.PointsTo(); pt != nil {
return pt.IsFunction()
}
return false
}
func (t *Type) IsFunction() bool {
if t == nil {
//fmt.Println("nil sent to IsFunction()")
return false
}
if td := t.Typedef(); td != nil {
return td.IsFunction()
}
return t.Node.IsFunction()
}
func (t *Type) IsValist() bool {
if t == nil {
return false
}
if t.GoType() == "__va_list_tag" { // OS dependent
return true
}
return false
}
func (t *Type) ReturnType() *Type {
if td := t.Typedef(); td != nil {
return td.ReturnType()
}
if rt := t.Node.ReturnType(); rt != nil {
return NewType(rt, t.Class)
}
return nil
}
func (t *Type) IsPointer() bool {
if t == nil {
return false
}
if td := t.Typedef(); td != nil {
return td.IsPointer()
}
return t.Node.IsPointer()
}
// cast C value to CGo
func (t *Type) CToGo(cval string) string {
if t.IsPointer() {
cval = "unsafe.Pointer(" + cval + ")"
}
return fmt.Sprintf("(%s)(%s)", t.GoType(), cval)
}
// Call a C function from Go with a given return type and parameter types
func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*Type, fun, fin bool, cm bool) string {
if rtype == nil {
//fmt.Println("nil sent to GoToC")
return ""
}
var ret strings.Builder
rt := rtype.CType()
rtgt := rtype.GoType()
//fmt.Printf("GoToC(%s): rtgt == %s\n",name,rtgt)
if PtrIsGoInterface(rtgt) {
//fmt.Printf(" PtrIsGoInterface(%s) = true\n",rtgt)
rtgt = "*Id"
}
sw := PtrShouldWrap(rtgt) || rtgt == "*Id" || TypedefShouldWrap(rtgt)
isptr := rtype.IsPointer()
if rt != "void" {
switch {
case PtrShouldWrap(rtgt), rtgt == "*Id":
isptr = true
if PtrIsGoInterface(rtgt) {
rtgt = "*Id"
}
ret.WriteString(fmt.Sprintf(
`ret := &%s{}
ret.ptr = unsafe.Pointer(`, rtgt[1:]))
case TypedefShouldWrap(rtgt):
isptr = true
rtgt := rtype.Typedef().GoType()
if PtrIsGoInterface(rtgt) {
rtgt = "*Id"
}
ret.WriteString(fmt.Sprintf(
`ret := &%s{}
ret.ptr = unsafe.Pointer(`, rtgt[1:]))
default:
if rtgt == "BOOL" {
ret.WriteString("ret := (")
rtgt = "bool"
} else {
ret.WriteString("ret := (" + rtgt + ")(")
}
if isptr {
ret.WriteString("unsafe.Pointer(")
}
}
}
ret.WriteString("C." + name + "(")
parms := []string{}
for i := 0; i < len(pnames); i++ {
pn, pt := pnames[i], ptypes[i]
ptgt := pt.GoType()
p := pn
switch {
case (PtrShouldWrap(ptgt) || PtrIsGoInterface(ptgt)) && !pt.Variadic:
p = pn + ".Ptr()"
case TypedefShouldWrap(ptgt) && !pt.Variadic && fun:
p = "(" + pt.CGoType() + ")(" + pn + ".Ptr())"
case TypedefShouldWrap(ptgt) && !pt.Variadic && !fun:
p = pn + ".Ptr()"
case snames[i] != "":
p = "unsafe.Pointer(&" + snames[i] + "[0])"
case pt.Variadic:
p = "unsafe.Pointer(&" + p + ")"
case pt.IsPointer() && !fun:
p = "unsafe.Pointer(" + pn + ")"
case pt.IsPointer() && fun && pt.BaseType().CType() == "void":
p = pn
default:
p = "(" + pt.CGoType() + ")(" + pn + ")"
}
parms = append(parms, p)
}
ret.WriteString(strings.Join(parms, ", "))
ret.WriteString(")")
if rt != "void" && !sw {
ret.WriteString(")")
}
if isptr {
ret.WriteString(")")
}
if rt == "BOOL" {
ret.WriteString(" != 0")
}
for i, sname := range snames {
if sname == "" {
continue
}
ptgt := ptypes[i].GoType()
if len(ptgt) < 2 {
fmt.Printf("Error in function translation -- argument %s to %s should be pointer to pointer\n",pnames[i],name)
os.Exit(-1)
}
ptgt = ptgt[2:]
if IsGoInterface(ptgt) {
ptgt = "Id"
}
ret.WriteString(fmt.Sprintf(`
(*%s) = (*%s)[:cap(*%s)]
for i := 0; i < len(*%s); i++ {
if %s[i] == nil {
(*%s) = (*%s)[:i]
break
}
if (*%s)[i] == nil {
(*%s)[i] = &%s{}
}
(*%s)[i].ptr = %s[i]
}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, pnames[i], sname))
}
if rt != "void" {
cmp := ""
if sw {
if !cm && sname != "copy" && sname != "mutableCopy" {
cmp = fmt.Sprintf(`
if ret.ptr == o.ptr { return (%s)(unsafe.Pointer(o)) }`,rtgt)
}
ret.WriteString(fmt.Sprintf(`
if ret.ptr == nil { return ret }%s`,cmp))
}
if fin {
dbg := ""
dbg2 := ""
if Debug {
dbg = fmt.Sprintf(`fmt.Printf("Setting finalizer (%s): %%p -> %%p\n", ret, ret.ptr)
`, rtgt)
dbg2 = fmt.Sprintf(`fmt.Printf("Finalizer (%s): release %%p -> %%p\n", o, o.ptr)
`, rtgt)
}
ret.WriteString(fmt.Sprintf(`
%sruntime.SetFinalizer(ret, func(o %s) {
%so.Release()
})`, dbg, rtgt, dbg2))
}
ret.WriteString(`
return ret`)
}
return ret.String()
}