2019-04-09 11:52:21 -04:00
package wrap
import (
"fmt"
2019-04-26 22:44:30 -04:00
"os"
"path"
2019-05-02 14:14:29 -04:00
"regexp"
2019-04-29 16:14:45 -04:00
"strconv"
2019-04-09 23:19:49 -04:00
"strings"
2019-04-09 11:52:21 -04:00
2019-04-26 15:34:36 -04:00
"gitlab.wow.st/gmp/nswrap/ast"
"gitlab.wow.st/gmp/nswrap/types"
2019-04-09 11:52:21 -04:00
)
2019-04-10 14:00:48 -04:00
var (
Debug = false
)
2019-04-09 11:52:21 -04:00
type Wrapper struct {
2019-04-26 22:44:30 -04:00
Package string
2019-05-01 10:58:29 -04:00
Interfaces map [ string ] * Interface
Functions map [ string ] * Method
2019-05-02 14:14:29 -04:00
NamedEnums map [ string ] * Enum
AnonEnums [ ] * Enum
2019-04-23 15:48:11 -04:00
2019-05-02 14:14:29 -04:00
cgoFlags strings . Builder // put cGo directives here
2019-04-26 14:08:43 -04:00
cCode strings . Builder // put cGo code here
goTypes strings . Builder // put Go type declarations here
2019-05-02 14:14:29 -04:00
goConst strings . Builder // put Go constants (from C enums) here
2019-04-26 14:08:43 -04:00
goCode strings . Builder // put Go code here
goHelpers strings . Builder // put Go helper functions here
2019-04-10 14:00:48 -04:00
Processed map [ string ] bool
2019-04-29 16:14:45 -04:00
VaArgs int
2019-04-09 11:52:21 -04:00
}
2019-04-11 11:46:24 -04:00
func NewWrapper ( debug bool ) * Wrapper {
Debug = debug
if Debug { fmt . Println ( "// Debug mode" ) }
2019-04-26 15:34:36 -04:00
ret := & Wrapper {
2019-05-01 10:58:29 -04:00
Interfaces : map [ string ] * Interface { } ,
Functions : map [ string ] * Method { } ,
2019-05-02 14:14:29 -04:00
NamedEnums : map [ string ] * Enum { } ,
AnonEnums : [ ] * Enum { } ,
2019-04-23 15:48:11 -04:00
Processed : map [ string ] bool { } ,
2019-04-29 16:14:45 -04:00
VaArgs : 16 ,
2019-04-09 23:19:49 -04:00
}
2019-05-02 14:14:29 -04:00
ret . cgoFlags . WriteString ( fmt . Sprintf ( ` / *
2019-04-26 15:34:36 -04:00
# cgo CFLAGS : - x objective - c
2019-05-02 14:14:29 -04:00
` ) )
2019-04-29 11:46:48 -04:00
ret . goTypes . WriteString ( `
2019-04-30 09:14:25 -04:00
type Id struct { }
func ( o * Id ) Ptr ( ) unsafe . Pointer { return unsafe . Pointer ( o ) }
2019-04-26 15:34:36 -04:00
` )
return ret
}
2019-05-02 14:14:29 -04:00
func ( w * Wrapper ) Frameworks ( ss [ ] string ) {
if len ( ss ) == 0 {
return
}
for _ , s := range ss {
w . cCode . WriteString ( fmt . Sprintf ( "#import <%s/%s.h>\n" , s , s ) )
}
w . cgoFlags . WriteString ( "#cgo LDFLAGS: -framework " + strings . Join ( ss , " -framework " ) )
}
2019-04-26 15:34:36 -04:00
func ( w * Wrapper ) Import ( ss [ ] string ) {
for _ , s := range ss {
2019-05-02 14:14:29 -04:00
w . cCode . WriteString ( "\n#import \"" + s + "\"\n" )
2019-04-26 15:34:36 -04:00
}
}
func ( w * Wrapper ) SysImport ( ss [ ] string ) {
for _ , s := range ss {
2019-04-26 15:41:29 -04:00
w . cCode . WriteString ( "\n#import <" + s + ">\n" )
}
}
func ( w * Wrapper ) Pragma ( ss [ ] string ) {
for _ , s := range ss {
2019-05-02 14:14:29 -04:00
w . cgoFlags . WriteString ( "\n#pragma " + s + "\n" )
2019-04-26 15:34:36 -04:00
}
2019-04-09 23:19:49 -04:00
}
type Property struct {
2019-04-26 15:08:44 -04:00
Name , Attr string
Type * types . Type
2019-04-09 23:19:49 -04:00
}
type Parameter struct {
2019-04-26 15:08:44 -04:00
Pname , Vname string
Type * types . Type
2019-04-09 23:19:49 -04:00
}
type Method struct {
2019-05-02 14:14:29 -04:00
Name , Class , GoClass string
2019-04-26 15:08:44 -04:00
Type * types . Type
2019-04-11 17:00:36 -04:00
ClassMethod bool
2019-05-01 10:58:29 -04:00
Parameters [ ] * Parameter
2019-04-09 23:19:49 -04:00
}
2019-05-02 14:14:29 -04:00
type Enum struct {
Name string
Type * types . Type
Constants [ ] string
}
2019-04-26 15:08:44 -04:00
//isVoid() returns true if the method has no return value.
2019-04-10 14:00:48 -04:00
func ( m Method ) isVoid ( ) bool {
2019-04-26 15:08:44 -04:00
return m . Type . CType ( ) == "void"
2019-04-10 14:00:48 -04:00
}
2019-04-26 14:08:43 -04:00
//hasFunctionParam() returns true if a method has a function as a parameter.
func ( m Method ) hasFunctionParam ( ) bool {
for _ , p := range m . Parameters {
2019-05-03 13:14:30 -04:00
if p . Type . IsFunction ( ) || p . Type . IsFunctionPtr ( ) {
2019-04-26 14:08:43 -04:00
return true
}
}
return false
}
2019-05-01 10:58:29 -04:00
func ( w Wrapper ) cparamlist ( m * Method ) ( string , string ) {
ns := make ( [ ] string , 0 )
2019-04-11 17:00:36 -04:00
ret := make ( [ ] string , 0 )
if ! m . ClassMethod {
2019-05-01 10:58:29 -04:00
ret = append ( ret , "void* o" )
2019-04-11 17:00:36 -04:00
}
2019-04-11 11:46:24 -04:00
for _ , p := range m . Parameters {
2019-04-26 14:08:43 -04:00
var tp string
2019-05-01 10:58:29 -04:00
if p . Type . IsPointer ( ) || p . Type . Variadic {
2019-04-11 13:09:02 -04:00
tp = "void*"
2019-04-26 14:08:43 -04:00
} else {
2019-04-26 15:08:44 -04:00
tp = p . Type . CType ( )
2019-04-11 13:09:02 -04:00
}
2019-05-01 10:58:29 -04:00
ns = append ( ns , p . Vname )
2019-04-11 13:09:02 -04:00
ret = append ( ret , fmt . Sprintf ( "%s %s" , tp , p . Vname ) )
2019-04-10 14:00:48 -04:00
}
2019-05-01 10:58:29 -04:00
return strings . Join ( ns , ", " ) , strings . Join ( ret , ", " )
2019-04-10 14:00:48 -04:00
}
2019-05-01 10:58:29 -04:00
func ( w Wrapper ) objcparamlist ( m * Method ) string {
2019-04-10 14:00:48 -04:00
if len ( m . Parameters ) == 0 {
return m . Name
}
2019-04-11 11:46:24 -04:00
first := true
ret := [ ] string { }
for _ , p := range m . Parameters {
2019-04-29 16:14:45 -04:00
if first && ! p . Type . Variadic {
2019-04-11 11:46:24 -04:00
ret = append ( ret , m . Name + ":" + p . Vname )
first = false
} else {
2019-04-29 16:14:45 -04:00
if p . Type . Variadic {
str := [ ] string { m . Name + ":arr[0]" }
for i := 1 ; i < w . VaArgs ; i ++ {
str = append ( str , "arr[" + strconv . Itoa ( i ) + "]" )
}
str = append ( str , "nil" )
ret = append ( ret , strings . Join ( str , ", " ) )
} else {
ret = append ( ret , p . Pname + ":" + p . Vname )
}
2019-04-11 11:46:24 -04:00
}
2019-04-10 14:00:48 -04:00
}
return strings . Join ( ret , " " )
}
2019-04-23 15:48:11 -04:00
//goreserved is a map telling whether a word is a go reserved word that is not
//also a C/Objective-C reserved word.
2019-04-11 11:46:24 -04:00
var goreserved map [ string ] bool = map [ string ] bool {
"range" : true ,
2019-05-02 14:14:29 -04:00
"type" : true ,
2019-04-11 11:46:24 -04:00
}
2019-05-01 11:01:50 -04:00
func ( w * Wrapper ) gpntp ( m * Method ) ( [ ] string , [ ] * types . Type , string ) {
w . processType ( m . Type )
2019-04-26 09:04:53 -04:00
ns := [ ] string { }
tps := [ ] * types . Type { }
2019-04-26 10:56:00 -04:00
if ! m . ClassMethod {
ns = append ( ns , "o" )
tps = append ( tps , types . NewTypeFromString ( m . Class + "*" , "" ) )
}
2019-04-26 09:04:53 -04:00
for _ , p := range m . Parameters {
gname := p . Vname
if goreserved [ gname ] {
gname = gname + "_"
}
ns = append ( ns , gname )
2019-04-26 15:08:44 -04:00
tps = append ( tps , p . Type )
2019-04-26 09:04:53 -04:00
}
2019-05-01 11:01:50 -04:00
w . processTypes ( tps )
2019-04-26 10:56:00 -04:00
ret := [ ] string { }
i := 0
if ! m . ClassMethod { i = 1 }
for ; i < len ( ns ) ; i ++ {
2019-04-26 14:08:43 -04:00
gt := tps [ i ] . GoType ( )
if gt == "*Void" {
gt = "unsafe.Pointer"
}
2019-04-29 16:14:45 -04:00
if tps [ i ] . Variadic {
gt = "..." + gt
ns [ i ] = ns [ i ] + "s"
}
2019-04-26 14:08:43 -04:00
ret = append ( ret , ns [ i ] + " " + gt )
2019-04-26 10:56:00 -04:00
}
return ns , tps , strings . Join ( ret , ", " )
2019-04-26 09:04:53 -04:00
}
2019-04-10 14:00:48 -04:00
2019-04-09 23:19:49 -04:00
type Interface struct {
2019-05-02 14:14:29 -04:00
Name , GoName string
2019-05-01 10:58:29 -04:00
Properties map [ string ] * Property
Methods map [ string ] * Method
2019-04-09 23:19:49 -04:00
}
func ( w * Wrapper ) AddInterface ( n * ast . ObjCInterfaceDecl ) {
//fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name)
w . add ( n . Name , n . Children ( ) )
}
func ( w * Wrapper ) AddCategory ( n * ast . ObjCCategoryDecl ) {
ns := n . Children ( )
2019-04-23 15:48:11 -04:00
if len ( ns ) > 0 {
switch x := ns [ 0 ] . ( type ) {
case * ast . ObjCInterface :
w . add ( x . Name , ns [ 1 : ] )
return
}
2019-04-09 23:19:49 -04:00
}
2019-04-23 15:48:11 -04:00
fmt . Printf ( "Not adding methods for %s: interface name not found in first child node of category defclaration\n" , n . Name )
2019-04-09 11:52:21 -04:00
}
2019-05-01 10:58:29 -04:00
func ( w * Wrapper ) AddFunction ( n * ast . FunctionDecl ) {
//treat functions as class methods with no class
tp := types . NewTypeFromString ( n . Type , "" )
m := & Method {
Name : n . Name ,
Type : tp . ReturnType ( ) ,
Class : "" ,
ClassMethod : true ,
Parameters : [ ] * Parameter { } ,
}
f := tp . Node . Children [ len ( tp . Node . Children ) - 1 ] // Function node
if f . Kind != "Function" {
//fmt.Printf("AddFunction(%s): not a function -- Node type is %s\n%s",n.Name,f.Kind,tp.String())
return
}
//fmt.Printf("FunctionDecl: %s (%s) %s\n",n.Type,m.Type.CType(),n.Name)
i := 0
for _ , c := range n . Children ( ) {
switch x := c . ( type ) {
case * ast . ParmVarDecl :
p := & Parameter {
Vname : x . Name ,
Type : types . NewTypeFromString ( x . Type , "" ) ,
}
m . Parameters = append ( m . Parameters , p )
i ++
//fmt.Printf(" %s\n",p.Type.CType())
}
}
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
}
2019-05-02 14:14:29 -04:00
//FIXME: copied from nswrap/main.go, should put this in a utils package
func matches ( x string , rs [ ] string ) bool {
for _ , r := range rs {
if m , _ := regexp . MatchString ( "^" + r + "$" , x ) ; m {
return true
}
}
return false
}
func ( w * Wrapper ) AddEnum ( n * ast . EnumDecl , rs [ ] string ) {
if n . Name != "" && ! matches ( n . Name , rs ) {
return
}
//fmt.Printf("Adding enum: (%s) %s\n",n.Type,n.Name)
var tp * types . Type
a := ( * Avail ) ( & [ ] AvailAttr { } )
if n . Type == "" {
tp = nil
} else {
tp = types . NewTypeFromString ( n . Type , "" )
//fmt.Printf(" type: %s\n",tp.CType())
}
e := & Enum {
Name : n . Name , // NOTE: may be empty string
Type : tp ,
Constants : [ ] string { } ,
}
for _ , c := range n . Children ( ) {
switch x := c . ( type ) {
case * ast . AvailabilityAttr , * ast . UnavailableAttr :
a . Add ( x )
case * ast . EnumConstantDecl :
//fmt.Printf("*ast.EnumConstantDecl: (%s) '%s': %s\n",n.Type,n.Name,x.Name)
if n . Name == "" && ! matches ( x . Name , rs ) {
continue
}
e . Constants = append ( e . Constants , x . Name )
}
}
if a . Available ( ) && len ( e . Constants ) > 0 {
if e . Name == "" {
w . AnonEnums = append ( w . AnonEnums , e )
} else {
w . NamedEnums [ e . Name ] = e
}
//fmt.Printf(" added\n")
}
}
2019-04-09 23:19:49 -04:00
func ( w * Wrapper ) add ( name string , ns [ ] ast . Node ) {
2019-05-01 10:58:29 -04:00
var i * Interface
2019-04-09 23:19:49 -04:00
var ok bool
2019-05-02 14:14:29 -04:00
goname := types . NewTypeFromString ( name , name ) . GoType ( )
types . Wrap ( goname )
2019-04-09 23:19:49 -04:00
if i , ok = w . Interfaces [ name ] ; ! ok {
2019-05-01 10:58:29 -04:00
i = & Interface {
2019-04-09 23:19:49 -04:00
Name : name ,
2019-05-02 14:14:29 -04:00
GoName : goname ,
2019-05-01 10:58:29 -04:00
Properties : map [ string ] * Property { } ,
Methods : map [ string ] * Method { } ,
2019-04-09 23:19:49 -04:00
}
}
2019-04-11 11:46:24 -04:00
var avail bool
2019-04-09 23:19:49 -04:00
for _ , c := range ns {
2019-04-09 11:52:21 -04:00
switch x := c . ( type ) {
2019-04-09 23:19:49 -04:00
case * ast . ObjCPropertyDecl :
2019-04-11 11:46:24 -04:00
//fmt.Printf("ObjCPropertyDecl: %s\n",x.Name)
2019-05-01 10:58:29 -04:00
p := & Property {
2019-04-09 23:19:49 -04:00
Name : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , name ) ,
2019-04-09 23:19:49 -04:00
}
2019-04-11 11:46:24 -04:00
//_,avail = w.GetParms(x,name) // TODO
//if avail {
i . Properties [ p . Name ] = p
//}
2019-04-09 11:52:21 -04:00
case * ast . ObjCMethodDecl :
2019-04-30 06:59:05 -04:00
//fmt.Printf("ObjCMethodDecl: %s (%s) %s\n",x.Type,name,x.Name)
2019-05-01 10:58:29 -04:00
m := & Method {
2019-04-09 23:19:49 -04:00
Name : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , name ) ,
2019-04-11 11:46:24 -04:00
Class : name ,
2019-05-02 14:14:29 -04:00
GoClass : goname ,
2019-04-11 17:00:36 -04:00
ClassMethod : x . ClassMethod ,
2019-04-11 11:46:24 -04:00
}
2019-04-30 06:59:05 -04:00
//fmt.Println(m.Type.Node.String())
2019-04-11 11:46:24 -04:00
m . Parameters , avail = w . GetParms ( x , name )
if avail {
i . Methods [ m . Name ] = m
2019-04-09 23:19:49 -04:00
}
case * ast . ObjCProtocol :
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
case * ast . ObjCInterface :
2019-04-10 14:00:48 -04:00
if x . Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
2019-04-26 09:04:53 -04:00
types . SetSuper ( name , x . Name )
2019-04-10 14:00:48 -04:00
}
2019-04-26 22:44:30 -04:00
case * ast . ObjCTypeParamDecl :
//fmt.Printf("ObjCTypeParamDecl: %s = %s\n",x.Name,x.Type)
types . SetTypeParam ( name , x . Name , x . Type )
2019-04-09 23:19:49 -04:00
case * ast . Unknown :
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
2019-04-09 11:52:21 -04:00
default :
2019-04-09 23:19:49 -04:00
//fmt.Println(reflect.TypeOf(x))
}
}
2019-05-01 10:58:29 -04:00
//Add class methods from super class
var supmethods func ( * Interface , string )
supmethods = func ( i * Interface , s string ) {
if sup , ok := w . Interfaces [ s ] ; ! ok {
return
} else {
for _ , m := range sup . Methods {
if ! m . ClassMethod {
continue
}
m2 := & Method {
Name : m . Name ,
Class : i . Name ,
2019-05-02 14:14:29 -04:00
GoClass : i . GoName ,
2019-05-01 10:58:29 -04:00
Type : m . Type . CloneToClass ( i . Name ) ,
ClassMethod : true ,
Parameters : [ ] * Parameter { } ,
}
for _ , p := range m . Parameters {
m2 . Parameters = append ( m2 . Parameters ,
& Parameter {
Pname : p . Pname ,
Vname : p . Vname ,
Type : p . Type . CloneToClass ( i . Name ) ,
} )
}
i . Methods [ m . Name ] = m2
}
}
supmethods ( i , types . Super ( s ) )
}
supmethods ( i , types . Super ( i . Name ) )
2019-04-23 15:48:11 -04:00
//fmt.Println("Add interface ",i.Name)
2019-04-09 23:19:49 -04:00
w . Interfaces [ i . Name ] = i
}
2019-04-11 11:46:24 -04:00
type AvailAttr struct {
OS , Version string
Deprecated bool
}
2019-05-02 14:14:29 -04:00
type Avail [ ] AvailAttr
func ( a * Avail ) Add ( n ast . Node ) {
switch x := n . ( type ) {
case * ast . AvailabilityAttr :
* a = append ( * a , AvailAttr {
OS : x . OS ,
Version : x . Version ,
Deprecated : ( x . Unknown1 != "0" ) || x . IsUnavailable ,
} )
case * ast . UnavailableAttr :
* a = append ( * a , AvailAttr {
OS : "macos" , Deprecated : true } )
}
}
func ( a * Avail ) Available ( ) bool {
if len ( * a ) == 0 {
return true
}
for _ , x := range * a {
if x . OS == "macos" && x . Deprecated == false {
return true
}
}
return false
}
2019-04-23 15:48:11 -04:00
//GetParms returns the parameters of a method declaration and a bool
//indicating whether the given method is available on MacOS and not
//deprecated.
2019-05-01 10:58:29 -04:00
func ( w * Wrapper ) GetParms ( n ast . Node , class string ) ( [ ] * Parameter , bool ) {
ret := make ( [ ] * Parameter , 0 )
2019-05-02 14:14:29 -04:00
avail := ( * Avail ) ( & [ ] AvailAttr { } )
2019-05-01 10:58:29 -04:00
var parms [ ] string
switch x := n . ( type ) {
case * ast . ObjCMethodDecl :
parms = x . Parameters
case * ast . FunctionDecl :
default :
panic ( "GetParms called with wrong node type" )
}
2019-04-11 11:46:24 -04:00
j := 0
for _ , c := range n . Children ( ) {
2019-04-09 23:19:49 -04:00
switch x := c . ( type ) {
case * ast . ParmVarDecl :
2019-05-01 10:58:29 -04:00
p := & Parameter {
2019-04-11 11:46:24 -04:00
Vname : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , class ) ,
2019-04-09 23:19:49 -04:00
}
2019-05-01 10:58:29 -04:00
if parms != nil {
p . Pname = parms [ j ]
}
2019-04-11 11:46:24 -04:00
ret = append ( ret , p )
j ++
2019-04-29 16:14:45 -04:00
case * ast . Variadic :
ret [ j - 1 ] . Type . Variadic = true
2019-05-02 14:14:29 -04:00
case * ast . AvailabilityAttr , * ast . UnavailableAttr :
avail . Add ( x )
2019-04-11 11:46:24 -04:00
case * ast . Unknown :
if Debug { fmt . Printf ( "GetParms(): ast.Unknown: %s\n" , x . Name ) }
2019-04-09 23:19:49 -04:00
}
}
2019-04-18 09:38:46 -04:00
// check that the method is available for this OS and not deprecated
2019-05-02 14:14:29 -04:00
if ! avail . Available ( ) {
2019-04-11 11:46:24 -04:00
return nil , false
2019-04-09 23:19:49 -04:00
}
2019-04-18 09:38:46 -04:00
// check that we found the right number of parameters
2019-05-01 10:58:29 -04:00
//if len(ret) != len(n.Parameters) {
// fmt.Printf("Error in method declaration %s: Wrong number of ParmVarDecl children: %d parameters but %d ParmVarDecl children\n",n.Name,len(n.Parameters),len(ret))
//}
2019-04-11 11:46:24 -04:00
return ret , true
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:08:44 -04:00
func ( w * Wrapper ) processTypes ( tps [ ] * types . Type ) {
2019-04-26 10:07:34 -04:00
switch len ( tps ) {
case 0 :
return
case 1 :
2019-04-26 15:08:44 -04:00
w . processType ( tps [ 0 ] )
2019-04-26 10:07:34 -04:00
default :
for _ , tp := range tps {
2019-04-26 15:08:44 -04:00
w . processType ( tp )
2019-04-26 10:07:34 -04:00
}
return
}
2019-04-26 14:08:43 -04:00
}
2019-04-26 15:08:44 -04:00
func ( w * Wrapper ) processType ( tp * types . Type ) {
2019-04-29 10:37:43 -04:00
bt := tp . BaseType ( )
gt := bt . GoType ( )
2019-04-27 22:24:05 -04:00
if gt == "" {
2019-04-26 10:07:34 -04:00
return
}
2019-04-27 22:24:05 -04:00
if gt [ 0 ] == '*' {
2019-04-29 10:37:43 -04:00
w . processType ( bt . PointsTo ( ) )
2019-04-27 22:24:05 -04:00
return
}
if w . Processed [ gt ] { return }
w . Processed [ gt ] = true
if gt == "Char" {
2019-04-26 14:08:43 -04:00
w . CharHelpers ( )
}
2019-05-02 14:14:29 -04:00
if gt == "NSEnumerator" {
w . EnumeratorHelpers ( )
}
2019-05-03 13:14:30 -04:00
if bt . IsFunction ( ) || bt . IsFunctionPtr ( ) {
2019-04-27 22:24:05 -04:00
return
}
super := types . Super ( gt )
2019-04-26 14:08:43 -04:00
if super != "" {
2019-05-02 14:14:29 -04:00
tp := types . NewTypeFromString ( super , "" )
types . Wrap ( tp . GoType ( ) )
w . processType ( tp )
2019-04-26 14:08:43 -04:00
}
2019-04-29 11:46:48 -04:00
w . goTypes . WriteString ( bt . GoTypeDecl ( ) )
2019-04-26 14:08:43 -04:00
}
func ( w * Wrapper ) CharHelpers ( ) {
w . goHelpers . WriteString ( `
2019-05-03 13:14:30 -04:00
func CharWithGoString ( s string ) * Char {
2019-04-26 14:08:43 -04:00
return ( * Char ) ( unsafe . Pointer ( C . CString ( s ) ) )
}
func CharFromBytes ( b [ ] byte ) * Char {
return ( * Char ) ( unsafe . Pointer ( C . CString ( string ( b ) ) ) )
}
func ( c * Char ) String ( ) string {
return C . GoString ( ( * C . char ) ( c ) )
}
` )
2019-04-26 10:07:34 -04:00
}
2019-05-02 14:14:29 -04:00
func ( w * Wrapper ) EnumeratorHelpers ( ) {
w . goHelpers . WriteString ( `
func ( e * NSEnumerator ) ForIn ( f func ( * Id ) bool ) {
for o := e . NextObject ( ) ; o != nil ; o = e . NextObject ( ) {
if ! f ( o ) { break }
}
2019-05-03 13:14:30 -04:00
}
` )
2019-05-02 14:14:29 -04:00
}
2019-05-01 10:58:29 -04:00
func ( w * Wrapper ) ProcessMethod ( m * Method ) {
w . _processMethod ( m , false )
}
func ( w * Wrapper ) ProcessFunction ( m * Method ) {
w . _processMethod ( m , true )
}
func ( w * Wrapper ) _processMethod ( m * Method , fun bool ) {
if Debug {
fmt . Printf ( " method: %s (%s)\n" , m . Name , m . Type )
}
2019-05-03 13:14:30 -04:00
if m . Type . IsFunction ( ) || m . Type . IsFunctionPtr ( ) || m . hasFunctionParam ( ) {
2019-05-01 10:58:29 -04:00
return
}
gname := strings . Title ( m . Name )
2019-05-01 12:32:42 -04:00
switch {
case ! m . ClassMethod :
2019-05-02 14:14:29 -04:00
case m . Type . GoType ( ) != "*" + m . GoClass :
gname = m . GoClass + gname
2019-05-01 12:32:42 -04:00
default :
lens1 := len ( m . Class )
i := 0
if len ( gname ) < len ( m . Class ) { i = lens1 - len ( gname ) }
for ; i < lens1 ; i ++ {
if m . Class [ i : ] == gname [ : lens1 - i ] { break }
}
if lens1 - i >= len ( gname ) {
2019-05-02 14:14:29 -04:00
gname = m . GoClass + gname
2019-05-01 12:32:42 -04:00
} else {
2019-05-02 14:14:29 -04:00
gname = m . GoClass + gname [ lens1 - i : ]
2019-05-01 12:32:42 -04:00
}
2019-05-01 10:58:29 -04:00
}
2019-05-03 13:14:30 -04:00
receiver := ""
if ! m . ClassMethod {
receiver = "(o *" + m . GoClass + ") "
}
2019-05-01 10:58:29 -04:00
cname := m . Name
if m . Class != "" {
cname = m . Class + "_" + cname
}
cmtype := m . Type . CTypeAttrib ( )
2019-05-01 11:01:50 -04:00
ns , tps , gplist := w . gpntp ( m )
2019-05-01 10:58:29 -04:00
grtype := m . Type . GoType ( )
if grtype == "Void" {
grtype = ""
}
2019-05-01 12:32:42 -04:00
if types . IsGoInterface ( grtype ) {
grtype = "*Id"
}
2019-05-06 09:00:38 -04:00
if grtype == "BOOL" { // convert objective-c bools to Go bools
grtype = "bool"
}
2019-05-02 14:14:29 -04:00
if gname == grtype { // avoid name conflicts between methods and types
gname = "Get" + gname
}
2019-05-01 10:58:29 -04:00
w . goCode . WriteString ( fmt . Sprintf ( `
//%s
2019-05-03 13:14:30 -04:00
func % s % s ( % s ) % s {
` , m . Type . CType ( ) , receiver , gname , gplist , grtype ) )
2019-05-01 10:58:29 -04:00
lparm := len ( tps ) - 1
if len ( tps ) > 0 && tps [ lparm ] . Variadic {
vn := ns [ lparm ]
vn = vn [ : len ( vn ) - 1 ]
ns [ lparm ] = vn
w . goCode . WriteString ( fmt . Sprintf (
` var % s [ % d ] unsafe . Pointer
for i , o := range % ss {
% s [ i ] = o . Ptr ( )
}
` , vn , w . VaArgs , vn , vn ) )
}
w . goCode . WriteString ( ` ` +
types . GoToC ( cname , ns , m . Type , tps ) + "\n}\n\n" )
cret := ""
if ! m . isVoid ( ) {
cret = "return "
}
var cobj string
if m . ClassMethod {
cobj = m . Class
} else {
2019-05-02 14:14:29 -04:00
cobj = "(" + m . Class + "*)o"
2019-05-01 10:58:29 -04:00
}
cns , cntps := w . cparamlist ( m )
_ = cns
if fun {
return
}
w . cCode . WriteString ( fmt . Sprintf ( `
% s
% s ( % s ) {
` , cmtype , cname , cntps ) )
if len ( tps ) > 0 && tps [ lparm ] . Variadic {
w . cCode . WriteString ( fmt . Sprintf (
` % s * arr = % s ;
` , tps [ lparm ] . CType ( ) , ns [ lparm ] ) )
}
if fun {
w . cCode . WriteString ( fmt . Sprintf ( ` % s % s ( % s ) ;
} ` , cret , m . Name , cns ) )
} else {
w . cCode . WriteString ( fmt . Sprintf ( ` % s [ % s % s ] ;
} ` , cret , cobj , w . objcparamlist ( m ) ) )
}
2019-05-03 13:14:30 -04:00
// create GoString helper method
if ok , _ := regexp . MatchString ( "WithString$" , m . Name ) ; ok {
//fmt.Printf("--%s\n",gname)
gname2 := gname [ : len ( gname ) - 6 ] + "GoString"
gps := [ ] string { }
i := 0
if ! m . ClassMethod { i = 1 }
for ; i < len ( ns ) ; i ++ {
gt := tps [ i ] . GoType ( )
//fmt.Printf(" %s\n",gt)
ns2 := ns [ i ]
if gt == "*NSString" {
gt = "string"
ns [ i ] = gStringToNsstring ( ns [ i ] )
}
gps = append ( gps , ns2 + " " + gt )
}
gplist = strings . Join ( gps , ", " )
obj := ""
if ! m . ClassMethod {
obj = "o."
ns = ns [ 1 : ]
}
w . goCode . WriteString ( fmt . Sprintf ( `
func % s % s ( % s ) % s {
return % s % s ( % s )
}
` , receiver , gname2 , gplist , grtype , obj , gname , strings . Join ( ns , ", " ) ) )
}
2019-05-01 10:58:29 -04:00
}
2019-04-10 14:00:48 -04:00
2019-05-03 13:14:30 -04:00
func gStringToNsstring ( s string ) string {
return fmt . Sprintf ( "NSStringWithUTF8String(CharWithGoString(%s))" , s )
}
2019-05-02 14:14:29 -04:00
func ( w * Wrapper ) ProcessEnum ( e * Enum ) {
gtp := ""
if e . Type != nil {
gtp = e . Type . GoType ( )
if ! w . Processed [ gtp ] {
w . goTypes . WriteString ( fmt . Sprintf ( `
type % s C . % s
` , gtp , e . Type . CType ( ) ) )
w . Processed [ gtp ] = true
}
}
gtp = gtp + " "
for _ , c := range e . Constants {
w . goConst . WriteString ( fmt . Sprintf ( `
const % s % s = C . % s
` , c , gtp , c ) )
}
}
2019-04-23 15:48:11 -04:00
func ( w * Wrapper ) Wrap ( toproc [ ] string ) {
2019-04-26 22:44:30 -04:00
if w . Package == "" { w . Package = "ns" }
err := os . MkdirAll ( w . Package , 0755 )
if err != nil {
fmt . Printf ( "Error creating directory '%s'\n%s\n" , w . Package , err )
os . Exit ( - 1 )
}
of , err := os . Create ( path . Join ( w . Package , "main.go" ) )
if err != nil {
fmt . Printf ( "Error opening file %s\n%s\n" , path . Join ( w . Package , "main.go" ) , err )
os . Exit ( - 1 )
}
fmt . Printf ( "Writing output to %s\n" , path . Join ( w . Package , "main.go" ) )
2019-05-01 10:58:29 -04:00
pInterfaces := map [ string ] * Interface { }
2019-04-23 15:48:11 -04:00
for _ , iface := range toproc {
pInterfaces [ iface ] = w . Interfaces [ iface ]
}
2019-04-26 15:08:44 -04:00
//FIXME: sort pInterfaces
2019-04-26 22:44:30 -04:00
for _ , i := range pInterfaces {
2019-05-02 14:14:29 -04:00
if i == nil {
continue
}
2019-04-26 22:44:30 -04:00
fmt . Printf ( "Interface %s: %d properties, %d methods\n" ,
i . Name , len ( i . Properties ) , len ( i . Methods ) )
2019-04-10 14:00:48 -04:00
2019-04-26 15:34:36 -04:00
w . goCode . WriteString ( fmt . Sprintf ( `
2019-05-04 23:32:57 -04:00
func % sAlloc ( ) * % s {
return ( * % s ) ( unsafe . Pointer ( C . % sAlloc ( ) ) )
2019-04-10 14:00:48 -04:00
}
2019-05-02 14:14:29 -04:00
` , i . GoName , i . GoName , i . GoName , i . Name ) )
2019-04-10 14:00:48 -04:00
w . cCode . WriteString ( fmt . Sprintf ( `
% s *
2019-05-04 23:32:57 -04:00
% sAlloc ( ) {
2019-04-11 17:00:36 -04:00
return [ % s alloc ] ;
2019-04-10 14:00:48 -04:00
}
2019-04-26 15:34:36 -04:00
` , i . Name , i . Name , i . Name ) )
2019-04-10 14:00:48 -04:00
2019-04-26 15:08:44 -04:00
//FIXME: sort properties
2019-04-23 15:48:11 -04:00
for _ , p := range i . Properties {
2019-04-10 14:00:48 -04:00
if Debug {
2019-04-26 15:08:44 -04:00
fmt . Printf ( " property: %s (%s)\n" , p . Name , p . Type . CType ( ) )
2019-04-10 14:00:48 -04:00
}
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:08:44 -04:00
//FIXME: sort methods
2019-04-23 15:48:11 -04:00
for _ , m := range i . Methods {
2019-05-01 10:58:29 -04:00
w . ProcessMethod ( m )
2019-04-10 14:00:48 -04:00
2019-04-09 11:52:21 -04:00
}
}
2019-05-01 10:58:29 -04:00
for _ , m := range w . Functions {
//fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name)
w . ProcessFunction ( m )
}
2019-05-02 14:14:29 -04:00
for _ , e := range w . NamedEnums {
w . ProcessEnum ( e )
}
for _ , e := range w . AnonEnums {
w . ProcessEnum ( e )
}
2019-05-01 10:58:29 -04:00
fmt . Printf ( "%d functions\n" , len ( w . Functions ) )
2019-05-02 14:14:29 -04:00
fmt . Printf ( "%d enums\n" , len ( w . NamedEnums ) + len ( w . AnonEnums ) )
2019-04-26 22:44:30 -04:00
of . WriteString ( "package " + w . Package + "\n\n" )
2019-05-02 14:14:29 -04:00
of . WriteString ( w . cgoFlags . String ( ) + "\n" )
2019-04-26 22:44:30 -04:00
of . WriteString ( w . cCode . String ( ) )
of . WriteString ( `
2019-04-10 14:00:48 -04:00
* /
import "C"
import (
"unsafe"
)
` )
2019-04-26 22:44:30 -04:00
of . WriteString ( w . goTypes . String ( ) )
2019-05-02 14:14:29 -04:00
of . WriteString ( w . goConst . String ( ) )
2019-04-26 22:44:30 -04:00
of . WriteString ( w . goHelpers . String ( ) )
of . WriteString ( w . goCode . String ( ) )
of . Close ( )
2019-04-09 11:52:21 -04:00
}