diff --git a/README.md b/README.md index 9773166..1584bd2 100644 --- a/README.md +++ b/README.md @@ -238,15 +238,14 @@ not necessary because `NSButton` embeds `NSControl` which in turn embeds receivers. Go will automatically find the indirectly embedded `NSView` and call the right method. -Go's type inference appears to be slightly broken (as of 1.12.1) because -the following does not work. Look out for this if you like to chain your +Note that, since `InitWithFrame()` is defined only for `NSView` and returns +an `NSView` type, +the following will not work. Look out for this if you like to chain your `Alloc` and `Init` methods and are getting type errors: ```go -//DO NOT DO THIS +//DO NOT DO THIS -- InitWithFrame returns NSView, not NSButton b := ns.NSButtonAlloc().InitWithFrame(ns.MakeRect(100,100,200,200)) -//For some reason Go thinks b has type ns.NSView, because InitWithFrame is defined for ns.NSView, -//even though NSButtonAlloc() returns an ns.NSButton. ``` Go has no trouble finding embedded methods for your `NSButton` and will diff --git a/types/convert.go b/types/convert.go index b98b8ca..9af08d6 100644 --- a/types/convert.go +++ b/types/convert.go @@ -192,9 +192,10 @@ func _goType(ct string) string { if IsGoInterface(ct) { return ct } + /* if ct == "Id" { ct = "Id" - } + }*/ if len(ct) > 1 && ShouldWrap(ct[1:]) { return ct[1:] } @@ -296,7 +297,7 @@ func (t *Type) IsFunctionPtr() bool { func (t *Type) IsFunction() bool { if t == nil { - fmt.Println("nil sent to IsFunction()") + //fmt.Println("nil sent to IsFunction()") return false } if td := t.Typedef(); td != nil { @@ -316,6 +317,9 @@ func (t *Type) IsValist() bool { } func (t *Type) ReturnType() *Type { + if td := t.Typedef(); td != nil { + return td.ReturnType() + } if rt := t.Node.ReturnType(); rt != nil { return NewType(rt,t.Class) } @@ -323,6 +327,9 @@ func (t *Type) ReturnType() *Type { } func (t *Type) IsPointer() bool { + if t == nil { + return false + } if td := t.Typedef(); td != nil { return td.IsPointer() } @@ -340,7 +347,7 @@ func (t *Type) CToGo(cval string) string { // Call a C function from Go with a given return type and parameter types func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fun bool) string { if rtype == nil { - fmt.Println("nil sent to GoToC") + //fmt.Println("nil sent to GoToC") return "" } var ret strings.Builder diff --git a/types/convert_test.go b/types/convert_test.go new file mode 100644 index 0000000..304d9ae --- /dev/null +++ b/types/convert_test.go @@ -0,0 +1,324 @@ +package types + +import ( + "fmt" + "reflect" + "testing" +) + +func isNil(x interface{}) bool { + return x == nil || + (reflect.ValueOf(x).Kind() == reflect.Ptr && + reflect.ValueOf(x).IsNil()) +} + +func TestType(t *testing.T) { + i := 1 + + var str string + var n *Node + var tp *Type + + chk := func(actual, expected interface{}) { + t.Run(fmt.Sprintf("TestType%d",i),func(t *testing.T) { + if isNil(actual) && isNil(expected) { return } + if !reflect.DeepEqual(actual,expected) { + t.Errorf("Test failed for %s\n",str) + fmt.Println("Actual:\n",actual) + fmt.Println("Expected:\n",expected) + } + }) + } + + chk_newtype := func() { + tp = NewTypeFromString(str,"") + chk(tp,&Type{ Node: n, Class: "" }) + } + + //tests on nil Type pointers: + chk(tp.BaseType(),nil) + chk(tp.CType(),"") + chk(tp.IsFunction(),false) + chk(tp.IsPointer(),false) + chk(tp.IsFunctionPtr(),false) + chk(tp.IsValist(),false) + + str = "int" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypeSpecifier", "int", []*Node{} } } } + chk_newtype() + tint := tp + + str = "NSObject" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypedefName", "NSObject", []*Node{} } } } + chk_newtype() + nso := tp + + str = "NSString" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypedefName", "NSString", []*Node{} } } } + chk_newtype() + nst := tp + + str = "NSString*" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypedefName", "NSString", []*Node{} }, + &Node{ "Pointer", "*", []*Node{} } } } + chk_newtype() + chk(tp.IsPointer(),true) + chk(tp.Typedef(),nil) + tpNSString := tp + + str = "myTypedef" + AddTypedef("myTypedef",tp) + n = &Node { "TypeName", "", []*Node{ + &Node{ "TypedefName", "myTypedef", []*Node{} }}} + chk_newtype() + chk(tp.Typedef(),tpNSString) + + str = "const NSArray *" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypeQualifier", "const", []*Node{} }, + &Node{ "TypedefName", "NSArray", []*Node{} }, + &Node{ "GenericList", "", []*Node{ + &Node{ "Generic", "", []*Node{ + &Node{ "TypedefName", "ObjectType", []*Node{} }, + &Node{ "Pointer", "*", []*Node{} }, + &Node{ "NullableAnnotation", "_Nonnull", []*Node{} } } } } }, + &Node{ "Pointer", "*", []*Node{} } } } + chk_newtype() + chk(tp.CType(),"NSArray*") + chk(tp.CTypeAttrib(),"const NSArray*") + chk(tp.CGoType(),"*C.NSArray") + chk(tp.GoType(),"*NSArray") + + SetTypeParam("MyClass","ObjectType","MyClass") + str = "id" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypedefName", "id", []*Node{} }, + &Node{ "GenericList", "", []*Node{ + &Node{ "Generic", "", []*Node{ + &Node{ "TypedefName", "ObjectType", []*Node{} }, + &Node{ "Pointer", "*", []*Node{} } } } } } } } + chk_newtype() + tp = NewType(n,"MyClass") + chk(tp.CType(),"NSObject*") + chk(tp.String(), +` '' +- 'id' +- '' +-- '' +--- 'MyClass' +--- '*' +`) + x,_ := clean(nil,"MyClass") + chk(x,nil) + SetSuper("NSString","NSObject") + chk(Super("NSString"),"NSObject") + + tp2 := tp.CloneToClass("NSObject") + chk(tp2.Class,"NSObject") + + str = "you can't parse this" + tp = NewTypeFromString(str,"") + tp2 = &Type{} + chk(tp,tp2) + + str = "id" + tp2 = NewTypeFromString(str,"MyClass") + chk(tp.BaseType(),tp) + + str = "id*" + tp = NewTypeFromString(str,"MyClass") + chk(tp.BaseType(),tp2) + chk(tp.PointsTo(),tp2) + AddTypedef("myTypedef",tp) + + str = "myTypedef" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypedefName", "myTypedef", []*Node{} } } } + chk_newtype() + chk(tp.PointsTo(),tp2) + chk(tp2.PointsTo(),nil) + + chk(tp.GoTypeDecl(),` +type MyTypedef **C.NSObject +`) + chk(tp2.GoTypeDecl(),"") + chk(nst.GoTypeDecl(),` +type NSString C.NSString +`) + str = "void" + n = &Node{ "TypeName", "", []*Node{ + &Node{ "TypeSpecifier", "void", []*Node{} } } } + chk_newtype() + chk(tp.GoTypeDecl(),"") + void := tp + + str = "BOOL" + n,_ = Parse(str) + chk_newtype() + bl := tp + + str = "void**" + n,_ = Parse(str) + chk_newtype() + chk(tp.GoType(),"*unsafe.Pointer") + chk(tp.CToGo("var"),"(*unsafe.Pointer)(unsafe.Pointer(var))") + voidpp := tp + + Wrap("NSObject") + str = "NSObject*" + n,_ = Parse(str) + chk_newtype() + chk(tp.GoType(),"NSObject") + + Wrap("NSString") + chk(nso.GoTypeDecl(),` +type NSObject interface { + Ptr() unsafe.Pointer +} +`) + chk(nso.GoType(),"NSObject") + + chk(nst.GoTypeDecl(),` +type NSString struct { Id } +func (o NSString) Ptr() unsafe.Pointer { return o.ptr } +func (o Id) NSString() NSString { + ret := NSString{} + ret.ptr = o.ptr + return ret +} +`) + str = "int(void)" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsFunction(),true) + chk(tp.IsFunctionPtr(),false) + chk(tp.ReturnType().CType(),"int") + + str = "int *(void)" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsFunction(),true) + chk(tp.ReturnType().CType(),"int*") + chk(tp.ReturnType().GoType(),"*Int") + fn := tp + + AddTypedef("myTypedef",fn) + str = "myTypedef" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsFunction(),true) + chk(tp.IsFunctionPtr(),false) + chk(tp.ReturnType().CType(),"int*") + + str = "int (*)(void)" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsFunction(),false) + chk(tp.IsPointer(),true) + chk(tp.IsFunctionPtr(),true) + chk(tp.ReturnType(),nil) + fnptr := tp + + AddTypedef("myTypedef",fnptr) + str = "myTypedef" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsFunction(),false) + chk(tp.IsPointer(),true) + chk(tp.IsFunctionPtr(),true) + chk(tp.ReturnType(),nil) + + chk(tp.IsValist(),false) + str = "__va_list_tag" + n,_ = Parse(str) + chk_newtype() + chk(tp.IsValist(),true) + + str = "GoToC" + var rtype *Type + ptypes := []*Type{ nst, nso, tint, voidpp } + pnames := []string{"p1","p2","p3", "p4"} + snames := []string{"","","",""} + + chk_gotoc := func(expected string) { + chk(GoToC("myFun",pnames,snames,rtype,ptypes,false),expected) + } + + chk_gotoc("") + + rtype = void + chk_gotoc(`C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4))`) + + rtype = bl + chk_gotoc( +`ret := (C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4))) != 0 + return ret`) + + rtype = voidpp + chk_gotoc( +`ret := (*unsafe.Pointer)(unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)))) + return ret`) + + rtype = nst + chk_gotoc( +`ret := NSString{} + ret.ptr = C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)) + return ret`) + + rtype = nso + chk_gotoc( +`ret := Id{} + ret.ptr = C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)) + return ret`) + + ptypes[1].Variadic = true + chk_gotoc( +`ret := Id{} + ret.ptr = C.myFun(p1.Ptr(), unsafe.Pointer(&p2), (C.int)(p3), unsafe.Pointer(p4)) + return ret`) + ptypes[1].Variadic = false + + snames[1] = "p2p" + chk_gotoc( +`ret := Id{} + ret.ptr = C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)) + (*p2) = (*p2)[:cap(*p2)] + for i := 0; i < len(*p2); i++ { + if p2p[i] == nil { + (*p2) = (*p2)[:i] + break + } + (*p2)[i].ptr = p2p[i] + } + return ret`) + snames[1] = "" + snames[2] = "p3p" + chk_gotoc( +`ret := Id{} + ret.ptr = C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), unsafe.Pointer(p4)) + (*p3) = (*p3)[:cap(*p3)] + for i := 0; i < len(*p3); i++ { + if p3p[i] == nil { + (*p3) = (*p3)[:i] + break + } + (*p3)[i].ptr = p3p[i] + } + return ret`) + chk(GoToC("myFun",pnames,snames,rtype,ptypes,true), +`ret := Id{} + ret.ptr = C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), (**C.void)(p4)) + (*p3) = (*p3)[:cap(*p3)] + for i := 0; i < len(*p3); i++ { + if p3p[i] == nil { + (*p3) = (*p3)[:i] + break + } + (*p3)[i].ptr = p3p[i] + } + return ret`) +}