Compare commits

..

2 Commits

Author SHA1 Message Date
d0654613ea Documentation updates. 2019-06-06 00:30:21 -04:00
90166a4379 Bug fixes. Read availability attributes when adding interfaces and
categories. Memory leak fixes. Add "autorelease" confiruation directive.
Fix parameter types for subclass method overriding.
2019-06-06 00:22:07 -04:00
12 changed files with 5769 additions and 114 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. return types or paramater types such as blocks and function pointers.
```go ```go
s1 := ns.NSStringAlloc() // allocate and autorelease an instance of NSString s1 := ns.NSStringAlloc() // allocate an instance of NSString
s2 := ns.NSStringWithSting(s1) // call a class method of NSString s2 := ns.NSStringWithSting(s1) // call a class method of NSString
class := ns.NSStringClass() // class method returning the class of NSString class := ns.NSStringClass() // class method returning the class of NSString
fmt.Println(s2.UTF8String()) // call UTF8String, an NSString instance method fmt.Println(s2.UTF8String()) // call UTF8String, an NSString instance method
@ -437,8 +437,9 @@ which seem to work but have not been extensively tested.
Since everything inherits methods from `NSObject`, you can call `Retain()`, Since everything inherits methods from `NSObject`, you can call `Retain()`,
`Release()` and `Autorelease()` on any object. `Release()` and `Autorelease()` on any object.
All allocation functions created by NSWrap (i.e. those ending in `Alloc`) If the autorelease configuration directive is set to "true", all allocation functions
call `autorelease` before they return an object. 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.
If you are not working in an environment (such as an If you are not working in an environment (such as an
Application Delegate callback) that provides an autorelease pool, you can Application Delegate callback) that provides an autorelease pool, you can
create your own: create your own:
@ -448,7 +449,9 @@ create your own:
```go ```go
swamp := ns.NSAutoreleasePoolAlloc().Init() swamp := ns.NSAutoreleasePoolAlloc().Init()
del := ns.AppDelegateAlloc() del := ns.AppDelegateAlloc()
//del.Autorelease() // if autorelease: true is not set in nswrap.yaml
menu := ns.NSMenuAlloc().InitWithTitle(nst("Main")) menu := ns.NSMenuAlloc().InitWithTitle(nst("Main"))
//menu.Autorelease()
str := ns.NSStringWithGoString("these objects will be automatically deallocated when swamp is drained.") str := ns.NSStringWithGoString("these objects will be automatically deallocated when swamp is drained.")
... ...
swamp.Drain() swamp.Drain()
@ -473,8 +476,19 @@ You will need to make sure `NSAutoreleasePool` is included in the `classes`
directive in your configuration file before working with directive in your configuration file before working with
`NSAutoreleasePool` objects or the `AutoreleasePool` helper function. `NSAutoreleasePool` objects or the `AutoreleasePool` helper function.
Memory management seems to work but there ought to be a comprehensive * Pitfalls
tests before anyone should feel confident with it.
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.
## Delegates ## Delegates

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 {