17
17
package main
18
18
19
19
import (
20
+ "errors"
20
21
"fmt"
21
22
"io"
22
23
"os"
24
+ "path/filepath"
25
+ "strconv"
26
+ "strings"
27
+ "unsafe"
23
28
24
29
"github.com/goplus/llgo/c"
25
30
"github.com/goplus/llgo/c/cjson"
31
+ "github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
32
+ "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
33
+ "github.com/goplus/llgo/chore/llcppg/types"
34
+ "github.com/goplus/llgo/cpp/llvm"
35
+ "github.com/goplus/llgo/xtool/nm"
26
36
)
27
37
28
38
func main () {
@@ -40,18 +50,256 @@ func main() {
40
50
}
41
51
check (err )
42
52
43
- conf := cjson .ParseBytes (data )
44
- if conf == nil {
53
+ conf , err := config .GetConf (data )
54
+ check (err )
55
+ defer conf .Delete ()
56
+
57
+ if err != nil {
45
58
fmt .Fprintln (os .Stderr , "Failed to parse config file:" , cfgFile )
46
- os .Exit (1 )
47
59
}
48
- defer conf .Delete ( )
60
+ symbols , err := parseDylibSymbols ( conf .Libs )
49
61
50
- c .Printf (c .Str ("%s" ), conf .Print ())
62
+ check (err )
63
+
64
+ filepaths := generateHeaderFilePath (conf .CFlags , conf .Include )
65
+ astInfos , err := parse .ParseHeaderFile (filepaths )
66
+ check (err )
67
+
68
+ symbolInfo := getCommonSymbols (symbols , astInfos , conf .TrimPrefixes )
69
+
70
+ err = genSymbolTableFile (symbolInfo )
71
+ check (err )
51
72
}
52
73
53
74
func check (err error ) {
54
75
if err != nil {
55
76
panic (err )
56
77
}
57
78
}
79
+
80
+ func parseDylibSymbols (lib string ) ([]types.CPPSymbol , error ) {
81
+ dylibPath , err := generateDylibPath (lib )
82
+ if err != nil {
83
+ return nil , errors .New ("failed to generate dylib path" )
84
+ }
85
+
86
+ files , err := nm .New ("" ).List (dylibPath )
87
+ if err != nil {
88
+ return nil , errors .New ("failed to list symbols in dylib" )
89
+ }
90
+
91
+ var symbols []types.CPPSymbol
92
+
93
+ for _ , file := range files {
94
+ for _ , sym := range file .Symbols {
95
+ demangleName := decodeSymbolName (sym .Name )
96
+ symbols = append (symbols , types.CPPSymbol {
97
+ Symbol : sym ,
98
+ DemangleName : demangleName ,
99
+ })
100
+ }
101
+ }
102
+
103
+ return symbols , nil
104
+ }
105
+
106
+ func generateDylibPath (lib string ) (string , error ) {
107
+ output := lib
108
+ libPath := ""
109
+ libName := ""
110
+ for _ , part := range strings .Fields (string (output )) {
111
+ if strings .HasPrefix (part , "-L" ) {
112
+ libPath = part [2 :]
113
+ } else if strings .HasPrefix (part , "-l" ) {
114
+ libName = part [2 :]
115
+ }
116
+ }
117
+
118
+ if libPath == "" || libName == "" {
119
+ return "" , fmt .Errorf ("failed to parse pkg-config output: %s" , output )
120
+ }
121
+
122
+ dylibPath := filepath .Join (libPath , "lib" + libName + ".dylib" )
123
+ return dylibPath , nil
124
+ }
125
+
126
+ func decodeSymbolName (symbolName string ) string {
127
+ if symbolName == "" {
128
+ return ""
129
+ }
130
+
131
+ demangled := llvm .ItaniumDemangle (symbolName , true )
132
+ if demangled == nil {
133
+ return symbolName
134
+ }
135
+ defer c .Free (unsafe .Pointer (demangled ))
136
+
137
+ demangleName := c .GoString (demangled )
138
+ if demangleName == "" {
139
+ return symbolName
140
+ }
141
+
142
+ decodedName := strings .TrimSpace (demangleName )
143
+ decodedName = strings .ReplaceAll (decodedName ,
144
+ "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const" ,
145
+ "std::string" )
146
+
147
+ return decodedName
148
+ }
149
+
150
+ func generateHeaderFilePath (cflags string , files []string ) []string {
151
+ prefixPath := cflags
152
+ prefixPath = strings .TrimPrefix (prefixPath , "-I" )
153
+ var includePaths []string
154
+ for _ , file := range files {
155
+ includePaths = append (includePaths , filepath .Join (prefixPath , "/" + file ))
156
+ }
157
+ return includePaths
158
+ }
159
+
160
+ func getCommonSymbols (dylibSymbols []types.CPPSymbol , astInfoList []types.ASTInformation , prefix []string ) []types.SymbolInfo {
161
+ var commonSymbols []types.SymbolInfo
162
+ functionNameMap := make (map [string ]int )
163
+
164
+ for _ , astInfo := range astInfoList {
165
+ for _ , dylibSym := range dylibSymbols {
166
+ if strings .TrimPrefix (dylibSym .Name , "_" ) == astInfo .Symbol {
167
+ cppName := generateCPPName (astInfo )
168
+ functionNameMap [cppName ]++
169
+ symbolInfo := types.SymbolInfo {
170
+ Mangle : strings .TrimPrefix (dylibSym .Name , "_" ),
171
+ CPP : cppName ,
172
+ Go : generateMangle (astInfo , functionNameMap [cppName ], prefix ),
173
+ }
174
+ commonSymbols = append (commonSymbols , symbolInfo )
175
+ break
176
+ }
177
+ }
178
+ }
179
+
180
+ return commonSymbols
181
+ }
182
+
183
+ func generateCPPName (astInfo types.ASTInformation ) string {
184
+ cppName := astInfo .Name
185
+ if astInfo .Class != "" {
186
+ cppName = astInfo .Class + "::" + astInfo .Name
187
+ }
188
+ return cppName
189
+ }
190
+
191
+ func generateMangle (astInfo types.ASTInformation , count int , prefixes []string ) string {
192
+ astInfo .Class = removePrefix (astInfo .Class , prefixes )
193
+ astInfo .Name = removePrefix (astInfo .Name , prefixes )
194
+ res := ""
195
+ if astInfo .Class != "" {
196
+ if astInfo .Class == astInfo .Name {
197
+ res = "(*" + astInfo .Class + ")." + "Init"
198
+ if count > 1 {
199
+ res += "__" + strconv .Itoa (count - 1 )
200
+ }
201
+ } else if astInfo .Name == "~" + astInfo .Class {
202
+ res = "(*" + astInfo .Class + ")." + "Dispose"
203
+ if count > 1 {
204
+ res += "__" + strconv .Itoa (count - 1 )
205
+ }
206
+ } else {
207
+ res = "(*" + astInfo .Class + ")." + astInfo .Name
208
+ if count > 1 {
209
+ res += "__" + strconv .Itoa (count - 1 )
210
+ }
211
+ }
212
+ } else {
213
+ res = astInfo .Name
214
+ if count > 1 {
215
+ res += "__" + strconv .Itoa (count - 1 )
216
+ }
217
+ }
218
+ return res
219
+ }
220
+
221
+ func removePrefix (str string , prefixes []string ) string {
222
+ for _ , prefix := range prefixes {
223
+ if strings .HasPrefix (str , prefix ) {
224
+ return strings .TrimPrefix (str , prefix )
225
+ }
226
+ }
227
+ return str
228
+ }
229
+
230
+ func genSymbolTableFile (symbolInfos []types.SymbolInfo ) error {
231
+ // keep open follow code block can run successfully
232
+ for i := range symbolInfos {
233
+ println ("symbol" , symbolInfos [i ].Go )
234
+ }
235
+
236
+ fileName := "llcppg.symb.json"
237
+ existingSymbols , err := readExistingSymbolTable (fileName )
238
+ if err != nil {
239
+ return err
240
+ }
241
+
242
+ for i := range symbolInfos {
243
+ if existingSymbol , exists := existingSymbols [symbolInfos [i ].Mangle ]; exists {
244
+ symbolInfos [i ].Go = existingSymbol .Go
245
+ }
246
+ }
247
+
248
+ root := cjson .Array ()
249
+ defer root .Delete ()
250
+
251
+ for _ , symbol := range symbolInfos {
252
+ item := cjson .Object ()
253
+ item .SetItem (c .Str ("mangle" ), cjson .String (c .AllocaCStr (symbol .Mangle )))
254
+ item .SetItem (c .Str ("c++" ), cjson .String (c .AllocaCStr (symbol .CPP )))
255
+ item .SetItem (c .Str ("go" ), cjson .String (c .AllocaCStr (symbol .Go )))
256
+ root .AddItem (item )
257
+ }
258
+
259
+ cStr := root .Print ()
260
+ if cStr == nil {
261
+ return errors .New ("symbol table is empty" )
262
+ }
263
+ defer c .Free (unsafe .Pointer (cStr ))
264
+
265
+ data := unsafe .Slice ((* byte )(unsafe .Pointer (cStr )), c .Strlen (cStr ))
266
+
267
+ if err := os .WriteFile (fileName , data , 0644 ); err != nil {
268
+ return errors .New ("failed to write symbol table file" )
269
+ }
270
+ return nil
271
+ }
272
+ func readExistingSymbolTable (fileName string ) (map [string ]types.SymbolInfo , error ) {
273
+ existingSymbols := make (map [string ]types.SymbolInfo )
274
+
275
+ if _ , err := os .Stat (fileName ); err != nil {
276
+ return existingSymbols , nil
277
+ }
278
+
279
+ data , err := os .ReadFile (fileName )
280
+ if err != nil {
281
+ return nil , errors .New ("failed to read symbol table file" )
282
+ }
283
+
284
+ parsedJSON := cjson .ParseBytes (data )
285
+ if parsedJSON == nil {
286
+ return nil , errors .New ("failed to parse JSON" )
287
+ }
288
+
289
+ arraySize := parsedJSON .GetArraySize ()
290
+
291
+ for i := 0 ; i < int (arraySize ); i ++ {
292
+ item := parsedJSON .GetArrayItem (c .Int (i ))
293
+ if item == nil {
294
+ continue
295
+ }
296
+ symbol := types.SymbolInfo {
297
+ Mangle : config .GetStringItem (item , "mangle" , "" ),
298
+ CPP : config .GetStringItem (item , "c++" , "" ),
299
+ Go : config .GetStringItem (item , "go" , "" ),
300
+ }
301
+ existingSymbols [symbol .Mangle ] = symbol
302
+ }
303
+
304
+ return existingSymbols , nil
305
+ }
0 commit comments