From 962778239423b5900d00dc7a253b0f215a2c9f19 Mon Sep 17 00:00:00 2001 From: Nicholas Wiersma Date: Mon, 22 Jun 2020 15:24:04 +0200 Subject: [PATCH] feature: add Sizeof and Alignof to unsafe --- _test/unsafe5.go | 22 ++++++++++++++++++++++ interp/hooks.go | 30 ++++++++++++++++++++++++++++++ interp/interp.go | 25 +++++-------------------- interp/run.go | 2 +- stdlib/unsafe/unsafe.go | 12 ++++++++++++ 5 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 _test/unsafe5.go create mode 100644 interp/hooks.go diff --git a/_test/unsafe5.go b/_test/unsafe5.go new file mode 100644 index 00000000..e3d4c40e --- /dev/null +++ b/_test/unsafe5.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "unsafe" +) + +type S struct { + X int + Y int + Z int +} + +func main() { + size := unsafe.Sizeof(S{}) + align := unsafe.Alignof(S{}) + + fmt.Println(size, align) +} + +// Output: +// 24 8 diff --git a/interp/hooks.go b/interp/hooks.go new file mode 100644 index 00000000..5c501597 --- /dev/null +++ b/interp/hooks.go @@ -0,0 +1,30 @@ +package interp + +import "reflect" + +const hooksPath = "github.com/containous/yaegi" + +// convertFn is the signature of a symbol converter. +type convertFn func(from, to reflect.Type) func(src, dest reflect.Value) + +// hooks are external symbol bindings. +type hooks struct { + convert []convertFn +} + +func (h *hooks) Parse(m map[string]reflect.Value) { + if con, ok := getConvertFn(m["convert"]); ok { + h.convert = append(h.convert, con) + } +} + +func getConvertFn(v reflect.Value) (convertFn, bool) { + if !v.IsValid() { + return nil, false + } + fn, ok := v.Interface().(func(from, to reflect.Type) func(src, dest reflect.Value)) + if !ok { + return nil, false + } + return fn, true +} diff --git a/interp/interp.go b/interp/interp.go index 928c30e3..e40d1e49 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -96,9 +96,6 @@ func (f *frame) clone() *frame { } } -// convertFn is the signature of a symbol converter. -type convertFn func(from, to reflect.Type) func(src, dest reflect.Value) - // Exports stores the map of binary packages per package path. type Exports map[string]map[string]reflect.Value @@ -142,7 +139,7 @@ type Interpreter struct { pkgNames map[string]string // package names, indexed by path done chan struct{} // for cancellation of channel operations - convert []convertFn // converters from symbols + hooks *hooks // symbol hooks } const ( @@ -221,6 +218,7 @@ func New(options Options) *Interpreter { srcPkg: imports{}, pkgNames: map[string]string{}, rdir: map[string]bool{}, + hooks: &hooks{}, } i.opt.context.GOPATH = options.GoPath @@ -471,28 +469,15 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type { // they can be used in interpreted code. func (interp *Interpreter) Use(values Exports) { for k, v := range values { - if k != "github.com/containous/yaegi" { - interp.binPkg[k] = v + if k == hooksPath { + interp.hooks.Parse(v) continue } - if con, ok := extractConverter(v["convert"]); ok { - interp.convert = append(interp.convert, con) - } + interp.binPkg[k] = v } } -func extractConverter(v reflect.Value) (convertFn, bool) { - if !v.IsValid() { - return nil, false - } - fn, ok := v.Interface().(func(from, to reflect.Type) func(src, dest reflect.Value)) - if !ok { - return nil, false - } - return fn, true -} - // REPL performs a Read-Eval-Print-Loop on input reader. // Results are printed on output writer. func (interp *Interpreter) REPL(in io.Reader, out io.Writer) { diff --git a/interp/run.go b/interp/run.go index 667136df..e7fbd772 100644 --- a/interp/run.go +++ b/interp/run.go @@ -329,7 +329,7 @@ func convert(n *node) { value = genValue(c) } - for _, con := range n.interp.convert { + for _, con := range n.interp.hooks.convert { if c.typ.rtype == nil { continue } diff --git a/stdlib/unsafe/unsafe.go b/stdlib/unsafe/unsafe.go index 05f5b320..a6d7fd24 100644 --- a/stdlib/unsafe/unsafe.go +++ b/stdlib/unsafe/unsafe.go @@ -18,6 +18,10 @@ func init() { Symbols["github.com/containous/yaegi"] = map[string]reflect.Value{ "convert": reflect.ValueOf(convert), } + + // Add builtin functions to unsafe. + Symbols["unsafe"]["Sizeof"] = reflect.ValueOf(sizeof) + Symbols["unsafe"]["Alignof"] = reflect.ValueOf(alignof) } func convert(from, to reflect.Type) func(src, dest reflect.Value) { @@ -46,4 +50,12 @@ func convert(from, to reflect.Type) func(src, dest reflect.Value) { } } +func sizeof(i interface{}) uintptr { + return reflect.ValueOf(i).Type().Size() +} + +func alignof(i interface{}) uintptr { + return uintptr(reflect.ValueOf(i).Type().Align()) +} + //go:generate ../../cmd/goexports/goexports unsafe