bind: generate ObjC initializers
For functions on the form
New<T>... (...) *T
or
New<T>... (...) (*T, error)
generate corresponding initializers. The name of an initializer is
the function name where "New<T>" is replaced by "init".
If no functions match for a type *T, generate a default (empty)
initializer that returns new(T). The default initializer mirrors
the default constructor in Java.
Fixes golang/go#20254.
Change-Id: I3c317418fa517d3f2de3f67f400867285b11ea4f
Reviewed-on: https://go-review.googlesource.com/52012
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/bind/genobjc.go b/bind/genobjc.go
index f306d5e..38d723e 100644
--- a/bind/genobjc.go
+++ b/bind/genobjc.go
@@ -33,6 +33,10 @@
// 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 {
@@ -47,6 +51,7 @@
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
@@ -84,6 +89,11 @@
}
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 {
@@ -407,6 +417,7 @@
sig *types.Signature
params, retParams []paramInfo
hasself bool
+ initName string
}
type paramInfo struct {
@@ -462,6 +473,11 @@
}
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:
@@ -532,6 +548,10 @@
}
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 {
@@ -555,7 +575,19 @@
}
params = append(params, fmt.Sprintf("%s:(%s)%s", key, g.objcType(p.typ)+"*", p.name))
}
- return fmt.Sprintf("(%s)%s%s", s.ret, objcNameReplacer(lowerFirst(s.name)), strings.Join(params, " "))
+ 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.objcType(p.typ), p.name))
+ }
+ return strings.Join(params, " ")
}
func (s *funcSummary) callMethod(g *ObjcGen) string {
@@ -1028,9 +1060,19 @@
g.Printf("}\n")
g.Printf("@property(strong, readonly) id _ref;\n")
g.Printf("\n")
- g.Printf("- (id)initWithRef:(id)ref;\n")
- if oinf != nil {
- g.Printf("- (id)init;\n")
+ g.Printf("- (instancetype)initWithRef:(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 {
+ g.Printf("- (instancetype)init;\n")
}
// accessors to exported fields.
@@ -1064,15 +1106,21 @@
oinf := g.ostructs[obj]
g.Printf("@implementation %s%s {\n", g.namePrefix, obj.Name())
g.Printf("}\n\n")
- g.Printf("- (id)initWithRef:(id)ref {\n")
+ g.Printf("- (instancetype)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")
- if oinf != nil {
- g.Printf("- (id)init {\n")
+ cons := g.constructors[obj]
+ if oinf == nil {
+ for _, f := range cons {
+ g.genInitM(obj, f)
+ }
+ }
+ if oinf != nil || len(cons) == 0 {
+ g.Printf("- (instancetype)init {\n")
g.Indent()
g.Printf("self = [super init];\n")
g.Printf("if (self) {\n")
@@ -1109,6 +1157,51 @@
g.Printf("@end\n\n")
}
+func (g *ObjcGen) genInitH(obj *types.TypeName, f *types.Func) {
+ s := g.funcSummary(obj, f)
+ g.Printf("- (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...))
}
diff --git a/bind/objc/SeqTest.m b/bind/objc/SeqTest.m
index 139a9f9..21c395c 100644
--- a/bind/objc/SeqTest.m
+++ b/bind/objc/SeqTest.m
@@ -453,4 +453,28 @@
- (void)testTags {
XCTAssertEqual(42, TestpkgTaggedConst, @"Tagged const must exist");
}
+
+- (void)testConstructors {
+ id<TestpkgInterface> i = [[TestpkgConcrete alloc] init];
+ [i f];
+
+ TestpkgS2 *s = [[TestpkgS2 alloc] init:1 y:2];
+ XCTAssertEqual(3.0, [s sum]);
+ XCTAssertEqualObjects(@"gostring", [s tryTwoStrings:@"go" second:@"string"]);
+
+ TestpkgS3 *s3 __attribute__((unused)) = [[TestpkgS3 alloc] init];
+
+ TestpkgS4 *s4 = [[TestpkgS4 alloc] initWithInt:123];
+ XCTAssertEqual(123, s4.i);
+
+ s4 = [[TestpkgS4 alloc] initWithFloat: 123.456];
+ XCTAssertEqual(123, s4.i);
+
+ s4 = [[TestpkgS4 alloc] initWithBoolAndError: false];
+ XCTAssertEqual(0, s4.i);
+
+ s4 = [[TestpkgS4 alloc] initWithBoolAndError: true];
+ XCTAssertEqual(s4, NULL);
+}
+
@end
diff --git a/bind/testdata/ignore.objc.h.golden b/bind/testdata/ignore.objc.h.golden
index 29e3296..055dde5 100644
--- a/bind/testdata/ignore.objc.h.golden
+++ b/bind/testdata/ignore.objc.h.golden
@@ -18,7 +18,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
// skipped field S.F with unsupported type: *types.Interface
// skipped method S.Argument with unsupported parameter or return types
diff --git a/bind/testdata/ignore.objc.m.golden b/bind/testdata/ignore.objc.m.golden
index ed5b7c2..2e536c7 100644
--- a/bind/testdata/ignore.objc.m.golden
+++ b/bind/testdata/ignore.objc.m.golden
@@ -12,12 +12,20 @@
@implementation IgnoreS {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_ignore_S());
+ }
+ return self;
+}
+
// skipped unsupported field F with type *types.Var
// skipped method S.Argument with unsupported parameter or return types
diff --git a/bind/testdata/issue10788.objc.h.golden b/bind/testdata/issue10788.objc.h.golden
index 083208b..ee469df 100644
--- a/bind/testdata/issue10788.objc.h.golden
+++ b/bind/testdata/issue10788.objc.h.golden
@@ -18,7 +18,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
- (NSString*)value;
- (void)setValue:(NSString*)v;
@end
diff --git a/bind/testdata/issue10788.objc.m.golden b/bind/testdata/issue10788.objc.m.golden
index aa752c7..e8f6ba3 100644
--- a/bind/testdata/issue10788.objc.m.golden
+++ b/bind/testdata/issue10788.objc.m.golden
@@ -12,12 +12,20 @@
@implementation Issue10788TestStruct {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_issue10788_TestStruct());
+ }
+ return self;
+}
+
- (NSString*)value {
int32_t refnum = go_seq_go_to_refnum(self._ref);
nstring r0 = proxyissue10788_TestStruct_Value_Get(refnum);
diff --git a/bind/testdata/issue12328.objc.h.golden b/bind/testdata/issue12328.objc.h.golden
index 0cf0d70..7499c14 100644
--- a/bind/testdata/issue12328.objc.h.golden
+++ b/bind/testdata/issue12328.objc.h.golden
@@ -16,7 +16,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
- (NSError*)err;
- (void)setErr:(NSError*)v;
@end
diff --git a/bind/testdata/issue12328.objc.m.golden b/bind/testdata/issue12328.objc.m.golden
index 5487b26..e46f6ac 100644
--- a/bind/testdata/issue12328.objc.m.golden
+++ b/bind/testdata/issue12328.objc.m.golden
@@ -12,12 +12,20 @@
@implementation Issue12328T {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_issue12328_T());
+ }
+ return self;
+}
+
- (NSError*)err {
int32_t refnum = go_seq_go_to_refnum(self._ref);
int32_t r0 = proxyissue12328_T_Err_Get(refnum);
diff --git a/bind/testdata/structs.objc.h.golden b/bind/testdata/structs.objc.h.golden
index 424d2b1..00ad30c 100644
--- a/bind/testdata/structs.objc.h.golden
+++ b/bind/testdata/structs.objc.h.golden
@@ -19,7 +19,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
- (double)x;
- (void)setX:(double)v;
- (double)y;
@@ -32,7 +33,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
- (void)m;
- (NSString*)string;
@end
diff --git a/bind/testdata/structs.objc.m.golden b/bind/testdata/structs.objc.m.golden
index f4557ce..3d28302 100644
--- a/bind/testdata/structs.objc.m.golden
+++ b/bind/testdata/structs.objc.m.golden
@@ -12,12 +12,20 @@
@implementation StructsS {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_structs_S());
+ }
+ return self;
+}
+
- (double)x {
int32_t refnum = go_seq_go_to_refnum(self._ref);
double r0 = proxystructs_S_X_Get(refnum);
@@ -86,12 +94,20 @@
@implementation StructsS2 {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_structs_S2());
+ }
+ return self;
+}
+
- (void)m {
int32_t refnum = go_seq_go_to_refnum(self._ref);
proxystructs_S2_M(refnum);
diff --git a/bind/testdata/vars.objc.h.golden b/bind/testdata/vars.objc.h.golden
index eb39edc..9453f6f 100644
--- a/bind/testdata/vars.objc.h.golden
+++ b/bind/testdata/vars.objc.h.golden
@@ -18,7 +18,8 @@
}
@property(strong, readonly) id _ref;
-- (id)initWithRef:(id)ref;
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
@end
@protocol VarsI <NSObject>
diff --git a/bind/testdata/vars.objc.m.golden b/bind/testdata/vars.objc.m.golden
index 2aee07b..6e5cb3c 100644
--- a/bind/testdata/vars.objc.m.golden
+++ b/bind/testdata/vars.objc.m.golden
@@ -12,12 +12,20 @@
@implementation VarsS {
}
-- (id)initWithRef:(id)ref {
+- (instancetype)initWithRef:(id)ref {
self = [super init];
if (self) { __ref = ref; }
return self;
}
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ __ref = go_seq_from_refnum(new_vars_S());
+ }
+ return self;
+}
+
@end