Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functions: Add support for functions #193

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
98308fe
generate: Add function support
tmc Aug 20, 2023
0ec752c
generate: iterate on supporting functions
tmc Aug 20, 2023
84b66dd
generate: iterate on function support
tmc Aug 20, 2023
55e7c12
generate: Progress on functions;
tmc Aug 20, 2023
50e1358
generate: add partial struct support
tmc Aug 21, 2023
725c96b
generate: Generate Ref aliases for structs
tmc Aug 21, 2023
e9c3816
coregraphics: Hand-assemble call
tmc Aug 21, 2023
02c63fd
generate: Add CName to aid in translation to and from c
tmc Aug 21, 2023
eb559c5
codegen: cleanup
tmc Aug 21, 2023
2e5a56b
generate: iterate on function support
tmc Aug 21, 2023
7f162f0
coregraphics: Clean up overlapping parts
tmc Sep 3, 2023
29856d9
coregraphics: Add basic test
tmc Sep 3, 2023
1052597
generate: Progress on functions
tmc Sep 3, 2023
76f6c45
generate: Further iteration on function support
tmc Sep 3, 2023
b13b9f4
generate: Further iteration on function support
tmc Sep 3, 2023
f16e18e
generate: Further iteration on function support
tmc Sep 3, 2023
b0b7296
generate: Populate deprecated flag
tmc Sep 3, 2023
b32ad1c
iter
tmc Sep 3, 2023
5dcbd09
generate: Further progress on function support, add basic cstring han…
tmc Sep 3, 2023
74cc497
generate: Further progress on function and struct support
tmc Sep 3, 2023
74611e2
generate: Further progress on function and struct support
tmc Sep 3, 2023
3fd06bf
codegen: Ad struct alias support
tmc Sep 4, 2023
c467a4a
generate: Add handling of alias pointers
tmc Sep 4, 2023
283d52a
generate: Clean up some cruft in function calling support
tmc Sep 4, 2023
7720ce8
generate: Clean up some cruft in function calling support
tmc Sep 4, 2023
902e8a4
generate: Expand function support, enhance skip lists
tmc Sep 4, 2023
0d3ec16
generate: be a bit more specific about types and functions to skip
tmc Sep 4, 2023
cf4fd46
generate: Iterate on function support
tmc Sep 4, 2023
4e70fcd
xx
tmc Sep 4, 2023
77d8f1d
generate: place mpsgraph and mps earlier in the list to be found first
tmc Sep 4, 2023
c1af86d
wip: metal support
tmc Sep 5, 2023
c07de15
generate: Add some basic improve support for protocol types
tmc Sep 5, 2023
a187eb5
generate: improve support for protocol type handling
tmc Sep 5, 2023
645e013
generate: render current result
tmc Sep 5, 2023
102ecf9
codegen: Handle a few more cases, populate some mps structs
tmc Sep 7, 2023
8fe2080
metal: Check if returned value responds to selector
tmc Sep 7, 2023
6407548
macos: regenerate
tmc Sep 10, 2023
013a9f2
macos: regenerate
tmc Sep 10, 2023
39c8509
mps: Add additional structs
tmc Sep 7, 2023
8555133
mps: Remove test for now as direct matrix creation is not supported
tmc Sep 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
333 changes: 333 additions & 0 deletions generate/codegen/gen_function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
package codegen

import (
"fmt"
"strings"

"github.com/progrium/macdriver/internal/set"

"github.com/progrium/macdriver/generate/modules"
"github.com/progrium/macdriver/generate/typing"
)

// Function is code generator for objective-c (and c) functions.
type Function struct {
Type *typing.FunctionType
Name string // the first part of objc function name
GoName string
Parameters []*Param
ReturnType typing.Type
Deprecated bool // if has been deprecated
Suffix bool // GoName conflicts so add suffix to this function
Description string
DocURL string

goFuncName string
identifier string
}

var reservedWords = map[string]bool{
"func": true,
"map": true,
"new": true,
"var": true,
"len": true,
"copy": true,
"range": true,
"type": true,
"string": true,
}

