x/debug: and support for index and slice expressions.

Slicing an array may yield a type which is not in the DWARF information
of the debugged program, so we add a type sliceOf to handle this.

Change-Id: I79200b036cf183bee7e24cabc5b145e1db522256
Reviewed-on: https://go-review.googlesource.com/16039
Reviewed-by: Dave Day <djd@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index ba2530d..522fab0 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -187,6 +187,41 @@
 	`&local_int16`:              program.Pointer{42, 42},
 	`*&local_int16`:             int16(-32321),
 	`*&*&*&*&local_int16`:       int16(-32321),
+	`local_array[2]`:            int8(3),
+	`local_slice[1]`:            uint8(108),
+	`local_slice_2[1]`:          int8(121),
+	`&local_array[1]`:           program.Pointer{42, 42},
+	`&local_slice[1]`:           program.Pointer{42, 42},
+	`local_map[-21]`:            float32(3.54321),
+	`local_map[+21]`:            float32(0),
+	`local_map_3[1024]`:         int8(1),
+	`local_map_3[512]`:          int8(-1),
+	`local_map_empty[21]`:       float32(0),
+	`local_map_nil[32]`:         float32(0),
+	`local_string[2]`:           uint8('m'),
+	`"hello"[2]`:                uint8('l'),
+	`local_array[1:3][1]`:       int8(3),
+	`local_array[0:4][2:3][0]`:  int8(3),
+	`local_array[:]`:            program.Slice{program.Array{42, 42, 5, 8}, 5},
+	`local_array[:2]`:           program.Slice{program.Array{42, 42, 2, 8}, 5},
+	`local_array[2:]`:           program.Slice{program.Array{42, 42, 3, 8}, 3},
+	`local_array[1:3]`:          program.Slice{program.Array{42, 42, 2, 8}, 4},
+	`local_array[:3:4]`:         program.Slice{program.Array{42, 42, 3, 8}, 4},
+	`local_array[1:3:4]`:        program.Slice{program.Array{42, 42, 2, 8}, 3},
+	`local_array[1:][1:][1:]`:   program.Slice{program.Array{42, 42, 2, 8}, 2},
+	`(&local_array)[:]`:         program.Slice{program.Array{42, 42, 5, 8}, 5},
+	`(&local_array)[:2]`:        program.Slice{program.Array{42, 42, 2, 8}, 5},
+	`(&local_array)[2:]`:        program.Slice{program.Array{42, 42, 3, 8}, 3},
+	`(&local_array)[1:3]`:       program.Slice{program.Array{42, 42, 2, 8}, 4},
+	`(&local_array)[:3:4]`:      program.Slice{program.Array{42, 42, 3, 8}, 4},
+	`(&local_array)[1:3:4]`:     program.Slice{program.Array{42, 42, 2, 8}, 3},
+	`local_slice[1:5][0:3][1]`:  uint8('i'),
+	`local_slice[:]`:            program.Slice{program.Array{42, 42, 5, 8}, 5},
+	`local_slice[:2]`:           program.Slice{program.Array{42, 42, 2, 8}, 5},
+	`local_slice[2:]`:           program.Slice{program.Array{42, 42, 3, 8}, 3},
+	`local_slice[1:3]`:          program.Slice{program.Array{42, 42, 2, 8}, 4},
+	`local_slice[:3:4]`:         program.Slice{program.Array{42, 42, 3, 8}, 4},
+	`local_slice[1:3:4]`:        program.Slice{program.Array{42, 42, 2, 8}, 3},
 	`5 + false`:                 nil,
 	``:                          nil,
 	`x + ""`:                    nil,
@@ -198,6 +233,14 @@
 	`x % 0`:                     nil,
 	`0 % 0`:                     nil,
 	`'a' % ('a'-'a')`:           nil,
