Bug fixes. Read availability attributes when adding interfaces and

categories. Memory leak fixes. Add "autorelease" confiruation directive.
Fix parameter types for subclass method overriding.
This commit is contained in:
Greg 2019-06-06 00:22:07 -04:00
parent 50b9387a91
commit 90166a4379
11 changed files with 5750 additions and 109 deletions

View File

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

View File

@ -38,3 +38,4 @@ delegates:
- peripheralDidUpdateValueForCharacteristic
pragma: [ clang diagnostic ignored "-Wformat-security" ]
autorelease: true

175
examples/memory/main.go Normal file
View File

@ -0,0 +1,175 @@
package main
import "C"
import (
"fmt"
"runtime"
"time"
"git.wow.st/gmp/nswrap/examples/memory/ns"
)
func dealloc() {
//[super dealloc] is called for you automatically, so no Supermethods
//struct is provided here.
fmt.Println("--dealloc called")
}
func release(super ns.MyClassSupermethods) {
fmt.Println("--release called")
super.Release() // comment out for leak
}
//Basic memory allocation test using a manual Autorelease pool. Also utilizes
//a custom object that overrides dealloc and release methods. Make sure you
//call [super release] or you will have a leak (see above). [super dealloc] is
//called for youautomatically as it is basically always required.
func memtest1() {
//because time.Sleep() is called within each loop, it is necessary
//to lock this goroutine to a thread. Otherwise, Sleep can return
//and continue execution on a different thread, causing the risk that
//the next call to NSAutoreleasePool.Drain() seg faults because it is
//not in the same thread where the NSAutoreleasePool was allocated.
runtime.LockOSThread()
fmt.Println("memtest1: started")
for {
pool := ns.NSAutoreleasePoolAlloc().Init()
o1 := ns.MyClassAlloc()
//If autorelease: true is set in nswrap.yaml, the manual calls to
//autorelease are not necessary.
o1.Autorelease()
o1.DeallocCallback(dealloc)
o1.ReleaseCallback(release)
o2 := ns.NSObjectAlloc()
o2.Autorelease()
o3 := ns.NSMutableArrayAlloc()
o3.Autorelease()
o4 := ns.NSStringAlloc()
o4.Autorelease()
_ = o1
_ = o2
_ = o3
_ = o4
pool.Drain()
time.Sleep(time.Second/2)
}
fmt.Println("memtest1: done")
}
//Test the ns.Autoreleasepool() function. Also confirm that we do not need
//to release or manually autorelease objects returned by constructor methods
//(i.e. not created with *Alloc()).
func memtest2() {
runtime.LockOSThread()
fmt.Println("memtest2: started")
i := 0
for {
ns.Autoreleasepool(func() {
o1 := ns.NSObjectAlloc()
o1.Autorelease()
s1 := ns.NSStringWithGoString(fmt.Sprintf("string-%d",i))
_ = s1
//o1.Retain() // uncomment for leak
})
time.Sleep(time.Second/3)
}
fmt.Println("memtest2: done")
}
//Test nested Autoreleasepool invocations -- confirms that objects in the
//outer pool are not deallocated by the inner pool.
func memtest3() {
runtime.LockOSThread() // comment out for crash
fmt.Println("memtest3: started")
for { ns.Autoreleasepool(func() {
arr := ns.NSMutableArrayAlloc().Init()
arr.Autorelease()
arr.AddObject(ns.NSStringWithGoString("my string"))
for { ns.Autoreleasepool(func() {
str := arr.ObjectAtIndex(0).NSString()
fmt.Println(str) // does not leak in an autorelease pool
time.Sleep(time.Second / 2)
})}
time.Sleep(time.Second)
})}
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.
func memtest4() {
go memtest4a()
go memtest4a()
go memtest4a()
go memtest4b()
go memtest4b()
go memtest4b()
go memtest4c()
go memtest4c()
go memtest4c()
// Exactly one goroutine (locked to an OS thread) can use an
// autorelease pool (?)
go memtest1()
}
func memtest4a() {
for {
o1 := ns.NSObjectAlloc()
o1.Init()
time.Sleep(time.Second/50)
o1.Release()
}
}
func memtest4b() {
for {
o1 := ns.NSObjectAlloc() // need to Release
s1 := ns.NSStringWithGoString("a string")
arr := ns.NSArrayAlloc().InitWithObjects(s1) // need to Release
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
//fmt.Println(s1) // uncomment for leak
time.Sleep(time.Second/50)
o1.Release()
arr.Release()
_ = o1
_ = s2
}
}
func memtest4c() {
for {
o1 := ns.NSArrayAlloc()
o2 := ns.NSStringAlloc()
time.Sleep(time.Second/50)
o1.Release()
o2.Release()
}
}
func main() {
//Uncomment more than one test at a time for a crash.
//Note: You may not run autorelease pools from multiple goroutines.
//Within an autorelease pool, do not do anything that can result in a
//switch to a different thread.
//go memtest1()
//go memtest2()
go memtest3()
//go memtest4()
select { }
}

BIN
examples/memory/memory Executable file

Binary file not shown.

View File

@ -0,0 +1,36 @@
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 MyClassDealloc
func MyClassDealloc(o unsafe.Pointer) {
cb := MyClassLookup[o].Dealloc
if cb == nil { return }
cb()
}
//export MyClassRelease
func MyClassRelease(o unsafe.Pointer) {
cb := MyClassLookup[o].Release
if cb == nil { return }
self := MyClass{}
self.ptr = o
super := MyClassSupermethods{
self.SuperRelease,
}
cb(super)
}

5322
examples/memory/ns/main.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
inputfiles:
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
classes:
- NSAutoreleasePool
- NSArray
- NSMutableArray
- NSString
- NSObject
subclasses:
MyClass:
NSObject:
- dealloc
- release
frameworks:
- Foundation
pragma: [ clang diagnostic ignored "-Wformat-security" ]

BIN
examples/memory/tests Executable file

Binary file not shown.

View File

@ -38,6 +38,7 @@ type conf struct {
Vaargs int
//Arc flag for debugging only, builds will break
Arc bool
Autorelease bool
}
var Config conf
@ -250,6 +251,9 @@ func Start() (err error) {
if Config.Arc {
wrap.Arc = true
}
if Config.Autorelease {
wrap.Autorelease = true
}
//NOTE: converting in parallel is slower on my system
//nodes := convertLinesToNodesParallel(lines)
nodes := convertLinesToNodes(lines)

View File

@ -547,6 +547,7 @@ func TestFuncs(t *testing.T) {
str = "id"
n,err = Parse(str)
f(str,n.IsId(),true)
f(str,n.IsPointer(),true)
str = "int * _Nullable * _Nonnull"
n,err = Parse(str)

View File

@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path"
"reflect"
"regexp"
"sort"
"strconv"
@ -17,6 +18,7 @@ var (
Debug = false
// Arc flag is for debugging only, your builds will break if you turn it on
Arc = false
Autorelease = false
)
type Wrapper struct {
@ -344,17 +346,21 @@ type Interface struct {
ProcessedInstanceMethods map[string]bool
}
//AddInterface adds an Objective-C interface to a Wrapper.
func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) {
//fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name)
w.add(n.Name, n.Children())
if Debug { fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name) }
w.addIntCat(n.Name, n.Children())
}
//AddCategory adds an Objective-C category to a Wrapper.
//the first child node of an ObjCCategoryDecl is always an ObjCInterface
//indicating which interface is being extended by this category.
func (w *Wrapper) AddCategory(n *ast.ObjCCategoryDecl) {
ns := n.Children()
if len(ns) > 0 {
switch x := ns[0].(type) {
case *ast.ObjCInterface:
w.add(x.Name, ns[1:])
w.addIntCat(x.Name, ns[1:])
return
}
}
@ -377,7 +383,7 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
//fmt.Printf("AddFunction(%s): not a function -- Node type is %s\n%s",n.Name,f.Kind,tp.String())
return
}
//fmt.Printf("FunctionDecl: %s (%s) %s\n",n.Type,m.Type.CType(),n.Name)
if Debug { fmt.Printf("FunctionDecl: %s (%s) %s\n",n.Type,m.Type.CType(),n.Name) }
i := 0
for _,c := range n.Children() {
switch x := c.(type) {
@ -388,7 +394,7 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
}
m.Parameters = append(m.Parameters,p)
i++
//fmt.Printf(" %s\n",p.Type.CType())
if Debug { fmt.Printf(" %s\n",p.Type.CType()) }
case *ast.Variadic:
p := &Parameter{
Vname: "object",
@ -399,25 +405,19 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
i++
}
}
if i > 0 && len(f.Children) > i {
if e := f.Children[i]; len(e.Children) > 0 {
//fmt.Println(" Next parameter: ",e.Children[0].String())
//m.Parameters[i-1].Type.Variadic = true
}
}
w.Functions[n.Name] = m
}
func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
p := w.Protocols[n.Name]
if p == nil {
//fmt.Printf("Adding protocol %s\n",n.Name)
p = &Protocol{ }
//p.GoName = types.NewTypeFromString(n.Name,n.Name).GoType()
p.ClassMethods = NewMethodCollection(n.Name)
p.InstanceMethods = NewMethodCollection(n.Name)
if Debug { fmt.Printf("Adding protocol %s\n",n.Name) }
p = &Protocol{
ClassMethods: NewMethodCollection(n.Name),
InstanceMethods: NewMethodCollection(n.Name),
}
}
//fmt.Printf("Protocol %s\n",p.Name)
if Debug { fmt.Printf("Protocol %s\n",n.Name) }
for _,c := range n.Children() {
switch x := c.(type) {
case *ast.ObjCMethodDecl:
@ -448,13 +448,14 @@ func (w *Wrapper) AddMethod(p *MethodCollection, x *ast.ObjCMethodDecl) {
GoClass: strings.Title(p.Class),
ClassMethod: x.ClassMethod,
}
//fmt.Printf(" -- Method %s\n",m.Name)
if Debug { fmt.Printf(" -- Method %s\n",m.Name) }
var avail bool
m.Parameters, avail = w.GetParms(x,p.Class)
if avail {
//fmt.Printf("%s: Adding %s (%d)\n",p.Class,m.Name,len(m.Parameters))
//fmt.Printf("--Method name is %s\n",m.Name)
if Debug { fmt.Printf("%s: Adding %s (%d)\n",p.Class,m.Name,len(m.Parameters)) }
p.Methods = append(p.Methods,m)
} else {
if Debug { fmt.Printf("--Method %s is not available\n",m.Name) }
}
}
@ -472,14 +473,14 @@ func (w *Wrapper) AddEnum(n *ast.EnumDecl,rs []string) {
if n.Name != "" && !matches(n.Name,rs) {
return
}
//fmt.Printf("Adding enum: (%s) %s\n",n.Type,n.Name)
if Debug { fmt.Printf("Adding enum: (%s) %s\n",n.Type,n.Name) }
var tp *types.Type
a := (*Avail)(&[]AvailAttr{})
if n.Type == "" {
tp = nil
} else {
tp = types.NewTypeFromString(n.Type,"")
//fmt.Printf(" type: %s -> %s\n",n.Type,tp.CType())
if Debug { fmt.Printf(" type: %s -> %s\n",n.Type,tp.CType()) }
}
e := &Enum{
Name: n.Name, // NOTE: may be empty string
@ -491,7 +492,7 @@ func (w *Wrapper) AddEnum(n *ast.EnumDecl,rs []string) {
case *ast.AvailabilityAttr, *ast.UnavailableAttr, *ast.DeprecatedAttr:
a.Add(x)
case *ast.EnumConstantDecl:
//fmt.Printf("*ast.EnumConstantDecl: (%s) '%s': %s\n",n.Type,n.Name,x.Name)
if Debug { fmt.Printf("*ast.EnumConstantDecl: (%s) '%s': %s\n",n.Type,n.Name,x.Name) }
if n.Name == "" && !matches(x.Name,rs) {
continue
}
@ -504,12 +505,12 @@ func (w *Wrapper) AddEnum(n *ast.EnumDecl,rs []string) {
} else {
w.NamedEnums[e.Name] = e
}
//fmt.Printf(" added\n")
if Debug { fmt.Printf(" added\n") }
}
}
//Add an Interface or add a Category to an Interface
func (w *Wrapper) add(name string, ns []ast.Node) {
func (w *Wrapper) addIntCat(name string, ns []ast.Node) {
var i *Interface
var ok bool
goname := strings.Title(types.NewTypeFromString(name,name).GoType())
@ -518,11 +519,11 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
i = &Interface{ }
i.Name = name
i.GoName = goname
i.Properties = map[string]*Property{}
i.InstanceMethods = NewMethodCollection(name)
i.ClassMethods = NewMethodCollection(name)
i.Protocols = []string{}
i.ProcessedInstanceMethods = map[string]bool{}
/*
m := &Method{
Name: "class",
GoName: "Class",
@ -533,47 +534,66 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
Parameters: []*Parameter{},
}
i.ClassMethods.Methods = []*Method{m}
*/
}
//var avail bool
avail := (*Avail)(&[]AvailAttr{})
mcc := NewMethodCollection(name)
mci := NewMethodCollection(name)
prots := []string{}
for _,c := range ns {
switch x := c.(type) {
case *ast.ObjCPropertyDecl:
//fmt.Printf("ObjCPropertyDecl: %s\n",x.Name)
p := &Property{
Name: x.Name,
Type: types.NewTypeFromString(x.Type,name),
}
//FIXME: Properties are not supported, typically there
//will be setter/getter methods you can use instead
if Debug { fmt.Printf("ObjCPropertyDecl: %s\n",x.Name) }
//p := &Property{
// Name: x.Name,
// Type: types.NewTypeFromString(x.Type,name),
//}
//_,avail = w.GetParms(x,name) // TODO
//if avail {
i.Properties[p.Name] = p
// i.Properties[p.Name] = p
//}
case *ast.ObjCMethodDecl:
//fmt.Printf("ObjCMethodDecl: %s (%s) %s\n",x.Type,name,x.Name)
if Debug { fmt.Printf("ObjCMethodDecl: %s (%s) %s\n",x.Type,name,x.Name) }
if name == "NSObject" && x.Name == "initialize" {
continue
}
if x.ClassMethod {
w.AddMethod(i.ClassMethods,x)
w.AddMethod(mcc,x)
} else {
w.AddMethod(i.InstanceMethods,x)
w.AddMethod(mci,x)
}
case *ast.ObjCProtocol:
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
i.Protocols = append(i.Protocols,x.Name)
if Debug { fmt.Printf("ast.ObjCProtocol: %s\n",x.Name) }
prots = append(prots,x.Name)
case *ast.ObjCInterface:
if x.Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
if Debug { fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name) }
types.SetSuper(name,x.Name)
}
case *ast.ObjCTypeParamDecl:
//fmt.Printf("ObjCTypeParamDecl: %s = %s\n",x.Name,x.Type)
if Debug { fmt.Printf("ObjCTypeParamDecl: %s = %s\n",x.Name,x.Type) }
types.SetTypeParam(name,x.Name,x.Type)
case *ast.AvailabilityAttr, *ast.UnavailableAttr, *ast.DeprecatedAttr:
avail.Add(x)
case *ast.Unknown:
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
if Debug { fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content) }
case *ast.ObjCRootClassAttr, *ast.VisibilityAttr,
*ast.ObjCIvarDecl, *ast.ArcWeakrefUnavailableAttr,
*ast.ObjCExceptionAttr:
default:
//fmt.Println(reflect.TypeOf(x))
fmt.Printf("AST parse error: node type is %s\n",reflect.TypeOf(x).String())
}
}
if !avail.Available() {
if Debug { fmt.Printf("-- %s is not available\n",i.Name) }
return
}
i.ClassMethods.Methods = append(i.ClassMethods.Methods, mcc.Methods...)
i.InstanceMethods.Methods = append(i.InstanceMethods.Methods, mci.Methods...)
i.Protocols = append(i.Protocols, prots...)
//Add class methods from super class
var supmethods func(*Interface,string)
supmethods = func(i *Interface, s string) {
@ -615,7 +635,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
}
}
supmethods(i,types.Super(i.Name))
//fmt.Println("Add interface ",i.Name)
if Debug { fmt.Println("Add interface ",i.Name) }
Disambiguate(i.ClassMethods)
Disambiguate(i.InstanceMethods)
w.Interfaces[i.Name] = i
@ -631,12 +651,15 @@ type Avail []AvailAttr
func (a *Avail) Add(n ast.Node) {
switch x := n.(type) {
case *ast.AvailabilityAttr:
if Debug { fmt.Printf(" AvailabilityAttr: OS: %s, Version: %s, Deprecated: %t\n",x.OS,x.Version,(x.Unknown1 != "0") || x.IsUnavailable) }
if x.OS != "macos" { return }
*a = append(*a, AvailAttr{
OS: x.OS,
Version: x.Version,
Deprecated: (x.Unknown1 != "0") || x.IsUnavailable,
})
case *ast.UnavailableAttr, *ast.DeprecatedAttr:
if Debug { fmt.Printf(" DeprecatedAttr\n") }
*a = append(*a, AvailAttr{
OS: "macos", Deprecated: true })
}
@ -706,10 +729,9 @@ func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) {
func (w *Wrapper) AddTypedef(n,t string) {
tp := types.NewTypeFromString(t,"")
gt := tp.GoType()
//fmt.Printf("Typedef %s -> %s\n",n,t)
if Debug { fmt.Printf("Typedef %s -> %s\n",n,t) }
if types.ShouldWrap(gt) {
//fmt.Printf(" should wrap\n")
//fmt.Printf(" processing type for %s (%s)\n",n,gt)
if Debug { fmt.Printf(" processing type for %s (%s)\n",n,gt) }
types.Wrap(n)
types.SetSuper(n,gt)
w._processType(tp,"*" + n)
@ -731,7 +753,7 @@ func (w *Wrapper) processType(tp *types.Type) {
}
func (w *Wrapper) _processType(bt *types.Type, gt string) {
//fmt.Printf("processType: gt = %s bt = %s\n",gt,bt)
if Debug { fmt.Printf("processType: gt = %s bt = %s\n",gt,bt) }
if gt == "" {
return
}
@ -775,6 +797,10 @@ func CharWithBytes(b []byte) *Char {
func (c *Char) String() string {
return C.GoString((*C.char)(c))
}
func (c *Char) Free() {
C.free(unsafe.Pointer(c))
}
`)
}
@ -863,9 +889,7 @@ func (w *Wrapper) ProcessFunction(m *Method) {
}
func (w *Wrapper) _processMethod(m *Method,fun bool) {
if Debug {
fmt.Printf(" method: %s (%s)\n", m.Name, m.Type)
}
if Debug { fmt.Printf(" method: %s (%s)\n", m.Name, m.Type) }
if m.HasUnsupportedType() {
return
}
@ -881,7 +905,7 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
} else {
cname = gname
}
//fmt.Printf("Method %s (GoClass %s)\n",cname,m.GoClass)
if Debug { fmt.Printf("Method %s (GoClass %s)\n",cname,m.GoClass) }
switch {
case !m.ClassMethod:
if types.IsGoInterface(m.GoClass) {
@ -1004,28 +1028,38 @@ func %s%s(%s) %s {
` %s* arr = %s;
`, tps[lparm].CType(), ns[lparm]))
}
if fun {
switch {
case fun:
w.cCode.WriteString(fmt.Sprintf(` %s%s(%s);
}`, cret, m.Name, cns))
} else {
case len(m.Name) >= 5 && m.Name[:5] == "alloc" && m.Class != "NSAutoreleasePool":
if Autorelease { w.cCode.WriteString(fmt.Sprintf(` %s[[%s %s] autorelease];
}`, cret, cobj, w.objcparamlist(m))) } else {
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
}`, cret, cobj, w.objcparamlist(m)))
}
default:
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
}`, cret, cobj, w.objcparamlist(m)))
}
// create GoString helper method
if ok,_ := regexp.MatchString("WithString$",m.Name); ok {
//fmt.Printf("--%s\n",gname)
if Debug { fmt.Printf("--%s\n",gname) }
cvts := ""
gname2 := gname[:len(gname)-6] + "GoString"
gps := []string{}
i := 0
if !m.ClassMethod { i = 1 }
for ; i < len(ns); i++ {
gt := tps[i].GoType()
//fmt.Printf(" %s\n",gt)
if Debug { fmt.Printf(" %s\n",gt) }
ns2 := ns[i]
if gt == "NSString" {
gt = "string"
ns[i] = gStringToNsstring(ns[i])
//ns[i] = gStringToNsstring(ns[i])
cvts = cvts + gStringToNSString(ns[i])
ns[i] = "NSStringWithUTF8String(" + ns[i] + "_chr)"
}
gps = append(gps,ns2 + " " + gt)
}
@ -1037,19 +1071,22 @@ func %s%s(%s) %s {
}
w.goCode.WriteString(fmt.Sprintf(`
func %s%s(%s) %s {
return %s%s(%s)
%sreturn %s%s(%s)
}
`,receiver,gname2,gplist,grtype,obj,gname,strings.Join(ns,", ")))
`,receiver,gname2,gplist,grtype,cvts,obj,gname,strings.Join(ns,", ")))
}
}
func gStringToNsstring(s string) string {
return fmt.Sprintf("NSStringWithUTF8String(CharWithGoString(%s))",s)
func gStringToNSString(s string) string {
return fmt.Sprintf(
`%s_chr := CharWithGoString(%s)
defer %s_chr.Free()
`,s,s,s)
}
func (w *Wrapper) ProcessEnum(e *Enum) {
//fmt.Printf("Processing enum (%s)\n",e.Name)
if Debug { fmt.Printf("Processing enum (%s)\n",e.Name) }
w.processType(e.Type)
gtp := ""
ctp := ""
@ -1069,7 +1106,7 @@ type %s %s
}
}
gtp = gtp + " "
//fmt.Printf(" gtp = %s; ctp = %s\n",gtp,ctp)
if Debug { fmt.Printf(" gtp = %s; ctp = %s\n",gtp,ctp) }
for _,c := range e.Constants {
w.goConst.WriteString(fmt.Sprintf(`const %s %s= C.%s
`,c,gtp,c))
@ -1180,19 +1217,23 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
fmt.Printf("Failed to find interface %s for subclass %s\n",pname,dname)
os.Exit(-1)
}
//fmt.Printf(" subclass for %s\n",pname)
if Debug { fmt.Printf(" subclass for %s\n",pname) }
mc := NewMethodCollection(dname)
var addmeths func(s string)
addmeths = func(s string) {
if sup := types.Super(s); w.Interfaces[sup] != nil {
addmeths(sup)
}
//fmt.Printf("Adding methods for %s\n",s)
if Debug { fmt.Printf("Adding methods for interface %s\n",s) }
for _,m := range w.Interfaces[s].InstanceMethods.Methods {
if Debug { fmt.Printf(" -> %s\n",m.Name) }
mc.Methods = append(mc.Methods,m)
}
//mc.Methods = append(mc.Methods,w.Interfaces[s].InstanceMethods...)
for _,p := range w.Interfaces[s].Protocols {
if Debug { fmt.Printf("Adding methods for protocol %s\n",p) }
for _,m := range w.Protocols[p].InstanceMethods.Methods {
if Debug { fmt.Printf(" -> %s\n",m.Name) }
mc.Methods = append(mc.Methods,m)
}
}
@ -1207,14 +1248,15 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
fmt.Printf("Failed to find protocol %s for delegate %s\n",pname,dname)
os.Exit(-1)
}
//fmt.Printf(" proto %s\n",pname)
if Debug { fmt.Printf(" proto %s\n",pname) }
ms = proto.InstanceMethods.Methods
fmt.Printf("Protocol %s\n",pname)
if Debug { fmt.Printf("Protocol %s\n",pname) }
types.SetSuper(dname,"Id")
supr = "Id"
}
for _,m := range ms {
//note:we may have capitalized the first character to make a GoName...
if Debug { fmt.Printf("--Method: %s\n",m.Name) }
if !matches(string(m.Name[0])+m.GoName[1:],pats) {
continue
}
@ -1260,8 +1302,10 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
vnames[i][0] = "self"
getypes[i][0] = "unsafe.Pointer"
gtypes[i] = make([]string,len(m.Parameters)+1)
gtypes[i][0] = gname + "Supermethods"
//fmt.Printf("%s: %s\n",dname,m.Name)
if m.Name != "dealloc" {
gtypes[i][0] = gname + "Supermethods"
}
if Debug { fmt.Printf("%s: %s\n",dname,m.Name) }
var parms string
var cparms string
if len(m.Parameters) == 0 {
@ -1300,23 +1344,28 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
}
methprotos[i] = fmt.Sprintf(
`- (%s)%s%s;`,m.Type.Node.CType(),m.Name,parms)
ct := m.Type.Node.CType()
if i < sms {
smethprotos[i] = fmt.Sprintf(
`- (%s)super_%s%s;`,ct,m.Name,parms)
}
if ct == "instancetype" {
ct = gname + "*"
}
if i < sms {
sfunprotos[i] = fmt.Sprintf(
`%s %s_super_%s(%s);`,ct,dname,m.Name,cparms)
}
if x := m.Type.GoType(); x == "Void" {
grtypes[i] = ""
} else {
grtypes[i] = " " + x
}
ct := m.Type.Node.CType()
if ct == "instancetype" {
ct = dname + "*"
}
if ct == "id" {
ct = "void*"
grtypes[i] = " Id"
}
if i < sms {
smethprotos[i] = fmt.Sprintf(
`- (%s)super_%s%s;`,ct,m.Name,parms)
}
if i < sms {
_,cntps,_ := w.cparamlist(m)
sfunprotos[i] = fmt.Sprintf(
`%s %s_super_%s(%s);`,ct,dname,m.Name,cntps)
}
crtypes[i] = m.Type.CTypeAttrib()
if m.Type.IsPointer() {
cgtypes[i] = "unsafe.Pointer"
@ -1339,14 +1388,27 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
{ }
%s
`,dname,supcls,protos,strings.Join(methprotos,"\n")))
havesupmethods := sms > 0
if sub {
ccode.WriteString(strings.Join(smethprotos,"\n"))
for i,sp := range smethprotos {
if methods[i].Name != "dealloc" {
ccode.WriteString(sp + "\n")
} else {
if sms == 1 {
havesupmethods = false
}
}
}
}
ccode.WriteString(`
@end
`)
if sub {
ccode.WriteString(strings.Join(sfunprotos,"\n"))
for i,sf := range sfunprotos {
if methods[i].Name != "dealloc" {
ccode.WriteString(sf + "\n")
}
}
}
//2. ObjC implementation
@ -1355,10 +1417,13 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
sfundecls := make([]string,len(methods))
for i,mp := range methprotos {
mp := mp[:len(mp)-1]
var smp, sfp string
var smp, sfp, superdealloc string
if sub && i < sms {
smp = smethprotos[i][:len(smethprotos[i])-1]
sfp = sfunprotos[i][:len(sfunprotos[i])-1]
if methods[i].Name == "dealloc" {
superdealloc = "\n\t[super dealloc];"
}
}
var ret string
if crtypes[i] != "void" {
@ -1373,9 +1438,9 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
methdecls[i] = fmt.Sprintf(`
%s
{
%s%s(%s);
%s%s(%s);%s
}
`,mp,ret,gname + gnames[i],strings.Join(vnames[i],", "))
`,mp,ret,gname + gnames[i],strings.Join(vnames[i],", "),superdealloc)
if sub && i < sms {
smethdecls[i] = fmt.Sprintf(`
%s
@ -1386,7 +1451,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
sfundecls[i] = fmt.Sprintf(`
%s
{
%s[(%s*)self super_%s];
%s[(%s*)o super_%s];
}
`,sfp,ret,dname,strings.Join(vpnames[i]," "))
}
@ -1396,22 +1461,35 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Me
%s
`,dname,strings.Join(methdecls,"\n")))
if sub {
ccode.WriteString(strings.Join(smethdecls,"\n"))
for i,sm := range smethdecls {
if methods[i].Name != "dealloc" {
ccode.WriteString(sm + "\n")
}
}
}
ccode.WriteString(`
@end
`)
if sub {
ccode.WriteString(strings.Join(sfundecls,"\n"))
for i,sf := range sfundecls {
if methods[i].Name != "dealloc" {
ccode.WriteString(sf + "\n")
}
}
}
//3. ObjC constructor function
ccode.WriteString(fmt.Sprintf(`
void*
%sAlloc() {
return [[%s alloc] autorelease];
`,dname))
if Autorelease { ccode.WriteString(fmt.Sprintf(
` return [[%s alloc] autorelease];
}
`,dname,dname))
`,dname)) } else { ccode.WriteString(fmt.Sprintf(
` return [%s alloc];
}
`,dname)) }
//4. Go type
@ -1436,9 +1514,10 @@ func %sAlloc() %s {
}
dispitems[i] = fmt.Sprintf(
` %s func(%s)%s`,n,strings.Join(gtypes[i],", "),grtypes[i])
if sub && i < sms {
if sub && i < sms && methods[i].Name != "dealloc" {
sdispitems[i] = fmt.Sprintf(
` %s func(%s)%s`,n,strings.Join(gtypes[i][1:],", "),grtypes[i])
` %s func(%s)%s
`,n,strings.Join(gtypes[i][1:],", "),grtypes[i])
}
}
gocode.WriteString(fmt.Sprintf(`
@ -1448,12 +1527,12 @@ type %sDispatch struct {
var %sLookup map[unsafe.Pointer]%sDispatch =
map[unsafe.Pointer]%sDispatch{}
`,gname,strings.Join(dispitems,"\n"),gname,gname,gname))
if sub && sms > 0 {
if sub && sms > 0 && havesupmethods {
gocode.WriteString(fmt.Sprintf(`
type %sSupermethods struct {
%s
}
`,gname,strings.Join(sdispitems,"\n")))
`,gname,strings.Join(sdispitems,"")))
}
//To create (per method):
cprotos.WriteString("\n\n")
@ -1475,7 +1554,7 @@ func (d %s) %sCallback(f func(%s)%s) {
earglist := []string{"o unsafe.Pointer"}
garglist := []string{}
gargconv := []string{}
if sub && sms > 0 {
if sub && sms > 0 && m.Name != "dealloc" {
garglist = []string{"super"}
}
for j := 1; j < len(vnames[i]); j++ {
@ -1521,17 +1600,20 @@ func (d %s) %sCallback(f func(%s)%s) {
}
sdispentries := make([]string,sms)
for i,_ := range sdispentries {
sdispentries[i] = fmt.Sprintf(
` self.Super%s`,gnames[i])
if methods[i].Name != "dealloc" {
sdispentries[i] = fmt.Sprintf(
` self.Super%s,
`,gnames[i])
}
}
sper := ""
if sub && sms > 0 {
if sub && sms > 0 && m.Name != "dealloc" {
sper = fmt.Sprintf(
` self := (*%s)(o)
` self := %s{}
self.ptr = o
super := %sSupermethods{
%s,
}
`,gname,gname,strings.Join(sdispentries,",\n"))
%s }
`,gname,gname,strings.Join(sdispentries,""))
}
if len(gargconv) > 0 {
retn = "\n " + retn
@ -1548,6 +1630,7 @@ func %s%s(%s)%s {
`,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,sper,strings.Join(gargconv,"\n"),retn,strings.Join(garglist,", "),retnparen))
//4. Go wrapper functions for superclass methods
if !sub || i >= sms { continue } // for subclasses only
if m.Name == "dealloc" { continue }
grtype := m.Type.GoType()
if grtype == "Void" {
grtype = ""
@ -1575,7 +1658,7 @@ func (o %s) Super%s(%s) %s {
}
`,vn,w.Vaargs,vn,vn))
}
gocode.WriteString(` ` + types.GoToC(dname + "_super_"+m.Name,ns,snames,m.Type,tps,false) + "\n}\n")
gocode.WriteString("\t" + types.GoToC(dname + "_super_"+m.Name,ns,snames,m.Type,tps,false) + "\n}\n")
}
}
w.cCode.WriteString(cprotos.String())
@ -1622,13 +1705,15 @@ func (w *Wrapper) Wrap(toproc []string) {
if i.Name == "NSEnumerator" {
w.EnumeratorHelpers()
}
gname := i.GoName
/*gname := i.GoName
if types.IsGoInterface(i.GoName) {
gname = "Id"
}
*/
fmt.Printf("Interface %s: %d properties, %d class methods, %d instance methods\n",
i.Name, len(i.Properties), len(i.ClassMethods.Methods), len(i.InstanceMethods.Methods))
/*
w.goCode.WriteString(fmt.Sprintf(`
func %sAlloc() %s {
ret := %s{}
@ -1653,12 +1738,13 @@ void*
}
`, i.Name, i.Name))
}
*/
//FIXME: sort properties
for _,p := range i.Properties {
if Debug {
fmt.Printf(" property: %s (%s)\n", p.Name, p.Type.CType())
}
//Properties are not supported, use getter/setter
//methods instead.
if Debug { fmt.Printf(" property: %s (%s)\n", p.Name, p.Type.CType()) }
}
for _,m := range i.ClassMethods.Methods {
w.ProcessMethod(m)
@ -1682,7 +1768,7 @@ void*
}
}
for _,m := range w.Functions {
//fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name)
if Debug { fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name) }
w.ProcessFunction(m)
}
for _,e := range w.NamedEnums {