From 8f1c08c176e3541f7fb09bbe3fd604cce040552c Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 16 Sep 2026 11:13:14 +0800 Subject: [PATCH 1/2] cl wrapInlineFunc: write c++ func prototype --- cl/_testcpp/inline/wrap.cpp | 4 ++-- cl/class.go | 8 ++++++-- cl/func.go | 9 +++++---- cl/wrap.go | 19 +++++++++++++------ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/cl/_testcpp/inline/wrap.cpp b/cl/_testcpp/inline/wrap.cpp index eb67aeff..c06e14d8 100644 --- a/cl/_testcpp/inline/wrap.cpp +++ b/cl/_testcpp/inline/wrap.cpp @@ -1,5 +1,5 @@ -unsigned int _llcppg__ZN3bar1fEi(int a) { +unsigned int _llcppg__ZN3bar1fEi(bar* this, int a) { } -void _llcppg__ZN3bar2_gEv() { +void _llcppg__ZN3bar2_gEv(bar* this) { } diff --git a/cl/class.go b/cl/class.go index ed70c819..cd59409a 100644 --- a/cl/class.go +++ b/cl/class.go @@ -35,6 +35,8 @@ type classMethod struct { type classCtx struct { scopeCtx + decl clang.Cursor + typNamed *types.Named fields []*types.Var publicMethods []*classMethod inPublic bool @@ -55,12 +57,13 @@ func compileClass(ctx *pkgCtx, scope *classCtx, cls clang.Cursor) { substObj(pkgTypes, pkgTypes.Scope(), origName, typNamed.Obj()) } scope.reorder() + scope.typNamed = typNamed for _, method := range scope.publicMethods { obj := method.obj if decl := method.outsideDecl; decl.Kind != 0 { - compileFuncOrMethod(ctx, decl, obj, typNamed) + compileFuncOrMethod(ctx, decl, obj, scope) } else { - compileFuncOrMethod(ctx, obj.decl, obj, typNamed) + compileFuncOrMethod(ctx, obj.decl, obj, scope) } } } @@ -69,6 +72,7 @@ func loadClass(ctx *pkgCtx, cls clang.Cursor, defaultInPublic bool) { pkg := ctx.pkg pkgTypes := pkg.Types scope := &classCtx{ + decl: cls, overloads: make(map[string]*overloads), inPublic: defaultInPublic, } diff --git a/cl/func.go b/cl/func.go index 50a24add..e749d690 100644 --- a/cl/func.go +++ b/cl/func.go @@ -37,7 +37,7 @@ func loadGlobalFunc(ctx *pkgCtx, scope *scopeCtx, decl clang.Cursor) { }) } -func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, typNamed *types.Named) { +func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, cls *classCtx) { manglingName := clang.Mangling(fn) origName := obj.name if fn.IsFunctionInlined() != 0 { @@ -47,7 +47,7 @@ func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, typNamed *ty } return } - manglingName = wrapInlineFunc(ctx, manglingName, fn) + manglingName = wrapInlineFunc(ctx, manglingName, fn, cls) } else if _, ok := ctx.nameLookup(manglingName); !ok { if debugCompileDecl { log.Println("func", origName, "- skipped") @@ -65,9 +65,10 @@ func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, typNamed *ty var recv *types.Var var nameInPkg string var fnName, rewritten = ctx.getPubName(origName, obj.order()) - if typNamed == nil { + if cls == nil { nameInPkg = fnName } else { + typNamed := cls.typNamed nameInPkg = "(*" + typNamed.Obj().Name() + ")." + fnName recv = types.NewParam(token.NoPos, pkgTypes, "this", types.NewPointer(typNamed)) } @@ -80,7 +81,7 @@ func compileFuncOrMethod(ctx *pkgCtx, fn clang.Cursor, obj *object, typNamed *ty log.Panicln("compileFunc:", origName, err) } - if typNamed == nil { + if cls == nil { ctx.forceImportUnsafe() f.SetComments(pkg, &ast.CommentGroup{ List: []*ast.Comment{ diff --git a/cl/wrap.go b/cl/wrap.go index 8685a6be..a3ddb6ce 100644 --- a/cl/wrap.go +++ b/cl/wrap.go @@ -49,7 +49,7 @@ func newWrapFile(ctx *pkgCtx) *WrapFile { return &WrapFile{Filename: filename} } -func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor) string { +func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor, cls *classCtx) string { first := ctx.wrap == nil if first { ctx.wrap = newWrapFile(ctx) @@ -59,7 +59,7 @@ func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor) string { w.WriteByte('\n') } wrapName := "_llcppg_" + manglingName - writeFunc(w, wrapName, fn) + writeFunc(w, wrapName, fn, cls) return wrapName } @@ -67,20 +67,27 @@ func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor) string { type writerT = bytes.Buffer -func writeFunc(b *writerT, name string, fn clang.Cursor) { - writeFuncProto(b, name, fn) +func writeFunc(b *writerT, name string, fn clang.Cursor, cls *classCtx) { + writeFuncProto(b, name, fn, cls) b.WriteString(` { } `) } -func writeFuncProto(out *writerT, name string, fn clang.Cursor) { +func writeFuncProto(out *writerT, name string, fn clang.Cursor, cls *classCtx) { var b writerT b.WriteString(name) b.WriteByte('(') + notFirst := cls != nil + if notFirst { + b.WriteString(clang.String(cls.decl.Type())) + b.WriteString("* this") + } for i := range c.Uint(fn.NumArguments()) { - if i > 0 { + if notFirst { b.WriteString(", ") + } else { + notFirst = true } arg := fn.Argument(i) argName := clang.String(arg) From eaf32355384582344f9e0dc757c0541b82e48d73 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 16 Sep 2026 11:39:51 +0800 Subject: [PATCH 2/2] cl wrapInlineFunc: call c/c++ func --- cl/_testc/inline/in.cfg | 3 ++- cl/_testc/inline/wrap.c | 4 ++++ cl/_testcpp/inline/in.cfg | 3 ++- cl/_testcpp/inline/wrap.cpp | 4 ++++ cl/cltest/cltest.go | 5 +++-- cl/compile.go | 8 ++++++-- cl/compile_test.go | 11 ++++++----- cl/ctx.go | 2 ++ cl/wrap.go | 31 +++++++++++++++++++++++-------- 9 files changed, 52 insertions(+), 19 deletions(-) diff --git a/cl/_testc/inline/in.cfg b/cl/_testc/inline/in.cfg index 06657c0b..86a2677a 100644 --- a/cl/_testc/inline/in.cfg +++ b/cl/_testc/inline/in.cfg @@ -1,4 +1,5 @@ { "LLGoPackage": "link: -L/path/foo -lfoo", - "CFlags": "-I/path/foo/include" + "CFlags": "-I/path/foo/include", + "WrapFileHeader": "#include \n" } diff --git a/cl/_testc/inline/wrap.c b/cl/_testc/inline/wrap.c index 8d0c030e..3ceaead3 100644 --- a/cl/_testc/inline/wrap.c +++ b/cl/_testc/inline/wrap.c @@ -1,5 +1,9 @@ +#include + int _llcppg_add(int a, int b) { + return add(a, b); } int _llcppg_mul(int a, int b) { + return mul(a, b); } diff --git a/cl/_testcpp/inline/in.cfg b/cl/_testcpp/inline/in.cfg index 06657c0b..86a2677a 100644 --- a/cl/_testcpp/inline/in.cfg +++ b/cl/_testcpp/inline/in.cfg @@ -1,4 +1,5 @@ { "LLGoPackage": "link: -L/path/foo -lfoo", - "CFlags": "-I/path/foo/include" + "CFlags": "-I/path/foo/include", + "WrapFileHeader": "#include \n" } diff --git a/cl/_testcpp/inline/wrap.cpp b/cl/_testcpp/inline/wrap.cpp index c06e14d8..da679932 100644 --- a/cl/_testcpp/inline/wrap.cpp +++ b/cl/_testcpp/inline/wrap.cpp @@ -1,5 +1,9 @@ +#include + unsigned int _llcppg__ZN3bar1fEi(bar* this, int a) { + return this->f(a); } void _llcppg__ZN3bar2_gEv(bar* this) { + this->_g(); } diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 9acae296..cafcd87f 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -30,8 +30,9 @@ import ( // Config represents the lltest configuration. type Config struct { - LLGoPackage string `json:"LLGoPackage"` - CFlags string `json:"CFlags"` + LLGoPackage string `json:"LLGoPackage"` + WrapFileHeader string `json:"WrapFileHeader"` + CFlags string `json:"CFlags"` } // LoadConf loads the lltest configuration. diff --git a/cl/compile.go b/cl/compile.go index ad7fe05d..c3ca0409 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -82,6 +82,10 @@ type Config struct { // If not specified, llcppg will skip wrapping inline functions/methods. CFlags string + // WrapFileHeader specifies the header content to be included at the top of the generated + // wrapper file (optional). + WrapFileHeader string + // NameLookup looks up the archive path for a given mangling name. It returns the // archive path and a boolean indicating whether the lookup was successful. If not // specified, llcppg uses a default lookup function that returns an empty archivePath @@ -136,8 +140,8 @@ func NewPackage(pkgPath, pkgName string, conf *Config, tu clang.TranslationUnit, } ctx := &pkgCtx{ pkg: pkg, cb: pkg.CB(), llgo: llgo, fset: pkg.Fset, tu: tu, c: c, - lang: conf.Language, cflags: conf.CFlags, nameLookup: nameLookup, - methods: make(map[string]*classMethod), + lang: conf.Language, cflags: conf.CFlags, wrapFileHeader: conf.WrapFileHeader, + nameLookup: nameLookup, methods: make(map[string]*classMethod), } ctx.initFiles(files) loadFiles(ctx) diff --git a/cl/compile_test.go b/cl/compile_test.go index 1f1c2d99..61517198 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -67,11 +67,12 @@ func testFromDir(t *testing.T, sel, relDir string, lang cl.Language) { conf, _ := cltest.LoadConf(pkgDir + "/in.cfg") imp := packages.NewImporter(nil) pkg, err := cl.NewPackage("", "foo", &cl.Config{ - Importer: imp, - LLGoPackage: conf.LLGoPackage, - Language: lang, - CFlags: conf.CFlags, - NameLookup: nil, + Importer: imp, + LLGoPackage: conf.LLGoPackage, + Language: lang, + WrapFileHeader: conf.WrapFileHeader, + CFlags: conf.CFlags, + NameLookup: nil, }, u, filename) if err != nil { t.Error("cl.NewPackage:", err) diff --git a/cl/ctx.go b/cl/ctx.go index 3779879b..a3c9e140 100644 --- a/cl/ctx.go +++ b/cl/ctx.go @@ -98,6 +98,8 @@ type pkgCtx struct { cflags string lang Language + wrapFileHeader string + nameLookup func(manglingName string) (archivePath string, ok bool) fileBases map[clang.File]int // clang.File => base diff --git a/cl/wrap.go b/cl/wrap.go index a3ddb6ce..d8aa60cd 100644 --- a/cl/wrap.go +++ b/cl/wrap.go @@ -55,9 +55,10 @@ func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor, cls *clas ctx.wrap = newWrapFile(ctx) } w := &ctx.wrap.Content - if !first { - w.WriteByte('\n') + if first { + w.WriteString(ctx.wrapFileHeader) } + w.WriteByte('\n') wrapName := "_llcppg_" + manglingName writeFunc(w, wrapName, fn, cls) return wrapName @@ -68,22 +69,34 @@ func wrapInlineFunc(ctx *pkgCtx, manglingName string, fn clang.Cursor, cls *clas type writerT = bytes.Buffer func writeFunc(b *writerT, name string, fn clang.Cursor, cls *classCtx) { - writeFuncProto(b, name, fn, cls) - b.WriteString(` { -} -`) + var call writerT + writeFuncProto(b, &call, name, fn, cls) + b.WriteString(" {\n") + b.Write(call.Bytes()) + b.WriteString("}\n") } -func writeFuncProto(out *writerT, name string, fn clang.Cursor, cls *classCtx) { +func writeFuncProto(out, call *writerT, name string, fn clang.Cursor, cls *classCtx) { var b writerT b.WriteString(name) b.WriteByte('(') + call.WriteByte('\t') + retType := fn.ResultType() + if retType.Kind != lc.TypeVoid { + call.WriteString("return ") + } notFirst := cls != nil if notFirst { b.WriteString(clang.String(cls.decl.Type())) b.WriteString("* this") + call.WriteString("this->") } + call.WriteString(clang.String(fn)) + call.WriteByte('(') for i := range c.Uint(fn.NumArguments()) { + if i > 0 { + call.WriteString(", ") + } if notFirst { b.WriteString(", ") } else { @@ -92,9 +105,11 @@ func writeFuncProto(out *writerT, name string, fn clang.Cursor, cls *classCtx) { arg := fn.Argument(i) argName := clang.String(arg) writeParam(&b, arg.Type(), argName) + call.WriteString(argName) } b.WriteByte(')') - writeParam(out, fn.ResultType(), b.String()) + call.WriteString(");\n") + writeParam(out, retType, b.String()) } func writeParam(b *writerT, typ lc.Type, name string) {