Skip to content

Commit 01aba91

Browse files
committed
generate: Add function support
1 parent 85c1d2d commit 01aba91

File tree

4 files changed

+392
-0
lines changed

4 files changed

+392
-0
lines changed

generate/codegen/gen_function.go

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
package codegen
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/progrium/macdriver/internal/set"
8+
"github.com/progrium/macdriver/internal/stringx"
9+
10+
"github.com/progrium/macdriver/generate/modules"
11+
"github.com/progrium/macdriver/generate/typing"
12+
)
13+
14+
// Function is code generator for objective-c function
15+
type Function struct {
16+
Type *typing.FunctionType
17+
Name string // the first part of objc function name
18+
GoName string
19+
Params []*Param
20+
ReturnType typing.Type
21+
Deprecated bool // if has been deprecated
22+
Suffix bool // GoName conflicts so add suffix to this function
23+
Description string
24+
DocURL string
25+
26+
goFuncName string
27+
identifier string
28+
}
29+
30+
func (f *Function) Init() {
31+
}
32+
33+
func (f *Function) WriteGoCode(cw *CodeWriter) {
34+
panic("implement me")
35+
}
36+
37+
// Copy for copy fetcher cache value
38+
func (f *Function) Copy() CodeGen {
39+
if f == nil {
40+
return nil
41+
}
42+
return &Function{
43+
Type: f.Type,
44+
Name: f.Name,
45+
GoName: f.GoName,
46+
Params: f.Params,
47+
ReturnType: f.ReturnType,
48+
Deprecated: f.Deprecated,
49+
Suffix: f.Suffix,
50+
Description: f.Description,
51+
DocURL: f.DocURL,
52+
goFuncName: f.goFuncName,
53+
identifier: f.identifier,
54+
}
55+
}
56+
57+
func (m *Function) needRelease() bool {
58+
switch m.ReturnType.(type) {
59+
case *typing.PrimitiveType, *typing.StringType:
60+
return false
61+
}
62+
return strings.HasPrefix(m.Name, "new") || !strings.HasPrefix(m.Name, "init") && strings.HasPrefix(m.Name, "Initial") ||
63+
strings.HasPrefix(m.Name, "copy") || strings.HasPrefix(m.Name, "mutableCopy")
64+
}
65+
66+
// Selector return full Objc function name
67+
func (m *Function) Selector() string {
68+
if m.identifier == "" {
69+
var sb strings.Builder
70+
sb.WriteString(m.Name)
71+
for idx, p := range m.Params {
72+
if idx > 0 {
73+
sb.WriteString(p.FieldName)
74+
}
75+
sb.WriteString(":")
76+
}
77+
m.identifier = sb.String()
78+
}
79+
return m.identifier
80+
}
81+
82+
func (m *Function) String() string {
83+
return m.Selector()
84+
}
85+
86+
// NormalizeInstanceTypeFunction return new init function.
87+
func (m *Function) NormalizeInstanceTypeFunction(returnType *typing.ClassType) *Function {
88+
nm := &Function{
89+
Name: m.Name,
90+
GoName: m.GoName,
91+
Params: m.Params,
92+
ReturnType: returnType,
93+
goFuncName: m.goFuncName,
94+
Suffix: m.Suffix,
95+
}
96+
return nm
97+
}
98+
99+
// WriteGoCallCode generate go function code to call c wrapper code
100+
func (m *Function) WriteGoCallCode(currentModule *modules.Module, typeName string, cw *CodeWriter) {
101+
funcDeclare := m.GoFuncDeclare(currentModule, typeName)
102+
103+
if m.Deprecated {
104+
return
105+
cw.WriteLine("// deprecated")
106+
}
107+
108+
if m.DocURL != "" {
109+
cw.WriteLine(fmt.Sprintf("// %s [Full Topic]", m.Description))
110+
cw.WriteLine(fmt.Sprintf("//\n// [Full Topic]: %s", m.DocURL))
111+
}
112+
113+
var receiver string
114+
receiver = strings.ToLower(typeName[0:1] + "_")
115+
cw.WriteLine("func (" + receiver + " " + typeName + ") " + funcDeclare + " {")
116+
117+
cw.Indent()
118+
119+
var returnTypeStr string
120+
rt := typing.UnwrapAlias(m.ReturnType)
121+
switch rt.(type) {
122+
case *typing.VoidType:
123+
returnTypeStr = "objc.Void"
124+
default:
125+
returnTypeStr = m.ReturnType.GoName(currentModule, true)
126+
}
127+
callCode := fmt.Sprintf("objc.Call[%s](%s, objc.Sel(\"%s\")", returnTypeStr, receiver, m.Selector())
128+
var sb strings.Builder
129+
for idx, p := range m.Params {
130+
sb.WriteString(", ")
131+
switch tt := p.Type.(type) {
132+
case *typing.ClassType:
133+
sb.WriteString("objc.Ptr(" + p.GoName() + ")")
134+
case *typing.ProtocolType:
135+
pvar := fmt.Sprintf("po%d", idx)
136+
cw.WriteLineF("%s := objc.WrapAsProtocol(\"%s\", %s)", pvar, tt.Name, p.GoName())
137+
sb.WriteString(pvar)
138+
case *typing.PointerType:
139+
switch tt.Type.(type) {
140+
case *typing.ClassType: //object pointer convert to unsafe.Pointer, avoiding ffi treat it as PointerHolder
141+
sb.WriteString(fmt.Sprintf("unsafe.Pointer(%s)", p.GoName()))
142+
default:
143+
sb.WriteString(p.GoName())
144+
}
145+
default:
146+
sb.WriteString(p.GoName())
147+
}
148+
}
149+
callCode += sb.String() + ")"
150+
151+
switch rt.(type) {
152+
case *typing.VoidType:
153+
cw.WriteLine(callCode)
154+
default:
155+
var resultName = "rv"
156+
cw.WriteLine(resultName + " := " + callCode)
157+
if m.needRelease() {
158+
cw.WriteLine(resultName + ".Autorelease()")
159+
}
160+
cw.WriteLine("return " + resultName)
161+
}
162+
cw.UnIndent()
163+
cw.WriteLine("}")
164+
}
165+
166+
// WriteGoInterfaceCode generate go interface function signature code
167+
func (f *Function) WriteGoInterfaceCode(currentModule *modules.Module, classType *typing.ClassType, w *CodeWriter) {
168+
if f.Deprecated {
169+
return
170+
w.WriteLine("// deprecated")
171+
}
172+
funcDeclare := f.GoFuncDeclare(currentModule, classType.GName)
173+
w.WriteLine(funcDeclare)
174+
}
175+
176+
// GoFuncDeclare generate go function declaration
177+
func (f *Function) GoFuncDeclare(currentModule *modules.Module, goTypeName string) string {
178+
var paramStrs []string
179+
for _, p := range f.Params {
180+
paramStrs = append(paramStrs, p.GoDeclare(currentModule, false))
181+
}
182+
183+
var returnType = f.ReturnType.GoName(currentModule, true)
184+
return f.GoFuncName() + "(" + strings.Join(paramStrs, ", ") + ")" + " " + returnType
185+
}
186+
187+
// GoFuncName return go func name
188+
func (f *Function) GoFuncName() string {
189+
if f.goFuncName == "" {
190+
var sb strings.Builder
191+
name := f.GoName
192+
if len(f.Params) == 0 {
193+
sb.WriteString(stringx.Capitalize(name))
194+
}
195+
196+
for _, p := range f.Params {
197+
sb.WriteString(stringx.Capitalize(p.FieldName))
198+
if p.Object {
199+
sb.WriteString("Object")
200+
}
201+
}
202+
203+
f.goFuncName = sb.String()
204+
}
205+
if f.Suffix || f.goFuncName == "Object" {
206+
return f.goFuncName + "_"
207+
}
208+
return f.goFuncName
209+
}
210+
211+
// ProtocolGoFuncFieldType generate go function declaration for protocol struct impl field
212+
func (f *Function) ProtocolGoFuncFieldType(currentModule *modules.Module) string {
213+
var paramStrs []string
214+
for _, p := range f.Params {
215+
paramStrs = append(paramStrs, p.GoDeclare(currentModule, true))
216+
}
217+
218+
return "(" + strings.Join(paramStrs, ", ") + ")" + " " + f.ReturnType.GoName(currentModule, false)
219+
}
220+
221+
// ProtocolGoFuncName return go protocol func name
222+
func (f *Function) ProtocolGoFuncName() string {
223+
if f.goFuncName == "" {
224+
var sb strings.Builder
225+
sb.WriteString(stringx.Capitalize(f.Name))
226+
for idx, p := range f.Params {
227+
if idx == 0 {
228+
continue
229+
}
230+
sb.WriteString(stringx.Capitalize(p.FieldName))
231+
if p.Object {
232+
sb.WriteString("Object")
233+
}
234+
}
235+
236+
f.goFuncName = sb.String()
237+
}
238+
if f.Suffix {
239+
return f.goFuncName + "_"
240+
}
241+
return f.goFuncName
242+
}
243+
244+
// GoImports return all imports for go file
245+
func (f *Function) GoImports() set.Set[string] {
246+
var imports = set.New("github.com/progrium/macdriver/objc")
247+
for _, param := range f.Params {
248+
imports.AddSet(param.Type.GoImports())
249+
}
250+
if f.ReturnType != nil {
251+
imports.AddSet(f.ReturnType.GoImports())
252+
}
253+
return imports
254+
}
255+
256+
func (f *Function) HasProtocolParam() bool {
257+
for _, p := range f.Params {
258+
switch p.Type.(type) {
259+
case *typing.ProtocolType:
260+
return true
261+
}
262+
}
263+
return false
264+
}
265+
266+
func (f *Function) ToProtocolParamAsObjectFunction() *Function {
267+
var newParams = make([]*Param, len(f.Params))
268+
for i, p := range f.Params {
269+
switch p.Type.(type) {
270+
case *typing.ProtocolType:
271+
newParams[i] = &Param{
272+
Name: p.Name,
273+
Type: typing.Object,
274+
FieldName: p.FieldName,
275+
Object: true,
276+
}
277+
default:
278+
newParams[i] = p
279+
}
280+
}
281+
return &Function{
282+
Name: f.Name,
283+
GoName: f.GoName,
284+
Params: newParams,
285+
Suffix: f.Suffix,
286+
ReturnType: f.ReturnType,
287+
Deprecated: f.Deprecated,
288+
Description: f.Description,
289+
DocURL: f.DocURL,
290+
}
291+
}

