From fd72fd66f6942eba57acf68b91749da38ff16e9b Mon Sep 17 00:00:00 2001 From: Tim King Date: Sun, 6 Mar 2022 14:32:59 -0800 Subject: go/ssa/interp: adding external functions for tests Adding additional external functions to be available for go/ssa/interp's testdata. These functions are used by main files in $GOROOT/test/typeparams/*.go Updates golang/go#48525 Change-Id: I80b280e2efb4616d24b50ccf3d2aefa7b486a893 Reviewed-on: https://go-review.googlesource.com/c/tools/+/390294 Reviewed-by: Robert Findley Run-TryBot: Tim King gopls-CI: kokoro TryBot-Result: Gopher Robot Trust: Tim King --- go/ssa/interp/external.go | 58 +++++++++++++++++++++++++++ go/ssa/interp/testdata/src/fmt/fmt.go | 44 +++++++++++++++++--- go/ssa/interp/testdata/src/io/io.go | 5 +++ go/ssa/interp/testdata/src/log/log.go | 15 +++++++ go/ssa/interp/testdata/src/math/math.go | 2 + go/ssa/interp/testdata/src/reflect/reflect.go | 41 +++++++++++++++++++ go/ssa/interp/testdata/src/sort/sort.go | 5 +++ go/ssa/interp/testdata/src/strconv/strconv.go | 6 +++ go/ssa/interp/testdata/src/strings/strings.go | 17 ++++++++ go/ssa/interp/testdata/src/sync/sync.go | 36 +++++++++++++++++ 10 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 go/ssa/interp/testdata/src/io/io.go create mode 100644 go/ssa/interp/testdata/src/log/log.go create mode 100644 go/ssa/interp/testdata/src/sort/sort.go create mode 100644 go/ssa/interp/testdata/src/strconv/strconv.go create mode 100644 go/ssa/interp/testdata/src/sync/sync.go diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go index 68ddee318..51b3be0bd 100644 --- a/go/ssa/interp/external.go +++ b/go/ssa/interp/external.go @@ -12,6 +12,8 @@ import ( "math" "os" "runtime" + "sort" + "strconv" "strings" "time" "unicode/utf8" @@ -79,6 +81,7 @@ func init() { "math.Log": ext۰math۰Log, "math.Min": ext۰math۰Min, "math.NaN": ext۰math۰NaN, + "math.Sqrt": ext۰math۰Sqrt, "os.Exit": ext۰os۰Exit, "os.Getenv": ext۰os۰Getenv, "reflect.New": ext۰reflect۰New, @@ -93,10 +96,18 @@ func init() { "runtime.Goexit": ext۰runtime۰Goexit, "runtime.Gosched": ext۰runtime۰Gosched, "runtime.NumCPU": ext۰runtime۰NumCPU, + "sort.Float64s": ext۰sort۰Float64s, + "sort.Ints": ext۰sort۰Ints, + "sort.Strings": ext۰sort۰Strings, + "strconv.Atoi": ext۰strconv۰Atoi, + "strconv.Itoa": ext۰strconv۰Itoa, + "strconv.FormatFloat": ext۰strconv۰FormatFloat, "strings.Count": ext۰strings۰Count, + "strings.EqualFold": ext۰strings۰EqualFold, "strings.Index": ext۰strings۰Index, "strings.IndexByte": ext۰strings۰IndexByte, "strings.Replace": ext۰strings۰Replace, + "strings.ToLower": ext۰strings۰ToLower, "time.Sleep": ext۰time۰Sleep, "unicode/utf8.DecodeRuneInString": ext۰unicode۰utf8۰DecodeRuneInString, } { @@ -179,15 +190,58 @@ func ext۰math۰Log(fr *frame, args []value) value { return math.Log(args[0].(float64)) } +func ext۰math۰Sqrt(fr *frame, args []value) value { + return math.Sqrt(args[0].(float64)) +} + func ext۰runtime۰Breakpoint(fr *frame, args []value) value { runtime.Breakpoint() return nil } +func ext۰sort۰Ints(fr *frame, args []value) value { + x := args[0].([]value) + sort.Slice(x, func(i, j int) bool { + return x[i].(int) < x[j].(int) + }) + return nil +} +func ext۰sort۰Strings(fr *frame, args []value) value { + x := args[0].([]value) + sort.Slice(x, func(i, j int) bool { + return x[i].(string) < x[j].(string) + }) + return nil +} +func ext۰sort۰Float64s(fr *frame, args []value) value { + x := args[0].([]value) + sort.Slice(x, func(i, j int) bool { + return x[i].(float64) < x[j].(float64) + }) + return nil +} + +func ext۰strconv۰Atoi(fr *frame, args []value) value { + i, e := strconv.Atoi(args[0].(string)) + if e != nil { + return tuple{i, iface{fr.i.runtimeErrorString, e.Error()}} + } + return tuple{i, iface{}} +} +func ext۰strconv۰Itoa(fr *frame, args []value) value { + return strconv.Itoa(args[0].(int)) +} +func ext۰strconv۰FormatFloat(fr *frame, args []value) value { + return strconv.FormatFloat(args[0].(float64), args[1].(byte), args[2].(int), args[3].(int)) +} + func ext۰strings۰Count(fr *frame, args []value) value { return strings.Count(args[0].(string), args[1].(string)) } +func ext۰strings۰EqualFold(fr *frame, args []value) value { + return strings.EqualFold(args[0].(string), args[1].(string)) +} func ext۰strings۰IndexByte(fr *frame, args []value) value { return strings.IndexByte(args[0].(string), args[1].(byte)) } @@ -205,6 +259,10 @@ func ext۰strings۰Replace(fr *frame, args []value) value { return strings.Replace(s, old, new, n) } +func ext۰strings۰ToLower(fr *frame, args []value) value { + return strings.ToLower(args[0].(string)) +} + func ext۰runtime۰GOMAXPROCS(fr *frame, args []value) value { // Ignore args[0]; don't let the interpreted program // set the interpreter's GOMAXPROCS! diff --git a/go/ssa/interp/testdata/src/fmt/fmt.go b/go/ssa/interp/testdata/src/fmt/fmt.go index 2185eb708..af304029c 100644 --- a/go/ssa/interp/testdata/src/fmt/fmt.go +++ b/go/ssa/interp/testdata/src/fmt/fmt.go @@ -1,14 +1,28 @@ package fmt +import ( + "errors" + "strings" +) + func Sprint(args ...interface{}) string -func Print(args ...interface{}) { +func Sprintln(args ...interface{}) string { + return Sprint(args...) + "\n" +} + +func Print(args ...interface{}) (int, error) { + var n int for i, arg := range args { if i > 0 { print(" ") + n++ } - print(Sprint(arg)) + msg := Sprint(arg) + n += len(msg) + print(msg) } + return n, nil } func Println(args ...interface{}) { @@ -17,10 +31,30 @@ func Println(args ...interface{}) { } // formatting is too complex to fake +// handle the bare minimum needed for tests -func Printf(args ...interface{}) string { - panic("Printf is not supported") +func Printf(format string, args ...interface{}) (int, error) { + msg := Sprintf(format, args...) + print(msg) + return len(msg), nil } + func Sprintf(format string, args ...interface{}) string { - panic("Sprintf is not supported") + // handle extremely simple cases that appear in tests. + if len(format) == 0 { + return "" + } + switch { + case strings.HasPrefix("%v", format) || strings.HasPrefix("%s", format): + return Sprint(args[0]) + Sprintf(format[2:], args[1:]...) + case !strings.HasPrefix("%", format): + return format[:1] + Sprintf(format[1:], args...) + default: + panic("unsupported format string for testing Sprintf") + } +} + +func Errorf(format string, args ...interface{}) error { + msg := Sprintf(format, args...) + return errors.New(msg) } diff --git a/go/ssa/interp/testdata/src/io/io.go b/go/ssa/interp/testdata/src/io/io.go new file mode 100644 index 000000000..8cde43061 --- /dev/null +++ b/go/ssa/interp/testdata/src/io/io.go @@ -0,0 +1,5 @@ +package io + +import "errors" + +var EOF = errors.New("EOF") diff --git a/go/ssa/interp/testdata/src/log/log.go b/go/ssa/interp/testdata/src/log/log.go new file mode 100644 index 000000000..8897c1d21 --- /dev/null +++ b/go/ssa/interp/testdata/src/log/log.go @@ -0,0 +1,15 @@ +package log + +import ( + "fmt" + "os" +) + +func Println(v ...interface{}) { + fmt.Println(v...) +} + +func Fatalln(v ...interface{}) { + Println(v...) + os.Exit(1) +} diff --git a/go/ssa/interp/testdata/src/math/math.go b/go/ssa/interp/testdata/src/math/math.go index f51e5f572..64fe60c99 100644 --- a/go/ssa/interp/testdata/src/math/math.go +++ b/go/ssa/interp/testdata/src/math/math.go @@ -11,3 +11,5 @@ func Float64bits(float64) uint64 func Signbit(x float64) bool { return Float64bits(x)&(1<<63) != 0 } + +func Sqrt(x float64) float64 diff --git a/go/ssa/interp/testdata/src/reflect/reflect.go b/go/ssa/interp/testdata/src/reflect/reflect.go index f6c4e2794..8a23d272f 100644 --- a/go/ssa/interp/testdata/src/reflect/reflect.go +++ b/go/ssa/interp/testdata/src/reflect/reflect.go @@ -2,6 +2,8 @@ package reflect type Type interface { String() string + Kind() Kind + Elem() Type } type Value struct { @@ -9,8 +11,47 @@ type Value struct { func (Value) String() string +func (Value) Elem() string +func (Value) Kind() Kind +func (Value) Int() int64 + func SliceOf(Type) Type func TypeOf(interface{}) Type func ValueOf(interface{}) Value + +type Kind uint + +// Constants need to be kept in sync with the actual definitions for comparisons in tests. +const ( + Invalid Kind = iota + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + Array + Chan + Func + Interface + Map + Pointer + Slice + String + Struct + UnsafePointer +) + +const Ptr = Pointer diff --git a/go/ssa/interp/testdata/src/sort/sort.go b/go/ssa/interp/testdata/src/sort/sort.go new file mode 100644 index 000000000..d94d6dabe --- /dev/null +++ b/go/ssa/interp/testdata/src/sort/sort.go @@ -0,0 +1,5 @@ +package sort + +func Strings(x []string) +func Ints(x []int) +func Float64s(x []float64) diff --git a/go/ssa/interp/testdata/src/strconv/strconv.go b/go/ssa/interp/testdata/src/strconv/strconv.go new file mode 100644 index 000000000..3f6f8772b --- /dev/null +++ b/go/ssa/interp/testdata/src/strconv/strconv.go @@ -0,0 +1,6 @@ +package strconv + +func Itoa(i int) string +func Atoi(s string) (int, error) + +func FormatFloat(float64, byte, int, int) string diff --git a/go/ssa/interp/testdata/src/strings/strings.go b/go/ssa/interp/testdata/src/strings/strings.go index dd86dcf4f..4c74f1b82 100644 --- a/go/ssa/interp/testdata/src/strings/strings.go +++ b/go/ssa/interp/testdata/src/strings/strings.go @@ -7,3 +7,20 @@ func Index(haystack, needle string) int func Contains(haystack, needle string) bool { return Index(haystack, needle) >= 0 } + +func HasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[0:len(prefix)] == prefix +} + +func EqualFold(s, t string) bool +func ToLower(s string) string + +type Builder struct { + s string +} + +func (b *Builder) WriteString(s string) (int, error) { + b.s += s + return len(s), nil +} +func (b *Builder) String() string { return b.s } diff --git a/go/ssa/interp/testdata/src/sync/sync.go b/go/ssa/interp/testdata/src/sync/sync.go new file mode 100644 index 000000000..457a670d6 --- /dev/null +++ b/go/ssa/interp/testdata/src/sync/sync.go @@ -0,0 +1,36 @@ +package sync + +// Rudimentary implementation of a mutex for interp tests. +type Mutex struct { + c chan int // Mutex is held when held c!=nil and is empty. Access is guarded by g. +} + +func (m *Mutex) Lock() { + c := ch(m) + <-c +} + +func (m *Mutex) Unlock() { + c := ch(m) + c <- 1 +} + +// sequentializes Mutex.c access. +var g = make(chan int, 1) + +func init() { + g <- 1 +} + +// ch initializes the m.c field if needed and returns it. +func ch(m *Mutex) chan int { + <-g + defer func() { + g <- 1 + }() + if m.c == nil { + m.c = make(chan int, 1) + m.c <- 1 + } + return m.c +} -- cgit v1.2.3