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 <gri@golang.org>
This commit is contained in:
parent
ca3901c3d8
commit
4b1d99f7f3
|
@ -80,6 +80,7 @@ func init() {
|
||||||
"math.Min": ext۰math۰Min,
|
"math.Min": ext۰math۰Min,
|
||||||
"os.runtime_args": ext۰os۰runtime_args,
|
"os.runtime_args": ext۰os۰runtime_args,
|
||||||
"reflect.New": ext۰reflect۰New,
|
"reflect.New": ext۰reflect۰New,
|
||||||
|
"reflect.SliceOf": ext۰reflect۰SliceOf,
|
||||||
"reflect.TypeOf": ext۰reflect۰TypeOf,
|
"reflect.TypeOf": ext۰reflect۰TypeOf,
|
||||||
"reflect.ValueOf": ext۰reflect۰ValueOf,
|
"reflect.ValueOf": ext۰reflect۰ValueOf,
|
||||||
"reflect.init": ext۰reflect۰Init,
|
"reflect.init": ext۰reflect۰Init,
|
||||||
|
|
|
@ -631,6 +631,20 @@ func init() {
|
||||||
environ = append(environ, "GOARCH="+runtime.GOARCH)
|
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.
|
// Interpret interprets the Go program whose main package is mainpkg.
|
||||||
// mode specifies various interpreter options. filename and args are
|
// mode specifies various interpreter options. filename and args are
|
||||||
// the initial values of os.Args for the target program. sizes is the
|
// 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":
|
case "syscall":
|
||||||
setGlobal(i, pkg, "envs", environ)
|
setGlobal(i, pkg, "envs", environ)
|
||||||
|
|
||||||
|
case "reflect":
|
||||||
|
deleteBodies(pkg, "DeepEqual", "deepValueEqual")
|
||||||
|
|
||||||
case "runtime":
|
case "runtime":
|
||||||
sz := sizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type())
|
sz := sizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type())
|
||||||
setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz))
|
setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz))
|
||||||
|
deleteBodies(pkg, "GOROOT", "gogetenv")
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,7 +717,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri
|
||||||
case string:
|
case string:
|
||||||
fmt.Fprintln(os.Stderr, "panic:", p)
|
fmt.Fprintln(os.Stderr, "panic:", p)
|
||||||
default:
|
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?
|
// TODO(adonovan): dump panicking interpreter goroutine?
|
||||||
|
|
|
@ -146,6 +146,7 @@ var testdataTests = []string{
|
||||||
"mrvchain.go",
|
"mrvchain.go",
|
||||||
"range.go",
|
"range.go",
|
||||||
"recover.go",
|
"recover.go",
|
||||||
|
"reflect.go",
|
||||||
"static.go",
|
"static.go",
|
||||||
"callstack.go",
|
"callstack.go",
|
||||||
}
|
}
|
||||||
|
@ -276,11 +277,13 @@ func printFailures(failures []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "normal" success predicate.
|
func success(exitcode int, output string) error {
|
||||||
func exitsZero(exitcode int, _ string) error {
|
|
||||||
if exitcode != 0 {
|
if exitcode != 0 {
|
||||||
return fmt.Errorf("exit code was %d", exitcode)
|
return fmt.Errorf("exit code was %d", exitcode)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(output, "BUG") {
|
||||||
|
return fmt.Errorf("exited zero but output contained 'BUG'")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +291,7 @@ func exitsZero(exitcode int, _ string) error {
|
||||||
func TestTestdataFiles(t *testing.T) {
|
func TestTestdataFiles(t *testing.T) {
|
||||||
var failures []string
|
var failures []string
|
||||||
for _, input := range testdataTests {
|
for _, input := range testdataTests {
|
||||||
if !run(t, "testdata"+slash, input, exitsZero) {
|
if !run(t, "testdata"+slash, input, success) {
|
||||||
failures = append(failures, input)
|
failures = append(failures, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,16 +306,6 @@ func TestGorootTest(t *testing.T) {
|
||||||
|
|
||||||
var failures []string
|
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 {
|
for _, input := range gorootTestTests {
|
||||||
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
|
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
|
||||||
failures = append(failures, input)
|
failures = append(failures, input)
|
||||||
|
|
|
@ -150,8 +150,13 @@ func ext۰reflect۰New(fr *frame, args []value) value {
|
||||||
return makeReflectValue(types.NewPointer(t), &alloc)
|
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 {
|
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})
|
return makeReflectType(rtype{args[0].(iface).t})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue