The gobind and gomobile bind tools have historically overlapped: gobind outputs generated bindings, and gomobile bind will generate bindings before building them. However, the gobind bindings were never used for building and thus allowed to not be complete. To simplify version control, debugging, instrumentation and build system flexibility, this CL upgrades the gobind tool to be the canonical binding generator and change gomobile bind to use gobind instead of its own generator code. This greatly simplifies gomobile bind, but also paves the way to skip gomobile bind entirely. For example: $ gobind -outdir=$GOPATH golang.org/x/mobile/example/bind/hello $ GOOS=android GOARCH=arm64 CC=<ndk-toolchain>/bin/clang go build -buildmode=c-shared -o libgobind.so gobind $ ls libgobind.* libgobind.h libgobind.so The same applies to iOS, although the go build command line is more involved. By skipping gomobile it is possible to freely customize the Android or iOS SDK level or any other flags not supported by gomobile bind. By checking in the generated source code, the cost of supporting gomobile in a custom build system is also decreased. Change-Id: I59c14a77d625ac1377c23b3213672e0d83a48c85 Reviewed-on: https://go-review.googlesource.com/99316 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
755 lines
19 KiB
Go
755 lines
19 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package bind
|
|
|
|
import (
|
|
"path"
|
|
"strings"
|
|
|
|
"golang.org/x/mobile/internal/importers/objc"
|
|
)
|
|
|
|
type (
|
|
// ObjCWrapper generates Go and C stubs for ObjC interfaces and protocols.
|
|
ObjcWrapper struct {
|
|
*Printer
|
|
imported map[string]*objc.Named
|
|
// The list of ObjC types.
|
|
types []*objc.Named
|
|
// The list of Go package paths with ObjC wrappers.
|
|
pkgNames []string
|
|
modules []string
|
|
// For each ObjC module, the list of ObjC types within.
|
|
modMap map[string][]*objc.Named
|
|
// For each module/name Go package path, the ObjC type
|
|
// with static functions or constants.
|
|
typePkgs map[string]*objc.Named
|
|
// supers is the map of types that need Super methods.
|
|
supers map[string]struct{}
|
|
}
|
|
)
|
|
|
|
// Init initializes the ObjC types wrapper generator. Types is the
|
|
// list of types to wrap, genNames the list of generated type names.
|
|
func (g *ObjcWrapper) Init(types []*objc.Named, genNames []string) {
|
|
g.supers = make(map[string]struct{})
|
|
for _, s := range genNames {
|
|
g.supers[s] = struct{}{}
|
|
}
|
|
g.types = types
|
|
g.imported = make(map[string]*objc.Named)
|
|
g.modMap = make(map[string][]*objc.Named)
|
|
g.typePkgs = make(map[string]*objc.Named)
|
|
pkgSet := make(map[string]struct{})
|
|
for _, n := range types {
|
|
g.imported[n.GoName] = n
|
|
typePkg := n.Module + "/" + n.GoName
|
|
g.typePkgs[typePkg] = n
|
|
if !n.Generated {
|
|
if _, exists := g.modMap[n.Module]; !exists {
|
|
g.modules = append(g.modules, n.Module)
|
|
}
|
|
}
|
|
g.modMap[n.Module] = append(g.modMap[n.Module], n)
|
|
if _, exists := pkgSet[n.Module]; !exists {
|
|
pkgSet[n.Module] = struct{}{}
|
|
g.pkgNames = append(g.pkgNames, n.Module)
|
|
}
|
|
g.pkgNames = append(g.pkgNames, typePkg)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) GenM() {
|
|
g.Printf("//File is generated by gobind. Do not edit.\n\n")
|
|
// For objc_msgSend* functions.
|
|
g.Printf("@import ObjectiveC.message;\n")
|
|
g.Printf("#include \"seq.h\"\n")
|
|
g.Printf("#include \"interfaces.h\"\n\n")
|
|
for _, n := range g.types {
|
|
g.genM(n)
|
|
}
|
|
g.Printf("\n")
|
|
for _, n := range g.types {
|
|
for _, f := range n.AllMethods {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.genCFuncDecl("cproxy", n.GoName, f)
|
|
g.genCFuncBody(n, f, false)
|
|
if _, exists := g.supers[n.GoName]; exists {
|
|
g.genCFuncDecl("csuper", n.GoName, f)
|
|
g.genCFuncBody(n, f, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genCFuncBody(n *objc.Named, f *objc.Func, super bool) {
|
|
g.Printf(" {\n")
|
|
g.Indent()
|
|
if !f.Static {
|
|
g.Printf("%s _this = go_seq_from_refnum(this).obj;\n", n.ObjcType())
|
|
}
|
|
var errParam *objc.Param
|
|
for i, a := range f.Params {
|
|
if i == len(f.Params)-1 && g.isErrorType(a.Type) {
|
|
errParam = a
|
|
break
|
|
}
|
|
g.genCToObjC(a.Name, a.Type, modeTransient)
|
|
}
|
|
if errParam != nil {
|
|
g.Printf("NSError *%s = nil;\n", errParam.Name)
|
|
}
|
|
if f.Constructor {
|
|
g.Printf("%s _this = [%s alloc];\n", n.ObjcType(), n.Name)
|
|
}
|
|
if super {
|
|
g.Printf("struct objc_super _super = {\n")
|
|
g.Printf(" .receiver = _this,\n")
|
|
g.Printf(" .super_class = class_getSuperclass([%s class]),\n", n.Name)
|
|
g.Printf("};\n")
|
|
}
|
|
retType := "void"
|
|
if f.Ret != nil {
|
|
retType = g.objcType(f.Ret)
|
|
g.Printf("%s res = ", retType)
|
|
}
|
|
// There is no direct way to send a message to a class' super
|
|
// class from outside the class itself. Use objc_msgSendSuper instead
|
|
// which is what the compiler uses itself. To keep us honest and to exercise
|
|
// the code paths more use objc_msgSend for regular calls as well.
|
|
//
|
|
// A regular call looks like this:
|
|
//
|
|
// res = ((<return type> (*)(id, SEL, <argument_types>))objc_msgSend)(_this, @selector(...), <arguments>)
|
|
//
|
|
// a call to super looks like this:
|
|
//
|
|
// ret = ((<return type> (*)(id, SEL, <argument_types>))objc_msgSendSuper)(<struct objc_super>, <arguments>)
|
|
if f.Ret != nil {
|
|
switch f.Ret.Kind {
|
|
case objc.String, objc.Bool, objc.Data, objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double, objc.Class, objc.Protocol:
|
|
default:
|
|
// If support for struct results is added, objc_msgSend_stret must be used
|
|
panic("unsupported type kind - use objc_msgSend_stret?")
|
|
}
|
|
}
|
|
g.Printf("((%s (*)(", retType)
|
|
if super {
|
|
g.Printf("struct objc_super *")
|
|
} else {
|
|
g.Printf("id")
|
|
}
|
|
g.Printf(", SEL")
|
|
for _, a := range f.Params {
|
|
g.Printf(", %s", g.objcType(a.Type))
|
|
}
|
|
g.Printf("))")
|
|
if super {
|
|
g.Printf("objc_msgSendSuper")
|
|
} else {
|
|
g.Printf("objc_msgSend")
|
|
}
|
|
g.Printf(")(")
|
|
if f.Static && !f.Constructor {
|
|
g.Printf("[%s class]", n.Name)
|
|
} else {
|
|
if super {
|
|
g.Printf("&_super")
|
|
} else {
|
|
g.Printf("_this")
|
|
}
|
|
}
|
|
g.Printf(", @selector(%s)", f.Sig)
|
|
for _, a := range f.Params {
|
|
arg := "_" + a.Name
|
|
if a == errParam {
|
|
arg = "&" + a.Name
|
|
}
|
|
g.Printf(", %s", arg)
|
|
}
|
|
g.Printf(");\n")
|
|
if errParam != nil {
|
|
g.Printf("NSError *_%s = nil;\n", errParam.Name)
|
|
if f.Ret != nil {
|
|
g.Printf("if (!res && %s != nil) {\n", errParam.Name)
|
|
} else {
|
|
g.Printf("if (%s != nil) {\n", errParam.Name)
|
|
}
|
|
g.Printf(" _%[1]s = %[1]s;\n", errParam.Name)
|
|
g.Printf("}\n")
|
|
g.genObjCToC("_"+errParam.Name, g.errType(), modeRetained)
|
|
}
|
|
ret := f.Ret
|
|
if ret != nil && ret.Kind == objc.Bool && errParam != nil {
|
|
ret = nil
|
|
}
|
|
if ret != nil {
|
|
g.genObjCToC("res", ret, modeRetained)
|
|
}
|
|
switch {
|
|
case ret != nil && errParam != nil:
|
|
stype := strings.Replace(g.cType(ret), " ", "_", -1)
|
|
g.Printf("ret_%s _sres = {_res, __%s};\n", stype, errParam.Name)
|
|
g.Printf("return _sres;\n")
|
|
case ret != nil:
|
|
g.Printf("return _res;\n")
|
|
case errParam != nil:
|
|
g.Printf("return __%s;\n", errParam.Name)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (_ *ObjcWrapper) errType() *objc.Type {
|
|
return &objc.Type{Kind: objc.Class, Name: "NSError"}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genM(n *objc.Named) {
|
|
for _, f := range n.Funcs {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.genCFuncDecl("cproxy", n.GoName, f)
|
|
g.genCFuncBody(n, f, false)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) GenH() {
|
|
g.Printf("//File is generated by gobind. Do not edit.\n\n")
|
|
g.Printf("#include \"seq.h\"\n\n")
|
|
for _, m := range g.modules {
|
|
g.Printf("@import %s;\n", m)
|
|
}
|
|
// Include header files for generated types
|
|
for _, n := range g.pkgNames {
|
|
hasGen := false
|
|
for _, t := range g.modMap[n] {
|
|
if t.Generated {
|
|
hasGen = true
|
|
break
|
|
}
|
|
}
|
|
if hasGen {
|
|
g.Printf("#import %q\n", n+".objc.h")
|
|
}
|
|
}
|
|
for _, tn := range []string{"int", "nstring", "nbyteslice", "long", "unsigned long", "short", "unsigned short", "bool", "char", "unsigned char", "float", "double"} {
|
|
sn := strings.Replace(tn, " ", "_", -1)
|
|
g.Printf("typedef struct ret_%s {\n", sn)
|
|
g.Printf(" %s res;\n", tn)
|
|
g.Printf(" int err;\n")
|
|
g.Printf("} ret_%s;\n", sn)
|
|
}
|
|
g.Printf("\n")
|
|
for _, n := range g.types {
|
|
for _, f := range n.AllMethods {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("extern ")
|
|
g.genCFuncDecl("cproxy", n.GoName, f)
|
|
g.Printf(";\n")
|
|
if _, exists := g.supers[n.GoName]; exists {
|
|
g.Printf("extern ")
|
|
g.genCFuncDecl("csuper", n.GoName, f)
|
|
g.Printf(";\n")
|
|
}
|
|
}
|
|
}
|
|
for _, cls := range g.types {
|
|
g.genH(cls)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genH(n *objc.Named) {
|
|
for _, f := range n.Funcs {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("extern ")
|
|
g.genCFuncDecl("cproxy", n.GoName, f)
|
|
g.Printf(";\n")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genCFuncDecl(prefix, name string, f *objc.Func) {
|
|
returnsErr := len(f.Params) > 0 && g.isErrorType(f.Params[len(f.Params)-1].Type)
|
|
ret := f.Ret
|
|
if ret != nil && returnsErr && ret.Kind == objc.Bool {
|
|
ret = nil
|
|
}
|
|
switch {
|
|
case ret != nil && returnsErr:
|
|
g.Printf("ret_%s", strings.Replace(g.cType(ret), " ", "_", -1))
|
|
case ret != nil:
|
|
g.Printf(g.cType(ret))
|
|
case returnsErr:
|
|
g.Printf("int")
|
|
default:
|
|
g.Printf("void")
|
|
}
|
|
g.Printf(" ")
|
|
g.Printf(prefix)
|
|
if f.Static {
|
|
g.Printf("_s")
|
|
}
|
|
g.Printf("_%s_%s(", name, f.GoName)
|
|
if !f.Static {
|
|
g.Printf("int this")
|
|
}
|
|
for i, p := range f.Params {
|
|
if i == len(f.Params)-1 && returnsErr {
|
|
break
|
|
}
|
|
if !f.Static || i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("%s %s", g.cType(p.Type), p.Name)
|
|
}
|
|
g.Printf(")")
|
|
}
|
|
|
|
func (g *ObjcWrapper) GenGo() {
|
|
g.Printf("// File is generated by gobind. Do not edit.\n\n")
|
|
g.Printf("package main\n\n")
|
|
g.Printf("// #include \"interfaces.h\"\n")
|
|
g.Printf("import \"C\"\n\n")
|
|
g.Printf("import \"ObjC\"\n")
|
|
g.Printf("import _seq \"golang.org/x/mobile/bind/seq\"\n")
|
|
|
|
for _, n := range g.types {
|
|
for _, f := range n.Funcs {
|
|
if g.isFuncSupported(f) {
|
|
pkgName := n.Module + "/" + n.GoName
|
|
g.Printf("import %q\n", "ObjC/"+pkgName)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
g.Printf("\n")
|
|
g.Printf("type proxy interface { Bind_proxy_refnum__() int32 }\n\n")
|
|
g.Printf("// Suppress unused package error\n\n")
|
|
g.Printf("var _ = _seq.FromRefNum\n")
|
|
g.Printf("const _ = ObjC.Dummy\n\n")
|
|
for _, n := range g.types {
|
|
g.genGo(n)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genGo(n *objc.Named) {
|
|
g.Printf("func init() {\n")
|
|
g.Indent()
|
|
for _, f := range n.Funcs {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("%s.%s = func", n.GoName, f.GoName)
|
|
g.genFuncDecl(false, f)
|
|
g.genFuncBody(n, f, "cproxy")
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
g.Printf("type proxy_class_%s _seq.Ref\n\n", n.GoName)
|
|
g.Printf("func (p *proxy_class_%s) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }\n\n", n.GoName)
|
|
for _, f := range n.AllMethods {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("func (p *proxy_class_%s) %s", n.GoName, f.GoName)
|
|
g.genFuncDecl(false, f)
|
|
g.genFuncBody(n, f, "cproxy")
|
|
}
|
|
if _, exists := g.supers[n.GoName]; exists {
|
|
g.Printf("func (p *proxy_class_%s) Super() ObjC.%s {\n", n.GoName, n.Module+"_"+n.GoName)
|
|
g.Printf(" return &super_%s{p}\n", n.GoName)
|
|
g.Printf("}\n\n")
|
|
g.Printf("type super_%s struct {*proxy_class_%[1]s}\n\n", n.GoName)
|
|
for _, f := range n.AllMethods {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("func (p *super_%s) %s", n.GoName, f.GoName)
|
|
g.genFuncDecl(false, f)
|
|
g.genFuncBody(n, f, "csuper")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genFuncBody(n *objc.Named, f *objc.Func, prefix string) {
|
|
g.Printf(" {\n")
|
|
g.Indent()
|
|
var errParam *objc.Param
|
|
for i, a := range f.Params {
|
|
if i == len(f.Params)-1 && g.isErrorType(a.Type) {
|
|
errParam = a
|
|
break
|
|
}
|
|
g.genWrite(a)
|
|
}
|
|
ret := f.Ret
|
|
if ret != nil && errParam != nil && ret.Kind == objc.Bool {
|
|
ret = nil
|
|
}
|
|
if ret != nil || errParam != nil {
|
|
g.Printf("res := ")
|
|
}
|
|
g.Printf("C.")
|
|
g.Printf(prefix)
|
|
if f.Static {
|
|
g.Printf("_s")
|
|
}
|
|
g.Printf("_%s_%s(", n.GoName, f.GoName)
|
|
if !f.Static {
|
|
g.Printf("C.int(p.Bind_proxy_refnum__())")
|
|
}
|
|
for i, a := range f.Params {
|
|
if a == errParam {
|
|
break
|
|
}
|
|
if !f.Static || i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("_%s", a.Name)
|
|
}
|
|
g.Printf(")\n")
|
|
switch {
|
|
case ret != nil && errParam != nil:
|
|
g.genRead("_res", "res.res", ret)
|
|
g.genRefRead("_"+errParam.Name, "res.err", "error", "proxy_error")
|
|
g.Printf("return _res, _%s\n", errParam.Name)
|
|
case ret != nil:
|
|
g.genRead("_res", "res", ret)
|
|
g.Printf("return _res\n")
|
|
case errParam != nil:
|
|
g.genRefRead("_"+errParam.Name, "res", "error", "proxy_error")
|
|
g.Printf("return _%s\n", errParam.Name)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *ObjcWrapper) genCToObjC(name string, t *objc.Type, mode varMode) {
|
|
switch t.Kind {
|
|
case objc.String:
|
|
g.Printf("NSString *_%s = go_seq_to_objc_string(%s);\n", name, name)
|
|
case objc.Bool:
|
|
g.Printf("BOOL _%s = %s ? YES : NO;\n", name, name)
|
|
case objc.Data:
|
|
g.Printf("NSData *_%s = go_seq_to_objc_bytearray(%s, %d);\n", name, name, toCFlag(mode == modeRetained))
|
|
case objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double:
|
|
g.Printf("%s _%s = (%s)%s;\n", g.objcType(t), name, g.objcType(t), name)
|
|
case objc.Class, objc.Protocol:
|
|
g.Printf("GoSeqRef* %s_ref = go_seq_from_refnum(%s);\n", name, name)
|
|
g.Printf("%s _%s;\n", g.objcType(t), name)
|
|
g.Printf("if (%s_ref != NULL) {\n", name)
|
|
g.Printf(" _%s = %s_ref.obj;\n", name, name)
|
|
g.Printf("}\n")
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genObjCToC(name string, t *objc.Type, mode varMode) {
|
|
switch t.Kind {
|
|
case objc.String:
|
|
g.Printf("nstring _%s = go_seq_from_objc_string(%s);\n", name, name)
|
|
case objc.Data:
|
|
g.Printf("nbyteslice _%s = go_seq_from_objc_bytearray(%s, %d);\n", name, name, toCFlag(mode == modeRetained))
|
|
case objc.Bool, objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double:
|
|
g.Printf("%s _%s = (%s)%s;\n", g.cType(t), name, g.cType(t), name)
|
|
case objc.Protocol, objc.Class:
|
|
g.Printf("int _%s = go_seq_to_refnum(%s);\n", name, name)
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genWrite(a *objc.Param) {
|
|
switch a.Type.Kind {
|
|
case objc.String:
|
|
g.Printf("_%s := encodeString(%s)\n", a.Name, a.Name)
|
|
case objc.Data:
|
|
g.Printf("_%s := fromSlice(%s, 0)\n", a.Name, a.Name)
|
|
case objc.Bool:
|
|
g.Printf("_%s := %s(0)\n", a.Name, g.cgoType(a.Type))
|
|
g.Printf("if %s {\n", a.Name)
|
|
g.Printf(" _%s = %s(1)\n", a.Name, g.cgoType(a.Type))
|
|
g.Printf("}\n")
|
|
case objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double:
|
|
g.Printf("_%s := %s(%s)\n", a.Name, g.cgoType(a.Type), a.Name)
|
|
case objc.Protocol, objc.Class:
|
|
g.Printf("var _%s %s = _seq.NullRefNum\n", a.Name, g.cgoType(a.Type))
|
|
g.Printf("if %s != nil {\n", a.Name)
|
|
g.Printf(" _%s = %s(_seq.ToRefNum(%s))\n", a.Name, g.cgoType(a.Type), a.Name)
|
|
g.Printf("}\n")
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genRead(to, from string, t *objc.Type) {
|
|
switch t.Kind {
|
|
case objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double:
|
|
g.Printf("%s := %s(%s)\n", to, g.goType(t, false), from)
|
|
case objc.Bool:
|
|
g.Printf("%s := %s != 0\n", to, from)
|
|
case objc.String:
|
|
g.Printf("%s := decodeString(%s)\n", to, from)
|
|
case objc.Data:
|
|
g.Printf("%s := toSlice(%s, 1)\n", to, from)
|
|
case objc.Protocol, objc.Class:
|
|
var proxyName string
|
|
if n := g.lookupImported(t); n != nil {
|
|
proxyName = "proxy_class_" + n.GoName
|
|
}
|
|
g.genRefRead(to, from, g.goType(t, false), proxyName)
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genRefRead(to, from string, intfName, proxyName string) {
|
|
g.Printf("var %s %s\n", to, intfName)
|
|
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", to, from)
|
|
g.Printf("if %s_ref != nil {\n", to)
|
|
g.Printf(" if %s < 0 { // go object\n", from)
|
|
g.Printf(" %s = %s_ref.Get().(%s)\n", to, to, intfName)
|
|
if proxyName != "" {
|
|
g.Printf(" } else { // foreign object\n")
|
|
g.Printf(" %s = (*%s)(%s_ref)\n", to, proxyName, to)
|
|
}
|
|
g.Printf(" }\n")
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
// Packages return the list of Go packages to be generated.
|
|
func (g *ObjcWrapper) Packages() []string {
|
|
return g.pkgNames
|
|
}
|
|
|
|
func (g *ObjcWrapper) GenPackage(idx int) {
|
|
pkg := g.pkgNames[idx]
|
|
g.Printf("//File is generated by gobind. Do not edit.\n\n")
|
|
g.Printf("package %s\n\n", path.Base(pkg))
|
|
g.Printf("import \"ObjC\"\n\n")
|
|
g.Printf("const _ = ObjC.Dummy\n\n")
|
|
for _, n := range g.modMap[pkg] {
|
|
g.Printf("type %s ObjC.%s\n", n.GoName, n.Module+"_"+n.GoName)
|
|
}
|
|
if n, ok := g.typePkgs[pkg]; ok {
|
|
g.Printf("var (\n")
|
|
g.Indent()
|
|
// Functions
|
|
for _, f := range n.Funcs {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf("%s func", f.GoName)
|
|
g.genFuncDecl(false, f)
|
|
g.Printf("\n")
|
|
}
|
|
g.Outdent()
|
|
g.Printf(")\n\n")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) GenInterfaces() {
|
|
g.Printf("//File is generated by gobind. Do not edit.\n\n")
|
|
g.Printf("package ObjC\n\n")
|
|
g.Printf("// Used to silence this package not used errors\n")
|
|
g.Printf("const Dummy = 0\n\n")
|
|
for _, n := range g.types {
|
|
g.genInterface(n)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) genInterface(n *objc.Named) {
|
|
g.Printf("type %s interface {\n", n.Module+"_"+n.GoName)
|
|
g.Indent()
|
|
// Methods
|
|
for _, f := range n.AllMethods {
|
|
if !g.isFuncSupported(f) {
|
|
continue
|
|
}
|
|
g.Printf(f.GoName)
|
|
g.genFuncDecl(true, f)
|
|
g.Printf("\n")
|
|
}
|
|
if _, exists := g.supers[n.GoName]; exists {
|
|
g.Printf("Super() %s\n", n.Module+"_"+n.GoName)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *ObjcWrapper) genFuncDecl(local bool, f *objc.Func) {
|
|
var returnsErr bool
|
|
g.Printf("(")
|
|
for i, p := range f.Params {
|
|
if i == len(f.Params)-1 && g.isErrorType(p.Type) {
|
|
returnsErr = true
|
|
break
|
|
}
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("%s %s", p.Name, g.goType(p.Type, local))
|
|
}
|
|
g.Printf(")")
|
|
if f.Ret != nil || returnsErr {
|
|
ret := f.Ret
|
|
if ret.Kind == objc.Bool && returnsErr {
|
|
// Skip the bool result and use the error results.
|
|
ret = nil
|
|
}
|
|
if ret != nil {
|
|
g.Printf(" (%s", g.goType(f.Ret, local))
|
|
if returnsErr {
|
|
g.Printf(", error")
|
|
}
|
|
g.Printf(")")
|
|
} else {
|
|
g.Printf(" error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) isFuncSupported(f *objc.Func) bool {
|
|
for i, p := range f.Params {
|
|
if !g.isSupported(p.Type) {
|
|
if i < len(f.Params)-1 || !g.isErrorType(p.Type) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if f.Ret != nil {
|
|
return g.isSupported(f.Ret)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (g *ObjcWrapper) isErrorType(t *objc.Type) bool {
|
|
// Must be a NSError ** type
|
|
return t.Kind == objc.Class && t.Indirect && t.Name == "NSError"
|
|
}
|
|
|
|
func (g *ObjcWrapper) isSupported(t *objc.Type) bool {
|
|
if t.Indirect {
|
|
return false
|
|
}
|
|
switch t.Kind {
|
|
case objc.Unknown:
|
|
return false
|
|
case objc.Protocol:
|
|
// TODO: support inout parameters.
|
|
return !strings.HasSuffix(t.Decl, " *")
|
|
case objc.Class:
|
|
return t.Name != "SEL" && t.Name != "void"
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) cgoType(t *objc.Type) string {
|
|
switch t.Kind {
|
|
case objc.Uint:
|
|
return "C.ulong"
|
|
case objc.Ushort:
|
|
return "C.ushort"
|
|
case objc.Uchar:
|
|
return "C.uchar"
|
|
default:
|
|
return "C." + g.cType(t)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) cType(t *objc.Type) string {
|
|
switch t.Kind {
|
|
case objc.Protocol, objc.Class:
|
|
return "int"
|
|
case objc.String:
|
|
return "nstring"
|
|
case objc.Data:
|
|
return "nbyteslice"
|
|
case objc.Int:
|
|
return "long"
|
|
case objc.Uint:
|
|
return "unsigned long"
|
|
case objc.Short:
|
|
return "short"
|
|
case objc.Ushort:
|
|
return "unsigned short"
|
|
case objc.Bool:
|
|
return "char"
|
|
case objc.Char:
|
|
return "char"
|
|
case objc.Uchar:
|
|
return "unsigned char"
|
|
case objc.Float:
|
|
return "float"
|
|
case objc.Double:
|
|
return "double"
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|
|
|
|
func (g *ObjcWrapper) objcType(t *objc.Type) string {
|
|
return t.Decl
|
|
}
|
|
|
|
func (g *ObjcWrapper) lookupImported(t *objc.Type) *objc.Named {
|
|
var mangled string
|
|
switch t.Kind {
|
|
case objc.Class:
|
|
mangled = t.Name + "C"
|
|
case objc.Protocol:
|
|
mangled = t.Name + "P"
|
|
default:
|
|
panic("invalid type kind")
|
|
}
|
|
if n, exists := g.imported[mangled]; exists {
|
|
return n
|
|
}
|
|
return g.imported[t.Name]
|
|
}
|
|
|
|
func (g *ObjcWrapper) goType(t *objc.Type, local bool) string {
|
|
switch t.Kind {
|
|
case objc.String:
|
|
return "string"
|
|
case objc.Data:
|
|
return "[]byte"
|
|
case objc.Int:
|
|
return "int"
|
|
case objc.Uint:
|
|
return "uint"
|
|
case objc.Short:
|
|
return "int16"
|
|
case objc.Ushort:
|
|
return "uint16"
|
|
case objc.Bool:
|
|
return "bool"
|
|
case objc.Char:
|
|
return "byte"
|
|
case objc.Float:
|
|
return "float32"
|
|
case objc.Double:
|
|
return "float64"
|
|
case objc.Protocol, objc.Class:
|
|
var n *objc.Named
|
|
n = g.lookupImported(t)
|
|
name := n.Module + "_" + n.GoName
|
|
if !local {
|
|
name = "ObjC." + name
|
|
}
|
|
return name
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
}
|