Compare commits

..

No commits in common. "4aa1211d7346ac96351840e67a020c5fcdbbc953" and "79bb282188fa593eecc8a53389c5250c9bf390b1" have entirely different histories.

9 changed files with 132 additions and 389 deletions

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"git.wow.st/gmp/nswrap/examples/app/ns" "git.wow.st/gmp/nswrap/examples/app/ns"
"runtime" "runtime"
"time"
) )
//Shortcut for literal NSStrings //Shortcut for literal NSStrings
@ -32,9 +31,8 @@ func didFinishLaunching(n *ns.NSNotification) {
ns.NSBackingStoreBuffered, ns.NSBackingStoreBuffered,
0, 0,
) )
// We do not need to retain this because we are in garbage collection mode // retain win since we called Alloc and did not add it to a collection
// and have assigned it to a global variable. win.Retain()
//win.Retain()
win.SetTitle(nst("Hi World")) win.SetTitle(nst("Hi World"))
win.MakeKeyAndOrderFront(win) win.MakeKeyAndOrderFront(win)
@ -121,12 +119,12 @@ var (
) )
func app() { func app() {
// Lock OS thread because Cocoa uses thread-local storage //Lock OS thread because Cocoa uses thread-local storage
runtime.LockOSThread() runtime.LockOSThread()
a = ns.NSApplicationSharedApplication() a = ns.NSApplicationSharedApplication()
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular) a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
// Set up an AppDelegate //Set up an AppDelegate
del := ns.AppDelegateAlloc() del := ns.AppDelegateAlloc()
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed) del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
@ -135,20 +133,12 @@ func app() {
a.SetDelegate(del) a.SetDelegate(del)
// Run the app //Run the app
a.Run() a.Run()
} }
func main() { func main() {
// Run GC every second to ensure pointers are not being prematurely released. //Run our app in an autorelease pool just for fun
go func() {
for {
runtime.GC()
time.Sleep(time.Second)
}
}()
// Run our app in an autorelease pool just for fun
go ns.Autoreleasepool(app) go ns.Autoreleasepool(app)
select {} select {}
} }

View File

@ -44,5 +44,4 @@ subclasses:
frameworks: [ Foundation, AppKit ] frameworks: [ Foundation, AppKit ]
pragma: [ clang diagnostic ignored "-Wformat-security" ] pragma: [ clang diagnostic ignored "-Wformat-security" ]
#autorelease: true autorelease: true
gogc: true

View File

