| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package interp |
| |
| // Emulated functions that we cannot interpret because they are |
| // external or because they use "unsafe" or "reflect" operations. |
| |
| import ( |
| "go/types" |
| "math" |
| "os" |
| "runtime" |
| "strings" |
| "sync/atomic" |
| "time" |
| "unsafe" |
| |
| "golang.org/x/tools/go/ssa" |
| ) |
| |
| type externalFn func(fr *frame, args []value) value |
| |
| // TODO(adonovan): fix: reflect.Value abstracts an lvalue or an |
| // rvalue; Set() causes mutations that can be observed via aliases. |
| // We have not captured that correctly here. |
| |
| // Key strings are from Function.String(). |
| var externals = make(map[string]externalFn) |
| |
| func init() { |
| // That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd]. |
| for k, v := range map[string]externalFn{ |
| "(*sync.Pool).Get": ext۰sync۰Pool۰Get, |
| "(*sync.Pool).Put": ext۰nop, |
| "(reflect.Value).Bool": ext۰reflect۰Value۰Bool, |
| "(reflect.Value).CanAddr": ext۰reflect۰Value۰CanAddr, |
| "(reflect.Value).CanInterface": ext۰reflect۰Value۰CanInterface, |
| "(reflect.Value).Elem": ext۰reflect۰Value۰Elem, |
| "(reflect.Value).Field": ext۰reflect۰Value۰Field, |
| "(reflect.Value).Float": ext۰reflect۰Value۰Float, |
| "(reflect.Value).Index": ext۰reflect۰Value۰Index, |
| "(reflect.Value).Int": ext۰reflect۰Value۰Int, |
| "(reflect.Value).Interface": ext۰reflect۰Value۰Interface, |
| "(reflect.Value).IsNil": ext۰reflect۰Value۰IsNil, |
| "(reflect.Value).IsValid": ext۰reflect۰Value۰IsValid, |
| "(reflect.Value).Kind": ext۰reflect۰Value۰Kind, |
| "(reflect.Value).Len": ext۰reflect۰Value۰Len, |
| "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, |
| "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, |
| "(reflect.Value).NumField": ext۰reflect۰Value۰NumField, |
| "(reflect.Value).NumMethod": ext۰reflect۰Value۰NumMethod, |
| "(reflect.Value).Pointer": ext۰reflect۰Value۰Pointer, |
| "(reflect.Value).Set": ext۰reflect۰Value۰Set, |
| "(reflect.Value).String": ext۰reflect۰Value۰String, |
| "(reflect.Value).Type": ext۰reflect۰Value۰Type, |
| "(reflect.Value).Uint": ext۰reflect۰Value۰Uint, |
| "(reflect.error).Error": ext۰reflect۰error۰Error, |
| "(reflect.rtype).Bits": ext۰reflect۰rtype۰Bits, |
| "(reflect.rtype).Elem": ext۰reflect۰rtype۰Elem, |
| "(reflect.rtype).Field": ext۰reflect۰rtype۰Field, |
| "(reflect.rtype).In": ext۰reflect۰rtype۰In, |
| "(reflect.rtype).Kind": ext۰reflect۰rtype۰Kind, |
| "(reflect.rtype).NumField": ext۰reflect۰rtype۰NumField, |
| "(reflect.rtype).NumIn": ext۰reflect۰rtype۰NumIn, |
| "(reflect.rtype).NumMethod": ext۰reflect۰rtype۰NumMethod, |
| "(reflect.rtype).NumOut": ext۰reflect۰rtype۰NumOut, |
| "(reflect.rtype).Out": ext۰reflect۰rtype۰Out, |
| "(reflect.rtype).Size": ext۰reflect۰rtype۰Size, |
| "(reflect.rtype).String": ext۰reflect۰rtype۰String, |
| "bytes.init": ext۰nop, // avoid asm dependency |
| "bytes.Equal": ext۰bytes۰Equal, |
| "bytes.IndexByte": ext۰bytes۰IndexByte, |
| "hash/crc32.haveSSE42": ext۰crc32۰haveSSE42, |
| "internal/cpu.cpuid": ext۰cpu۰cpuid, |
| "internal/syscall/unix.syscall_fcntl": ext۰syscall۰unix۰syscall_fcntl, |
| "math.Abs": ext۰math۰Abs, |
| "math.Exp": ext۰math۰Exp, |
| "math.Float32bits": ext۰math۰Float32bits, |
| "math.Float32frombits": ext۰math۰Float32frombits, |
| "math.Float64bits": ext۰math۰Float64bits, |
| "math.Float64frombits": ext۰math۰Float64frombits, |
| "math.Ldexp": ext۰math۰Ldexp, |
| "math.Log": ext۰math۰Log, |
| "math.Min": ext۰math۰Min, |
| "math.hasSSE4": ext۰math۰hasSSE4, |
| "math.hasVectorFacility": ext۰math۰hasVectorFacility, |
| "os.runtime_args": ext۰os۰runtime_args, |
| "os.runtime_beforeExit": ext۰nop, |
| "os/signal.init": ext۰nop, |
| "reflect.New": ext۰reflect۰New, |
| "reflect.SliceOf": ext۰reflect۰SliceOf, |
| "reflect.TypeOf": ext۰reflect۰TypeOf, |
| "reflect.ValueOf": ext۰reflect۰ValueOf, |
| "reflect.Zero": ext۰reflect۰Zero, |
| "reflect.init": ext۰reflect۰Init, |
| "reflect.valueInterface": ext۰reflect۰valueInterface, |
| "runtime.Breakpoint": ext۰runtime۰Breakpoint, |
| "runtime.Caller": ext۰runtime۰Caller, |
| "runtime.Callers": ext۰runtime۰Callers, |
| "runtime.FuncForPC": ext۰runtime۰FuncForPC, |
| "runtime.GC": ext۰runtime۰GC, |
| "runtime.GOMAXPROCS": ext۰runtime۰GOMAXPROCS, |
| "runtime.Goexit": ext۰runtime۰Goexit, |
| "runtime.Gosched": ext۰runtime۰Gosched, |
| "runtime.init": ext۰nop, |
| "runtime.KeepAlive": ext۰nop, |
| "runtime.NumCPU": ext۰runtime۰NumCPU, |
| "runtime.NumGoroutine": ext۰runtime۰NumGoroutine, |
| "runtime.ReadMemStats": ext۰runtime۰ReadMemStats, |
| "runtime.SetFinalizer": ext۰nop, // ignore |
| "(*runtime.Func).Entry": ext۰runtime۰Func۰Entry, |
| "(*runtime.Func).FileLine": ext۰runtime۰Func۰FileLine, |
| "(*runtime.Func).Name": ext۰runtime۰Func۰Name, |
| "runtime.environ": ext۰runtime۰environ, |
| "runtime.getgoroot": ext۰runtime۰getgoroot, |
| "strings.init": ext۰nop, // avoid asm dependency |
| "strings.Count": ext۰strings۰Count, |
| "strings.Index": ext۰strings۰Index, |
| "strings.IndexByte": ext۰strings۰IndexByte, |
| "sync.runtime_Semacquire": ext۰nop, // unimplementable |
| "sync.runtime_Semrelease": ext۰nop, // unimplementable |
| "sync.runtime_Syncsemcheck": ext۰nop, // unimplementable |
| "sync.runtime_notifyListCheck": ext۰nop, |
| "sync.runtime_registerPoolCleanup": ext۰nop, |
| "sync/atomic.AddInt32": ext۰atomic۰AddInt32, |
| "sync/atomic.AddUint32": ext۰atomic۰AddUint32, |
| "sync/atomic.CompareAndSwapInt32": ext۰atomic۰CompareAndSwapInt32, |
| "sync/atomic.CompareAndSwapUint32": ext۰atomic۰CompareAndSwapUint32, |
| "sync/atomic.LoadInt32": ext۰atomic۰LoadInt32, |
| "sync/atomic.LoadUint32": ext۰atomic۰LoadUint32, |
| "sync/atomic.StoreInt32": ext۰atomic۰StoreInt32, |
| "sync/atomic.StoreUint32": ext۰atomic۰StoreUint32, |
| "sync/atomic.AddInt64": ext۰atomic۰AddInt64, |
| "sync/atomic.AddUint64": ext۰atomic۰AddUint64, |
| "sync/atomic.CompareAndSwapInt64": ext۰atomic۰CompareAndSwapInt64, |
| "sync/atomic.CompareAndSwapUint64": ext۰atomic۰CompareAndSwapUint64, |
| "sync/atomic.LoadInt64": ext۰atomic۰LoadInt64, |
| "sync/atomic.LoadUint64": ext۰atomic۰LoadUint64, |
| "sync/atomic.StoreInt64": ext۰atomic۰StoreInt64, |
| "sync/atomic.StoreUint64": ext۰atomic۰StoreUint64, |
| "(*sync/atomic.Value).Load": ext۰atomic۰ValueLoad, |
| "(*sync/atomic.Value).Store": ext۰atomic۰ValueStore, |
| "testing.MainStart": ext۰testing۰MainStart, |
| "time.Sleep": ext۰time۰Sleep, |
| "time.now": ext۰time۰now, |
| } { |
| externals[k] = v |
| } |
| } |
| |
| // wrapError returns an interpreted 'error' interface value for err. |
| func wrapError(err error) value { |
| if err == nil { |
| return iface{} |
| } |
| return iface{t: errorType, v: err.Error()} |
| } |
| |
| func ext۰nop(fr *frame, args []value) value { return nil } |
| |
| func ext۰sync۰Pool۰Get(fr *frame, args []value) value { |
| Pool := fr.i.prog.ImportedPackage("sync").Type("Pool").Object() |
| _, newIndex, _ := types.LookupFieldOrMethod(Pool.Type(), false, Pool.Pkg(), "New") |
| |
| if New := (*args[0].(*value)).(structure)[newIndex[0]]; New != nil { |
| return call(fr.i, fr, 0, New, nil) |
| } |
| return nil |
| } |
| |
| func ext۰bytes۰Equal(fr *frame, args []value) value { |
| // func Equal(a, b []byte) bool |
| a := args[0].([]value) |
| b := args[1].([]value) |
| if len(a) != len(b) { |
| return false |
| } |
| for i := range a { |
| if a[i] != b[i] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func ext۰bytes۰IndexByte(fr *frame, args []value) value { |
| // func IndexByte(s []byte, c byte) int |
| s := args[0].([]value) |
| c := args[1].(byte) |
| for i, b := range s { |
| if b.(byte) == c { |
| return i |
| } |
| } |
| return -1 |
| } |
| |
| func ext۰crc32۰haveSSE42(fr *frame, args []value) value { |
| return false |
| } |
| |
| func ext۰math۰Float64frombits(fr *frame, args []value) value { |
| return math.Float64frombits(args[0].(uint64)) |
| } |
| |
| func ext۰math۰Float64bits(fr *frame, args []value) value { |
| return math.Float64bits(args[0].(float64)) |
| } |
| |
| func ext۰math۰Float32frombits(fr *frame, args []value) value { |
| return math.Float32frombits(args[0].(uint32)) |
| } |
| |
| func ext۰math۰Abs(fr *frame, args []value) value { |
| return math.Abs(args[0].(float64)) |
| } |
| |
| func ext۰math۰Exp(fr *frame, args []value) value { |
| return math.Exp(args[0].(float64)) |
| } |
| |
| func ext۰math۰Float32bits(fr *frame, args []value) value { |
| return math.Float32bits(args[0].(float32)) |
| } |
| |
| func ext۰math۰Min(fr *frame, args []value) value { |
| return math.Min(args[0].(float64), args[1].(float64)) |
| } |
| |
| func ext۰math۰hasSSE4(fr *frame, args []value) value { |
| return false |
| } |
| |
| func ext۰math۰hasVectorFacility(fr *frame, args []value) value { |
| return false |
| } |
| |
| func ext۰math۰Ldexp(fr *frame, args []value) value { |
| return math.Ldexp(args[0].(float64), args[1].(int)) |
| } |
| |
| func ext۰math۰Log(fr *frame, args []value) value { |
| return math.Log(args[0].(float64)) |
| } |
| |
| func ext۰os۰runtime_args(fr *frame, args []value) value { |
| return fr.i.osArgs |
| } |
| |
| func ext۰runtime۰Breakpoint(fr *frame, args []value) value { |
| runtime.Breakpoint() |
| return nil |
| } |
| |
| func ext۰runtime۰Caller(fr *frame, args []value) value { |
| // func Caller(skip int) (pc uintptr, file string, line int, ok bool) |
| skip := 1 + args[0].(int) |
| for i := 0; i < skip; i++ { |
| if fr != nil { |
| fr = fr.caller |
| } |
| } |
| var pc uintptr |
| var file string |
| var line int |
| var ok bool |
| if fr != nil { |
| fn := fr.fn |
| // TODO(adonovan): use pc/posn of current instruction, not start of fn. |
| // (Required to interpret the log package's tests.) |
| pc = uintptr(unsafe.Pointer(fn)) |
| posn := fn.Prog.Fset.Position(fn.Pos()) |
| file = posn.Filename |
| line = posn.Line |
| ok = true |
| } |
| return tuple{pc, file, line, ok} |
| } |
| |
| func ext۰runtime۰Callers(fr *frame, args []value) value { |
| // Callers(skip int, pc []uintptr) int |
| skip := args[0].(int) |
| pc := args[1].([]value) |
| for i := 0; i < skip; i++ { |
| if fr != nil { |
| fr = fr.caller |
| } |
| } |
| i := 0 |
| for fr != nil && i < len(pc) { |
| pc[i] = uintptr(unsafe.Pointer(fr.fn)) |
| i++ |
| fr = fr.caller |
| } |
| return i |
| } |
| |
| func ext۰runtime۰FuncForPC(fr *frame, args []value) value { |
| // FuncForPC(pc uintptr) *Func |
| pc := args[0].(uintptr) |
| var fn *ssa.Function |
| if pc != 0 { |
| fn = (*ssa.Function)(unsafe.Pointer(pc)) // indeed unsafe! |
| } |
| var Func value |
| Func = structure{fn} // a runtime.Func |
| return &Func |
| } |
| |
| func ext۰runtime۰environ(fr *frame, args []value) value { |
| // This function also implements syscall.runtime_envs. |
| return environ |
| } |
| |
| func ext۰runtime۰getgoroot(fr *frame, args []value) value { |
| return os.Getenv("GOROOT") |
| } |
| |
| func ext۰strings۰Count(fr *frame, args []value) value { |
| // Call compiled version to avoid asm dependency. |
| return strings.Count(args[0].(string), args[1].(string)) |
| } |
| |
| func ext۰strings۰IndexByte(fr *frame, args []value) value { |
| // Call compiled version to avoid asm dependency. |
| return strings.IndexByte(args[0].(string), args[1].(byte)) |
| } |
| |
| func ext۰strings۰Index(fr *frame, args []value) value { |
| // Call compiled version to avoid asm dependency. |
| return strings.Index(args[0].(string), args[1].(string)) |
| } |
| |
| func ext۰runtime۰GOMAXPROCS(fr *frame, args []value) value { |
| // Ignore args[0]; don't let the interpreted program |
| // set the interpreter's GOMAXPROCS! |
| return runtime.GOMAXPROCS(0) |
| } |
| |
| func ext۰runtime۰Goexit(fr *frame, args []value) value { |
| // TODO(adonovan): don't kill the interpreter's main goroutine. |
| runtime.Goexit() |
| return nil |
| } |
| |
| func ext۰runtime۰GC(fr *frame, args []value) value { |
| runtime.GC() |
| return nil |
| } |
| |
| func ext۰runtime۰Gosched(fr *frame, args []value) value { |
| runtime.Gosched() |
| return nil |
| } |
| |
| func ext۰runtime۰NumCPU(fr *frame, args []value) value { |
| return runtime.NumCPU() |
| } |
| |
| func ext۰runtime۰NumGoroutine(fr *frame, args []value) value { |
| return int(atomic.LoadInt32(&fr.i.goroutines)) |
| } |
| |
| func ext۰runtime۰ReadMemStats(fr *frame, args []value) value { |
| // TODO(adonovan): populate args[0].(Struct) |
| return nil |
| } |
| |
| func ext۰atomic۰LoadUint32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| return (*args[0].(*value)).(uint32) |
| } |
| |
| func ext۰atomic۰StoreUint32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| *args[0].(*value) = args[1].(uint32) |
| return nil |
| } |
| |
| func ext۰atomic۰LoadInt32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| return (*args[0].(*value)).(int32) |
| } |
| |
| func ext۰atomic۰StoreInt32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| *args[0].(*value) = args[1].(int32) |
| return nil |
| } |
| |
| func ext۰atomic۰CompareAndSwapInt32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| if (*p).(int32) == args[1].(int32) { |
| *p = args[2].(int32) |
| return true |
| } |
| return false |
| } |
| |
| func ext۰atomic۰CompareAndSwapUint32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| if (*p).(uint32) == args[1].(uint32) { |
| *p = args[2].(uint32) |
| return true |
| } |
| return false |
| } |
| |
| func ext۰atomic۰AddInt32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| newv := (*p).(int32) + args[1].(int32) |
| *p = newv |
| return newv |
| } |
| |
| func ext۰atomic۰AddUint32(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| newv := (*p).(uint32) + args[1].(uint32) |
| *p = newv |
| return newv |
| } |
| |
| func ext۰atomic۰LoadUint64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| return (*args[0].(*value)).(uint64) |
| } |
| |
| func ext۰atomic۰StoreUint64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| *args[0].(*value) = args[1].(uint64) |
| return nil |
| } |
| |
| func ext۰atomic۰LoadInt64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| return (*args[0].(*value)).(int64) |
| } |
| |
| func ext۰atomic۰StoreInt64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| *args[0].(*value) = args[1].(int64) |
| return nil |
| } |
| |
| func ext۰atomic۰CompareAndSwapInt64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| if (*p).(int64) == args[1].(int64) { |
| *p = args[2].(int64) |
| return true |
| } |
| return false |
| } |
| |
| func ext۰atomic۰CompareAndSwapUint64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| if (*p).(uint64) == args[1].(uint64) { |
| *p = args[2].(uint64) |
| return true |
| } |
| return false |
| } |
| |
| func ext۰atomic۰AddInt64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| newv := (*p).(int64) + args[1].(int64) |
| *p = newv |
| return newv |
| } |
| |
| func ext۰atomic۰AddUint64(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| p := args[0].(*value) |
| newv := (*p).(uint64) + args[1].(uint64) |
| *p = newv |
| return newv |
| } |
| |
| func ext۰atomic۰ValueLoad(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| // Receiver is *struct{v interface{}}. |
| return (*args[0].(*value)).(structure)[0] |
| } |
| |
| func ext۰atomic۰ValueStore(fr *frame, args []value) value { |
| // TODO(adonovan): fix: not atomic! |
| // Receiver is *struct{v interface{}}. |
| (*args[0].(*value)).(structure)[0] = args[1] |
| return nil |
| } |
| |
| func ext۰cpu۰cpuid(fr *frame, args []value) value { |
| return tuple{uint32(0), uint32(0), uint32(0), uint32(0)} |
| } |
| |
| func ext۰syscall۰unix۰syscall_fcntl(fr *frame, args []value) value { |
| return tuple{int(0), wrapError(nil)} |
| } |
| |
| // Pretend: type runtime.Func struct { entry *ssa.Function } |
| |
| func ext۰runtime۰Func۰FileLine(fr *frame, args []value) value { |
| // func (*runtime.Func) FileLine(uintptr) (string, int) |
| f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) |
| pc := args[1].(uintptr) |
| _ = pc |
| if f != nil { |
| // TODO(adonovan): use position of current instruction, not fn. |
| posn := f.Prog.Fset.Position(f.Pos()) |
| return tuple{posn.Filename, posn.Line} |
| } |
| return tuple{"", 0} |
| } |
| |
| func ext۰runtime۰Func۰Name(fr *frame, args []value) value { |
| // func (*runtime.Func) Name() string |
| f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) |
| if f != nil { |
| return f.String() |
| } |
| return "" |
| } |
| |
| func ext۰runtime۰Func۰Entry(fr *frame, args []value) value { |
| // func (*runtime.Func) Entry() uintptr |
| f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) |
| return uintptr(unsafe.Pointer(f)) |
| } |
| |
| func ext۰time۰now(fr *frame, args []value) value { |
| nano := time.Now().UnixNano() |
| return tuple{int64(nano / 1e9), int32(nano % 1e9), int64(0)} |
| } |
| |
| func ext۰time۰Sleep(fr *frame, args []value) value { |
| time.Sleep(time.Duration(args[0].(int64))) |
| return nil |
| } |
| |
| func valueToBytes(v value) []byte { |
| in := v.([]value) |
| b := make([]byte, len(in)) |
| for i := range in { |
| b[i] = in[i].(byte) |
| } |
| return b |
| } |
| |
| func ext۰testing۰MainStart(fr *frame, args []value) value { |
| // We no longer support interpretation of the "testing" package |
| // because it changes too often and uses low-level features that |
| // are a pain to emulate. |
| panic(`interpretation of the "testing" package is no longer supported`) |
| } |