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/gc/gc
examples/gc/ns
ns-old

View File

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

View File

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

View File

@ -18,7 +18,7 @@ func releaseX(x int) func (ns.MyClassSupermethods) {
func memtest1() {
fmt.Println("memtest1 started")
for {
arr := make([]ns.MyClass,1000)
arr := make([]*ns.MyClass,1000)
for i := 0; i < 1000; i++ {
// Alloc methods set a finalizer that causes the Go GC to
// Release these objects.
@ -27,8 +27,6 @@ func memtest1() {
// You can still manually retain objects, but that will cause
// 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
}
// Manually run the Go GC at every loop iteration. May not be needed
@ -41,24 +39,31 @@ func memtest1() {
func memtest2() {
fmt.Println("memtest2 started")
i := 0
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,
// and then calls "retain" on them before returning to Go. A Go
// 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
//o1.Release()
//o1.Release()
runtime.GC()
time.Sleep(time.Second/50)
}
}
func addStr(arr ns.NSMutableArray) {
func addStr(arr *ns.NSMutableArray) {
s1 := ns.NSStringAlloc().InitWithGoString("a string")
arr.AddObject(s1)
@ -86,14 +91,31 @@ func memtest4() {
c1 := o1.UTF8String()
_ = o1
_ = c1
runtime.GC()
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() {
go memtest1()
go memtest2()
go memtest3()
go memtest4()
go memtest5()
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:
- dealloc
- release
functions:
- NSMakeRange
frameworks:
- Foundation
pragma: [ clang diagnostic ignored "-Wformat-security" ]

View File

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

View File

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

View File

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

View File

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