From 8913eeb1c1774bcd883134ffbfe85c03e07caebf Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 9 Sep 2024 10:18:59 +0800 Subject: [PATCH 1/4] llcppsymg:translation unit util --- .../_xtool/llcppsymg/clangutils/clangutils.go | 72 +++++++++++++++++++ chore/_xtool/llcppsymg/parse/parse.go | 30 +++----- 2 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 chore/_xtool/llcppsymg/clangutils/clangutils.go diff --git a/chore/_xtool/llcppsymg/clangutils/clangutils.go b/chore/_xtool/llcppsymg/clangutils/clangutils.go new file mode 100644 index 00000000..c03281dd --- /dev/null +++ b/chore/_xtool/llcppsymg/clangutils/clangutils.go @@ -0,0 +1,72 @@ +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 +} diff --git a/chore/_xtool/llcppsymg/parse/parse.go b/chore/_xtool/llcppsymg/parse/parse.go index f3e852eb..408e8419 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -4,10 +4,10 @@ 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 Context struct { @@ -133,35 +133,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]string, error) { + context = newContext(prefixes) for _, filename := range filepaths { - unit := index.ParseTranslationUnit( - c.AllocaCStr(filename), - unsafe.SliceData(args), 3, - nil, 0, - clang.TranslationUnit_None, - ) - - if unit == nil { + index, unit, err := clangutils.CreateTranslationUnit(&clangutils.Config{ + File: filename, + Temp: false, + IsCpp: isCpp, + }) + 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() } - - index.Dispose() - return context.symbolMap, nil } From 4688434c082ae1e1023d0ab039abee320fb49e34 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 9 Sep 2024 10:42:01 +0800 Subject: [PATCH 2/4] llcppsymg:parse with language config --- chore/_xtool/llcppsymg/llcppsymg.go | 2 +- chore/_xtool/llcppsymg/parse/parse.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 3be917a9..cfc842e5 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -61,7 +61,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) diff --git a/chore/_xtool/llcppsymg/parse/parse.go b/chore/_xtool/llcppsymg/parse/parse.go index 408e8419..55abd1a9 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -134,14 +134,14 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe } func ParseHeaderFile(filepaths []string, prefixes []string, isCpp bool) (map[string]string, error) { - context = newContext(prefixes) - + index := clang.CreateIndex(0, 0) for _, filename := range filepaths { - index, unit, err := clangutils.CreateTranslationUnit(&clangutils.Config{ + _, 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) @@ -151,7 +151,7 @@ func ParseHeaderFile(filepaths []string, prefixes []string, isCpp bool) (map[str context.setCurrentFile(filename) clang.VisitChildren(cursor, visit, nil) unit.Dispose() - index.Dispose() } + index.Dispose() return context.symbolMap, nil } From 78f0177ac4859cd785e1f0f5b1ca5f0a0d9af983 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 9 Sep 2024 14:24:36 +0800 Subject: [PATCH 3/4] llcppsymg:use clang's displayname with semantic parent to construct func proto for c/c++ --- .../_xtool/llcppsymg/clangutils/clangutils.go | 13 ++++++++ chore/_xtool/llcppsymg/llcppsymg.go | 22 +++---------- chore/_xtool/llcppsymg/parse/parse.go | 33 ++++++++++++++++--- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/chore/_xtool/llcppsymg/clangutils/clangutils.go b/chore/_xtool/llcppsymg/clangutils/clangutils.go index c03281dd..95681d25 100644 --- a/chore/_xtool/llcppsymg/clangutils/clangutils.go +++ b/chore/_xtool/llcppsymg/clangutils/clangutils.go @@ -70,3 +70,16 @@ func CreateTranslationUnit(config *Config) (*clang.Index, *clang.TranslationUnit 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 cfc842e5..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" ) @@ -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 55abd1a9..88ff1b4a 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -10,11 +10,16 @@ import ( "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), } } @@ -73,6 +78,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 +117,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 { @@ -133,7 +156,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -func ParseHeaderFile(filepaths []string, prefixes []string, isCpp bool) (map[string]string, error) { +func ParseHeaderFile(filepaths []string, prefixes []string, isCpp bool) (map[string]*SymbolInfo, error) { context = newContext(prefixes) index := clang.CreateIndex(0, 0) for _, filename := range filepaths { From c81b7f6bb42efb37eee824075002eb4215e56183 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 20 Sep 2024 10:16:00 +0800 Subject: [PATCH 4/4] llcppsymg:to camel --- chore/_xtool/llcppsymg/parse/parse.go | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/chore/_xtool/llcppsymg/parse/parse.go b/chore/_xtool/llcppsymg/parse/parse.go index 88ff1b4a..31cf40f3 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -53,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 == "" {