Compare commits

..

No commits in common. "d0654613ea7da36a26d61f01865c9df5c6af1631" and "50b9387a91906699997cfccce9955686f2e0f73b" have entirely different histories.

12 changed files with 111 additions and 5766 deletions

View File

@ -89,7 +89,7 @@ in Go types (as described below), except for methods that contain unsupported
return types or paramater types such as blocks and function pointers.
```go
s1 := ns.NSStringAlloc() // allocate an instance of NSString
s1 := ns.NSStringAlloc() // allocate and autorelease an instance of NSString
s2 := ns.NSStringWithSting(s1) // call a class method of NSString
class := ns.NSStringClass() // class method returning the class of NSString
fmt.Println(s2.UTF8String()) // call UTF8String, an NSString instance method
@ -437,9 +437,8 @@ which seem to work but have not been extensively tested.
Since everything inherits methods from `NSObject`, you can call `Retain()`,
`Release()` and `Autorelease()` on any object.
If the autorelease configuration directive is set to "true", all allocation functions
created by NSWrap (i.e. those ending in `Alloc`) will call `autorelease` before they
return an object. Alternately, objects can be manually sent an autorelease message.
All allocation functions created by NSWrap (i.e. those ending in `Alloc`)
call `autorelease` before they return an object.
If you are not working in an environment (such as an
Application Delegate callback) that provides an autorelease pool, you can
create your own:
@ -449,9 +448,7 @@ create your own:
```go
swamp := ns.NSAutoreleasePoolAlloc().Init()
del := ns.AppDelegateAlloc()
//del.Autorelease() // if autorelease: true is not set in nswrap.yaml
menu := ns.NSMenuAlloc().InitWithTitle(nst("Main"))
//menu.Autorelease()
str := ns.NSStringWithGoString("these objects will be automatically deallocated when swamp is drained.")
...
swamp.Drain()
@ -476,19 +473,8 @@ You will need to make sure `NSAutoreleasePool` is included in the `classes`
directive in your configuration file before working with
`NSAutoreleasePool` objects or the `AutoreleasePool` helper function.
* Pitfalls
Go concurrency does not play well with Objective-C memory management. In particular,
an AutoreleasePool needs to be allocated and drained from the same thread, and
only objects allocated within that thread will be drained. Objects allocated and
autoreleased from a different goroutine in the same thread are at risk of being
prematurely drained. Therefore, you should only work with one AutoreleasePool at
a time, and only within a thread that is locked to the OS thread
(by calling `runtime.LockOSThread()`). If you will be allocating Objective-C objects
from multiple goroutines, it is best not to use the `autorelease: true` directive
as that will cause all objects to receive an `autorelease` message even if they are
created outside the thread where are using your autorelease pool.
See `examples/memory` for some basic tests and read the comments to larn what to avoid.
Memory management seems to work but there ought to be a comprehensive
tests before anyone should feel confident with it.
## Delegates

View File

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

View File

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

View File

@ -1,175 +0,0 @@
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 { }
}

Binary file not shown.

View File

@ -1,36 +0,0 @@
package ns
/*
#cgo CFLAGS: -x objective-c -fno-objc-arc
#cgo LDFLAGS: -framework Foundation
#pragma clang diagnostic ignored "-Wformat-security"
#import <Foundation/Foundation.h>
*/
import "C"
import (
"unsafe"
)
//export 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)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
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" ]

Binary file not shown.

View File

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