From 4b1d99f7f3ee4b328430e99cb7b364ccba3a2eef Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Mon, 29 Dec 2014 11:44:01 -0500 Subject: [PATCH] go/ssa/interp: delete SSA bodies from standalone functions in "reflect" ...like we do for "runtime" functions, so that they fail informatively if executed. They all need intrinsics, but only some are yet defined. Also: - added test for issue 9462 - "BUG" in test output is now a failure in all tests (not just $GOROOT tests) - added intrinsic for reflect.SliceOf - show dynamic type of panic value Fixes issue 9462 Change-Id: I3a504c7faeed81e922fedc7dd59222717f3a7e95 Reviewed-on: https://go-review.googlesource.com/2145 Reviewed-by: Robert Griesemer --- go/ssa/interp/external.go | 1 + go/ssa/interp/interp.go | 33 ++++++++++++++++++------------- go/ssa/interp/interp_test.go | 19 ++++++------------ go/ssa/interp/reflect.go | 7 ++++++- go/ssa/interp/testdata/reflect.go | 11 +++++++++++ 5 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 go/ssa/interp/testdata/reflect.go diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go index e3a8ee07..fc43366a 100644 --- a/go/ssa/interp/external.go +++ b/go/ssa/interp/external.go @@ -80,6 +80,7 @@ func init() { "math.Min": ext۰math۰Min, "os.runtime_args": ext۰os۰runtime_args, "reflect.New": ext۰reflect۰New, + "reflect.SliceOf": ext۰reflect۰SliceOf, "reflect.TypeOf": ext۰reflect۰TypeOf, "reflect.ValueOf": ext۰reflect۰ValueOf, "reflect.init": ext۰reflect۰Init, diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go index 16a76a86..825d2d0d 100644 --- a/go/ssa/interp/interp.go +++ b/go/ssa/interp/interp.go @@ -631,6 +631,20 @@ func init() { environ = append(environ, "GOARCH="+runtime.GOARCH) } +// deleteBodies delete the bodies of all standalone functions except the +// specified ones. A missing intrinsic leads to a clear runtime error. +func deleteBodies(pkg *ssa.Package, except ...string) { + keep := make(map[string]bool) + for _, e := range except { + keep[e] = true + } + for _, mem := range pkg.Members { + if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] { + fn.Blocks = nil + } + } +} + // Interpret interprets the Go program whose main package is mainpkg. // mode specifies various interpreter options. filename and args are // the initial values of os.Args for the target program. sizes is the @@ -676,22 +690,13 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri case "syscall": setGlobal(i, pkg, "envs", environ) + case "reflect": + deleteBodies(pkg, "DeepEqual", "deepValueEqual") + case "runtime": sz := sizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type()) setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz)) - - // Delete the bodies of almost all "runtime" functions since they're magic. - // A missing intrinsic leads to a very clear error. - for _, mem := range pkg.Members { - if fn, ok := mem.(*ssa.Function); ok { - switch fn.Name() { - case "GOROOT", "gogetenv": - // keep - default: - fn.Blocks = nil - } - } - } + deleteBodies(pkg, "GOROOT", "gogetenv") } } @@ -712,7 +717,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri case string: fmt.Fprintln(os.Stderr, "panic:", p) default: - fmt.Fprintf(os.Stderr, "panic: unexpected type: %T\n", p) + fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) } // TODO(adonovan): dump panicking interpreter goroutine? diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index 456fdaf7..5d458f30 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -146,6 +146,7 @@ var testdataTests = []string{ "mrvchain.go", "range.go", "recover.go", + "reflect.go", "static.go", "callstack.go", } @@ -276,11 +277,13 @@ func printFailures(failures []string) { } } -// The "normal" success predicate. -func exitsZero(exitcode int, _ string) error { +func success(exitcode int, output string) error { if exitcode != 0 { return fmt.Errorf("exit code was %d", exitcode) } + if strings.Contains(output, "BUG") { + return fmt.Errorf("exited zero but output contained 'BUG'") + } return nil } @@ -288,7 +291,7 @@ func exitsZero(exitcode int, _ string) error { func TestTestdataFiles(t *testing.T) { var failures []string for _, input := range testdataTests { - if !run(t, "testdata"+slash, input, exitsZero) { + if !run(t, "testdata"+slash, input, success) { failures = append(failures, input) } } @@ -303,16 +306,6 @@ func TestGorootTest(t *testing.T) { var failures []string - // $GOROOT/tests are also considered a failure if they print "BUG". - success := func(exitcode int, output string) error { - if exitcode != 0 { - return fmt.Errorf("exit code was %d", exitcode) - } - if strings.Contains(output, "BUG") { - return fmt.Errorf("exited zero but output contained 'BUG'") - } - return nil - } for _, input := range gorootTestTests { if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) { failures = append(failures, input) diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go index 051695cc..fd190df9 100644 --- a/go/ssa/interp/reflect.go +++ b/go/ssa/interp/reflect.go @@ -150,8 +150,13 @@ func ext۰reflect۰New(fr *frame, args []value) value { return makeReflectValue(types.NewPointer(t), &alloc) } +func ext۰reflect۰SliceOf(fr *frame, args []value) value { + // Signature: func (t reflect.rtype) Type + return makeReflectType(rtype{types.NewSlice(args[0].(iface).v.(rtype).t)}) +} + func ext۰reflect۰TypeOf(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) string + // Signature: func (t reflect.rtype) Type return makeReflectType(rtype{args[0].(iface).t}) } diff --git a/go/ssa/interp/testdata/reflect.go b/go/ssa/interp/testdata/reflect.go new file mode 100644 index 00000000..6aa9a67a --- /dev/null +++ b/go/ssa/interp/testdata/reflect.go @@ -0,0 +1,11 @@ +package main + +import "reflect" + +func main() { + // Regression test for issue 9462. + got := reflect.SliceOf(reflect.TypeOf(byte(0))).String() + if got != "[]uint8" && got != "[]byte" { // result varies by toolchain + println("BUG: " + got) + } +}