Skip to content

Commit

Permalink
generate: Further progress on function support, add basic cstring han…
Browse files Browse the repository at this point in the history
…dling
  • Loading branch information
tmc committed Sep 3, 2023
1 parent 30f794a commit b8d925b
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 32 deletions.
54 changes: 45 additions & 9 deletions generate/codegen/gen_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package codegen

import (
"fmt"
"log"
"strings"

"github.com/progrium/macdriver/internal/set"
Expand Down Expand Up @@ -44,16 +43,18 @@ var typeMap = map[string]string{
"*kernel.UniChar": "*uint16",
"kernel.Boolean_t": "int",
"kernel.Pid_t": "int32",
"CGFloat": "float64",
"uint8_t": "byte",
}

// GoArgs return go function args
func (f *Function) GoArgs(currentModule *modules.Module) string {
log.Println("rendering function", f.Name)
// log.Println("rendering function", f.Name)
var args []string
var blankArgCounter = 0
for _, p := range f.Parameters {
log.Println("rendering function", f.Name, p.Name, p.Type)
log.Printf("rendering function ptype: %T", p.Type)
// log.Println("rendering function", f.Name, p.Name, p.Type)
// log.Printf("rendering function ptype: %T", p.Type)
// if is reserved word, add _ suffix
if p.Name == "" {
p.Name = fmt.Sprintf("arg%d", blankArgCounter)
Expand Down Expand Up @@ -146,7 +147,7 @@ func (f *Function) WriteGoCallCode(currentModule *modules.Module, cw *CodeWriter
cw.WriteLine("func " + funcDeclare + " {")
cw.Indent()

returnTypeStr := f.GoReturn(currentModule)
f.writeGoCallParameterPrep(currentModule, cw)

callCode := fmt.Sprintf("C.%s(\n", f.GoName)
var sb strings.Builder
Expand All @@ -164,6 +165,8 @@ func (f *Function) WriteGoCallCode(currentModule *modules.Module, cw *CodeWriter
} else {
sb.WriteString(cw.IndentStr + fmt.Sprintf("(C.%s)(%s)", tt.CName(), p.GoName()))
}
case *typing.CStringType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" %vVal", p.GoName()))
case *typing.RefType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" unsafe.Pointer(%s)", p.GoName()))
case *typing.StructType:
Expand All @@ -179,6 +182,7 @@ func (f *Function) WriteGoCallCode(currentModule *modules.Module, cw *CodeWriter
}
callCode += sb.String() + cw.IndentStr + ")"

returnTypeStr := f.GoReturn(currentModule)
if returnTypeStr == "" {
cw.WriteLine(callCode)
} else {
Expand All @@ -196,6 +200,19 @@ func (f *Function) WriteGoCallCode(currentModule *modules.Module, cw *CodeWriter
cw.WriteLine("}")
}

// writeGoCallParameterPrep generate go code to prepare parameters for c function call
func (f *Function) writeGoCallParameterPrep(currentModule *modules.Module, cw *CodeWriter) {
for _, p := range f.Parameters {
switch p.Type.(type) {
default:
continue
case *typing.CStringType:
cw.WriteLineF("%sVal := C.CString(%v)", p.GoName(), p.GoName())
cw.WriteLineF("defer C.free(unsafe.Pointer(%sVal))", p.GoName())
}
}
}

func hasBlockParam(params []*Param) bool {
for _, p := range params {
if _, ok := p.Type.(*typing.BlockType); ok {
Expand All @@ -211,6 +228,7 @@ func hasBlockParam(params []*Param) bool {
return false
}

// WriteObjcWrapper generate objc wrapper code that maps between C and ObjC.
func (f *Function) WriteObjcWrapper(currentModule *modules.Module, cw *CodeWriter) {
if f.Deprecated {
return
Expand All @@ -226,11 +244,29 @@ func (f *Function) WriteObjcWrapper(currentModule *modules.Module, cw *CodeWrite
}
cw.WriteLineF("%v %v(%v) {", returnTypeStr, f.GoName, f.CArgs(currentModule))
cw.Indent()
var args []string
for _, p := range f.Parameters {
args = append(args, p.Name)
cw.WriteLineF("return (%v)%v(", returnTypeStr, f.Type.Name)
cw.Indent()

for idx, p := range f.Parameters {
cw.WriteLineF("// %T", p.Type)

var conv string
switch tt := p.Type.(type) {
case *typing.PointerType:
conv = tt.ObjcName()
default:
conv = tt.ObjcName()
}
// get conversion to C type
arg := fmt.Sprintf("(%v)%v", conv, p.Name)
if idx < len(f.Parameters)-1 {
arg += ","
}
cw.WriteLineF("%v", arg)
}
cw.WriteLineF("return %v(%v);", f.Type.Name, strings.Join(args, ", "))
//cw.WriteLineF("return (%v)%v(%v);", returnTypeStr, f.Type.Name, strings.Join(args, ", "))
cw.UnIndent()
cw.WriteLine(");")
cw.UnIndent()
cw.WriteLine("}")
}
Expand Down
31 changes: 31 additions & 0 deletions generate/declparse/declparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1153,4 +1153,35 @@ var tests = []struct {
},
},
},
{
ParseOnly: true,
s: "bool CGPDFDocumentUnlockWithPassword(CGPDFDocumentRef document, const char *password);",
Hint: HintFunction,
n: &Statement{
Function: &FunctionDecl{
ReturnType: TypeInfo{
Name: "bool",
},
Name: "CGPDFDocumentUnlockWithPassword",
Args: FuncArgs{
ArgInfo{
Name: "document",
Type: TypeInfo{
Name: "CGPDFDocumentRef",
},
},
ArgInfo{
Name: "password",
Type: TypeInfo{
Name: "char",
IsPtr: true,
Annots: map[TypeAnnotation]bool{
TypeAnnotConst: true,
},
},
},
},
},
},
},
}
20 changes: 18 additions & 2 deletions generate/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ func (db *Generator) ToFunction(fw string, sym Symbol) *codegen.Function {
"OBEXSessionSetServerCallback": true,
"CFSocketCopyRegisteredValue": true,
}
if _, ok := map[string]bool{
//"CGColorSpaceCreateCalibratedGray": true,
"CGPDFDocumentUnlockWithPassword": true,
}[sym.Name]; !ok {
return nil
}
if knownIssues[sym.Name] {
_, err := sym.Parse()
log.Printf("skipping function %s %s because of known issue: decl='%s' err='%v'\n", fw, sym.Name, sym.Declaration, err)
Expand All @@ -260,9 +266,19 @@ func (db *Generator) ToFunction(fw string, sym Symbol) *codegen.Function {
DocURL: sym.DocURL(),
Type: fntyp,
}
// temporary skip for things deprecated in 14.0
// check if macOS platform is DeprecatedAt 14.0
for _, p := range sym.Platforms {
if p.Name == "macOS" && p.Deprecated {
fn.Deprecated = true
}
}

// populate params:
log.Printf("decl: %s\n", sym.Declaration)
log.Printf("params: %+v\n", fntyp.Parameters)
log.Printf("decl: %v %s\n", sym.Name, sym.Declaration)
for i, p := range fntyp.Parameters {
log.Printf(" param %#v: %v %+v\n", i, p.Name, p.Type.ObjcName())
}
for _, p := range fntyp.Parameters {
if p.Type == nil {
fmt.Printf("skipping %s: %s because of nil type\n", sym.Name, p.Name)
Expand Down
48 changes: 31 additions & 17 deletions generate/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generate

import (
"encoding/json"
"fmt"
"log"
"strings"
Expand Down Expand Up @@ -141,6 +142,7 @@ func (db *Generator) TypeFromSymbol(sym Symbol) typing.Type {

}

// ParseType parses a type from a declparse.TypeInfo.
func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
defer func() {
if r := recover(); r != nil {
Expand All @@ -152,18 +154,6 @@ func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
}
}
}()
if ti.Name == "CFURLRef" {
log.Printf("ParseType: %s\n", ti.Name)
log.Printf("info: %v\n", ti)
log.Printf("info fn: %v\n", ti.Func)
defer func() {
log.Printf("info typ: %T %+v\n", typ, typ)
if pt, ok := typ.(*typing.PointerType); ok {
log.Printf("info typ: %T %+v\n", pt.Type, pt.Type)
}

}()
}
if ti.Func != nil {
var blockParams []typing.BlockParam
for _, arg := range ti.Func.Args {
Expand Down Expand Up @@ -206,8 +196,27 @@ func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
case "Class":
// objc type
typ = typing.Class
case "CGFloat", "Float64":
case "off_t":
j, _ := json.Marshal(ti)
fmt.Println(string(j))

// to kernel type
typ = typing.Int
case "float", "CGFloat", "Float64":
typ = typing.Float
case "char":
j, _ := json.Marshal(ti)
fmt.Println(string(j))

cTyp := "char"
if ti.IsPtr {
typ = &typing.CStringType{}
} else {
typ = &typing.PrimitiveType{
GoName_: "byte",
ObjcName_: cTyp,
}
}
case "NSString":
typ = &typing.StringType{}
ref = true
Expand Down Expand Up @@ -243,15 +252,15 @@ func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
default:
var ok bool
typ, ok = typing.GetPrimitiveType(ti.Name)
log.Println("primitive", ti.Name, ok)
// log.Println("primitive", ti.Name, ok)
if !ok {
typ, ok = typing.GetDispatchType(ti.Name)
}
log.Println("dispatch", ti.Name, ok)
// log.Println("dispatch", ti.Name, ok)
if !ok {
typ, ok = typing.GetKernelType(ti.Name)
}
log.Println("kernel", ti.Name, ok)
// log.Println("kernel", ti.Name, ok)
if !ok {
typ = db.TypeFromSymbolName(ti.Name)
log.Println("symbol", ti.Name, typ, ok)
Expand All @@ -266,6 +275,10 @@ func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
}
}

if _, ok := typ.(*typing.CStringType); ok {
return typ
}

if ti.IsPtr && !ref {
if _, ok := typ.(*typing.VoidType); ok {
typ = &typing.VoidPointerType{}
Expand All @@ -274,7 +287,8 @@ func (db *Generator) ParseType(ti declparse.TypeInfo) (typ typing.Type) {
panic("nil type")
}
typ = &typing.PointerType{
Type: typ,
Type: typ,
IsConst: ti.Annots[declparse.TypeAnnotConst],
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions generate/typing/cstring_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package typing

import (
"github.com/progrium/macdriver/generate/modules"
"github.com/progrium/macdriver/internal/set"
)

var _ Type = (*CStringType)(nil)

// CStringType string
type CStringType struct {
IsConst bool
}

func (s *CStringType) GoImports() set.Set[string] {
return nil
}

func (s *CStringType) GoName(currentModule *modules.Module, receiveFromObjc bool) string {
return "string"
}

func (s *CStringType) ObjcName() string {
return "char*"
}

func (s *CStringType) CName() string {
return "char*"
}

func (c *CStringType) CSignature() string {
t := c.CName()
if c.IsConst {
t = "const " + t
}
return t
}

func (s *CStringType) DeclareModule() *modules.Module {
return nil
}
11 changes: 9 additions & 2 deletions generate/typing/pointer_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ var _ Type = (*PointerType)(nil)

// PointerType is c pointer type
type PointerType struct {
Type Type
Type Type
IsConst bool
}

func (c *PointerType) GoImports() set.Set[string] {
Expand All @@ -30,6 +31,8 @@ func (c *PointerType) GoName(currentModule *modules.Module, receiveFromObjc bool
return "*" + (&ClassType{Name: "NSArray", GName: "Array", Module: modules.Get("foundation")}).GoName(currentModule, true)
case *DictType:
return "*" + (&ClassType{Name: "NSDictionary", GName: "Dictionary", Module: modules.Get("foundation")}).GoName(currentModule, true)
case *PointerType:
return "*" + c.Type.GoName(currentModule, receiveFromObjc)
default:
panic("not supported pointer to: " + c.Type.ObjcName())
}
Expand All @@ -45,7 +48,11 @@ func (c *PointerType) CName() string {
}

func (c *PointerType) CSignature() string {
return c.Type.ObjcName() + "*"
t := c.Type.ObjcName() + "*"
if c.IsConst {
t = "const " + t
}
return t
}

func (c *PointerType) DeclareModule() *modules.Module {
Expand Down
3 changes: 2 additions & 1 deletion generate/typing/primitive_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ var Bool = &PrimitiveType{GoName_: "bool", ObjcName_: "BOOL"}
var Int = &PrimitiveType{GoName_: "int", ObjcName_: "NSInteger"}
var UInt = &PrimitiveType{GoName_: "uint", ObjcName_: "NSUInteger"}

var Float = &PrimitiveType{GoName_: "float64", ObjcName_: "float"}
var Float = &PrimitiveType{GoName_: "float64", ObjcName_: "CGFloat"}
var Double = &PrimitiveType{GoName_: "float64", ObjcName_: "double"}

var Int8 = &PrimitiveType{GoName_: "int8", ObjcName_: "int8_t"}
var UInt8 = &PrimitiveType{GoName_: "uint8", ObjcName_: "uint8_t"}
var Byte = &PrimitiveType{GoName_: "byte", ObjcName_: "char"}
var OffT = &PrimitiveType{GoName_: "float64", ObjcName_: "off_t"}

var Int16 = &PrimitiveType{GoName_: "int16", ObjcName_: "int16_t"}
var UInt16 = &PrimitiveType{GoName_: "uint16", ObjcName_: "uint16_t"}
Expand Down
2 changes: 1 addition & 1 deletion generate/typing/string_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var _ Type = (*StringType)(nil)

// StringType string
type StringType struct {
NeedNil bool // string type need nil value.If set to true, will use foundation.String instread string for generated go code.
NeedNil bool // string type need nil value.If set to true, will use foundation.String instead string for generated go code.
}

func (s *StringType) GoImports() set.Set[string] {
Expand Down

0 comments on commit b8d925b

Please sign in to comment.