Fixes golang/go#46956 Change-Id: Ifa1b8e7f2a0e46034f0eccc32249f69a064199e3 GitHub-Last-Rev: 175fa08b1696fca4c11b35b691e0df550ed66864 GitHub-Pull-Request: golang/mobile#67 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/331429 Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com> Reviewed-by: Hajime Hoshi <hajimehoshi@gmail.com> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1429 lines
37 KiB
Go
1429 lines
37 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/types"
|
|
"math"
|
|
"strings"
|
|
|
|
"golang.org/x/mobile/internal/importers/objc"
|
|
)
|
|
|
|
// TODO(hyangah): handle method name conflicts.
|
|
// - struct with SetF method and exported F field.
|
|
// - method names conflicting with NSObject methods. e.g. Init
|
|
// - interface type with InitWithRef.
|
|
|
|
// TODO(hyangah): error code/domain propagation
|
|
|
|
type ObjcGen struct {
|
|
Prefix string // prefix arg passed by flag.
|
|
|
|
*Generator
|
|
|
|
// fields set by init.
|
|
namePrefix string
|
|
// Map of all wrapped Objc types
|
|
wrapMap map[string]*objc.Named
|
|
// Structs that embeds Objc wrapper types.
|
|
ostructs map[*types.TypeName]*objcClassInfo
|
|
modules []string
|
|
// Constructors is a map from Go struct types to a list
|
|
// of exported constructor functions for the type, on the form
|
|
// func New<Type>(...) *Type
|
|
constructors map[*types.TypeName][]*types.Func
|
|
}
|
|
|
|
type objcClassInfo struct {
|
|
// The Objc class this class extends.
|
|
extends *objc.Named
|
|
// All classes and protocols this class extends and conforms to.
|
|
supers []*objc.Named
|
|
methods map[string]*objc.Func
|
|
}
|
|
|
|
func (g *ObjcGen) Init(wrappers []*objc.Named) {
|
|
g.Generator.Init()
|
|
g.namePrefix = g.namePrefixOf(g.Pkg)
|
|
g.wrapMap = make(map[string]*objc.Named)
|
|
g.constructors = make(map[*types.TypeName][]*types.Func)
|
|
modMap := make(map[string]struct{})
|
|
for _, w := range wrappers {
|
|
g.wrapMap[w.GoName] = w
|
|
if _, exists := modMap[w.Module]; !exists {
|
|
if !w.Generated {
|
|
g.modules = append(g.modules, w.Module)
|
|
}
|
|
modMap[w.Module] = struct{}{}
|
|
}
|
|
}
|
|
if _, exists := modMap["Foundation"]; !exists {
|
|
g.modules = append(g.modules, "Foundation")
|
|
}
|
|
g.ostructs = make(map[*types.TypeName]*objcClassInfo)
|
|
for _, s := range g.structs {
|
|
embds := embeddedObjcTypes(s.t)
|
|
if len(embds) == 0 {
|
|
continue
|
|
}
|
|
inf := &objcClassInfo{
|
|
methods: make(map[string]*objc.Func),
|
|
}
|
|
for _, n := range embds {
|
|
t := g.wrapMap[n]
|
|
for _, f := range t.AllMethods {
|
|
inf.methods[f.GoName] = f
|
|
}
|
|
inf.supers = append(inf.supers, t)
|
|
if !t.Protocol {
|
|
if inf.extends != nil {
|
|
g.errorf("%s embeds more than one ObjC class; only one is allowed.", s.obj)
|
|
}
|
|
inf.extends = t
|
|
}
|
|
}
|
|
g.ostructs[s.obj] = inf
|
|
}
|
|
for _, f := range g.funcs {
|
|
if t := g.constructorType(f); t != nil {
|
|
g.constructors[t] = append(g.constructors[t], f)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) namePrefixOf(pkg *types.Package) string {
|
|
if pkg == nil {
|
|
return "Universe"
|
|
}
|
|
p := g.Prefix
|
|
return p + strings.Title(pkg.Name())
|
|
}
|
|
|
|
func (g *ObjcGen) GenGoH() error {
|
|
var pkgPath string
|
|
if g.Pkg != nil {
|
|
pkgPath = g.Pkg.Path()
|
|
}
|
|
g.Printf(objcPreamble, pkgPath, g.gobindOpts(), pkgPath)
|
|
g.Printf("#ifndef __GO_%s_H__\n", g.pkgName)
|
|
g.Printf("#define __GO_%s_H__\n\n", g.pkgName)
|
|
g.Printf("#include <stdint.h>\n")
|
|
g.Printf("#include <objc/objc.h>\n")
|
|
|
|
for _, i := range g.interfaces {
|
|
if !i.summary.implementable {
|
|
continue
|
|
}
|
|
for _, m := range i.summary.callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", i.obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
g.genInterfaceMethodSignature(m, i.obj.Name(), true, g.paramName)
|
|
g.Printf("\n")
|
|
}
|
|
}
|
|
|
|
g.Printf("#endif\n")
|
|
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *ObjcGen) GenH() error {
|
|
var pkgPath string
|
|
if g.Pkg != nil {
|
|
pkgPath = g.Pkg.Path()
|
|
}
|
|
g.Printf(objcPreamble, pkgPath, g.gobindOpts(), pkgPath)
|
|
g.Printf("#ifndef __%s_H__\n", g.namePrefix)
|
|
g.Printf("#define __%s_H__\n", g.namePrefix)
|
|
g.Printf("\n")
|
|
for _, m := range g.modules {
|
|
g.Printf("@import %s;\n", m)
|
|
}
|
|
g.Printf("#include \"ref.h\"\n")
|
|
if g.Pkg != nil {
|
|
g.Printf("#include \"Universe.objc.h\"\n\n")
|
|
}
|
|
|
|
if g.Pkg != nil {
|
|
for _, pkg := range g.Pkg.Imports() {
|
|
if g.validPkg(pkg) {
|
|
g.Printf("#include %q\n", g.namePrefixOf(pkg)+".objc.h")
|
|
}
|
|
}
|
|
}
|
|
g.Printf("\n")
|
|
|
|
// Forward declaration of @class and @protocol
|
|
for _, s := range g.structs {
|
|
g.Printf("@class %s%s;\n", g.namePrefix, s.obj.Name())
|
|
}
|
|
for _, i := range g.interfaces {
|
|
g.Printf("@protocol %s%s;\n", g.namePrefix, i.obj.Name())
|
|
if i.summary.implementable {
|
|
g.Printf("@class %s%s;\n", g.namePrefix, i.obj.Name())
|
|
// Forward declaration for other cases will be handled at the beginning of GenM.
|
|
}
|
|
}
|
|
if len(g.structs) > 0 || len(g.interfaces) > 0 {
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// @interfaces
|
|
for _, i := range g.interfaces {
|
|
g.genInterfaceH(i.obj, i.t)
|
|
g.Printf("\n")
|
|
}
|
|
for _, s := range g.structs {
|
|
g.genStructH(s.obj, s.t)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// const
|
|
// TODO: prefix with k?, or use a class method?
|
|
for _, obj := range g.constants {
|
|
if _, ok := obj.Type().(*types.Basic); !ok || !g.isSupported(obj.Type()) {
|
|
g.Printf("// skipped const %s with unsupported type: %s\n\n", obj.Name(), obj.Type())
|
|
continue
|
|
}
|
|
g.objcdoc(g.docs[obj.Name()].Doc())
|
|
switch b := obj.Type().(*types.Basic); b.Kind() {
|
|
case types.String, types.UntypedString:
|
|
g.Printf("FOUNDATION_EXPORT NSString* _Nonnull 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")
|
|
}
|
|
|
|
// var
|
|
if len(g.vars) > 0 {
|
|
g.Printf("@interface %s : NSObject\n", g.namePrefix)
|
|
for _, obj := range g.vars {
|
|
if t := obj.Type(); !g.isSupported(t) {
|
|
g.Printf("// skipped variable %s with unsupported type: %s\n\n", obj.Name(), t)
|
|
continue
|
|
}
|
|
objcType := g.objcType(obj.Type())
|
|
g.objcdoc(g.docs[obj.Name()].Doc())
|
|
g.Printf("+ (%s) %s;\n", objcType, objcNameReplacer(lowerFirst(obj.Name())))
|
|
g.Printf("+ (void) set%s:(%s)v;\n", obj.Name(), objcType)
|
|
g.Printf("\n")
|
|
}
|
|
g.Printf("@end\n\n")
|
|
}
|
|
|
|
// static functions.
|
|
for _, obj := range g.funcs {
|
|
g.genFuncH(obj)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
for _, i := range g.interfaces {
|
|
if i.summary.implementable {
|
|
g.Printf("@class %s%s;\n\n", g.namePrefix, i.obj.Name())
|
|
}
|
|
}
|
|
for _, i := range g.interfaces {
|
|
if i.summary.implementable {
|
|
// @interface Interface -- similar to what genStructH does.
|
|
g.genInterfaceInterface(i.obj, i.summary, true)
|
|
g.Printf("\n")
|
|
}
|
|
}
|
|
|
|
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 != "" {
|
|
opts = append(opts, fmt.Sprintf("-prefix=%q", g.Prefix))
|
|
}
|
|
return strings.Join(opts, " ")
|
|
}
|
|
|
|
func (g *ObjcGen) GenM() error {
|
|
var pkgPath string
|
|
if g.Pkg != nil {
|
|
pkgPath = g.Pkg.Path()
|
|
}
|
|
g.Printf(objcPreamble, pkgPath, g.gobindOpts(), pkgPath)
|
|
g.Printf("#include <Foundation/Foundation.h>\n")
|
|
g.Printf("#include \"seq.h\"\n")
|
|
g.Printf("#include \"_cgo_export.h\"\n")
|
|
g.Printf("#include %q\n", g.namePrefix+".objc.h")
|
|
g.Printf("\n")
|
|
|
|
// struct
|
|
for _, s := range g.structs {
|
|
g.genStructM(s.obj, s.t)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// interface
|
|
var needProxy []*types.TypeName
|
|
for _, i := range g.interfaces {
|
|
if g.genInterfaceM(i.obj, i.t) {
|
|
needProxy = append(needProxy, i.obj)
|
|
}
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// const
|
|
for _, o := range g.constants {
|
|
g.genConstM(o)
|
|
}
|
|
if len(g.constants) > 0 {
|
|
g.Printf("\n")
|
|
}
|
|
|
|
// vars
|
|
if len(g.vars) > 0 {
|
|
g.Printf("@implementation %s\n", g.namePrefix)
|
|
for _, o := range g.vars {
|
|
g.genVarM(o)
|
|
}
|
|
g.Printf("@end\n\n")
|
|
}
|
|
|
|
g.Printf("\n")
|
|
|
|
for _, obj := range g.funcs {
|
|
if !g.isSigSupported(obj.Type()) {
|
|
g.Printf("// skipped function %s with unsupported parameter or return types\n\n", obj.Name())
|
|
continue
|
|
}
|
|
g.genFuncM(obj)
|
|
g.Printf("\n")
|
|
}
|
|
|
|
for _, i := range g.interfaces {
|
|
for _, m := range i.summary.callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", i.obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
g.genInterfaceMethodProxy(i.obj, m)
|
|
}
|
|
}
|
|
|
|
g.Printf("__attribute__((constructor)) static void init() {\n")
|
|
g.Indent()
|
|
g.Printf("init_seq();\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *ObjcGen) genVarM(o *types.Var) {
|
|
if t := o.Type(); !g.isSupported(t) {
|
|
g.Printf("// skipped variable %s with unsupported type: %s\n\n", o.Name(), t)
|
|
return
|
|
}
|
|
objcType := g.objcType(o.Type())
|
|
|
|
// setter
|
|
g.Printf("+ (void) set%s:(%s)v {\n", o.Name(), objcType)
|
|
g.Indent()
|
|
g.genWrite("v", o.Type(), modeRetained)
|
|
g.Printf("var_set%s_%s(_v);\n", g.pkgPrefix, o.Name())
|
|
g.genRelease("v", o.Type(), modeRetained)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
|
|
// getter
|
|
g.Printf("+ (%s) %s {\n", objcType, objcNameReplacer(lowerFirst(o.Name())))
|
|
g.Indent()
|
|
g.Printf("%s r0 = ", g.cgoType(o.Type()))
|
|
g.Printf("var_get%s_%s();\n", g.pkgPrefix, o.Name())
|
|
g.genRead("_r0", "r0", o.Type(), modeRetained)
|
|
g.Printf("return _r0;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genConstM(o *types.Const) {
|
|
if _, ok := o.Type().(*types.Basic); !ok || !g.isSupported(o.Type()) {
|
|
g.Printf("// skipped const %s with unsupported type: %s\n\n", o.Name(), o.Type())
|
|
return
|
|
}
|
|
cName := fmt.Sprintf("%s%s", g.namePrefix, o.Name())
|
|
objcType := 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().ExactString())
|
|
|
|
case types.Int, types.Int8, types.Int16, types.Int32:
|
|
g.Printf("const %s %s = %s;\n", objcType, 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(), objcType)
|
|
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", objcType, cName, f)
|
|
|
|
default:
|
|
g.errorf("unsupported const type %s for %s", b, o.Name())
|
|
}
|
|
}
|
|
|
|
type funcSummary struct {
|
|
name string
|
|
goname string
|
|
ret string
|
|
sig *types.Signature
|
|
params, retParams []paramInfo
|
|
hasself bool
|
|
initName string
|
|
}
|
|
|
|
type paramInfo struct {
|
|
typ types.Type
|
|
name string
|
|
}
|
|
|
|
func (g *ObjcGen) funcSummary(obj *types.TypeName, f *types.Func) *funcSummary {
|
|
sig := f.Type().(*types.Signature)
|
|
s := &funcSummary{goname: f.Name(), sig: sig}
|
|
var om *objc.Func
|
|
var sigElems []string
|
|
oinf := g.ostructs[obj]
|
|
if oinf != nil {
|
|
om = oinf.methods[f.Name()]
|
|
}
|
|
if om != nil {
|
|
sigElems = strings.Split(om.Sig, ":")
|
|
s.name = sigElems[0]
|
|
} else {
|
|
s.name = f.Name()
|
|
}
|
|
params := sig.Params()
|
|
first := 0
|
|
if oinf != nil {
|
|
if params.Len() > 0 {
|
|
v := params.At(0)
|
|
if v.Name() == "self" {
|
|
t := v.Type()
|
|
if t, ok := t.(*types.Named); ok {
|
|
if pkg := t.Obj().Pkg(); pkgFirstElem(pkg) == "ObjC" {
|
|
s.hasself = true
|
|
module := pkg.Path()[len("ObjC/"):]
|
|
typName := module + "." + t.Obj().Name()
|
|
exp := g.namePrefix + "." + obj.Name()
|
|
if typName != exp {
|
|
g.errorf("the type %s of the `this` argument to method %s is not %s", typName, f.Name(), exp)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for i := first; i < params.Len(); i++ {
|
|
p := params.At(i)
|
|
v := paramInfo{
|
|
typ: p.Type(),
|
|
}
|
|
if om != nil {
|
|
v.name = sigElems[i-first]
|
|
} else {
|
|
v.name = g.paramName(params, i)
|
|
}
|
|
s.params = append(s.params, v)
|
|
}
|
|
if obj != nil {
|
|
if pref := "New" + obj.Name(); strings.Index(f.Name(), pref) != -1 {
|
|
s.initName = "init" + f.Name()[len(pref):]
|
|
}
|
|
}
|
|
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_"
|
|
}
|
|
typ := res.At(0).Type()
|
|
s.retParams = append(s.retParams, paramInfo{
|
|
typ: typ,
|
|
name: name,
|
|
})
|
|
if isNullableType(typ) {
|
|
s.ret = g.objcType(typ) // Return is nullable, so satisfies the ObjC/Swift error protocol
|
|
} else {
|
|
s.ret = "BOOL" // Return is not nullable, must use an output parameter and return bool
|
|
}
|
|
|
|
if !isErrorType(res.At(1).Type()) {
|
|
g.errorf("second result value must be of type error: %s", f)
|
|
return nil
|
|
}
|
|
s.retParams = append(s.retParams, paramInfo{
|
|
typ: res.At(1).Type(),
|
|
name: "error", // TODO(hyangah): name collision check.
|
|
})
|
|
default:
|
|
// TODO(hyangah): relax the constraint on multiple return params.
|
|
g.errorf("too many result values: %s", f)
|
|
return nil
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *funcSummary) asFunc(g *ObjcGen) string {
|
|
var params []string
|
|
for _, p := range s.params {
|
|
params = append(params, g.objcParamType(p.typ)+" "+p.name)
|
|
}
|
|
skip := 0
|
|
if s.returnsVal() {
|
|
skip = 1
|
|
}
|
|
for _, p := range s.retParams[skip:] {
|
|
params = append(params, g.objcType(p.typ)+"* _Nullable "+p.name)
|
|
}
|
|
paramContents := "void"
|
|
if len(params) > 0 {
|
|
paramContents = strings.Join(params, ", ")
|
|
}
|
|
return fmt.Sprintf("%s %s%s(%s)", s.ret, g.namePrefix, s.name, paramContents)
|
|
}
|
|
|
|
func (s *funcSummary) asMethod(g *ObjcGen) string {
|
|
return fmt.Sprintf("(%s)%s%s", s.ret, objcNameReplacer(lowerFirst(s.name)), s.asSignature(g))
|
|
}
|
|
|
|
func (s *funcSummary) asSignature(g *ObjcGen) string {
|
|
var params []string
|
|
skip := 0
|
|
if s.hasself {
|
|
skip = 1
|
|
}
|
|
for i, p := range s.params[skip:] {
|
|
var key string
|
|
if i != 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcParamType(p.typ), p.name))
|
|
}
|
|
skip = 0
|
|
if s.returnsVal() {
|
|
skip = 1
|
|
}
|
|
for _, p := range s.retParams[skip:] {
|
|
var key string
|
|
if len(params) > 0 {
|
|
key = p.name
|
|
}
|
|
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcType(p.typ)+"* _Nullable", p.name))
|
|
}
|
|
return strings.Join(params, " ")
|
|
}
|
|
|
|
func (s *funcSummary) asInitSignature(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.objcParamType(p.typ), p.name))
|
|
}
|
|
return 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))
|
|
}
|
|
skip := 0
|
|
if s.returnsVal() {
|
|
skip = 1
|
|
}
|
|
for _, p := range s.retParams[skip:] {
|
|
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", objcNameReplacer(lowerFirst(s.name)), strings.Join(params, " "))
|
|
}
|
|
|
|
func (s *funcSummary) returnsVal() bool {
|
|
return (len(s.retParams) == 1 && !isErrorType(s.retParams[0].typ)) || (len(s.retParams) == 2 && isNullableType(s.retParams[0].typ))
|
|
}
|
|
|
|
func (g *ObjcGen) paramName(params *types.Tuple, pos int) string {
|
|
name := basicParamName(params, pos)
|
|
return objcNameReplacer(name)
|
|
}
|
|
|
|
func (g *ObjcGen) genFuncH(obj *types.Func) {
|
|
if !g.isSigSupported(obj.Type()) {
|
|
g.Printf("// skipped function %s with unsupported parameter or return types\n\n", obj.Name())
|
|
return
|
|
}
|
|
if s := g.funcSummary(nil, obj); s != nil {
|
|
g.objcdoc(g.docs[obj.Name()].Doc())
|
|
g.Printf("FOUNDATION_EXPORT %s;\n", s.asFunc(g))
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) genFuncM(obj *types.Func) {
|
|
s := g.funcSummary(nil, obj)
|
|
if s == nil {
|
|
return
|
|
}
|
|
g.Printf("%s {\n", s.asFunc(g))
|
|
g.Indent()
|
|
g.genFunc(s, "")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genGetter(oName string, f *types.Var) {
|
|
t := f.Type()
|
|
g.Printf("- (%s)%s {\n", g.objcType(t), objcNameReplacer(lowerFirst(f.Name())))
|
|
g.Indent()
|
|
g.Printf("int32_t refnum = go_seq_go_to_refnum(self._ref);\n")
|
|
g.Printf("%s r0 = ", g.cgoType(f.Type()))
|
|
g.Printf("proxy%s_%s_%s_Get(refnum);\n", g.pkgPrefix, oName, f.Name())
|
|
g.genRead("_r0", "r0", f.Type(), modeRetained)
|
|
g.Printf("return _r0;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genSetter(oName string, f *types.Var) {
|
|
t := f.Type()
|
|
|
|
g.Printf("- (void)set%s:(%s)v {\n", f.Name(), g.objcType(t))
|
|
g.Indent()
|
|
g.Printf("int32_t refnum = go_seq_go_to_refnum(self._ref);\n")
|
|
g.genWrite("v", f.Type(), modeRetained)
|
|
g.Printf("proxy%s_%s_%s_Set(refnum, _v);\n", g.pkgPrefix, oName, f.Name())
|
|
g.genRelease("v", f.Type(), modeRetained)
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genWrite(varName string, t types.Type, mode varMode) {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.String:
|
|
g.Printf("nstring _%s = go_seq_from_objc_string(%s);\n", varName, varName)
|
|
default:
|
|
g.Printf("%s _%s = (%s)%s;\n", g.cgoType(t), varName, g.cgoType(t), varName)
|
|
}
|
|
case *types.Slice:
|
|
switch e := t.Elem().(type) {
|
|
case *types.Basic:
|
|
switch e.Kind() {
|
|
case types.Uint8: // Byte.
|
|
g.Printf("nbyteslice _%s = go_seq_from_objc_bytearray(%s, %d);\n", varName, varName, toCFlag(mode == modeRetained))
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
case *types.Named:
|
|
switch u := t.Underlying().(type) {
|
|
case *types.Interface:
|
|
g.genRefWrite(varName)
|
|
default:
|
|
g.errorf("unsupported named type: %s / %T", u, u)
|
|
}
|
|
case *types.Pointer:
|
|
g.genRefWrite(varName)
|
|
default:
|
|
g.Printf("%s _%s = (%s)%s;\n", g.cgoType(t), varName, g.cgoType(t), varName)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) genRefWrite(varName string) {
|
|
g.Printf("int32_t _%s;\n", varName)
|
|
g.Printf("if ([%s conformsToProtocol:@protocol(goSeqRefInterface)]) {\n", varName)
|
|
g.Indent()
|
|
g.Printf("id<goSeqRefInterface> %[1]s_proxy = (id<goSeqRefInterface>)(%[1]s);\n", varName)
|
|
g.Printf("_%s = go_seq_go_to_refnum(%s_proxy._ref);\n", varName, varName)
|
|
g.Outdent()
|
|
g.Printf("} else {\n")
|
|
g.Indent()
|
|
g.Printf("_%s = go_seq_to_refnum(%s);\n", varName, varName)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genRefRead(toName, fromName string, t types.Type) {
|
|
ptype := g.refTypeBase(t)
|
|
g.Printf("%s* %s = nil;\n", ptype, toName)
|
|
g.Printf("GoSeqRef* %s_ref = go_seq_from_refnum(%s);\n", toName, fromName)
|
|
g.Printf("if (%s_ref != NULL) {\n", toName)
|
|
g.Printf(" %s = %s_ref.obj;\n", toName, toName)
|
|
g.Printf(" if (%s == nil) {\n", toName)
|
|
if isObjcType(t) {
|
|
g.Printf(" LOG_FATAL(@\"unexpected NULL reference\");\n")
|
|
} else {
|
|
g.Printf(" %s = [[%s alloc] initWithRef:%s_ref];\n", toName, ptype, toName)
|
|
}
|
|
g.Printf(" }\n")
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genRead(toName, fromName string, t types.Type, mode varMode) {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.String:
|
|
g.Printf("NSString *%s = go_seq_to_objc_string(%s);\n", toName, fromName)
|
|
case types.Bool:
|
|
g.Printf("BOOL %s = %s ? YES : NO;\n", toName, fromName)
|
|
default:
|
|
g.Printf("%s %s = (%s)%s;\n", g.objcType(t), toName, g.objcType(t), fromName)
|
|
}
|
|
case *types.Slice:
|
|
switch e := t.Elem().(type) {
|
|
case *types.Basic:
|
|
switch e.Kind() {
|
|
case types.Uint8: // Byte.
|
|
g.Printf("NSData *%s = go_seq_to_objc_bytearray(%s, %d);\n", toName, fromName, toCFlag(mode == modeRetained))
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
case *types.Pointer:
|
|
switch t := t.Elem().(type) {
|
|
case *types.Named:
|
|
g.genRefRead(toName, fromName, types.NewPointer(t))
|
|
default:
|
|
g.errorf("unsupported type %s", t)
|
|
}
|
|
case *types.Named:
|
|
switch t.Underlying().(type) {
|
|
case *types.Interface, *types.Pointer:
|
|
g.genRefRead(toName, fromName, t)
|
|
default:
|
|
g.errorf("unsupported, direct named type %s", t)
|
|
}
|
|
default:
|
|
g.Printf("%s %s = (%s)%s;\n", g.objcType(t), toName, g.objcType(t), fromName)
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) genFunc(s *funcSummary, objName string) {
|
|
skip := 0
|
|
if objName != "" {
|
|
g.Printf("int32_t refnum = go_seq_go_to_refnum(self._ref);\n")
|
|
if s.hasself {
|
|
skip = 1
|
|
g.Printf("int32_t _self = go_seq_to_refnum(self);\n")
|
|
}
|
|
}
|
|
for _, p := range s.params[skip:] {
|
|
g.genWrite(p.name, p.typ, modeTransient)
|
|
}
|
|
resPrefix := ""
|
|
if len(s.retParams) > 0 {
|
|
if len(s.retParams) == 1 {
|
|
g.Printf("%s r0 = ", g.cgoType(s.retParams[0].typ))
|
|
} else {
|
|
resPrefix = "res."
|
|
g.Printf("struct proxy%s_%s_%s_return res = ", g.pkgPrefix, objName, s.goname)
|
|
}
|
|
}
|
|
g.Printf("proxy%s_%s_%s(", g.pkgPrefix, objName, s.goname)
|
|
if objName != "" {
|
|
g.Printf("refnum")
|
|
if s.hasself {
|
|
g.Printf(", _self")
|
|
}
|
|
}
|
|
for i, p := range s.params[skip:] {
|
|
if i > 0 || objName != "" {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("_%s", p.name)
|
|
}
|
|
g.Printf(");\n")
|
|
for _, p := range s.params {
|
|
g.genRelease(p.name, p.typ, modeTransient)
|
|
}
|
|
|
|
for i, r := range s.retParams {
|
|
g.genRead("_"+r.name, fmt.Sprintf("%sr%d", resPrefix, i), r.typ, modeRetained)
|
|
}
|
|
skip = 0
|
|
if s.returnsVal() {
|
|
skip = 1
|
|
}
|
|
for _, p := range s.retParams[skip:] {
|
|
if isErrorType(p.typ) {
|
|
g.Printf("if (_%s != nil && %s != nil) {\n", p.name, p.name)
|
|
g.Indent()
|
|
g.Printf("*%s = _%s;\n", p.name, p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("*%s = _%s;\n", p.name, p.name)
|
|
}
|
|
}
|
|
|
|
if n := len(s.retParams); n > 0 {
|
|
var (
|
|
first = s.retParams[0]
|
|
last = s.retParams[n-1]
|
|
)
|
|
if (n == 1 && isErrorType(last.typ)) || (n == 2 && !isNullableType(first.typ) && isErrorType(last.typ)) {
|
|
g.Printf("return (_%s == nil);\n", last.name)
|
|
} else {
|
|
if s.returnsVal() && isErrorType(last.typ) {
|
|
g.Printf("if (_%s != nil) {\n", last.name)
|
|
g.Indent()
|
|
g.Printf("return nil;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
g.Printf("return _%s;\n", first.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) genInterfaceInterface(obj *types.TypeName, summary ifaceSummary, isProtocol bool) {
|
|
doc := g.docs[obj.Name()]
|
|
g.objcdoc(doc.Doc())
|
|
g.Printf("@interface %[1]s%[2]s : ", g.namePrefix, obj.Name())
|
|
if isErrorType(obj.Type()) {
|
|
g.Printf("NSError")
|
|
} else {
|
|
g.Printf("NSObject")
|
|
}
|
|
prots := []string{"goSeqRefInterface"}
|
|
if isProtocol {
|
|
prots = append(prots, fmt.Sprintf("%[1]s%[2]s", g.namePrefix, obj.Name()))
|
|
}
|
|
g.Printf(" <%s>", strings.Join(prots, ", "))
|
|
g.Printf(" {\n}\n")
|
|
g.Printf("@property(strong, readonly) _Nonnull id _ref;\n")
|
|
g.Printf("\n")
|
|
g.Printf("- (nonnull instancetype)initWithRef:(_Nonnull id)ref;\n")
|
|
for _, m := range summary.callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
s := g.funcSummary(nil, m)
|
|
g.objcdoc(doc.Member(m.Name()))
|
|
g.Printf("- %s;\n", s.asMethod(g))
|
|
}
|
|
g.Printf("@end\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genInterfaceH(obj *types.TypeName, t *types.Interface) {
|
|
doc := g.docs[obj.Name()]
|
|
summary := makeIfaceSummary(t)
|
|
if !summary.implementable {
|
|
g.genInterfaceInterface(obj, summary, false)
|
|
return
|
|
}
|
|
g.Printf("@protocol %s%s <NSObject>\n", g.namePrefix, obj.Name())
|
|
for _, m := range makeIfaceSummary(t).callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
s := g.funcSummary(nil, m)
|
|
g.objcdoc(doc.Member(m.Name()))
|
|
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)
|
|
|
|
// @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("- (nonnull instancetype)initWithRef:(id)ref {\n")
|
|
g.Indent()
|
|
if isErrorType(obj.Type()) {
|
|
g.Printf("if (self) {\n")
|
|
g.Printf(" __ref = ref;\n")
|
|
g.Printf(" self = [super initWithDomain:@\"go\" code:1 userInfo:@{NSLocalizedDescriptionKey: [self error]}];\n")
|
|
g.Printf("}\n")
|
|
} else {
|
|
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 {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
s := g.funcSummary(nil, m)
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(s, obj.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
g.Printf("@end\n")
|
|
g.Printf("\n")
|
|
|
|
return summary.implementable
|
|
}
|
|
|
|
func (g *ObjcGen) genInterfaceMethodProxy(obj *types.TypeName, m *types.Func) {
|
|
oName := obj.Name()
|
|
s := g.funcSummary(nil, m)
|
|
g.genInterfaceMethodSignature(m, oName, false, g.paramName)
|
|
g.Indent()
|
|
g.Printf("@autoreleasepool {\n")
|
|
g.Indent()
|
|
g.Printf("%s* o = go_seq_objc_from_refnum(refnum);\n", g.refTypeBase(obj.Type()))
|
|
for _, p := range s.params {
|
|
g.genRead("_"+p.name, p.name, p.typ, modeTransient)
|
|
}
|
|
|
|
// call method
|
|
for _, p := range s.retParams {
|
|
if isErrorType(p.typ) {
|
|
g.Printf("NSError* %s = nil;\n", p.name)
|
|
} else {
|
|
g.Printf("%s %s;\n", g.objcType(p.typ), p.name)
|
|
}
|
|
}
|
|
|
|
if isErrorType(obj.Type()) && m.Name() == "Error" {
|
|
// As a special case, ObjC NSErrors are passed to Go pretending to implement the Go error interface.
|
|
// They don't actually have an Error method, so calls to to it needs to be rerouted.
|
|
g.Printf("%s = [o localizedDescription];\n", s.retParams[0].name)
|
|
} else {
|
|
if s.ret == "void" {
|
|
g.Printf("[o %s];\n", s.callMethod(g))
|
|
} else if !s.returnsVal() {
|
|
g.Printf("%s returnVal = [o %s];\n", s.ret, s.callMethod(g))
|
|
} else {
|
|
g.Printf("%s = [o %s];\n", s.retParams[0].name, s.callMethod(g))
|
|
}
|
|
}
|
|
|
|
if len(s.retParams) > 0 {
|
|
if len(s.retParams) == 1 && !isErrorType(s.retParams[0].typ) {
|
|
p := s.retParams[0]
|
|
g.genWrite(p.name, p.typ, modeRetained)
|
|
g.Printf("return _%s;\n", p.name)
|
|
} else {
|
|
var rets []string
|
|
for _, p := range s.retParams {
|
|
if isErrorType(p.typ) {
|
|
g.Printf("NSError *_%s = nil;\n", p.name)
|
|
if !s.returnsVal() {
|
|
g.Printf("if (!returnVal) {\n")
|
|
} else {
|
|
g.Printf("if (%s != nil) {\n", p.name)
|
|
}
|
|
g.Indent()
|
|
g.Printf("_%[1]s = %[1]s;\n", p.name)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.genWrite("_"+p.name, p.typ, modeRetained)
|
|
rets = append(rets, "__"+p.name)
|
|
} else {
|
|
g.genWrite(p.name, p.typ, modeRetained)
|
|
rets = append(rets, "_"+p.name)
|
|
}
|
|
}
|
|
if len(rets) > 1 {
|
|
g.Printf("cproxy%s_%s_%s_return _sres = {\n", g.pkgPrefix, oName, m.Name())
|
|
g.Printf(" %s\n", strings.Join(rets, ", "))
|
|
g.Printf("};\n")
|
|
g.Printf("return _sres;\n")
|
|
} else {
|
|
g.Printf("return %s;\n", rets[0])
|
|
}
|
|
}
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
// genRelease cleans up arguments that weren't copied in genWrite.
|
|
func (g *ObjcGen) genRelease(varName string, t types.Type, mode varMode) {
|
|
switch t := t.(type) {
|
|
case *types.Slice:
|
|
switch e := t.Elem().(type) {
|
|
case *types.Basic:
|
|
switch e.Kind() {
|
|
case types.Uint8: // Byte.
|
|
if mode == modeTransient {
|
|
// If the argument was not mutable, go_seq_from_objc_bytearray created a copy.
|
|
// Free it here.
|
|
g.Printf("if (![%s isKindOfClass:[NSMutableData class]]) {\n", varName)
|
|
g.Printf(" free(_%s.ptr);\n", varName)
|
|
g.Printf("}\n")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *ObjcGen) genStructH(obj *types.TypeName, t *types.Struct) {
|
|
doc := g.docs[obj.Name()]
|
|
g.objcdoc(doc.Doc())
|
|
g.Printf("@interface %s%s : ", g.namePrefix, obj.Name())
|
|
oinf := g.ostructs[obj]
|
|
var prots []string
|
|
if oinf != nil {
|
|
for _, sup := range oinf.supers {
|
|
if !sup.Protocol {
|
|
g.Printf(sup.Name)
|
|
} else {
|
|
prots = append(prots, sup.Name)
|
|
}
|
|
}
|
|
} else {
|
|
g.Printf("NSObject")
|
|
prots = append(prots, "goSeqRefInterface")
|
|
}
|
|
pT := types.NewPointer(obj.Type())
|
|
for _, iface := range g.allIntf {
|
|
p := iface.obj.Pkg()
|
|
if g.Pkg != nil && g.Pkg != p {
|
|
// To avoid header include cycles, only declare implementation of interfaces
|
|
// from imported packages. TODO(elias.naur): Include every interface that
|
|
// doesn't introduce an include cycle.
|
|
found := false
|
|
for _, imp := range g.Pkg.Imports() {
|
|
if imp == p {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
continue
|
|
}
|
|
}
|
|
obj := iface.obj
|
|
if types.AssignableTo(pT, obj.Type()) {
|
|
n := fmt.Sprintf("%s%s", g.namePrefixOf(obj.Pkg()), obj.Name())
|
|
prots = append(prots, n)
|
|
}
|
|
}
|
|
|
|
if len(prots) > 0 {
|
|
g.Printf(" <%s>", strings.Join(prots, ", "))
|
|
}
|
|
g.Printf(" {\n")
|
|
g.Printf("}\n")
|
|
g.Printf("@property(strong, readonly) _Nonnull id _ref;\n")
|
|
g.Printf("\n")
|
|
g.Printf("- (nonnull instancetype)initWithRef:(_Nonnull id)ref;\n")
|
|
cons := g.constructors[obj]
|
|
if oinf == nil {
|
|
for _, f := range cons {
|
|
if !g.isSigSupported(f.Type()) {
|
|
g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", obj.Name(), f.Name())
|
|
continue
|
|
}
|
|
g.genInitH(obj, f)
|
|
}
|
|
}
|
|
if oinf != nil || len(cons) == 0 {
|
|
// default constructor won't return nil
|
|
g.Printf("- (nonnull instancetype)init;\n")
|
|
}
|
|
|
|
// accessors to exported fields.
|
|
for _, f := range exportedFields(t) {
|
|
if t := f.Type(); !g.isSupported(t) {
|
|
g.Printf("// skipped field %s.%s with unsupported type: %s\n\n", obj.Name(), f.Name(), t)
|
|
continue
|
|
}
|
|
name, typ := f.Name(), g.objcType(f.Type())
|
|
g.objcdoc(doc.Member(f.Name()))
|
|
|
|
// properties are atomic by default so explicitly say otherwise
|
|
g.Printf("@property (nonatomic) %s %s;\n", typ, objcNameReplacer(lowerFirst(name)))
|
|
}
|
|
|
|
// exported methods
|
|
for _, m := range exportedMethodSet(types.NewPointer(obj.Type())) {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
s := g.funcSummary(obj, m)
|
|
g.objcdoc(doc.Member(m.Name()))
|
|
g.Printf("- %s;\n", s.asMethod(g))
|
|
}
|
|
g.Printf("@end\n")
|
|
}
|
|
|
|
func (g *ObjcGen) objcdoc(doc string) {
|
|
if doc == "" {
|
|
return
|
|
}
|
|
g.Printf("/**\n * %s */\n", doc)
|
|
}
|
|
|
|
func (g *ObjcGen) genStructM(obj *types.TypeName, t *types.Struct) {
|
|
fields := exportedFields(t)
|
|
methods := exportedMethodSet(types.NewPointer(obj.Type()))
|
|
|
|
g.Printf("\n")
|
|
oinf := g.ostructs[obj]
|
|
g.Printf("@implementation %s%s {\n", g.namePrefix, obj.Name())
|
|
g.Printf("}\n\n")
|
|
g.Printf("- (nonnull instancetype)initWithRef:(_Nonnull 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")
|
|
cons := g.constructors[obj]
|
|
if oinf == nil {
|
|
for _, f := range cons {
|
|
if !g.isSigSupported(f.Type()) {
|
|
g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", obj, f.Name())
|
|
continue
|
|
}
|
|
g.genInitM(obj, f)
|
|
}
|
|
}
|
|
if oinf != nil || len(cons) == 0 {
|
|
g.Printf("- (nonnull instancetype)init {\n")
|
|
g.Indent()
|
|
g.Printf("self = [super init];\n")
|
|
g.Printf("if (self) {\n")
|
|
g.Indent()
|
|
g.Printf("__ref = go_seq_from_refnum(new_%s_%s());\n", g.pkgPrefix, obj.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
g.Printf("return self;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
for _, f := range fields {
|
|
if !g.isSupported(f.Type()) {
|
|
g.Printf("// skipped unsupported field %s with type %s\n\n", f.Name(), f.Type())
|
|
continue
|
|
}
|
|
g.genGetter(obj.Name(), f)
|
|
g.genSetter(obj.Name(), f)
|
|
}
|
|
|
|
for _, m := range methods {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
s := g.funcSummary(obj, m)
|
|
g.Printf("- %s {\n", s.asMethod(g))
|
|
g.Indent()
|
|
g.genFunc(s, obj.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
g.Printf("@end\n\n")
|
|
}
|
|
|
|
func (g *ObjcGen) genInitH(obj *types.TypeName, f *types.Func) {
|
|
s := g.funcSummary(obj, f)
|
|
doc := g.docs[f.Name()]
|
|
g.objcdoc(doc.Doc())
|
|
|
|
// custom inits can return nil in Go so make them nullable
|
|
g.Printf("- (nullable instancetype)%s%s;\n", s.initName, s.asInitSignature(g))
|
|
}
|
|
|
|
func (g *ObjcGen) genInitM(obj *types.TypeName, f *types.Func) {
|
|
s := g.funcSummary(obj, f)
|
|
g.Printf("- (instancetype)%s%s {\n", s.initName, s.asInitSignature(g))
|
|
g.Indent()
|
|
g.Printf("self = [super init];\n")
|
|
g.Printf("if (!self) return nil;\n")
|
|
for _, p := range s.params {
|
|
g.genWrite(p.name, p.typ, modeTransient)
|
|
}
|
|
// Constructors always return a mandatory *T and an optional error
|
|
if len(s.retParams) == 1 {
|
|
g.Printf("%s refnum = ", g.cgoType(s.retParams[0].typ))
|
|
} else {
|
|
g.Printf("struct proxy%s__%s_return res = ", g.pkgPrefix, s.goname)
|
|
}
|
|
g.Printf("proxy%s__%s(", g.pkgPrefix, s.goname)
|
|
for i, p := range s.params {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("_%s", p.name)
|
|
}
|
|
g.Printf(");\n")
|
|
for _, p := range s.params {
|
|
g.genRelease(p.name, p.typ, modeTransient)
|
|
}
|
|
if len(s.retParams) == 2 {
|
|
g.Printf("int32_t refnum = res.r0;\n")
|
|
g.Printf("GoSeqRef *_err = go_seq_from_refnum(res.r1);\n")
|
|
}
|
|
g.Printf("__ref = go_seq_from_refnum(refnum);\n")
|
|
if len(s.retParams) == 2 {
|
|
g.Printf("if (_err != NULL)\n")
|
|
g.Printf(" return nil;\n")
|
|
}
|
|
g.Printf("return self;\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\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 isObjcType(typ) {
|
|
return g.wrapMap[n.Name()].Name
|
|
}
|
|
if isErrorType(typ) || g.validPkg(n.Pkg()) {
|
|
switch typ.Underlying().(type) {
|
|
case *types.Interface, *types.Struct:
|
|
return g.namePrefixOf(n.Pkg()) + n.Name()
|
|
}
|
|
}
|
|
}
|
|
|
|
// fallback to whatever objcType returns. This must not happen.
|
|
return g.objcType(typ)
|
|
}
|
|
|
|
func (g *ObjcGen) objcParamType(t types.Type) string {
|
|
|
|
switch typ := t.(type) {
|
|
case *types.Basic:
|
|
switch typ.Kind() {
|
|
case types.String, types.UntypedString:
|
|
return "NSString* _Nullable"
|
|
}
|
|
}
|
|
|
|
return g.objcType(t)
|
|
|
|
}
|
|
|
|
func (g *ObjcGen) objcType(typ types.Type) string {
|
|
|
|
if isErrorType(typ) {
|
|
return "NSError* _Nullable"
|
|
}
|
|
|
|
switch typ := typ.(type) {
|
|
case *types.Basic:
|
|
switch typ.Kind() {
|
|
case types.Bool, types.UntypedBool:
|
|
return "BOOL"
|
|
case types.Int:
|
|
return "long"
|
|
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* _Nonnull"
|
|
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* _Nullable"
|
|
}
|
|
// 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()) + "* _Nullable"
|
|
}
|
|
g.errorf("unsupported pointer to type: %s", typ)
|
|
return "TODO"
|
|
case *types.Named:
|
|
n := typ.Obj()
|
|
if isObjcType(typ) {
|
|
w := g.wrapMap[n.Name()]
|
|
return w.ObjcType()
|
|
}
|
|
if !isErrorType(typ) && !g.validPkg(n.Pkg()) {
|
|
g.errorf("type %s is in package %s, which is not bound", n.Name(), n.Pkg().Name())
|
|
return "TODO"
|
|
}
|
|
switch t := typ.Underlying().(type) {
|
|
case *types.Interface:
|
|
if makeIfaceSummary(t).implementable {
|
|
return "id<" + g.namePrefixOf(n.Pkg()) + n.Name() + "> _Nullable"
|
|
} else {
|
|
return g.namePrefixOf(n.Pkg()) + n.Name() + "* _Nullable"
|
|
}
|
|
case *types.Struct:
|
|
return g.namePrefixOf(n.Pkg()) + n.Name()
|
|
}
|
|
g.errorf("unsupported, named type %s", typ)
|
|
return "TODO"
|
|
default:
|
|
g.errorf("unsupported type: %#+v, %s", typ, typ)
|
|
return "TODO"
|
|
}
|
|
}
|
|
|
|
// embeddedObjcTypes returns the possible empty list of Objc types embedded
|
|
// in the given struct type.
|
|
func embeddedObjcTypes(t *types.Struct) []string {
|
|
typeSet := make(map[string]struct{})
|
|
var typs []string
|
|
for i := 0; i < t.NumFields(); i++ {
|
|
f := t.Field(i)
|
|
if !f.Exported() {
|
|
continue
|
|
}
|
|
if ft := f.Type(); isObjcType(ft) {
|
|
name := ft.(*types.Named).Obj().Name()
|
|
if _, exists := typeSet[name]; !exists {
|
|
typeSet[name] = struct{}{}
|
|
typs = append(typs, name)
|
|
}
|
|
}
|
|
}
|
|
return typs
|
|
}
|
|
|
|
func isObjcType(t types.Type) bool {
|
|
return typePkgFirstElem(t) == "ObjC"
|
|
}
|
|
|
|
var objcNameReplacer = newNameSanitizer([]string{
|
|
"bool", "bycopy", "byref", "char", "const", "double", "float",
|
|
"id", "in", "init", "inout", "int", "long", "nil", "oneway",
|
|
"out", "self", "short", "signed", "super", "unsigned", "void",
|
|
"volatile"})
|
|
|
|
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.
|
|
|
|
`
|
|
)
|