From be92b200adf0eecc72acb5d4a1f2edec17c44816 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 1 May 2019 10:58:29 -0400 Subject: [PATCH] Handle functions identified by regexp in nswrap.toml config file. Handle inherited class methods. --- examples/foundation/main.go | 13 +- examples/foundation/nswrap.toml | 4 + main.go | 15 ++ types/convert.go | 72 ++++++-- types/cparser.go | 2 +- types/main.go | 22 ++- wrap/main.go | 284 ++++++++++++++++++++++---------- 7 files changed, 308 insertions(+), 104 deletions(-) diff --git a/examples/foundation/main.go b/examples/foundation/main.go index 49c82da..65ef07b 100644 --- a/examples/foundation/main.go +++ b/examples/foundation/main.go @@ -8,14 +8,19 @@ import ( ) func main() { - n1 := ns.StringWithUTF8String(ns.CharFromString("hi there")) + n1 := ns.NSStringStringWithUTF8String(ns.CharFromString("hi there")) c1 := n1.CapitalizedString() gs := c1.UTF8String().String() fmt.Println(gs) - n2 := ns.StringWithUTF8String(ns.CharFromString("hi world")) - n3 := ns.StringWithUTF8String(ns.CharFromString("ok bye")) - a := ns.ArrayWithObjects(n1,n2,n3) + n2 := ns.NSStringStringWithUTF8String(ns.CharFromString("hi world")) + n3 := ns.NSStringStringWithUTF8String(ns.CharFromString("ok bye")) + a := ns.NSMutableArrayArrayWithObjects(n1,n2,n3) fmt.Println("Length(a) = ",a.Count()) fmt.Println("is n2 in a?",a.ContainsObject(n2)) fmt.Println("is c1 in a?",a.ContainsObject(c1)) + n4 := n2.SubstringFromIndex(3) + n5 := n3.SubstringToIndex(4) + a.AddObject(n4) + a.AddObject(n5) + fmt.Println("Length(a) = ",a.Count()) } diff --git a/examples/foundation/nswrap.toml b/examples/foundation/nswrap.toml index 1c22470..b434e6b 100644 --- a/examples/foundation/nswrap.toml +++ b/examples/foundation/nswrap.toml @@ -3,6 +3,7 @@ InputFiles = [ ] Classes = [ "NSArray", + "NSMutableArray", "NSDictionary", "NSSet", "NSDate", @@ -14,6 +15,9 @@ Classes = [ "NSScanner", "NSFileManager", ] +Functions = [ + "NSMakeRange", +] SysImports = [ "Foundation/Foundation.h" ] Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ] VaArgs = 32 diff --git a/main.go b/main.go index f61c2d7..67bd50c 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "regexp" "runtime" "strings" @@ -19,6 +20,7 @@ type conf struct { Package string InputFiles []string Classes []string + Functions []string Imports []string SysImports []string Pragma []string @@ -134,6 +136,15 @@ func buildTree(nodes []treeNode, depth int) []ast.Node { return results } +func matches(x string, rs []string) bool { + for _,r := range rs { + if m, _ := regexp.MatchString("^" + r + "$",x); m { + return true + } + } + return false +} + // Start begins transpiling an input file. func Start() (err error) { // 1. Compile it first (checking for errors) @@ -185,6 +196,10 @@ func Start() (err error) { w.AddCategory(x) case *ast.TypedefDecl: types.AddTypedef(x.Name,x.Type) + case *ast.FunctionDecl: + if matches(x.Name,Config.Functions) { + w.AddFunction(x) + } } } } diff --git a/types/convert.go b/types/convert.go index a1a8b62..dc85fa9 100644 --- a/types/convert.go +++ b/types/convert.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "regexp" "strings" ) @@ -36,12 +37,22 @@ 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) 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 { @@ -71,6 +82,14 @@ type Type struct { 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 @@ -84,8 +103,8 @@ func clean(n *Node,c string) (*Node,bool) { recur = ret.renameTypedefs(k,v) } } - recur = recur || ret.renameTypedefs("instancename",c) - recur = recur || ret.renameTypedefs("instancetype",c + "*") +// recur = recur || ret.renameTypedefs("instancename",c) +// recur = recur || ret.renameTypedefs("instancetype",c + "*") if recur { clean(n, c) return ret,true @@ -127,7 +146,14 @@ func (t *Type) String() string { } func (t *Type) PointsTo() *Type { - return NewType(t.Node.PointsTo(), t.Class) + 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) { @@ -136,6 +162,9 @@ func Wrap(s string) { } func (t *Type) BaseType() *Type { + if t == nil { + return nil + } ret := NewType( t.Node.BaseType(), t.Class, @@ -172,10 +201,12 @@ func _goType(ct string) string { if len(ct) > 0 && ct[0] == '*' && isGoInterface(ct[1:]) { return ct[1:] } + if ct == "Id" { + ct = "*Id" + } return ct } - func (t *Type) CType() string { return t._CType(false) } @@ -185,6 +216,10 @@ func (t *Type) CTypeAttrib() string { } func (t *Type) _CType(attrib bool) string { + if t == nil { + fmt.Println("nil sent to _CType()") + return "" + } //if !attrib && c.ctype != "" ... FIXME? if t.ctype != "" { // cache return t.ctype @@ -196,9 +231,9 @@ func (t *Type) _CType(attrib bool) string { } else { ct = t.Node.CtypeSimplified() } - if len(ct) > 1 && ct[:2] == "id" { - ct = "NSObject*" + ct[2:] - } + ct = r_id.ReplaceAllString(ct,"NSObject*") + ct = r_instancename.ReplaceAllString(ct,t.Class) + ct = r_instancetype.ReplaceAllString(ct,t.Class + "*") if attrib { t._CType(false) } else { @@ -261,12 +296,23 @@ func (o *%s) Ptr() unsafe.Pointer { return unsafe.Pointer(o) } } 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) ReturnType() *Type { + if rt := t.Node.ReturnType(); rt != nil { + return NewType(rt,t.Class) + } + return nil +} + func (t *Type) IsPointer() bool { if td := t.Typedef(); td != nil { return td.IsPointer() @@ -283,6 +329,10 @@ func (t *Type) CToGo(cval string) string { // cast C value to CGo // Call a C function from Go with a given return type and parameter types func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type) string { + if rtype == nil { + fmt.Println("nil sent to GoToC") + return "" + } var ret strings.Builder rt := rtype.CType() if rt != "void" { @@ -290,8 +340,8 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type) string { if isGoInterface(rtgt) { rtgt = "*Id" } - ret.WriteString(" return (" + rtgt + ")(") - if rtype.Node.IsPointer() { + ret.WriteString("return (" + rtgt + ")(") + if rtype.IsPointer() { ret.WriteString("unsafe.Pointer(") } } @@ -306,7 +356,7 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type) string { switch { case pt.Variadic: p = "unsafe.Pointer(&" + p + ")" - case pt.Node.IsPointer(): + case pt.IsPointer(): p = "unsafe.Pointer(" + pn + ")" default: p = "(" + pt.CGoType() + ")(" + pn + ")" @@ -318,7 +368,7 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type) string { ret.WriteString(")") if rt != "void" { ret.WriteString(")") - if rtype.Node.IsPointer() { + if rtype.IsPointer() { ret.WriteString(")") } ret.WriteString("\n") diff --git a/types/cparser.go b/types/cparser.go index 0171ec9..ce7f629 100644 --- a/types/cparser.go +++ b/types/cparser.go @@ -110,7 +110,7 @@ func AbstractDeclarator(s string, n *Node) (string, *Node) { Opt(Pointer), OneOrMore(DirectAbstractDeclarator)), Pointer, - Id, + //Id, Block, )(s,n) } diff --git a/types/main.go b/types/main.go index 0e01f32..24fbedc 100644 --- a/types/main.go +++ b/types/main.go @@ -97,7 +97,7 @@ func (n *Node) PointsTo() *Node { //IsPointer returns true if the node is a pointer func (n *Node) IsPointer() bool { - return n.IsId() || n.PointsTo() != nil + return n.IsId() || n.IsInstancetype() || n.PointsTo() != nil } //ArrayOf, when called on an array node returns a node describing the type @@ -127,11 +127,29 @@ func (n *Node) IsFunction() bool { return n.Children[len(n.Children)-1].Kind == "Function" } +func (n *Node) ReturnType() *Node { + if !n.IsFunction() { + return nil + } + ret := NewNode(n.Kind) + ret.Children = n.Children[:len(n.Children)-1] + return ret +} + func (n *Node) IsId() bool { if n == nil || len(n.Children) < 1 { return false } - return n.Children[0].Kind == "TypedefName" && n.Children[0].Content == "id" + return n.Children[0].Kind == "TypedefName" && + n.Children[0].Content == "id" +} + +func (n *Node) IsInstancetype() bool { + if n == nil || len(n.Children) < 1 { + return false + } + return n.Children[0].Kind == "TypedefName" && + n.Children[0].Content == "instancetype" } //BaseType strips off all layers of pointer indirection diff --git a/wrap/main.go b/wrap/main.go index d2d1484..0ad6995 100644 --- a/wrap/main.go +++ b/wrap/main.go @@ -17,7 +17,8 @@ var ( type Wrapper struct { Package string - Interfaces map[string]Interface + Interfaces map[string]*Interface + Functions map[string]*Method cCode strings.Builder // put cGo code here goTypes strings.Builder // put Go type declarations here @@ -31,7 +32,8 @@ func NewWrapper(debug bool) *Wrapper { Debug = debug if Debug { fmt.Println("// Debug mode") } ret := &Wrapper{ - Interfaces: map[string]Interface{}, + Interfaces: map[string]*Interface{}, + Functions: map[string]*Method{}, Processed: map[string]bool{}, VaArgs: 16, } @@ -80,7 +82,7 @@ type Method struct { Name, Class string Type *types.Type ClassMethod bool - Parameters []Parameter + Parameters []*Parameter } //isVoid() returns true if the method has no return value. @@ -98,24 +100,26 @@ func (m Method) hasFunctionParam() bool { return false } -func (w Wrapper) cparamlist(m Method) string { +func (w Wrapper) cparamlist(m *Method) (string,string) { + ns := make([]string,0) ret := make([]string,0) if !m.ClassMethod { - ret = append(ret,"void* obj") + ret = append(ret,"void* o") } for _,p := range m.Parameters { var tp string - if p.Type.Node.IsPointer() || p.Type.Variadic { + if p.Type.IsPointer() || p.Type.Variadic { tp = "void*" } else { tp = p.Type.CType() } + ns = append(ns,p.Vname) ret = append(ret,fmt.Sprintf("%s %s",tp,p.Vname)) } - return strings.Join(ret,", ") + return strings.Join(ns,", "),strings.Join(ret,", ") } -func (w Wrapper) objcparamlist(m Method) string { +func (w Wrapper) objcparamlist(m *Method) string { if len(m.Parameters) == 0 { return m.Name } @@ -182,8 +186,8 @@ func (m *Method) gpntp() ([]string,[]*types.Type,string) { type Interface struct { Name string - Properties map[string]Property - Methods map[string]Method + Properties map[string]*Property + Methods map[string]*Method } func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) { @@ -203,14 +207,52 @@ func (w *Wrapper) AddCategory(n *ast.ObjCCategoryDecl) { fmt.Printf("Not adding methods for %s: interface name not found in first child node of category defclaration\n",n.Name) } +func (w *Wrapper) AddFunction(n *ast.FunctionDecl) { + //treat functions as class methods with no class + tp := types.NewTypeFromString(n.Type,"") + m := &Method{ + Name: n.Name, + Type: tp.ReturnType(), + Class: "", + ClassMethod: true, + Parameters: []*Parameter{}, + } + f := tp.Node.Children[len(tp.Node.Children)-1] // Function node + if f.Kind != "Function" { + //fmt.Printf("AddFunction(%s): not a function -- Node type is %s\n%s",n.Name,f.Kind,tp.String()) + return + } + //fmt.Printf("FunctionDecl: %s (%s) %s\n",n.Type,m.Type.CType(),n.Name) + i := 0 + for _,c := range n.Children() { + switch x := c.(type) { + case *ast.ParmVarDecl: + p := &Parameter{ + Vname: x.Name, + Type: types.NewTypeFromString(x.Type,""), + } + m.Parameters = append(m.Parameters,p) + i++ + //fmt.Printf(" %s\n",p.Type.CType()) + } + } + if i > 0 && len(f.Children) > i { + if e := f.Children[i]; len(e.Children) > 0 { + //fmt.Println(" Next parameter: ",e.Children[0].String()) + m.Parameters[i-1].Type.Variadic = true + } + } + w.Functions[n.Name] = m +} + func (w *Wrapper) add(name string, ns []ast.Node) { - var i Interface + var i *Interface var ok bool if i,ok = w.Interfaces[name]; !ok { - i = Interface{ + i = &Interface{ Name: name, - Properties: map[string]Property{}, - Methods: map[string]Method{}, + Properties: map[string]*Property{}, + Methods: map[string]*Method{}, } } tp := types.NewTypeFromString(name,name) @@ -220,7 +262,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) { switch x := c.(type) { case *ast.ObjCPropertyDecl: //fmt.Printf("ObjCPropertyDecl: %s\n",x.Name) - p := Property{ + p := &Property{ Name: x.Name, Type: types.NewTypeFromString(x.Type,name), } @@ -230,7 +272,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) { //} case *ast.ObjCMethodDecl: //fmt.Printf("ObjCMethodDecl: %s (%s) %s\n",x.Type,name,x.Name) - m := Method{ + m := &Method{ Name: x.Name, Type: types.NewTypeFromString(x.Type,name), Class: name, @@ -257,6 +299,37 @@ func (w *Wrapper) add(name string, ns []ast.Node) { //fmt.Println(reflect.TypeOf(x)) } } + //Add class methods from super class + var supmethods func(*Interface,string) + supmethods = func(i *Interface, s string) { + if sup,ok := w.Interfaces[s]; !ok { + return + } else { + for _,m := range sup.Methods { + if !m.ClassMethod { + continue + } + m2 := &Method{ + Name: m.Name, + Class: i.Name, + Type: m.Type.CloneToClass(i.Name), + ClassMethod: true, + Parameters: []*Parameter{}, + } + for _,p := range m.Parameters { + m2.Parameters = append(m2.Parameters, + &Parameter{ + Pname: p.Pname, + Vname: p.Vname, + Type: p.Type.CloneToClass(i.Name), + }) + } + i.Methods[m.Name] = m2 + } + } + supmethods(i,types.Super(s)) + } + supmethods(i,types.Super(i.Name)) //fmt.Println("Add interface ",i.Name) w.Interfaces[i.Name] = i } @@ -269,18 +342,28 @@ type AvailAttr struct { //GetParms returns the parameters of a method declaration and a bool //indicating whether the given method is available on MacOS and not //deprecated. -func (w *Wrapper) GetParms(n *ast.ObjCMethodDecl,class string) ([]Parameter,bool) { - ret := make([]Parameter,0) +func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) { + ret := make([]*Parameter,0) avail := make([]AvailAttr,0) + var parms []string + switch x := n.(type) { + case *ast.ObjCMethodDecl: + parms = x.Parameters + case *ast.FunctionDecl: + default: + panic("GetParms called with wrong node type") + } j := 0 for _,c := range n.Children() { switch x := c.(type) { case *ast.ParmVarDecl: - p := Parameter{ - Pname: n.Parameters[j], + p := &Parameter{ Vname: x.Name, Type: types.NewTypeFromString(x.Type,class), } + if parms != nil { + p.Pname = parms[j] + } ret = append(ret,p) j++ case *ast.Variadic: @@ -316,9 +399,9 @@ func (w *Wrapper) GetParms(n *ast.ObjCMethodDecl,class string) ([]Parameter,bool return nil, false } // check that we found the right number of parameters - if len(ret) != len(n.Parameters) { - fmt.Printf("Error in method declaration %s: Wrong number of ParmVarDecl children: %d parameters but %d ParmVarDecl children\n",n.Name,len(n.Parameters),len(ret)) - } + //if len(ret) != len(n.Parameters) { + // fmt.Printf("Error in method declaration %s: Wrong number of ParmVarDecl children: %d parameters but %d ParmVarDecl children\n",n.Name,len(n.Parameters),len(ret)) + //} return ret, true } @@ -378,6 +461,91 @@ func (c *Char) String() string { `) } +func (w *Wrapper) ProcessMethod(m *Method) { + w._processMethod(m,false) +} + +func (w *Wrapper) ProcessFunction(m *Method) { + w._processMethod(m,true) +} + +func (w *Wrapper) _processMethod(m *Method,fun bool) { + if Debug { + fmt.Printf(" method: %s (%s)\n", m.Name, m.Type) + } + if m.Type.IsFunction() || m.hasFunctionParam() { + return + } + gname := strings.Title(m.Name) + if !m.ClassMethod { + gname = "(o *" + m.Class + ") " + gname + } else { + gname = m.Class + gname + } + cname := m.Name + if m.Class != "" { + cname = m.Class + "_" + cname + } + + cmtype := m.Type.CTypeAttrib() + ns,tps,gplist := m.gpntp() + w.processTypes(tps) + w.processType(m.Type) + grtype := m.Type.GoType() + if grtype == "Void" { + grtype = "" + } + w.goCode.WriteString(fmt.Sprintf(` +//%s +func %s(%s) %s { +`,m.Type.CType(),gname,gplist,grtype)) + lparm := len(tps)-1 + if len(tps) > 0 && tps[lparm].Variadic { + vn := ns[lparm] + vn = vn[:len(vn)-1] + ns[lparm] = vn + w.goCode.WriteString(fmt.Sprintf( +` var %s [%d]unsafe.Pointer + for i,o := range %ss { + %s[i] = o.Ptr() + } +`,vn,w.VaArgs,vn,vn)) + } + w.goCode.WriteString(` ` + + types.GoToC(cname,ns,m.Type,tps) + "\n}\n\n") + + cret := "" + if !m.isVoid() { + cret = "return " + } + var cobj string + if m.ClassMethod { + cobj = m.Class + } else { + cobj = "(id)o" + } + cns,cntps := w.cparamlist(m) + _ = cns + if fun { + return + } + w.cCode.WriteString(fmt.Sprintf(` +%s +%s(%s) { +`, cmtype, cname, cntps)) + if len(tps) > 0 && tps[lparm].Variadic { + w.cCode.WriteString(fmt.Sprintf( +` %s* arr = %s; +`, tps[lparm].CType(), ns[lparm])) + } + if fun { + w.cCode.WriteString(fmt.Sprintf(` %s%s(%s); +}`, cret, m.Name, cns)) + } else { + w.cCode.WriteString(fmt.Sprintf(` %s[%s %s]; +}`, cret, cobj, w.objcparamlist(m))) + } +} func (w *Wrapper) Wrap(toproc []string) { if w.Package == "" { w.Package = "ns" } @@ -392,7 +560,7 @@ func (w *Wrapper) Wrap(toproc []string) { os.Exit(-1) } fmt.Printf("Writing output to %s\n",path.Join(w.Package,"main.go")) - pInterfaces := map[string]Interface{} + pInterfaces := map[string]*Interface{} for _,iface := range toproc { pInterfaces[iface] = w.Interfaces[iface] } @@ -422,71 +590,15 @@ New%s() { } //FIXME: sort methods for _,m := range i.Methods { - if Debug { - fmt.Printf(" method: %s (%s)\n", m.Name, m.Type) - } - if m.Type.IsFunction() { - continue - } - if m.hasFunctionParam() { - continue - } - gname := strings.Title(m.Name) - if !m.ClassMethod { - gname = "(o *" + i.Name + ") " + gname - } - cname := i.Name + "_" + m.Name + w.ProcessMethod(m) - cmtype := m.Type.CTypeAttrib() - ns,tps,gplist := m.gpntp() - w.processTypes(tps) - w.processType(m.Type) - grtype := m.Type.GoType() - if grtype == "Void" { - grtype = "" - } - w.goCode.WriteString(fmt.Sprintf(` -//%s -func %s(%s) %s { -`,m.Type.CType(),gname,gplist,grtype)) - lparm := len(tps)-1 - if len(tps) > 0 && tps[lparm].Variadic { - vn := ns[lparm] - vn = vn[:len(vn)-1] - ns[lparm] = vn - w.goCode.WriteString(fmt.Sprintf( -` var %s [%d]unsafe.Pointer - for i,o := range %ss { - %s[i] = o.Ptr() - } -`,vn,w.VaArgs,vn,vn)) - } - w.goCode.WriteString( - types.GoToC(cname,ns,m.Type,tps) + "}\n\n") - - cret := "" - if !m.isVoid() { - cret = "return " - } - var cobj string - if m.ClassMethod { - cobj = i.Name - } else { - cobj = "(id)obj" - } - w.cCode.WriteString(fmt.Sprintf(` -%s -%s(%s) { -`, cmtype, cname, w.cparamlist(m))) - if len(tps) > 0 && tps[lparm].Variadic { - w.cCode.WriteString(fmt.Sprintf( -` %s* arr = %s; -`, tps[lparm].CType(), ns[lparm])) - } - w.cCode.WriteString(fmt.Sprintf(` %s[%s %s]; -}`, cret, cobj, w.objcparamlist(m))) } } + for _,m := range w.Functions { + //fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name) + w.ProcessFunction(m) + } + fmt.Printf("%d functions\n", len(w.Functions)) of.WriteString("package " + w.Package + "\n\n") of.WriteString(w.cCode.String()) of.WriteString(`