Change Go types to plain structs instead of pointers to structs.

Add methods for protocols that Objective-C interfaces claim to
implement. Separate class methods from instance methods in
internal data structures. Add layout constraints with visual
formats to examples/app. Fix typedefs for wrapped objective-c
object types.
This commit is contained in:
Greg 2019-05-23 16:31:47 -04:00
parent 4f6930002d
commit a616761475
10 changed files with 355 additions and 220 deletions

View File

@ -13,7 +13,6 @@ import (
"gopkg.in/yaml.v2"
"gitlab.wow.st/gmp/nswrap/ast"
"gitlab.wow.st/gmp/nswrap/types"
"gitlab.wow.st/gmp/nswrap/wrap"
)
@ -226,19 +225,13 @@ func Start() (err error) {
case *ast.ObjCCategoryDecl:
w.AddCategory(x)
case *ast.TypedefDecl:
types.AddTypedef(x.Name,x.Type)
w.AddTypedef(x.Name,x.Type)
case *ast.FunctionDecl:
if matches(x.Name,Config.Functions) {
w.AddFunction(x)
}
case *ast.ObjCProtocolDecl:
for _,ds := range Config.Delegates {
for ps,_ := range ds {
if matches(x.Name,[]string{ps}) {
w.AddProtocol(x)
}
}
}
w.AddProtocol(x)
case *ast.EnumDecl:
w.AddEnum(x,Config.Enums)
}

View File

@ -7,57 +7,24 @@ import (
"gitlab.wow.st/gmp/nswrap/examples/app/ns"
)
//Shortcut for literal NSStrings
var nst = ns.NSStringWithGoString
func didFinishLaunching(n *ns.NSNotification) {
func didFinishLaunching(n ns.NSNotification) {
fmt.Println("Go: did finish launching")
}
func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL {
return 1
}
func willTerminate(n *ns.NSNotification) {
fmt.Println("Go: will terminate")
}
func didBecomeActive(n *ns.NSNotification) {
fmt.Println("Go: did become active")
vc := win.ContentViewController()
if vc == nil {
fmt.Println("vc == nil")
} else {
fmt.Println("vc is not nil")
}
}
var (
win *ns.NSWindow
)
func app() {
//Lock OS thread because Cocoa uses thread-local storage
runtime.LockOSThread()
a := ns.NSApplicationSharedApplication()
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
//Set up an AppDelegate
del := ns.AppDelegateAlloc()
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
del.ApplicationWillTerminateCallback(willTerminate)
del.ApplicationDidBecomeActiveCallback(didBecomeActive)
a.SetDelegate(del)
fmt.Printf("Notification: %s\n",n.Name().UTF8String())
//Set up an NSWindow
win = ns.NSWindowAlloc().InitWithContentRect(
ns.NSMakeRect(200,200,600,600),
ns.NSWindowStyleMaskTitled | ns.NSWindowStyleMaskClosable |
ns.NSWindowStyleMaskResizable,
ns.NSWindowStyleMaskResizable,
ns.NSBackingStoreBuffered,
0,
nil,
ns.NSScreen{},
)
// retain win since we called Alloc and did not add it to a collection
win.Retain()
win.SetTitle(nst("Hi World"))
win.MakeKeyAndOrderFront(win)
win.SetAlphaValue(0.85)
@ -83,6 +50,67 @@ func app() {
a.SetMainMenu(m1)
//add some buttons and do some layout
//don't do this:
//b := ns.NSButtonAlloc().InitWithFrame(ns.NSMakeRect(100,100,100,50))
b1 := ns.NSButtonWithTitle(nst("PUSH"),ns.Id{},ns.Selector(""))
b2 := ns.NSButtonWithTitle(nst("QUIT"),ns.Id{},ns.Selector("terminate:"))
b1.SetTranslatesAutoresizingMaskIntoConstraints(0)
b2.SetTranslatesAutoresizingMaskIntoConstraints(0)
cv := win.ContentView()
cv.AddSubview(b1.NSView,ns.NSWindowAbove,ns.NSView{})
cv.AddSubview(b2.NSView,ns.NSWindowAbove,ns.NSView{})
viewmap := ns.NSDictionaryWithObjects(
ns.NSArrayWithObjects(b1,b2),
ns.NSArrayWithObjects(nst("b1"),nst("b2")))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("V:|-[b1]"),0, ns.NSDictionary{}, viewmap))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("H:|-[b1]"),0, ns.NSDictionary{}, viewmap))
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("H:[b1]-[b2]"),ns.NSLayoutFormatAlignAllBaseline,
ns.NSDictionary{}, viewmap))
}
func shouldTerminateAfterLastWindowClosed(s ns.NSApplication) ns.BOOL {
return 1
}
func willTerminate(n ns.NSNotification) {
fmt.Println("Go: will terminate")
}
func didBecomeActive(n ns.NSNotification) {
fmt.Println("Go: did become active")
fmt.Printf("Notification: %s\n",n.Name().UTF8String())
}
var (
a ns.NSApplication
win ns.NSWindow
)
func app() {
//Lock OS thread because Cocoa uses thread-local storage
runtime.LockOSThread()
a = ns.NSApplicationSharedApplication()
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
//Set up an AppDelegate
del := ns.AppDelegateAlloc()
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
del.ApplicationWillTerminateCallback(willTerminate)
del.ApplicationDidBecomeActiveCallback(didBecomeActive)
a.SetDelegate(del)
//Run the app
a.Run()
}

