Generate go and objc wrapper functions. Translate classes into wrapped

unsafe.Pointer() instances. Embed super-class to simulate inheritance
and hold pointers only at the base class.
This commit is contained in:
Greg 2019-04-10 14:00:48 -04:00
parent bcb4a0680d
commit 2fbd9134a0
6 changed files with 309 additions and 34 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
clast clast
ast.txt ast.txt
*.ast *.ast
simple

View File

@ -188,12 +188,14 @@ func Parse(fullline string) Node {
return parseNotTailCalledAttr(line) return parseNotTailCalledAttr(line)
case "ObjCCategoryDecl": case "ObjCCategoryDecl":
return parseObjCCategoryDecl(line) return parseObjCCategoryDecl(line)
case "ObjCImplementation":
return parseObjCImplementation(line)
case "ObjCInterface": case "ObjCInterface":
return parseObjCInterface(line) return parseObjCInterface(line)
case "ObjCInterfaceType": case "ObjCInterfaceType":
return parseObjCInterfaceType(line) return parseObjCInterfaceType(line)
case "super ObjCInterface": case "super ObjCInterface":
return parseObjCInterface(line) return parseSuperObjCInterface(line)
case "ObjCInterfaceDecl": case "ObjCInterfaceDecl":
return parseObjCInterfaceDecl(line) return parseObjCInterfaceDecl(line)
case "getter ObjCMethod": case "getter ObjCMethod":

View File

@ -0,0 +1,44 @@
package ast
// ObjCImplementation is an Objective-C implementation
type ObjCImplementation struct {
Addr Address
Name string
ChildNodes []Node
}
func parseObjCImplementation(line string) *ObjCImplementation {
groups := groupsFromRegex(
"'(?P<name>.*)'",
line,
)
return &ObjCImplementation{
Addr: ParseAddress(groups["address"]),
Name: groups["name"],
ChildNodes: []Node{},
}
}
// AddChild adds a new child node. Child nodes can then be accessed with the
// Children attribute.
func (n *ObjCImplementation) AddChild(node Node) {
n.ChildNodes = append(n.ChildNodes, node)
}
// Address returns the numeric address of the node. See the documentation for
// the Address type for more information.
func (n *ObjCImplementation) Address() Address {
return n.Addr
}
// Children returns the child nodes. If this node does not have any children or
// this node does not support children it will always return an empty slice.
func (n *ObjCImplementation) Children() []Node {
return n.ChildNodes
}
// Position returns the position in the original source code.
func (n *ObjCImplementation) Position() Position {
return Position{}
}

View File

@ -4,9 +4,16 @@ package ast
type ObjCInterface struct { type ObjCInterface struct {
Addr Address Addr Address
Name string Name string
Super bool
ChildNodes []Node ChildNodes []Node
} }
func parseSuperObjCInterface(line string) *ObjCInterface {
ret := parseObjCInterface(line)
ret.Super = true
return ret
}
func parseObjCInterface(line string) *ObjCInterface { func parseObjCInterface(line string) *ObjCInterface {
groups := groupsFromRegex( groups := groupsFromRegex(
"'(?P<name>.*)'", "'(?P<name>.*)'",
@ -16,6 +23,7 @@ func parseObjCInterface(line string) *ObjCInterface {
return &ObjCInterface{ return &ObjCInterface{
Addr: ParseAddress(groups["address"]), Addr: ParseAddress(groups["address"]),
Name: groups["name"], Name: groups["name"],
Super: false,
ChildNodes: []Node{}, ChildNodes: []Node{},
} }
} }

View File

@ -212,7 +212,7 @@ func Start(args ProgramArgs) (err error) {
w.AddCategory(x) w.AddCategory(x)
} }
} }
w.Wrap() w.Wrap("ClassOne")
return nil return nil
} }

View File

