blob: 181b17a0aece8369e09e5119b096094a45ed75b3 [file] [log] [blame]
// 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"
}
}