diff --git a/chore/_xtool/llcppsymg/clangutils/clangutils.go b/chore/_xtool/llcppsymg/clangutils/clangutils.go new file mode 100644 index 00000000..95681d25 --- /dev/null +++ b/chore/_xtool/llcppsymg/clangutils/clangutils.go @@ -0,0 +1,85 @@ +package clangutils + +import ( + "errors" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/clang" +) + +type Config struct { + File string + Temp bool + Args []string + IsCpp bool + Index *clang.Index +} + +func CreateTranslationUnit(config *Config) (*clang.Index, *clang.TranslationUnit, error) { + // default use the c/c++ standard of clang; c:gnu17 c++:gnu++17 + // https://clang.llvm.org/docs/CommandGuide/clang.html + defaultArgs := []string{"-x", "c"} + if config.IsCpp { + defaultArgs = []string{"-x", "c++"} + } + allArgs := append(defaultArgs, config.Args...) + + cArgs := make([]*c.Char, len(allArgs)) + for i, arg := range allArgs { + cArgs[i] = c.AllocaCStr(arg) + } + + var index *clang.Index + if config.Index != nil { + index = config.Index + } else { + index = clang.CreateIndex(0, 0) + } + + var unit *clang.TranslationUnit + + if config.Temp { + content := c.AllocaCStr(config.File) + tempFile := &clang.UnsavedFile{ + Filename: c.Str("temp.h"), + Contents: content, + Length: c.Ulong(c.Strlen(content)), + } + + unit = index.ParseTranslationUnit( + tempFile.Filename, + unsafe.SliceData(cArgs), c.Int(len(cArgs)), + tempFile, 1, + clang.DetailedPreprocessingRecord, + ) + + } else { + cFile := c.AllocaCStr(config.File) + unit = index.ParseTranslationUnit( + cFile, + unsafe.SliceData(cArgs), c.Int(len(cArgs)), + nil, 0, + clang.DetailedPreprocessingRecord, + ) + } + + if unit == nil { + return nil, nil, errors.New("failed to parse translation unit") + } + + return index, unit, nil +} + +// Traverse up the semantic parents +func BuildScopingParts(cursor clang.Cursor) []string { + var parts []string + for cursor.IsNull() != 1 && cursor.Kind != clang.CursorTranslationUnit { + name := cursor.String() + qualified := c.GoString(name.CStr()) + parts = append([]string{qualified}, parts...) + cursor = cursor.SemanticParent() + name.Dispose() + } + return parts +} diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 3be917a9..e9e0dc78 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -30,7 +30,6 @@ import ( "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/cpp/llvm" "github.com/goplus/llgo/xtool/nm" ) @@ -61,7 +60,7 @@ func main() { check(err) filepaths := genHeaderFilePath(conf.CFlags, conf.Include) - headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes) + headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes, conf.Cplusplus) check(err) symbolInfo := getCommonSymbols(symbols, headerInfos, conf.TrimPrefixes) @@ -115,19 +114,6 @@ func genDylibPath(lib string) (string, error) { return dylibPath, nil } -func decodeSymbol(symbolName string) string { - if symbolName == "" { - return "" - } - demangled := llvm.ItaniumDemangle(symbolName, true) - if demangled == nil { - return symbolName - } - defer c.Free(unsafe.Pointer(demangled)) - demangleName := c.GoString(demangled) - return strings.TrimSpace(demangleName) -} - func genHeaderFilePath(cflags string, files []string) []string { prefixPath := cflags prefixPath = strings.TrimPrefix(prefixPath, "-I") @@ -138,15 +124,15 @@ func genHeaderFilePath(cflags string, files []string) []string { return includePaths } -func getCommonSymbols(dylibSymbols []*nm.Symbol, symbolMap map[string]string, prefix []string) []*types.SymbolInfo { +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 goName, ok := symbolMap[symName]; ok { + if symInfo, ok := symbolMap[symName]; ok { symbolInfo := &types.SymbolInfo{ Mangle: symName, - CPP: decodeSymbol(dylibSym.Name), - Go: goName, + CPP: symInfo.ProtoName, + Go: symInfo.GoName, } commonSymbols = append(commonSymbols, symbolInfo) } diff --git a/chore/_xtool/llcppsymg/parse/parse.go b/chore/_xtool/llcppsymg/parse/parse.go index 89172255..98923b3a 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -4,17 +4,22 @@ import ( "errors" "strconv" "strings" - "unsafe" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/clang" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils" ) +type SymbolInfo struct { + GoName string + ProtoName string +} + type Context struct { namespaceName string className string prefixes []string - symbolMap map[string]string + symbolMap map[string]*SymbolInfo currentFile string nameCounts map[string]int } @@ -22,7 +27,7 @@ type Context struct { func newContext(prefixes []string) *Context { return &Context{ prefixes: prefixes, - symbolMap: make(map[string]string), + symbolMap: make(map[string]*SymbolInfo), nameCounts: make(map[string]int), } } @@ -48,9 +53,35 @@ func (c *Context) removePrefix(str string) string { return str } -func (c *Context) genGoName(name string) string { - class := c.removePrefix(c.className) +func toTitle(s string) string { + if s == "" { + return "" + } + return strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) +} + +func toCamel(originName string) string { + if originName == "" { + return "" + } + subs := strings.Split(string(originName), "_") + name := "" + for _, sub := range subs { + name += toTitle(sub) + } + return name +} + +// 1. remove prefix from config +// 2. convert to camel case +func (c *Context) toGoName(name string) string { name = c.removePrefix(name) + return toCamel(name) +} + +func (c *Context) genGoName(name string) string { + class := c.toGoName(c.className) + name = c.toGoName(name) var baseName string if class == "" { @@ -73,6 +104,22 @@ func (c *Context) genMethodName(class, name string) string { return prefix + name } +func (p *Context) genProtoName(cursor clang.Cursor) string { + displayName := cursor.DisplayName() + defer displayName.Dispose() + + scopingParts := clangutils.BuildScopingParts(cursor.SemanticParent()) + + var builder strings.Builder + for _, part := range scopingParts { + builder.WriteString(part) + builder.WriteString("::") + } + + builder.WriteString(c.GoString(displayName.CStr())) + return builder.String() +} + func (c *Context) addSuffix(name string) string { c.nameCounts[name]++ count := c.nameCounts[name] @@ -96,8 +143,10 @@ func collectFuncInfo(cursor clang.Cursor) { defer symbol.Dispose() defer cursorStr.Dispose() - goName := context.genGoName(name) - context.symbolMap[symbolName] = goName + context.symbolMap[symbolName] = &SymbolInfo{ + GoName: context.genGoName(name), + ProtoName: context.genProtoName(cursor), + } } func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult { @@ -141,35 +190,25 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -func ParseHeaderFile(filepaths []string, prefixes []string) (map[string]string, error) { - index := clang.CreateIndex(0, 0) - args := make([]*c.Char, 3) - args[0] = c.Str("-x") - args[1] = c.Str("c++") - args[2] = c.Str("-std=c++11") +func ParseHeaderFile(filepaths []string, prefixes []string, isCpp bool) (map[string]*SymbolInfo, error) { context = newContext(prefixes) - + index := clang.CreateIndex(0, 0) for _, filename := range filepaths { - unit := index.ParseTranslationUnit( - c.AllocaCStr(filename), - unsafe.SliceData(args), 3, - nil, 0, - clang.TranslationUnit_None, - ) - - if unit == nil { + _, unit, err := clangutils.CreateTranslationUnit(&clangutils.Config{ + File: filename, + Temp: false, + IsCpp: isCpp, + Index: index, + }) + if err != nil { return nil, errors.New("Unable to parse translation unit for file " + filename) } cursor := unit.Cursor() context.setCurrentFile(filename) - clang.VisitChildren(cursor, visit, nil) - unit.Dispose() } - index.Dispose() - return context.symbolMap, nil }