From a6167614757097a650ec0f9534f237cb170a7557 Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 23 May 2019 16:31:47 -0400 Subject: [PATCH] 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. --- cmd/nswrap/main.go | 11 +- examples/app/main.go | 110 +++++++----- examples/app/nswrap.yaml | 15 +- examples/bluetooth/main.go | 45 +++-- examples/bluetooth/nswrap.yaml | 2 +- examples/foundation/main.go | 30 +++- examples/foundation/nswrap.yaml | 1 + examples/simple/nswrap.yaml | 2 +- types/convert.go | 66 ++++--- wrap/main.go | 293 ++++++++++++++++++++------------ 10 files changed, 355 insertions(+), 220 deletions(-) diff --git a/cmd/nswrap/main.go b/cmd/nswrap/main.go index a84879d..a29d16e 100644 --- a/cmd/nswrap/main.go +++ b/cmd/nswrap/main.go @@ -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) } diff --git a/examples/app/main.go b/examples/app/main.go index 64ebca9..336466f 100644 --- a/examples/app/main.go +++ b/examples/app/main.go @@ -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() } diff --git a/examples/app/nswrap.yaml b/examples/app/nswrap.yaml index 89c3b5e..01274bf 100644 --- a/examples/app/nswrap.yaml +++ b/examples/app/nswrap.yaml @@ -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 - diff --git a/examples/bluetooth/main.go b/examples/bluetooth/main.go index a31e8d7..5dae98a 100644 --- a/examples/bluetooth/main.go +++ b/examples/bluetooth/main.go @@ -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 { } } diff --git a/examples/bluetooth/nswrap.yaml b/examples/bluetooth/nswrap.yaml index f6eb854..f1eed94 100644 --- a/examples/bluetooth/nswrap.yaml +++ b/examples/bluetooth/nswrap.yaml @@ -27,7 +27,7 @@ functions: [ NSMakeRange, dispatch_queue_create ] enums: [ CB.* ] frameworks: [ Foundation, CoreBluetooth ] delegates: - BleDelegate: + CBDelegate: CBCentralManagerDelegate: - centralManagerDidUpdateState - centralManagerDidDiscoverPeripheral diff --git a/examples/foundation/main.go b/examples/foundation/main.go index ca683c4..8760e32 100644 --- a/examples/foundation/main.go +++ b/examples/foundation/main.go @@ -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 + } + }) } diff --git a/examples/foundation/nswrap.yaml b/examples/foundation/nswrap.yaml index b1f2a67..2657b71 100644 --- a/examples/foundation/nswrap.yaml +++ b/examples/foundation/nswrap.yaml @@ -15,6 +15,7 @@ classes: - NSString - NSScanner - NSFileManager + - NSObject functions: [ NSMakeRange ] enums: [ P_ALL, CF.* ] frameworks: [ Foundation ] diff --git a/examples/simple/nswrap.yaml b/examples/simple/nswrap.yaml index ab74417..36ebeba 100644 --- a/examples/simple/nswrap.yaml +++ b/examples/simple/nswrap.yaml @@ -6,6 +6,6 @@ classes: subclasses: ClassThree: ClassTwo: - - hi.* + - .* imports: [ simple.h ] frameworks: [ Foundation ] diff --git a/types/convert.go b/types/convert.go index 254c23e..13c243b 100644 --- a/types/convert.go +++ b/types/convert.go @@ -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() } diff --git a/wrap/main.go b/wrap/main.go index fad7fbf..23a3162 100644 --- a/wrap/main.go +++ b/wrap/main.go @@ -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 {