+	`local_array[-2] + 1`:       nil,
+	`local_array[22] + 1`:       nil,
+	`local_slice[-2] + 1`:       nil,
+	`local_slice[22] + 1`:       nil,
+	`local_string[-2]`:          nil,
+	`local_string[22]`:          nil,
+	`"hello"[-2]`:               nil,
+	`"hello"[22]`:               nil,
 }
 
 func isHex(r uint8) bool {
diff --git a/ogle/program/server/eval.go b/ogle/program/server/eval.go
index 2199d1a..475331a 100644
--- a/ogle/program/server/eval.go
+++ b/ogle/program/server/eval.go
@@ -14,6 +14,7 @@
 
 import (
 	"errors"
+	"fmt"
 	"go/ast"
 	"go/parser"
 	"go/token"
@@ -28,10 +29,11 @@
 
 var (
 	// Some big.Ints to use in overflow checks.
-	bigIntMaxInt32 = new(big.Int).SetInt64(math.MaxInt32)
-	bigIntMinInt32 = new(big.Int).SetInt64(math.MinInt32)
-	bigIntMaxInt64 = new(big.Int).SetInt64(math.MaxInt64)
-	bigIntMinInt64 = new(big.Int).SetInt64(math.MinInt64)
+	bigIntMaxInt32  = big.NewInt(math.MaxInt32)
+	bigIntMinInt32  = big.NewInt(math.MinInt32)
+	bigIntMaxInt64  = big.NewInt(math.MaxInt64)
+	bigIntMinInt64  = big.NewInt(math.MinInt64)
+	bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64)
 )
 
 // result stores an intermediate value produced during evaluation of an expression.
@@ -95,6 +97,11 @@
 	a uint64
 }
 
+// A sliceOf is a slice created by slicing an array.
+// Unlike program.Slice, the DWARF type stored alongside a value of this type is
+// the type of the slice's elements, not the type of the slice.
+type sliceOf program.Slice
+
 // evalExpression evaluates a Go expression.
 // If the program counter and stack pointer are nonzero, they are used to determine
 // what local variables are available and where in memory they are.
@@ -144,6 +151,8 @@
 		return program.String{Length: uint64(len(v)), String: string(v)}, nil
 	case pointerToValue:
 		return program.Pointer{TypeID: uint64(val.d.Common().Offset), Address: v.a}, nil
+	case sliceOf:
+		return program.Slice(v), nil
 	case nil, addressableValue:
 		// This case should not be reachable.
 		return nil, errors.New("unknown error")
@@ -276,7 +285,7 @@
 			if pt, ok := t.(*dwarf.PtrType); ok {
 				return e.resultFrom(v.Address, pt.Type, getAddress)
 			} else {
-				e.err("invalid pointer type")
+				return e.err("invalid DWARF type for pointer")
 			}
 		case pointerToValue:
 			return e.resultFrom(v.a, x.d, getAddress)
@@ -285,6 +294,272 @@
 		}
 		return e.err("invalid indirect")
 