// list of fixups for types that are not properly mapped
// ideally we shorten this list over time
var goTypeFixupMap = map[string]string{
"*kernel.Boolean_t": "*int",
"*kernel.Mode_t": "*int",
"*kernel.Uid_t": "*int",
"*kernel.Gid_t": "*int",
"*kernel.UniChar": "*uint16",
"CGFloat": "float64",
"kernel.Boolean_t": "int",
"kernel.Cpu_type_t": "int",
"kernel.Gid_t": "int",
"kernel.Mode_t": "int",
"kernel.Pid_t": "int32",
"kernel.Uid_t": "int",
"kernel.UniChar": "uint16",
"uint8_t": "byte",
}

// GoArgs return go function args
func (f *Function) GoArgs(currentModule *modules.Module) string {
var args []string
var blankArgCounter = 0
for _, p := range f.Parameters {
// if is reserved word, add _ suffix
if p.Name == "" {
p.Name = fmt.Sprintf("arg%d", blankArgCounter)
blankArgCounter++
}
if _, ok := reservedWords[p.Name]; ok {
p.Name = p.Name + "_"
}
typ := p.Type.GoName(currentModule, true)
if v, ok := goTypeFixupMap[typ]; ok {
typ = v
}
args = append(args, fmt.Sprintf("%s %s", p.Name, typ))
}
return strings.Join(args, ", ")
}

// GoReturn return go function return
func (f *Function) GoReturn(currentModule *modules.Module) string {
if f.ReturnType == nil {
return ""
}
typ := f.ReturnType.GoName(currentModule, true)
if v, ok := goTypeFixupMap[typ]; ok {
typ = v
}
return typ
}

// CArgs return go function args
func (f *Function) CArgs(currentModule *modules.Module) string {
var args []string
for _, p := range f.Parameters {
typ := p.Type.CName()
if cs, ok := p.Type.(hasCSignature); ok {
typ = cs.CSignature()
}
// check reserved words
if _, ok := reservedWords[p.Name]; ok {
p.Name = p.Name + "_"
}
args = append(args, fmt.Sprintf("%s %s", typ, p.Name))

}
return strings.Join(args, ", ")
}

// Selector return full Objc function name
func (f *Function) Selector() string {
if f.identifier == "" {
var sb strings.Builder
sb.WriteString(f.Name)
for idx, p := range f.Parameters {
if idx > 0 {
sb.WriteString(p.FieldName)
}
sb.WriteString(":")
}
f.identifier = sb.String()
}
return f.identifier
}

func (f *Function) String() string {
return f.Selector()
}

// WriteGoCallCode generate go function code to call c wrapper code
func (f *Function) WriteGoCallCode(currentModule *modules.Module, cw *CodeWriter) {
funcDeclare := f.GoFuncDeclare(currentModule)

if f.Deprecated {
cw.WriteLine("// deprecated")
return
}

if hasBlockParam(f.Parameters) {
cw.WriteLineF("// // TODO: %v not implemented (missing block param support)", f.Name)
return
}

if f.DocURL != "" {
cw.WriteLine(fmt.Sprintf("// %s [Full Topic]", f.Description))
cw.WriteLine(fmt.Sprintf("//\n// [Full Topic]: %s", f.DocURL))
}

cw.WriteLine("func " + funcDeclare + " {")
cw.Indent()

f.writeGoCallParameterPrep(currentModule, cw)

callCode := fmt.Sprintf("C.%s(\n", f.GoName)
var sb strings.Builder
for _, p := range f.Parameters {
// cast to C type
sb.WriteString(fmt.Sprintf(cw.IndentStr+" // %T\n", p.Type))
typ := p.Type
switch tt := typ.(type) {
case *typing.AliasType:
sb.WriteString(fmt.Sprintf(cw.IndentStr+" // %T\n", tt.Type))
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:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" *(*C.%s)(unsafe.Pointer(&%s))", tt.CName(), p.GoName()))
case *typing.PrimitiveType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" C.%s(%s)", tt.CName(), p.GoName()))
case *typing.PointerType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" (*C.%s)(unsafe.Pointer(&%s))", tt.CName(), p.GoName()))
case *typing.DispatchType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" (*C.%s)(unsafe.Pointer(&%s))", tt.CName(), p.GoName()))
case *typing.IDType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" %s.Ptr()", p.GoName()))
case *typing.ClassType, *typing.ProtocolType:
sb.WriteString(cw.IndentStr + fmt.Sprintf(" unsafe.Pointer(&%s)", p.GoName()))
default:
sb.WriteString(cw.IndentStr + p.GoName())
}
sb.WriteString(",\n")
}
callCode += sb.String() + cw.IndentStr + ")"

