From 19adc625f161020c9d7297c3984d7f76cd112ad9 Mon Sep 17 00:00:00 2001 From: visualfc Date: Mon, 20 Mar 2023 22:35:24 +0800 Subject: [PATCH 01/22] load: add linkname.go --- load/linkname.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 load/linkname.go diff --git a/load/linkname.go b/load/linkname.go new file mode 100644 index 00000000..95e4d6e1 --- /dev/null +++ b/load/linkname.go @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package load + +import ( + "fmt" + "go/ast" + "go/token" + "strings" +) + +type LinkSym struct { + PkgPath string + Name string + Linkname Linkname +} + +type Linkname struct { + PkgPath string + Name string + Recv string + Method string +} + +// ParseLinkname parse ast files go:linkname +// //go:linkname . +// //go:linkname .. +// //go:linkname .<(*type)>. +func ParseLinkname(fset *token.FileSet, pkgPath string, files []*ast.File) ([]*LinkSym, error) { + var links []*LinkSym + for _, file := range files { + var hasUnsafe bool + for _, imp := range file.Imports { + if imp.Path.Value == `"unsafe"` { + hasUnsafe = true + } + } + for _, cg := range file.Comments { + for _, c := range cg.List { + link, err := parseLinknameComment(pkgPath, file, c, hasUnsafe) + if err != nil { + return nil, fmt.Errorf("%s: %w", fset.Position(c.Pos()), err) + } + links = append(links, link) + } + } + } + return links, nil +} + +func parseLinknameComment(pkgPath string, file *ast.File, comment *ast.Comment, hasUnsafe bool) (*LinkSym, error) { + if !strings.HasPrefix(comment.Text, "//go:linkname ") { + return nil, nil + } + if !hasUnsafe { + return nil, fmt.Errorf(`//go:linkname only allowed in Go files that import "unsafe"`) + } + fields := strings.Fields(comment.Text) + if len(fields) != 3 { + return nil, fmt.Errorf(`usage: //go:linkname localname [linkname]`) + } + + localName := fields[1] + linkPkg, linkName := "", fields[2] + if pos := strings.LastIndexByte(linkName, '/'); pos != -1 { + if idx := strings.IndexByte(linkName[pos+1:], '.'); idx != -1 { + linkPkg, linkName = linkName[0:pos+idx+1], linkName[pos+idx+2:] + } + } else if idx := strings.IndexByte(linkName, '.'); idx != -1 { + linkPkg, linkName = linkName[0:idx], linkName[idx+1:] + } + + obj := file.Scope.Lookup(localName) + if obj == nil || obj.Kind != ast.Fun || obj.Kind != ast.Var { + return nil, fmt.Errorf("//go:linkname must refer to declared function or variable") + } + + var recv, method string + pos := strings.IndexByte(linkName, '.') + if pos != -1 { + recv, method = linkName[:pos], linkName[pos+1:] + size := len(recv) + if size > 2 && recv[0] == '(' && recv[size-1] == ')' { + recv = recv[1 : size-1] + } + } + return &LinkSym{ + PkgPath: pkgPath, + Name: localName, + Linkname: Linkname{PkgPath: linkPkg, Name: linkName, Recv: recv, Method: method}, + }, nil +} From 9eae51a253017660500a388bc29a183819ece724 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 21 Mar 2023 07:45:43 +0800 Subject: [PATCH 02/22] sourcePackage: parse linkname --- context.go | 4 ++++ load/linkname.go | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 84c9c8a8..ea188119 100644 --- a/context.go +++ b/context.go @@ -108,6 +108,7 @@ type sourcePackage struct { Info *types.Info Dir string Files []*ast.File + Links []*load.LinkSym } func (sp *sourcePackage) Load() (err error) { @@ -140,6 +141,9 @@ func (sp *sourcePackage) Load() (err error) { } } types.NewChecker(conf, sp.Context.FileSet, sp.Package, sp.Info).Files(sp.Files) + if err == nil { + sp.Links, err = load.ParseLinkname(sp.Context.FileSet, sp.Package.Path(), sp.Files) + } } return } diff --git a/load/linkname.go b/load/linkname.go index 95e4d6e1..7ecd3d38 100644 --- a/load/linkname.go +++ b/load/linkname.go @@ -29,6 +29,10 @@ type LinkSym struct { Linkname Linkname } +func (l *LinkSym) String() string { + return l.PkgPath + "." + l.Name + "->" + l.Linkname.PkgPath + "." + l.Linkname.Name +} + type Linkname struct { PkgPath string Name string @@ -55,7 +59,9 @@ func ParseLinkname(fset *token.FileSet, pkgPath string, files []*ast.File) ([]*L if err != nil { return nil, fmt.Errorf("%s: %w", fset.Position(c.Pos()), err) } - links = append(links, link) + if link != nil { + links = append(links, link) + } } } } @@ -85,10 +91,14 @@ func parseLinknameComment(pkgPath string, file *ast.File, comment *ast.Comment, } obj := file.Scope.Lookup(localName) - if obj == nil || obj.Kind != ast.Fun || obj.Kind != ast.Var { + if obj == nil || (obj.Kind != ast.Fun && obj.Kind != ast.Var) { return nil, fmt.Errorf("//go:linkname must refer to declared function or variable") } + if pkgPath == linkPkg && localName == linkName { + return nil, nil + } + var recv, method string pos := strings.IndexByte(linkName, '.') if pos != -1 { From 79b5a93c3772f125a302421d5f36df1dbefeb128 Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 21 Mar 2023 17:37:25 +0800 Subject: [PATCH 03/22] visit: find linkname function --- visit.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/visit.go b/visit.go index e818d247..48e8859d 100644 --- a/visit.go +++ b/visit.go @@ -23,6 +23,7 @@ import ( "reflect" "strings" + "github.com/visualfc/xtype" "golang.org/x/tools/go/ssa" ) @@ -100,6 +101,22 @@ func (visit *visitor) program() { } } +func (visit *visitor) findLink(fn *ssa.Function) *ssa.Function { + if sp, ok := visit.intp.ctx.pkgs[fn.Pkg.Pkg.Path()]; ok { + for _, link := range sp.Links { + if link.Name == fn.Name() { + for pkg := range visit.pkgs { + if pkg.Pkg.Path() == link.Linkname.PkgPath { + return pkg.Func(link.Linkname.Name) + } + } + break + } + } + } + return nil +} + func (visit *visitor) function(fn *ssa.Function) { if visit.seen[fn] { return @@ -117,15 +134,26 @@ func (visit *visitor) function(fn *ssa.Function) { if fn.Blocks == nil { if _, ok := visit.pkgs[fn.Pkg]; ok { if _, ok = findExternFunc(visit.intp, fn); !ok { + if link := visit.findLink(fn); link != nil { + ftyp := visit.intp.preToType(fn.Type()) + typ := visit.intp.preToType(link.Type()) + pfn := visit.intp.loadFunction(link) + ext := visit.intp.makeFunction(typ, pfn, nil) + if typ != ftyp && checkFuncCompatible(typ, ftyp) { + ext = xtype.ConvertFunc(ext, xtype.TypeOfType(typ)) + } + visit.intp.ctx.override[fnPath] = ext + return + } if visit.intp.ctx.Mode&EnableNoStrict != 0 { typ := visit.intp.preToType(fn.Type()) numOut := typ.NumOut() if numOut == 0 { - externValues[fnPath] = reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { + visit.intp.ctx.override[fnPath] = reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { return }) } else { - externValues[fnPath] = reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { + visit.intp.ctx.override[fnPath] = reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { results = make([]reflect.Value, numOut) for i := 0; i < numOut; i++ { results[i] = reflect.New(typ.Out(i)).Elem() From 2468222a805d599e5e78e908f26cc7cf5ba93e5e Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 21 Mar 2023 22:13:31 +0800 Subject: [PATCH 04/22] visit: find linkname method --- visit.go | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/visit.go b/visit.go index 48e8859d..7194df0d 100644 --- a/visit.go +++ b/visit.go @@ -19,6 +19,7 @@ package igop import ( "fmt" "go/token" + "go/types" "log" "reflect" "strings" @@ -107,6 +108,20 @@ func (visit *visitor) findLink(fn *ssa.Function) *ssa.Function { if link.Name == fn.Name() { for pkg := range visit.pkgs { if pkg.Pkg.Path() == link.Linkname.PkgPath { + if typ := link.Linkname.Recv; typ != "" { + var star bool + if typ[0] == '*' { + star = true + typ = typ[1:] + } + if obj := pkg.Pkg.Scope().Lookup(typ); obj != nil { + t := obj.Type() + if star { + t = types.NewPointer(t) + } + return visit.prog.LookupMethod(t, pkg.Pkg, link.Linkname.Method) + } + } return pkg.Func(link.Linkname.Name) } } @@ -117,6 +132,17 @@ func (visit *visitor) findLink(fn *ssa.Function) *ssa.Function { return nil } +func wrapMethodType(sig *types.Signature) *types.Signature { + params := sig.Params() + n := params.Len() + list := make([]*types.Var, n+1) + list[0] = sig.Recv() + for i := 0; i < n; i++ { + list[i+1] = params.At(i) + } + return types.NewSignature(nil, types.NewTuple(list...), sig.Results(), sig.Variadic()) +} + func (visit *visitor) function(fn *ssa.Function) { if visit.seen[fn] { return @@ -135,10 +161,15 @@ func (visit *visitor) function(fn *ssa.Function) { if _, ok := visit.pkgs[fn.Pkg]; ok { if _, ok = findExternFunc(visit.intp, fn); !ok { if link := visit.findLink(fn); link != nil { - ftyp := visit.intp.preToType(fn.Type()) - typ := visit.intp.preToType(link.Type()) + visit.function(link) + sig := link.Signature + if sig.Recv() != nil { + sig = wrapMethodType(sig) + } + typ := visit.intp.preToType(sig) pfn := visit.intp.loadFunction(link) ext := visit.intp.makeFunction(typ, pfn, nil) + ftyp := visit.intp.preToType(fn.Type()) if typ != ftyp && checkFuncCompatible(typ, ftyp) { ext = xtype.ConvertFunc(ext, xtype.TypeOfType(typ)) } From 84f3930a44119de25c2ca8991c45b645cd165527 Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 22 Mar 2023 16:21:33 +0800 Subject: [PATCH 05/22] visit: finc linksym check user func --- cmd/igop/gox_support.go | 3 +- context.go | 4 +- visit.go | 85 +++++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/cmd/igop/gox_support.go b/cmd/igop/gox_support.go index 4122e35d..b3f66792 100644 --- a/cmd/igop/gox_support.go +++ b/cmd/igop/gox_support.go @@ -32,5 +32,6 @@ type positioner interface { func checker_infer(check *types.Checker, posn positioner, tparams []*types.TypeParam, targs []types.Type, params *types.Tuple, args []*operand) (result []types.Type) func init() { - igop.RegisterExternal("github.com/goplus/gox.checker_infer", checker_infer) + // support github.com/goplus/gox.checker_infer + igop.RegisterExternal("go/types.(*Checker).infer", checker_infer) } diff --git a/context.go b/context.go index ea188119..ee9883de 100644 --- a/context.go +++ b/context.go @@ -383,7 +383,7 @@ func (ctx *Context) loadTestPackage(bp *build.Package, path string, dir string) if err != nil { return nil, err } - f, err := parser.ParseFile(ctx.FileSet, "_testmain.go", data, 0) + f, err := parser.ParseFile(ctx.FileSet, "_testmain.go", data, parser.ParseComments) if err != nil { return nil, err } @@ -404,7 +404,7 @@ func (ctx *Context) parseGoFiles(dir string, filenames []string) ([]*ast.File, e for i, filename := range filenames { go func(i int, filepath string) { defer wg.Done() - files[i], errors[i] = parser.ParseFile(ctx.FileSet, filepath, nil, 0) + files[i], errors[i] = parser.ParseFile(ctx.FileSet, filepath, nil, parser.ParseComments) }(i, filepath.Join(dir, filename)) } wg.Wait() diff --git a/visit.go b/visit.go index 7194df0d..e942805a 100644 --- a/visit.go +++ b/visit.go @@ -24,6 +24,7 @@ import ( "reflect" "strings" + "github.com/goplus/igop/load" "github.com/visualfc/xtype" "golang.org/x/tools/go/ssa" ) @@ -102,31 +103,35 @@ func (visit *visitor) program() { } } -func (visit *visitor) findLink(fn *ssa.Function) *ssa.Function { +func (visit *visitor) findLinkSym(fn *ssa.Function) (*load.LinkSym, bool) { if sp, ok := visit.intp.ctx.pkgs[fn.Pkg.Pkg.Path()]; ok { for _, link := range sp.Links { if link.Name == fn.Name() { - for pkg := range visit.pkgs { - if pkg.Pkg.Path() == link.Linkname.PkgPath { - if typ := link.Linkname.Recv; typ != "" { - var star bool - if typ[0] == '*' { - star = true - typ = typ[1:] - } - if obj := pkg.Pkg.Scope().Lookup(typ); obj != nil { - t := obj.Type() - if star { - t = types.NewPointer(t) - } - return visit.prog.LookupMethod(t, pkg.Pkg, link.Linkname.Method) - } - } - return pkg.Func(link.Linkname.Name) + return link, true + } + } + } + return nil, false +} + +func (visit *visitor) findFunction(sym *load.LinkSym) *ssa.Function { + for pkg := range visit.pkgs { + if pkg.Pkg.Path() == sym.Linkname.PkgPath { + if typ := sym.Linkname.Recv; typ != "" { + var star bool + if typ[0] == '*' { + star = true + typ = typ[1:] + } + if obj := pkg.Pkg.Scope().Lookup(typ); obj != nil { + t := obj.Type() + if star { + t = types.NewPointer(t) } + return visit.prog.LookupMethod(t, pkg.Pkg, sym.Linkname.Method) } - break } + return pkg.Func(sym.Linkname.Name) } } return nil @@ -143,6 +148,25 @@ func wrapMethodType(sig *types.Signature) *types.Signature { return types.NewSignature(nil, types.NewTuple(list...), sig.Results(), sig.Variadic()) } +func (visit *visitor) findLinkFunc(sym *load.LinkSym) (ext reflect.Value, ok bool) { + ext, ok = findUserFunc(visit.intp, sym.Linkname.PkgPath+"."+sym.Linkname.Name) + if ok { + return + } + if link := visit.findFunction(sym); link != nil { + visit.function(link) + sig := link.Signature + if sig.Recv() != nil { + sig = wrapMethodType(sig) + } + typ := visit.intp.preToType(sig) + pfn := visit.intp.loadFunction(link) + ext = visit.intp.makeFunction(typ, pfn, nil) + ok = true + } + return +} + func (visit *visitor) function(fn *ssa.Function) { if visit.seen[fn] { return @@ -160,21 +184,16 @@ func (visit *visitor) function(fn *ssa.Function) { if fn.Blocks == nil { if _, ok := visit.pkgs[fn.Pkg]; ok { if _, ok = findExternFunc(visit.intp, fn); !ok { - if link := visit.findLink(fn); link != nil { - visit.function(link) - sig := link.Signature - if sig.Recv() != nil { - sig = wrapMethodType(sig) - } - typ := visit.intp.preToType(sig) - pfn := visit.intp.loadFunction(link) - ext := visit.intp.makeFunction(typ, pfn, nil) - ftyp := visit.intp.preToType(fn.Type()) - if typ != ftyp && checkFuncCompatible(typ, ftyp) { - ext = xtype.ConvertFunc(ext, xtype.TypeOfType(typ)) + if sym, ok := visit.findLinkSym(fn); ok { + if ext, ok := visit.findLinkFunc(sym); ok { + typ := visit.intp.preToType(fn.Type()) + ftyp := ext.Type() + if typ != ftyp && checkFuncCompatible(typ, ftyp) { + ext = xtype.ConvertFunc(ext, xtype.TypeOfType(typ)) + } + visit.intp.ctx.override[fnPath] = ext + return } - visit.intp.ctx.override[fnPath] = ext - return } if visit.intp.ctx.Mode&EnableNoStrict != 0 { typ := visit.intp.preToType(fn.Type()) From b678479578a9ea43a412721791fa1865af819e87 Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 22 Mar 2023 17:52:17 +0800 Subject: [PATCH 06/22] test: move runtime test --- interp_test.go | 613 ------------------------------------------------ runtime_test.go | 613 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 613 insertions(+), 613 deletions(-) diff --git a/interp_test.go b/interp_test.go index 9c41ed33..4d5e4218 100644 --- a/interp_test.go +++ b/interp_test.go @@ -2498,619 +2498,6 @@ func Get() OutputID { } } -func TestExperimentFuncForPC(t *testing.T) { - src := `package main - -import ( - "fmt" - "reflect" - "runtime" -) - -func test() { - println("hello") -} - -func main() { - pc := reflect.ValueOf(test).Pointer() - fn := runtime.FuncForPC(pc) - if fn == nil { - panic("error runtime.FuncForPC") - } - if fn.Name() != "main.test" { - panic("error name: " + fn.Name()) - } - file, line := fn.FileLine(fn.Entry()) - if file != "main.go" { - panic("error file:" + file) - } - if line != 9 { - panic(fmt.Errorf("error line: %v", line)) - } -} -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCaller(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - panic("error g1") - }() - defer func() { - panic("error g2") - }() - f() - panic("unreachable") -} - -func main() { - defer func() { - recover() - var list []string - for i := 0; ; i++ { - pc, file, line, ok := runtime.Caller(i) - if !ok { - break - } - fn := runtime.FuncForPC(pc) - fnName := fn.Name() - if fnName == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(fnName, "main.") { - _, fname := filepath.Split(file) - list = append(list, fmt.Sprintf("%v %v:%v", fnName, fname, line)) - } - fmt.Println(pc, fnName, file, line) - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:41 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.g.func2 main.go:30 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:59]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers1(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - // defer func() { - // panic("error g1") - // }() - // defer func() { - // panic("error g2") - // }() - f() - panic("unreachable") -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers2(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - panic("error g1") - }() - // defer func() { - // panic("error g2") - // }() - f() - panic("unreachable") -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers3(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - panic("error g1") - }() - defer func() { - panic("error g2") - }() - f() - panic("unreachable") -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.g.func2 main.go:30 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers4(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - // panic("error g1") - }() - defer func() { - // panic("error g2") - f() - }() - // f() -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 main.g main.go:34 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers5(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - // panic("error g1") - }() - defer func() { - // panic("error g2") - f() - }() - f() -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 runtime.gopanic main.f main.go:18 main.g main.go:33 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallers6(t *testing.T) { - src := `package main - -import ( - "fmt" - "path/filepath" - "runtime" - "strings" -) - -var t *struct { - c chan int -} - -var c chan int - -func f() { - select { - case <-t.c: // THIS IS LINE 18 - break - case <-c: - break - } -} - -func g() { - defer func() { - panic("error g1") - }() - defer func() { - // panic("error g2") - f() - }() - // f() -} - -func main() { - defer func() { - recover() - rpc := make([]uintptr, 64) - runtime.Callers(0, rpc) - fs := runtime.CallersFrames(rpc) - var list []string - for { - f, more := fs.Next() - if f.Function == "runtime.gopanic" { - list = append(list, "runtime.gopanic") - } else if strings.HasPrefix(f.Function, "main.") { - _, fname := filepath.Split(f.File) - list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) - } - fmt.Println(f) - if !more { - break - } - } - if v := fmt.Sprint(list); v != infos { - panic(v) - } - }() - g() -} - -var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 main.g main.go:34 main.main main.go:60]" -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - -func TestRuntimeCallerMethodWrapper(t *testing.T) { - src := `package main - -import ( - "fmt" - "reflect" - "runtime" - "strings" -) - -type T int - -func (T) f() int { return 0 } - -func (*T) g() int { return 0 } - -type I interface { - f() int -} - -var ( - index int -) - -func check(v interface{}, name string, autogen bool) { - index++ - pc := reflect.ValueOf(v).Pointer() - fn := runtime.FuncForPC(pc) - typ := reflect.ValueOf(v).Type() - file, line := fn.FileLine(pc) - fmt.Println(typ, fn.Name(), file, line) - if name != "" && name != fn.Name() { - panic(fmt.Errorf("%v: name got %v, want %v", index, fn.Name(), name)) - } - if autogen { - if file != "" || line != 1 { - panic(fmt.Errorf("%v: file must autogen %v %v", index, file, line)) - } - } else { - if !strings.Contains(file, "main.go") { - panic(fmt.Errorf("%v: file must main.go %v %v", index, file, line)) - } - } -} - -func main() { - ver := runtime.Version()[:6] - if ver < "go1.18" { - // $thunk - check(T.f, "main.T.f", false) - check((*T).g, "main.(*T).g", false) - check((struct{ T }).f, "", true) - check((struct{ *T }).g, "", true) - // $bound - check(T(0).f, "main.T.f-fm", false) - check(new(T).f, "main.T.f-fm", false) - check(new(T).g, "main.(*T).g-fm", false) - // wrapper - check(I.f, "main.I.f", true) - } else { - // $thunk - check(T.f, "main.T.f", false) - check((*T).g, "main.(*T).g", false) - check((struct{ T }).f, "", true) - check((struct{ *T }).g, "", true) - // $bound - check(T(0).f, "main.T.f-fm", true) - check(new(T).f, "main.T.f-fm", true) - check(new(T).g, "main.(*T).g-fm", true) - // wrapper - check(I.f, "main.I.f", true) - } -} -` - _, err := igop.RunFile("main.go", src, nil, 0) - if err != nil { - t.Fatal(err) - } -} - func TestMakeChan(t *testing.T) { src := `package main diff --git a/runtime_test.go b/runtime_test.go index acd2d12e..a0a89fc9 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -7,6 +7,619 @@ import ( _ "github.com/goplus/igop/pkg/log" ) +func TestFuncForPC(t *testing.T) { + src := `package main + +import ( + "fmt" + "reflect" + "runtime" +) + +func test() { + println("hello") +} + +func main() { + pc := reflect.ValueOf(test).Pointer() + fn := runtime.FuncForPC(pc) + if fn == nil { + panic("error runtime.FuncForPC") + } + if fn.Name() != "main.test" { + panic("error name: " + fn.Name()) + } + file, line := fn.FileLine(fn.Entry()) + if file != "main.go" { + panic("error file:" + file) + } + if line != 9 { + panic(fmt.Errorf("error line: %v", line)) + } +} +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCaller(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + panic("error g1") + }() + defer func() { + panic("error g2") + }() + f() + panic("unreachable") +} + +func main() { + defer func() { + recover() + var list []string + for i := 0; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + fn := runtime.FuncForPC(pc) + fnName := fn.Name() + if fnName == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(fnName, "main.") { + _, fname := filepath.Split(file) + list = append(list, fmt.Sprintf("%v %v:%v", fnName, fname, line)) + } + fmt.Println(pc, fnName, file, line) + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:41 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.g.func2 main.go:30 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:59]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers1(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + // defer func() { + // panic("error g1") + // }() + // defer func() { + // panic("error g2") + // }() + f() + panic("unreachable") +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers2(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + panic("error g1") + }() + // defer func() { + // panic("error g2") + // }() + f() + panic("unreachable") +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers3(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + panic("error g1") + }() + defer func() { + panic("error g2") + }() + f() + panic("unreachable") +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.g.func2 main.go:30 runtime.gopanic main.f main.go:18 main.g main.go:32 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers4(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + // panic("error g1") + }() + defer func() { + // panic("error g2") + f() + }() + // f() +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 main.g main.go:34 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers5(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + // panic("error g1") + }() + defer func() { + // panic("error g2") + f() + }() + f() +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 runtime.gopanic main.f main.go:18 main.g main.go:33 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallers6(t *testing.T) { + src := `package main + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +var t *struct { + c chan int +} + +var c chan int + +func f() { + select { + case <-t.c: // THIS IS LINE 18 + break + case <-c: + break + } +} + +func g() { + defer func() { + panic("error g1") + }() + defer func() { + // panic("error g2") + f() + }() + // f() +} + +func main() { + defer func() { + recover() + rpc := make([]uintptr, 64) + runtime.Callers(0, rpc) + fs := runtime.CallersFrames(rpc) + var list []string + for { + f, more := fs.Next() + if f.Function == "runtime.gopanic" { + list = append(list, "runtime.gopanic") + } else if strings.HasPrefix(f.Function, "main.") { + _, fname := filepath.Split(f.File) + list = append(list, fmt.Sprintf("%v %v:%v", f.Function, fname, f.Line)) + } + fmt.Println(f) + if !more { + break + } + } + if v := fmt.Sprint(list); v != infos { + panic(v) + } + }() + g() +} + +var infos = "[main.main.func1 main.go:40 runtime.gopanic main.g.func1 main.go:27 runtime.gopanic main.f main.go:18 main.g.func2 main.go:31 main.g main.go:34 main.main main.go:60]" +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntimeCallerMethodWrapper(t *testing.T) { + src := `package main + +import ( + "fmt" + "reflect" + "runtime" + "strings" +) + +type T int + +func (T) f() int { return 0 } + +func (*T) g() int { return 0 } + +type I interface { + f() int +} + +var ( + index int +) + +func check(v interface{}, name string, autogen bool) { + index++ + pc := reflect.ValueOf(v).Pointer() + fn := runtime.FuncForPC(pc) + typ := reflect.ValueOf(v).Type() + file, line := fn.FileLine(pc) + fmt.Println(typ, fn.Name(), file, line) + if name != "" && name != fn.Name() { + panic(fmt.Errorf("%v: name got %v, want %v", index, fn.Name(), name)) + } + if autogen { + if file != "" || line != 1 { + panic(fmt.Errorf("%v: file must autogen %v %v", index, file, line)) + } + } else { + if !strings.Contains(file, "main.go") { + panic(fmt.Errorf("%v: file must main.go %v %v", index, file, line)) + } + } +} + +func main() { + ver := runtime.Version()[:6] + if ver < "go1.18" { + // $thunk + check(T.f, "main.T.f", false) + check((*T).g, "main.(*T).g", false) + check((struct{ T }).f, "", true) + check((struct{ *T }).g, "", true) + // $bound + check(T(0).f, "main.T.f-fm", false) + check(new(T).f, "main.T.f-fm", false) + check(new(T).g, "main.(*T).g-fm", false) + // wrapper + check(I.f, "main.I.f", true) + } else { + // $thunk + check(T.f, "main.T.f", false) + check((*T).g, "main.(*T).g", false) + check((struct{ T }).f, "", true) + check((struct{ *T }).g, "", true) + // $bound + check(T(0).f, "main.T.f-fm", true) + check(new(T).f, "main.T.f-fm", true) + check(new(T).g, "main.(*T).g-fm", true) + // wrapper + check(I.f, "main.I.f", true) + } +} +` + _, err := igop.RunFile("main.go", src, nil, 0) + if err != nil { + t.Fatal(err) + } +} + func TestGC15281(t *testing.T) { // $GOROOT/test/fixedbugs/issue15281.go src := `// run From 8a7cf7cbe7c803f464a050652eaa139182e91238 Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 22 Mar 2023 22:01:32 +0800 Subject: [PATCH 07/22] TestLinkname --- interp_test.go | 1 - runtime_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/interp_test.go b/interp_test.go index 4d5e4218..cf0d4f3c 100644 --- a/interp_test.go +++ b/interp_test.go @@ -2550,7 +2550,6 @@ type point struct { func mytest1(n myint, pt point) myint func mytest2(n myint, pt *point) myint - func main() { n := mytest1(100, point{100,200}) if n != 400 { diff --git a/runtime_test.go b/runtime_test.go index a0a89fc9..b0a4c59b 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -693,3 +693,80 @@ func inuse() int64 { t.Fatal(err) } } + +func TestLinkname(t *testing.T) { + pkg := `package pkg +type point struct { + x int + y int +} +func (t point) scale(n int) point { + return point{t.x*n,t.y*n} +} +func (t *point) setScale(n int) { + t.x *= n + t.y *= n +} +func (t *point) info() (int,int) { + return t.x,t.y +} +func add(v ...int) (sum int) { + for _, n := range v { + sum += n + } + return +} +func New(x int, y int) *point { + return &point{x,y} +} +` + src := `package main +import ( + _ "unsafe" + _ "pkg" + "fmt" +) + +//go:linkname add pkg.add +func add(v ...int) int + +type point struct { + x int + y int +} + +//go:linkname newPoint pkg.New +func newPoint(x int, y int) *point + +//go:linkname scalePoint pkg.point.scale +func scalePoint(point,int) point + +//go:linkname setScale pkg.(*point).setScale +func setScale(*point,int) + +func main() { + v := add(100,200,300) + if v != 600 { + panic(fmt.Errorf("add got %v, must 600",v)) + } + pt := newPoint(100,200) + if pt == nil || pt.x != 100 || pt.y != 200 { + panic(fmt.Errorf("newPoint got %v, must &{100 200}",pt)) + } + pt2 := scalePoint(*pt,2) + if pt2.x != 200 || pt2.y != 400 { + panic(fmt.Errorf("scalePoint got %v, must {200 400}",pt2)) + } + setScale(pt,3) + if pt.x != 300 || pt.y != 600 { + panic(fmt.Errorf("setScale got %v, must &{300 600}",pt)) + } +} +` + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + _, err := ctx.RunFile("main.go", src, nil) + if err != nil { + t.Fatal(err) + } +} From 3f364eb4ac951e25743a53cb0cd8e2c1479ef24b Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 22 Mar 2023 22:10:19 +0800 Subject: [PATCH 08/22] TestLinknameSource, TestLinknameExtern --- runtime_test.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/runtime_test.go b/runtime_test.go index b0a4c59b..b6341886 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -694,7 +694,7 @@ func inuse() int64 { } } -func TestLinkname(t *testing.T) { +func TestLinknameSource(t *testing.T) { pkg := `package pkg type point struct { x int @@ -770,3 +770,77 @@ func main() { t.Fatal(err) } } + +func TestLinknameExtern(t *testing.T) { + pkg := `package pkg +func add(v ...int) (sum int) { + for _, n := range v { + sum += n + } + return +} +` + src := `package main +import ( + _ "unsafe" + _ "pkg" + "fmt" +) + +//go:linkname add pkg.add +func add(v ...int) int + +type point struct { + x int + y int +} + +//go:linkname newPoint pkg.New +func newPoint(x int, y int) *point + +//go:linkname scalePoint pkg.point.scale +func scalePoint(point,int) point + +//go:linkname setScale pkg.(*point).setScale +func setScale(*point,int) + +func main() { + v := add(100,200,300) + if v != 600 { + panic(fmt.Errorf("add got %v, must 600",v)) + } + pt := newPoint(100,200) + if pt == nil || pt.x != 100 || pt.y != 200 { + panic(fmt.Errorf("newPoint got %v, must &{100 200}",pt)) + } + pt2 := scalePoint(*pt,2) + if pt2.x != 200 || pt2.y != 400 { + panic(fmt.Errorf("scalePoint got %v, must {200 400}",pt2)) + } + setScale(pt,3) + if pt.x != 300 || pt.y != 600 { + panic(fmt.Errorf("setScale got %v, must &{300 600}",pt)) + } +} +` + type point struct { + x int + y int + } + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + ctx.SetOverrideFunction("pkg.New", func(x int, y int) *point { + return &point{x, y} + }) + ctx.SetOverrideFunction("pkg.point.scale", func(pt point, n int) point { + return point{pt.x * n, pt.y * n} + }) + ctx.SetOverrideFunction("pkg.(*point).setScale", func(pt *point, n int) { + pt.x *= n + pt.y *= n + }) + _, err := ctx.RunFile("main.go", src, nil) + if err != nil { + t.Fatal(err) + } +} From bc1d375031f19b5909fd15cf6feb5f40be675796 Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 22 Mar 2023 22:19:21 +0800 Subject: [PATCH 09/22] TestLinknameError --- runtime_test.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/runtime_test.go b/runtime_test.go index b6341886..2f2e1715 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -844,3 +844,62 @@ func main() { t.Fatal(err) } } + +func TestLinknameError1(t *testing.T) { + pkg := `package pkg +func add(v ...int) (sum int) { + for _, n := range v { + sum += n + } + return +} +` + src := `package main +import ( + _ "pkg" +) + +//go:linkname add pkg.add +func add(v ...int) int + +func main() { +} +` + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + _, err := ctx.RunFile("main.go", src, nil) + if err == nil { + t.Fatal("must error") + } + t.Log("dump error:", err) +} + +func TestLinknameError2(t *testing.T) { + pkg := `package pkg +func add(v ...int) (sum int) { + for _, n := range v { + sum += n + } + return +} +` + src := `package main +import ( + _ "unsafe" + _ "pkg" +) + +//go:linkname add2 pkg.add +func add(v ...int) int + +func main() { +} +` + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + _, err := ctx.RunFile("main.go", src, nil) + if err == nil { + t.Fatal("must error") + } + t.Log("dump error:", err) +} From 94dab4a4a2eb2fb08497c74930861c41a8dd304f Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 23 Mar 2023 15:52:45 +0800 Subject: [PATCH 10/22] interp: check linkname var --- interp.go | 28 +++++++++++++++++++++++----- load/linkname.go | 2 ++ opblock.go | 12 ++++++------ ops.go | 2 +- repl.go | 2 +- visit.go | 2 +- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/interp.go b/interp.go index 97cd53e0..5e68e9d3 100644 --- a/interp.go +++ b/interp.go @@ -47,6 +47,7 @@ package igop import ( "fmt" + "go/ast" "go/constant" "go/token" "go/types" @@ -82,7 +83,7 @@ type Interp struct { ctx *Context mainpkg *ssa.Package // the SSA main package record *TypesRecord // lookup type and ToType - globals map[ssa.Value]value // addresses of global variables (immutable) + globals map[string]value // addresses of global variables (immutable) preloadTypes map[types.Type]reflect.Type // preload types.Type -> reflect.Type funcs map[*ssa.Function]*function // ssa.Function -> *function msets map[reflect.Type](map[string]*ssa.Function) // user defined type method sets @@ -1145,7 +1146,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ i := &Interp{ ctx: ctx, mainpkg: mainpkg, - globals: make(map[ssa.Value]value), + globals: make(map[string]value), goroutines: 1, preloadTypes: make(map[types.Type]reflect.Type), funcs: make(map[*ssa.Function]*function), @@ -1157,6 +1158,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ i.record.Load(mainpkg) var pkgs []*ssa.Package + for _, pkg := range mainpkg.Prog.AllPackages() { // skip external pkg if pkg.Func("init").Blocks == nil { @@ -1168,13 +1170,29 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ switch v := m.(type) { case *ssa.Global: typ := i.preToType(deref(v.Type())) - i.globals[v] = reflect.New(typ).Interface() + i.globals[v.String()] = reflect.New(typ).Interface() + } + } + } + // check linkname var + for _, sp := range i.ctx.pkgs { + for _, link := range sp.Links { + if link.Kind == ast.Var { + localName, targetName := link.PkgPath+"."+link.Name, link.Linkname.PkgPath+"."+link.Linkname.Name + if _, ok := i.globals[localName]; ok { + if v, ok := findExternValue(i, targetName); ok && v.Kind() == reflect.Ptr { + i.globals[localName] = v.Interface() + } else if v, ok := i.globals[targetName]; ok { + i.globals[localName] = v + } + } } } } + // check globals for repl if globals != nil { for k := range i.globals { - if fv, ok := globals[k.String()]; ok { + if fv, ok := globals[k]; ok { i.globals[k] = fv } } @@ -1306,7 +1324,7 @@ func (i *Interp) GetVarAddr(key string) (interface{}, bool) { if !ok { return nil, false } - p, ok := i.globals[v] + p, ok := i.globals[v.String()] return p, ok } diff --git a/load/linkname.go b/load/linkname.go index 7ecd3d38..fb7ad51d 100644 --- a/load/linkname.go +++ b/load/linkname.go @@ -24,6 +24,7 @@ import ( ) type LinkSym struct { + Kind ast.ObjKind PkgPath string Name string Linkname Linkname @@ -109,6 +110,7 @@ func parseLinknameComment(pkgPath string, file *ast.File, comment *ast.Comment, } } return &LinkSym{ + Kind: obj.Kind, PkgPath: pkgPath, Name: localName, Linkname: Linkname{PkgPath: linkPkg, Name: linkName, Recv: recv, Method: method}, diff --git a/opblock.go b/opblock.go index 411147f5..8e449192 100644 --- a/opblock.go +++ b/opblock.go @@ -239,12 +239,12 @@ func (p *function) regInstr(v ssa.Value) uint32 { return i } -func findUserFunc(interp *Interp, fnName string) (ext reflect.Value, ok bool) { - // check override func - ext, ok = interp.ctx.override[fnName] +func findExternValue(interp *Interp, name string) (ext reflect.Value, ok bool) { + // check override value + ext, ok = interp.ctx.override[name] if !ok { - // check extern func - ext, ok = externValues[fnName] + // check extern value + ext, ok = externValues[name] } return } @@ -275,7 +275,7 @@ func checkFuncCompatible(t1, t2 reflect.Type) bool { func findExternFunc(interp *Interp, fn *ssa.Function) (ext reflect.Value, ok bool) { fnName := fn.String() - ext, ok = findUserFunc(interp, fnName) + ext, ok = findExternValue(interp, fnName) if ok { typ := interp.preToType(fn.Type()) ftyp := ext.Type() diff --git a/ops.go b/ops.go index 522b21d8..fbeb7e00 100644 --- a/ops.go +++ b/ops.go @@ -110,7 +110,7 @@ func globalToValue(i *Interp, key *ssa.Global) (interface{}, bool) { } } } - if v, ok := i.globals[key]; ok { + if v, ok := i.globals[key.String()]; ok { return v, true } return nil, false diff --git a/repl.go b/repl.go index cf5fb961..484dd883 100644 --- a/repl.go +++ b/repl.go @@ -260,7 +260,7 @@ func (r *Repl) eval(tok token.Token, expr string) (err error) { r.fsInit = rinit r.fsMain = rmain for k, v := range i.globals { - r.globalMap[k.String()] = v + r.globalMap[k] = v } if inMain { r.infuncs = append(r.infuncs, expr) diff --git a/visit.go b/visit.go index e942805a..1dd3d5d0 100644 --- a/visit.go +++ b/visit.go @@ -149,7 +149,7 @@ func wrapMethodType(sig *types.Signature) *types.Signature { } func (visit *visitor) findLinkFunc(sym *load.LinkSym) (ext reflect.Value, ok bool) { - ext, ok = findUserFunc(visit.intp, sym.Linkname.PkgPath+"."+sym.Linkname.Name) + ext, ok = findExternValue(visit.intp, sym.Linkname.PkgPath+"."+sym.Linkname.Name) if ok { return } From 1e2284ae072dcafe462d5ae4a774b3666376e3a5 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 23 Mar 2023 16:17:40 +0800 Subject: [PATCH 11/22] TestLinknameVar --- runtime_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/runtime_test.go b/runtime_test.go index 2f2e1715..42634eca 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -845,6 +845,49 @@ func main() { } } +func TestLinknameVar(t *testing.T) { + pkg := `package pkg +var n int = 100 +func N() int { + return n +} +` + src := `package main +import ( + _ "unsafe" + "pkg" + "fmt" +) + +//go:linkname v pkg.n +var v int + +//go:linkname e igop_test_linkname.e +var e int + +func main() { + if v != 100 { + panic(fmt.Errorf("v got %v, must 100",v)) + } + v = 200 + if pkg.N() != 200 { + panic(fmt.Errorf("pkg.N got %v, must 200",pkg.N())) + } + if e != 100 { + panic(fmt.Errorf("3 got %v, must 100",e)) + } +} +` + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + var e int = 100 + igop.RegisterExternal("igop_test_linkname.e", &e) + _, err := ctx.RunFile("main.go", src, nil) + if err != nil { + t.Fatal(err) + } +} + func TestLinknameError1(t *testing.T) { pkg := `package pkg func add(v ...int) (sum int) { From 421bb23c62d2b9adb3e1144b1359f8a86892e280 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 23 Mar 2023 19:38:08 +0800 Subject: [PATCH 12/22] context: rename SetOverrideFunction to RegisterExternal --- context.go | 25 +++++++++++++++---------- interp_test.go | 6 +++--- package.go | 15 +++++++++++++-- repl.go | 2 +- runtime_test.go | 6 +++--- typeparam_test.go | 2 +- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/context.go b/context.go index ee9883de..2632c473 100644 --- a/context.go +++ b/context.go @@ -26,6 +26,7 @@ import ( "go/token" "go/types" "io" + "log" "os" "path/filepath" "reflect" @@ -165,7 +166,7 @@ func NewContext(mode Mode) *Context { ctx.BuilderMode |= ssa.PrintFunctions } if mode&ExperimentalSupportGC != 0 { - ctx.SetOverrideFunction("runtime.GC", runtimeGC) + ctx.RegisterExternal("runtime.GC", runtimeGC) } ctx.sizes = types.SizesFor("gc", runtime.GOARCH) ctx.Lookup = new(load.ListDriver).Lookup @@ -196,15 +197,19 @@ func (ctx *Context) SetDebug(fn func(*DebugInfo)) { ctx.debugFunc = fn } -// SetOverrideFunction register external function to override function. -// match func fullname and signature -func (ctx *Context) SetOverrideFunction(key string, fn interface{}) { - ctx.override[key] = reflect.ValueOf(fn) -} - -// ClearOverrideFunction reset override function -func (ctx *Context) ClearOverrideFunction(key string) { - delete(ctx.override, key) +// RegisterExternal register external value must variable address or func. +func (ctx *Context) RegisterExternal(key string, i interface{}) { + if i == nil { + delete(ctx.override, key) + return + } + v := reflect.ValueOf(i) + switch v.Kind() { + case reflect.Func, reflect.Ptr: + ctx.override[key] = v + default: + log.Printf("register external must variable address or func. not %v\n", v.Kind()) + } } // SetPrintOutput is captured builtin print/println output diff --git a/interp_test.go b/interp_test.go index cf0d4f3c..f6e7ef59 100644 --- a/interp_test.go +++ b/interp_test.go @@ -127,7 +127,7 @@ func main() { func TestOverrideFunction(t *testing.T) { ctx := igop.NewContext(0) - ctx.SetOverrideFunction("main.call", func(i, j int) int { + ctx.RegisterExternal("main.call", func(i, j int) int { return i * j }) src := `package main @@ -148,7 +148,7 @@ func main() { } // reset override func - ctx.ClearOverrideFunction("main.call") + ctx.RegisterExternal("main.call", nil) _, err = ctx.RunFile("main.go", src, nil) if err == nil || err.Error() != "30" { t.Fatal("must panic 30") @@ -2568,7 +2568,7 @@ func main() { }) int { return n + pt.x + pt.y }) - ctx.SetOverrideFunction("main.mytest2", func(n int, pt *struct { + ctx.RegisterExternal("main.mytest2", func(n int, pt *struct { x int y int }) int { diff --git a/package.go b/package.go index 31c147a9..dfc498a5 100644 --- a/package.go +++ b/package.go @@ -18,6 +18,7 @@ package igop import ( "go/constant" + "log" "reflect" "sort" ) @@ -98,7 +99,17 @@ var ( externValues = make(map[string]reflect.Value) ) -// RegisterExternal is register external function for no function body +// RegisterExternal is register external variable address or func func RegisterExternal(key string, i interface{}) { - externValues[key] = reflect.ValueOf(i) + if i == nil { + delete(externValues, key) + return + } + v := reflect.ValueOf(i) + switch v.Kind() { + case reflect.Func, reflect.Ptr: + externValues[key] = v + default: + log.Printf("register external must variable address or func. not %v\n", v.Kind()) + } } diff --git a/repl.go b/repl.go index 484dd883..7920b53e 100644 --- a/repl.go +++ b/repl.go @@ -81,7 +81,7 @@ func NewRepl(ctx *Context) *Repl { r.lastDump = toDump(v) }) // reset runtime.GC to default - ctx.SetOverrideFunction("runtime.GC", runtime.GC) + ctx.RegisterExternal("runtime.GC", runtime.GC) ctx.evalCallFn = func(interp *Interp, call *ssa.Call, res ...interface{}) { if len(*call.Referrers()) != 0 { return diff --git a/runtime_test.go b/runtime_test.go index 42634eca..e83f567b 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -829,13 +829,13 @@ func main() { } ctx := igop.NewContext(0) ctx.AddImportFile("pkg", "pkg.go", pkg) - ctx.SetOverrideFunction("pkg.New", func(x int, y int) *point { + ctx.RegisterExternal("pkg.New", func(x int, y int) *point { return &point{x, y} }) - ctx.SetOverrideFunction("pkg.point.scale", func(pt point, n int) point { + ctx.RegisterExternal("pkg.point.scale", func(pt point, n int) point { return point{pt.x * n, pt.y * n} }) - ctx.SetOverrideFunction("pkg.(*point).setScale", func(pt *point, n int) { + ctx.RegisterExternal("pkg.(*point).setScale", func(pt *point, n int) { pt.x *= n pt.y *= n }) diff --git a/typeparam_test.go b/typeparam_test.go index cb846e12..ac22d288 100644 --- a/typeparam_test.go +++ b/typeparam_test.go @@ -203,7 +203,7 @@ func eq(a, b interface{}) string { ` ctx := igop.NewContext(0) var buf bytes.Buffer - ctx.SetOverrideFunction("fmt.Printf", func(format string, a ...interface{}) (n int, err error) { + ctx.RegisterExternal("fmt.Printf", func(format string, a ...interface{}) (n int, err error) { fmt.Fprintf(&buf, format, a...) return fmt.Printf(format, a...) }) From 7ec1904147c34f0b8d6cb457cb042f8364bf0dfc Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 23 Mar 2023 19:50:57 +0800 Subject: [PATCH 13/22] interp: RegisterExternal support global value --- interp.go | 11 ++++++++--- interp_test.go | 9 ++++++++- runtime_test.go | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/interp.go b/interp.go index 5e68e9d3..3b74543d 100644 --- a/interp.go +++ b/interp.go @@ -1170,7 +1170,12 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ switch v := m.(type) { case *ssa.Global: typ := i.preToType(deref(v.Type())) - i.globals[v.String()] = reflect.New(typ).Interface() + key := v.String() + if ext, ok := findExternValue(i, key); ok && ext.Kind() == reflect.Ptr && ext.Elem().Type() == typ { + i.globals[key] = ext.Interface() + } else { + i.globals[key] = reflect.New(typ).Interface() + } } } } @@ -1180,8 +1185,8 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ if link.Kind == ast.Var { localName, targetName := link.PkgPath+"."+link.Name, link.Linkname.PkgPath+"."+link.Linkname.Name if _, ok := i.globals[localName]; ok { - if v, ok := findExternValue(i, targetName); ok && v.Kind() == reflect.Ptr { - i.globals[localName] = v.Interface() + if ext, ok := findExternValue(i, targetName); ok && ext.Kind() == reflect.Ptr { + i.globals[localName] = ext.Interface() } else if v, ok := i.globals[targetName]; ok { i.globals[localName] = v } diff --git a/interp_test.go b/interp_test.go index f6e7ef59..21984f93 100644 --- a/interp_test.go +++ b/interp_test.go @@ -125,21 +125,28 @@ func main() { } } -func TestOverrideFunction(t *testing.T) { +func TestRegisterExternal(t *testing.T) { ctx := igop.NewContext(0) ctx.RegisterExternal("main.call", func(i, j int) int { return i * j }) + var v int = 200 + ctx.RegisterExternal("main.v", &v) src := `package main func call(i, j int) int { return i+j } +var v int + func main() { if n := call(10,20); n != 200 { panic(n) } + if v != 200 { + panic(v) + } } ` _, err := ctx.RunFile("main.go", src, nil) diff --git a/runtime_test.go b/runtime_test.go index e83f567b..b8693bfd 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -862,7 +862,7 @@ import ( //go:linkname v pkg.n var v int -//go:linkname e igop_test_linkname.e +//go:linkname e pkg.e var e int func main() { @@ -881,7 +881,7 @@ func main() { ctx := igop.NewContext(0) ctx.AddImportFile("pkg", "pkg.go", pkg) var e int = 100 - igop.RegisterExternal("igop_test_linkname.e", &e) + ctx.RegisterExternal("pkg.e", &e) _, err := ctx.RunFile("main.go", src, nil) if err != nil { t.Fatal(err) From 250b7c5e8b30bf7faea2c8e167cdefc56c0f1c1e Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 24 Mar 2023 06:48:51 +0800 Subject: [PATCH 14/22] interp: check linkname vars duplicated definition --- interp.go | 21 ++++++++++++++++++++- opblock.go | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/interp.go b/interp.go index 3b74543d..6eb282ad 100644 --- a/interp.go +++ b/interp.go @@ -58,6 +58,7 @@ import ( "sync/atomic" "unsafe" + "github.com/goplus/igop/load" "github.com/goplus/reflectx" "github.com/visualfc/goid" "github.com/visualfc/xtype" @@ -84,6 +85,7 @@ type Interp struct { mainpkg *ssa.Package // the SSA main package record *TypesRecord // lookup type and ToType globals map[string]value // addresses of global variables (immutable) + chkinit map[string]bool // init vars preloadTypes map[types.Type]reflect.Type // preload types.Type -> reflect.Type funcs map[*ssa.Function]*function // ssa.Function -> *function msets map[reflect.Type](map[string]*ssa.Function) // user defined type method sets @@ -1147,6 +1149,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ ctx: ctx, mainpkg: mainpkg, globals: make(map[string]value), + chkinit: make(map[string]bool), goroutines: 1, preloadTypes: make(map[types.Type]reflect.Type), funcs: make(map[*ssa.Function]*function), @@ -1173,6 +1176,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ key := v.String() if ext, ok := findExternValue(i, key); ok && ext.Kind() == reflect.Ptr && ext.Elem().Type() == typ { i.globals[key] = ext.Interface() + i.chkinit[key] = true } else { i.globals[key] = reflect.New(typ).Interface() } @@ -1180,6 +1184,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ } } // check linkname var + links := make(map[*load.LinkSym]bool) for _, sp := range i.ctx.pkgs { for _, link := range sp.Links { if link.Kind == ast.Var { @@ -1187,8 +1192,10 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ if _, ok := i.globals[localName]; ok { if ext, ok := findExternValue(i, targetName); ok && ext.Kind() == reflect.Ptr { i.globals[localName] = ext.Interface() + links[link] = true } else if v, ok := i.globals[targetName]; ok { i.globals[localName] = v + links[link] = false } } } @@ -1202,12 +1209,24 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ } } } - // static types check err := checkPackages(i, pkgs) if err != nil { return i, err } + // check linkname duplicated definition + for link, ext := range links { + localName, targetName := link.PkgPath+"."+link.Name, link.Linkname.PkgPath+"."+link.Linkname.Name + if ext { + if i.chkinit[localName] { + return i, fmt.Errorf("duplicated definition of symbol %v, from %v and %v", targetName, link.PkgPath, link.Linkname.PkgPath) + } + } else { + if i.chkinit[localName] && i.chkinit[targetName] { + return i, fmt.Errorf("duplicated definition of symbol %v, from %v and %v", targetName, link.PkgPath, link.Linkname.PkgPath) + } + } + } return i, err } diff --git a/opblock.go b/opblock.go index 8e449192..296ee1b6 100644 --- a/opblock.go +++ b/opblock.go @@ -898,6 +898,9 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr } } } + if pfn.Fn.Name() == "init" && pfn.Fn.Synthetic == "package initializer" { + pfn.Interp.chkinit[instr.Addr.String()] = true + } ia := pfn.regIndex(instr.Addr) iv, kv, vv := pfn.regIndex3(instr.Val) if kv.isStatic() { From c0d2fe14bd0884db1475c2c0eb6902377ec54623 Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 24 Mar 2023 07:25:03 +0800 Subject: [PATCH 15/22] x --- interp.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/interp.go b/interp.go index 6eb282ad..21cb7426 100644 --- a/interp.go +++ b/interp.go @@ -1184,7 +1184,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ } } // check linkname var - links := make(map[*load.LinkSym]bool) + var links []*load.LinkSym for _, sp := range i.ctx.pkgs { for _, link := range sp.Links { if link.Kind == ast.Var { @@ -1192,10 +1192,11 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ if _, ok := i.globals[localName]; ok { if ext, ok := findExternValue(i, targetName); ok && ext.Kind() == reflect.Ptr { i.globals[localName] = ext.Interface() - links[link] = true + i.chkinit[targetName] = true + links = append(links, link) } else if v, ok := i.globals[targetName]; ok { i.globals[localName] = v - links[link] = false + links = append(links, link) } } } @@ -1215,16 +1216,10 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ return i, err } // check linkname duplicated definition - for link, ext := range links { + for _, link := range links { localName, targetName := link.PkgPath+"."+link.Name, link.Linkname.PkgPath+"."+link.Linkname.Name - if ext { - if i.chkinit[localName] { - return i, fmt.Errorf("duplicated definition of symbol %v, from %v and %v", targetName, link.PkgPath, link.Linkname.PkgPath) - } - } else { - if i.chkinit[localName] && i.chkinit[targetName] { - return i, fmt.Errorf("duplicated definition of symbol %v, from %v and %v", targetName, link.PkgPath, link.Linkname.PkgPath) - } + if i.chkinit[localName] && i.chkinit[targetName] { + return i, fmt.Errorf("duplicated definition of symbol %v, from %v and %v", targetName, link.PkgPath, link.Linkname.PkgPath) } } return i, err From cf990a5ae98e4c802f01ebe002678a4f25d90a32 Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 24 Mar 2023 19:21:00 +0800 Subject: [PATCH 16/22] fincExternVar: check linkname ctx.override/externValue/installpkg --- interp.go | 2 +- opblock.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/interp.go b/interp.go index 21cb7426..d2f75a8d 100644 --- a/interp.go +++ b/interp.go @@ -1190,7 +1190,7 @@ func newInterp(ctx *Context, mainpkg *ssa.Package, globals map[string]interface{ if link.Kind == ast.Var { localName, targetName := link.PkgPath+"."+link.Name, link.Linkname.PkgPath+"."+link.Linkname.Name if _, ok := i.globals[localName]; ok { - if ext, ok := findExternValue(i, targetName); ok && ext.Kind() == reflect.Ptr { + if ext, ok := findExternVar(i, link.Linkname.PkgPath, link.Linkname.Name); ok && ext.Kind() == reflect.Ptr { i.globals[localName] = ext.Interface() i.chkinit[targetName] = true links = append(links, link) diff --git a/opblock.go b/opblock.go index 296ee1b6..1e134f25 100644 --- a/opblock.go +++ b/opblock.go @@ -249,6 +249,25 @@ func findExternValue(interp *Interp, name string) (ext reflect.Value, ok bool) { return } +func findExternVar(interp *Interp, pkgPath string, name string) (ext reflect.Value, ok bool) { + fullName := pkgPath + "." + name + // check override value + ext, ok = interp.ctx.override[fullName] + if ok { + return + } + // check extern value + ext, ok = externValues[fullName] + if ok { + return + } + // check install pkg + if pkg, found := interp.installed(pkgPath); found { + ext, ok = pkg.Vars[name] + } + return +} + func checkFuncCompatible(t1, t2 reflect.Type) bool { i1 := t1.NumIn() i2 := t2.NumIn() From d7d1cc3dddbe5cc27b798507cee2e29032461717 Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 24 Mar 2023 19:31:27 +0800 Subject: [PATCH 17/22] TestLinknameVar --- runtime_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runtime_test.go b/runtime_test.go index b8693bfd..651f079a 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -857,6 +857,7 @@ import ( _ "unsafe" "pkg" "fmt" + "os" ) //go:linkname v pkg.n @@ -865,6 +866,9 @@ var v int //go:linkname e pkg.e var e int +//go:linkname stdout os.Stdout +var stdout *os.File + func main() { if v != 100 { panic(fmt.Errorf("v got %v, must 100",v)) @@ -876,6 +880,9 @@ func main() { if e != 100 { panic(fmt.Errorf("3 got %v, must 100",e)) } + if stdout != os.Stdout { + panic("error stdout") + } } ` ctx := igop.NewContext(0) From c771b73cae9b4a57a6e95b55f245db9d85cdbf96 Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 24 Mar 2023 19:54:29 +0800 Subject: [PATCH 18/22] findExternLinkFunc --- opblock.go | 36 ++++++++++++++++++++++++++++++++++++ visit.go | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/opblock.go b/opblock.go index 1e134f25..7ee6f284 100644 --- a/opblock.go +++ b/opblock.go @@ -27,6 +27,7 @@ import ( "sync/atomic" "unsafe" + "github.com/goplus/igop/load" "github.com/goplus/reflectx" "github.com/visualfc/funcval" "github.com/visualfc/xtype" @@ -249,6 +250,41 @@ func findExternValue(interp *Interp, name string) (ext reflect.Value, ok bool) { return } +func findExternLinkFunc(interp *Interp, link *load.Linkname) (ext reflect.Value, ok bool) { + fullName := link.PkgPath + "." + link.Name + // check override value + ext, ok = interp.ctx.override[fullName] + if ok { + return + } + // check extern value + ext, ok = externValues[fullName] + if ok { + return + } + // check install pkg + if pkg, found := interp.installed(link.PkgPath); found { + if recv := link.Recv; recv != "" { + var star bool + if recv[0] == '*' { + star = true + recv = recv[1:] + } + if typ, ok := pkg.NamedTypes[recv]; ok { + if star { + typ = reflect.PtrTo(typ) + } + if m, ok := reflectx.MethodByName(typ, link.Method); ok { + return m.Func, true + } + } + return + } + ext, ok = pkg.Funcs[link.Name] + } + return +} + func findExternVar(interp *Interp, pkgPath string, name string) (ext reflect.Value, ok bool) { fullName := pkgPath + "." + name // check override value diff --git a/visit.go b/visit.go index 1dd3d5d0..9cecbad9 100644 --- a/visit.go +++ b/visit.go @@ -149,7 +149,7 @@ func wrapMethodType(sig *types.Signature) *types.Signature { } func (visit *visitor) findLinkFunc(sym *load.LinkSym) (ext reflect.Value, ok bool) { - ext, ok = findExternValue(visit.intp, sym.Linkname.PkgPath+"."+sym.Linkname.Name) + ext, ok = findExternLinkFunc(visit.intp, &sym.Linkname) if ok { return } From fc477fa9ce81c0ac4b8ba9423c0fd139801d7e58 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 25 Mar 2023 07:18:11 +0800 Subject: [PATCH 19/22] checkFuncCompatible --- opblock.go | 10 ---------- visit.go | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/opblock.go b/opblock.go index 7ee6f284..cf565339 100644 --- a/opblock.go +++ b/opblock.go @@ -310,21 +310,11 @@ func checkFuncCompatible(t1, t2 reflect.Type) bool { if i1 != i2 { return false } - o1 := t1.NumOut() - o2 := t2.NumOut() - if o1 != o2 { - return false - } for i := 0; i < i1; i++ { if t1.In(i).Size() != t2.In(i).Size() { return false } } - for i := 0; i < o1; i++ { - if t1.Out(i).Size() != t2.Out(i).Size() { - return false - } - } return true } diff --git a/visit.go b/visit.go index 9cecbad9..250ebf94 100644 --- a/visit.go +++ b/visit.go @@ -188,7 +188,7 @@ func (visit *visitor) function(fn *ssa.Function) { if ext, ok := visit.findLinkFunc(sym); ok { typ := visit.intp.preToType(fn.Type()) ftyp := ext.Type() - if typ != ftyp && checkFuncCompatible(typ, ftyp) { + if typ != ftyp { ext = xtype.ConvertFunc(ext, xtype.TypeOfType(typ)) } visit.intp.ctx.override[fnPath] = ext From 41a7669abc94c1af2a25a64ae42bb57d78440d44 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 25 Mar 2023 20:03:58 +0800 Subject: [PATCH 20/22] update test pkgpath to pkgpath.test --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 2632c473..9138545e 100644 --- a/context.go +++ b/context.go @@ -393,7 +393,7 @@ func (ctx *Context) loadTestPackage(bp *build.Package, path string, dir string) return nil, err } return &sourcePackage{ - Package: types.NewPackage(path, "main"), + Package: types.NewPackage(path+".test", "main"), Files: []*ast.File{f}, Dir: dir, Context: ctx, From def37a27dbedd490329dca5057a93949ebc2c201 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 25 Mar 2023 20:28:55 +0800 Subject: [PATCH 21/22] TestLinknamePkg --- runtime_test.go | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/runtime_test.go b/runtime_test.go index 651f079a..5e05cf7a 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -857,7 +857,6 @@ import ( _ "unsafe" "pkg" "fmt" - "os" ) //go:linkname v pkg.n @@ -866,9 +865,6 @@ var v int //go:linkname e pkg.e var e int -//go:linkname stdout os.Stdout -var stdout *os.File - func main() { if v != 100 { panic(fmt.Errorf("v got %v, must 100",v)) @@ -880,9 +876,6 @@ func main() { if e != 100 { panic(fmt.Errorf("3 got %v, must 100",e)) } - if stdout != os.Stdout { - panic("error stdout") - } } ` ctx := igop.NewContext(0) @@ -895,6 +888,45 @@ func main() { } } +func TestLinknamePkg(t *testing.T) { + src := `package main +import ( + _ "unsafe" + _ "strings" + "os" + "bytes" +) + +//go:linkname stdout os.Stdout +var stdout *os.File + +//go:linkname join strings.Join +func join(elems []string, sep string) string + +//go:linkname writeBuffer bytes.(*Buffer).Write +func writeBuffer(buf *bytes.Buffer, data []byte) (n int, err error) + +func main() { + if stdout != os.Stdout { + panic("error stdout") + } + if v := join([]string{"hello","world"},","); v != "hello,world" { + panic("error join "+v) + } + var buf bytes.Buffer + writeBuffer(&buf,[]byte("hello")) + if v := buf.String(); v != "hello" { + panic("error write buffer "+v) + } +} +` + ctx := igop.NewContext(0) + _, err := ctx.RunFile("main.go", src, nil) + if err != nil { + t.Fatal(err) + } +} + func TestLinknameError1(t *testing.T) { pkg := `package pkg func add(v ...int) (sum int) { From 4f1a6d54f8f8c6620286d596d503204a0d1d16ac Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 25 Mar 2023 20:43:25 +0800 Subject: [PATCH 22/22] update TestLinknameError --- runtime_test.go | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/runtime_test.go b/runtime_test.go index 5e05cf7a..04fdca7d 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -953,7 +953,9 @@ func main() { if err == nil { t.Fatal("must error") } - t.Log("dump error:", err) + if err.Error() != `main.go:6:1: //go:linkname only allowed in Go files that import "unsafe"` { + t.Fatal(err) + } } func TestLinknameError2(t *testing.T) { @@ -983,5 +985,34 @@ func main() { if err == nil { t.Fatal("must error") } - t.Log("dump error:", err) + if err.Error() != `main.go:7:1: //go:linkname must refer to declared function or variable` { + t.Fatal(err) + } +} + +func TestLinknameError3(t *testing.T) { + pkg := `package pkg +var v int = 100 +` + src := `package main +import ( + _ "unsafe" + _ "pkg" +) + +//go:linkname a pkg.v +var a int = 200 + +func main() { +} +` + ctx := igop.NewContext(0) + ctx.AddImportFile("pkg", "pkg.go", pkg) + _, err := ctx.RunFile("main.go", src, nil) + if err == nil { + t.Fatal("must error") + } + if err.Error() != `duplicated definition of symbol pkg.v, from main and pkg` { + t.Fatal(err) + } }