diff --git a/chore/_xtool/llcppsymg/dylib/dylib_test/dylib.go b/chore/_xtool/llcppsymg/_cmptest/dylib_test/dylib.go similarity index 100% rename from chore/_xtool/llcppsymg/dylib/dylib_test/dylib.go rename to chore/_xtool/llcppsymg/_cmptest/dylib_test/dylib.go diff --git a/chore/_xtool/llcppsymg/dylib/dylib_test/llgo.expect b/chore/_xtool/llcppsymg/_cmptest/dylib_test/llgo.expect similarity index 100% rename from chore/_xtool/llcppsymg/dylib/dylib_test/llgo.expect rename to chore/_xtool/llcppsymg/_cmptest/dylib_test/llgo.expect diff --git a/chore/_xtool/llcppsymg/header/header_test/header.go b/chore/_xtool/llcppsymg/_cmptest/header_test/header.go similarity index 60% rename from chore/_xtool/llcppsymg/header/header_test/header.go rename to chore/_xtool/llcppsymg/_cmptest/header_test/header.go index 9378aae7..44611b29 100644 --- a/chore/_xtool/llcppsymg/header/header_test/header.go +++ b/chore/_xtool/llcppsymg/_cmptest/header_test/header.go @@ -4,43 +4,10 @@ import ( "fmt" "os" "path/filepath" - "strings" + + "github.com/goplus/llgo/chore/_xtool/llcppsymg/header" ) -func GenHeaderFilePath(cflags string, files []string) ([]string, error) { - prefixPath := strings.TrimPrefix(cflags, "-I") - - var validPaths []string - var errs []string - - for _, file := range files { - if file == "" { - continue - } - fullPath := filepath.Join(prefixPath, file) - if f, err := os.Open(fullPath); err != nil { - if os.IsNotExist(err) { - errs = append(errs, fmt.Sprintf("file not found: %s", file)) - } else { - errs = append(errs, fmt.Sprintf("error accessing file %s: %v", file, err)) - } - } else { - f.Close() - validPaths = append(validPaths, fullPath) - } - } - - if len(validPaths) == 0 && len(errs) == 0 { - return nil, fmt.Errorf("no valid header files") - } - - if len(errs) > 0 { - return validPaths, fmt.Errorf("some files not found or inaccessible: %v", errs) - } - - return validPaths, nil -} - func TestGenHeaderFilePath() { fmt.Println("=== Test GenHeaderFilePath ===") @@ -83,7 +50,7 @@ func TestGenHeaderFilePath() { fmt.Printf("Test case: %s\n", tc.name) fmt.Printf("Input files: %v\n", tc.files) - result, err := GenHeaderFilePath(tc.cflags, tc.files) + result, err := header.GenHeaderFilePath(tc.cflags, tc.files) if err != nil { fmt.Printf("Error: %v\n", err) diff --git a/chore/_xtool/llcppsymg/header/header_test/llgo.expect b/chore/_xtool/llcppsymg/_cmptest/header_test/llgo.expect similarity index 100% rename from chore/_xtool/llcppsymg/header/header_test/llgo.expect rename to chore/_xtool/llcppsymg/_cmptest/header_test/llgo.expect diff --git a/chore/_xtool/llcppsymg/parse/parse_test/llgo.expect b/chore/_xtool/llcppsymg/_cmptest/parse_test/llgo.expect similarity index 100% rename from chore/_xtool/llcppsymg/parse/parse_test/llgo.expect rename to chore/_xtool/llcppsymg/_cmptest/parse_test/llgo.expect diff --git a/chore/_xtool/llcppsymg/parse/parse_test/parse.go b/chore/_xtool/llcppsymg/_cmptest/parse_test/parse.go similarity index 100% rename from chore/_xtool/llcppsymg/parse/parse_test/parse.go rename to chore/_xtool/llcppsymg/_cmptest/parse_test/parse.go diff --git a/chore/_xtool/llcppsymg/_cmptest/symbol_test/llgo.expect b/chore/_xtool/llcppsymg/_cmptest/symbol_test/llgo.expect new file mode 100644 index 00000000..4a329943 --- /dev/null +++ b/chore/_xtool/llcppsymg/_cmptest/symbol_test/llgo.expect @@ -0,0 +1,46 @@ +#stdout +=== Test GetCommonSymbols === + +Test Case: Lua symbols +Common Symbols (4): +Mangle: lua_absindex, CPP: lua_absindex(lua_State *, int), Go: Absindex +Mangle: lua_arith, CPP: lua_arith(lua_State *, int), Go: Arith +Mangle: lua_atpanic, CPP: lua_atpanic(lua_State *, lua_CFunction), Go: Atpanic +Mangle: lua_callk, CPP: lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction), Go: Callk + +Test Case: INIReader and Std library symbols +Common Symbols (3): +Mangle: ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x, CPP: INIReader::GetInteger64(const std::string &, const std::string &, int64_t), Go: (*Reader).GetInteger64 +Mangle: ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d, CPP: INIReader::GetReal(const std::string &, const std::string &, double), Go: (*Reader).GetReal +Mangle: ZNK9INIReader10ParseErrorEv, CPP: INIReader::ParseError(), Go: (*Reader).ParseError + +=== Test ReadExistingSymbolTable === +Symbols read from the file: +Symbol Map GoName: (*Reader).Init__1, ProtoName In HeaderFile: INIReader::INIReader(const char *, size_t), MangledName: _ZN9INIReaderC1EPKcm +Symbol Map GoName: (*Reader).GetBoolean, ProtoName In HeaderFile: INIReader::GetBoolean(const std::string &, const std::string &, bool), MangledName: _ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b +Symbol Map GoName: (*Reader).ParseError, ProtoName In HeaderFile: INIReader::ParseError(), MangledName: _ZNK9INIReader10ParseErrorEv +Havent existed symb file + +=== Test GenSymbolTableData === +[{ + "mangle": "lua_absindex", + "c++": "lua_absindex(lua_State *, int)", + "go": "Absindex" + }, { + "mangle": "lua_arith", + "c++": "lua_arith(lua_State *, int)", + "go": "Arith" + }, { + "mangle": "lua_atpanic", + "c++": "lua_atpanic(lua_State *, lua_CFunction)", + "go": "Atpanic" + }, { + "mangle": "lua_callk", + "c++": "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", + "go": "ModifiedCallk" + }] + + +#stderr + +#exit 0 diff --git a/chore/_xtool/llcppsymg/_cmptest/symbol_test/symbol.go b/chore/_xtool/llcppsymg/_cmptest/symbol_test/symbol.go new file mode 100644 index 00000000..81bbe460 --- /dev/null +++ b/chore/_xtool/llcppsymg/_cmptest/symbol_test/symbol.go @@ -0,0 +1,152 @@ +package main + +import ( + "fmt" + "os" + "sort" + + "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol" + "github.com/goplus/llgo/chore/llcppg/types" + "github.com/goplus/llgo/xtool/nm" +) + +func main() { + TestGetCommonSymbols() + TestReadExistingSymbolTable() + TestGenSymbolTableData() +} +func TestGetCommonSymbols() { + fmt.Println("=== Test GetCommonSymbols ===") + testCases := []struct { + name string + dylibSymbols []*nm.Symbol + headerSymbols map[string]*parse.SymbolInfo + }{ + { + name: "Lua symbols", + dylibSymbols: []*nm.Symbol{ + {Name: "_lua_absindex"}, + {Name: "_lua_arith"}, + {Name: "_lua_atpanic"}, + {Name: "_lua_callk"}, + {Name: "_lua_lib_nonexistent"}, + }, + headerSymbols: map[string]*parse.SymbolInfo{ + "lua_absindex": {ProtoName: "lua_absindex(lua_State *, int)", GoName: "Absindex"}, + "lua_arith": {ProtoName: "lua_arith(lua_State *, int)", GoName: "Arith"}, + "lua_atpanic": {ProtoName: "lua_atpanic(lua_State *, lua_CFunction)", GoName: "Atpanic"}, + "lua_callk": {ProtoName: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", GoName: "Callk"}, + "lua_header_nonexistent": {ProtoName: "lua_header_nonexistent()", GoName: "HeaderNonexistent"}, + }, + }, + { + name: "INIReader and Std library symbols", + dylibSymbols: []*nm.Symbol{ + {Name: "_ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x"}, + {Name: "_ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d"}, + {Name: "_ZNK9INIReader10ParseErrorEv"}, + }, + headerSymbols: map[string]*parse.SymbolInfo{ + "ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x": {GoName: "(*Reader).GetInteger64", ProtoName: "INIReader::GetInteger64(const std::string &, const std::string &, int64_t)"}, + "ZNK9INIReader13GetUnsigned64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_y": {GoName: "(*Reader).GetUnsigned64", ProtoName: "INIReader::GetUnsigned64(const std::string &, const std::string &, uint64_t)"}, + "ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d": {GoName: "(*Reader).GetReal", ProtoName: "INIReader::GetReal(const std::string &, const std::string &, double)"}, + "ZNK9INIReader10ParseErrorEv": {GoName: "(*Reader).ParseError", ProtoName: "INIReader::ParseError()"}, + "ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b": {GoName: "(*Reader).GetBoolean", ProtoName: "INIReader::GetBoolean(const std::string &, const std::string &, bool)"}, + }, + }, + } + + for _, tc := range testCases { + fmt.Printf("\nTest Case: %s\n", tc.name) + commonSymbols := symbol.GetCommonSymbols(tc.dylibSymbols, tc.headerSymbols) + fmt.Printf("Common Symbols (%d):\n", len(commonSymbols)) + for _, sym := range commonSymbols { + fmt.Printf("Mangle: %s, CPP: %s, Go: %s\n", sym.Mangle, sym.CPP, sym.Go) + } + } + fmt.Println() +} + +func TestReadExistingSymbolTable() { + fmt.Println("=== Test ReadExistingSymbolTable ===") + + tmpFile, err := os.CreateTemp("", "llcppg.symb.json") + if err != nil { + fmt.Printf("Failed to create temp file: %v\n", err) + return + } + defer os.Remove(tmpFile.Name()) + + testData := `[ + { + "mangle": "_ZN9INIReaderC1EPKcm", + "c++": "INIReader::INIReader(const char *, size_t)", + "go": "(*Reader).Init__1" + }, + { + "mangle": "_ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b", + "c++": "INIReader::GetBoolean(const std::string &, const std::string &, bool)", + "go": "(*Reader).GetBoolean" + }, + { + "mangle": "_ZNK9INIReader10ParseErrorEv", + "c++": "INIReader::ParseError()", + "go": "(*Reader).ParseError" + } + ]` + if _, err := tmpFile.Write([]byte(testData)); err != nil { + fmt.Printf("Failed to write test data: %v\n", err) + return + } + tmpFile.Close() + + symbols, exist := symbol.ReadExistingSymbolTable(tmpFile.Name()) + if !exist { + fmt.Printf("ReadExistingSymbolTable failed") + return + } + + fmt.Println("Symbols read from the file:") + var keys []string + for key := range symbols { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + info := symbols[key] + fmt.Printf("Symbol Map GoName: %s, ProtoName In HeaderFile: %s, MangledName: %s\n", + info.Go, info.CPP, key) + } + + _, exist = symbol.ReadExistingSymbolTable("other.json") + if !exist { + fmt.Println("Havent existed symb file") + } + fmt.Println() +} +func TestGenSymbolTableData() { + fmt.Println("=== Test GenSymbolTableData ===") + + commonSymbols := []*types.SymbolInfo{ + {Mangle: "lua_absindex", CPP: "lua_absindex(lua_State *, int)", Go: "Absindex"}, + {Mangle: "lua_arith", CPP: "lua_arith(lua_State *, int)", Go: "Arith"}, + {Mangle: "lua_atpanic", CPP: "lua_atpanic(lua_State *, lua_CFunction)", Go: "Atpanic"}, + {Mangle: "lua_callk", CPP: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", Go: "Callk"}, + } + + existingSymbols := map[string]types.SymbolInfo{ + "lua_absindex": {Mangle: "lua_absindex", CPP: "lua_absindex(lua_State *, int)", Go: "Absindex"}, + "lua_arith": {Mangle: "lua_arith", CPP: "lua_arith(lua_State *, int)", Go: "Arith"}, + "lua_callk": {Mangle: "lua_callk", CPP: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", Go: "ModifiedCallk"}, + } + + data, err := symbol.GenSymbolTableData(commonSymbols, existingSymbols) + if err != nil { + fmt.Printf("Error generating symbol table data: %v\n", err) + return + } + fmt.Println(string(data)) + fmt.Println() +} diff --git a/chore/_xtool/llcppsymg/_cmptest/symg_test/llgo.expect b/chore/_xtool/llcppsymg/_cmptest/symg_test/llgo.expect new file mode 100644 index 00000000..93bf120d --- /dev/null +++ b/chore/_xtool/llcppsymg/_cmptest/symg_test/llgo.expect @@ -0,0 +1,45 @@ +#stdout +=== Test Case: inireader === +[{ + "mangle": "_ZN9INIReaderC1EPKc", + "c++": "INIReader::INIReader(const char *)", + "go": "(*Reader).Init" + }, { + "mangle": "_ZN9INIReaderC1EPKcl", + "c++": "INIReader::INIReader(const char *, long)", + "go": "(*Reader).Init__1" + }, { + "mangle": "_ZN9INIReaderD1Ev", + "c++": "INIReader::~INIReader()", + "go": "(*Reader).Dispose" + }, { + "mangle": "_ZNK9INIReader10ParseErrorEv", + "c++": "INIReader::ParseError()", + "go": "(*Reader).ModifyedParseError" + }, { + "mangle": "_ZNK9INIReader3GetEPKcS1_S1_", + "c++": "INIReader::Get(const char *, const char *, const char *)", + "go": "(*Reader).Get" + }] +=== Test Case: lua === +[{ + "mangle": "lua_error", + "c++": "lua_error(lua_State *)", + "go": "Error" + }, { + "mangle": "lua_next", + "c++": "lua_next(lua_State *, int)", + "go": "Next" + }, { + "mangle": "lua_concat", + "c++": "lua_concat(lua_State *, int)", + "go": "Concat" + }, { + "mangle": "lua_stringtonumber", + "c++": "lua_stringtonumber(lua_State *, const char *)", + "go": "Stringtonumber" + }] + +#stderr + +#exit 0 diff --git a/chore/_xtool/llcppsymg/_cmptest/symg_test/symg.go b/chore/_xtool/llcppsymg/_cmptest/symg_test/symg.go new file mode 100644 index 00000000..11173662 --- /dev/null +++ b/chore/_xtool/llcppsymg/_cmptest/symg_test/symg.go @@ -0,0 +1,117 @@ +package main + +import ( + "fmt" + "os" + + "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol" + "github.com/goplus/llgo/xtool/nm" +) + +func main() { + TestParseHeaderFile() +} +func TestParseHeaderFile() { + testCases := []struct { + name string + content string + isCpp bool + prefixes []string + dylibSymbols []*nm.Symbol + symbFileContent string + }{ + { + name: "inireader", + content: ` +#define INI_API __attribute__((visibility("default"))) +class INIReader { + public: + __attribute__((visibility("default"))) explicit INIReader(const char *filename); + INI_API explicit INIReader(const char *buffer, long buffer_size); + ~INIReader(); + INI_API int ParseError() const; + INI_API const char * Get(const char *section, const char *name, + const char *default_value) const; + private: + static const char * MakeKey(const char *section, const char *name); +}; + `, + isCpp: true, + prefixes: []string{"INI"}, + dylibSymbols: []*nm.Symbol{ + {Name: "__ZN9INIReaderC1EPKc"}, + {Name: "__ZN9INIReaderC1EPKcl"}, + {Name: "__ZN9INIReaderD1Ev"}, + {Name: "__ZNK9INIReader10ParseErrorEv"}, + {Name: "__ZNK9INIReader3GetEPKcS1_S1_"}, + }, + symbFileContent: ` +[{ + "mangle": "_ZN9INIReaderC1EPKc", + "c++": "INIReader::INIReader(const char *)", + "go": "(*Reader).Init" +}, { + "mangle": "_ZN9INIReaderC1EPKcl", + "c++": "INIReader::INIReader(const char *, long)", + "go": "(*Reader).Init__1" +}, { + "mangle": "_ZN9INIReaderD1Ev", + "c++": "INIReader::~INIReader()", + "go": "(*Reader).Dispose" +}, { + "mangle": "_ZNK9INIReader10ParseErrorEv", + "c++": "INIReader::ParseError()", + "go": "(*Reader).ModifyedParseError" +}]`, + }, + { + name: "lua", + content: ` +typedef struct lua_State lua_State; + +LUA_API int(lua_error)(lua_State *L); + +LUA_API int(lua_next)(lua_State *L, int idx); + +LUA_API void(lua_concat)(lua_State *L, int n); +LUA_API void(lua_len)(lua_State *L, int idx); + +LUA_API long unsigned int(lua_stringtonumber)(lua_State *L, const char *s); + +LUA_API void(lua_setallocf)(lua_State *L, lua_Alloc f, void *ud); + +LUA_API void(lua_toclose)(lua_State *L, int idx); +LUA_API void(lua_closeslot)(lua_State *L, int idx); + `, + isCpp: false, + prefixes: []string{"lua_"}, + dylibSymbols: []*nm.Symbol{ + {Name: "_lua_error"}, + {Name: "_lua_next"}, + {Name: "_lua_concat"}, + {Name: "_lua_stringtonumber"}, + }, + }, + } + + for _, tc := range testCases { + fmt.Printf("=== Test Case: %s ===\n", tc.name) + headerSymbolMap, err := parse.ParseHeaderFile([]string{tc.content}, tc.prefixes, tc.isCpp, true) + if err != nil { + fmt.Println("Error:", err) + } + tmpFile, err := os.CreateTemp("", "llcppg.symb.json") + if err != nil { + fmt.Printf("Failed to create temp file: %v\n", err) + return + } + tmpFile.Write([]byte(tc.symbFileContent)) + symbolData, err := symbol.GenerateAndUpdateSymbolTable(tc.dylibSymbols, headerSymbolMap, tmpFile.Name()) + if err != nil { + fmt.Println("Error:", err) + } + fmt.Println(string(symbolData)) + os.Remove(tmpFile.Name()) + } +} diff --git a/chore/_xtool/llcppsymg/header/header.go b/chore/_xtool/llcppsymg/header/header.go index 392b0628..9390ea2c 100644 --- a/chore/_xtool/llcppsymg/header/header.go +++ b/chore/_xtool/llcppsymg/header/header.go @@ -2,22 +2,41 @@ package header import ( "fmt" + "os" "path/filepath" "strings" ) func GenHeaderFilePath(cflags string, files []string) ([]string, error) { - fmt.Printf("get filepath from config cflags%s & include:%v\n", cflags, files) prefixPath := strings.TrimPrefix(cflags, "-I") - var includePaths []string + + var validPaths []string + var errs []string + for _, file := range files { if file == "" { continue } - includePaths = append(includePaths, filepath.Join(prefixPath, "/"+file)) + fullPath := filepath.Join(prefixPath, file) + if f, err := os.Open(fullPath); err != nil { + if os.IsNotExist(err) { + errs = append(errs, fmt.Sprintf("file not found: %s", file)) + } else { + errs = append(errs, fmt.Sprintf("error accessing file %s: %v", file, err)) + } + } else { + f.Close() + validPaths = append(validPaths, fullPath) + } } - if len(includePaths) == 0 { + + if len(validPaths) == 0 && len(errs) == 0 { return nil, fmt.Errorf("no valid header files") } - return includePaths, nil + + if len(errs) > 0 { + return validPaths, fmt.Errorf("some files not found or inaccessible: %v", errs) + } + + return validPaths, nil } diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 3a212cae..272efd7a 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -17,25 +17,20 @@ package main import ( - "errors" "fmt" "io" "os" - "strings" - "unsafe" - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/dylib" "github.com/goplus/llgo/chore/_xtool/llcppsymg/header" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" - "github.com/goplus/llgo/chore/llcppg/types" - "github.com/goplus/llgo/xtool/nm" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol" ) func main() { cfgFile := "llcppg.cfg" + symbFile := "llcppg.symb.json" if len(os.Args) > 1 { cfgFile = os.Args[1] } @@ -63,9 +58,10 @@ func main() { headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes, conf.Cplusplus, false) check(err) - symbolInfo := getCommonSymbols(symbols, headerInfos, conf.TrimPrefixes) + symbolData, err := symbol.GenerateAndUpdateSymbolTable(symbols, headerInfos, symbFile) + check(err) - err = genSymbolTableFile(symbolInfo) + err = os.WriteFile(symbFile, symbolData, 0644) check(err) } @@ -74,92 +70,3 @@ func check(err error) { panic(err) } } - -func getCommonSymbols(dylibSymbols []*nm.Symbol, symbolMap map[string]*parse.SymbolInfo, prefix []string) []*types.SymbolInfo { - var commonSymbols []*types.SymbolInfo - for _, dylibSym := range dylibSymbols { - symName := strings.TrimPrefix(dylibSym.Name, "_") - if symInfo, ok := symbolMap[symName]; ok { - symbolInfo := &types.SymbolInfo{ - Mangle: symName, - CPP: symInfo.ProtoName, - Go: symInfo.GoName, - } - commonSymbols = append(commonSymbols, symbolInfo) - } - } - return commonSymbols -} - -func genSymbolTableFile(symbolInfos []*types.SymbolInfo) error { - fileName := "llcppg.symb.json" - existingSymbols, err := readExistingSymbolTable(fileName) - if err != nil { - return err - } - - for i := range symbolInfos { - if existingSymbol, exists := existingSymbols[symbolInfos[i].Mangle]; exists { - symbolInfos[i].Go = existingSymbol.Go - } - } - - root := cjson.Array() - defer root.Delete() - - for _, symbol := range symbolInfos { - item := cjson.Object() - item.SetItem(c.Str("mangle"), cjson.String(c.AllocaCStr(symbol.Mangle))) - item.SetItem(c.Str("c++"), cjson.String(c.AllocaCStr(symbol.CPP))) - item.SetItem(c.Str("go"), cjson.String(c.AllocaCStr(symbol.Go))) - root.AddItem(item) - } - - cStr := root.Print() - if cStr == nil { - return errors.New("symbol table is empty") - } - defer c.Free(unsafe.Pointer(cStr)) - - data := unsafe.Slice((*byte)(unsafe.Pointer(cStr)), c.Strlen(cStr)) - - if err := os.WriteFile(fileName, data, 0644); err != nil { - return errors.New("failed to write symbol table file") - } - return nil -} - -func readExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, error) { - existingSymbols := make(map[string]types.SymbolInfo) - - if _, err := os.Stat(fileName); err != nil { - return existingSymbols, nil - } - - data, err := os.ReadFile(fileName) - if err != nil { - return nil, errors.New("failed to read symbol table file") - } - - parsedJSON := cjson.ParseBytes(data) - if parsedJSON == nil { - return nil, errors.New("failed to parse JSON") - } - - arraySize := parsedJSON.GetArraySize() - - for i := 0; i < int(arraySize); i++ { - item := parsedJSON.GetArrayItem(c.Int(i)) - if item == nil { - continue - } - symbol := types.SymbolInfo{ - Mangle: config.GetStringItem(item, "mangle", ""), - CPP: config.GetStringItem(item, "c++", ""), - Go: config.GetStringItem(item, "go", ""), - } - existingSymbols[symbol.Mangle] = symbol - } - - return existingSymbols, nil -} diff --git a/chore/_xtool/llcppsymg/symbol/symbol.go b/chore/_xtool/llcppsymg/symbol/symbol.go new file mode 100644 index 00000000..60ab5c6d --- /dev/null +++ b/chore/_xtool/llcppsymg/symbol/symbol.go @@ -0,0 +1,107 @@ +package symbol + +import ( + "errors" + "os" + "strings" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/config" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" + "github.com/goplus/llgo/chore/llcppg/types" + "github.com/goplus/llgo/xtool/nm" +) + +// finds the intersection of symbols from the dynamic library's symbol table and the symbols parsed from header files. +// It returns a list of symbols that can be externally linked. +func GetCommonSymbols(dylibSymbols []*nm.Symbol, headerSymbols map[string]*parse.SymbolInfo) []*types.SymbolInfo { + var commonSymbols []*types.SymbolInfo + for _, dylibSym := range dylibSymbols { + symName := strings.TrimPrefix(dylibSym.Name, "_") + if symInfo, ok := headerSymbols[symName]; ok { + symbolInfo := &types.SymbolInfo{ + Mangle: symName, + CPP: symInfo.ProtoName, + Go: symInfo.GoName, + } + commonSymbols = append(commonSymbols, symbolInfo) + } + } + return commonSymbols +} + +func ReadExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, bool) { + if _, err := os.Stat(fileName); err != nil { + return nil, false + } + + data, err := os.ReadFile(fileName) + if err != nil { + return nil, false + } + + parsedJSON := cjson.ParseBytes(data) + if parsedJSON == nil { + return nil, false + } + + existingSymbols := make(map[string]types.SymbolInfo) + arraySize := parsedJSON.GetArraySize() + + for i := 0; i < int(arraySize); i++ { + item := parsedJSON.GetArrayItem(c.Int(i)) + symbol := types.SymbolInfo{ + Mangle: config.GetStringItem(item, "mangle", ""), + CPP: config.GetStringItem(item, "c++", ""), + Go: config.GetStringItem(item, "go", ""), + } + existingSymbols[symbol.Mangle] = symbol + } + + return existingSymbols, true +} + +func GenSymbolTableData(commonSymbols []*types.SymbolInfo, existingSymbols map[string]types.SymbolInfo) ([]byte, error) { + // todo(zzy): len(existingSymbols) !=0 + // https://github.com/goplus/llgo/issues/808 will cause unexpected panic + // https://github.com/goplus/llgo/pull/793 this pr can fix it + for i := range commonSymbols { + if existingSymbol, exists := existingSymbols[commonSymbols[i].Mangle]; exists { + commonSymbols[i].Go = existingSymbol.Go + } + } + + root := cjson.Array() + defer root.Delete() + + for _, symbol := range commonSymbols { + item := cjson.Object() + item.SetItem(c.Str("mangle"), cjson.String(c.AllocaCStr(symbol.Mangle))) + item.SetItem(c.Str("c++"), cjson.String(c.AllocaCStr(symbol.CPP))) + item.SetItem(c.Str("go"), cjson.String(c.AllocaCStr(symbol.Go))) + root.AddItem(item) + } + + cStr := root.Print() + if cStr == nil { + return nil, errors.New("symbol table is empty") + } + defer c.Free(unsafe.Pointer(cStr)) + result := []byte(c.GoString(cStr)) + return result, nil +} + +func GenerateAndUpdateSymbolTable(symbols []*nm.Symbol, headerInfos map[string]*parse.SymbolInfo, symbFile string) ([]byte, error) { + commonSymbols := GetCommonSymbols(symbols, headerInfos) + + existSymbols, _ := ReadExistingSymbolTable(symbFile) + + symbolData, err := GenSymbolTableData(commonSymbols, existSymbols) + if err != nil { + return nil, err + } + + return symbolData, nil +}