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 ] frameworks: [ Foundation, AppKit ]
pragma: [ clang diagnostic ignored "-Wformat-security" ] pragma: [ clang diagnostic ignored "-Wformat-security" ]
autorelease: true

View File

@ -38,3 +38,4 @@ delegates:
- peripheralDidUpdateValueForCharacteristic - peripheralDidUpdateValueForCharacteristic
pragma: [ clang diagnostic ignored "-Wformat-security" ] 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 Vaargs int
//Arc flag for debugging only, builds will break //Arc flag for debugging only, builds will break
Arc bool Arc bool
Autorelease bool
} }
var Config conf var Config conf
@ -250,6 +251,9 @@ func Start() (err error) {
if Config.Arc { if Config.Arc {
wrap.Arc = true wrap.Arc = true
} }
if Config.Autorelease {
wrap.Autorelease = true
}
//NOTE: converting in parallel is slower on my system //NOTE: converting in parallel is slower on my system
//nodes := convertLinesToNodesParallel(lines) //nodes := convertLinesToNodesParallel(lines)
nodes := convertLinesToNodes(lines) nodes := convertLinesToNodes(lines)

View File

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

View File

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