Update golang/go#12475 Change-Id: I7fdc22462b5925c84ebbeb54517032c2fbd0545b Reviewed-on: https://go-review.googlesource.com/15120 Reviewed-by: David Crawshaw <crawshaw@golang.org>
903 lines
23 KiB
Go
903 lines
23 KiB
Go
// Copyright 2015 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 (
|
|
"fmt"
|
|
"go/constant"
|
|
"go/token"
|
|
"go/types"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
// TODO(hyangah): error code/domain propagation
|
|
|
|
type objcGen struct {
|
|
*printer
|
|
fset *token.FileSet
|
|
pkg *types.Package
|
|
err ErrorList
|
|
|
|
prefix string // prefix arg passed by flag.
|
|
|
|
// fields set by init.
|
|
pkgName string
|
|
namePrefix string
|
|
funcs []*types.Func
|
|
names []*types.TypeName
|
|
constants []*types.Const
|
|
}
|
|
|
|
func (g *objcGen) init() {
|
|
g.pkgName = g.pkg.Name()
|
|
g.namePrefix = g.prefix + strings.Title(g.pkgName)
|
|
g.funcs = nil
|
|
g.names = nil
|
|
|
|
scope := g.pkg.Scope()
|
|
for _, name := range scope.Names() {
|
|
obj := scope.Lookup(name)
|
|
if !obj.Exported() {
|
|
continue
|
|
}
|
|
switch obj := obj.(type) {
|
|
case *types.Func:
|
|
if isCallable(obj) {
|
|
g.funcs = append(g.funcs, obj)
|
|
}
|
|
case *types.TypeName:
|
|
g.names = append(g.names, obj)
|
|
case *types.Const:
|
|
if _, ok := obj.Type().(*types.Basic); !ok {
|
|
g.errorf("unsupported exported const for %s: %T", obj.Name(), obj)
|
|
continue
|
|
}
|
|
g.constants = append(g.constants, obj)
|
|
default:
|
|
g.errorf("unsupported exported type for %s: %T", obj.Name(), obj)
|
|
// TODO(hyangah): *types.Var
|
|
}
|
|
}
|
|
}
|
|
|
|
const objcPreamble = `// Objective-C API for talking to %[1]s Go package.
|
|
// gobind %[2]s %[3]s
|
|
//
|
|
// File is generated by gobind. Do not edit.
|
|
|
|
`
|
|
|
|
func (g *objcGen) genH() error {
|
|
g.init()
|
|
|
|
g.Printf(objcPreamble, g.pkg.Path(), g.gobindOpts(), g.pkg.Path())
|
|
g.Printf("#ifndef __Go%s_H__\n", strings.Title(g.pkgName))
|
|
g.Printf("#define __Go%s_H__\n", strings.Title(g.pkgName))
|
|
g.Printf("\n")
|
|
g.Printf("#include <Foundation/Foundation.h>")
|
|
g.Printf("\n\n")
|
|
|
|
// @class names
|
|
for _, obj := range g.names {
|
|
named := obj.Type().(*types.Named)
|
|
switch t := named.Underlying().(type) {
|
|
case *types.Struct:
|
|
g.Printf("@class %s%s;\n\n", g.namePrefix, obj.Name())
|
|
case *types.Interface:
|
|
if !makeIfaceSummary(t).implementable {
|
|
g.Printf("@class %s%s;\n\n", g.namePrefix, obj.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
// @interfaces
|
|
for _, obj := range g.names {
|
|
named := obj.Type().(*types.Named)
|
|
switch t := named.Underlying().(type) {
|
|
case *types.Struct:
|
|
g.genStructH(obj, t)
|
|
g.Printf("\n")
|
|
case *types.Interface:
|
|
g.genInterfaceH(obj, t)
|
|
g.Printf("\n")
|
|
}
|
|
}
|
|
|
|
// const
|
|
for _, obj := range g.constants {
|
|
switch b := obj.Type().(*types.Basic); b.Kind() {
|
|
case types.String, types.UntypedString:
|
|
g.Printf("FOUNDATION_EXPORT NSString* const %s%s;\n", g.namePrefix, obj.Name())
|
|
default:
|
|
g.Printf("FOUNDATION_EXPORT const %s %s%s;\n", g.objcType(obj.Type()), g.namePrefix, obj.Name())
|
|
}
|
|
}
|
|
if len(g.constants) > 0 {
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// static functions.
|
|
for _, obj := range g.funcs {
|
|
g.genFuncH(obj)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// declare all named types first.
|
|
g.Printf("#endif\n")
|
|
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *objcGen) gobindOpts() string {
|
|
opts := []string{"-lang=objc"}
|
|
if g.prefix != "Go" {
|
|
opts = append(opts, "-prefix="+g.prefix)
|
|
}
|
|
return strings.Join(opts, " ")
|
|
}
|
|
|
|
func (g *objcGen) genM() error {
|
|
g.init()
|
|
|
|
g.Printf(objcPreamble, g.pkg.Path(), g.gobindOpts(), g.pkg.Path())
|
|
g.Printf("#include %q\n", g.namePrefix+".h")
|
|
g.Printf("#include <Foundation/Foundation.h>\n")
|
|
g.Printf("#include \"seq.h\"\n")
|
|
g.Printf("\n")
|
|
g.Printf("static NSString* errDomain = @\"go.%s\";\n", g.pkg.Path())
|
|
g.Printf("\n")
|
|
|
|
g.Printf("@protocol goSeqRefInterface\n")
|
|
g.Printf("-(GoSeqRef*) ref;\n")
|
|
g.Printf("@end\n")
|
|
g.Printf("\n")
|
|
|
|
g.Printf("#define _DESCRIPTOR_ %q\n\n", g.pkgName)
|
|
for i, obj := range g.funcs {
|
|
g.Printf("#define _CALL_%s_ %d\n", obj.Name(), i+1)
|
|
}
|
|
g.Printf("\n")
|
|
|
|
// struct, interface.
|
|
var interfaces []*types.TypeName
|
|
for _, obj := range g.names {
|
|
named := obj.Type().(*types.Named)
|
|
switch t := named.Underlying().(type) {
|
|
case *types.Struct:
|
|
g.genStructM(obj, t)
|
|
case *types.Interface:
|
|
if g.genInterfaceM(obj, t) {
|
|
interfaces = append(interfaces, obj)
|
|
}
|
|
}
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// const
|
|
for _, o := range g.constants {
|
|
g.genConstM(o)
|
|
}
|
|
if len(g.constants) > 0 {
|
|
g.Printf("\n")
|
|
}
|
|
// global functions.
|
|
for _, obj := range g.funcs {
|
|
g.genFuncM(obj)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// register proxy functions.
|
|
if len(interfaces) > 0 {
|
|
g.Printf("__attribute__((constructor)) static void init() {\n")
|
|
g.Indent()
|
|
for _, obj := range interfaces {
|
|
g.Printf("go_seq_register_proxy(\"go.%s.%s\", proxy%s%s);\n", g.pkgName, obj.Name(), g.namePrefix, obj.Name())
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *objcGen) genConstM(o *types.Const) {
|
|
cName := fmt.Sprintf("%s%s", g.namePrefix, o.Name())
|
|
cType := g.objcType(o.Type())
|
|
|
|
switch b := o.Type().(*types.Basic); b.Kind() {
|
|
case types.Bool, types.UntypedBool:
|
|
v := "NO"
|
|
if constant.BoolVal(o.Val()) {
|
|
v = "YES"
|
|
}
|
|
g.Printf("const BOOL %s = %s;\n", cName, v)
|
|
|
|
case types.String, types.UntypedString:
|
|
g.Printf("NSString* const %s = @%s;\n", cName, o.Val())
|
|
|
|
case types.Int, types.Int8, types.Int16, types.Int32:
|
|
g.Printf("const %s %s = %s;\n", cType, cName, o.Val())
|
|
|
|
case types.Int64, types.UntypedInt:
|
|
i, exact := constant.Int64Val(o.Val())
|
|
if !exact {
|
|
g.errorf("const value %s for %s cannot be represented as %s", o.Val(), o.Name(), cType)
|
|
return
|
|
}
|
|
if i == math.MinInt64 {
|
|
// -9223372036854775808LL does not work because 922337203685477508 is
|
|
// larger than max int64.
|
|
g.Printf("const int64_t %s = %dLL-1;\n", cName, i+1)
|
|
} else {
|
|
g.Printf("const int64_t %s = %dLL;\n", cName, i)
|
|
}
|
|
|
|
case types.Float32, types.Float64, types.UntypedFloat:
|
|
f, _ := constant.Float64Val(o.Val())
|
|
if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 {
|
|
g.errorf("const value %s for %s cannot be represented as double", o.Val(), o.Name())
|
|
return
|
|
}
|
|
g.Printf("const %s %s = %g;\n", cType, cName, f)
|
|
|
|
default:
|
|
g.errorf("unsupported const type %s for %s", b, o.Name())
|
|
}
|
|
}
|
|
|
|
type funcSummary struct {
|
|
name string
|
|
ret string
|
|
params, retParams []paramInfo
|
|
}
|
|
|
|
type paramInfo struct {
|
|
typ types.Type
|
|
name string
|
|
}
|
|
|
|
func (g *objcGen) funcSummary(obj *types.Func) *funcSummary {
|
|
s := &funcSummary{name: obj.Name()}
|
|
|
|
sig := obj.Type().(*types.Signature)
|
|
params := sig.Params()
|
|
for i := 0; i < params.Len(); i++ {
|
|
p := params.At(i)
|
|
v := paramInfo{
|
|
typ: p.Type(),
|
|
name: paramName(params, i),
|
|
}
|
|
s.params = append(s.params, v)
|
|
}
|
|
|
|
res := sig.Results()
|
|
switch res.Len() {
|
|
case 0:
|
|
s.ret = "void"
|
|
case 1:
|
|
p := res.At(0)
|
|
if isErrorType(p.Type()) {
|
|
s.retParams = append(s.retParams, paramInfo{
|
|
typ: p.Type(),
|
|
name: "error",
|
|
})
|
|
s.ret = "BOOL"
|
|
} else {
|
|
name := p.Name()
|
|
if name == "" || paramRE.MatchString(name) {
|
|
name = "ret0_"
|
|
}
|
|
typ := p.Type()
|
|
s.retParams = append(s.retParams, paramInfo{typ: typ, name: name})
|
|
s.ret = g.objcType(typ)
|
|
}
|
|
case 2:
|
|
name := res.At(0).Name()
|
|
if name == "" || paramRE.MatchString(name) {
|
|
name = "ret0_"
|
|
}
|
|
s.retParams = append(s.retParams, paramInfo{
|
|
typ: res.At(0).Type(),
|
|
name: name,
|
|
})
|
|
|
|
if !isErrorType(res.At(1).Type()) {
|
|
g.errorf("second result value must be of type error: %s", obj)
|
|
return nil
|
|
}
|
|
s.retParams = append(s.retParams, paramInfo{
|
|
typ: res.At(1).Type(),
|
|
name: "error", // TODO(hyangah): name collision check.
|
|
})
|
|
s.ret = "BOOL"
|
|
default:
|
|
// TODO(hyangah): relax the constraint on multiple return params.
|
|
g.errorf("too many result values: %s", obj)
|
|
return nil
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *funcSummary) asFunc(g *objcGen) string {
|
|
var params []string
|
|
for _, p := range s.params {
|
|
params = append(params, g.objcType(p.typ)+" "+p.name)
|
|
}
|
|
if !s.returnsVal() {
|
|
for _, p := range s.retParams {
|
|
params = append(params, g.objcType(p.typ)+"* "+p.name)
|
|
}
|
|
}
|
|
return fmt.Sprintf("%s %s%s(%s)", s.ret, g.namePrefix, s.name, strings.Join(params, ", "))
|
|
}
|
|
|
|
func (s *funcSummary) asMethod(g *objcGen) string {
|
|
var params []string
|
|
for i, p := range s.params {
|
|
var key string
|
|
if i != 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcType(p.typ), p.name))
|
|
}
|
|
if !s.returnsVal() {
|
|
for _, p := range s.retParams {
|
|
var key string
|
|
if len(params) > 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcType(p.typ)+"*", p.name))
|
|
}
|
|
}
|
|
return fmt.Sprintf("(%s)%s%s", s.ret, s.name, strings.Join(params, " "))
|
|
}
|
|
|
|
func (s *funcSummary) callMethod(g *objcGen) string {
|
|
var params []string
|
|
for i, p := range s.params {
|
|
var key string
|
|
if i != 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:%s", key, p.name))
|
|
}
|
|
if !s.returnsVal() {
|
|
for _, p := range s.retParams {
|
|
var key string
|
|
if len(params) > 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:&%s", key, p.name))
|
|
}
|
|
}
|
|
return fmt.Sprintf("%s%s", s.name, strings.Join(params, " "))
|
|
}
|
|
|
|
func (s *funcSummary) returnsVal() bool {
|
|
return len(s.retParams) == 1 && !isErrorType(s.retParams[0].typ)
|
|
}
|
|
|
|
func (g *objcGen) genFuncH(obj *types.Func) {
|
|
if s := g.funcSummary(obj); s != nil {
|
|
g.Printf("FOUNDATION_EXPORT %s;\n", s.asFunc(g))
|
|
}
|
|
}
|
|
|
|
func (g *objcGen) seqType(typ types.Type) string {
|
|
s := seqType(typ)
|
|
if s == "String" {
|
|
// TODO(hyangah): non utf-8 strings.
|
|
s = "UTF8"
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (g *objcGen) genFuncM(obj *types.Func) {
|
|
s := g.funcSummary(obj)
|
|
if s == nil {
|
|
return
|
|
}
|
|
g.Printf("%s {\n", s.asFunc(g))
|
|
g.Indent()
|
|
g.genFunc("_DESCRIPTOR_", fmt.Sprintf("_CALL_%s_", s.name), s, false)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *objcGen) genGetter(desc string, f *types.Var) {
|
|
t := f.Type()
|
|
if isErrorType(t) {
|
|
t = types.Typ[types.String]
|
|
}
|
|
s := &funcSummary{
|
|
name: f.Name(),
|
|
ret: g.objcType(t),
|
|
retParams: []paramInfo{{typ: t, name: "ret_"}},
|
|
}
|
|
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(desc+"_DESCRIPTOR_", desc+"_FIELD_"+f.Name()+"_GET_", s, true)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *objcGen) genSetter(desc string, f *types.Var) {
|
|
t := f.Type()
|
|
if isErrorType(t) {
|
|
t = types.Typ[types.String]
|
|
}
|
|
s := &funcSummary{
|
|
name: "set" + f.Name(),
|
|
ret: "void",
|
|
params: []paramInfo{{typ: t, name: "v"}},
|
|
}
|
|
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(desc+"_DESCRIPTOR_", desc+"_FIELD_"+f.Name()+"_SET_", s, true)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *objcGen) genFunc(pkgDesc, callDesc string, s *funcSummary, isMethod bool) {
|
|
g.Printf("GoSeq in_ = {};\n")
|
|
g.Printf("GoSeq out_ = {};\n")
|
|
if isMethod {
|
|
g.Printf("go_seq_writeRef(&in_, self.ref);\n")
|
|
}
|
|
for _, p := range s.params {
|
|
st := g.seqType(p.typ)
|
|
if st == "Ref" {
|
|
g.Printf("if ([(id<NSObject>)(%s) isKindOfClass:[%s class]]) {\n", p.name, g.refTypeBase(p.typ))
|
|
g.Indent()
|
|
g.Printf("id<goSeqRefInterface> %[1]s_proxy = (id<goSeqRefInterface>)(%[1]s);\n", p.name)
|
|
g.Printf("go_seq_writeRef(&in_, %s_proxy.ref);\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("} else {\n")
|
|
g.Indent()
|
|
g.Printf("go_seq_writeObjcRef(&in_, %s);\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("go_seq_write%s(&in_, %s);\n", st, p.name)
|
|
}
|
|
}
|
|
g.Printf("go_seq_send(%s, %s, &in_, &out_);\n", pkgDesc, callDesc)
|
|
|
|
if s.returnsVal() {
|
|
p := s.retParams[0]
|
|
if seqTyp := g.seqType(p.typ); seqTyp != "Ref" {
|
|
g.Printf("%s %s = go_seq_read%s(&out_);\n", g.objcType(p.typ), p.name, g.seqType(p.typ))
|
|
} else {
|
|
ptype := g.objcType(p.typ)
|
|
g.Printf("GoSeqRef* %s_ref = go_seq_readRef(&out_);\n", p.name)
|
|
g.Printf("%s %s = %s_ref.obj;\n", ptype, p.name, p.name)
|
|
g.Printf("if (%s == NULL) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("%s = [[%s alloc] initWithRef:%s_ref];\n", p.name, g.refTypeBase(p.typ), p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
} else {
|
|
for _, p := range s.retParams {
|
|
if isErrorType(p.typ) {
|
|
g.Printf("NSString* _%s = go_seq_readUTF8(&out_);\n", p.name)
|
|
g.Printf("if ([_%s length] != 0 && %s != nil) {\n", p.name, p.name)
|
|
g.Indent()
|
|
g.Printf("NSMutableDictionary* details = [NSMutableDictionary dictionary];\n")
|
|
g.Printf("[details setValue:_%s forKey:NSLocalizedDescriptionKey];\n", p.name)
|
|
g.Printf("*%s = [NSError errorWithDomain:errDomain code:1 userInfo:details];\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else if seqTyp := g.seqType(p.typ); seqTyp != "Ref" {
|
|
g.Printf("%s %s_val = go_seq_read%s(&out_);\n", g.objcType(p.typ), p.name, g.seqType(p.typ))
|
|
g.Printf("if (%s != NULL) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("*%s = %s_val;\n", p.name, p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("GoSeqRef* %s_ref = go_seq_readRef(&out_);\n", p.name)
|
|
g.Printf("if (%s != NULL) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("*%s = %s_ref.obj;\n", p.name, p.name)
|
|
g.Printf("if (*%s == NULL) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("*%s = [[%s alloc] initWithRef:%s_ref];\n", p.name, g.refTypeBase(p.typ), p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
g.Printf("go_seq_free(&in_);\n")
|
|
g.Printf("go_seq_free(&out_);\n")
|
|
if n := len(s.retParams); n > 0 {
|
|
p := s.retParams[n-1]
|
|
if isErrorType(p.typ) {
|
|
g.Printf("return ([_%s length] == 0);\n", p.name)
|
|
} else {
|
|
g.Printf("return %s;\n", p.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *objcGen) genInterfaceInterface(obj *types.TypeName, summary ifaceSummary, isProtocol bool) {
|
|
g.Printf("@interface %[1]s%[2]s : NSObject", g.namePrefix, obj.Name())
|
|
if isProtocol {
|
|
g.Printf(" <%[1]s%[2]s>", g.namePrefix, obj.Name())
|
|
}
|
|
g.Printf(" {\n}\n")
|
|
g.Printf("@property(strong, readonly) id ref;\n")
|
|
g.Printf("\n")
|
|
g.Printf("- (id)initWithRef:(id)ref;\n")
|
|
for _, m := range summary.callable {
|
|
s := g.funcSummary(m)
|
|
g.Printf("- %s;\n", s.asMethod(g))
|
|
}
|
|
g.Printf("@end\n")
|
|
g.Printf("\n")
|
|
}
|
|
|
|
func (g *objcGen) genInterfaceH(obj *types.TypeName, t *types.Interface) {
|
|
summary := makeIfaceSummary(t)
|
|
if !summary.implementable {
|
|
g.genInterfaceInterface(obj, summary, false)
|
|
return
|
|
}
|
|
g.Printf("@protocol %s%s\n", g.namePrefix, obj.Name())
|
|
for _, m := range makeIfaceSummary(t).callable {
|
|
s := g.funcSummary(m)
|
|
g.Printf("- %s;\n", s.asMethod(g))
|
|
}
|
|
g.Printf("@end\n")
|
|
}
|
|
|
|
func (g *objcGen) genInterfaceM(obj *types.TypeName, t *types.Interface) bool {
|
|
summary := makeIfaceSummary(t)
|
|
|
|
desc := fmt.Sprintf("_GO_%s_%s", g.pkgName, obj.Name())
|
|
g.Printf("#define %s_DESCRIPTOR_ \"go.%s.%s\"\n", desc, g.pkgName, obj.Name())
|
|
for i, m := range summary.callable {
|
|
g.Printf("#define %s_%s_ (0x%x0a)\n", desc, m.Name(), i+1)
|
|
}
|
|
g.Printf("\n")
|
|
|
|
if summary.implementable {
|
|
// @interface Interface -- similar to what genStructH does.
|
|
g.genInterfaceInterface(obj, summary, true)
|
|
}
|
|
|
|
// @implementation Interface -- similar to what genStructM does.
|
|
g.Printf("@implementation %s%s {\n", g.namePrefix, obj.Name())
|
|
g.Printf("}\n")
|
|
g.Printf("\n")
|
|
g.Printf("- (id)initWithRef:(id)ref {\n")
|
|
g.Indent()
|
|
g.Printf("self = [super init];\n")
|
|
g.Printf("if (self) { _ref = ref; }\n")
|
|
g.Printf("return self;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Printf("\n")
|
|
|
|
for _, m := range summary.callable {
|
|
s := g.funcSummary(m)
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(desc+"_DESCRIPTOR_", desc+"_"+m.Name()+"_", s, true)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
g.Printf("@end\n")
|
|
g.Printf("\n")
|
|
|
|
// proxy function.
|
|
if summary.implementable {
|
|
g.Printf("static void proxy%s%s(id obj, int code, GoSeq* in, GoSeq* out) {\n", g.namePrefix, obj.Name())
|
|
g.Indent()
|
|
g.Printf("switch (code) {\n")
|
|
for _, m := range summary.callable {
|
|
g.Printf("case %s_%s_: {\n", desc, m.Name())
|
|
g.Indent()
|
|
g.genInterfaceMethodProxy(obj, g.funcSummary(m))
|
|
g.Outdent()
|
|
g.Printf("} break;\n")
|
|
}
|
|
g.Printf("default:\n")
|
|
g.Indent()
|
|
g.Printf("NSLog(@\"unknown code %%x for %s_DESCRIPTOR_\", code);\n", desc)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
return summary.implementable
|
|
}
|
|
|
|
func (g *objcGen) genInterfaceMethodProxy(obj *types.TypeName, s *funcSummary) {
|
|
g.Printf("id<%[1]s%[2]s> o = (id<%[1]s%[2]s>)(obj);\n", g.namePrefix, obj.Name())
|
|
// read params from GoSeq* inseq
|
|
for _, p := range s.params {
|
|
stype := g.seqType(p.typ)
|
|
ptype := g.objcType(p.typ)
|
|
if stype == "Ref" {
|
|
g.Printf("GoSeqRef* %s_ref = go_seq_readRef(in);\n", p.name)
|
|
g.Printf("%s %s = %s_ref.obj;\n", ptype, p.name, p.name)
|
|
g.Printf("if (%s == NULL) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("%s = [[%s alloc] initWithRef:%s_ref];\n", p.name, g.refTypeBase(p.typ), p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("%s %s = go_seq_read%s(in);\n", ptype, p.name, stype)
|
|
}
|
|
}
|
|
|
|
// call method
|
|
if !s.returnsVal() {
|
|
for _, p := range s.retParams {
|
|
if isErrorType(p.typ) {
|
|
g.Printf("NSError* %s = NULL;\n", p.name)
|
|
} else {
|
|
g.Printf("%s %s;\n", g.objcType(p.typ), p.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
if s.ret == "void" {
|
|
g.Printf("[o %s];\n", s.callMethod(g))
|
|
} else {
|
|
g.Printf("%s returnVal = [o %s];\n", s.ret, s.callMethod(g))
|
|
}
|
|
|
|
// write result to GoSeq* outseq
|
|
if len(s.retParams) == 0 {
|
|
return
|
|
}
|
|
if s.returnsVal() { // len(s.retParams) == 1 && s.retParams[0] != error
|
|
p := s.retParams[0]
|
|
if stype := g.seqType(p.typ); stype == "Ref" {
|
|
g.Printf("if ([(id<NSObject>)(returnVal) isKindOfClass:[%s class]]) {\n", g.refTypeBase(p.typ))
|
|
g.Indent()
|
|
g.Printf("id<goSeqRefInterface>retVal_proxy = (id<goSeqRefInterface>)(returnVal);\n")
|
|
g.Printf("go_seq_writeRef(out, retVal_proxy.ref);\n")
|
|
g.Outdent()
|
|
g.Printf("} else {\n")
|
|
g.Indent()
|
|
g.Printf("go_seq_writeRef(out, returnVal);\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("go_seq_write%s(out, returnVal);\n", stype)
|
|
}
|
|
return
|
|
}
|
|
for i, p := range s.retParams {
|
|
if isErrorType(p.typ) {
|
|
if i == len(s.retParams)-1 { // last param.
|
|
g.Printf("if (returnVal) {\n")
|
|
} else {
|
|
g.Printf("if (%s == NULL) {\n", p.name)
|
|
}
|
|
g.Indent()
|
|
g.Printf("go_seq_writeUTF8(out, NULL);\n")
|
|
g.Outdent()
|
|
g.Printf("} else {\n")
|
|
g.Indent()
|
|
g.Printf("NSString* %[1]sDesc = [%[1]s localizedDescription];\n", p.name)
|
|
g.Printf("if (%[1]sDesc == NULL || %[1]sDesc.length == 0) {\n", p.name)
|
|
g.Indent()
|
|
g.Printf("%[1]sDesc = @\"gobind: unknown error\";\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Printf("go_seq_writeUTF8(out, %sDesc);\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else if seqTyp := g.seqType(p.typ); seqTyp == "Ref" {
|
|
// TODO(hyangah): NULL.
|
|
g.Printf("if ([(id<NSObject>)(%s) isKindOfClass:[%s class]]) {\n", p.name, g.refTypeBase(p.typ))
|
|
g.Indent()
|
|
g.Printf("id<goSeqRefInterface>%[1]s_proxy = (id<goSeqRefInterface>)(%[1]s);\n", p.name)
|
|
g.Printf("go_seq_writeRef(out, %s_proxy.ref);\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("} else {\n")
|
|
g.Indent()
|
|
g.Printf("go_seq_writeObjcRef(out, %s);\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("go_seq_write%s(out, %s);\n", seqTyp, p.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *objcGen) genStructH(obj *types.TypeName, t *types.Struct) {
|
|
g.Printf("@interface %s%s : NSObject {\n", g.namePrefix, obj.Name())
|
|
g.Printf("}\n")
|
|
g.Printf("@property(strong, readonly) id ref;\n")
|
|
g.Printf("\n")
|
|
g.Printf("- (id)initWithRef:(id)ref;\n")
|
|
|
|
// accessors to exported fields.
|
|
for _, f := range exportedFields(t) {
|
|
name, typ := f.Name(), g.objcFieldType(f.Type())
|
|
g.Printf("- (%s)%s;\n", typ, name)
|
|
g.Printf("- (void)set%s:(%s)v;\n", name, typ)
|
|
}
|
|
|
|
// exported methods
|
|
for _, m := range exportedMethodSet(types.NewPointer(obj.Type())) {
|
|
s := g.funcSummary(m)
|
|
g.Printf("- %s;\n", s.asMethod(g))
|
|
}
|
|
g.Printf("@end\n")
|
|
}
|
|
|
|
func (g *objcGen) genStructM(obj *types.TypeName, t *types.Struct) {
|
|
fields := exportedFields(t)
|
|
methods := exportedMethodSet(types.NewPointer(obj.Type()))
|
|
|
|
desc := fmt.Sprintf("_GO_%s_%s", g.pkgName, obj.Name())
|
|
g.Printf("#define %s_DESCRIPTOR_ \"go.%s.%s\"\n", desc, g.pkgName, obj.Name())
|
|
for i, f := range fields {
|
|
g.Printf("#define %s_FIELD_%s_GET_ (0x%x0f)\n", desc, f.Name(), i)
|
|
g.Printf("#define %s_FIELD_%s_SET_ (0x%x1f)\n", desc, f.Name(), i)
|
|
}
|
|
for i, m := range methods {
|
|
g.Printf("#define %s_%s_ (0x%x0c)\n", desc, m.Name(), i)
|
|
}
|
|
|
|
g.Printf("\n")
|
|
g.Printf("@implementation %s%s {\n", g.namePrefix, obj.Name())
|
|
g.Printf("}\n\n")
|
|
g.Printf("- (id)initWithRef:(id)ref {\n")
|
|
g.Indent()
|
|
g.Printf("self = [super init];\n")
|
|
g.Printf("if (self) { _ref = ref; }\n")
|
|
g.Printf("return self;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
|
|
for _, f := range fields {
|
|
g.genGetter(desc, f)
|
|
g.genSetter(desc, f)
|
|
}
|
|
|
|
for _, m := range methods {
|
|
s := g.funcSummary(m)
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(desc+"_DESCRIPTOR_", desc+"_"+m.Name()+"_", s, true)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
g.Printf("@end\n")
|
|
}
|
|
|
|
func (g *objcGen) errorf(format string, args ...interface{}) {
|
|
g.err = append(g.err, fmt.Errorf(format, args...))
|
|
}
|
|
|
|
func (g *objcGen) refTypeBase(typ types.Type) string {
|
|
switch typ := typ.(type) {
|
|
case *types.Pointer:
|
|
if _, ok := typ.Elem().(*types.Named); ok {
|
|
return g.objcType(typ.Elem())
|
|
}
|
|
case *types.Named:
|
|
n := typ.Obj()
|
|
if n.Pkg() == g.pkg {
|
|
switch typ.Underlying().(type) {
|
|
case *types.Interface, *types.Struct:
|
|
return g.namePrefix + n.Name()
|
|
}
|
|
}
|
|
}
|
|
|
|
// fallback to whatever objcType returns. This must not happen.
|
|
panic(fmt.Sprintf("wtf: %+T", typ))
|
|
return g.objcType(typ)
|
|
}
|
|
|
|
func (g *objcGen) objcFieldType(t types.Type) string {
|
|
if isErrorType(t) {
|
|
return "NSString*"
|
|
}
|
|
return g.objcType(t)
|
|
}
|
|
|
|
func (g *objcGen) objcType(typ types.Type) string {
|
|
if isErrorType(typ) {
|
|
return "NSError*"
|
|
}
|
|
|
|
switch typ := typ.(type) {
|
|
case *types.Basic:
|
|
switch typ.Kind() {
|
|
case types.Bool, types.UntypedBool:
|
|
return "BOOL"
|
|
case types.Int:
|
|
return "int"
|
|
case types.Int8:
|
|
return "int8_t"
|
|
case types.Int16:
|
|
return "int16_t"
|
|
case types.Int32, types.UntypedRune: // types.Rune
|
|
return "int32_t"
|
|
case types.Int64, types.UntypedInt:
|
|
return "int64_t"
|
|
case types.Uint8:
|
|
// byte is an alias of uint8, and the alias is lost.
|
|
return "byte"
|
|
case types.Uint16:
|
|
return "uint16_t"
|
|
case types.Uint32:
|
|
return "uint32_t"
|
|
case types.Uint64:
|
|
return "uint64_t"
|
|
case types.Float32:
|
|
return "float"
|
|
case types.Float64, types.UntypedFloat:
|
|
return "double"
|
|
case types.String, types.UntypedString:
|
|
return "NSString*"
|
|
default:
|
|
g.errorf("unsupported type: %s", typ)
|
|
return "TODO"
|
|
}
|
|
case *types.Slice:
|
|
elem := g.objcType(typ.Elem())
|
|
// Special case: NSData seems to be a better option for byte slice.
|
|
if elem == "byte" {
|
|
return "NSData*"
|
|
}
|
|
// TODO(hyangah): support other slice types: NSArray or CFArrayRef.
|
|
// Investigate the performance implication.
|
|
g.errorf("unsupported type: %s", typ)
|
|
return "TODO"
|
|
case *types.Pointer:
|
|
if _, ok := typ.Elem().(*types.Named); ok {
|
|
return g.objcType(typ.Elem()) + "*"
|
|
}
|
|
g.errorf("unsupported pointer to type: %s", typ)
|
|
return "TODO"
|
|
case *types.Named:
|
|
n := typ.Obj()
|
|
if n.Pkg() != g.pkg {
|
|
g.errorf("type %s is in package %s; only types defined in package %s is supported", n.Name(), n.Pkg().Name(), g.pkg.Name())
|
|
return "TODO"
|
|
}
|
|
switch t := typ.Underlying().(type) {
|
|
case *types.Interface:
|
|
if makeIfaceSummary(t).implementable {
|
|
return "id<" + g.namePrefix + n.Name() + ">"
|
|
} else {
|
|
return g.namePrefix + n.Name() + "*"
|
|
}
|
|
case *types.Struct:
|
|
return g.namePrefix + n.Name()
|
|
}
|
|
g.errorf("unsupported, named type %s", typ)
|
|
return "TODO"
|
|
default:
|
|
g.errorf("unsupported type: %#+v, %s", typ, typ)
|
|
return "TODO"
|
|
}
|
|
}
|