Merge pull request #780 from luoliwoshang/llcppsymg/language

llcppsymg:parse c / c++ &  output  func proto
This commit is contained in:
xushiwei
2024-09-22 08:03:19 +08:00
committed by GitHub
3 changed files with 155 additions and 45 deletions

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}