+	case *ast.IndexExpr:
+		x, index := e.evalNode(n.X, false), e.evalNode(n.Index, false)
+		if x.v == nil || index.v == nil {
+			return result{}
+		}
+		// The expression is x[index]
+		if m, ok := x.v.(program.Map); ok {
+			if getAddress {
+				return e.err("can't take address of map value")
+			}
+			mt, ok := followTypedefs(x.d).(*dwarf.MapType)
+			if !ok {
+				return e.err("invalid DWARF type for map")
+			}
+			var (
+				found bool   // true if the key was found
+				value result // the map value for the key
+				abort bool   // true if an error occurred while searching
+				// fn is a function that checks if one (key, value) pair corresponds
+				// to the index in the expression.
+				fn = func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool {
+					key := e.resultFrom(keyAddr, keyType, false)
+					if key.v == nil {
+						abort = true
+						return false // stop searching map
+					}
+					equal, ok := e.evalBinaryOp(token.EQL, index, key).v.(bool)
+					if !ok {
+						abort = true
+						return false // stop searching map
+					}
+					if equal {
+						found = true
+						value = e.resultFrom(valAddr, valType, false)
+						return false // stop searching map
+					}
+					return true // continue searching map
+				}
+			)
+			if err := e.server.peekMapValues(mt, m.Address, fn); err != nil {
+				return e.err(err.Error())
+			}
+			if abort {
+				// Some operation on individual map keys failed.
+				return result{}
+			}
+			if found {
+				return value
+			}
+			// The key wasn't in the map; return the zero value.
+			return e.zero(mt.ElemType)
+		}
+
+		// The index should be a non-negative integer for the remaining cases.
+		u, err := uint64FromResult(index)
+		if err != nil {
+			return e.err("invalid index: " + err.Error())
+		}
+		switch v := x.v.(type) {
+		case program.Array:
+			if u >= v.Length {
+				return e.err("array index out of bounds")
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return e.resultFrom(v.Element(u).Address, elemType, getAddress)
+		case program.Slice:
+			if u >= v.Length {
+				return e.err("slice index out of bounds")
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return e.resultFrom(v.Element(u).Address, elemType, getAddress)
+		case sliceOf:
+			if u >= v.Length {
+				return e.err("slice index out of bounds")
+			}
+			return e.resultFrom(v.Element(u).Address, x.d, getAddress)
+		case program.String:
+			if getAddress {
+				return e.err("can't take address of string element")
+			}
+			if u >= v.Length {
+				return e.err("string index out of bounds")
+			}
+			if u >= uint64(len(v.String)) {
+				return e.err("string element unavailable")
+			}
+			return e.uint8Result(v.String[u])
+		case untString:
+			if getAddress {
+				return e.err("can't take address of string element")
+			}
+			if u >= uint64(len(v)) {
+				return e.err("string index out of bounds")
+			}
+			return e.uint8Result(v[u])
+		}
+		return e.err("invalid index expression")
+
+	case *ast.SliceExpr:
+		if n.Slice3 && n.High == nil {
+			return e.err("middle index required in full slice")
+		}
+		if n.Slice3 && n.Max == nil {
+			return e.err("final index required in full slice")
+		}
+		var (
+			low, high, max uint64
+			err            error
+		)
+		if n.Low != nil {
+			low, err = uint64FromResult(e.evalNode(n.Low, false))
+			if err != nil {
+				return e.err("invalid slice lower bound: " + err.Error())
+			}
+		}
+		if n.High != nil {
+			high, err = uint64FromResult(e.evalNode(n.High, false))
+			if err != nil {
+				return e.err("invalid slice upper bound: " + err.Error())
+			}
+		}
+		if n.Max != nil {
+			max, err = uint64FromResult(e.evalNode(n.Max, false))
+			if err != nil {
+				return e.err("invalid slice capacity: " + err.Error())
+			}
+		}
+		x := e.evalNode(n.X, false)
+		switch v := x.v.(type) {
+		case program.Array, program.Pointer, pointerToValue:
+			// This case handles the slicing of arrays and pointers to arrays.
+			var arr program.Array
+			switch v := x.v.(type) {
+			case program.Array:
+				arr = v
+			case program.Pointer:
+				pt, ok := followTypedefs(x.d).(*dwarf.PtrType)
+				if !ok {
+					return e.err("invalid DWARF type for pointer")
+				}
+				a := e.resultFrom(v.Address, pt.Type, false)
+				arr, ok = a.v.(program.Array)
+				if !ok {
+					// v is a pointer to something other than an array.
+					return e.err("cannot slice pointer")
+				}
+			case pointerToValue:
+				a := e.resultFrom(v.a, x.d, false)
+				var ok bool
+				arr, ok = a.v.(program.Array)
+				if !ok {
+					// v is a pointer to something other than an array.
+					return e.err("cannot slice pointer")
+				}
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(arr.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			if n.High == nil {
+				high = arr.Length
+			} else if high > arr.Length {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = arr.Length
+			} else if max > arr.Length {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			return result{
+				d: elemType,
+				v: sliceOf{
+					Array: program.Array{
+						ElementTypeID: arr.ElementTypeID,
+						Address:       arr.Element(low).Address,
+						Length:        high - low,
+						StrideBits:    uint64(elemType.Common().ByteSize) * 8,
+					},
+					Capacity: max - low,
+				},
+			}
+		case program.Slice:
+			if n.High == nil {
+				high = v.Length
+			} else if high > v.Capacity {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = v.Capacity
+			} else if max > v.Capacity {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			v.Address += low * (v.StrideBits / 8)
+			v.Length = high - low
+			v.Capacity = max - low
+			return result{x.d, v}
+		case sliceOf:
+			if n.High == nil {
+				high = v.Length
+			} else if high > v.Capacity {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = v.Capacity
+			} else if max > v.Capacity {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			v.Address += low * (v.StrideBits / 8)
+			v.Length = high - low
+			v.Capacity = max - low
+			return result{x.d, v}
+		case program.String:
+			if n.Max != nil {
+				return e.err("full slice of string")
+			}
+			if n.High == nil {
+				high = v.Length
+			}
+			if low > high || high > v.Length {
+				return e.err("invalid slice index")
+			}
+			v.Length = high - low
+			if low > uint64(len(v.String)) {
+				// v.String was truncated before the point where this slice starts.
+				v.String = ""
+			} else {
+				if high > uint64(len(v.String)) {
+					// v.String was truncated before the point where this slice ends.
+					high = uint64(len(v.String))
+				}
+				v.String = v.String[low:high]
+			}
+			return result{x.d, v}
+		case untString:
+			if n.Max != nil {
+				return e.err("full slice of string")
+			}
+			if n.High == nil {
+				high = uint64(len(v))
+			}
+			if low > high {
+				return e.err("invalid slice expression")
+			}
+			if high > uint64(len(v)) {
+				return e.err("slice upper bound is too large")
+			}
+			return e.stringResult(string(v[low:high]))
+		default:
+			return e.err("invalid slice expression")
+		}
+
 	case *ast.UnaryExpr:
 		if n.Op == token.AND {
 			x := e.evalNode(n.X, true)
@@ -1333,6 +1608,15 @@
 	return result{t, uint8(v)}
 }
 
+// stringResult constructs a result for a string value.
+func (e *evaluator) stringResult(s string) result {
+	t, ok := e.getBaseType("string")
+	if !ok {
+		e.err("couldn't construct string")
+	}
+	return result{t, program.String{Length: uint64(len(s)), String: s}}
+}
+
 // getBaseType returns the *dwarf.Type with a given name.
 // TODO: cache this.
 func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) {
@@ -1365,6 +1649,85 @@
 	return result{t, v}
 }
 
+// zero returns the zero value of type t.
+// TODO: implement for array and struct.
+func (e *evaluator) zero(t dwarf.Type) result {
+	var v interface{}
+	switch typ := followTypedefs(t).(type) {
+	case *dwarf.CharType, *dwarf.IntType, *dwarf.EnumType:
+		switch typ.Common().ByteSize {
+		case 1:
+			v = int8(0)
+		case 2:
+			v = int16(0)
+		case 4:
+			v = int32(0)
+		case 8:
+			v = int64(0)
+		default:
+			return e.err("invalid integer size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.UcharType, *dwarf.UintType:
+		switch typ.Common().ByteSize {
+		case 1:
+			v = uint8(0)
+		case 2:
+			v = uint16(0)
+		case 4:
+			v = uint32(0)
+		case 8:
+			v = uint64(0)
+		default:
+			return e.err("invalid unsigned integer size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.FloatType:
+		switch typ.Common().ByteSize {
+		case 4:
+			v = float32(0)
+		case 8:
+			v = float64(0)
+		default:
+			return e.err("invalid float size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.ComplexType:
+		switch typ.Common().ByteSize {
+		case 8:
+			v = complex64(0)
+		case 16:
+			v = complex128(0)
+		default:
+			return e.err("invalid complex size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.BoolType:
+		v = false
+	case *dwarf.PtrType:
+		v = program.Pointer{TypeID: uint64(t.Common().Offset)}
+	case *dwarf.SliceType:
+		v = program.Slice{
+			Array: program.Array{
+				ElementTypeID: uint64(typ.ElemType.Common().Offset),
+				StrideBits:    uint64(typ.ElemType.Common().ByteSize) * 8,
+			},
+		}
+	case *dwarf.StringType:
+		v = program.String{}
+	case *dwarf.InterfaceType:
+		v = program.Interface{}
+	case *dwarf.FuncType:
+		v = program.Func{}
+	case *dwarf.MapType:
+		v = program.Map{TypeID: uint64(t.Common().Offset)}
+	case *dwarf.ChanType:
+		v = program.Channel{
+			ElementTypeID: uint64(typ.ElemType.Common().Offset),
+			Stride:        uint64(typ.ElemType.Common().ByteSize),
+		}
+	default:
+		return e.err("can't get zero value of this type")
+	}
+	return result{t, v}
+}
+
 // convertUntyped converts x to be the same type as y, if x is untyped and the
 // conversion is possible.
 //
@@ -1552,6 +1915,85 @@
 	return x
 }
 
+// uint64FromResult converts a result into a uint64 for slice or index expressions.
+// It returns an error if the conversion cannot be done.
+func uint64FromResult(x result) (uint64, error) {
+	switch v := x.v.(type) {
+	case int8:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int16:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int32:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int64:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case uint8:
+		return uint64(v), nil
+	case uint16:
+		return uint64(v), nil
+	case uint32:
+		return uint64(v), nil
+	case uint64:
+		return v, nil
+	case untInt:
+		if v.Int.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		if v.Int.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return v.Int.Uint64(), nil
+	case untRune:
+		if v.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		if v.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return v.Uint64(), nil
+	case untFloat:
+		if !v.IsInt() {
+			return 0, errors.New("value is not an integer")
+		}
+		if v.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		i, _ := v.Int(nil)
+		if i.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return i.Uint64(), nil
+	case untComplex:
+		if v.i.Sign() != 0 {
+			return 0, errors.New("value is complex")
+		}
+		if !v.r.IsInt() {
+			return 0, errors.New("value is not an integer")
+		}
+		if v.r.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		i, _ := v.r.Int(nil)
+		if i.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return i.Uint64(), nil
+	}
+	return 0, fmt.Errorf("cannot convert to unsigned integer")
+}
+
 // followTypedefs returns the underlying type of t, removing any typedefs.
 // If t leads to a cycle of typedefs, followTypedefs returns nil.
 func followTypedefs(t dwarf.Type) dwarf.Type {
diff --git a/ogle/program/server/eval.m4 b/ogle/program/server/eval.m4
index e671a37..694b7c1 100644
--- a/ogle/program/server/eval.m4
+++ b/ogle/program/server/eval.m4
@@ -14,6 +14,7 @@
 
 import (
 	"errors"
+	"fmt"
 	"go/ast"
 	"go/parser"
 	"go/token"
@@ -28,10 +29,11 @@
 
 var (
 	// Some big.Ints to use in overflow checks.
-	bigIntMaxInt32 = new(big.Int).SetInt64(math.MaxInt32)
-	bigIntMinInt32 = new(big.Int).SetInt64(math.MinInt32)
-	bigIntMaxInt64 = new(big.Int).SetInt64(math.MaxInt64)
-	bigIntMinInt64 = new(big.Int).SetInt64(math.MinInt64)
+	bigIntMaxInt32  = big.NewInt(math.MaxInt32)
+	bigIntMinInt32  = big.NewInt(math.MinInt32)
+	bigIntMaxInt64  = big.NewInt(math.MaxInt64)
+	bigIntMinInt64  = big.NewInt(math.MinInt64)
+	bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64)
 )
 
 // result stores an intermediate value produced during evaluation of an expression.
@@ -95,6 +97,11 @@
 	a uint64
 }
 
+// A sliceOf is a slice created by slicing an array.
+// Unlike program.Slice, the DWARF type stored alongside a value of this type is
+// the type of the slice's elements, not the type of the slice.
+type sliceOf program.Slice
+
 // evalExpression evaluates a Go expression.
 // If the program counter and stack pointer are nonzero, they are used to determine
 // what local variables are available and where in memory they are.
@@ -144,6 +151,8 @@
 		return program.String{Length: uint64(len(v)), String: string(v)}, nil
 	case pointerToValue:
 		return program.Pointer{TypeID: uint64(val.d.Common().Offset), Address: v.a}, nil
+	case sliceOf:
+		return program.Slice(v), nil
 	case nil, addressableValue:
 		// This case should not be reachable.
 		return nil, errors.New("unknown error")
@@ -276,7 +285,7 @@
 			if pt, ok := t.(*dwarf.PtrType); ok {
 				return e.resultFrom(v.Address, pt.Type, getAddress)
 			} else {
-				e.err("invalid pointer type")
+				return e.err("invalid DWARF type for pointer")
 			}
 		case pointerToValue:
 			return e.resultFrom(v.a, x.d, getAddress)
@@ -285,6 +294,272 @@
 		}
 		return e.err("invalid indirect")
 
+	case *ast.IndexExpr:
+		x, index := e.evalNode(n.X, false), e.evalNode(n.Index, false)
+		if x.v == nil || index.v == nil {
+			return result{}
+		}
+		// The expression is x[index]
+		if m, ok := x.v.(program.Map); ok {
+			if getAddress {
+				return e.err("can't take address of map value")
+			}
+			mt, ok := followTypedefs(x.d).(*dwarf.MapType)
+			if !ok {
+				return e.err("invalid DWARF type for map")
+			}
+			var (
+				found bool   // true if the key was found
+				value result // the map value for the key
+				abort bool   // true if an error occurred while searching
+				// fn is a function that checks if one (key, value) pair corresponds
+				// to the index in the expression.
+				fn = func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool {
+					key := e.resultFrom(keyAddr, keyType, false)
+					if key.v == nil {
+						abort = true
+						return false // stop searching map
+					}
+					equal, ok := e.evalBinaryOp(token.EQL, index, key).v.(bool)
+					if !ok {
+						abort = true
+						return false // stop searching map
+					}
+					if equal {
+						found = true
+						value = e.resultFrom(valAddr, valType, false)
+						return false // stop searching map
+					}
+					return true // continue searching map
+				}
+			)
+			if err := e.server.peekMapValues(mt, m.Address, fn); err != nil {
+				return e.err(err.Error())
+			}
+			if abort {
+				// Some operation on individual map keys failed.
+				return result{}
+			}
+			if found {
+				return value
+			}
+			// The key wasn't in the map; return the zero value.
+			return e.zero(mt.ElemType)
+		}
+
+		// The index should be a non-negative integer for the remaining cases.
+		u, err := uint64FromResult(index)
+		if err != nil {
+			return e.err("invalid index: " + err.Error())
+		}
+		switch v := x.v.(type) {
+		case program.Array:
+			if u >= v.Length {
+				return e.err("array index out of bounds")
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return e.resultFrom(v.Element(u).Address, elemType, getAddress)
+		case program.Slice:
+			if u >= v.Length {
+				return e.err("slice index out of bounds")
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(v.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return e.resultFrom(v.Element(u).Address, elemType, getAddress)
+		case sliceOf:
+			if u >= v.Length {
+				return e.err("slice index out of bounds")
+			}
+			return e.resultFrom(v.Element(u).Address, x.d, getAddress)
+		case program.String:
+			if getAddress {
+				return e.err("can't take address of string element")
+			}
+			if u >= v.Length {
+				return e.err("string index out of bounds")
+			}
+			if u >= uint64(len(v.String)) {
+				return e.err("string element unavailable")
+			}
+			return e.uint8Result(v.String[u])
+		case untString:
+			if getAddress {
+				return e.err("can't take address of string element")
+			}
+			if u >= uint64(len(v)) {
+				return e.err("string index out of bounds")
+			}
+			return e.uint8Result(v[u])
+		}
+		return e.err("invalid index expression")
+
+	case *ast.SliceExpr:
+		if n.Slice3 && n.High == nil {
+			return e.err("middle index required in full slice")
+		}
+		if n.Slice3 && n.Max == nil {
+			return e.err("final index required in full slice")
+		}
+		var (
+			low, high, max uint64
+			err            error
+		)
+		if n.Low != nil {
+			low, err = uint64FromResult(e.evalNode(n.Low, false))
+			if err != nil {
+				return e.err("invalid slice lower bound: " + err.Error())
+			}
+		}
+		if n.High != nil {
+			high, err = uint64FromResult(e.evalNode(n.High, false))
+			if err != nil {
+				return e.err("invalid slice upper bound: " + err.Error())
+			}
+		}
+		if n.Max != nil {
+			max, err = uint64FromResult(e.evalNode(n.Max, false))
+			if err != nil {
+				return e.err("invalid slice capacity: " + err.Error())
+			}
+		}
+		x := e.evalNode(n.X, false)
+		switch v := x.v.(type) {
+		case program.Array, program.Pointer, pointerToValue:
+			// This case handles the slicing of arrays and pointers to arrays.
+			var arr program.Array
+			switch v := x.v.(type) {
+			case program.Array:
+				arr = v
+			case program.Pointer:
+				pt, ok := followTypedefs(x.d).(*dwarf.PtrType)
+				if !ok {
+					return e.err("invalid DWARF type for pointer")
+				}
+				a := e.resultFrom(v.Address, pt.Type, false)
+				arr, ok = a.v.(program.Array)
+				if !ok {
+					// v is a pointer to something other than an array.
+					return e.err("cannot slice pointer")
+				}
+			case pointerToValue:
+				a := e.resultFrom(v.a, x.d, false)
+				var ok bool
+				arr, ok = a.v.(program.Array)
+				if !ok {
+					// v is a pointer to something other than an array.
+					return e.err("cannot slice pointer")
+				}
+			}
+			elemType, err := e.server.dwarfData.Type(dwarf.Offset(arr.ElementTypeID))
+			if err != nil {
+				return e.err(err.Error())
+			}
+			if n.High == nil {
+				high = arr.Length
+			} else if high > arr.Length {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = arr.Length
+			} else if max > arr.Length {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			return result{
+				d: elemType,
+				v: sliceOf{
+					Array: program.Array{
+						ElementTypeID: arr.ElementTypeID,
+						Address:       arr.Element(low).Address,
+						Length:        high - low,
+						StrideBits:    uint64(elemType.Common().ByteSize) * 8,
+					},
+					Capacity: max - low,
+				},
+			}
+		case program.Slice:
+			if n.High == nil {
+				high = v.Length
+			} else if high > v.Capacity {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = v.Capacity
+			} else if max > v.Capacity {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			v.Address += low * (v.StrideBits / 8)
+			v.Length = high - low
+			v.Capacity = max - low
+			return result{x.d, v}
+		case sliceOf:
+			if n.High == nil {
+				high = v.Length
+			} else if high > v.Capacity {
+				return e.err("slice upper bound is too large")
+			}
+			if n.Max == nil {
+				max = v.Capacity
+			} else if max > v.Capacity {
+				return e.err("slice capacity is too large")
+			}
+			if low > high || high > max {
+				return e.err("invalid slice index")
+			}
+			v.Address += low * (v.StrideBits / 8)
+			v.Length = high - low
+			v.Capacity = max - low
+			return result{x.d, v}
+		case program.String:
+			if n.Max != nil {
+				return e.err("full slice of string")
+			}
+			if n.High == nil {
+				high = v.Length
+			}
+			if low > high || high > v.Length {
+				return e.err("invalid slice index")
+			}
+			v.Length = high - low
+			if low > uint64(len(v.String)) {
+				// v.String was truncated before the point where this slice starts.
+				v.String = ""
+			} else {
+				if high > uint64(len(v.String)) {
+					// v.String was truncated before the point where this slice ends.
+					high = uint64(len(v.String))
+				}
+				v.String = v.String[low:high]
+			}
+			return result{x.d, v}
+		case untString:
+			if n.Max != nil {
+				return e.err("full slice of string")
+			}
+			if n.High == nil {
+				high = uint64(len(v))
+			}
+			if low > high {
+				return e.err("invalid slice expression")
+			}
+			if high > uint64(len(v)) {
+				return e.err("slice upper bound is too large")
+			}
+			return e.stringResult(string(v[low:high]))
+		default:
+			return e.err("invalid slice expression")
+		}
+
 	case *ast.UnaryExpr:
 		if n.Op == token.AND {
 			x := e.evalNode(n.X, true)
@@ -941,6 +1216,15 @@
 	return result{t, uint8(v)}
 }
 
+// stringResult constructs a result for a string value.
+func (e *evaluator) stringResult(s string) result {
+	t, ok := e.getBaseType("string")
+	if !ok {
+		e.err("couldn't construct string")
+	}
+	return result{t, program.String{Length: uint64(len(s)), String: s}}
+}
+
 // getBaseType returns the *dwarf.Type with a given name.
 // TODO: cache this.
 func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) {
@@ -973,6 +1257,85 @@
 	return result{t, v}
 }
 
+// zero returns the zero value of type t.
+// TODO: implement for array and struct.
+func (e *evaluator) zero(t dwarf.Type) result {
+	var v interface{}
+	switch typ := followTypedefs(t).(type) {
+	case *dwarf.CharType, *dwarf.IntType, *dwarf.EnumType:
+		switch typ.Common().ByteSize {
+		case 1:
+			v = int8(0)
+		case 2:
+			v = int16(0)
+		case 4:
+			v = int32(0)
+		case 8:
+			v = int64(0)
+		default:
+			return e.err("invalid integer size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.UcharType, *dwarf.UintType:
+		switch typ.Common().ByteSize {
+		case 1:
+			v = uint8(0)
+		case 2:
+			v = uint16(0)
+		case 4:
+			v = uint32(0)
+		case 8:
+			v = uint64(0)
+		default:
+			return e.err("invalid unsigned integer size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.FloatType:
+		switch typ.Common().ByteSize {
+		case 4:
+			v = float32(0)
+		case 8:
+			v = float64(0)
+		default:
+			return e.err("invalid float size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.ComplexType:
+		switch typ.Common().ByteSize {
+		case 8:
+			v = complex64(0)
+		case 16:
+			v = complex128(0)
+		default:
+			return e.err("invalid complex size " + fmt.Sprint(typ.Common().ByteSize))
+		}
+	case *dwarf.BoolType:
+		v = false
+	case *dwarf.PtrType:
+		v = program.Pointer{TypeID: uint64(t.Common().Offset)}
+	case *dwarf.SliceType:
+		v = program.Slice{
+			Array: program.Array{
+				ElementTypeID: uint64(typ.ElemType.Common().Offset),
+				StrideBits:    uint64(typ.ElemType.Common().ByteSize) * 8,
+			},
+		}
+	case *dwarf.StringType:
+		v = program.String{}
+	case *dwarf.InterfaceType:
+		v = program.Interface{}
+	case *dwarf.FuncType:
+		v = program.Func{}
+	case *dwarf.MapType:
+		v = program.Map{TypeID: uint64(t.Common().Offset)}
+	case *dwarf.ChanType:
+		v = program.Channel{
+			ElementTypeID: uint64(typ.ElemType.Common().Offset),
+			Stride:        uint64(typ.ElemType.Common().ByteSize),
+		}
+	default:
+		return e.err("can't get zero value of this type")
+	}
+	return result{t, v}
+}
+
 // convertUntyped converts x to be the same type as y, if x is untyped and the
 // conversion is possible.
 //
@@ -1160,6 +1523,85 @@
 	return x
 }
 
+// uint64FromResult converts a result into a uint64 for slice or index expressions.
+// It returns an error if the conversion cannot be done.
+func uint64FromResult(x result) (uint64, error) {
+	switch v := x.v.(type) {
+	case int8:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int16:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int32:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case int64:
+		if v < 0 {
+			return 0, errors.New("value is negative")
+		}
+		return uint64(v), nil
+	case uint8:
+		return uint64(v), nil
+	case uint16:
+		return uint64(v), nil
+	case uint32:
+		return uint64(v), nil
+	case uint64:
+		return v, nil
+	case untInt:
+		if v.Int.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		if v.Int.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return v.Int.Uint64(), nil
+	case untRune:
+		if v.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		if v.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return v.Uint64(), nil
+	case untFloat:
+		if !v.IsInt() {
+			return 0, errors.New("value is not an integer")
+		}
+		if v.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		i, _ := v.Int(nil)
+		if i.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return i.Uint64(), nil
+	case untComplex:
+		if v.i.Sign() != 0 {
+			return 0, errors.New("value is complex")
+		}
+		if !v.r.IsInt() {
+			return 0, errors.New("value is not an integer")
+		}
+		if v.r.Sign() == -1 {
+			return 0, errors.New("value is negative")
+		}
+		i, _ := v.r.Int(nil)
+		if i.Cmp(bigIntMaxUint64) == +1 {
+			return 0, errors.New("value is too large")
+		}
+		return i.Uint64(), nil
+	}
+	return 0, fmt.Errorf("cannot convert to unsigned integer")
+}
+
 // followTypedefs returns the underlying type of t, removing any typedefs.
 // If t leads to a cycle of typedefs, followTypedefs returns nil.
 func followTypedefs(t dwarf.Type) dwarf.Type {