Convert all Go wrapped objects to pointers so the Go GC can track

all of the Objective-C pointers. Still working on bug with typedefs
to wrapped types.
This commit is contained in:
Greg 2019-06-13 09:25:53 -04:00
parent b0139b9aa8
commit ac9eecafd8
11 changed files with 246 additions and 5537 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ examples/memory/memory
examples/memory/ns examples/memory/ns
examples/gc/gc examples/gc/gc
examples/gc/ns examples/gc/ns
ns-old

View File

@ -20,8 +20,12 @@ func pb2() {
a.Terminate(a) a.Terminate(a)
} }
func didFinishLaunching(n ns.NSNotification) { func didFinishLaunching(n *ns.NSNotification) {
fmt.Println("Go: did finish launching") fmt.Println("Go: did finish launching")
fmt.Printf("Notification n = %p\n",n)
fmt.Printf("Notification n.Ptr() = %p\n",n.Ptr())
fmt.Printf("Notification n.Name() = %p\n",n.Name())
fmt.Printf("Notification n.Name().Ptr() = %p\n",n.Name().Ptr())
fmt.Printf("Notification: %s\n", n.Name().UTF8String()) fmt.Printf("Notification: %s\n", n.Name().UTF8String())
//Set up an NSWindow //Set up an NSWindow
win = ns.NSWindowAlloc().InitWithContentRectStyleMask( win = ns.NSWindowAlloc().InitWithContentRectStyleMask(
@ -83,40 +87,39 @@ func didFinishLaunching(n ns.NSNotification) {
cv := win.ContentView() cv := win.ContentView()
cv.AddSubview(b1.NSView) cv.AddSubview(&b1.NSView)
cv.AddSubview(b2.NSView) cv.AddSubview(&b2.NSView)
viewmap := ns.NSDictionaryWithObjectsForKeys( viewmap := ns.NSDictionaryWithObjectsForKeys(
ns.NSArrayWithObjects(b1, b2), ns.NSArrayWithObjects(b1, b2),
ns.NSArrayWithObjects(nst("b1"), nst("b2"))) ns.NSArrayWithObjects(nst("b1"), nst("b2")))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat( cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("V:|-[b1]"), 0, ns.NSDictionary{}, viewmap)) nst("V:|-[b1]"), 0, nil, viewmap))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat( cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("H:|-[b1]"), 0, ns.NSDictionary{}, viewmap)) nst("H:|-[b1]"), 0, nil, viewmap))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat( cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("H:[b1]-[b2]"), ns.NSLayoutFormatAlignAllBaseline, nst("H:[b1]-[b2]"), ns.NSLayoutFormatAlignAllBaseline, nil, viewmap))
ns.NSDictionary{}, viewmap))
a.ActivateIgnoringOtherApps(1) a.ActivateIgnoringOtherApps(1)
} }
func shouldTerminateAfterLastWindowClosed(s ns.NSApplication) ns.BOOL { func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL {
return 1 return 1
} }
func willTerminate(n ns.NSNotification) { func willTerminate(n *ns.NSNotification) {
fmt.Println("Go: will terminate") fmt.Println("Go: will terminate")
} }
func didBecomeActive(n ns.NSNotification) { func didBecomeActive(n *ns.NSNotification) {
fmt.Println("Go: did become active") fmt.Println("Go: did become active")
fmt.Printf("Notification: %s\n", n.Name().UTF8String()) fmt.Printf("Notification: %s\n", n.Name().UTF8String())
} }
var ( var (
a ns.NSApplication a *ns.NSApplication
win ns.NSWindow win *ns.NSWindow
) )
func app() { func app() {

View File

@ -38,21 +38,21 @@ func main() {
fmt.Printf("i1 = %@\n", i1) fmt.Printf("i1 = %@\n", i1)
fmt.Printf("i1.Ptr() = %p\n", i1.Ptr()) fmt.Printf("i1.Ptr() = %p\n", i1.Ptr())
fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n") fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n")
a.ObjectEnumerator().ForIn(func(o ns.Id) bool { a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
fmt.Println(o.NSString()) fmt.Println(o.NSString())
return true return true
}) })
fmt.Printf("\nNSSetWithObjectsCount():\n") fmt.Printf("\nNSSetWithObjectsCount():\n")
s1 := ns.NSSetWithObjectsCount(&[]ns.Id{n1.Id, n2.Id}, 2) s1 := ns.NSSetWithObjectsCount(&[]*ns.Id{&n1.Id, &n2.Id}, 2)
fmt.Printf("\nNSSet.ObjectEnumerator().ForIn():\n") fmt.Printf("\nNSSet.ObjectEnumerator().ForIn():\n")
s1.ObjectEnumerator().ForIn(func(o ns.Id) bool { s1.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
fmt.Println(o.NSString()) fmt.Println(o.NSString())
return true return true
}) })
fmt.Printf("\nNSMutableArrayWithObjects()\n") fmt.Printf("\nNSMutableArrayWithObjects()\n")
a = ns.NSMutableArrayWithObjects(n1, s1) a = ns.NSMutableArrayWithObjects(n1, s1)
fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n") fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n")
a.ObjectEnumerator().ForIn(func(o ns.Id) bool { a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
fmt.Printf("%s -- ", o.ClassName().UTF8String()) fmt.Printf("%s -- ", o.ClassName().UTF8String())
switch { switch {
case o.IsKindOfClass(ns.NSStringClass()): case o.IsKindOfClass(ns.NSStringClass()):
@ -67,7 +67,7 @@ func main() {
fmt.Printf("\nNSArrayWithObjects()\n") fmt.Printf("\nNSArrayWithObjects()\n")
a2 = ns.NSArrayWithObjects(n1, n2, n3, s1) a2 = ns.NSArrayWithObjects(n1, n2, n3, s1)
fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n") fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n")
a2.ObjectEnumerator().ForIn(func(o ns.Id) bool { a2.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
switch { switch {
case o.IsKindOfClass(ns.NSStringClass()): case o.IsKindOfClass(ns.NSStringClass()):
fmt.Println(o.NSString().UTF8String()) fmt.Println(o.NSString().UTF8String())
@ -84,9 +84,9 @@ func main() {
ns.NSArrayWithObjects(nst("obj1"), nst("obj2")), ns.NSArrayWithObjects(nst("obj1"), nst("obj2")),
ns.NSArrayWithObjects(nst("key1"), nst("key2")), ns.NSArrayWithObjects(nst("key1"), nst("key2")),
) )
os := make([]ns.Id, 0, 5) os := make([]*ns.Id, 0, 5)
fmt.Printf("Length of os is %d\n", len(os)) fmt.Printf("Length of os is %d\n", len(os))
ks := make([]ns.Id, 0, 5) ks := make([]*ns.Id, 0, 5)
fmt.Printf("\nGetObjects()\n") fmt.Printf("\nGetObjects()\n")
d.GetObjects(&os, &ks, 4) d.GetObjects(&os, &ks, 4)
fmt.Printf("Length of os is now %d\n", len(os)) fmt.Printf("Length of os is now %d\n", len(os))
@ -94,7 +94,7 @@ func main() {
fmt.Printf("-- %s -> %s\n", k.NSString(), os[i].NSString()) fmt.Printf("-- %s -> %s\n", k.NSString(), os[i].NSString())
} }
fmt.Printf("\nNSStringWithContentsOfURLEncoding()\n") fmt.Printf("\nNSStringWithContentsOfURLEncoding()\n")
err := make([]ns.NSError, 1) err := make([]*ns.NSError, 1)
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), 0, &err) n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), 0, &err)
fmt.Printf("err: %s\n", err[0].LocalizedDescription()) fmt.Printf("err: %s\n", err[0].LocalizedDescription())

View File

@ -18,7 +18,7 @@ func releaseX(x int) func (ns.MyClassSupermethods) {
func memtest1() { func memtest1() {
fmt.Println("memtest1 started") fmt.Println("memtest1 started")
for { for {
arr := make([]ns.MyClass,1000) arr := make([]*ns.MyClass,1000)
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
// Alloc methods set a finalizer that causes the Go GC to // Alloc methods set a finalizer that causes the Go GC to
// Release these objects. // Release these objects.
@ -27,8 +27,6 @@ func memtest1() {
// You can still manually retain objects, but that will cause // You can still manually retain objects, but that will cause
// them to stick around after their Go pointers are collected. // them to stick around after their Go pointers are collected.
// This may be necessary if you are adding objects to an
// Objective-C collection?
//arr[i].Retain() // uncomment for leak //arr[i].Retain() // uncomment for leak
} }
// Manually run the Go GC at every loop iteration. May not be needed // Manually run the Go GC at every loop iteration. May not be needed
@ -41,24 +39,31 @@ func memtest1() {
func memtest2() { func memtest2() {
fmt.Println("memtest2 started") fmt.Println("memtest2 started")
i := 0
for { for {
o1 := ns.NSStringAlloc().InitWithGoString("one string") o1 := ns.NSStringAlloc().InitWithGoString(fmt.Sprintf("two string %d",i))
o2 := ns.NSStringWithGoString(fmt.Sprintf("two string %d",i))
// NSWrap runs object constructors inside an @autoreleasepool block, // NSWrap runs object constructors inside an @autoreleasepool block,
// and then calls "retain" on them before returning to Go. A Go // and then calls "retain" on them before returning to Go. A Go
// finalizer is set allowing the Go GC to call Release(). // finalizer is set allowing the Go GC to call Release().
o2 := ns.NSStringWithGoString("two string") // does not leak o3 := ns.NSStringWithString(o1)
o4 := ns.NSStringAlloc()
_ = o4
arr := ns.NSArrayAlloc().InitWithObjects(o1,o2) //arr := ns.NSArrayAlloc().InitWithObjects(o1,o1)
arr := ns.NSArrayWithObjects(o1,o2,o3,o4)
_ = arr _ = arr
//o1.Release()
//o1.Release()
runtime.GC() runtime.GC()
time.Sleep(time.Second/50) time.Sleep(time.Second/50)
} }
} }
func addStr(arr ns.NSMutableArray) { func addStr(arr *ns.NSMutableArray) {
s1 := ns.NSStringAlloc().InitWithGoString("a string") s1 := ns.NSStringAlloc().InitWithGoString("a string")
arr.AddObject(s1) arr.AddObject(s1)
@ -86,14 +91,31 @@ func memtest4() {
c1 := o1.UTF8String() c1 := o1.UTF8String()
_ = o1 _ = o1
_ = c1 _ = c1
runtime.GC()
time.Sleep(time.Second/10) time.Sleep(time.Second/10)
} }
} }
func memtest5() {
fmt.Println("memtest5 started")
i := 0
for {
str := ns.NSStringWithGoString(fmt.Sprintf("five string %d",i))
_ = str
sub := str.SubstringFromIndex(5)
_ = sub
fmt.Printf("sub = %s\n",sub)
time.Sleep(time.Second/10)
runtime.GC()
i++
}
}
func main() { func main() {
go memtest1() go memtest1()
go memtest2() go memtest2()
go memtest3() go memtest3()
go memtest4() go memtest4()
go memtest5()
select {} select {}
} }

View File

@ -1,40 +0,0 @@
package ns
/*
#cgo CFLAGS: -x objective-c -fno-objc-arc
#cgo LDFLAGS: -framework Foundation
#pragma clang diagnostic ignored "-Wformat-security"
#import <Foundation/Foundation.h>
*/
import "C"
import (
"unsafe"
)
//export MyClassRelease
func MyClassRelease(o unsafe.Pointer) {
MyClassMux.RLock()
cb := MyClassLookup[o].Release
MyClassMux.RUnlock()
if cb == nil { return }
self := MyClass{}
self.ptr = o
super := MyClassSupermethods{
self.SuperRelease,
}
cb(super)
}
//export MyClassDealloc
func MyClassDealloc(o unsafe.Pointer) {
MyClassMux.RLock()
cb := MyClassLookup[o].Dealloc
MyClassMux.RUnlock()
if cb == nil { return }
cb()
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,8 @@ subclasses:
NSObject: NSObject:
- dealloc - dealloc
- release - release
functions:
- NSMakeRange
frameworks: frameworks:
- Foundation - Foundation
pragma: [ clang diagnostic ignored "-Wformat-security" ] pragma: [ clang diagnostic ignored "-Wformat-security" ]

View File

@ -74,6 +74,7 @@ func memtest2() {
s1 := ns.NSStringWithGoString(fmt.Sprintf("string-%d", i)) s1 := ns.NSStringWithGoString(fmt.Sprintf("string-%d", i))
_ = s1 _ = s1
//o1.Retain() // uncomment for leak //o1.Retain() // uncomment for leak
i++
}) })
time.Sleep(time.Second / 3) time.Sleep(time.Second / 3)
} }
@ -94,24 +95,24 @@ func memtest3() {
arr.Autorelease() arr.Autorelease()
arr.AddObject(ns.NSStringWithGoString(fmt.Sprintf("my string %d",i))) arr.AddObject(ns.NSStringWithGoString(fmt.Sprintf("my string %d",i)))
s1 := ns.NSStringWithGoString(fmt.Sprintf("my other string %d",i)) s1 := ns.NSStringWithGoString(fmt.Sprintf("my other string %d",i))
fmt.Printf("%s\n",arr.ObjectAtIndex(0).NSString())
_ = s1 _ = s1
i++
for { for x := 0; x < 3; x++ {
ns.Autoreleasepool(func() { ns.Autoreleasepool(func() {
str := arr.ObjectAtIndex(0).NSString() str := arr.ObjectAtIndex(0).NSString()
fmt.Println(str) // does not leak in an autorelease pool fmt.Printf("%d->%s\n",x,str) // does not leak in an autorelease pool
time.Sleep(time.Second / 2) time.Sleep(time.Second / 5)
}) })
} }
time.Sleep(time.Second) time.Sleep(time.Second/2)
i++
}) })
} }
fmt.Println("memtest3: done") fmt.Println("memtest3: done")
} }
//Test of manual memory management. Lets run multiple goroutines here to //Test of manual memory management.
//confirm we can use multiple threads if autorelease pools are not in play.
func memtest4() { func memtest4() {
go memtest4a() go memtest4a()
go memtest4a() go memtest4a()
@ -122,10 +123,16 @@ func memtest4() {
go memtest4c() go memtest4c()
go memtest4c() go memtest4c()
go memtest4c() go memtest4c()
// Exactly one goroutine (locked to an OS thread) can use an // running multiple separate threads with autorelease pools...
// autorelease pool (?)
go memtest1() go memtest1()
go memtest2() go memtest2()
go memtest3()
go memtest1()
go memtest2()
go memtest3()
go memtest1()
go memtest2()
go memtest3()
} }
func memtest4a() { func memtest4a() {
@ -140,24 +147,34 @@ func memtest4b() {
i := 0 i := 0
for { for {
o1 := ns.NSObjectAlloc() // need to Release o1 := ns.NSObjectAlloc() // need to Release
// These object constructors will always leak. In the case of an
// immutable string, it is not an issue unless a large number of different
// strings are going to be made.
//s1 := ns.NSStringWithGoString(fmt.Sprintf("a string %d",i)) // uncomment for leak //s1 := ns.NSStringWithGoString(fmt.Sprintf("a string %d",i)) // uncomment for leak
s1 := ns.NSStringWithGoString("a string") s1 := ns.NSStringWithGoString("a string")
i++ i++
arr := ns.NSArrayAlloc().InitWithObjects(s1) // need to Release arr arr := ns.NSArrayAlloc().InitWithObjects(s1) // need to Release arr
s2 := arr.ObjectAtIndex(0).NSString() s2 := arr.ObjectAtIndex(0).NSString()
//If you try to convert an NSString to UTF8String, CString (*Char), // If you try to convert an NSString to UTF8String, CString (*Char),
//or GoString, Objective-C runtime will leak an // or GoString, Objective-C runtime creates an autoreleased
//NSTaggedPointerCStringContainer. Don't know why or how to fix it. // NSTaggedPointerCStringContainer internally and there is no way to
//There would be no leak if we were using an autorelease pool. // release it unless you called your method from within an autorelease
//u := str.UTF8String() // uncomment for leak // pool. The following two calls cause a leak.
//u := s1.UTF8String() // uncomment for leak
//fmt.Println(s1) // uncomment for leak //fmt.Println(s1) // uncomment for leak
time.Sleep(time.Second / 50) time.Sleep(time.Second / 50)
o1.Release() o1.Release()
arr.Release() arr.Release()
//s1.Release() // does not work
//s1.Release() // does not prevent the leak caused by
// NSStringWithGoString: s1 is autoreleased by the Objective-C
// runtime, these methods will always leak without autorelease
// pools.
_ = o1 _ = o1
_ = s2 _ = s2
} }
@ -173,17 +190,6 @@ func memtest4c() {
} }
} }
func fin(o *ns.MyClass) {
o.Release()
}
func releaseX(x int) func (ns.MyClassSupermethods) {
return func(super ns.MyClassSupermethods) {
fmt.Printf("--release %d\n", x)
super.Release() // comment out for leak
}
}
func main() { func main() {
//Uncomment more than one test at a time for a crash. //Uncomment more than one test at a time for a crash.
//Note: You may not run autorelease pools from multiple goroutines. //Note: You may not run autorelease pools from multiple goroutines.

View File

@ -300,7 +300,9 @@ func Start() (err error) {
case *ast.ObjCCategoryDecl: case *ast.ObjCCategoryDecl:
w.AddCategory(x) w.AddCategory(x)
case *ast.TypedefDecl: case *ast.TypedefDecl:
w.AddTypedef(x.Name, x.Type) if !x.IsImplicit {
w.AddTypedef(x.Name, x.Type)
}
case *ast.FunctionDecl: case *ast.FunctionDecl:
if matches(x.Name, Config.Functions) { if matches(x.Name, Config.Functions) {
w.AddFunction(x) w.AddFunction(x)

View File

@ -17,6 +17,10 @@ func ShouldWrap(gt string) bool {
return wrapped[gt] return wrapped[gt]
} }
func PtrShouldWrap(gt string) bool {
return gt != "" && gt[0] == '*' && wrapped[gt[1:]]
}
//goInterfaces records the names of top level Go interfaces. //goInterfaces records the names of top level Go interfaces.
var goInterfaces map[string]bool var goInterfaces map[string]bool
@ -24,6 +28,10 @@ func IsGoInterface(gt string) bool {
return goInterfaces[gt] return goInterfaces[gt]
} }
func PtrIsGoInterface(gt string) bool {
return gt != "" && gt[0] == '*' && goInterfaces[gt[1:]]
}
//TypeParameters maps, for each class, a TypedefName to a type, representing //TypeParameters maps, for each class, a TypedefName to a type, representing
//the Objective-C type parameters for that class //the Objective-C type parameters for that class
var TypeParameters map[string]map[string]string var TypeParameters map[string]map[string]string
@ -148,6 +156,7 @@ func (t *Type) PointsTo() *Type {
} }
func Wrap(s string) { func Wrap(s string) {
//fmt.Printf("wrapped[%s] = true\n",s)
wrapped[s] = true wrapped[s] = true
} }
@ -155,6 +164,9 @@ func (t *Type) BaseType() *Type {
if t == nil { if t == nil {
return nil return nil
} }
// if td := t.Typedef(); td != nil {
// return td.BaseType()
// }
ret := NewType( ret := NewType(
t.Node.BaseType(), t.Node.BaseType(),
t.Class, t.Class,
@ -184,19 +196,21 @@ func (t *Type) GoType() string {
} }
func _goType(ct string) string { func _goType(ct string) string {
ct = swapstars(ct)
ct = strings.Title(ct) ct = strings.Title(ct)
ct = strings.ReplaceAll(ct, " ", "") ct = strings.ReplaceAll(ct, " ", "")
ct = strings.ReplaceAll(ct, "Struct", "") ct = strings.TrimPrefix(ct, "Struct")
if IsGoInterface(ct) { ct = swapstars(ct)
return ct if len(ct) > 0 && ct[0] == '*' && IsGoInterface(ct[1:]) {
//return ct[1:]
return ct // ??
} }
/*
if ct == "Id" { if ct == "Id" {
ct = "Id" ct = "*Id"
}*/ }
if len(ct) > 1 && ShouldWrap(ct[1:]) { //if len(ct) > 1 && ShouldWrap(ct[1:]) {
return ct[1:] if ShouldWrap(ct) {
return ct
} }
if len(ct) > 4 && ct[len(ct)-4:len(ct)] == "Void" { if len(ct) > 4 && ct[len(ct)-4:len(ct)] == "Void" {
ct = ct[:len(ct)-5] + "unsafe.Pointer" ct = ct[:len(ct)-5] + "unsafe.Pointer"
@ -234,32 +248,37 @@ func (t *Type) GoTypeDecl() string {
if wrapped[t.GoType()] { if wrapped[t.GoType()] {
return t.GoInterfaceDecl() return t.GoInterfaceDecl()
} }
tp := t.BaseType() if t.Node.IsId() {
if tp.Node.IsId() {
return "" return ""
} }
gt := tp.GoType() gt := t.GoType()
if Debug {
fmt.Printf(" writing GoTypeDecl for %s\n",gt)
}
switch gt { switch gt {
case "", "Void": case "", "Void":
return "" return ""
default: default:
var cgt string
if td := tp.Typedef(); td != nil {
cgt = td.CGoType()
} else {
cgt = tp.CGoType()
}
return fmt.Sprintf(` return fmt.Sprintf(`
type %s %s type %s %s
`, gt, cgt) `, gt, t.CGoType())
} }
} }
func (t *Type) GoInterfaceDecl() string { func (t *Type) GoInterfaceDecl() string {
ct := t.CType() ct := t.CType()
gt := t.GoType() gt := t.GoType()
if Debug {
fmt.Printf(" writing GoInterfaceDecl for %s\n",gt)
}
if gt[0] == '*' {
gt = gt[1:] // dereference wrapped types
ct = ct[:len(ct)-1]
fmt.Printf(" dereferenced %s\n",gt)
}
super := Super(ct) super := Super(ct)
if super == "" { if super == "" {
//fmt.Printf("goInterfaces[%s] = true\n",gt)
goInterfaces[gt] = true goInterfaces[gt] = true
return fmt.Sprintf(` return fmt.Sprintf(`
type %s interface { type %s interface {
@ -272,9 +291,9 @@ type %s interface {
} }
return fmt.Sprintf(` return fmt.Sprintf(`
type %s struct { %s } type %s struct { %s }
func (o %s) Ptr() unsafe.Pointer { return o.ptr } func (o *%s) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
func (o Id) %s() %s { func (o *Id) %s() *%s {
ret := %s{} ret := &%s{}
ret.ptr = o.ptr ret.ptr = o.ptr
return ret return ret
} }
@ -344,7 +363,7 @@ func (t *Type) CToGo(cval string) string {
} }
// Call a C function from Go with a given return type and parameter types // 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, gogc bool) string { func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fun, fin bool) string {
if rtype == nil { if rtype == nil {
//fmt.Println("nil sent to GoToC") //fmt.Println("nil sent to GoToC")
return "" return ""
@ -352,15 +371,17 @@ func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fu
var ret strings.Builder var ret strings.Builder
rt := rtype.CType() rt := rtype.CType()
rtgt := rtype.GoType() rtgt := rtype.GoType()
if IsGoInterface(rtgt) { //fmt.Printf("GoToC(%s): rtgt == %s\n",name,rtgt)
rtgt = "Id" if PtrIsGoInterface(rtgt) {
//fmt.Printf(" PtrIsGoInterface(%s) = true\n",rtgt)
rtgt = "*Id"
} }
sw := ShouldWrap(rtgt) || rtgt == "Id" sw := PtrShouldWrap(rtgt) || rtgt == "*Id"
if rt != "void" { if rt != "void" {
if sw { if sw {
ret.WriteString(fmt.Sprintf( ret.WriteString(fmt.Sprintf(
`ret := %s{} `ret := &%s{}
ret.ptr = `, rtgt)) ret.ptr = `, rtgt[1:]))
} else { } else {
if rtgt == "BOOL" { if rtgt == "BOOL" {
ret.WriteString("ret := (") ret.WriteString("ret := (")
@ -378,7 +399,7 @@ func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fu
for i := 0; i < len(pnames); i++ { for i := 0; i < len(pnames); i++ {
pn, pt := pnames[i], ptypes[i] pn, pt := pnames[i], ptypes[i]
p := pn p := pn
if (ShouldWrap(pt.GoType()) || IsGoInterface(pt.GoType())) && !pt.Variadic { if (PtrShouldWrap(pt.GoType()) || PtrIsGoInterface(pt.GoType())) && !pt.Variadic {
p = pn + ".Ptr()" p = pn + ".Ptr()"
} else { } else {
switch { switch {
@ -409,6 +430,10 @@ func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fu
if sname == "" { if sname == "" {
continue continue
} }
ptgt := (ptypes[i].GoType())[2:]
if IsGoInterface(ptgt) {
ptgt = "Id"
}
ret.WriteString(fmt.Sprintf(` ret.WriteString(fmt.Sprintf(`
(*%s) = (*%s)[:cap(*%s)] (*%s) = (*%s)[:cap(*%s)]
for i := 0; i < len(*%s); i++ { for i := 0; i < len(*%s); i++ {
@ -416,17 +441,18 @@ func GoToC(name string, pnames, snames []string, rtype *Type, ptypes []*Type, fu
(*%s) = (*%s)[:i] (*%s) = (*%s)[:i]
break break
} }
if (*%s)[i] == nil {
(*%s)[i] = &%s{}
}
(*%s)[i].ptr = %s[i] (*%s)[i].ptr = %s[i]
}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], sname)) }`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, pnames[i], sname))
} }
if rt != "void" { if rt != "void" {
if sw && gogc && rtgt != "NSAutoreleasePool" { if fin {
// if m,_ :=regexp.MatchString(`Alloc(WithZone)?`,name); m { ret.WriteString(fmt.Sprintf(`
ret.WriteString(fmt.Sprintf(` runtime.SetFinalizer(ret, func(o *%s) {
runtime.SetFinalizer(&ret, func(o *%s) {
o.Release() o.Release()
})`,rtgt)) })`,rtgt))
// }
} }
ret.WriteString(` ret.WriteString(`
return ret`) return ret`)

View File

@ -75,7 +75,7 @@ func NewWrapper(debug bool) *Wrapper {
type Id struct { type Id struct {
ptr unsafe.Pointer ptr unsafe.Pointer
} }
func (o Id) Ptr() unsafe.Pointer { return o.ptr } func (o *Id) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
`) `)
return ret return ret
} }
@ -150,6 +150,14 @@ type Method struct {
Unavailable bool Unavailable bool
} }
func (m *Method) ShouldFinalize() bool {
return shouldFinalize(m.Type.GoType(), m.Name)
}
func shouldFinalize (grtype, name string) bool {
return Gogc && grtype != "NSAutoreleasePool" && (types.ShouldWrap(grtype) || grtype == "Id") && (len(name) < 4 || name[:4] != "init")
}
//Fully disambiguated method name (m.GoName + all parameter names) //Fully disambiguated method name (m.GoName + all parameter names)
func (m *Method) LongName() string { func (m *Method) LongName() string {
ret := m.GoName ret := m.GoName
@ -331,14 +339,17 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) {
if gt == "*Void" { if gt == "*Void" {
gt = "unsafe.Pointer" gt = "unsafe.Pointer"
} }
if types.PtrIsGoInterface(gt) {
gt = gt[1:]
}
if tps[i].Variadic { if tps[i].Variadic {
gt = "..." + gt gt = "..." + gt
ns[i] = ns[i] + "s" ns[i] = ns[i] + "s"
} }
if len(gt) > 2 && gt[:2] == "**" && types.ShouldWrap(gt[2:]) { if len(gt) > 2 && gt[:1] == "*" && types.PtrShouldWrap(gt[1:]) {
x := gt[2:] x := gt[1:]
if types.IsGoInterface(x) { if types.PtrIsGoInterface(x) {
x = "Id" x = "*Id"
} }
gt = "*[]" + x gt = "*[]" + x
snames[i] = "goSlice" + strconv.Itoa(i) snames[i] = "goSlice" + strconv.Itoa(i)
@ -747,13 +758,19 @@ func (w *Wrapper) AddTypedef(n, t string) {
} }
if types.ShouldWrap(gt) { if types.ShouldWrap(gt) {
if Debug { if Debug {
fmt.Printf(" processing type for %s (%s)\n", n, gt) fmt.Printf(" processing wrapped type for %s (%s)\n", n, gt)
} }
types.Wrap(n) types.Wrap(n)
types.SetSuper(n, gt) types.SetSuper(n, gt)
w._processType(tp, "*"+n) //w._processType(tp, n)
w._processType(tp)
} else { } else {
cgt := tp.CGoType()
if Debug {
fmt.Printf(" processing un-wrapped type for %s -> %s\n", n, cgt)
}
types.AddTypedef(n, tp) types.AddTypedef(n, tp)
} }
} }
@ -765,11 +782,14 @@ func (w *Wrapper) processTypes(tps []*types.Type) {
func (w *Wrapper) processType(tp *types.Type) { func (w *Wrapper) processType(tp *types.Type) {
bt := tp.BaseType() bt := tp.BaseType()
gt := bt.GoType() //gt := bt.GoType()
w._processType(bt, gt) //w._processType(bt, gt)
w._processType(bt)
} }
func (w *Wrapper) _processType(bt *types.Type, gt string) { //func (w *Wrapper) _processType(bt *types.Type, gt string) {
func (w *Wrapper) _processType(bt *types.Type) {
gt := bt.GoType()
if Debug { if Debug {
fmt.Printf("processType: gt = %s bt = %s\n", gt, bt) fmt.Printf("processType: gt = %s bt = %s\n", gt, bt)
} }
@ -802,6 +822,9 @@ func (w *Wrapper) _processType(bt *types.Type, gt string) {
types.Wrap(tp.GoType()) types.Wrap(tp.GoType())
w.processType(tp) w.processType(tp)
} }
if Debug {
fmt.Printf("Writing go type for %s -> %s\n",bt.CType(),gt)
}
w.goTypes.WriteString(bt.GoTypeDecl()) w.goTypes.WriteString(bt.GoTypeDecl())
} }
@ -827,7 +850,7 @@ func (c *Char) Free() {
func (w *Wrapper) StringHelpers() { func (w *Wrapper) StringHelpers() {
w.goHelpers.WriteString(` w.goHelpers.WriteString(`
func (o NSString) String() string { func (o *NSString) String() string {
return o.UTF8String().String() return o.UTF8String().String()
} }
`) `)
@ -835,7 +858,7 @@ func (o NSString) String() string {
func (w *Wrapper) EnumeratorHelpers() { func (w *Wrapper) EnumeratorHelpers() {
w.goHelpers.WriteString(` w.goHelpers.WriteString(`
func (e NSEnumerator) ForIn(f func(Id) bool) { func (e *NSEnumerator) ForIn(f func(*Id) bool) {
for o := e.NextObject(); o.Ptr() != nil; o = e.NextObject() { for o := e.NextObject(); o.Ptr() != nil; o = e.NextObject() {
if !f(o) { break } if !f(o) { break }
} }
@ -924,9 +947,9 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
switch { switch {
case !m.ClassMethod: case !m.ClassMethod:
if types.IsGoInterface(m.GoClass) { if types.IsGoInterface(m.GoClass) {
receiver = "(o Id) " receiver = "(o *Id) "
} else { } else {
receiver = "(o " + m.GoClass + ") " receiver = "(o *" + m.GoClass + ") "
} }
//Disambiguate instance methods with same name as a class method //Disambiguate instance methods with same name as a class method
cname = "inst_" + cname cname = "inst_" + cname
@ -969,8 +992,8 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
if grtype == "Void" { if grtype == "Void" {
grtype = "" grtype = ""
} }
if types.IsGoInterface(grtype) { if types.PtrIsGoInterface(grtype) {
grtype = "Id" grtype = "*Id"
} }
if grtype == "BOOL" { // convert objective-c bools to Go bools if grtype == "BOOL" { // convert objective-c bools to Go bools
grtype = "bool" grtype = "bool"
@ -1022,7 +1045,7 @@ func %s%s(%s) %s {
`, snames[i], n, n, snames[i], n)) `, snames[i], n, n, snames[i], n))
} }
w.goCode.WriteString(` ` + w.goCode.WriteString(` ` +
types.GoToC(cname, ns, snames, m.Type, tps, fun, Gogc) + "\n}\n") types.GoToC(cname, ns, snames, m.Type, tps, fun, m.ShouldFinalize()) + "\n}\n")
cret := "" cret := ""
if !m.isVoid() { if !m.isVoid() {
@ -1062,9 +1085,15 @@ func %s%s(%s) %s {
default: default:
if Gogc && !m.isVoid() { if Gogc && !m.isVoid() {
rtn := "" rtn := ""
//if m.ClassMethod && types.ShouldWrap(m.Type.GoType()) {
if types.ShouldWrap(m.Type.GoType()) { if types.ShouldWrap(m.Type.GoType()) {
if m.ClassMethod {
rtn = ` rtn = `
[ret retain];` [ret retain];`
} else {
rtn = `
if (o != ret) { [ret retain]; }`
}
} }
w.cCode.WriteString(fmt.Sprintf( w.cCode.WriteString(fmt.Sprintf(
` %s ret; ` %s ret;
@ -1097,10 +1126,10 @@ func %s%s(%s) %s {
fmt.Printf(" %s\n", gt) fmt.Printf(" %s\n", gt)
} }
ns2 := ns[i] ns2 := ns[i]
if gt == "NSString" { if gt == "*NSString" {
gt = "string" gt = "string"
//ns[i] = gStringToNsstring(ns[i]) //ns[i] = gStringToNsstring(ns[i])
cvts = cvts + gStringToNSString(ns[i]) cvts = gStringToNSString(ns[i])
ns[i] = "NSStringWithUTF8String(" + ns[i] + "_chr)" ns[i] = "NSStringWithUTF8String(" + ns[i] + "_chr)"
} }
gps = append(gps, ns2+" "+gt) gps = append(gps, ns2+" "+gt)
@ -1111,11 +1140,21 @@ func %s%s(%s) %s {
obj = "o." obj = "o."
ns = ns[1:] ns = ns[1:]
} }
finalizer := ""
if m.ShouldFinalize() && false {
w.goImports["runtime"] = true
finalizer = fmt.Sprintf(
`runtime.SetFinalizer(ret, func(o *%s) {
o.Release()
})
`, grtype)
}
w.goCode.WriteString(fmt.Sprintf(` w.goCode.WriteString(fmt.Sprintf(`
func %s%s(%s) %s { func %s%s(%s) %s {
%sreturn %s%s(%s) %sret := %s%s(%s)
%sreturn ret
} }
`, receiver, gname2, gplist, grtype, cvts, obj, gname, strings.Join(ns, ", "))) `, receiver, gname2, gplist, grtype, cvts, obj, gname, strings.Join(ns, ", "), finalizer))
} }
} }
@ -1214,6 +1253,9 @@ func (w *Wrapper) ProcessSubclass(sname string, sc *Subclass) {
for i, sig := range sc.NewMethods { for i, sig := range sc.NewMethods {
nms[i] = w.MethodFromSig(sig, sname) nms[i] = w.MethodFromSig(sig, sname)
} }
if Debug {
fmt.Println("ProcessSubclass(%s)\n",sname)
}
w._ProcessDelSub(sname, ps, nms, true) w._ProcessDelSub(sname, ps, nms, true)
} }
@ -1567,17 +1609,17 @@ void*
//5. Go constructor //5. Go constructor
var finalizer string var finalizer string
if Gogc { if shouldFinalize(gname,"") {
w.goImports["runtime"] = true w.goImports["runtime"] = true
finalizer = fmt.Sprintf( finalizer = fmt.Sprintf(
`runtime.SetFinalizer(&ret,func(o *%s) { `runtime.SetFinalizer(ret,func(o *%s) {
o.Release() o.Release()
}) })
`,gname) `,gname)
} }
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
func %sAlloc() %s { func %sAlloc() *%s {
ret := %s{} ret := &%s{}
ret.ptr = unsafe.Pointer(C.%sAlloc()) ret.ptr = unsafe.Pointer(C.%sAlloc())
%sreturn ret %sreturn ret
} }
@ -1646,10 +1688,10 @@ func (d %s) %sCallback(f func(%s)%s) {
} else { } else {
gt2 = gtypes[i][j-1] gt2 = gtypes[i][j-1]
} }
if types.IsGoInterface(gt2) { if types.IsGoInterface(gt2) || types.ShouldWrap(gt2) {
gt2 = "Id" gt2 = "*Id"
} }
if types.ShouldWrap(gt2) || gt2 == "Id" { if gt2 == "*Id" {
garglist = append(garglist, fmt.Sprintf( garglist = append(garglist, fmt.Sprintf(
`a%d`, j)) `a%d`, j))
gargconv = append(gargconv, fmt.Sprintf( gargconv = append(gargconv, fmt.Sprintf(
@ -1723,14 +1765,14 @@ func %s%s(%s)%s {
grtype = "" grtype = ""
} }
if types.IsGoInterface(grtype) { if types.IsGoInterface(grtype) {
grtype = "Id" grtype = "*Id"
} }
if grtype == "BOOL" { if grtype == "BOOL" {
grtype = "bool" grtype = "bool"
} }
if sub { if sub {
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
func (o %s) Super%s(%s) %s { func (o *%s) Super%s(%s) %s {
`, gname, gnames[i], strings.Join(earglist[1:], ", "), grtype)) `, gname, gnames[i], strings.Join(earglist[1:], ", "), grtype))
ns, snames, tps, _ := w.gpntp(m) ns, snames, tps, _ := w.gpntp(m)
lparm := len(tps) - 1 lparm := len(tps) - 1
@ -1745,7 +1787,7 @@ func (o %s) Super%s(%s) %s {
} }
`, vn, w.Vaargs, vn, vn)) `, vn, w.Vaargs, vn, vn))
} }
gocode.WriteString("\t" + types.GoToC(dname+"_super_"+m.Name, ns, snames, m.Type, tps, false, Gogc) + "\n}\n") gocode.WriteString("\t" + types.GoToC(dname+"_super_"+m.Name, ns, snames, m.Type, tps, false, m.ShouldFinalize()) + "\n}\n")
} }
} }
w.cCode.WriteString(cprotos.String()) w.cCode.WriteString(cprotos.String())
@ -1823,6 +1865,27 @@ func (w *Wrapper) AddProtocolMethods(i *Interface, p *Protocol) {
procmeths(i.InstanceMethods, p.InstanceMethods) procmeths(i.InstanceMethods, p.InstanceMethods)
} }
func printDebug() {
fmt.Printf("ShouldWrap(NSString) = %t\n",types.ShouldWrap("NSString"))
fmt.Printf("ShouldWrap(*NSString) = %t\n",types.ShouldWrap("*NSString"))
fmt.Printf("IsGoInterface(NSObject) = %t\n",types.IsGoInterface("NSObject"))
fmt.Printf("IsGoInterface(*NSObject) = %t\n",types.IsGoInterface("*NSObject"))
fmt.Printf("IsGoInterface(NSString) = %t\n",types.IsGoInterface("NSString"))
fmt.Printf("IsGoInterface(*NSString) = %t\n",types.IsGoInterface("*NSString"))
fmt.Printf("PtrShouldWrap(NSString) = %t\n",types.PtrShouldWrap("NSString"))
fmt.Printf("PtrShouldWrap(*NSString) = %t\n",types.PtrShouldWrap("*NSString"))
fmt.Printf("PtrIsGoInterface(NSObject) = %t\n",types.PtrIsGoInterface("NSObject"))
fmt.Printf("PtrIsGoInterface(*NSObject) = %t\n",types.PtrIsGoInterface("*NSObject"))
fmt.Printf("PtrIsGoInterface(NSString) = %t\n",types.PtrIsGoInterface("NSString"))
fmt.Printf("PtrIsGoInterface(*NSString) = %t\n",types.PtrIsGoInterface("*NSString"))
fmt.Printf("Super(NSString) = %s\n",types.Super("NSString"))
fmt.Printf("Super(*NSString) = %s\n",types.Super("*NSString"))
fmt.Printf("Super(NSObject) = %s\n",types.Super("NSObject"))
fmt.Printf("Super(*NSObject) = %s\n",types.Super("*NSObject"))
fmt.Printf("Super(NSString*) = %s\n",types.Super("NSString*"))
fmt.Printf("Super(NSObject*) = %s\n",types.Super("NSObject*"))
}
func (w *Wrapper) Wrap(toproc []string) { func (w *Wrapper) Wrap(toproc []string) {
if w.Package == "" { if w.Package == "" {
w.Package = "ns" w.Package = "ns"
@ -1879,12 +1942,6 @@ func (w *Wrapper) Wrap(toproc []string) {
os.Exit(-1) os.Exit(-1)
} }
w.AddProtocolMethods(i,prot) w.AddProtocolMethods(i,prot)
// for _, m := range prot.ClassMethods.Methods {
// w.ProcessMethodForClass(m, i.Name)
// }
// for _, m := range prot.InstanceMethods.Methods {
// w.ProcessMethodForClass(m, i.Name)
// }
} }
Disambiguate(i.ClassMethods) Disambiguate(i.ClassMethods)
Disambiguate(i.InstanceMethods) Disambiguate(i.InstanceMethods)