@ -5,8 +5,6 @@ import "C"
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"runtime"
"time"
"git.wow.st/gmp/nswrap/examples/bluetooth/ns" "git.wow.st/gmp/nswrap/examples/bluetooth/ns"
) )
@ -27,53 +25,34 @@ func updateState(c *ns.CBCentralManager) {
fmt.Printf(" powered on\n") fmt.Printf(" powered on\n")
cm.ScanForPeripheralsWithServices(ns.NSArrayWithObjects(hrm_uuid), nil) cm.ScanForPeripheralsWithServices(ns.NSArrayWithObjects(hrm_uuid), nil)
} }
fmt.Printf("Go: updateState returning\n")
} }
func discoverPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral, d *ns.NSDictionary, rssi *ns.NSNumber) { func discoverPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral, d *ns.NSDictionary, rssi *ns.NSNumber) {
fmt.Printf("Did discover peripheral\n") fmt.Printf("Did discover peripheral\n")
c.StopScan() c.StopScan()
if peripheral.Ptr() != nil {
// if we already have a pointer to a peripheral, check that this one is different,
// and if so release the old one. Be careful to check the Objective-C pointers
// here as the Go pointers will differ.
if peripheral != nil && p.Ptr() != peripheral.Ptr() {
peripheral.Release() peripheral.Release()
} }
peripheral = p peripheral = p
// we need to take ownership of this peripheral so CoreBluetooth doesn't
// dealloc it.
peripheral.Retain() peripheral.Retain()
c.ConnectPeripheral(peripheral, nil) c.ConnectPeripheral(peripheral, nil)
} }
func connectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) { func connectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) {
fmt.Printf("Did connect peripheral\n") fmt.Printf("Did connect peripheral\n")
// set ourselves up as a peripheral delegate
p.SetDelegate(cd) p.SetDelegate(cd)
// discover all services on this device
p.DiscoverServices(nil) p.DiscoverServices(nil)
fmt.Printf("Go: discoverPeripheral returning\n")
} }
func discoverServices(p *ns.CBPeripheral, e *ns.NSError) { func discoverServices(p *ns.CBPeripheral, e *ns.NSError) {
fmt.Printf("Did discover services\n") fmt.Printf("Did discover services\n")
p.Services().ObjectEnumerator().ForIn(func(o *ns.Id) bool { p.Services().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
serv := o.CBService() serv := o.CBService()
uuid := serv.UUID()
switch { switch {
case uuid.IsEqualTo(hrm_uuid): case serv.UUID().IsEqualTo(hrm_uuid):
fmt.Printf("--heart rate monitor service\n") fmt.Printf("--heart rate monitor service\n")
p.DiscoverCharacteristics(nil, serv) p.DiscoverCharacteristics(nil, serv)
case uuid.IsEqualTo(info_uuid): case serv.UUID().IsEqualTo(ns.CBUUIDWithGoString("180A")):
fmt.Printf("--device information service\n") fmt.Printf("--device information service\n")
p.DiscoverCharacteristics(nil, serv) p.DiscoverCharacteristics(nil, serv)
default: default:
@ -81,7 +60,6 @@ func discoverServices(p *ns.CBPeripheral, e *ns.NSError) {
} }
return true return true
}) })
fmt.Printf("Go: discoverServices returning\n")
} }
func hr(d *ns.NSData) int { func hr(d *ns.NSData) int {
@ -99,14 +77,12 @@ func hr(d *ns.NSData) int {
func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) { func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) {
fmt.Printf("Did discover characteristics\n") fmt.Printf("Did discover characteristics\n")
uuid := s.UUID() fmt.Printf("----%s\n", s.UUID().UUIDString().UTF8String())
fmt.Printf("----%s\n", uuid.UUIDString().UTF8String()) if s.UUID().IsEqualTo(hrm_uuid) {
if uuid.IsEqualTo(hrm_uuid) {
s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool { s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
chr := o.CBCharacteristic() chr := o.CBCharacteristic()
chuuid := chr.UUID() fmt.Printf("------%s\n", chr.UUID().UUIDString().UTF8String())
fmt.Printf("------%s\n", chuuid.UUIDString().UTF8String()) if chr.UUID().IsEqualTo(hrv_uuid) {
if chuuid.IsEqualTo(hrv_uuid) {
p.SetNotifyValue(1, chr) p.SetNotifyValue(1, chr)
v := chr.Value() v := chr.Value()
fmt.Println(hr(v)) fmt.Println(hr(v))
@ -114,7 +90,6 @@ func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError)
return true return true
}) })
} }
fmt.Printf("Go: discoverCharacteristics returning\n")
} }
func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) { func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) {
@ -122,13 +97,11 @@ func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) {
v := chr.Value() v := chr.Value()
fmt.Printf("Heart rate: %d\n", hr(v)) fmt.Printf("Heart rate: %d\n", hr(v))
} }
fmt.Printf("Go: updateValue returning\n")
} }
var ( var (
hrm_uuid *ns.CBUUID hrm_uuid *ns.CBUUID
hrv_uuid *ns.CBUUID hrv_uuid *ns.CBUUID
info_uuid *ns.CBUUID
cd *ns.CBDelegate cd *ns.CBDelegate
cm *ns.CBCentralManager cm *ns.CBCentralManager
peripheral *ns.CBPeripheral peripheral *ns.CBPeripheral
@ -148,18 +121,9 @@ func main() {
hrm_uuid = ns.CBUUIDWithGoString("180D") hrm_uuid = ns.CBUUIDWithGoString("180D")
hrv_uuid = ns.CBUUIDWithGoString("2A37") hrv_uuid = ns.CBUUIDWithGoString("2A37")
info_uuid = ns.CBUUIDWithGoString("180A")
// We defined our own queue because this won't work on the main queue. //We defined our own queue because this won't work on the main queue.
cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(cd, queue) cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(cd, queue)
// For debugging purposes, run GC every second to make sure things are not
// over-released.
go func() {
for {
runtime.GC()
time.Sleep(time.Second)
}
}()
select {} select {}
} }

View File

@ -22,7 +22,6 @@ classes:
- NSDictionary - NSDictionary
- NSEnumerator - NSEnumerator
- NSString - NSString
- NSAutoreleasePool
functions: [ NSMakeRange, dispatch_queue_create ] functions: [ NSMakeRange, dispatch_queue_create ]
enums: [ CB.* ] enums: [ CB.* ]
@ -39,4 +38,4 @@ delegates:
- peripheralDidUpdateValueForCharacteristic - peripheralDidUpdateValueForCharacteristic
pragma: [ clang diagnostic ignored "-Wformat-security" ] pragma: [ clang diagnostic ignored "-Wformat-security" ]
gogc: true #gogc: true

View File

@ -15,7 +15,6 @@ func releaseX(x int) func (ns.MyClassSupermethods) {
super.Release() // comment out for leak super.Release() // comment out for leak
} }
} }
func memtest1() { func memtest1() {
fmt.Println("memtest1 started") fmt.Println("memtest1 started")
for { for {
@ -34,7 +33,7 @@ func memtest1() {
// in a real program. // in a real program.
runtime.GC() runtime.GC()
time.Sleep(time.Second/50) time.Sleep(time.Second/50)
//fmt.Printf("Loop complete\n") fmt.Printf("Loop complete\n")
} }
} }
@ -53,35 +52,18 @@ func memtest2() {
o4 := ns.NSStringAlloc() o4 := ns.NSStringAlloc()
_ = o4 _ = o4
a1 := ns.NSArrayAlloc() //arr := ns.NSArrayAlloc().InitWithObjects(o1,o1)
arr := ns.NSArrayWithObjects(o1,o2,o3,o4)
// init methods in Objective-C always return a retained object. _ = arr
// init may or may not return the same object that was sent in.
a1 = a1.InitWithObjects(o1,o2,o3,o4)
a2 := ns.NSArrayWithObjects(o1,o2,o3,o4)
// you can always nest alloc and init.
a3 := ns.NSMutableArrayAlloc().Init()
a3.AddObject(o1)
a3.AddObject(o2)
a3.AddObject(o3)
a3.AddObject(o4)
_ = a1
_ = a2
_ = a3
//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) {
// temporary strings made by the 'WithGoString' methods should be released
// automatically by the GC.
s1 := ns.NSStringAlloc().InitWithGoString("a string") s1 := ns.NSStringAlloc().InitWithGoString("a string")
arr.AddObject(s1) arr.AddObject(s1)
@ -93,35 +75,24 @@ func addStr(arr *ns.NSMutableArray) {
func memtest3() { func memtest3() {
fmt.Println("memtest3 started") fmt.Println("memtest3 started")
for { arr := ns.NSMutableArrayAlloc().Init() // arr will be garbage collected by Go
// arr will be garbage collected by Go
arr := ns.NSMutableArrayAlloc().Init()
addStr(arr) addStr(arr)
runtime.GC() runtime.GC()
time.Sleep(time.Second) time.Sleep(time.Second)
// check that our string was retained.
s1 := arr.ObjectAtIndex(0) s1 := arr.ObjectAtIndex(0)
gstr := s1.NSString().UTF8String().String() fmt.Println(s1.NSString().UTF8String())
_ = gstr fmt.Println("memtest3 done")
}
} }
func memtest4() { func memtest4() {
fmt.Println("memtest4 started") fmt.Println("memtest4 started")
for { for {
o1 := ns.NSStringAlloc().InitWithGoString("red string") o1 := ns.NSStringAlloc().InitWithGoString("four string")
// conversions to UTF8String internally create autoreleased strings
// in the Objective-C runtime. NSWrap runs these in a mini-
// @autoreleasepool block.
c1 := o1.UTF8String() c1 := o1.UTF8String()
_ = o1 _ = o1
_ = c1 _ = c1
runtime.GC() runtime.GC()
time.Sleep(time.Second/50) time.Sleep(time.Second/10)
} }
} }
@ -129,29 +100,14 @@ func memtest5() {
fmt.Println("memtest5 started") fmt.Println("memtest5 started")
i := 0 i := 0
for { for {
// by incrementing i we can ensure that Objective-C needs to create str := ns.NSStringWithGoString(fmt.Sprintf("five string %d",i))
// a new NSString object at each loop iteration and cannot be reusing _ = str
// the same string object.
str := ns.NSStringWithGoString(fmt.Sprintf("blue string %d",i))
// SubstringFromIndex should be returning a newly allocated NSString,
// which is getting retained by NSWrap and released by a Go GC
// finalizer.
sub := str.SubstringFromIndex(5) sub := str.SubstringFromIndex(5)
sub2 := sub.Copy().NSString() _ = sub
sub3 := sub2.MutableCopy().NSString() fmt.Printf("sub = %s\n",sub)
u := sub.UTF8String() time.Sleep(time.Second/10)
u2 := sub2.UTF8String()
u3 := sub3.UTF8String()
_ = u
_ = u2
_ = u3
time.Sleep(time.Second/50)
runtime.GC() runtime.GC()
i++ i++
//fmt.Printf("loop completed\n")
} }
} }
@ -161,12 +117,5 @@ func main() {
go memtest3() go memtest3()
go memtest4() go memtest4()
go memtest5() go memtest5()
go func() {
for {
// print a progress indicator
fmt.Printf("t = %s\n",time.Now())
time.Sleep(time.Second * 10)
}
}()
select {} select {}
} }

View File

@ -246,10 +246,10 @@ func (t *Type) _CType(attrib bool) string {
return ct return ct
} }
func (t *Type) GoTypeDecl(fin bool) string { func (t *Type) GoTypeDecl() string {
gt := t.GoType() gt := t.GoType()
if wrapped[gt] { if wrapped[gt] {
return t.GoInterfaceDecl(fin) return t.GoInterfaceDecl()
} }
if t.Node.IsId() { if t.Node.IsId() {
return "" return ""
@ -286,7 +286,7 @@ type %s %s
} }
} }
func (t *Type) GoInterfaceDecl(fin bool) string { func (t *Type) GoInterfaceDecl() string {
ct := t.CType() ct := t.CType()
gt := t.GoType() gt := t.GoType()
if Debug { if Debug {
@ -314,7 +314,9 @@ type %s interface {
type %s struct { %s } type %s struct { %s }
func (o *%s) Ptr() unsafe.Pointer { if o == nil { return nil }; 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 {
return (*%s)(unsafe.Pointer(o)) ret := &%s{}
ret.ptr = o.ptr
return ret
} }
`, gt, super, gt, gt, gt, gt) `, gt, super, gt, gt, gt, gt)
} }
@ -382,7 +384,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(sname, name string, pnames, snames []string, rtype *Type, ptypes []*Type, fun, fin bool, cm 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 ""
@ -492,28 +494,11 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T
}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, 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" {
cmp := ""
if sw {
if !cm && sname != "copy" && sname != "mutableCopy" {
cmp = fmt.Sprintf(`
if ret.ptr == o.ptr { return (%s)(unsafe.Pointer(o)) }`,rtgt)
}
ret.WriteString(fmt.Sprintf(`
if ret.ptr == nil { return ret }%s`,cmp))
}
if fin { if fin {
dbg := ""
dbg2 := ""
if Debug {
dbg = fmt.Sprintf(`fmt.Printf("Setting finalizer (%s): %%p -> %%p\n", ret, ret.ptr)
`, rtgt)
dbg2 = fmt.Sprintf(`fmt.Printf("Finalizer (%s): release %%p -> %%p\n", o, o.ptr)
`, rtgt)
}
ret.WriteString(fmt.Sprintf(` ret.WriteString(fmt.Sprintf(`
%sruntime.SetFinalizer(ret, func(o %s) { runtime.SetFinalizer(ret, func(o %s) {
%so.Release() o.Release()
})`, dbg, rtgt, dbg2)) })`,rtgt))
} }
ret.WriteString(` ret.WriteString(`
return ret`) return ret`)

View File

@ -177,14 +177,14 @@ func TestType(t *testing.T) {
chk(tp.PointsTo(), tp2) chk(tp.PointsTo(), tp2)
chk(tp2.PointsTo(), nil) chk(tp2.PointsTo(), nil)
chk(tp.GoTypeDecl(false), ` chk(tp.GoTypeDecl(), `
type MyTypedef **C.NSObject type MyTypedef **C.NSObject
`) `)
str = "void" str = "void"
n = &Node{"TypeName", "", []*Node{ n = &Node{"TypeName", "", []*Node{
&Node{"TypeSpecifier", "void", []*Node{}}}} &Node{"TypeSpecifier", "void", []*Node{}}}}
chk_newtype() chk_newtype()
chk(tp.GoTypeDecl(false), "") chk(tp.GoTypeDecl(), "")
void := tp void := tp
str = "BOOL" str = "BOOL"
@ -206,14 +206,14 @@ type MyTypedef **C.NSObject
chk(tp.GoType(), "*NSObject") chk(tp.GoType(), "*NSObject")
Wrap("NSString") Wrap("NSString")
chk(nso.GoTypeDecl(false), ` chk(nso.GoTypeDecl(), `
type NSObject interface { type NSObject interface {
Ptr() unsafe.Pointer Ptr() unsafe.Pointer
} }
`) `)
chk(nso.GoType(), "NSObject") chk(nso.GoType(), "NSObject")
chk(nst.GoTypeDecl(false), ` chk(nst.GoTypeDecl(), `
type NSString struct { Id } type NSString struct { Id }
func (o *NSString) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } func (o *NSString) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
func (o *Id) NSString() *NSString { func (o *Id) NSString() *NSString {
@ -276,7 +276,8 @@ func (o *Id) NSString() *NSString {
snames := []string{"", "", "", ""} snames := []string{"", "", "", ""}
chk_gotoc := func(expected string) { chk_gotoc := func(expected string) {
chk(GoToC("myFun", pnames, snames, rtype, ptypes, false, false, false), expected) fmt.Printf("chk_gotoc\n")
chk(GoToC("myFun", pnames, snames, rtype, ptypes, false, false), expected)
} }
chk_gotoc("") chk_gotoc("")
@ -351,7 +352,7 @@ func (o *Id) NSString() *NSString {
} }
return ret`) return ret`)
chk(GoToC("myFun", pnames, snames, rtype, ptypes, true, false, false), chk(GoToC("myFun", pnames, snames, rtype, ptypes, true, false),
`ret := &Id{} `ret := &Id{}
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), p4)) ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), p4))
(*p3) = (*p3)[:cap(*p3)] (*p3) = (*p3)[:cap(*p3)]

View File

@ -9,7 +9,7 @@ var (
) )
func dbg(f string, xs ...interface{}) { func dbg(f string, xs ...interface{}) {
if Debug && false { if Debug {
fmt.Printf(f, xs...) fmt.Printf(f, xs...)
} }
} }

View File

@ -52,7 +52,6 @@ type Wrapper struct {
func NewWrapper(debug bool) *Wrapper { func NewWrapper(debug bool) *Wrapper {
Debug = debug Debug = debug
types.Debug = Debug
if Debug { if Debug {
fmt.Println("// Debug mode") fmt.Println("// Debug mode")
} }
@ -136,7 +135,6 @@ type Subclass struct {
type Property struct { type Property struct {
Name, Attr string Name, Attr string
Type *types.Type Type *types.Type
retained, notretained bool
} }
type Parameter struct { type Parameter struct {
@ -153,45 +151,13 @@ type Method struct {
} }
func (m *Method) ShouldFinalize() bool { func (m *Method) ShouldFinalize() bool {
grtype := m.Type.GoType() return shouldFinalize(m.Type.GoType(), m.Name)
return Gogc && grtype != "NSAutoreleasePool" &&
(types.PtrShouldWrap(grtype) || grtype == "*Id") &&
(!m.ClassMethod || IsRetained(m.Name))
} }
// IsRetained returns true if a given instance method returns a retained object. func shouldFinalize (grtype, name string) bool {
func IsRetained(name string) bool { return Gogc && grtype != "NSAutoreleasePool" && (types.ShouldWrap(grtype) || grtype == "Id") && (len(name) < 4 || name[:4] != "init")
return (
(len(name) >= 3 && name[:3] == "new") ||
(len(name) >= 4 && name[:4] == "init") ||
(len(name) >= 4 && name[:4] == "copy") ||
(len(name) >= 5 && name[:5] == "alloc") ||
(len(name) >= 11 && name[:11] == "mutableCopy"))
} }
// IsRetainedProperty returns true if the name matches a retained property for
// the given interface.
func (i *Interface) IsRetainedProperty(name string) bool {
if p, ok := i.Properties[name]; ok {
if p.retained {
return true
}
if p.notretained {
return false
}
attrs := strings.Split(p.Attr," ")
for _, a := range attrs {
if a == "retain" {
p.retained = true // cache this result
return true
}
}
p.notretained = true // cache this result
}
return false
}
//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
@ -218,14 +184,13 @@ type Protocol struct {
} }
type MethodCollection struct { type MethodCollection struct {
Class, GoClass string Class string
Methods []*Method Methods []*Method
} }
func NewMethodCollection(class string) *MethodCollection { func NewMethodCollection(class string) *MethodCollection {
ret := &MethodCollection{ ret := &MethodCollection{
Class: class, Class: class,
GoClass: strings.Title(class),
Methods: []*Method{}, Methods: []*Method{},
} }
return ret return ret
@ -599,7 +564,7 @@ func (w *Wrapper) AddEnum(n *ast.EnumDecl, rs []string) {
func (w *Wrapper) addIntCat(name string, ns []ast.Node) { func (w *Wrapper) addIntCat(name string, ns []ast.Node) {
var i *Interface var i *Interface
var ok bool var ok bool
goname := strings.Title(name) goname := strings.Title(types.NewTypeFromString(name, name).GoType())
types.Wrap(goname) types.Wrap(goname)
if i, ok = w.Interfaces[name]; !ok { if i, ok = w.Interfaces[name]; !ok {
i = &Interface{} i = &Interface{}
@ -607,7 +572,6 @@ func (w *Wrapper) addIntCat(name string, ns []ast.Node) {
i.GoName = goname i.GoName = goname
i.InstanceMethods = NewMethodCollection(name) i.InstanceMethods = NewMethodCollection(name)
i.ClassMethods = NewMethodCollection(name) i.ClassMethods = NewMethodCollection(name)
i.Properties = map[string]*Property{}
i.Protocols = []string{} i.Protocols = []string{}
i.ProcessedInstanceMethods = map[string]bool{} i.ProcessedInstanceMethods = map[string]bool{}
} }
@ -618,15 +582,19 @@ func (w *Wrapper) addIntCat(name string, ns []ast.Node) {
for _, c := range ns { for _, c := range ns {
switch x := c.(type) { switch x := c.(type) {
case *ast.ObjCPropertyDecl: case *ast.ObjCPropertyDecl:
//FIXME: Properties are not supported, typically there
//will be setter/getter methods you can use instead
if Debug { if Debug {
fmt.Printf("ObjCPropertyDecl: %s\n", x.Name) fmt.Printf("ObjCPropertyDecl: %s\n", x.Name)
} }
p := &Property{ //p := &Property{
Name: x.Name, // Name: x.Name,
Type: types.NewTypeFromString(x.Type,name), // Type: types.NewTypeFromString(x.Type,name),
Attr: x.Attr, //}
} //_,avail = w.GetParms(x,name) // TODO
i.Properties[x.Name] = p //if avail {
// i.Properties[p.Name] = p
//}
case *ast.ObjCMethodDecl: case *ast.ObjCMethodDecl:
if Debug { if Debug {
fmt.Printf("ObjCMethodDecl: %s (%s) %s\n", x.Type, name, x.Name) fmt.Printf("ObjCMethodDecl: %s (%s) %s\n", x.Type, name, x.Name)
@ -797,6 +765,7 @@ func (w *Wrapper) AddTypedef(n, t string) {
} }
types.Wrap(n) types.Wrap(n)
types.SetSuper(n, gt) types.SetSuper(n, gt)
//w._processType(tp, n)
w._processType(tp) w._processType(tp)
} else { } else {
cgt := tp.CGoType() cgt := tp.CGoType()
@ -816,6 +785,8 @@ 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()
//w._processType(bt, gt)
w._processType(bt) w._processType(bt)
} }
@ -833,10 +804,8 @@ func (w *Wrapper) _processType(bt *types.Type) {
return return
} }
if w.ProcessedTypes[gt] { if w.ProcessedTypes[gt] {
if Debug { fmt.Printf(" -- already seen\n") }
return return
} }
if Debug { fmt.Printf(" -- not yet seen\n") }
w.ProcessedTypes[gt] = true w.ProcessedTypes[gt] = true
if gt == "Char" { if gt == "Char" {
w.CharHelpers() w.CharHelpers()
@ -859,7 +828,7 @@ func (w *Wrapper) _processType(bt *types.Type) {
if Debug { if Debug {
fmt.Printf("Writing go type for %s -> %s\n",bt.CType(),gt) fmt.Printf("Writing go type for %s -> %s\n",bt.CType(),gt)
} }
w.goTypes.WriteString(bt.GoTypeDecl(Gogc)) w.goTypes.WriteString(bt.GoTypeDecl())
} }
func (w *Wrapper) CharHelpers() { func (w *Wrapper) CharHelpers() {
@ -891,19 +860,13 @@ func (o *NSString) String() string {
} }
func (w *Wrapper) EnumeratorHelpers() { func (w *Wrapper) EnumeratorHelpers() {
var re1, re2 string w.goHelpers.WriteString(`
if Gogc && false { // FIXME: don't need this
re1 = "o.Release(); "
re2 = `
o.Release()`
}
w.goHelpers.WriteString(fmt.Sprintf(`
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) { %sbreak }%s if !f(o) { break }
} }
} }
`, re1, re2)) `)
} }
func (w *Wrapper) AutoreleaseHelpers() { func (w *Wrapper) AutoreleaseHelpers() {
@ -935,13 +898,12 @@ func (w *Wrapper) ProcessMethod(m *Method) {
} }
func (w *Wrapper) ProcessMethodForClass(m *Method, class string) { func (w *Wrapper) ProcessMethodForClass(m *Method, class string) {
goclass := strings.Title(class) goclass := strings.Title(types.NewTypeFromString(class, class).GoType())
m2 := &Method{ m2 := &Method{
Name: m.Name, GoName: m.GoName, Class: class, GoClass: goclass, Name: m.Name, GoName: m.GoName, Class: class, GoClass: goclass,
Type: m.Type.CloneToClass(class), Type: m.Type.CloneToClass(class),
ClassMethod: m.ClassMethod, ClassMethod: m.ClassMethod,
Parameters: make([]*Parameter, len(m.Parameters)), Parameters: make([]*Parameter, len(m.Parameters)),
Unavailable: m.Unavailable,
} }
for i, p := range m.Parameters { for i, p := range m.Parameters {
m2.Parameters[i] = &Parameter{ m2.Parameters[i] = &Parameter{
@ -976,7 +938,6 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
gname = strings.Title(gname) gname = strings.Title(gname)
gname = strings.ReplaceAll(gname, " ", "") gname = strings.ReplaceAll(gname, " ", "")
receiver := "" receiver := ""
constructor := false // this is an autoreleased object constructor
var cname string var cname string
if fun { if fun {
cname = m.Name cname = m.Name
@ -986,16 +947,6 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
if Debug { if Debug {
fmt.Printf("Method %s (GoClass %s)\n", cname, m.GoClass) fmt.Printf("Method %s (GoClass %s)\n", cname, m.GoClass)
} }
grtype := m.Type.GoType()
if grtype == "Void" {
grtype = ""
}
if types.PtrIsGoInterface(grtype) {
grtype = "*Id"
}
if grtype == "BOOL" { // convert objective-c bools to Go bools
grtype = "bool"
}
switch { switch {
case !m.ClassMethod: case !m.ClassMethod:
if types.IsGoInterface(m.GoClass) { if types.IsGoInterface(m.GoClass) {
@ -1005,19 +956,15 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
} }
//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
case m.ClassMethod: default:
//Shorten class method names //Shorten class method names
lens1 := len(m.Class) lens1 := len(m.Class)
i := 0 i := 0
if len(gname) < lens1 { if len(gname) < len(m.Class) {
i = lens1 - len(gname) i = lens1 - len(gname)
} }
for ; i < lens1; i++ { for ; i < lens1; i++ {
if m.Class[i:] == gname[:lens1-i] { if m.Class[i:] == gname[:lens1-i] {
if Gogc &&
(types.PtrShouldWrap(grtype) || grtype == "*Id") {
constructor = true
}
break break
} }
} }
@ -1044,26 +991,34 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
cmtype = m.Type.CTypeAttrib() cmtype = m.Type.CTypeAttrib()
} }
ns, snames, tps, gplist := w.gpntp(m) ns, snames, tps, gplist := w.gpntp(m)
grtype := m.Type.GoType()
if grtype == "Void" {
grtype = ""
}
if types.PtrIsGoInterface(grtype) {
grtype = "*Id"
}
if grtype == "BOOL" { // convert objective-c bools to Go bools
grtype = "bool"
}
if gname == grtype { // avoid name conflicts between methods and types if gname == grtype { // avoid name conflicts between methods and types
gname = "Get" + gname gname = "Get" + gname
} }
var inter *Interface
if m.ClassMethod { if m.ClassMethod {
if w.ProcessedClassMethods[gname] { if w.ProcessedClassMethods[gname] {
return return
} }
w.ProcessedClassMethods[gname] = true w.ProcessedClassMethods[gname] = true
} else { } else {
var ok bool i, ok := w.Interfaces[m.Class]
inter,ok = w.Interfaces[m.Class]
if !ok { if !ok {
fmt.Printf("Can't find interface %s for method %s\n", m.Class, m.Name) fmt.Printf("Can't find interface %s for method %s\n", m.Class, m.Name)
os.Exit(-1) os.Exit(-1)
} }
if inter.ProcessedInstanceMethods[gname] { if i.ProcessedInstanceMethods[gname] {
return return
} }
inter.ProcessedInstanceMethods[gname] = true i.ProcessedInstanceMethods[gname] = true
} }
w.goCode.WriteString(fmt.Sprintf(` w.goCode.WriteString(fmt.Sprintf(`
@ -1093,7 +1048,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(m.Name, cname, ns, snames, m.Type, tps, fun, constructor || m.ShouldFinalize(), m.ClassMethod) + "\n}\n") types.GoToC(cname, ns, snames, m.Type, tps, fun, m.ShouldFinalize()) + "\n}\n")
cret := "" cret := ""
if !m.isVoid() { if !m.isVoid() {
@ -1133,64 +1088,29 @@ func %s%s(%s) %s {
default: default:
if Gogc && !m.isVoid() { if Gogc && !m.isVoid() {
rtn := "" rtn := ""
if types.PtrShouldWrap(m.Type.GoType()) { //if m.ClassMethod && types.ShouldWrap(m.Type.GoType()) {
switch { if types.ShouldWrap(m.Type.GoType()) {
case m.ClassMethod: if m.ClassMethod {
if grtype != "*NSAutoreleasePool" && constructor {
// retain objects returned by class constructor methods
rtn = ` rtn = `
[ret retain];` [ret retain];`
} } else {
// do not retain new, alloc, init and copy methods
case IsRetained(m.Name):
default:
// by default, for instance methods, retain
// if returning a new object
rtn = ` rtn = `
if (o != ret) { [ret retain]; }` if (o != ret) { [ret retain]; }`
} }
} }
var ar1, ar2 string
if constructor || true {
ar1 = "@autoreleasepool {\n\t\t"
ar2 = "\n }"
}
w.cCode.WriteString(fmt.Sprintf( w.cCode.WriteString(fmt.Sprintf(
` %s ret; ` %s ret;
%sret = [%s %s];%s%s @autoreleasepool {
ret = [%s %s];%s
}
return ret; return ret;
}`, m.Type.CTypeAttrib(), ar1, cobj, w.objcparamlist(m), rtn, ar2)) }`, m.Type.CTypeAttrib(), cobj, w.objcparamlist(m),rtn))
} else { } else {
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s]; w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
}`, cret, cobj, w.objcparamlist(m))) }`, cret, cobj, w.objcparamlist(m)))
} }
} }
// create SetFinalizer methods when we see an alloc function:
if Gogc && m.Name == "alloc" {
cls := m.GoClass
if types.IsGoInterface(cls) {
cls = "Id"
}
dbg := ""
dbg2 := ""
if Debug {
dbg = fmt.Sprintf(`fmt.Printf("Setting GC finalizer (%s): %%p -> %%p\n", o, o.ptr)
`,cls)
dbg2 = fmt.Sprintf(`fmt.Printf("GC finalizer (%s): release %%p -> %%p\n", o, o.ptr)
`, cls)
}
w.goCode.WriteString(fmt.Sprintf(`
func (o *%s) GC() {
if o.ptr == nil { return }
%sruntime.SetFinalizer(o, func(o *%s) {
%so.Release()
})
}
`, cls, dbg, cls, dbg2))
}
// create GoString helper method // create GoString helper method
if ok, _ := regexp.MatchString("WithString$", m.Name); ok { if ok, _ := regexp.MatchString("WithString$", m.Name); ok {
if Debug { if Debug {
@ -1223,12 +1143,21 @@ func (o *%s) GC() {
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 {
%sret := %s%s(%s) %sret := %s%s(%s)
return ret %sreturn ret
} }
`, receiver, gname2, gplist, grtype, cvts, obj, gname, strings.Join(ns, ", "))) `, receiver, gname2, gplist, grtype, cvts, obj, gname, strings.Join(ns, ", "), finalizer))
} }
} }
@ -1317,40 +1246,7 @@ func (w *Wrapper) MethodFromSig(sig, class string) *Method {
return ret return ret
} }
func (mc *MethodCollection) AddMethod(m *Method) {
m2 := &Method{
Name: m.Name,
GoName: m.GoName,
Class: mc.Class,
GoClass: mc.GoClass,
Type: m.Type.CloneToClass(mc.Class),
ClassMethod: m.ClassMethod,
Parameters: []*Parameter{},
Unavailable: m.Unavailable,
}
for _, p := range m.Parameters {
p2 := &Parameter{
Pname: p.Pname,
Vname: p.Vname,
Type: p.Type.CloneToClass(mc.Class),
}
m2.Parameters = append(m2.Parameters, p2)
}
mc.Methods = append(mc.Methods, m2)
}
func (mc *MethodCollection) AddMethods(smc *MethodCollection) {
for _, m := range smc.Methods {
mc.AddMethod(m)
}
}
func (w *Wrapper) ProcessSubclass(sname string, sc *Subclass) { func (w *Wrapper) ProcessSubclass(sname string, sc *Subclass) {
i := &Interface{
ProcessedInstanceMethods: map[string]bool{},
Properties: map[string]*Property{},
}
w.Interfaces[sname] = i
gname := strings.Title(sname) gname := strings.Title(sname)
types.Wrap(gname) types.Wrap(gname)
types.SetSuper(gname, sc.Super) types.SetSuper(gname, sc.Super)
@ -1361,17 +1257,12 @@ func (w *Wrapper) ProcessSubclass(sname string, sc *Subclass) {
nms[i] = w.MethodFromSig(sig, sname) nms[i] = w.MethodFromSig(sig, sname)
} }
if Debug { if Debug {
fmt.Printf("ProcessSubclass(%s)\n",sname) fmt.Println("ProcessSubclass(%s)\n",sname)
} }
w._ProcessDelSub(sname, ps, nms, true) w._ProcessDelSub(sname, ps, nms, true)
} }
func (w *Wrapper) ProcessDelegate(dname string, ps map[string][]string) { func (w *Wrapper) ProcessDelegate(dname string, ps map[string][]string) {
i := &Interface{
ProcessedInstanceMethods: map[string]bool{},
Properties: map[string]*Property{},
}
w.Interfaces[dname] = i
w._ProcessDelSub(dname, ps, nil, false) w._ProcessDelSub(dname, ps, nil, false)
} }
@ -1385,7 +1276,6 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
//5. Go constructor //5. Go constructor
//6. Go dispatch database for callbacks //6. Go dispatch database for callbacks
//7. Go superclass dispatch function //7. Go superclass dispatch function
//8. Methods inherited from parent class
//To create (per method): //To create (per method):
//1. ObjC function prototypes for go exports //1. ObjC function prototypes for go exports
//2. Go callback registration functions //2. Go callback registration functions
@ -1400,7 +1290,6 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
sms := 0 // the number of methods that have super-methods sms := 0 // the number of methods that have super-methods
gnames := []string{} // go names for methods gnames := []string{} // go names for methods
pnames := make([]string, len(ps)) pnames := make([]string, len(ps))
supmeths := []*Method{}
var supr string var supr string
i := 0 i := 0
for pname, pats := range ps { for pname, pats := range ps {
@ -1475,8 +1364,6 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
fmt.Printf("--Method: %s\n", m.Name) fmt.Printf("--Method: %s\n", m.Name)
} }
if !matches(string(m.Name[0])+m.GoName[1:], pats) { if !matches(string(m.Name[0])+m.GoName[1:], pats) {
//methods from superclass that we are not overriding
supmeths = append(supmeths,m)
continue continue
} }
if m.HasUnsupportedType() { if m.HasUnsupportedType() {
@ -1719,29 +1606,19 @@ void*
} }
//4. Go type //4. Go type
if !w.ProcessedTypes[gname] {
gotypes.WriteString( gotypes.WriteString(
types.NewTypeFromString(gname, supr).GoInterfaceDecl(Gogc)) types.NewTypeFromString(gname, supr).GoInterfaceDecl())
//5. Go constructor //5. Go constructor
var finalizer string var finalizer string
dbg := "" if shouldFinalize(gname,"") {
dbg2 := ""
if Debug {
dbg = fmt.Sprintf(`fmt.Printf("Setting finalizer (%s): %%p -> %%p\n", ret, ret.ptr)
`, gname)
dbg2 = fmt.Sprintf(`fmt.Printf("Finalizer (%s): release %%p -> %%p\n", o, o.ptr)
`, gname)
}
if Gogc {
w.goImports["runtime"] = true w.goImports["runtime"] = true
if Debug { w.goImports["fmt"] = true }
finalizer = fmt.Sprintf( finalizer = fmt.Sprintf(
`if ret.ptr == nil { return ret } `runtime.SetFinalizer(ret,func(o *%s) {
%sruntime.SetFinalizer(ret,func(o *%s) { o.Release()
%so.Release()
}) })
`, dbg, gname, dbg2) `,gname)
} }
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
func %sAlloc() *%s { func %sAlloc() *%s {
@ -1750,22 +1627,6 @@ func %sAlloc() *%s {
%sreturn ret %sreturn ret
} }
`, gname, gname, gname, dname, finalizer)) `, gname, gname, gname, dname, finalizer))
if Gogc {
if Debug {
dbg = fmt.Sprintf(`fmt.Printf("Setting finalizer (%s): %%p -> %%p\n", o, o.ptr)
`, gname)
}
gocode.WriteString(fmt.Sprintf(`
func (o *%s) GC() {
if o.ptr == nil { return }
%sruntime.SetFinalizer(o,func(o *%s) {
%so.Release()
})
}
`, gname, dbg, gname, dbg2))
}
}
w.ProcessedTypes[gname] = true
//6. Go dispatch database for callbacks //6. Go dispatch database for callbacks
dispitems := make([]string, len(gnames)) dispitems := make([]string, len(gnames))
@ -1929,7 +1790,7 @@ func (o *%s) Super%s(%s) %s {
} }
`, vn, w.Vaargs, vn, vn)) `, vn, w.Vaargs, vn, vn))
} }
gocode.WriteString("\t" + types.GoToC(m.Name, dname+"_super_"+m.Name, ns, snames, m.Type, tps, false, m.ShouldFinalize(), m.ClassMethod) + "\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())
@ -1937,11 +1798,6 @@ func (o *%s) Super%s(%s) %s {
w.goTypes.WriteString(gotypes.String()) w.goTypes.WriteString(gotypes.String())
w.goCode.WriteString(gocode.String()) w.goCode.WriteString(gocode.String())
w.goExports.WriteString(goexports.String()) w.goExports.WriteString(goexports.String())
// add methods from parent class that we are not overriding
for _,m := range supmeths {
w.ProcessMethodForClass(m,dname)
}
} }
//Add class and instance methods from super class //Add class and instance methods from super class
@ -1952,8 +1808,8 @@ func (w *Wrapper) AddSupermethods(i *Interface) {
m2 := &Method{ m2 := &Method{
Name: m.Name, Name: m.Name,
GoName: m.GoName, GoName: m.GoName,
Class: mc.Class, Class: i.Name,
GoClass: mc.GoClass, GoClass: i.GoName,
Type: m.Type.CloneToClass(i.Name), Type: m.Type.CloneToClass(i.Name),
ClassMethod: m.ClassMethod, ClassMethod: m.ClassMethod,
Parameters: []*Parameter{}, Parameters: []*Parameter{},