@ -8,12 +8,45 @@ import (
"gitlab.wow.st/gmp/clast/ast" "gitlab.wow.st/gmp/clast/ast"
) )
var (
Debug = false
)
type Wrapper struct { type Wrapper struct {
Interfaces map[string]Interface Interfaces map[string]Interface
Types map[string]string Types map[string]string
cCode strings.Builder // put cGo code here
goTypes strings.Builder // put Go type declarations here
goCode strings.Builder // put Go code here
Processed map[string]bool
} }
// translate C builtin types to CGo // translate C builtin types to CGo
var builtinTypes map[string]string = map[string]string{
"char": "byte",
"signed char": "byte",
"unsigned char": "byte",
"short": "int",
"unsigned short": "int",
"int": "int",
"unsigned int": "int",
"long": "int",
"unsigned long": "int",
"long long": "int",
"unsigned long long": "int",
"float": "float",
"double": "float",
"complex float": "C.complexfloat",
"complex double": "C.complexdouble",
}
var gobuiltinTypes map[string]bool = map[string]bool{
"byte": true,
"int": true,
"float": true,
}
/*
var builtinTypes map[string]string = map[string]string{ var builtinTypes map[string]string = map[string]string{
"char": "C.char", "char": "C.char",
"signed char": "C.schar", "signed char": "C.schar",
@ -30,28 +63,42 @@ var builtinTypes map[string]string = map[string]string{
"double": "C.double", "double": "C.double",
"complex float": "C.complexfloat", "complex float": "C.complexfloat",
"complex double": "C.complexdouble", "complex double": "C.complexdouble",
} }*/
func (w *Wrapper) AddType(t string) { func (w *Wrapper) AddType(t string) {
// only for objects and pointers? if _,ok := builtinTypes[t]; ok {
return
}
nt, err := goType(t)
if err != nil {
return
}
w.Types[nt] = t
}
func goType(t string) (string, error) {
// strip off pointers, < > and blank space // strip off pointers, < > and blank space
nt := strings.ReplaceAll(t,"*","") nt := strings.ReplaceAll(t,"*","")
if len(nt) > 3 && nt[0:3] == "id<" { if len(nt) > 3 && nt[0:3] == "id<" {
nt = t[3:len(t)-1] nt = t[3:len(t)-1]
} }
nt = strings.ReplaceAll(nt,"<","_") nt = strings.ReplaceAll(nt,"<","__")
nt = strings.ReplaceAll(nt,">","_") nt = strings.ReplaceAll(nt,">","__")
if _,ok := builtinTypes[nt]; ok { // do not add builtin types nt = strings.ReplaceAll(nt,",","_")
return if x,ok := builtinTypes[nt]; ok { // do not add builtin types
return x, nil
} }
if len(nt) > 5 && nt[0:5] == "enum " { // FIXME: deal with enums? if len(nt) > 5 && nt[0:5] == "enum " { // FIXME: deal with enums?
return return "", fmt.Errorf("goType(): enum")
} }
nt = strings.ReplaceAll(nt," ","") nt = strings.ReplaceAll(nt," ","")
if nt == "void" { if nt == "void" {
return return "", fmt.Errorf("goType(): void")
} }
w.Types[nt] = t if nt[len(nt)-1] == ')' { // skip function pointers
return "", fmt.Errorf("goType(): function pointer")
}
return nt, nil
} }
func NewWrapper() *Wrapper { func NewWrapper() *Wrapper {
@ -74,12 +121,64 @@ type Method struct {
Parameters map[string]Parameter Parameters map[string]Parameter
} }
func (m Method) isVoid() bool {
return typeOrType2(m.Type,m.Type2) == "void"
}
func (m Method) cparamlist() string {
ret := []string{"void* obj"}
for k,v := range m.Parameters {
ret = append(ret,fmt.Sprintf("%s %s",typeOrType2(v.Type,v.Type2),k))
}
return strings.Join(ret,", ")
}
func (m Method) objcparamlist() string {
if len(m.Parameters) == 0 {
return m.Name
}
ret := []string{fmt.Sprintf("%s:",m.Name)}
for k,_ := range m.Parameters {
ret = append(ret, fmt.Sprintf("%s:%s",k,k))
}
return strings.Join(ret," ")
}
func (m Method) goparamlist() (string,[]string) {
ret := []string{}
tps := []string{}
for k,v := range m.Parameters {
tp,err := goType(typeOrType2(v.Type,v.Type2))
if err != nil {
return "UNSUPPORTED TYPE", []string{}
}
tps = append(tps,tp)
ret = append(ret,fmt.Sprintf("%s %s",k,tp))
}
return strings.Join(ret,", "),tps
}
func (m Method) goparamnames() string {
ret := []string{"o.ptr"}
for k,_ := range m.Parameters {
ret = append(ret,k)
}
return strings.Join(ret, ", ")
}
type Interface struct { type Interface struct {
Name string Name, Super string
Properties map[string]Property Properties map[string]Property
Methods map[string]Method Methods map[string]Method
} }
func (i Interface) IsRoot() bool {
if i.Super == "" || i.Super == i.Name {
return true
}
return false
}
func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) { func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) {
//fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name) //fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name)
w.add(n.Name, n.Children()) w.add(n.Name, n.Children())
@ -104,6 +203,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
Properties: map[string]Property{}, Properties: map[string]Property{},
Methods: map[string]Method{}, Methods: map[string]Method{},
} }
w.AddType(name)
} }
for _,c := range ns { for _,c := range ns {
switch x := c.(type) { switch x := c.(type) {
@ -116,7 +216,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
w.AddType(typeOrType2(x.Type,x.Type2)) w.AddType(typeOrType2(x.Type,x.Type2))
i.Properties[p.Name] = p i.Properties[p.Name] = p
case *ast.ObjCMethodDecl: case *ast.ObjCMethodDecl:
fmt.Printf("ObjcMethodDecl: %s\n",x.Name) //fmt.Printf("ObjCMethodDecl: %s\n",x.Name)
m := Method{ m := Method{
Name: x.Name, Name: x.Name,
Type: x.Type, Type: x.Type,
@ -128,7 +228,10 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
case *ast.ObjCProtocol: case *ast.ObjCProtocol:
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name) //fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
case *ast.ObjCInterface: case *ast.ObjCInterface:
//fmt.Printf("ast.ObjCInterface: %s\n",x.Name) if x.Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
i.Super = x.Name
}
case *ast.Unknown: case *ast.Unknown:
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content) //fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
default: default:
@ -192,29 +295,146 @@ func typeOrType2(t1, t2 string) string {
return t1 return t1
} }
func (w *Wrapper) Wrap() { func (w *Wrapper) ProcessType(gotype string) {
//var cCode strings.Builder if gotype == "" {
var goCode strings.Builder return
for k,t := range w.Types { // FIXME: SORT FIRST
w := fmt.Sprintf(`
// %s
type %s struct { ptr unsafe.Pointer }
`,t, k)
goCode.WriteString(w)
} }
for k,v := range w.Interfaces { if _,ok := gobuiltinTypes[gotype]; ok {
fmt.Printf("Interface %s: %d properties, %d methods\n", return
k, len(v.Properties), len(v.Methods)) }
for _,y := range v.Properties { ctype := w.Types[gotype]
fmt.Printf(" property: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2)) if _,ok := builtinTypes[ctype]; ok {
return
}
if Debug {
fmt.Printf("Processing %s (%s)\n",gotype,ctype)
}
if w.Processed[gotype] {
return
}
if i,ok := w.Interfaces[gotype]; ok {
if Debug {
fmt.Printf("Have interface for %s. super = %s\n",gotype,i.Super)
} }
for _,y := range v.Methods { if i.Name != i.Super {
fmt.Printf(" method: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2)) w.ProcessType(i.Super)
for _,z := range y.Parameters { }
fmt.Printf(" %s:%s\n", z.Name, typeOrType2(z.Type,z.Type2)) }
var fields string
// if there is an Interface known for this type, decide if it is the
// root object and if so give it a pointer element:
if i,ok := w.Interfaces[gotype]; ok && i.IsRoot() {
fields = "ptr unsafe.Pointer"
} else {
fields = i.Super // embed superclass
}
w.goTypes.WriteString(fmt.Sprintf(`
// %s
type %s struct { %s }
`,ctype, gotype, fields))
w.Processed[gotype] = true
}
func (w *Wrapper) ProcessTypes(tps []string) {
for _,tp := range tps {
w.ProcessType(tp)
}
}
func (w *Wrapper) Wrap(toproc string) {
w.Processed = make(map[string]bool)
w.cCode.WriteString(`/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework CoreBluetooth
`)
pInterfaces := map[string]Interface {toproc: w.Interfaces[toproc]}
for k,v := range pInterfaces {
if Debug {
fmt.Printf("Interface %s: %d properties, %d methods\n",
k, len(v.Properties), len(v.Methods))
}
w.goCode.WriteString(fmt.Sprintf(`
func New%s() *%s {
ret := &%s{}
ret.ptr = unsafe.Pointer(C.New%s())
return ret
}
`,v.Name,v.Name,v.Name,v.Name))
w.cCode.WriteString(fmt.Sprintf(`
%s*
New%s() {
return [[%s alloc] init];
}
`, v.Name, v.Name, v.Name))
for _,y := range v.Properties {
if Debug {
fmt.Printf(" property: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2))
} }
} }
for _,y := range v.Methods {
if Debug {
fmt.Printf(" method: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2))
}
gname := strings.Title(y.Name)
grtype := ""
grptr := ""
cmtype := typeOrType2(y.Type,y.Type2)
if !y.isVoid() {
var err error
grtype,err = goType(cmtype)
if err != nil {
grtype = fmt.Sprintf("// goType(%s): NOT IMPLEMENTED\n",cmtype)
}
}
gcast := ""
gcast2 := ""
if grtype != "" {
if _,ok := w.Interfaces[grtype]; ok {
grptr = "*"
gcast = "ret := &" + grtype + "{}\n ret.ptr = unsafe.Pointer("
gcast2 = ")\n return ret"
} else {
gcast = fmt.Sprintf("return (%s)(",grtype)
gcast2 = ")"
}
}
w.ProcessType(grtype)
gplist, gptypes := y.goparamlist()
w.ProcessTypes(gptypes)
w.goCode.WriteString(fmt.Sprintf(`
func (o *%s) %s(%s) %s%s {
%sC.%s_%s(%s)%s
}`,v.Name, gname, gplist, grptr, grtype, gcast, v.Name, y.Name, y.goparamnames(),gcast2))
cret := ""
if !y.isVoid() {
cret = "return "
}
w.cCode.WriteString(fmt.Sprintf(`
%s
%s_%s(%s) {
%s[(id)obj %s];
}`, cmtype, v.Name, y.Name, y.cparamlist(), cret, y.objcparamlist()))
}
} }
fmt.Println(goCode.String()) fmt.Println(`package main
`)
fmt.Println(w.cCode.String())
fmt.Println(`
*/
import "C"
import (
"unsafe"
)
`)
fmt.Println(w.goTypes.String())
fmt.Println(w.goCode.String())
} }