-
-
Notifications
You must be signed in to change notification settings - Fork 160
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
tmc
wants to merge
32
commits into
progrium:main
Choose a base branch
from
tmc:functions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
72bc506
generate: Add function support
tmc 199c402
generate: Add flagging to struct and function generation
tmc 5abf36d
generate: iterate on supporting functions
tmc 402fa4a
generate: iterate on function support
tmc 89b648e
generate: Progress on functions;
tmc 1b3a438
generate: add partial struct support
tmc d805f17
generate: remove panic on nil type
tmc ea32ec1
generate: Generate Ref aliases for structs
tmc 2464bef
coregraphics: Hand-assemble call
tmc d19847f
generate: Add CName to aid in translation to and from c
tmc cb697cd
codegen: cleanup
tmc 789e606
generate: Reflect darwinkit rename
tmc 8f2666c
generate: iterate on function support
tmc 3dfa06c
generate: Progress on functions
tmc c28de5e
generate: Further iteration on function support
tmc 7058276
generate: Further iteration on function support
tmc e7c25fd
generate: Further iteration on function support
tmc f163b31
generate: Populate deprecated flag
tmc 11efdab
generate: Further progress on function support, add basic cstring han…
tmc bbaac76
generate: Further progress on function and struct support
tmc fcb0dd2
codegen: Ad struct alias support
tmc 6aadb23
generate: Add handling of alias pointers
tmc cad5b26
generate: Clean up some cruft in function calling support
tmc 79403a2
generate: Clean up some cruft in function calling support
tmc 008d2a4
generate: Expand function support, enhance skip lists
tmc c94bfaf
generate: be a bit more specific about types and functions to skip
tmc 30e3972
generate: Iterate on function support
tmc 0d9358c
generate: place mpsgraph and mps earlier in the list to be found first
tmc 9fafbd6
generate: Add some basic improve support for protocol types
tmc a0f1afa
generate: improve support for protocol type handling
tmc a35a378
codegen: Handle a few more cases, populate some mps structs
tmc bc8d39d
codegen: drop explicit skips
tmc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,333 @@ | ||
package codegen | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/progrium/darwinkit/internal/set" | ||
|
||
"github.com/progrium/darwinkit/generate/modules" | ||
"github.com/progrium/darwinkit/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/darwinkit/objc") | ||
for _, param := range f.Parameters { | ||
imports.AddSet(param.Type.GoImports()) | ||
} | ||
if f.ReturnType != nil { | ||
imports.AddSet(f.ReturnType.GoImports()) | ||
} | ||
return imports | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package codegen | ||
|
||
import ( | ||
"github.com/progrium/darwinkit/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 | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think since you originally submitted this i added a struct generator and we should be generating all of them for the frameworks we have: https://github.com/progrium/darwinkit/blob/main/generate/tools/structs.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh great, thanks, happy to cull this.