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:
parent
bcb4a0680d
commit
2fbd9134a0
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
clast
|
clast
|
||||||
ast.txt
|
ast.txt
|
||||||
*.ast
|
*.ast
|
||||||
|
simple
|
||||||
|
|
|
@ -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":
|
||||||
|
|
44
ast/objc_implementation.go
Normal file
44
ast/objc_implementation.go
Normal 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{}
|
||||||
|
}
|
|
@ -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{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
284
wrap/main.go
284
wrap/main.go
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user