-
Notifications
You must be signed in to change notification settings - Fork 0
/
proto.go
140 lines (129 loc) · 2.42 KB
/
proto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package main
import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"sort"
)
type Parameter struct {
Name string
Size int
IsFloat bool
}
type Function struct {
Name string
Args []*Parameter
Ret *Parameter
}
func (f *Function) ArgsSize() (ret int) {
add := func(sz int, align bool) {
ret = (ret + sz - 1) &^ (sz - 1)
if !align {
ret += sz
}
}
for _, v := range f.Args {
add(v.Size, false)
}
add(8, true)
if f.Ret != nil {
add(f.Ret.Size, false)
}
add(8, true)
return
}
type Functions []*Function
func paramParse(args *ast.FieldList) (ret []*Parameter, err error) {
// void result only
if args == nil {
return []*Parameter{nil}, nil
}
for _, v := range args.List {
if len(v.Names) == 0 {
err = errors.New("need parameter name")
return
}
var sz int
var fp bool
switch t := v.Type.(type) {
case *ast.StarExpr:
// pointer
sz = 8
case *ast.SelectorExpr:
// unsafe.Pointer
if n, ok := t.X.(*ast.Ident); ok && n.Name == "unsafe" && t.Sel.Name == "Pointer" {
sz = 8
}
case *ast.Ident:
switch t.Name {
case "int8", "uint8", "byte", "bool":
sz = 1
case "int16", "uint16":
sz = 2
case "float32":
sz, fp = 4, true
case "int32", "uint32", "rune":
sz = 4
case "float64":
sz, fp = 8, true
case "int64", "uint64", "uintptr", "int", "Pointer":
sz = 8
}
}
if sz == 0 {
err = fmt.Errorf("unsupport parameter: %v", v.Type)
return
}
for _, name := range v.Names {
ret = append(ret, &Parameter{
Name: name.Name,
Size: sz,
IsFloat: fp,
})
}
}
return
}
func protoParse(fpath string) (ret Functions, pkg string, err error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, nil, 0)
if err != nil {
return
}
pkg = f.Name.Name
for _, v := range f.Decls {
fd, ok := v.(*ast.FuncDecl)
if !ok {
continue
}
if fd.Recv != nil {
err = fmt.Errorf("not support method: %s", fd.Name.Name)
return
}
if fd.Type.Results.NumFields() > 1 {
err = fmt.Errorf("not support multi results: %s", fd.Name.Name)
return
}
args, err1 := paramParse(fd.Type.Params)
if err1 != nil {
err = err1
return
}
res, err1 := paramParse(fd.Type.Results)
if err1 != nil {
err = err1
return
}
ret = append(ret, &Function{
Name: fd.Name.Name,
Args: args,
Ret: res[0],
})
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].Name < ret[j].Name
})
return
}