generate/function.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package generate
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/progrium/macdriver/generate/codegen"
7+
"github.com/progrium/macdriver/generate/modules"
8+
"github.com/progrium/macdriver/generate/typing"
9+
)
10+
11+
func (db *Generator) ToFunctionGen(fw string, sym Symbol) *codegen.Function {
12+
if db.genCache == nil {
13+
db.genCache = make(map[string]codegen.CodeGen)
14+
}
15+
key := fmt.Sprintf("%s.%s", fw, sym.Name)
16+
fg, ok := db.genCache[key]
17+
fmt.Printf("function: %s\n", key, fg, ok)
18+
19+
type_ := &typing.FunctionType{
20+
Name: sym.Name,
21+
GName: modules.TrimPrefix(sym.Name),
22+
Module: modules.Get(fw),
23+
}
24+
functionGen := &codegen.Function{
25+
Description: sym.Description,
26+
DocURL: sym.DocURL(),
27+
Type: type_,
28+
}
29+
30+
db.genCache[key] = functionGen
31+
return functionGen
32+
33+
}

generate/generator.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type Generator struct {
2121
genCache map[string]codegen.CodeGen
2222
}
2323

24+
// Generate generates the code for the given platform, version, and framework
2425
func (db *Generator) Generate(platform string, version int, rootDir string, framework string, ignoreTypes set.Set[string]) {
2526
db.Platform = platform
2627
db.Version = version
@@ -105,6 +106,22 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram
105106
})
106107
continue
107108
}
109+
case "Function":
110+
functionGen := db.ToFunctionGen(framework, s)
111+
if functionGen == nil {
112+
log.Println("skipping function", s.Name)
113+
continue
114+
}
115+
functionGen.Init()
116+
fw := &codegen.FileWriter{
117+
Name: s.Name,
118+
Module: *functionGen.Type.Module,
119+
PlatformDir: rootDir,
120+
}
121+
fw.Add(functionGen)
122+
fw.WriteCode()
123+
default:
124+
log.Println("skipping", s.Kind, s.Name)
108125
}
109126
}
110127
mw.WriteCode()

0 commit comments

Comments
 (0)