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:
parent
b0139b9aa8
commit
ac9eecafd8
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ examples/memory/memory
|
|||
examples/memory/ns
|
||||
examples/gc/gc
|
||||
examples/gc/ns
|
||||
ns-old
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -11,6 +11,8 @@ subclasses:
|
|||
NSObject:
|
||||
- dealloc
|
||||
- release
|
||||
functions:
|
||||
- NSMakeRange
|
||||
frameworks:
|
||||
- Foundation
|
||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||
|
|
|
@ -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.
|
||||
|
|
2
main.go
2
main.go
|
@ -300,7 +300,9 @@ func Start() (err error) {
|
|||
case *ast.ObjCCategoryDecl:
|
||||
w.AddCategory(x)
|
||||
case *ast.TypedefDecl:
|
||||
if !x.IsImplicit {
|
||||
w.AddTypedef(x.Name, x.Type)
|
||||
}
|
||||
case *ast.FunctionDecl:
|
||||
if matches(x.Name, Config.Functions) {
|
||||
w.AddFunction(x)
|
||||
|
|
|
@ -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:]
|
||||
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 {
|
||||
if fin {
|
||||
ret.WriteString(fmt.Sprintf(`
|
||||
runtime.SetFinalizer(&ret, func(o *%s) {
|
||||
runtime.SetFinalizer(ret, func(o *%s) {
|
||||
o.Release()
|
||||
})`,rtgt))
|
||||
// }
|
||||
}
|
||||
ret.WriteString(`
|
||||
return ret`)
|
||||
|
|
131
wrap/main.go
131
wrap/main.go
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user