|  | // 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 | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "fmt" | 
|  | "go/constant" | 
|  | "go/token" | 
|  | "go/types" | 
|  | "os" | 
|  | "reflect" | 
|  | "strings" | 
|  | "sync" | 
|  | "unsafe" | 
|  |  | 
|  | "golang.org/x/tools/go/ssa" | 
|  | ) | 
|  |  | 
|  | // If the target program panics, the interpreter panics with this type. | 
|  | type targetPanic struct { | 
|  | v value | 
|  | } | 
|  |  | 
|  | func (p targetPanic) String() string { | 
|  | return toString(p.v) | 
|  | } | 
|  |  | 
|  | // If the target program calls exit, the interpreter panics with this type. | 
|  | type exitPanic int | 
|  |  | 
|  | // constValue returns the value of the constant with the | 
|  | // dynamic type tag appropriate for c.Type(). | 
|  | func constValue(c *ssa.Const) value { | 
|  | if c.Value == nil { | 
|  | return zero(c.Type()) // typed zero | 
|  | } | 
|  | // c is not a type parameter so it's underlying type is basic. | 
|  |  | 
|  | if t, ok := c.Type().Underlying().(*types.Basic); ok { | 
|  | // TODO(adonovan): eliminate untyped constants from SSA form. | 
|  | switch t.Kind() { | 
|  | case types.Bool, types.UntypedBool: | 
|  | return constant.BoolVal(c.Value) | 
|  | case types.Int, types.UntypedInt: | 
|  | // Assume sizeof(int) is same on host and target. | 
|  | return int(c.Int64()) | 
|  | case types.Int8: | 
|  | return int8(c.Int64()) | 
|  | case types.Int16: | 
|  | return int16(c.Int64()) | 
|  | case types.Int32, types.UntypedRune: | 
|  | return int32(c.Int64()) | 
|  | case types.Int64: | 
|  | return c.Int64() | 
|  | case types.Uint: | 
|  | // Assume sizeof(uint) is same on host and target. | 
|  | return uint(c.Uint64()) | 
|  | case types.Uint8: | 
|  | return uint8(c.Uint64()) | 
|  | case types.Uint16: | 
|  | return uint16(c.Uint64()) | 
|  | case types.Uint32: | 
|  | return uint32(c.Uint64()) | 
|  | case types.Uint64: | 
|  | return c.Uint64() | 
|  | case types.Uintptr: | 
|  | // Assume sizeof(uintptr) is same on host and target. | 
|  | return uintptr(c.Uint64()) | 
|  | case types.Float32: | 
|  | return float32(c.Float64()) | 
|  | case types.Float64, types.UntypedFloat: | 
|  | return c.Float64() | 
|  | case types.Complex64: | 
|  | return complex64(c.Complex128()) | 
|  | case types.Complex128, types.UntypedComplex: | 
|  | return c.Complex128() | 
|  | case types.String, types.UntypedString: | 
|  | if c.Value.Kind() == constant.String { | 
|  | return constant.StringVal(c.Value) | 
|  | } | 
|  | return string(rune(c.Int64())) | 
|  | } | 
|  | } | 
|  |  | 
|  | panic(fmt.Sprintf("constValue: %s", c)) | 
|  | } | 
|  |  | 
|  | // fitsInt returns true if x fits in type int according to sizes. | 
|  | func fitsInt(x int64, sizes types.Sizes) bool { | 
|  | intSize := sizes.Sizeof(types.Typ[types.Int]) | 
|  | if intSize < sizes.Sizeof(types.Typ[types.Int64]) { | 
|  | maxInt := int64(1)<<(intSize-1) - 1 | 
|  | minInt := -int64(1) << (intSize - 1) | 
|  | return minInt <= x && x <= maxInt | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | // asInt64 converts x, which must be an integer, to an int64. | 
|  | // | 
|  | // Callers that need a value directly usable as an int should combine this with fitsInt(). | 
|  | func asInt64(x value) int64 { | 
|  | switch x := x.(type) { | 
|  | case int: | 
|  | return int64(x) | 
|  | case int8: | 
|  | return int64(x) | 
|  | case int16: | 
|  | return int64(x) | 
|  | case int32: | 
|  | return int64(x) | 
|  | case int64: | 
|  | return x | 
|  | case uint: | 
|  | return int64(x) | 
|  | case uint8: | 
|  | return int64(x) | 
|  | case uint16: | 
|  | return int64(x) | 
|  | case uint32: | 
|  | return int64(x) | 
|  | case uint64: | 
|  | return int64(x) | 
|  | case uintptr: | 
|  | return int64(x) | 
|  | } | 
|  | panic(fmt.Sprintf("cannot convert %T to int64", x)) | 
|  | } | 
|  |  | 
|  | // asUint64 converts x, which must be an unsigned integer, to a uint64 | 
|  | // suitable for use as a bitwise shift count. | 
|  | func asUint64(x value) uint64 { | 
|  | switch x := x.(type) { | 
|  | case uint: | 
|  | return uint64(x) | 
|  | case uint8: | 
|  | return uint64(x) | 
|  | case uint16: | 
|  | return uint64(x) | 
|  | case uint32: | 
|  | return uint64(x) | 
|  | case uint64: | 
|  | return x | 
|  | case uintptr: | 
|  | return uint64(x) | 
|  | } | 
|  | panic(fmt.Sprintf("cannot convert %T to uint64", x)) | 
|  | } | 
|  |  | 
|  | // asUnsigned returns the value of x, which must be an integer type, as its equivalent unsigned type, | 
|  | // and returns true if x is non-negative. | 
|  | func asUnsigned(x value) (value, bool) { | 
|  | switch x := x.(type) { | 
|  | case int: | 
|  | return uint(x), x >= 0 | 
|  | case int8: | 
|  | return uint8(x), x >= 0 | 
|  | case int16: | 
|  | return uint16(x), x >= 0 | 
|  | case int32: | 
|  | return uint32(x), x >= 0 | 
|  | case int64: | 
|  | return uint64(x), x >= 0 | 
|  | case uint, uint8, uint32, uint64, uintptr: | 
|  | return x, true | 
|  | } | 
|  | panic(fmt.Sprintf("cannot convert %T to unsigned", x)) | 
|  | } | 
|  |  | 
|  | // zero returns a new "zero" value of the specified type. | 
|  | func zero(t types.Type) value { | 
|  | switch t := t.(type) { | 
|  | case *types.Basic: | 
|  | if t.Kind() == types.UntypedNil { | 
|  | panic("untyped nil has no zero value") | 
|  | } | 
|  | if t.Info()&types.IsUntyped != 0 { | 
|  | // TODO(adonovan): make it an invariant that | 
|  | // this is unreachable.  Currently some | 
|  | // constants have 'untyped' types when they | 
|  | // should be defaulted by the typechecker. | 
|  | t = types.Default(t).(*types.Basic) | 
|  | } | 
|  | switch t.Kind() { | 
|  | case types.Bool: | 
|  | return false | 
|  | case types.Int: | 
|  | return int(0) | 
|  | case types.Int8: | 
|  | return int8(0) | 
|  | case types.Int16: | 
|  | return int16(0) | 
|  | case types.Int32: | 
|  | return int32(0) | 
|  | case types.Int64: | 
|  | return int64(0) | 
|  | case types.Uint: | 
|  | return uint(0) | 
|  | case types.Uint8: | 
|  | return uint8(0) | 
|  | case types.Uint16: | 
|  | return uint16(0) | 
|  | case types.Uint32: | 
|  | return uint32(0) | 
|  | case types.Uint64: | 
|  | return uint64(0) | 
|  | case types.Uintptr: | 
|  | return uintptr(0) | 
|  | case types.Float32: | 
|  | return float32(0) | 
|  | case types.Float64: | 
|  | return float64(0) | 
|  | case types.Complex64: | 
|  | return complex64(0) | 
|  | case types.Complex128: | 
|  | return complex128(0) | 
|  | case types.String: | 
|  | return "" | 
|  | case types.UnsafePointer: | 
|  | return unsafe.Pointer(nil) | 
|  | default: | 
|  | panic(fmt.Sprint("zero for unexpected type:", t)) | 
|  | } | 
|  | case *types.Pointer: | 
|  | return (*value)(nil) | 
|  | case *types.Array: | 
|  | a := make(array, t.Len()) | 
|  | for i := range a { | 
|  | a[i] = zero(t.Elem()) | 
|  | } | 
|  | return a | 
|  | case *types.Named: | 
|  | return zero(t.Underlying()) | 
|  | case *types.Interface: | 
|  | return iface{} // nil type, methodset and value | 
|  | case *types.Slice: | 
|  | return []value(nil) | 
|  | case *types.Struct: | 
|  | s := make(structure, t.NumFields()) | 
|  | for i := range s { | 
|  | s[i] = zero(t.Field(i).Type()) | 
|  | } | 
|  | return s | 
|  | case *types.Tuple: | 
|  | if t.Len() == 1 { | 
|  | return zero(t.At(0).Type()) | 
|  | } | 
|  | s := make(tuple, t.Len()) | 
|  | for i := range s { | 
|  | s[i] = zero(t.At(i).Type()) | 
|  | } | 
|  | return s | 
|  | case *types.Chan: | 
|  | return chan value(nil) | 
|  | case *types.Map: | 
|  | if usesBuiltinMap(t.Key()) { | 
|  | return map[value]value(nil) | 
|  | } | 
|  | return (*hashmap)(nil) | 
|  | case *types.Signature: | 
|  | return (*ssa.Function)(nil) | 
|  | } | 
|  | panic(fmt.Sprint("zero: unexpected ", t)) | 
|  | } | 
|  |  | 
|  | // slice returns x[lo:hi:max].  Any of lo, hi and max may be nil. | 
|  | func slice(x, lo, hi, max value) value { | 
|  | var Len, Cap int | 
|  | switch x := x.(type) { | 
|  | case string: | 
|  | Len = len(x) | 
|  | case []value: | 
|  | Len = len(x) | 
|  | Cap = cap(x) | 
|  | case *value: // *array | 
|  | a := (*x).(array) | 
|  | Len = len(a) | 
|  | Cap = cap(a) | 
|  | } | 
|  |  | 
|  | l := int64(0) | 
|  | if lo != nil { | 
|  | l = asInt64(lo) | 
|  | } | 
|  |  | 
|  | h := int64(Len) | 
|  | if hi != nil { | 
|  | h = asInt64(hi) | 
|  | } | 
|  |  | 
|  | m := int64(Cap) | 
|  | if max != nil { | 
|  | m = asInt64(max) | 
|  | } | 
|  |  | 
|  | switch x := x.(type) { | 
|  | case string: | 
|  | return x[l:h] | 
|  | case []value: | 
|  | return x[l:h:m] | 
|  | case *value: // *array | 
|  | a := (*x).(array) | 
|  | return []value(a)[l:h:m] | 
|  | } | 
|  | panic(fmt.Sprintf("slice: unexpected X type: %T", x)) | 
|  | } | 
|  |  | 
|  | // lookup returns x[idx] where x is a map. | 
|  | func lookup(instr *ssa.Lookup, x, idx value) value { | 
|  | switch x := x.(type) { // map or string | 
|  | case map[value]value, *hashmap: | 
|  | var v value | 
|  | var ok bool | 
|  | switch x := x.(type) { | 
|  | case map[value]value: | 
|  | v, ok = x[idx] | 
|  | case *hashmap: | 
|  | v = x.lookup(idx.(hashable)) | 
|  | ok = v != nil | 
|  | } | 
|  | if !ok { | 
|  | v = zero(instr.X.Type().Underlying().(*types.Map).Elem()) | 
|  | } | 
|  | if instr.CommaOk { | 
|  | v = tuple{v, ok} | 
|  | } | 
|  | return v | 
|  | } | 
|  | panic(fmt.Sprintf("unexpected x type in Lookup: %T", x)) | 
|  | } | 
|  |  | 
|  | // binop implements all arithmetic and logical binary operators for | 
|  | // numeric datatypes and strings.  Both operands must have identical | 
|  | // dynamic type. | 
|  | func binop(op token.Token, t types.Type, x, y value) value { | 
|  | switch op { | 
|  | case token.ADD: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) + y.(int) | 
|  | case int8: | 
|  | return x.(int8) + y.(int8) | 
|  | case int16: | 
|  | return x.(int16) + y.(int16) | 
|  | case int32: | 
|  | return x.(int32) + y.(int32) | 
|  | case int64: | 
|  | return x.(int64) + y.(int64) | 
|  | case uint: | 
|  | return x.(uint) + y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) + y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) + y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) + y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) + y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) + y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) + y.(float32) | 
|  | case float64: | 
|  | return x.(float64) + y.(float64) | 
|  | case complex64: | 
|  | return x.(complex64) + y.(complex64) | 
|  | case complex128: | 
|  | return x.(complex128) + y.(complex128) | 
|  | case string: | 
|  | return x.(string) + y.(string) | 
|  | } | 
|  |  | 
|  | case token.SUB: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) - y.(int) | 
|  | case int8: | 
|  | return x.(int8) - y.(int8) | 
|  | case int16: | 
|  | return x.(int16) - y.(int16) | 
|  | case int32: | 
|  | return x.(int32) - y.(int32) | 
|  | case int64: | 
|  | return x.(int64) - y.(int64) | 
|  | case uint: | 
|  | return x.(uint) - y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) - y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) - y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) - y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) - y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) - y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) - y.(float32) | 
|  | case float64: | 
|  | return x.(float64) - y.(float64) | 
|  | case complex64: | 
|  | return x.(complex64) - y.(complex64) | 
|  | case complex128: | 
|  | return x.(complex128) - y.(complex128) | 
|  | } | 
|  |  | 
|  | case token.MUL: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) * y.(int) | 
|  | case int8: | 
|  | return x.(int8) * y.(int8) | 
|  | case int16: | 
|  | return x.(int16) * y.(int16) | 
|  | case int32: | 
|  | return x.(int32) * y.(int32) | 
|  | case int64: | 
|  | return x.(int64) * y.(int64) | 
|  | case uint: | 
|  | return x.(uint) * y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) * y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) * y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) * y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) * y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) * y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) * y.(float32) | 
|  | case float64: | 
|  | return x.(float64) * y.(float64) | 
|  | case complex64: | 
|  | return x.(complex64) * y.(complex64) | 
|  | case complex128: | 
|  | return x.(complex128) * y.(complex128) | 
|  | } | 
|  |  | 
|  | case token.QUO: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) / y.(int) | 
|  | case int8: | 
|  | return x.(int8) / y.(int8) | 
|  | case int16: | 
|  | return x.(int16) / y.(int16) | 
|  | case int32: | 
|  | return x.(int32) / y.(int32) | 
|  | case int64: | 
|  | return x.(int64) / y.(int64) | 
|  | case uint: | 
|  | return x.(uint) / y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) / y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) / y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) / y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) / y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) / y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) / y.(float32) | 
|  | case float64: | 
|  | return x.(float64) / y.(float64) | 
|  | case complex64: | 
|  | return x.(complex64) / y.(complex64) | 
|  | case complex128: | 
|  | return x.(complex128) / y.(complex128) | 
|  | } | 
|  |  | 
|  | case token.REM: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) % y.(int) | 
|  | case int8: | 
|  | return x.(int8) % y.(int8) | 
|  | case int16: | 
|  | return x.(int16) % y.(int16) | 
|  | case int32: | 
|  | return x.(int32) % y.(int32) | 
|  | case int64: | 
|  | return x.(int64) % y.(int64) | 
|  | case uint: | 
|  | return x.(uint) % y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) % y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) % y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) % y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) % y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) % y.(uintptr) | 
|  | } | 
|  |  | 
|  | case token.AND: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) & y.(int) | 
|  | case int8: | 
|  | return x.(int8) & y.(int8) | 
|  | case int16: | 
|  | return x.(int16) & y.(int16) | 
|  | case int32: | 
|  | return x.(int32) & y.(int32) | 
|  | case int64: | 
|  | return x.(int64) & y.(int64) | 
|  | case uint: | 
|  | return x.(uint) & y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) & y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) & y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) & y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) & y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) & y.(uintptr) | 
|  | } | 
|  |  | 
|  | case token.OR: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) | y.(int) | 
|  | case int8: | 
|  | return x.(int8) | y.(int8) | 
|  | case int16: | 
|  | return x.(int16) | y.(int16) | 
|  | case int32: | 
|  | return x.(int32) | y.(int32) | 
|  | case int64: | 
|  | return x.(int64) | y.(int64) | 
|  | case uint: | 
|  | return x.(uint) | y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) | y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) | y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) | y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) | y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) | y.(uintptr) | 
|  | } | 
|  |  | 
|  | case token.XOR: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) ^ y.(int) | 
|  | case int8: | 
|  | return x.(int8) ^ y.(int8) | 
|  | case int16: | 
|  | return x.(int16) ^ y.(int16) | 
|  | case int32: | 
|  | return x.(int32) ^ y.(int32) | 
|  | case int64: | 
|  | return x.(int64) ^ y.(int64) | 
|  | case uint: | 
|  | return x.(uint) ^ y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) ^ y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) ^ y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) ^ y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) ^ y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) ^ y.(uintptr) | 
|  | } | 
|  |  | 
|  | case token.AND_NOT: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) &^ y.(int) | 
|  | case int8: | 
|  | return x.(int8) &^ y.(int8) | 
|  | case int16: | 
|  | return x.(int16) &^ y.(int16) | 
|  | case int32: | 
|  | return x.(int32) &^ y.(int32) | 
|  | case int64: | 
|  | return x.(int64) &^ y.(int64) | 
|  | case uint: | 
|  | return x.(uint) &^ y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) &^ y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) &^ y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) &^ y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) &^ y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) &^ y.(uintptr) | 
|  | } | 
|  |  | 
|  | case token.SHL: | 
|  | u, ok := asUnsigned(y) | 
|  | if !ok { | 
|  | panic("negative shift amount") | 
|  | } | 
|  | y := asUint64(u) | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) << y | 
|  | case int8: | 
|  | return x.(int8) << y | 
|  | case int16: | 
|  | return x.(int16) << y | 
|  | case int32: | 
|  | return x.(int32) << y | 
|  | case int64: | 
|  | return x.(int64) << y | 
|  | case uint: | 
|  | return x.(uint) << y | 
|  | case uint8: | 
|  | return x.(uint8) << y | 
|  | case uint16: | 
|  | return x.(uint16) << y | 
|  | case uint32: | 
|  | return x.(uint32) << y | 
|  | case uint64: | 
|  | return x.(uint64) << y | 
|  | case uintptr: | 
|  | return x.(uintptr) << y | 
|  | } | 
|  |  | 
|  | case token.SHR: | 
|  | u, ok := asUnsigned(y) | 
|  | if !ok { | 
|  | panic("negative shift amount") | 
|  | } | 
|  | y := asUint64(u) | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) >> y | 
|  | case int8: | 
|  | return x.(int8) >> y | 
|  | case int16: | 
|  | return x.(int16) >> y | 
|  | case int32: | 
|  | return x.(int32) >> y | 
|  | case int64: | 
|  | return x.(int64) >> y | 
|  | case uint: | 
|  | return x.(uint) >> y | 
|  | case uint8: | 
|  | return x.(uint8) >> y | 
|  | case uint16: | 
|  | return x.(uint16) >> y | 
|  | case uint32: | 
|  | return x.(uint32) >> y | 
|  | case uint64: | 
|  | return x.(uint64) >> y | 
|  | case uintptr: | 
|  | return x.(uintptr) >> y | 
|  | } | 
|  |  | 
|  | case token.LSS: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) < y.(int) | 
|  | case int8: | 
|  | return x.(int8) < y.(int8) | 
|  | case int16: | 
|  | return x.(int16) < y.(int16) | 
|  | case int32: | 
|  | return x.(int32) < y.(int32) | 
|  | case int64: | 
|  | return x.(int64) < y.(int64) | 
|  | case uint: | 
|  | return x.(uint) < y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) < y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) < y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) < y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) < y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) < y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) < y.(float32) | 
|  | case float64: | 
|  | return x.(float64) < y.(float64) | 
|  | case string: | 
|  | return x.(string) < y.(string) | 
|  | } | 
|  |  | 
|  | case token.LEQ: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) <= y.(int) | 
|  | case int8: | 
|  | return x.(int8) <= y.(int8) | 
|  | case int16: | 
|  | return x.(int16) <= y.(int16) | 
|  | case int32: | 
|  | return x.(int32) <= y.(int32) | 
|  | case int64: | 
|  | return x.(int64) <= y.(int64) | 
|  | case uint: | 
|  | return x.(uint) <= y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) <= y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) <= y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) <= y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) <= y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) <= y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) <= y.(float32) | 
|  | case float64: | 
|  | return x.(float64) <= y.(float64) | 
|  | case string: | 
|  | return x.(string) <= y.(string) | 
|  | } | 
|  |  | 
|  | case token.EQL: | 
|  | return eqnil(t, x, y) | 
|  |  | 
|  | case token.NEQ: | 
|  | return !eqnil(t, x, y) | 
|  |  | 
|  | case token.GTR: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) > y.(int) | 
|  | case int8: | 
|  | return x.(int8) > y.(int8) | 
|  | case int16: | 
|  | return x.(int16) > y.(int16) | 
|  | case int32: | 
|  | return x.(int32) > y.(int32) | 
|  | case int64: | 
|  | return x.(int64) > y.(int64) | 
|  | case uint: | 
|  | return x.(uint) > y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) > y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) > y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) > y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) > y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) > y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) > y.(float32) | 
|  | case float64: | 
|  | return x.(float64) > y.(float64) | 
|  | case string: | 
|  | return x.(string) > y.(string) | 
|  | } | 
|  |  | 
|  | case token.GEQ: | 
|  | switch x.(type) { | 
|  | case int: | 
|  | return x.(int) >= y.(int) | 
|  | case int8: | 
|  | return x.(int8) >= y.(int8) | 
|  | case int16: | 
|  | return x.(int16) >= y.(int16) | 
|  | case int32: | 
|  | return x.(int32) >= y.(int32) | 
|  | case int64: | 
|  | return x.(int64) >= y.(int64) | 
|  | case uint: | 
|  | return x.(uint) >= y.(uint) | 
|  | case uint8: | 
|  | return x.(uint8) >= y.(uint8) | 
|  | case uint16: | 
|  | return x.(uint16) >= y.(uint16) | 
|  | case uint32: | 
|  | return x.(uint32) >= y.(uint32) | 
|  | case uint64: | 
|  | return x.(uint64) >= y.(uint64) | 
|  | case uintptr: | 
|  | return x.(uintptr) >= y.(uintptr) | 
|  | case float32: | 
|  | return x.(float32) >= y.(float32) | 
|  | case float64: | 
|  | return x.(float64) >= y.(float64) | 
|  | case string: | 
|  | return x.(string) >= y.(string) | 
|  | } | 
|  | } | 
|  | panic(fmt.Sprintf("invalid binary op: %T %s %T", x, op, y)) | 
|  | } | 
|  |  | 
|  | // eqnil returns the comparison x == y using the equivalence relation | 
|  | // appropriate for type t. | 
|  | // If t is a reference type, at most one of x or y may be a nil value | 
|  | // of that type. | 
|  | func eqnil(t types.Type, x, y value) bool { | 
|  | switch t.Underlying().(type) { | 
|  | case *types.Map, *types.Signature, *types.Slice: | 
|  | // Since these types don't support comparison, | 
|  | // one of the operands must be a literal nil. | 
|  | switch x := x.(type) { | 
|  | case *hashmap: | 
|  | return (x != nil) == (y.(*hashmap) != nil) | 
|  | case map[value]value: | 
|  | return (x != nil) == (y.(map[value]value) != nil) | 
|  | case *ssa.Function: | 
|  | switch y := y.(type) { | 
|  | case *ssa.Function: | 
|  | return (x != nil) == (y != nil) | 
|  | case *closure: | 
|  | return true | 
|  | } | 
|  | case *closure: | 
|  | return (x != nil) == (y.(*ssa.Function) != nil) | 
|  | case []value: | 
|  | return (x != nil) == (y.([]value) != nil) | 
|  | } | 
|  | panic(fmt.Sprintf("eqnil(%s): illegal dynamic type: %T", t, x)) | 
|  | } | 
|  |  | 
|  | return equals(t, x, y) | 
|  | } | 
|  |  | 
|  | func unop(instr *ssa.UnOp, x value) value { | 
|  | switch instr.Op { | 
|  | case token.ARROW: // receive | 
|  | v, ok := <-x.(chan value) | 
|  | if !ok { | 
|  | v = zero(instr.X.Type().Underlying().(*types.Chan).Elem()) | 
|  | } | 
|  | if instr.CommaOk { | 
|  | v = tuple{v, ok} | 
|  | } | 
|  | return v | 
|  | case token.SUB: | 
|  | switch x := x.(type) { | 
|  | case int: | 
|  | return -x | 
|  | case int8: | 
|  | return -x | 
|  | case int16: | 
|  | return -x | 
|  | case int32: | 
|  | return -x | 
|  | case int64: | 
|  | return -x | 
|  | case uint: | 
|  | return -x | 
|  | case uint8: | 
|  | return -x | 
|  | case uint16: | 
|  | return -x | 
|  | case uint32: | 
|  | return -x | 
|  | case uint64: | 
|  | return -x | 
|  | case uintptr: | 
|  | return -x | 
|  | case float32: | 
|  | return -x | 
|  | case float64: | 
|  | return -x | 
|  | case complex64: | 
|  | return -x | 
|  | case complex128: | 
|  | return -x | 
|  | } | 
|  | case token.MUL: | 
|  | return load(deref(instr.X.Type()), x.(*value)) | 
|  | case token.NOT: | 
|  | return !x.(bool) | 
|  | case token.XOR: | 
|  | switch x := x.(type) { | 
|  | case int: | 
|  | return ^x | 
|  | case int8: | 
|  | return ^x | 
|  | case int16: | 
|  | return ^x | 
|  | case int32: | 
|  | return ^x | 
|  | case int64: | 
|  | return ^x | 
|  | case uint: | 
|  | return ^x | 
|  | case uint8: | 
|  | return ^x | 
|  | case uint16: | 
|  | return ^x | 
|  | case uint32: | 
|  | return ^x | 
|  | case uint64: | 
|  | return ^x | 
|  | case uintptr: | 
|  | return ^x | 
|  | } | 
|  | } | 
|  | panic(fmt.Sprintf("invalid unary op %s %T", instr.Op, x)) | 
|  | } | 
|  |  | 
|  | // typeAssert checks whether dynamic type of itf is instr.AssertedType. | 
|  | // It returns the extracted value on success, and panics on failure, | 
|  | // unless instr.CommaOk, in which case it always returns a "value,ok" tuple. | 
|  | func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value { | 
|  | var v value | 
|  | err := "" | 
|  | if itf.t == nil { | 
|  | err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType) | 
|  |  | 
|  | } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok { | 
|  | v = itf | 
|  | err = checkInterface(i, idst, itf) | 
|  |  | 
|  | } else if types.Identical(itf.t, instr.AssertedType) { | 
|  | v = itf.v // extract value | 
|  |  | 
|  | } else { | 
|  | err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType) | 
|  | } | 
|  |  | 
|  | if err != "" { | 
|  | if !instr.CommaOk { | 
|  | panic(err) | 
|  | } | 
|  | return tuple{zero(instr.AssertedType), false} | 
|  | } | 
|  | if instr.CommaOk { | 
|  | return tuple{v, true} | 
|  | } | 
|  | return v | 
|  | } | 
|  |  | 
|  | // If CapturedOutput is non-nil, all writes by the interpreted program | 
|  | // to file descriptors 1 and 2 will also be written to CapturedOutput. | 
|  | // | 
|  | // (The $GOROOT/test system requires that the test be considered a | 
|  | // failure if "BUG" appears in the combined stdout/stderr output, even | 
|  | // if it exits zero.  This is a global variable shared by all | 
|  | // interpreters in the same process.) | 
|  | var CapturedOutput *bytes.Buffer | 
|  | var capturedOutputMu sync.Mutex | 
|  |  | 
|  | // write writes bytes b to the target program's standard output. | 
|  | // The print/println built-ins and the write() system call funnel | 
|  | // through here so they can be captured by the test driver. | 
|  | func print(b []byte) (int, error) { | 
|  | if CapturedOutput != nil { | 
|  | capturedOutputMu.Lock() | 
|  | CapturedOutput.Write(b) // ignore errors | 
|  | capturedOutputMu.Unlock() | 
|  | } | 
|  | return os.Stdout.Write(b) | 
|  | } | 
|  |  | 
|  | // callBuiltin interprets a call to builtin fn with arguments args, | 
|  | // returning its result. | 
|  | func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value { | 
|  | switch fn.Name() { | 
|  | case "append": | 
|  | if len(args) == 1 { | 
|  | return args[0] | 
|  | } | 
|  | if s, ok := args[1].(string); ok { | 
|  | // append([]byte, ...string) []byte | 
|  | arg0 := args[0].([]value) | 
|  | for i := 0; i < len(s); i++ { | 
|  | arg0 = append(arg0, s[i]) | 
|  | } | 
|  | return arg0 | 
|  | } | 
|  | // append([]T, ...[]T) []T | 
|  | return append(args[0].([]value), args[1].([]value)...) | 
|  |  | 
|  | case "copy": // copy([]T, []T) int or copy([]byte, string) int | 
|  | src := args[1] | 
|  | if _, ok := src.(string); ok { | 
|  | params := fn.Type().(*types.Signature).Params() | 
|  | src = conv(params.At(0).Type(), params.At(1).Type(), src) | 
|  | } | 
|  | return copy(args[0].([]value), src.([]value)) | 
|  |  | 
|  | case "close": // close(chan T) | 
|  | close(args[0].(chan value)) | 
|  | return nil | 
|  |  | 
|  | case "delete": // delete(map[K]value, K) | 
|  | switch m := args[0].(type) { | 
|  | case map[value]value: | 
|  | delete(m, args[1]) | 
|  | case *hashmap: | 
|  | m.delete(args[1].(hashable)) | 
|  | default: | 
|  | panic(fmt.Sprintf("illegal map type: %T", m)) | 
|  | } | 
|  | return nil | 
|  |  | 
|  | case "print", "println": // print(any, ...) | 
|  | ln := fn.Name() == "println" | 
|  | var buf bytes.Buffer | 
|  | for i, arg := range args { | 
|  | if i > 0 && ln { | 
|  | buf.WriteRune(' ') | 
|  | } | 
|  | buf.WriteString(toString(arg)) | 
|  | } | 
|  | if ln { | 
|  | buf.WriteRune('\n') | 
|  | } | 
|  | print(buf.Bytes()) | 
|  | return nil | 
|  |  | 
|  | case "len": | 
|  | switch x := args[0].(type) { | 
|  | case string: | 
|  | return len(x) | 
|  | case array: | 
|  | return len(x) | 
|  | case *value: | 
|  | return len((*x).(array)) | 
|  | case []value: | 
|  | return len(x) | 
|  | case map[value]value: | 
|  | return len(x) | 
|  | case *hashmap: | 
|  | return x.len() | 
|  | case chan value: | 
|  | return len(x) | 
|  | default: | 
|  | panic(fmt.Sprintf("len: illegal operand: %T", x)) | 
|  | } | 
|  |  | 
|  | case "cap": | 
|  | switch x := args[0].(type) { | 
|  | case array: | 
|  | return cap(x) | 
|  | case *value: | 
|  | return cap((*x).(array)) | 
|  | case []value: | 
|  | return cap(x) | 
|  | case chan value: | 
|  | return cap(x) | 
|  | default: | 
|  | panic(fmt.Sprintf("cap: illegal operand: %T", x)) | 
|  | } | 
|  |  | 
|  | case "real": | 
|  | switch c := args[0].(type) { | 
|  | case complex64: | 
|  | return real(c) | 
|  | case complex128: | 
|  | return real(c) | 
|  | default: | 
|  | panic(fmt.Sprintf("real: illegal operand: %T", c)) | 
|  | } | 
|  |  | 
|  | case "imag": | 
|  | switch c := args[0].(type) { | 
|  | case complex64: | 
|  | return imag(c) | 
|  | case complex128: | 
|  | return imag(c) | 
|  | default: | 
|  | panic(fmt.Sprintf("imag: illegal operand: %T", c)) | 
|  | } | 
|  |  | 
|  | case "complex": | 
|  | switch f := args[0].(type) { | 
|  | case float32: | 
|  | return complex(f, args[1].(float32)) | 
|  | case float64: | 
|  | return complex(f, args[1].(float64)) | 
|  | default: | 
|  | panic(fmt.Sprintf("complex: illegal operand: %T", f)) | 
|  | } | 
|  |  | 
|  | case "panic": | 
|  | // ssa.Panic handles most cases; this is only for "go | 
|  | // panic" or "defer panic". | 
|  | panic(targetPanic{args[0]}) | 
|  |  | 
|  | case "recover": | 
|  | return doRecover(caller) | 
|  |  | 
|  | case "ssa:wrapnilchk": | 
|  | recv := args[0] | 
|  | if recv.(*value) == nil { | 
|  | recvType := args[1] | 
|  | methodName := args[2] | 
|  | panic(fmt.Sprintf("value method (%s).%s called using nil *%s pointer", | 
|  | recvType, methodName, recvType)) | 
|  | } | 
|  | return recv | 
|  | } | 
|  |  | 
|  | panic("unknown built-in: " + fn.Name()) | 
|  | } | 
|  |  | 
|  | func rangeIter(x value, t types.Type) iter { | 
|  | switch x := x.(type) { | 
|  | case map[value]value: | 
|  | return &mapIter{iter: reflect.ValueOf(x).MapRange()} | 
|  | case *hashmap: | 
|  | return &hashmapIter{iter: reflect.ValueOf(x.entries()).MapRange()} | 
|  | case string: | 
|  | return &stringIter{Reader: strings.NewReader(x)} | 
|  | } | 
|  | panic(fmt.Sprintf("cannot range over %T", x)) | 
|  | } | 
|  |  | 
|  | // widen widens a basic typed value x to the widest type of its | 
|  | // category, one of: | 
|  | // | 
|  | //	bool, int64, uint64, float64, complex128, string. | 
|  | // | 
|  | // This is inefficient but reduces the size of the cross-product of | 
|  | // cases we have to consider. | 
|  | func widen(x value) value { | 
|  | switch y := x.(type) { | 
|  | case bool, int64, uint64, float64, complex128, string, unsafe.Pointer: | 
|  | return x | 
|  | case int: | 
|  | return int64(y) | 
|  | case int8: | 
|  | return int64(y) | 
|  | case int16: | 
|  | return int64(y) | 
|  | case int32: | 
|  | return int64(y) | 
|  | case uint: | 
|  | return uint64(y) | 
|  | case uint8: | 
|  | return uint64(y) | 
|  | case uint16: | 
|  | return uint64(y) | 
|  | case uint32: | 
|  | return uint64(y) | 
|  | case uintptr: | 
|  | return uint64(y) | 
|  | case float32: | 
|  | return float64(y) | 
|  | case complex64: | 
|  | return complex128(y) | 
|  | } | 
|  | panic(fmt.Sprintf("cannot widen %T", x)) | 
|  | } | 
|  |  | 
|  | // conv converts the value x of type t_src to type t_dst and returns | 
|  | // the result. | 
|  | // Possible cases are described with the ssa.Convert operator. | 
|  | func conv(t_dst, t_src types.Type, x value) value { | 
|  | ut_src := t_src.Underlying() | 
|  | ut_dst := t_dst.Underlying() | 
|  |  | 
|  | // Destination type is not an "untyped" type. | 
|  | if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { | 
|  | panic("oops: conversion to 'untyped' type: " + b.String()) | 
|  | } | 
|  |  | 
|  | // Nor is it an interface type. | 
|  | if _, ok := ut_dst.(*types.Interface); ok { | 
|  | if _, ok := ut_src.(*types.Interface); ok { | 
|  | panic("oops: Convert should be ChangeInterface") | 
|  | } else { | 
|  | panic("oops: Convert should be MakeInterface") | 
|  | } | 
|  | } | 
|  |  | 
|  | // Remaining conversions: | 
|  | //    + untyped string/number/bool constant to a specific | 
|  | //      representation. | 
|  | //    + conversions between non-complex numeric types. | 
|  | //    + conversions between complex numeric types. | 
|  | //    + integer/[]byte/[]rune -> string. | 
|  | //    + string -> []byte/[]rune. | 
|  | // | 
|  | // All are treated the same: first we extract the value to the | 
|  | // widest representation (int64, uint64, float64, complex128, | 
|  | // or string), then we convert it to the desired type. | 
|  |  | 
|  | switch ut_src := ut_src.(type) { | 
|  | case *types.Pointer: | 
|  | switch ut_dst := ut_dst.(type) { | 
|  | case *types.Basic: | 
|  | // *value to unsafe.Pointer? | 
|  | if ut_dst.Kind() == types.UnsafePointer { | 
|  | return unsafe.Pointer(x.(*value)) | 
|  | } | 
|  | } | 
|  |  | 
|  | case *types.Slice: | 
|  | // []byte or []rune -> string | 
|  | // TODO(adonovan): fix: type B byte; conv([]B -> string). | 
|  | switch ut_src.Elem().(*types.Basic).Kind() { | 
|  | case types.Byte: | 
|  | x := x.([]value) | 
|  | b := make([]byte, 0, len(x)) | 
|  | for i := range x { | 
|  | b = append(b, x[i].(byte)) | 
|  | } | 
|  | return string(b) | 
|  |  | 
|  | case types.Rune: | 
|  | x := x.([]value) | 
|  | r := make([]rune, 0, len(x)) | 
|  | for i := range x { | 
|  | r = append(r, x[i].(rune)) | 
|  | } | 
|  | return string(r) | 
|  | } | 
|  |  | 
|  | case *types.Basic: | 
|  | x = widen(x) | 
|  |  | 
|  | // integer -> string? | 
|  | // TODO(adonovan): fix: test integer -> named alias of string. | 
|  | if ut_src.Info()&types.IsInteger != 0 { | 
|  | if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String { | 
|  | return fmt.Sprintf("%c", x) | 
|  | } | 
|  | } | 
|  |  | 
|  | // string -> []rune, []byte or string? | 
|  | if s, ok := x.(string); ok { | 
|  | switch ut_dst := ut_dst.(type) { | 
|  | case *types.Slice: | 
|  | var res []value | 
|  | // TODO(adonovan): fix: test named alias of rune, byte. | 
|  | switch ut_dst.Elem().(*types.Basic).Kind() { | 
|  | case types.Rune: | 
|  | for _, r := range []rune(s) { | 
|  | res = append(res, r) | 
|  | } | 
|  | return res | 
|  | case types.Byte: | 
|  | for _, b := range []byte(s) { | 
|  | res = append(res, b) | 
|  | } | 
|  | return res | 
|  | } | 
|  | case *types.Basic: | 
|  | if ut_dst.Kind() == types.String { | 
|  | return x.(string) | 
|  | } | 
|  | } | 
|  | break // fail: no other conversions for string | 
|  | } | 
|  |  | 
|  | // unsafe.Pointer -> *value | 
|  | if ut_src.Kind() == types.UnsafePointer { | 
|  | // TODO(adonovan): this is wrong and cannot | 
|  | // really be fixed with the current design. | 
|  | // | 
|  | // return (*value)(x.(unsafe.Pointer)) | 
|  | // creates a new pointer of a different | 
|  | // type but the underlying interface value | 
|  | // knows its "true" type and so cannot be | 
|  | // meaningfully used through the new pointer. | 
|  | // | 
|  | // To make this work, the interpreter needs to | 
|  | // simulate the memory layout of a real | 
|  | // compiled implementation. | 
|  | // | 
|  | // To at least preserve type-safety, we'll | 
|  | // just return the zero value of the | 
|  | // destination type. | 
|  | return zero(t_dst) | 
|  | } | 
|  |  | 
|  | // Conversions between complex numeric types? | 
|  | if ut_src.Info()&types.IsComplex != 0 { | 
|  | switch ut_dst.(*types.Basic).Kind() { | 
|  | case types.Complex64: | 
|  | return complex64(x.(complex128)) | 
|  | case types.Complex128: | 
|  | return x.(complex128) | 
|  | } | 
|  | break // fail: no other conversions for complex | 
|  | } | 
|  |  | 
|  | // Conversions between non-complex numeric types? | 
|  | if ut_src.Info()&types.IsNumeric != 0 { | 
|  | kind := ut_dst.(*types.Basic).Kind() | 
|  | switch x := x.(type) { | 
|  | case int64: // signed integer -> numeric? | 
|  | switch kind { | 
|  | case types.Int: | 
|  | return int(x) | 
|  | case types.Int8: | 
|  | return int8(x) | 
|  | case types.Int16: | 
|  | return int16(x) | 
|  | case types.Int32: | 
|  | return int32(x) | 
|  | case types.Int64: | 
|  | return int64(x) | 
|  | case types.Uint: | 
|  | return uint(x) | 
|  | case types.Uint8: | 
|  | return uint8(x) | 
|  | case types.Uint16: | 
|  | return uint16(x) | 
|  | case types.Uint32: | 
|  | return uint32(x) | 
|  | case types.Uint64: | 
|  | return uint64(x) | 
|  | case types.Uintptr: | 
|  | return uintptr(x) | 
|  | case types.Float32: | 
|  | return float32(x) | 
|  | case types.Float64: | 
|  | return float64(x) | 
|  | } | 
|  |  | 
|  | case uint64: // unsigned integer -> numeric? | 
|  | switch kind { | 
|  | case types.Int: | 
|  | return int(x) | 
|  | case types.Int8: | 
|  | return int8(x) | 
|  | case types.Int16: | 
|  | return int16(x) | 
|  | case types.Int32: | 
|  | return int32(x) | 
|  | case types.Int64: | 
|  | return int64(x) | 
|  | case types.Uint: | 
|  | return uint(x) | 
|  | case types.Uint8: | 
|  | return uint8(x) | 
|  | case types.Uint16: | 
|  | return uint16(x) | 
|  | case types.Uint32: | 
|  | return uint32(x) | 
|  | case types.Uint64: | 
|  | return uint64(x) | 
|  | case types.Uintptr: | 
|  | return uintptr(x) | 
|  | case types.Float32: | 
|  | return float32(x) | 
|  | case types.Float64: | 
|  | return float64(x) | 
|  | } | 
|  |  | 
|  | case float64: // floating point -> numeric? | 
|  | switch kind { | 
|  | case types.Int: | 
|  | return int(x) | 
|  | case types.Int8: | 
|  | return int8(x) | 
|  | case types.Int16: | 
|  | return int16(x) | 
|  | case types.Int32: | 
|  | return int32(x) | 
|  | case types.Int64: | 
|  | return int64(x) | 
|  | case types.Uint: | 
|  | return uint(x) | 
|  | case types.Uint8: | 
|  | return uint8(x) | 
|  | case types.Uint16: | 
|  | return uint16(x) | 
|  | case types.Uint32: | 
|  | return uint32(x) | 
|  | case types.Uint64: | 
|  | return uint64(x) | 
|  | case types.Uintptr: | 
|  | return uintptr(x) | 
|  | case types.Float32: | 
|  | return float32(x) | 
|  | case types.Float64: | 
|  | return float64(x) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | panic(fmt.Sprintf("unsupported conversion: %s  -> %s, dynamic type %T", t_src, t_dst, x)) | 
|  | } | 
|  |  | 
|  | // sliceToArrayPointer converts the value x of type slice to type t_dst | 
|  | // a pointer to array and returns the result. | 
|  | func sliceToArrayPointer(t_dst, t_src types.Type, x value) value { | 
|  | if _, ok := t_src.Underlying().(*types.Slice); ok { | 
|  | if ptr, ok := t_dst.Underlying().(*types.Pointer); ok { | 
|  | if arr, ok := ptr.Elem().Underlying().(*types.Array); ok { | 
|  | x := x.([]value) | 
|  | if arr.Len() > int64(len(x)) { | 
|  | panic("array length is greater than slice length") | 
|  | } | 
|  | if x == nil { | 
|  | return zero(t_dst) | 
|  | } | 
|  | v := value(array(x[:arr.Len()])) | 
|  | return &v | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | panic(fmt.Sprintf("unsupported conversion: %s  -> %s, dynamic type %T", t_src, t_dst, x)) | 
|  | } | 
|  |  | 
|  | // checkInterface checks that the method set of x implements the | 
|  | // interface itype. | 
|  | // On success it returns "", on failure, an error message. | 
|  | func checkInterface(i *interpreter, itype *types.Interface, x iface) string { | 
|  | if meth, _ := types.MissingMethod(x.t, itype, true); meth != nil { | 
|  | return fmt.Sprintf("interface conversion: %v is not %v: missing method %s", | 
|  | x.t, itype, meth.Name()) | 
|  | } | 
|  | return "" // ok | 
|  | } |