fix for issue #1634 -- for passing a closure to exported Go function
This fixes issue #1634 includes special case for defer function. I could remove or significantly reduce the comment description, and just have that here for future reference: // per #1634, if v is already a func, then don't re-wrap! critically, the original wrapping // clones the frame, whereas the one here (below) does _not_ clone the frame, so it doesn't // generate the proper closure capture effects! // this path is the same as genValueAsFunctionWrapper which is the path taken above if // the value has an associated node, which happens when you do f := func() ..
This commit is contained in:
59
interp/interp_issue_1634_test.go
Normal file
59
interp/interp_issue_1634_test.go
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user