diff options
Diffstat (limited to 'src/pkg/exp/ogle/rvalue.go')
-rw-r--r-- | src/pkg/exp/ogle/rvalue.go | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/src/pkg/exp/ogle/rvalue.go b/src/pkg/exp/ogle/rvalue.go new file mode 100644 index 000000000..9077e238b --- /dev/null +++ b/src/pkg/exp/ogle/rvalue.go @@ -0,0 +1,579 @@ +// Copyright 2009 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 ogle + +import ( + "debug/proc"; + "exp/eval"; + "fmt"; +) + +// A RemoteMismatchError occurs when an operation that requires two +// identical remote processes is given different process. For +// example, this occurs when trying to set a pointer in one process to +// point to something in another process. +type RemoteMismatchError string + +func (e RemoteMismatchError) String() string { + return string(e); +} + +// A ReadOnlyError occurs when attempting to set or assign to a +// read-only value. +type ReadOnlyError string + +func (e ReadOnlyError) String() string { + return string(e); +} + +// A maker is a function that converts a remote address into an +// interpreter Value. +type maker func(remote) eval.Value + +type remoteValue interface { + addr() remote; +} + +// remote represents an address in a remote process. +type remote struct { + base proc.Word; + p *Process; +} + +func (v remote) Get(a aborter, size int) uint64 { + // TODO(austin) This variable might temporarily be in a + // register. We could trace the assembly back from the + // current PC, looking for the beginning of the function or a + // call (both of which guarantee that the variable is in + // memory), or an instruction that loads the variable into a + // register. + // + // TODO(austin) If this is a local variable, it might not be + // live at this PC. In fact, because the compiler reuses + // slots, there might even be a different local variable at + // this location right now. A simple solution to both + // problems is to include the range of PC's over which a local + // variable is live in the symbol table. + // + // TODO(austin) We need to prevent the remote garbage + // collector from collecting objects out from under us. + var arr [8]byte; + buf := arr[0:size]; + _, err := v.p.Peek(v.base, buf); + if err != nil { + a.Abort(err); + } + return uint64(v.p.ToWord(buf)); +} + +func (v remote) Set(a aborter, size int, x uint64) { + var arr [8]byte; + buf := arr[0:size]; + v.p.FromWord(proc.Word(x), buf); + _, err := v.p.Poke(v.base, buf); + if err != nil { + a.Abort(err); + } +} + +func (v remote) plus(x proc.Word) remote { + return remote{v.base + x, v.p}; +} + +func tryRVString(f func(a aborter) string) string { + var s string; + err := try(func(a aborter) { s = f(a) }); + if err != nil { + return fmt.Sprintf("<error: %v>", err); + } + return s; +} + +/* + * Bool + */ + +type remoteBool struct { + r remote; +} + +func (v remoteBool) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteBool) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.BoolValue).Get(t)); +} + +func (v remoteBool) Get(t *eval.Thread) bool { + return v.aGet(t); +} + +func (v remoteBool) aGet(a aborter) bool { + return v.r.Get(a, 1) != 0; +} + +func (v remoteBool) Set(t *eval.Thread, x bool) { + v.aSet(t, x); +} + +func (v remoteBool) aSet(a aborter, x bool) { + if x { + v.r.Set(a, 1, 1); + } else { + v.r.Set(a, 1, 0); + } +} + +func (v remoteBool) addr() remote { + return v.r; +} + +func mkBool(r remote) eval.Value { + return remoteBool{r}; +} + +/* + * Uint + */ + +type remoteUint struct { + r remote; + size int; +} + +func (v remoteUint) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteUint) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.UintValue).Get(t)); +} + +func (v remoteUint) Get(t *eval.Thread) uint64 { + return v.aGet(t); +} + +func (v remoteUint) aGet(a aborter) uint64 { + return v.r.Get(a, v.size); +} + +func (v remoteUint) Set(t *eval.Thread, x uint64) { + v.aSet(t, x); +} + +func (v remoteUint) aSet(a aborter, x uint64) { + v.r.Set(a, v.size, x); +} + +func (v remoteUint) addr() remote { + return v.r; +} + +func mkUint8(r remote) eval.Value { + return remoteUint{r, 1}; +} + +func mkUint16(r remote) eval.Value { + return remoteUint{r, 2}; +} + +func mkUint32(r remote) eval.Value { + return remoteUint{r, 4}; +} + +func mkUint64(r remote) eval.Value { + return remoteUint{r, 8}; +} + +func mkUint(r remote) eval.Value { + return remoteUint{r, r.p.IntSize()}; +} + +func mkUintptr(r remote) eval.Value { + return remoteUint{r, r.p.PtrSize()}; +} + +/* + * Int + */ + +type remoteInt struct { + r remote; + size int; +} + +func (v remoteInt) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteInt) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.IntValue).Get(t)); +} + +func (v remoteInt) Get(t *eval.Thread) int64 { + return v.aGet(t); +} + +func (v remoteInt) aGet(a aborter) int64 { + return int64(v.r.Get(a, v.size)); +} + +func (v remoteInt) Set(t *eval.Thread, x int64) { + v.aSet(t, x); +} + +func (v remoteInt) aSet(a aborter, x int64) { + v.r.Set(a, v.size, uint64(x)); +} + +func (v remoteInt) addr() remote { + return v.r; +} + +func mkInt8(r remote) eval.Value { + return remoteInt{r, 1}; +} + +func mkInt16(r remote) eval.Value { + return remoteInt{r, 2}; +} + +func mkInt32(r remote) eval.Value { + return remoteInt{r, 4}; +} + +func mkInt64(r remote) eval.Value { + return remoteInt{r, 8}; +} + +func mkInt(r remote) eval.Value { + return remoteInt{r, r.p.IntSize()}; +} + +/* + * Float + */ + +type remoteFloat struct { + r remote; + size int; +} + +func (v remoteFloat) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteFloat) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.FloatValue).Get(t)); +} + +func (v remoteFloat) Get(t *eval.Thread) float64 { + return v.aGet(t); +} + +func (v remoteFloat) aGet(a aborter) float64 { + bits := v.r.Get(a, v.size); + switch v.size { + case 4: + return float64(v.r.p.ToFloat32(uint32(bits))); + case 8: + return v.r.p.ToFloat64(bits); + } + panic("Unexpected float size ", v.size); +} + +func (v remoteFloat) Set(t *eval.Thread, x float64) { + v.aSet(t, x); +} + +func (v remoteFloat) aSet(a aborter, x float64) { + var bits uint64; + switch v.size{ + case 4: + bits = uint64(v.r.p.FromFloat32(float32(x))); + case 8: + bits = v.r.p.FromFloat64(x); + default: + panic("Unexpected float size ", v.size); + } + v.r.Set(a, v.size, bits); +} + +func (v remoteFloat) addr() remote { + return v.r; +} + +func mkFloat32(r remote) eval.Value { + return remoteFloat{r, 4}; +} + +func mkFloat64(r remote) eval.Value { + return remoteFloat{r, 8}; +} + +func mkFloat(r remote) eval.Value { + return remoteFloat{r, r.p.FloatSize()}; +} + +/* + * String + */ + +type remoteString struct { + r remote; +} + +func (v remoteString) String() string { + return tryRVString(func(a aborter) string { return v.aGet(a) }); +} + +func (v remoteString) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.StringValue).Get(t)); +} + +func (v remoteString) Get(t *eval.Thread) string { + return v.aGet(t); +} + +func (v remoteString) aGet(a aborter) string { + rs := v.r.p.runtime.String.mk(v.r).(remoteStruct); + str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)); + len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a); + + bytes := make([]uint8, len); + _, err := v.r.p.Peek(str, bytes); + if err != nil { + a.Abort(err); + } + return string(bytes); +} + +func (v remoteString) Set(t *eval.Thread, x string) { + v.aSet(t, x); +} + +func (v remoteString) aSet(a aborter, x string) { + // TODO(austin) This isn't generally possible without the + // ability to allocate remote memory. + a.Abort(ReadOnlyError("remote strings cannot be assigned to")); +} + +func mkString(r remote) eval.Value { + return remoteString{r}; +} + +/* + * Array + */ + +type remoteArray struct { + r remote; + len int64; + elemType *remoteType; +} + +func (v remoteArray) String() string { + res := "{"; + for i := int64(0); i < v.len; i++ { + if i > 0 { + res += ", "; + } + res += v.elem(i).String(); + } + return res + "}"; +} + +func (v remoteArray) Assign(t *eval.Thread, o eval.Value) { + // TODO(austin) Could do a bigger memcpy if o is a + // remoteArray in the same Process. + oa := o.(eval.ArrayValue); + for i := int64(0); i < v.len; i++ { + v.Elem(t, i).Assign(t, oa.Elem(t, i)); + } +} + +func (v remoteArray) Get(t *eval.Thread) eval.ArrayValue { + return v; +} + +func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { + return v.elem(i); +} + +func (v remoteArray) elem(i int64) eval.Value { + return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))); +} + +func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { + return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType}; +} + +/* + * Struct + */ + +type remoteStruct struct { + r remote; + layout []remoteStructField; +} + +type remoteStructField struct { + offset int; + fieldType *remoteType; +} + +func (v remoteStruct) String() string { + res := "{"; + for i := range v.layout { + if i > 0 { + res += ", "; + } + res += v.field(i).String(); + } + return res + "}"; +} + +func (v remoteStruct) Assign(t *eval.Thread, o eval.Value) { + // TODO(austin) Could do a bigger memcpy. + oa := o.(eval.StructValue); + l := len(v.layout); + for i := 0; i < l; i++ { + v.Field(t, i).Assign(t, oa.Field(t, i)); + } +} + +func (v remoteStruct) Get(t *eval.Thread) eval.StructValue { + return v; +} + +func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { + return v.field(i); +} + +func (v remoteStruct) field(i int) eval.Value { + f := &v.layout[i]; + return f.fieldType.mk(v.r.plus(proc.Word(f.offset))); +} + +func (v remoteStruct) addr() remote { + return v.r; +} + +/* + * Pointer + */ + +// TODO(austin) Comparing two remote pointers for equality in the +// interpreter will crash it because the Value's returned from +// remotePtr.Get() will be structs. + +type remotePtr struct { + r remote; + elemType *remoteType; +} + +func (v remotePtr) String() string { + return tryRVString(func(a aborter) string { + e := v.aGet(a); + if e == nil { + return "<nil>"; + } + return "&" + e.String(); + }); +} + +func (v remotePtr) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.PtrValue).Get(t)); +} + +func (v remotePtr) Get(t *eval.Thread) eval.Value { + return v.aGet(t); +} + +func (v remotePtr) aGet(a aborter) eval.Value { + addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())); + if addr == 0 { + return nil; + } + return v.elemType.mk(remote{addr, v.r.p}); +} + +func (v remotePtr) Set(t *eval.Thread, x eval.Value) { + v.aSet(t, x); +} + +func (v remotePtr) aSet(a aborter, x eval.Value) { + if x == nil { + v.r.Set(a, v.r.p.PtrSize(), 0); + return; + } + xr, ok := x.(remoteValue); + if !ok || v.r.p != xr.addr().p { + a.Abort(RemoteMismatchError("remote pointer must point within the same process")); + } + v.r.Set(a, v.r.p.PtrSize(), uint64(xr.addr().base)); +} + +func (v remotePtr) addr() remote { + return v.r; +} + +/* + * Slice + */ + +type remoteSlice struct { + r remote; + elemType *remoteType; +} + +func (v remoteSlice) String() string { + return tryRVString(func(a aborter) string { + b := v.aGet(a).Base; + if b == nil { + return "<nil>"; + } + return b.String(); + }); +} + +func (v remoteSlice) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.SliceValue).Get(t)); +} + +func (v remoteSlice) Get(t *eval.Thread) eval.Slice { + return v.aGet(t); +} + +func (v remoteSlice) aGet(a aborter) eval.Slice { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); + base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)); + nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a); + cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a); + if base == 0 { + return eval.Slice{nil, nel, cap}; + } + return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap}; +} + +func (v remoteSlice) Set(t *eval.Thread, x eval.Slice) { + v.aSet(t, x); +} + +func (v remoteSlice) aSet(a aborter, x eval.Slice) { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); + if x.Base == nil { + rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, 0); + } else { + ar, ok := x.Base.(remoteArray); + if !ok || v.r.p != ar.r.p { + a.Abort(RemoteMismatchError("remote slice must point within the same process")); + } + rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, uint64(ar.r.base)); + } + rs.field(v.r.p.f.Slice.Len).(remoteInt).aSet(a, x.Len); + rs.field(v.r.p.f.Slice.Cap).(remoteInt).aSet(a, x.Cap); +} |