View File

@ -1,3 +1,4 @@
# nswrap.yaml
inputfiles:
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
- /System/Library/Frameworks/AppKit.framework/Headers/AppKit.h
@ -17,17 +18,17 @@ classes:
- NSLayoutConstraint
- NSDictionary
- NSArray
- NSColor
- NSObject
functions: [NSMake.*]
enums:
- CF.*
- NSApplication.*
- NSBackingStore.*
- NSWindowStyleMask.*
- NSWindowButton
- NSWindowOrderingMode
- NSLayout.*
delegates:
AppDelegate:
@ -36,15 +37,7 @@ delegates:
- applicationDidFinishLaunching
- applicationShouldTerminateAfterLastWindowClosed
- applicationDidBecomeActive
subclasses:
ViewController:
NSViewController:
- viewDidLoad
- viewDidDisappear
- loadView
frameworks: [ Foundation, AppKit, CoreGraphics ]
frameworks: [ Foundation, AppKit ]
pragma: [ clang diagnostic ignored "-Wformat-security" ]
vaargs: 32

View File

@ -8,7 +8,7 @@ import (
"gitlab.wow.st/gmp/nswrap/examples/bluetooth/ns"
)
func updateState(c *ns.CBCentralManager) {
func updateState(c ns.CBCentralManager) {
fmt.Printf("Go: did update state\n")
switch cm.CBManager.State() {
case ns.CBManagerStateUnknown:
@ -23,44 +23,44 @@ func updateState(c *ns.CBCentralManager) {
fmt.Printf(" powered off\n")
case ns.CBManagerStatePoweredOn:
fmt.Printf(" powered on\n")
cm.ScanForPeripheralsWithServices(ns.NSArrayWithObjects(hrm_uuid),nil)
cm.ScanForPeripheralsWithServices(ns.NSArrayWithObjects(hrm_uuid),ns.NSDictionary{})
}
}
func discoverPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral, d *ns.NSDictionary, rssi *ns.NSNumber) {
func discoverPeripheral(c ns.CBCentralManager, p ns.CBPeripheral, d ns.NSDictionary, rssi ns.NSNumber) {
fmt.Printf("Did discover peripheral\n")
c.StopScan()
if peripheral != nil {
if peripheral.Ptr() != nil {
peripheral.Release()
}
peripheral = p
peripheral.Retain()
c.ConnectPeripheral(peripheral,nil)
c.ConnectPeripheral(peripheral,ns.NSDictionary{})
}
func connectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) {
func connectPeripheral(c ns.CBCentralManager, p ns.CBPeripheral) {
fmt.Printf("Did connect peripheral\n")
p.SetDelegate(cd)
p.DiscoverServices(nil)
p.DiscoverServices(ns.NSArray{})
}
func discoverServices(p *ns.CBPeripheral, e *ns.NSError) {
func discoverServices(p ns.CBPeripheral, e ns.NSError) {
fmt.Printf("Did discover services\n")
p.Services().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
p.Services().ObjectEnumerator().ForIn(func(o ns.Id) bool {
serv := o.CBService()
switch {
case serv.UUID().IsEqualTo(hrm_uuid):
fmt.Printf("--heart rate monitor service\n")
p.DiscoverCharacteristics(nil,serv)
p.DiscoverCharacteristics(ns.NSArray{},serv)
case serv.UUID().IsEqualTo(ns.CBUUIDWithGoString("180A")):
fmt.Printf("--device information service\n")
p.DiscoverCharacteristics(nil,serv)
p.DiscoverCharacteristics(ns.NSArray{},serv)
}
return true
})
}
func hr(d *ns.NSData) int {
func hr(d ns.NSData) int {
if l := int(d.Length()); l < 4 {
return 0
}
@ -73,11 +73,11 @@ func hr(d *ns.NSData) int {
}
}
func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) {
func discoverCharacteristics(p ns.CBPeripheral, s ns.CBService, e ns.NSError) {
fmt.Printf("Did discover characteristics\n")
fmt.Printf("----%s\n",s.UUID().UUIDString().UTF8String())
if s.UUID().IsEqualTo(hrm_uuid) {
s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
s.Characteristics().ObjectEnumerator().ForIn(func(o ns.Id) bool {
chr := o.CBCharacteristic()
fmt.Printf("------%s\n",chr.UUID().UUIDString().UTF8String())
if chr.UUID().IsEqualTo(hrv_uuid) {
@ -90,7 +90,7 @@ func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError)
}
}
func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) {
func updateValue(p ns.CBPeripheral, chr ns.CBCharacteristic, e ns.NSError) {
if chr.UUID().IsEqualTo(hrv_uuid) {
v := chr.Value()
fmt.Printf("Heart rate: %d\n",hr(v))
@ -98,17 +98,17 @@ func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) {
}
var (
hrm_uuid *ns.CBUUID
hrv_uuid *ns.CBUUID
cd *ns.BleDelegate
cm *ns.CBCentralManager
peripheral *ns.CBPeripheral
hrm_uuid ns.CBUUID
hrv_uuid ns.CBUUID
cd ns.CBDelegate
cm ns.CBCentralManager
peripheral ns.CBPeripheral
)
func main() {
queue := ns.DispatchQueueCreate(ns.CharWithGoString("st.wow.gitlab.ble"),nil)
cd = ns.BleDelegateAlloc()
cd = ns.CBDelegateAlloc()
cd.CentralManagerDidUpdateStateCallback(updateState)
cd.CentralManagerDidDiscoverPeripheralCallback(discoverPeripheral)
@ -120,8 +120,7 @@ func main() {
hrm_uuid = ns.CBUUIDWithGoString("180D")
hrv_uuid = ns.CBUUIDWithGoString("2A37")
cm = ns.CBCentralManagerAlloc()
cm.InitWithDelegate(cd,queue,nil)
cm = ns.CBCentralManagerAlloc().InitWithDelegate(cd,queue,ns.NSDictionary{})
select { }
}

View File

@ -27,7 +27,7 @@ functions: [ NSMakeRange, dispatch_queue_create ]
enums: [ CB.* ]
frameworks: [ Foundation, CoreBluetooth ]
delegates:
BleDelegate:
CBDelegate:
CBCentralManagerDelegate:
- centralManagerDidUpdateState
- centralManagerDidDiscoverPeripheral

View File

@ -27,9 +27,35 @@ func main() {
a2 := a.SubarrayWithRange(ns.NSMakeRange(1,3))
fmt.Println("Length(a2) = ",a2.Count())
i1 := a.ObjectAtIndex(1).NSString()
fmt.Println(i1.UTF8String())
a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
fmt.Printf("i1 = %@\n",i1)
fmt.Printf("i1.Ptr() = %p\n",i1.Ptr())
a.ObjectEnumerator().ForIn(func(o ns.Id) bool {
fmt.Println(o.NSString().UTF8String())
return true
})
s1 := ns.NSSetWithObjects(n1,n2)
a = ns.NSMutableArrayWithObjects(n1,s1)
a.ObjectEnumerator().ForIn(func(o ns.Id) bool {
fmt.Printf("%s -- ",o.ClassName().UTF8String())
switch {
case o.IsKindOfClass(ns.NSStringClass()):
fmt.Printf("It's a string\n")
case o.IsKindOfClass(ns.NSSetClass()):
fmt.Printf("It's a set\n")
default:
fmt.Printf("I don't know what it is!\n")
}
return true
})
a2 = ns.NSArrayWithObjects(n1,n2,n3,s1)
a2.ObjectEnumerator().ForIn(func (o ns.Id) bool {
switch {
case o.IsKindOfClass(ns.NSStringClass()):
fmt.Println(o.NSString().UTF8String())
return true // continue enumeration
default:
fmt.Println("Unknown class")
return false // terminate enumeration
}
})
}

View File

@ -15,6 +15,7 @@ classes:
- NSString
- NSScanner
- NSFileManager
- NSObject
functions: [ NSMakeRange ]
enums: [ P_ALL, CF.* ]
frameworks: [ Foundation ]

View File

@ -6,6 +6,6 @@ classes:
subclasses:
ClassThree:
ClassTwo:
- hi.*
- .*
imports: [ simple.h ]
frameworks: [ Foundation ]

View File

@ -13,12 +13,11 @@ var super map[string]string
//go struct.
var wrapped map[string]bool
func shouldWrap(gt string) bool {
return gt != "" && gt[0] == '*' && wrapped[gt[1:]]
func ShouldWrap(gt string) bool {
return wrapped[gt]
}
//goInterfaces records the names of top level Go interfaces. Pointers to these
//are dereferenced to bare interface names.
//goInterfaces records the names of top level Go interfaces.
var goInterfaces map[string]bool
func IsGoInterface(gt string) bool {
@ -69,9 +68,9 @@ func SetTypeParam(c, n, t string) {
TypeParameters[c][n] = t
}
func AddTypedef(n,t string) {
func AddTypedef(n string, tp *Type) {
//fmt.Printf("AddTypedef(): %s -> %s\n",n,t)
typedefs[n] = NewTypeFromString(t,"")
typedefs[n] = tp
}
type Type struct {
@ -189,11 +188,11 @@ func _goType(ct string) string {
ct = strings.Title(ct)
ct = strings.ReplaceAll(ct," ","")
ct = strings.ReplaceAll(ct,"Struct","")
if len(ct) > 0 && ct[0] == '*' && IsGoInterface(ct[1:]) {
return ct[1:]
if IsGoInterface(ct) {
return ct
}
if ct == "Id" {
ct = "*Id"
ct = "Id"
}
if len(ct) > 4 && ct[len(ct)-4:len(ct)] == "Void" {
ct = ct[:len(ct)-5] + "unsafe.Pointer"
@ -255,10 +254,6 @@ type %s %s
func (t *Type) GoInterfaceDecl() string {
ct := t.CType()
gt := t.GoType()
if gt[0] == '*' {
gt = gt[1:] // dereference wrapped types
ct = ct[:len(ct)-1]
}
super := Super(ct)
if super == "" {
goInterfaces[gt] = true
@ -273,8 +268,12 @@ type %s interface {
}
return fmt.Sprintf(`
type %s struct { %s }
func (o *%s) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
func (o *Id) %s() *%s { return (*%s)(unsafe.Pointer(o)) }
func (o %s) Ptr() unsafe.Pointer { return o.ptr }
func (o Id) %s() %s {
ret := %s{}
ret.ptr = o.ptr
return ret
}
`,gt,super,gt,gt,gt,gt)
}
@ -332,19 +331,26 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type, fun bool)
}
var ret strings.Builder
rt := rtype.CType()
rtgt := rtype.GoType()
if IsGoInterface(rtgt) {
rtgt = "Id"
}
sw := ShouldWrap(rtgt) || rtgt == "Id"
if rt != "void" {
rtgt := rtype.GoType()
if IsGoInterface(rtgt) {
rtgt = "*Id"
}
if rtgt == "BOOL" {
ret.WriteString("return (")
rtgt = "bool"
if sw {
ret.WriteString(fmt.Sprintf(
`ret := %s{}
ret.ptr = `,rtgt))
} else {
ret.WriteString("return (" + rtgt + ")(")
}
if rtype.IsPointer() {
ret.WriteString("unsafe.Pointer(")
if rtgt == "BOOL" {
ret.WriteString("return (")
rtgt = "bool"
} else {
ret.WriteString("return (" + rtgt + ")(")
}
if rtype.IsPointer() {
ret.WriteString("unsafe.Pointer(")
}
}
}
ret.WriteString("C." + name + "(")
@ -352,7 +358,7 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type, fun bool)
for i := 0; i < len(pnames); i++ {
pn,pt := pnames[i],ptypes[i]
p := pn
if (shouldWrap(pt.GoType()) || IsGoInterface(pt.GoType())) && !pt.Variadic {
if (ShouldWrap(pt.GoType()) || IsGoInterface(pt.GoType())) && !pt.Variadic {
p = pn + ".Ptr()"
} else {
switch {
@ -368,7 +374,7 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type, fun bool)
}
ret.WriteString(strings.Join(parms,", "))
ret.WriteString(")")
if rt != "void" {
if rt != "void" && !sw {
ret.WriteString(")")
if rtype.IsPointer() {
ret.WriteString(")")
@ -377,6 +383,10 @@ func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type, fun bool)
if rt == "BOOL" {
ret.WriteString(" != 0")
}
if sw {
ret.WriteString(`
return ret`)
}
return ret.String()
}

View File

@ -34,7 +34,9 @@ type Wrapper struct {
goCode strings.Builder // put Go code here
goExports strings.Builder // put exported Go functions here
goHelpers strings.Builder // put Go helper functions here
Processed map[string]bool
ProcessedTypes map[string]bool
ProcessedClassMethods map[string]bool
Vaargs int
}
@ -47,15 +49,18 @@ func NewWrapper(debug bool) *Wrapper {
NamedEnums: map[string]*Enum{},
AnonEnums: []*Enum{},
Protocols: map[string]*Protocol{},
Processed: map[string]bool{},
ProcessedTypes: map[string]bool{},
ProcessedClassMethods: map[string]bool{},
Vaargs: 16,
}
ret.cgoFlags.WriteString(fmt.Sprintf(`/*
#cgo CFLAGS: -x objective-c
`))
ret.goTypes.WriteString(`
type Id struct { }
func (o *Id) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
type Id struct {
ptr unsafe.Pointer
}
func (o Id) Ptr() unsafe.Pointer { return o.ptr }
`)
return ret
}
@ -121,8 +126,10 @@ type Enum struct {
type Protocol struct {
Name, GoName string
Methods map[string]*Method
Polymorphic map[string]int
ClassMethods map[string]*Method
InstanceMethods map[string]*Method
CPolymorphic map[string]int // polymorphic class methods
IPolymorphic map[string]int // polymorphic instance methods
}
//isVoid() returns true if the method has no return value.
@ -149,7 +156,8 @@ func (w Wrapper) cparamlist(m *Method) (string,string,string) {
}
for _,p := range m.Parameters {
var tp string
if p.Type.IsPointer() || p.Type.Variadic {
wp := types.ShouldWrap(p.Type.GoType())
if wp || p.Type.IsPointer() || p.Type.Variadic {
tp = "void*"
} else {
tp = p.Type.CType()
@ -231,7 +239,10 @@ func (w *Wrapper) gpntp(m *Method) ([]string,[]*types.Type,string) {
type Interface struct {
Name, GoName string
Properties map[string]*Property
Methods map[string]*Method
ClassMethods map[string]*Method
InstanceMethods map[string]*Method
Protocols []string // Protocols impelemented by this Interface
ProcessedInstanceMethods map[string]bool
}
func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) {
@ -292,14 +303,17 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
p := w.Protocols[n.Name]
if p == nil {
fmt.Printf("Adding protocol %s\n",n.Name)
//fmt.Printf("Adding protocol %s\n",n.Name)
p = &Protocol{
Name: n.Name,
GoName: types.NewTypeFromString(n.Name,n.Name).GoType(),
Methods: map[string]*Method{},
Polymorphic: map[string]int{},
ClassMethods: map[string]*Method{},
InstanceMethods: map[string]*Method{},
CPolymorphic: map[string]int{},
IPolymorphic: map[string]int{},
}
}
//fmt.Printf("Protocol %s\n",p.Name)
for _,c := range n.Children() {
switch x := c.(type) {
case *ast.ObjCMethodDecl:
@ -310,22 +324,32 @@ func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
GoClass: p.GoName,
ClassMethod: x.ClassMethod,
}
//fmt.Printf(" -- Method %s\n",m.Name)
var avail bool
m.Parameters, avail = w.GetParms(x,p.Name)
if avail {
var mth map[string]*Method
var poly map[string]int
if m.ClassMethod {
mth = p.ClassMethods
poly = p.CPolymorphic
} else {
mth = p.InstanceMethods
poly = p.IPolymorphic
}
pname := strings.Title(m.Name)
if x := p.Polymorphic[m.Name]; x != 0 {
p.Polymorphic[m.Name] = x + 1
if x := poly[m.Name]; x != 0 && len(m.Parameters) > 1 {
poly[m.Name] = x + 1
pname = pname + strings.Title(m.Parameters[1].Pname)
if m2 := p.Methods[pname]; m2 != nil {
if m2 := mth[pname]; m2 != nil {
pname2 := pname + strings.Title(m2.Parameters[1].Pname)
p.Methods[pname2] = m2
delete(p.Methods,m.Name)
mth[pname2] = m2
delete(mth,m.Name)
}
} else {
p.Polymorphic[m.Name] = 1
poly[m.Name] = 1
}
p.Methods[pname] = m
mth[pname] = m
}
}
}
@ -382,6 +406,7 @@ func (w *Wrapper) AddEnum(n *ast.EnumDecl,rs []string) {
}
}
//Add an Interface
func (w *Wrapper) add(name string, ns []ast.Node) {
var i *Interface
var ok bool
@ -392,8 +417,19 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
Name: name,
GoName: goname,
Properties: map[string]*Property{},
Methods: map[string]*Method{},
InstanceMethods: map[string]*Method{},
Protocols: []string{},
ProcessedInstanceMethods: map[string]bool{},
}
m := &Method{
Name: "class",
Class: i.Name,
GoClass: i.Name,
Type: types.NewTypeFromString("Class",i.Name),
ClassMethod: true,
Parameters: []*Parameter{},
}
i.ClassMethods = map[string]*Method{"class": m}
}
var avail bool
for _,c := range ns {
@ -423,10 +459,15 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
//fmt.Println(m.Type.Node.String())
m.Parameters, avail = w.GetParms(x,name)
if avail {
i.Methods[strings.Title(m.Name)] = m
if m.ClassMethod {
i.ClassMethods[strings.Title(m.Name)] = m
} else {
i.InstanceMethods[strings.Title(m.Name)] = m
}
}
case *ast.ObjCProtocol:
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
i.Protocols = append(i.Protocols,x.Name)
case *ast.ObjCInterface:
if x.Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
@ -447,10 +488,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
if sup,ok := w.Interfaces[s]; !ok {
return
} else {
for _,m := range sup.Methods {
if !m.ClassMethod {
continue
}
for _,m := range sup.ClassMethods {
m2 := &Method{
Name: m.Name,
Class: i.Name,
@ -467,7 +505,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
}
m2.Parameters = append(m2.Parameters,p2)
}
i.Methods[strings.Title(m.Name)] = m2
i.ClassMethods[strings.Title(m.Name)] = m2
}
}
supmethods(i,types.Super(s))
@ -552,6 +590,21 @@ func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) {
return ret, true
}
func (w *Wrapper) AddTypedef(n,t string) {
tp := types.NewTypeFromString(t,"")
gt := tp.GoType()
//fmt.Printf("Typedef %s -> %s\n",n,t)
if types.ShouldWrap(gt) {
//fmt.Printf(" should wrap\n")
//fmt.Printf(" processing type for %s (%s)\n",n,gt)
types.Wrap(n)
types.SetSuper(n,gt)
w._processType(tp,"*" + n)
} else {
types.AddTypedef(n,tp)
}
}
func (w *Wrapper) processTypes(tps []*types.Type) {
for _,tp := range tps {
w.processType(tp)
@ -561,6 +614,11 @@ func (w *Wrapper) processTypes(tps []*types.Type) {
func (w *Wrapper) processType(tp *types.Type) {
bt := tp.BaseType()
gt := bt.GoType()
w._processType(bt,gt)
}
func (w *Wrapper) _processType(bt *types.Type, gt string) {
//fmt.Printf("processType: gt = %s bt = %s\n",gt,bt)
if gt == "" {
return
}
@ -568,11 +626,8 @@ func (w *Wrapper) processType(tp *types.Type) {
w.processType(bt.PointsTo())
return
}
if w.Processed[gt] { return }
w.Processed[gt] = true
if gt == "NSObject" {
w.ObjectHelpers()
}
if w.ProcessedTypes[gt] { return }
w.ProcessedTypes[gt] = true
if gt == "Char" {
w.CharHelpers()
}
@ -597,41 +652,13 @@ func (w *Wrapper) processType(tp *types.Type) {
w.goTypes.WriteString(bt.GoTypeDecl())
}
func (w *Wrapper) ObjectHelpers() {
w.goHelpers.WriteString(`
func (o *Id) Retain() {
C.retain(unsafe.Pointer(o))
}
func (o *Id) Release() {
C.release(unsafe.Pointer(o))
}
func (o *Id) Autorelease() {
C.autorelease(unsafe.Pointer(o))
}
`)
w.cCode.WriteString(`
void
retain(void* obj) {
[(NSObject*)obj retain];
}
void
release(void* obj) {
[(NSObject*)obj release];
}
void
autorelease(void* obj) {
[(NSObject*)obj autorelease];
}
`)
}
func (w *Wrapper) CharHelpers() {
w.goHelpers.WriteString(`
func CharWithGoString(s string) *Char {
return (*Char)(unsafe.Pointer(C.CString(s)))
}
func CharFromBytes(b []byte) *Char {
func CharWithBytes(b []byte) *Char {
return (*Char)(unsafe.Pointer(C.CString(string(b))))
}
@ -643,8 +670,8 @@ func (c *Char) String() string {
func (w *Wrapper) EnumeratorHelpers() {
w.goHelpers.WriteString(`
func (e *NSEnumerator) ForIn(f func(*Id) bool) {
for o := e.NextObject(); o != nil; o = e.NextObject() {
func (e NSEnumerator) ForIn(f func(Id) bool) {
for o := e.NextObject(); o.Ptr() != nil; o = e.NextObject() {
if !f(o) { break }
}
}
@ -660,8 +687,10 @@ NSAutoreleasePool_init(void* o) {
}
`)
w.goHelpers.WriteString(`
func (o *NSAutoreleasePool) Init() *NSAutoreleasePool {
return (*NSAutoreleasePool)(unsafe.Pointer(C.NSAutoreleasePool_init(o.Ptr())))
func (o NSAutoreleasePool) Init() NSAutoreleasePool {
ret := NSAutoreleasePool{}
ret.ptr = C.NSAutoreleasePool_init(o.Ptr())
return ret
}
func Autoreleasepool(f func()) {
@ -690,6 +719,23 @@ func (w *Wrapper) ProcessMethod(m *Method) {
w._processMethod(m,false)
}
func (w *Wrapper) ProcessMethodForClass(m *Method, class string) {
goclass := strings.Title(types.NewTypeFromString(class,class).GoType())
m2 := &Method{
Name: m.Name, Class: class, GoClass: goclass,
Type: m.Type.CloneToClass(class),
ClassMethod: m.ClassMethod,
Parameters: make([]*Parameter,len(m.Parameters)),
}
for i,p := range m.Parameters {
m2.Parameters[i] = &Parameter{
Pname: p.Pname, Vname: p.Vname,
Type: p.Type.CloneToClass(class),
}
}
w._processMethod(m2,false)
}
func (w *Wrapper) ProcessFunction(m *Method) {
if m.Type.Node.IsId() {
//do not wrap functions that return ID because of CGo struct size bug
@ -712,15 +758,16 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
gname = strings.Title(gname)
gname = strings.ReplaceAll(gname," ","")
receiver := ""
cname := m.Name
switch {
case !m.ClassMethod:
if types.IsGoInterface(m.GoClass) {
receiver = "(o *Id) "
receiver = "(o Id) "
} else {
receiver = "(o *" + m.GoClass + ") "
receiver = "(o " + m.GoClass + ") "
}
case m.Type.GoType() != "*" + m.GoClass:
gname = m.GoClass + gname
//Disambiguate instance methods with same name as a class method
cname = "inst_" + cname
default:
//Shorten class method names
lens1 := len(m.Class)
@ -735,12 +782,11 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
gname = m.GoClass + gname[lens1-i:]
}
}
cname := m.Name
if m.Class != "" {
cname = m.Class + "_" + cname
}
var cmtype string
if m.Type.IsPointer() {
if m.Type.IsPointer() || types.ShouldWrap(m.Type.GoType()) {
// work around cgo bugs with struct size calculation
cmtype = "void*"
if x := m.Type.Node.Qualifiers(); x != "" {
@ -758,7 +804,7 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
grtype = ""
}
if types.IsGoInterface(grtype) {
grtype = "*Id"
grtype = "Id"
}
if grtype == "BOOL" { // convert objective-c bools to Go bools
grtype = "bool"
@ -766,6 +812,23 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
if gname == grtype { // avoid name conflicts between methods and types
gname = "Get" + gname
}
if m.ClassMethod {
if w.ProcessedClassMethods[gname] {
return
}
w.ProcessedClassMethods[gname] = true
} else {
i, ok := w.Interfaces[m.Class]
if !ok {
fmt.Printf("Can't find interface %s for method %s\n",m.Class,m.Name)
os.Exit(-1)
}
if i.ProcessedInstanceMethods[gname] {
return
}
i.ProcessedInstanceMethods[gname] = true
}
w.goCode.WriteString(fmt.Sprintf(`
func %s%s(%s) %s {
`,receiver,gname,gplist,grtype))
@ -826,7 +889,7 @@ func %s%s(%s) %s {
gt := tps[i].GoType()
//fmt.Printf(" %s\n",gt)
ns2 := ns[i]
if gt == "*NSString" {
if gt == "NSString" {
gt = "string"
ns[i] = gStringToNsstring(ns[i])
}
@ -864,20 +927,20 @@ func (w *Wrapper) ProcessEnum(e *Enum) {
ctp = e.Type.CGoType()
}
if e.Type != nil {
if !w.Processed[gtp] {
if !w.ProcessedTypes[gtp] {
w.goTypes.WriteString(fmt.Sprintf(`
type %s %s
`,gtp,ctp))
w.Processed[gtp] = true
w.ProcessedTypes[gtp] = true
}
}
gtp = gtp + " "
//fmt.Printf(" gtp = %s; ctp = %s\n",gtp,ctp)
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))
}
w.goConst.WriteString("\n")
}
func (w *Wrapper) ProcessSubclass(sname string, ps map[string][]string) {
@ -924,7 +987,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
os.Exit(-1)
}
//fmt.Printf(" subclass for %s\n",pname)
ms = interf.Methods
ms = interf.InstanceMethods
supr = interf.GoName
} else {
proto := w.Protocols[pname]
@ -933,7 +996,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
os.Exit(-1)
}
//fmt.Printf(" proto %s\n",pname)
ms = proto.Methods
ms = proto.InstanceMethods
supr = "Id"
}
for gname,m := range ms {
@ -1032,16 +1095,6 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
}
crtypes[i] = m.Type.CTypeAttrib()
if m.Type.IsPointer() {
// work around cgo bugs with struct size calculation
/*FIXME: DON'T NEED TO DO THIS?
crtypes[i] = "void*"
if x := m.Type.Node.Qualifiers(); x != "" {
crtypes[i] = x + " " + crtypes[i]
}
if x := m.Type.Node.Annotations(); x != "" {
crtypes[i] = crtypes[i] + " " + x
}
*/
cgtypes[i] = "unsafe.Pointer"
} else {
crtypes[i] = m.Type.CTypeAttrib()
@ -1106,7 +1159,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
sfundecls[i] = fmt.Sprintf(`
%s
{
%s[%s super_%s];
%s[(%s*)self super_%s];
}
`,sfp,ret,gname,strings.Join(vpnames[i]," "))
}
@ -1137,14 +1190,15 @@ void*
gotypes.WriteString(fmt.Sprintf(`
type %s struct { %s }
func (o *%s) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
func (o *Id) %s() *%s { return (*%s)(unsafe.Pointer(o)) }
`,gname,supr,gname,gname,gname,gname))
func (o %s) Ptr() unsafe.Pointer { return o.ptr }
`,gname,supr,gname))
//5. Go constructor
gocode.WriteString(fmt.Sprintf(`
func %sAlloc() *%s {
return (*%s)(unsafe.Pointer(C.%sAlloc()))
func %sAlloc() %s {
ret := %s{}
ret.ptr = unsafe.Pointer(C.%sAlloc())
return ret
}
`,gname,gname,gname,dname))
@ -1195,6 +1249,7 @@ func (d *%s) %sCallback(f func(%s)%s) {
//3. Go exported callback function wrappers
earglist := []string{"o unsafe.Pointer"}
garglist := []string{}
gargconv := []string{}
if sub {
garglist = []string{"super"}
}
@ -1207,10 +1262,17 @@ func (d *%s) %sCallback(f func(%s)%s) {
gt2 = gtypes[i][j-1]
}
if types.IsGoInterface(gt2) {
gt2 = "*Id"
gt2 = "Id"
}
garglist = append(garglist,fmt.Sprintf(
if types.ShouldWrap(gt2) || gt2 == "Id" {
garglist = append(garglist,fmt.Sprintf(
`a%d`,j))
gargconv = append(gargconv,fmt.Sprintf(
` a%d := %s{}; a%d.ptr = %s`,j,gt2,j,vnames[i][j]))
} else {
garglist = append(garglist,fmt.Sprintf(
`(%s)(%s)`,gt2,vnames[i][j]))
}
}
retdecl := ""
retname := ""
@ -1243,14 +1305,19 @@ func (d *%s) %sCallback(f func(%s)%s) {
}
`,gname,gname,strings.Join(sdispentries,",\n"))
}
if len(gargconv) > 0 {
retn = "\n " + retn
} else {
retn = " " + retn
}
goexports.WriteString(fmt.Sprintf(`
//export %s%s
func %s%s(%s)%s {
%scb := %sLookup[o].%s
if cb == nil { return%s }
%s%scb(%s)%s
%s%s%scb(%s)%s
}
`,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,sper,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
if !sub { continue } // for subclasses only
grtype := m.Type.GoType()
@ -1258,7 +1325,7 @@ func %s%s(%s)%s {
grtype = ""
}
if types.IsGoInterface(grtype) {
grtype = "*Id"
grtype = "Id"
}
if grtype == "BOOL" {
grtype = "bool"
@ -1325,12 +1392,14 @@ func (w *Wrapper) Wrap(toproc []string) {
if types.IsGoInterface(i.GoName) {
gname = "Id"
}
fmt.Printf("Interface %s: %d properties, %d methods\n",
i.Name, len(i.Properties), len(i.Methods))
fmt.Printf("Interface %s: %d properties, %d class methods, %d instance methods\n",
i.Name, len(i.Properties), len(i.ClassMethods), len(i.InstanceMethods))
w.goCode.WriteString(fmt.Sprintf(`
func %sAlloc() *%s {
return (*%s)(unsafe.Pointer(C.%sAlloc()))
func %sAlloc() %s {
ret := %s{}
ret.ptr = unsafe.Pointer(C.%sAlloc())
return ret
}
`,i.GoName,gname,gname,i.Name))
@ -1358,9 +1427,25 @@ void*
}
}
//FIXME: sort methods
for _,m := range i.Methods {
for _,m := range i.ClassMethods {
w.ProcessMethod(m)
}
for _,m := range i.InstanceMethods {
w.ProcessMethod(m)
}
// add methods for Protocols that this interface implements
for _,p := range i.Protocols {
prot,ok := w.Protocols[p]
if !ok {
fmt.Printf("Failed to find protocol %s for interface %s\n",p,i.Name)
os.Exit(-1)
}
for _,m := range prot.ClassMethods {
w.ProcessMethodForClass(m,i.Name)
}
for _,m := range prot.InstanceMethods {
w.ProcessMethodForClass(m,i.Name)
}
}
}
for _,m := range w.Functions {