Merge pull request #681 from luoliwoshang/llcppsymg/refine

llcppsymg:improve parse symbol
This commit is contained in:
xushiwei
2024-08-08 16:26:24 +08:00
committed by GitHub
3 changed files with 90 additions and 173 deletions

View File

@@ -22,7 +22,6 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"unsafe" "unsafe"
@@ -61,11 +60,11 @@ func main() {
check(err) check(err)
filepaths := generateHeaderFilePath(conf.CFlags, conf.Include) filepaths := genHeaderFilePath(conf.CFlags, conf.Include)
astInfos, err := parse.ParseHeaderFile(filepaths) headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes)
check(err) check(err)
symbolInfo := getCommonSymbols(symbols, astInfos, conf.TrimPrefixes) symbolInfo := getCommonSymbols(symbols, headerInfos, conf.TrimPrefixes)
err = genSymbolTableFile(symbolInfo) err = genSymbolTableFile(symbolInfo)
check(err) check(err)
@@ -77,8 +76,8 @@ func check(err error) {
} }
} }
func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { func parseDylibSymbols(lib string) ([]*nm.Symbol, error) {
dylibPath, err := generateDylibPath(lib) dylibPath, err := genDylibPath(lib)
if err != nil { if err != nil {
return nil, errors.New("failed to generate dylib path") return nil, errors.New("failed to generate dylib path")
} }
@@ -88,22 +87,15 @@ func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) {
return nil, errors.New("failed to list symbols in dylib") return nil, errors.New("failed to list symbols in dylib")
} }
var symbols []types.CPPSymbol var symbols []*nm.Symbol
for _, file := range files { for _, file := range files {
for _, sym := range file.Symbols { symbols = append(symbols, file.Symbols...)
demangleName := decodeSymbolName(sym.Name)
symbols = append(symbols, types.CPPSymbol{
Symbol: sym,
DemangleName: demangleName,
})
}
} }
return symbols, nil return symbols, nil
} }
func generateDylibPath(lib string) (string, error) { func genDylibPath(lib string) (string, error) {
output := lib output := lib
libPath := "" libPath := ""
libName := "" libName := ""
@@ -123,31 +115,20 @@ func generateDylibPath(lib string) (string, error) {
return dylibPath, nil return dylibPath, nil
} }
func decodeSymbolName(symbolName string) string { func decodeSymbol(symbolName string) string {
if symbolName == "" { if symbolName == "" {
return "" return ""
} }
demangled := llvm.ItaniumDemangle(symbolName, true) demangled := llvm.ItaniumDemangle(symbolName, true)
if demangled == nil { if demangled == nil {
return symbolName return symbolName
} }
defer c.Free(unsafe.Pointer(demangled)) defer c.Free(unsafe.Pointer(demangled))
demangleName := c.GoString(demangled) demangleName := c.GoString(demangled)
if demangleName == "" { return strings.TrimSpace(demangleName)
return symbolName
}
decodedName := strings.TrimSpace(demangleName)
decodedName = strings.ReplaceAll(decodedName,
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const",
"std::string")
return decodedName
} }
func generateHeaderFilePath(cflags string, files []string) []string { func genHeaderFilePath(cflags string, files []string) []string {
prefixPath := cflags prefixPath := cflags
prefixPath = strings.TrimPrefix(prefixPath, "-I") prefixPath = strings.TrimPrefix(prefixPath, "-I")
var includePaths []string var includePaths []string
@@ -157,77 +138,23 @@ func generateHeaderFilePath(cflags string, files []string) []string {
return includePaths return includePaths
} }
func getCommonSymbols(dylibSymbols []types.CPPSymbol, astInfoList []types.ASTInformation, prefix []string) []types.SymbolInfo { func getCommonSymbols(dylibSymbols []*nm.Symbol, symbolMap map[string]string, prefix []string) []*types.SymbolInfo {
var commonSymbols []types.SymbolInfo var commonSymbols []*types.SymbolInfo
functionNameMap := make(map[string]int) for _, dylibSym := range dylibSymbols {
symName := strings.TrimPrefix(dylibSym.Name, "_")
for _, astInfo := range astInfoList { if goName, ok := symbolMap[symName]; ok {
for _, dylibSym := range dylibSymbols { symbolInfo := &types.SymbolInfo{
if strings.TrimPrefix(dylibSym.Name, "_") == astInfo.Symbol { Mangle: symName,
cppName := generateCPPName(astInfo) CPP: decodeSymbol(dylibSym.Name),
functionNameMap[cppName]++ Go: goName,
symbolInfo := types.SymbolInfo{
Mangle: strings.TrimPrefix(dylibSym.Name, "_"),
CPP: cppName,
Go: generateMangle(astInfo, functionNameMap[cppName], prefix),
}
commonSymbols = append(commonSymbols, symbolInfo)
break
} }
commonSymbols = append(commonSymbols, symbolInfo)
} }
} }
return commonSymbols return commonSymbols
} }
func generateCPPName(astInfo types.ASTInformation) string { func genSymbolTableFile(symbolInfos []*types.SymbolInfo) error {
cppName := astInfo.Name
if astInfo.Class != "" {
cppName = astInfo.Class + "::" + astInfo.Name
}
return cppName
}
func generateMangle(astInfo types.ASTInformation, count int, prefixes []string) string {
astInfo.Class = removePrefix(astInfo.Class, prefixes)
astInfo.Name = removePrefix(astInfo.Name, prefixes)
res := ""
if astInfo.Class != "" {
if astInfo.Class == astInfo.Name {
res = "(*" + astInfo.Class + ")." + "Init"
if count > 1 {
res += "__" + strconv.Itoa(count-1)
}
} else if astInfo.Name == "~"+astInfo.Class {
res = "(*" + astInfo.Class + ")." + "Dispose"
if count > 1 {
res += "__" + strconv.Itoa(count-1)
}
} else {
res = "(*" + astInfo.Class + ")." + astInfo.Name
if count > 1 {
res += "__" + strconv.Itoa(count-1)
}
}
} else {
res = astInfo.Name
if count > 1 {
res += "__" + strconv.Itoa(count-1)
}
}
return res
}
func removePrefix(str string, prefixes []string) string {
for _, prefix := range prefixes {
if strings.HasPrefix(str, prefix) {
return strings.TrimPrefix(str, prefix)
}
}
return str
}
func genSymbolTableFile(symbolInfos []types.SymbolInfo) error {
// keep open follow code block can run successfully // keep open follow code block can run successfully
for i := range symbolInfos { for i := range symbolInfos {
println("symbol", symbolInfos[i].Go) println("symbol", symbolInfos[i].Go)
@@ -269,6 +196,7 @@ func genSymbolTableFile(symbolInfos []types.SymbolInfo) error {
} }
return nil return nil
} }
func readExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, error) { func readExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, error) {
existingSymbols := make(map[string]types.SymbolInfo) existingSymbols := make(map[string]types.SymbolInfo)

View File

@@ -3,23 +3,27 @@ package parse
import ( import (
"errors" "errors"
"strconv" "strconv"
"strings"
"unsafe" "unsafe"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/clang" "github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/llcppg/types"
) )
type Context struct { type Context struct {
namespaceName string namespaceName string
className string className string
astInfo []types.ASTInformation prefixes []string
symbolMap map[string]string
currentFile string currentFile string
nameCounts map[string]int
} }
func newContext() *Context { func newContext(prefixes []string) *Context {
return &Context{ return &Context{
astInfo: make([]types.ASTInformation, 0), prefixes: prefixes,
symbolMap: make(map[string]string),
nameCounts: make(map[string]int),
} }
} }
@@ -35,70 +39,83 @@ func (c *Context) setCurrentFile(filename string) {
c.currentFile = filename c.currentFile = filename
} }
var context = newContext() func (c *Context) removePrefix(str string) string {
for _, prefix := range c.prefixes {
if strings.HasPrefix(str, prefix) {
return strings.TrimPrefix(str, prefix)
}
}
return str
}
func collectFuncInfo(cursor clang.Cursor) types.ASTInformation { func (c *Context) genGoName(name string) string {
class := c.removePrefix(c.className)
name = c.removePrefix(name)
info := types.ASTInformation{ var baseName string
Namespace: context.namespaceName, if class == "" {
Class: context.className, baseName = name
} else {
baseName = c.genMethodName(class, name)
} }
return c.addSuffix(baseName)
}
func (c *Context) genMethodName(class, name string) string {
prefix := "(*" + class + ")."
if class == name {
return prefix + "Init"
}
if name == "~"+class {
return prefix + "Dispose"
}
return prefix + name
}
func (c *Context) addSuffix(name string) string {
c.nameCounts[name]++
count := c.nameCounts[name]
if count > 1 {
return name + "__" + strconv.Itoa(count-1)
}
return name
}
var context = newContext([]string{})
func collectFuncInfo(cursor clang.Cursor) {
cursorStr := cursor.String() cursorStr := cursor.String()
symbol := cursor.Mangling() symbol := cursor.Mangling()
info.Name = c.GoString(cursorStr.CStr()) name := c.GoString(cursorStr.CStr())
symbolName := c.GoString(symbol.CStr())
info.Symbol = c.GoString(symbol.CStr()) if len(symbolName) >= 1 && symbolName[0] == '_' {
if len(info.Symbol) >= 1 { symbolName = symbolName[1:]
if info.Symbol[0] == '_' {
info.Symbol = info.Symbol[1:]
}
} }
defer symbol.Dispose() defer symbol.Dispose()
defer cursorStr.Dispose() defer cursorStr.Dispose()
if context.namespaceName != "" { goName := context.genGoName(name)
info.Namespace = context.namespaceName context.symbolMap[symbolName] = goName
}
if context.className != "" {
info.Class = context.className
}
typeStr := cursor.ResultType().String()
defer typeStr.Dispose()
info.ReturnType = c.GoString(typeStr.CStr())
info.Parameters = make([]types.Parameter, cursor.NumArguments())
for i := 0; i < int(cursor.NumArguments()); i++ {
argCurSor := cursor.Argument(c.Uint(i))
argType := argCurSor.Type().String()
argName := argCurSor.String()
info.Parameters[i] = types.Parameter{
Name: c.GoString(argName.CStr()),
Type: c.GoString(argType.CStr()),
}
argType.Dispose()
argName.Dispose()
}
return info
} }
func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult { func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult {
if cursor.Kind == clang.Namespace { if cursor.Kind == clang.CursorNamespace {
nameStr := cursor.String() nameStr := cursor.String()
defer nameStr.Dispose()
context.setNamespaceName(c.GoString(nameStr.CStr())) context.setNamespaceName(c.GoString(nameStr.CStr()))
clang.VisitChildren(cursor, visit, nil) clang.VisitChildren(cursor, visit, nil)
context.setNamespaceName("") context.setNamespaceName("")
} else if cursor.Kind == clang.ClassDecl { } else if cursor.Kind == clang.CursorClassDecl {
nameStr := cursor.String() nameStr := cursor.String()
defer nameStr.Dispose()
context.setClassName(c.GoString(nameStr.CStr())) context.setClassName(c.GoString(nameStr.CStr()))
clang.VisitChildren(cursor, visit, nil) clang.VisitChildren(cursor, visit, nil)
context.setClassName("") context.setClassName("")
} else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl || cursor.Kind == clang.Constructor || cursor.Kind == clang.Destructor { } else if cursor.Kind == clang.CursorCXXMethod || cursor.Kind == clang.CursorFunctionDecl || cursor.Kind == clang.CursorConstructor || cursor.Kind == clang.CursorDestructor {
loc := cursor.Location() loc := cursor.Location()
var file clang.File var file clang.File
var line, column c.Uint var line, column c.Uint
@@ -107,9 +124,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe
filename := file.FileName() filename := file.FileName()
if c.Strcmp(filename.CStr(), c.AllocaCStr(context.currentFile)) == 0 { if c.Strcmp(filename.CStr(), c.AllocaCStr(context.currentFile)) == 0 {
info := collectFuncInfo(cursor) collectFuncInfo(cursor)
info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column))
context.astInfo = append(context.astInfo, info)
} }
defer filename.Dispose() defer filename.Dispose()
@@ -118,14 +133,13 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe
return clang.ChildVisit_Continue return clang.ChildVisit_Continue
} }
func ParseHeaderFile(filepaths []string) ([]types.ASTInformation, error) { func ParseHeaderFile(filepaths []string, prefixes []string) (map[string]string, error) {
index := clang.CreateIndex(0, 0) index := clang.CreateIndex(0, 0)
args := make([]*c.Char, 3) args := make([]*c.Char, 3)
args[0] = c.Str("-x") args[0] = c.Str("-x")
args[1] = c.Str("c++") args[1] = c.Str("c++")
args[2] = c.Str("-std=c++11") args[2] = c.Str("-std=c++11")
context = newContext() context = newContext(prefixes)
for _, filename := range filepaths { for _, filename := range filepaths {
unit := index.ParseTranslationUnit( unit := index.ParseTranslationUnit(
@@ -149,5 +163,5 @@ func ParseHeaderFile(filepaths []string) ([]types.ASTInformation, error) {
index.Dispose() index.Dispose()
return context.astInfo, nil return context.symbolMap, nil
} }

View File

@@ -16,10 +16,6 @@
package types package types
import (
"github.com/goplus/llgo/xtool/nm"
)
// Config represents a configuration for the llcppg tool. // Config represents a configuration for the llcppg tool.
type Config struct { type Config struct {
Name string `json:"name"` Name string `json:"name"`
@@ -29,27 +25,6 @@ type Config struct {
TrimPrefixes []string `json:"trimPrefixes"` TrimPrefixes []string `json:"trimPrefixes"`
} }
type CPPSymbol struct {
DemangleName string
*nm.Symbol
}
type ASTInformation struct {
Namespace string `json:"namespace"`
Class string `json:"class"`
Name string `json:"name"`
BaseClasses []string `json:"baseClasses"`
ReturnType string `json:"returnType"`
Location string `json:"location"`
Parameters []Parameter `json:"parameters"`
Symbol string `json:"symbol"`
}
type Parameter struct {
Name string `json:"name"`
Type string `json:"type"`
}
type SymbolInfo struct { type SymbolInfo struct {
Mangle string `json:"mangle"` // C++ Symbol Mangle string `json:"mangle"` // C++ Symbol
CPP string `json:"c++"` // C++ function name CPP string `json:"c++"` // C++ function name