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 eb67aeff..da679932 100644 --- a/cl/_testcpp/inline/wrap.cpp +++ b/cl/_testcpp/inline/wrap.cpp @@ -1,5 +1,9 @@ -unsigned int _llcppg__ZN3bar1fEi(int a) { +#include + +unsigned int _llcppg__ZN3bar1fEi(bar* this, int a) { + return this->f(a); } -void _llcppg__ZN3bar2_gEv() { +void _llcppg__ZN3bar2_gEv(bar* this) { + this->_g(); } 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/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/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..d8aa60cd 100644 --- a/cl/wrap.go +++ b/cl/wrap.go @@ -49,17 +49,18 @@ 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) } 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) + writeFunc(w, wrapName, fn, cls) return wrapName } @@ -67,27 +68,48 @@ 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) - b.WriteString(` { -} -`) +func writeFunc(b *writerT, name string, fn clang.Cursor, cls *classCtx) { + 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) { +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 { + notFirst = true } 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) {