diff --git a/interp/interp_issue_1634_test.go b/interp/interp_issue_1634_test.go new file mode 100644 index 00000000..384a519d --- /dev/null +++ b/interp/interp_issue_1634_test.go @@ -0,0 +1,59 @@ +package interp + +import ( + "bytes" + "io" + "os" + "reflect" + "testing" +) + +func TestExportClosureArg(t *testing.T) { + outExp := []byte("0\n1\n2\n") + // catch stdout + backupStdout := os.Stdout + defer func() { + os.Stdout = backupStdout + }() + r, w, _ := os.Pipe() + os.Stdout = w + + i := New(Options{}) + err := i.Use(Exports{ + "tmp/tmp": map[string]reflect.Value{ + "Func": reflect.ValueOf(func(s *[]func(), f func()) { *s = append(*s, f) }), + }, + }) + if err != nil { + t.Error(err) + } + i.ImportUsed() + + _, err = i.Eval(` +func main() { + fs := []func(){} + + for i := 0; i < 3; i++ { + i := i + tmp.Func(&fs, func() { println(i) }) + } + for _, f := range fs { + f() + } +} +`) + if err != nil { + t.Error(err) + } + // read stdout + if err = w.Close(); err != nil { + t.Fatal(err) + } + outInterp, err := io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(outInterp, outExp) { + t.Errorf("\nGot: %q,\n want: %q", string(outInterp), string(outExp)) + } +} diff --git a/interp/run.go b/interp/run.go index ba98be75..b8caa908 100644 --- a/interp/run.go +++ b/interp/run.go @@ -987,7 +987,20 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value { } funcType := n.typ.TypeOf() + value := genValue(n) + isDefer := false + if n.anc != nil && n.anc.anc != nil && n.anc.anc.kind == deferStmt { + isDefer = true + } + return func(f *frame) reflect.Value { + v := value(f) + if !isDefer && v.Kind() == reflect.Func { + // fixes #1634, if v is already a func, then don't re-wrap + // because original wrapping cloned the frame but this doesn't + return v + } + return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value { // Allocate and init local frame. All values to be settable and addressable. fr := newFrame(f, len(def.types), f.runid())