returnTypeStr := f.GoReturn(currentModule)
if returnTypeStr == "" {
cw.WriteLine(callCode)
} else {
var resultName = "rv"
cw.WriteLine(resultName + " := " + callCode)
cw.WriteLineF("// %T", f.ReturnType)
switch tt := f.ReturnType.(type) {
case *typing.StructType, *typing.PointerType:
cw.WriteLineF("return *(*%s)(unsafe.Pointer(&%s))", tt.GoName(currentModule, true), resultName)
case *typing.CStringType:
cw.WriteLineF("return C.GoString(%s)", resultName)
case *typing.ProtocolType:
cw.WriteLineF("return %s{objc.ObjectFrom(%s)}", returnTypeStr, resultName)
case *typing.AliasType:
cw.WriteLineF("return *(*%s)(unsafe.Pointer(&%s))", returnTypeStr, resultName)
default:
cw.WriteLineF("return %s(%s)", returnTypeStr, resultName)
}
}
cw.UnIndent()
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 {
return true
}
if pt, ok := p.Type.(*typing.AliasType); ok {
t := typing.UnwrapAlias(pt.Type)
if _, ok := t.(*typing.BlockType); ok {
return true
}
}
}
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
cw.WriteLine("// deprecated")
}
if hasBlockParam(f.Parameters) {
cw.WriteLineF("// // TODO: %v not implemented (missing block param support)", f.Name)
return
}
returnTypeStr := f.Type.ReturnType.CName()
if cs, ok := f.Type.ReturnType.(hasCSignature); ok {
returnTypeStr = cs.CSignature()
}
cw.WriteLineF("%v %v(%v) {", returnTypeStr, f.GoName, f.CArgs(currentModule))
cw.Indent()
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:
cw.WriteLineF("// -> %T", tt.Type)
conv = tt.ObjcName()
// case *typing.AliasType:
// 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(%v);", returnTypeStr, f.Type.Name, strings.Join(args, ", "))
cw.UnIndent()
cw.WriteLine(");")
cw.UnIndent()
cw.WriteLine("}")
}

type hasCSignature interface {
CSignature() string
}

func (f *Function) WriteCSignature(currentModule *modules.Module, cw *CodeWriter) {
var returnTypeStr string
rt := f.Type.ReturnType
returnTypeStr = rt.CName()
// check for CSignature:
if cs, ok := rt.(hasCSignature); ok {
returnTypeStr = cs.CSignature()
}

if hasBlockParam(f.Parameters) {
cw.WriteLineF("// // TODO: %v not implemented (missing block param support)", f.Name)
return
}
cw.WriteLineF("// %v %v(%v); ", returnTypeStr, f.GoName, f.CArgs(currentModule))
}

// WriteGoInterfaceCode generate go interface function signature code
func (f *Function) WriteGoInterfaceCode(currentModule *modules.Module, classType *typing.ClassType, w *CodeWriter) {
if f.Deprecated {
return
w.WriteLine("// deprecated")
}
funcDeclare := f.GoFuncDeclare(currentModule)
w.WriteLine(funcDeclare)
}

// GoFuncDeclare generate go function declaration
func (f *Function) GoFuncDeclare(currentModule *modules.Module) string {
var returnType = f.GoReturn(currentModule)
return f.Type.GoName(currentModule, true) + "(" + f.GoArgs(currentModule) + ") " + returnType
}

// GoImports return all imports for go file
func (f *Function) GoImports() set.Set[string] {
var imports = set.New("github.com/progrium/macdriver/objc")
for _, param := range f.Parameters {
imports.AddSet(param.Type.GoImports())
}
if f.ReturnType != nil {
imports.AddSet(f.ReturnType.GoImports())
}
return imports
}
19 changes: 19 additions & 0 deletions generate/codegen/gen_struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package codegen

import (
"github.com/progrium/macdriver/generate/typing"
)

// Struct is code generator for objective-c struct
type Struct struct {
Type typing.Type
Name string // the first part of objc function name
GoName string
Deprecated bool // if has been deprecated
Suffix bool // GoName conflicts so add suffix to this function
Description string
DocURL string

goFuncName string
identifier string
}