Create GoString helper methods for anything that has a "WithString"

method. Multiple bug fixes in type system, fixed parsing of id,
instancename and instancetype, allow NullableAnnotations after
generic lists (e.g. "id<ObjectType> _Nullable"). Helper function
to identify types that are function pointers.
This commit is contained in:
Greg 2019-05-03 13:14:30 -04:00
parent 8ea3d57cf0
commit eced251710
9 changed files with 103 additions and 33 deletions

View File

@ -11,7 +11,8 @@ func main() {
fmt.Println("LE capable:",cd.IsLECapableHardware()) fmt.Println("LE capable:",cd.IsLECapableHardware())
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
fmt.Println("LE capable:",cd.IsLECapableHardware()) fmt.Println("LE capable:",cd.IsLECapableHardware())
uuid := ble.CBUUIDWithString(ble.NSStringWithUTF8String(ble.CharFromString("180d"))) uuid := ble.CBUUIDWithGoString("180d")
cd.ScanFor(uuid) cd.ScanFor(uuid)
time.Sleep(time.Second * 15)
select { }
} }

View File

@ -8,12 +8,12 @@ import (
) )
func main() { func main() {
n1 := ns.NSStringWithUTF8String(ns.CharFromString("hi there")) n1 := ns.NSStringWithUTF8String(ns.CharWithGoString("hi there"))
c1 := n1.CapitalizedString() c1 := n1.CapitalizedString()
gs := c1.UTF8String().String() gs := c1.UTF8String().String()
fmt.Println(gs) fmt.Println(gs)
n2 := ns.NSStringWithUTF8String(ns.CharFromString("hi world")) n2 := ns.NSStringWithGoString("hi world")
n3 := ns.NSStringWithUTF8String(ns.CharFromString("ok bye")) n3 := ns.NSStringWithGoString("ok bye")
a := ns.NSMutableArrayWithObjects(n1,n2,n3) a := ns.NSMutableArrayWithObjects(n1,n2,n3)
fmt.Println("Length(a) = ",a.Count()) fmt.Println("Length(a) = ",a.Count())
fmt.Println("is n2 in a?",a.ContainsObject(n2)) fmt.Println("is n2 in a?",a.ContainsObject(n2))

View File

@ -22,6 +22,6 @@ Functions = [
Enums = [ Enums = [
"CF.*", "CF.*",
] ]
SysImports = [ "Foundation/Foundation.h" ] Frameworks = [ "Foundation" ]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ] Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]
VaArgs = 32 VaArgs = 32

View File

@ -2,3 +2,4 @@ Package = "ClassOne"
InputFiles = [ "ClassOne/simple.h" ] InputFiles = [ "ClassOne/simple.h" ]
Classes = [ "ClassOne","ClassTwo" ] Classes = [ "ClassOne","ClassTwo" ]
Imports = [ "simple.h" ] Imports = [ "simple.h" ]
Frameworks = [ "Foundation" ]

View File

@ -149,7 +149,6 @@ func matches(x string, rs []string) bool {
// Start begins transpiling an input file. // Start begins transpiling an input file.
func Start() (err error) { func Start() (err error) {
// 1. Compile it first (checking for errors)
for _, in := range Config.InputFiles { for _, in := range Config.InputFiles {
_, err := os.Stat(in) _, err := os.Stat(in)
if err != nil { if err != nil {
@ -159,10 +158,11 @@ func Start() (err error) {
// 2. Preprocess NOT DONE // 2. Preprocess NOT DONE
// 3. Generate JSON from AST // 3. Generate AST
cargs := []string{"-xobjective-c", "-Xclang", "-ast-dump", cargs := []string{"-xobjective-c", "-Xclang", "-ast-dump",
"-fsyntax-only","-fno-color-diagnostics"} "-fsyntax-only","-fno-color-diagnostics"}
cargs = append(cargs,Config.InputFiles...) cargs = append(cargs,Config.InputFiles...)
fmt.Printf("Generating AST\n")
astPP, err := exec.Command("clang",cargs...).Output() astPP, err := exec.Command("clang",cargs...).Output()
if err != nil { if err != nil {
// If clang fails it still prints out the AST, so we have to run it // If clang fails it still prints out the AST, so we have to run it
@ -175,9 +175,11 @@ func Start() (err error) {
lines := readAST(astPP) lines := readAST(astPP)
// Converting to nodes // Converting to nodes
fmt.Printf("Building nodes\n")
nodes := convertLinesToNodesParallel(lines) nodes := convertLinesToNodesParallel(lines)
// build tree // build tree
fmt.Printf("Assembling tree\n")
tree := buildTree(nodes, 0) tree := buildTree(nodes, 0)
w := wrap.NewWrapper(Debug) w := wrap.NewWrapper(Debug)
w.Package = Config.Package w.Package = Config.Package
@ -190,6 +192,7 @@ func Start() (err error) {
} }
w.VaArgs = Config.VaArgs w.VaArgs = Config.VaArgs
for _, u := range tree { for _, u := range tree {
fmt.Printf("--processing translation unit\n")
for _, n := range(u.Children()) { for _, n := range(u.Children()) {
switch x := n.(type) { switch x := n.(type) {
case *ast.ObjCInterfaceDecl: case *ast.ObjCInterfaceDecl:

View File

@ -50,7 +50,7 @@ func init() {
TypeParameters = make(map[string]map[string]string) TypeParameters = make(map[string]map[string]string)
typedefs = make(map[string]*Type) typedefs = make(map[string]*Type)
r_id = regexp.MustCompile("\bid\b") r_id = regexp.MustCompile(`\bid\b`)
r_instancename = regexp.MustCompile(`\binstancename\b`) r_instancename = regexp.MustCompile(`\binstancename\b`)
r_instancetype = regexp.MustCompile(`\binstancetype\b`) r_instancetype = regexp.MustCompile(`\binstancetype\b`)
} }
@ -117,7 +117,6 @@ func NewType(n *Node, c string) *Type {
return &Type{ return &Type{
Node: n2, Node: n2,
Class: c, Class: c,
//ctype: "",
} }
} }
@ -125,19 +124,16 @@ func NewTypeFromString(t,c string) *Type {
//fmt.Printf("t/c: %s/%s\n",t,c) //fmt.Printf("t/c: %s/%s\n",t,c)
n,err := Parse(t) n,err := Parse(t)
//fmt.Printf("%p %s",n,n.String()) //fmt.Printf("%p %s",n,n.String())
if n.IsId() {
n,err = Parse("NSObject*")
}
if err != nil { if err != nil {
return &Type{} return &Type{}
} }
if n2,ok := clean(n, c); ok { if n2,ok := clean(n, c); ok {
//found type parameters, re-parse
return NewTypeFromString(n2.Ctype(),c) return NewTypeFromString(n2.Ctype(),c)
} }
return &Type{ return &Type{
Node: n, Node: n,
Class: c, Class: c,
//ctype: "",
} }
} }
@ -219,9 +215,9 @@ func (t *Type) _CType(attrib bool) string {
//fmt.Println("nil sent to _CType()") //fmt.Println("nil sent to _CType()")
return "" return ""
} }
if !attrib && t.ctype != "" { // cache //if !attrib && t.ctype != "" { // cache
return t.ctype // return t.ctype
} //}
var ct string var ct string
if attrib { if attrib {
ignore := map[string]bool { "GenericList": true } ignore := map[string]bool { "GenericList": true }
@ -296,6 +292,19 @@ func (o *Id) %s() *%s { return (*%s)(unsafe.Pointer(o)) }
`,t.Node.Ctype(),t.BaseType().GoType(),gt,super,gt,gt,gt,gt) `,t.Node.Ctype(),t.BaseType().GoType(),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()
}
for pt := t.PointsTo(); pt != nil; pt = pt.PointsTo() {
return pt.IsFunction()
}
return false
}
func (t *Type) IsFunction() bool { func (t *Type) IsFunction() bool {
if t == nil { if t == nil {
fmt.Println("nil sent to IsFunction()") fmt.Println("nil sent to IsFunction()")

View File

@ -81,21 +81,31 @@ digit:
[0-9] [0-9]
*/ */
import (
"regexp"
)
var TypeName func(s string, n *Node) (string, *Node) var TypeName func(s string, n *Node) (string, *Node)
func init() { func init() {
instancename := regexp.MustCompile("instancename")
instancetype := regexp.MustCompile("instancetype")
cacheable := func(s string) bool {
return !instancetype.MatchString(s) && !instancename.MatchString(s)
}
cache := map[string]*Node{} cache := map[string]*Node{}
TypeName = func(s string, n *Node) (string, *Node) { TypeName = func(s string, n *Node) (string, *Node) {
if n2,ok := cache[s]; ok { if n2,ok := cache[s]; ok {
return "",n2 return "",n2
} }
s2,n2 := _TypeName(s,n) s2,n2 := _TypeName(s,n)
if s2 == "" { if s2 == "" && cacheable(s) {
cache[s] = n2 cache[s] = n2
} }
return s2,n2 return s2,n2
} }
TypeName = _TypeName //TypeName = _TypeName
} }
func _TypeName(s string, n *Node) (string, *Node) { func _TypeName(s string, n *Node) (string, *Node) {
@ -318,10 +328,10 @@ func BareTypedefName(s string, n *Node) (string, *Node) {
} }
func TypedefName(s string, n *Node) (string, *Node) { func TypedefName(s string, n *Node) (string, *Node) {
return OneOf( return Seq(
Seq(BareTypedefName, AngBracketed(GenericList)),
Seq(BareTypedefName, NullableAnnotation),
BareTypedefName, BareTypedefName,
Opt(AngBracketed(GenericList)),
Opt(NullableAnnotation),
)(s,n) )(s,n)
} }

View File

@ -53,7 +53,7 @@ func (n *Node) stripAbstract(k string) *Node {
return nil return nil
} }
ret := NewNode(n.Kind) ret := NewNode(n.Kind)
cs := n.Children[:] cs := append([]*Node{},n.Children...)
dbg("stripAbstract(): i = %d\n",i) dbg("stripAbstract(): i = %d\n",i)
//Scan backwords skipping TypeQualifier and NullableAnnotation tags //Scan backwords skipping TypeQualifier and NullableAnnotation tags
@ -97,7 +97,10 @@ func (n *Node) PointsTo() *Node {
//IsPointer returns true if the node is a pointer //IsPointer returns true if the node is a pointer
func (n *Node) IsPointer() bool { func (n *Node) IsPointer() bool {
return n.IsId() || n.IsInstancetype() || n.PointsTo() != nil if pt := n.PointsTo(); pt != nil {
return true
}
return n.IsInstancetype() || n.IsId()
} }
//ArrayOf, when called on an array node returns a node describing the type //ArrayOf, when called on an array node returns a node describing the type
@ -124,6 +127,9 @@ func (n *Node) IsFunction() bool {
if n == nil || len(n.Children) < 1 { if n == nil || len(n.Children) < 1 {
return false return false
} }
if pt := n.PointsTo(); pt != nil {
return false
}
return n.Children[len(n.Children)-1].Kind == "Function" return n.Children[len(n.Children)-1].Kind == "Function"
} }
@ -140,7 +146,8 @@ func (n *Node) IsId() bool {
if n == nil || len(n.Children) < 1 { if n == nil || len(n.Children) < 1 {
return false return false
} }
return n.Children[0].Kind == "TypedefName" && return !n.IsFunction() &&
n.Children[0].Kind == "TypedefName" &&
n.Children[0].Content == "id" n.Children[0].Content == "id"
} }

View File

@ -113,7 +113,7 @@ func (m Method) isVoid() bool {
//hasFunctionParam() returns true if a method has a function as a parameter. //hasFunctionParam() returns true if a method has a function as a parameter.
func (m Method) hasFunctionParam() bool { func (m Method) hasFunctionParam() bool {
for _,p := range m.Parameters { for _,p := range m.Parameters {
if p.Type.IsFunction() { if p.Type.IsFunction() || p.Type.IsFunctionPtr() {
return true return true
} }
} }
@ -521,7 +521,7 @@ func (w *Wrapper) processType(tp *types.Type) {
if gt == "NSEnumerator" { if gt == "NSEnumerator" {
w.EnumeratorHelpers() w.EnumeratorHelpers()
} }
if bt.IsFunction() { if bt.IsFunction() || bt.IsFunctionPtr() {
return return
} }
super := types.Super(gt) super := types.Super(gt)
@ -535,7 +535,7 @@ func (w *Wrapper) processType(tp *types.Type) {
func (w *Wrapper) CharHelpers() { func (w *Wrapper) CharHelpers() {
w.goHelpers.WriteString(` w.goHelpers.WriteString(`
func CharFromString(s string) *Char { func CharWithGoString(s string) *Char {
return (*Char)(unsafe.Pointer(C.CString(s))) return (*Char)(unsafe.Pointer(C.CString(s)))
} }
@ -555,7 +555,8 @@ func (e *NSEnumerator) ForIn(f func(*Id) bool) {
for o := e.NextObject(); o != nil; o = e.NextObject() { for o := e.NextObject(); o != nil; o = e.NextObject() {
if !f(o) { break } if !f(o) { break }
} }
}`) }
`)
} }
func (w *Wrapper) ProcessMethod(m *Method) { func (w *Wrapper) ProcessMethod(m *Method) {
@ -570,13 +571,12 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
if Debug { if Debug {
fmt.Printf(" method: %s (%s)\n", m.Name, m.Type) fmt.Printf(" method: %s (%s)\n", m.Name, m.Type)
} }
if m.Type.IsFunction() || m.hasFunctionParam() { if m.Type.IsFunction() || m.Type.IsFunctionPtr() || m.hasFunctionParam() {
return return
} }
gname := strings.Title(m.Name) gname := strings.Title(m.Name)
switch { switch {
case !m.ClassMethod: case !m.ClassMethod:
gname = "(o *" + m.GoClass + ") " + gname
case m.Type.GoType() != "*" + m.GoClass: case m.Type.GoType() != "*" + m.GoClass:
gname = m.GoClass + gname gname = m.GoClass + gname
default: default:
@ -592,6 +592,10 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
gname = m.GoClass + gname[lens1-i:] gname = m.GoClass + gname[lens1-i:]
} }
} }
receiver := ""
if !m.ClassMethod {
receiver = "(o *" + m.GoClass + ") "
}
cname := m.Name cname := m.Name
if m.Class != "" { if m.Class != "" {
cname = m.Class + "_" + cname cname = m.Class + "_" + cname
@ -611,8 +615,8 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
} }
w.goCode.WriteString(fmt.Sprintf(` w.goCode.WriteString(fmt.Sprintf(`
//%s //%s
func %s(%s) %s { func %s%s(%s) %s {
`,m.Type.CType(),gname,gplist,grtype)) `,m.Type.CType(),receiver,gname,gplist,grtype))
lparm := len(tps)-1 lparm := len(tps)-1
if len(tps) > 0 && tps[lparm].Variadic { if len(tps) > 0 && tps[lparm].Variadic {
vn := ns[lparm] vn := ns[lparm]
@ -659,7 +663,42 @@ func %s(%s) %s {
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s]; w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
}`, cret, cobj, w.objcparamlist(m))) }`, cret, cobj, w.objcparamlist(m)))
} }
// create GoString helper method
if ok,_ := regexp.MatchString("WithString$",m.Name); ok {
//fmt.Printf("--%s\n",gname)
gname2 := gname[:len(gname)-6] + "GoString"
gps := []string{}
i := 0
if !m.ClassMethod { i = 1 }
for ; i < len(ns); i++ {
gt := tps[i].GoType()
//fmt.Printf(" %s\n",gt)
ns2 := ns[i]
if gt == "*NSString" {
gt = "string"
ns[i] = gStringToNsstring(ns[i])
}
gps = append(gps,ns2 + " " + gt)
}
gplist = strings.Join(gps,", ")
obj := ""
if !m.ClassMethod {
obj = "o."
ns = ns[1:]
}
w.goCode.WriteString(fmt.Sprintf(`
func %s%s(%s) %s {
return %s%s(%s)
} }
`,receiver,gname2,gplist,grtype,obj,gname,strings.Join(ns,", ")))
}
}
func gStringToNsstring(s string) string {
return fmt.Sprintf("NSStringWithUTF8String(CharWithGoString(%s))",s)
}
func (w *Wrapper) ProcessEnum(e *Enum) { func (w *Wrapper) ProcessEnum(e *Enum) {
gtp := "" gtp := ""