exp/eval, exp/ogle: remove packages eval and ogle

An externally maintained version of exp/eval can
be found at: https://bitbucket.org/binet/go-eval/ .

R=golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/4695047
This commit is contained in:
Robert Griesemer 2011-07-13 09:40:53 -07:00
parent 050d839df0
commit 4c986d86b1
34 changed files with 0 additions and 12795 deletions

View File

@ -79,7 +79,6 @@ DIRS=\
encoding/pem\
exec\
exp/datafmt\
exp/eval\
exp/gui\
exp/gui/x11\
exp/regexp/syntax\

View File

@ -1,37 +0,0 @@
# 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.
include ../../../Make.inc
TARG=exp/eval
GOFILES=\
abort.go\
bridge.go\
compiler.go\
expr.go\
expr1.go\
func.go\
scope.go\
stmt.go\
type.go\
typec.go\
value.go\
world.go\
include ../../../Make.pkg
main.$O: main.go $(pkgdir)/$(TARG).a
$(GC) $<
eval: main.$O
$(LD) -o $@ $<
gen.$O: gen.go
$(GC) $<
generate: gen.$O
$(LD) -o $@ $<;\
./generate > expr1.go;\
gofmt -w expr1.go

View File

@ -1,85 +0,0 @@
// 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 eval
import (
"fmt"
"os"
"runtime"
)
// Abort aborts the thread's current computation,
// causing the innermost Try to return err.
func (t *Thread) Abort(err os.Error) {
if t.abort == nil {
panic("abort: " + err.String())
}
t.abort <- err
runtime.Goexit()
}
// Try executes a computation; if the computation
// Aborts, Try returns the error passed to abort.
func (t *Thread) Try(f func(t *Thread)) os.Error {
oc := t.abort
c := make(chan os.Error)
t.abort = c
go func() {
f(t)
c <- nil
}()
err := <-c
t.abort = oc
return err
}
type DivByZeroError struct{}
func (DivByZeroError) String() string { return "divide by zero" }
type NilPointerError struct{}
func (NilPointerError) String() string { return "nil pointer dereference" }
type IndexError struct {
Idx, Len int64
}
func (e IndexError) String() string {
if e.Idx < 0 {
return fmt.Sprintf("negative index: %d", e.Idx)
}
return fmt.Sprintf("index %d exceeds length %d", e.Idx, e.Len)
}
type SliceError struct {
Lo, Hi, Cap int64
}
func (e SliceError) String() string {
return fmt.Sprintf("slice [%d:%d]; cap %d", e.Lo, e.Hi, e.Cap)
}
type KeyError struct {
Key interface{}
}
func (e KeyError) String() string { return fmt.Sprintf("key '%v' not found in map", e.Key) }
type NegativeLengthError struct {
Len int64
}
func (e NegativeLengthError) String() string {
return fmt.Sprintf("negative length: %d", e.Len)
}
type NegativeCapacityError struct {
Len int64
}
func (e NegativeCapacityError) String() string {
return fmt.Sprintf("negative capacity: %d", e.Len)
}

View File

@ -1,164 +0,0 @@
// 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 eval
import (
"log"
"go/token"
"reflect"
)
/*
* Type bridging
*/
var (
evalTypes = make(map[reflect.Type]Type)
nativeTypes = make(map[Type]reflect.Type)
)
// TypeFromNative converts a regular Go type into a the corresponding
// interpreter Type.
func TypeFromNative(t reflect.Type) Type {
if et, ok := evalTypes[t]; ok {
return et
}
var nt *NamedType
if t.Name() != "" {
name := t.PkgPath() + "·" + t.Name()
nt = &NamedType{token.NoPos, name, nil, true, make(map[string]Method)}
evalTypes[t] = nt
}
var et Type
switch t.Kind() {
case reflect.Bool:
et = BoolType
case reflect.Float32:
et = Float32Type
case reflect.Float64:
et = Float64Type
case reflect.Int16:
et = Int16Type
case reflect.Int32:
et = Int32Type
case reflect.Int64:
et = Int64Type
case reflect.Int8:
et = Int8Type
case reflect.Int:
et = IntType
case reflect.Uint16:
et = Uint16Type
case reflect.Uint32:
et = Uint32Type
case reflect.Uint64:
et = Uint64Type
case reflect.Uint8:
et = Uint8Type
case reflect.Uint:
et = UintType
case reflect.Uintptr:
et = UintptrType
case reflect.String:
et = StringType
case reflect.Array:
et = NewArrayType(int64(t.Len()), TypeFromNative(t.Elem()))
case reflect.Chan:
log.Panicf("%T not implemented", t)
case reflect.Func:
nin := t.NumIn()
// Variadic functions have DotDotDotType at the end
variadic := t.IsVariadic()
if variadic {
nin--
}
in := make([]Type, nin)
for i := range in {
in[i] = TypeFromNative(t.In(i))
}
out := make([]Type, t.NumOut())
for i := range out {
out[i] = TypeFromNative(t.Out(i))
}
et = NewFuncType(in, variadic, out)
case reflect.Interface:
log.Panicf("%T not implemented", t)
case reflect.Map:
log.Panicf("%T not implemented", t)
case reflect.Ptr:
et = NewPtrType(TypeFromNative(t.Elem()))
case reflect.Slice:
et = NewSliceType(TypeFromNative(t.Elem()))
case reflect.Struct:
n := t.NumField()
fields := make([]StructField, n)
for i := 0; i < n; i++ {
sf := t.Field(i)
// TODO(austin) What to do about private fields?
fields[i].Name = sf.Name
fields[i].Type = TypeFromNative(sf.Type)
fields[i].Anonymous = sf.Anonymous
}
et = NewStructType(fields)
case reflect.UnsafePointer:
log.Panicf("%T not implemented", t)
default:
log.Panicf("unexpected reflect.Type: %T", t)
}
if nt != nil {
if _, ok := et.(*NamedType); !ok {
nt.Complete(et)
et = nt
}
}
nativeTypes[et] = t
evalTypes[t] = et
return et
}
// TypeOfNative returns the interpreter Type of a regular Go value.
func TypeOfNative(v interface{}) Type { return TypeFromNative(reflect.TypeOf(v)) }
/*
* Function bridging
*/
type nativeFunc struct {
fn func(*Thread, []Value, []Value)
in, out int
}
func (f *nativeFunc) NewFrame() *Frame {
vars := make([]Value, f.in+f.out)
return &Frame{nil, vars}
}
func (f *nativeFunc) Call(t *Thread) { f.fn(t, t.f.Vars[0:f.in], t.f.Vars[f.in:f.in+f.out]) }
// FuncFromNative creates an interpreter function from a native
// function that takes its in and out arguments as slices of
// interpreter Value's. While somewhat inconvenient, this avoids
// value marshalling.
func FuncFromNative(fn func(*Thread, []Value, []Value), t *FuncType) FuncValue {
return &funcV{&nativeFunc{fn, len(t.In), len(t.Out)}}
}
// FuncFromNativeTyped is like FuncFromNative, but constructs the
// function type from a function pointer using reflection. Typically,
// the type will be given as a nil pointer to a function with the
// desired signature.
func FuncFromNativeTyped(fn func(*Thread, []Value, []Value), t interface{}) (*FuncType, FuncValue) {
ft := TypeOfNative(t).(*FuncType)
return ft, FuncFromNative(fn, ft)
}

View File

@ -1,92 +0,0 @@
// 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 eval
import (
"fmt"
"go/scanner"
"go/token"
)
// A compiler captures information used throughout an entire
// compilation. Currently it includes only the error handler.
//
// TODO(austin) This might actually represent package level, in which
// case it should be package compiler.
type compiler struct {
fset *token.FileSet
errors scanner.ErrorHandler
numErrors int
silentErrors int
}
func (a *compiler) diagAt(pos token.Pos, format string, args ...interface{}) {
a.errors.Error(a.fset.Position(pos), fmt.Sprintf(format, args...))
a.numErrors++
}
func (a *compiler) numError() int { return a.numErrors + a.silentErrors }
// The universal scope
func newUniverse() *Scope {
sc := &Scope{nil, 0}
sc.block = &block{
offset: 0,
scope: sc,
global: true,
defs: make(map[string]Def),
}
return sc
}
var universe *Scope = newUniverse()
// TODO(austin) These can all go in stmt.go now
type label struct {
name string
desc string
// The PC goto statements should jump to, or nil if this label
// cannot be goto'd (such as an anonymous for loop label).
gotoPC *uint
// The PC break statements should jump to, or nil if a break
// statement is invalid.
breakPC *uint
// The PC continue statements should jump to, or nil if a
// continue statement is invalid.
continuePC *uint
// The position where this label was resolved. If it has not
// been resolved yet, an invalid position.
resolved token.Pos
// The position where this label was first jumped to.
used token.Pos
}
// A funcCompiler captures information used throughout the compilation
// of a single function body.
type funcCompiler struct {
*compiler
fnType *FuncType
// Whether the out variables are named. This affects what
// kinds of return statements are legal.
outVarsNamed bool
*codeBuf
flow *flowBuf
labels map[string]*label
}
// A blockCompiler captures information used throughout the compilation
// of a single block within a function.
type blockCompiler struct {
*funcCompiler
block *block
// The label of this block, used for finding break and
// continue labels.
label *label
// The blockCompiler for the block enclosing this one, or nil
// for a function-level block.
parent *blockCompiler
}

View File

@ -1,263 +0,0 @@
// 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 eval
import (
"big"
"flag"
"fmt"
"go/token"
"log"
"os"
"reflect"
"regexp"
"testing"
)
// All tests are done using the same file set.
var fset = token.NewFileSet()
// Print each statement or expression before parsing it
var noisy = false
func init() { flag.BoolVar(&noisy, "noisy", false, "chatter during eval tests") }
/*
* Generic statement/expression test framework
*/
type test []job
type job struct {
code string
cerr string
rterr string
val Value
noval bool
}
func runTests(t *testing.T, baseName string, tests []test) {
delta := 1
if testing.Short() {
delta = 16
}
for i := 0; i < len(tests); i += delta {
name := fmt.Sprintf("%s[%d]", baseName, i)
tests[i].run(t, name)
}
}
func (a test) run(t *testing.T, name string) {
w := newTestWorld()
for _, j := range a {
src := j.code + ";" // trailing semicolon to finish statement
if noisy {
println("code:", src)
}
code, err := w.Compile(fset, src)
if err != nil {
if j.cerr == "" {
t.Errorf("%s: Compile %s: %v", name, src, err)
break
}
if !match(t, err, j.cerr) {
t.Errorf("%s: Compile %s = error %s; want %v", name, src, err, j.cerr)
break
}
continue
}
if j.cerr != "" {
t.Errorf("%s: Compile %s succeeded; want %s", name, src, j.cerr)
break
}
val, err := code.Run()
if err != nil {
if j.rterr == "" {
t.Errorf("%s: Run %s: %v", name, src, err)
break
}
if !match(t, err, j.rterr) {
t.Errorf("%s: Run %s = error %s; want %v", name, src, err, j.rterr)
break
}
continue
}
if j.rterr != "" {
t.Errorf("%s: Run %s succeeded; want %s", name, src, j.rterr)
break
}
if !j.noval && !reflect.DeepEqual(val, j.val) {
t.Errorf("%s: Run %s = %T(%v) want %T(%v)", name, src, val, val, j.val, j.val)
}
}
}
func match(t *testing.T, err os.Error, pat string) bool {
ok, err1 := regexp.MatchString(pat, err.String())
if err1 != nil {
t.Fatalf("compile regexp %s: %v", pat, err1)
}
return ok
}
/*
* Test constructors
*/
// Expression compile error
func CErr(expr string, cerr string) test { return test([]job{{code: expr, cerr: cerr}}) }
// Expression runtime error
func RErr(expr string, rterr string) test { return test([]job{{code: expr, rterr: rterr}}) }
// Expression value
func Val(expr string, val interface{}) test {
return test([]job{{code: expr, val: toValue(val)}})
}
// Statement runs without error
func Run(stmts string) test { return test([]job{{code: stmts, noval: true}}) }
// Two statements without error.
// TODO(rsc): Should be possible with Run but the parser
// won't let us do both top-level and non-top-level statements.
func Run2(stmt1, stmt2 string) test {
return test([]job{{code: stmt1, noval: true}, {code: stmt2, noval: true}})
}
// Statement runs and test one expression's value
func Val1(stmts string, expr1 string, val1 interface{}) test {
return test([]job{
{code: stmts, noval: true},
{code: expr1, val: toValue(val1)},
})
}
// Statement runs and test two expressions' values
func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test {
return test([]job{
{code: stmts, noval: true},
{code: expr1, val: toValue(val1)},
{code: expr2, val: toValue(val2)},
})
}
/*
* Value constructors
*/
type vstruct []interface{}
type varray []interface{}
type vslice struct {
arr varray
len, cap int
}
func toValue(val interface{}) Value {
switch val := val.(type) {
case bool:
r := boolV(val)
return &r
case uint8:
r := uint8V(val)
return &r
case uint:
r := uintV(val)
return &r
case int:
r := intV(val)
return &r
case *big.Int:
return &idealIntV{val}
case float64:
r := float64V(val)
return &r
case *big.Rat:
return &idealFloatV{val}
case string:
r := stringV(val)
return &r
case vstruct:
elems := make([]Value, len(val))
for i, e := range val {
elems[i] = toValue(e)
}
r := structV(elems)
return &r
case varray:
elems := make([]Value, len(val))
for i, e := range val {
elems[i] = toValue(e)
}
r := arrayV(elems)
return &r
case vslice:
return &sliceV{Slice{toValue(val.arr).(ArrayValue), int64(val.len), int64(val.cap)}}
case Func:
return &funcV{val}
}
log.Panicf("toValue(%T) not implemented", val)
panic("unreachable")
}
/*
* Default test scope
*/
type testFunc struct{}
func (*testFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} }
func (*testFunc) Call(t *Thread) {
n := t.f.Vars[0].(IntValue).Get(t)
res := n + 1
t.f.Vars[1].(IntValue).Set(t, res)
}
type oneTwoFunc struct{}
func (*oneTwoFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} }
func (*oneTwoFunc) Call(t *Thread) {
t.f.Vars[0].(IntValue).Set(t, 1)
t.f.Vars[1].(IntValue).Set(t, 2)
}
type voidFunc struct{}
func (*voidFunc) NewFrame() *Frame { return &Frame{nil, []Value{}} }
func (*voidFunc) Call(t *Thread) {}
func newTestWorld() *World {
w := NewWorld()
def := func(name string, t Type, val interface{}) { w.DefineVar(name, t, toValue(val)) }
w.DefineConst("c", IdealIntType, toValue(big.NewInt(1)))
def("i", IntType, 1)
def("i2", IntType, 2)
def("u", UintType, uint(1))
def("f", Float64Type, 1.0)
def("s", StringType, "abc")
def("t", NewStructType([]StructField{{"a", IntType, false}}), vstruct{1})
def("ai", NewArrayType(2, IntType), varray{1, 2})
def("aai", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{1, 2}, varray{3, 4}})
def("aai2", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{5, 6}, varray{7, 8}})
def("fn", NewFuncType([]Type{IntType}, false, []Type{IntType}), &testFunc{})
def("oneTwo", NewFuncType([]Type{}, false, []Type{IntType, IntType}), &oneTwoFunc{})
def("void", NewFuncType([]Type{}, false, []Type{}), &voidFunc{})
def("sli", NewSliceType(IntType), vslice{varray{1, 2, 3}, 2, 3})
return w
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,355 +0,0 @@
// 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 eval
import (
"big"
"testing"
)
var undefined = "undefined"
var typeAsExpr = "type .* used as expression"
var badCharLit = "character literal"
var unknownEscape = "unknown escape sequence"
var opTypes = "illegal (operand|argument) type|cannot index into"
var badAddrOf = "cannot take the address"
var constantTruncated = "constant [^ ]* truncated"
var constantUnderflows = "constant [^ ]* underflows"
var constantOverflows = "constant [^ ]* overflows"
var implLimit = "implementation limit"
var mustBeUnsigned = "must be unsigned"
var divByZero = "divide by zero"
var hugeInteger = new(big.Int).Lsh(idealOne, 64)
var exprTests = []test{
Val("i", 1),
CErr("zzz", undefined),
// TODO(austin) Test variable in constant context
//CErr("t", typeAsExpr),
Val("'a'", big.NewInt('a')),
Val("'\\uffff'", big.NewInt('\uffff')),
Val("'\\n'", big.NewInt('\n')),
CErr("''+x", badCharLit),
// Produces two parse errors
//CErr("'''", ""),
CErr("'\n'", badCharLit),
CErr("'\\z'", unknownEscape),
CErr("'ab'", badCharLit),
Val("1.0", big.NewRat(1, 1)),
Val("1.", big.NewRat(1, 1)),
Val(".1", big.NewRat(1, 10)),
Val("1e2", big.NewRat(100, 1)),
Val("\"abc\"", "abc"),
Val("\"\"", ""),
Val("\"\\n\\\"\"", "\n\""),
CErr("\"\\z\"", unknownEscape),
CErr("\"abc", "string not terminated"),
Val("(i)", 1),
Val("ai[0]", 1),
Val("(&ai)[0]", 1),
Val("ai[1]", 2),
Val("ai[i]", 2),
Val("ai[u]", 2),
CErr("ai[f]", opTypes),
CErr("ai[0][0]", opTypes),
CErr("ai[2]", "index 2 exceeds"),
CErr("ai[1+1]", "index 2 exceeds"),
CErr("ai[-1]", "negative index"),
RErr("ai[i+i]", "index 2 exceeds"),
RErr("ai[-i]", "negative index"),
CErr("i[0]", opTypes),
CErr("f[0]", opTypes),
Val("aai[0][0]", 1),
Val("aai[1][1]", 4),
CErr("aai[2][0]", "index 2 exceeds"),
CErr("aai[0][2]", "index 2 exceeds"),
Val("sli[0]", 1),
Val("sli[1]", 2),
CErr("sli[-1]", "negative index"),
RErr("sli[-i]", "negative index"),
RErr("sli[2]", "index 2 exceeds"),
Val("s[0]", uint8('a')),
Val("s[1]", uint8('b')),
CErr("s[-1]", "negative index"),
RErr("s[-i]", "negative index"),
RErr("s[3]", "index 3 exceeds"),
Val("ai[0:2]", vslice{varray{1, 2}, 2, 2}),
Val("ai[0:1]", vslice{varray{1, 2}, 1, 2}),
Val("ai[0:]", vslice{varray{1, 2}, 2, 2}),
Val("ai[i:]", vslice{varray{2}, 1, 1}),
Val("sli[0:2]", vslice{varray{1, 2, 3}, 2, 3}),
Val("sli[0:i]", vslice{varray{1, 2, 3}, 1, 3}),
Val("sli[1:]", vslice{varray{2, 3}, 1, 2}),
CErr("1(2)", "cannot call"),
CErr("fn(1,2)", "too many"),
CErr("fn()", "not enough"),
CErr("fn(true)", opTypes),
CErr("fn(true)", "function call"),
// Single argument functions don't say which argument.
//CErr("fn(true)", "argument 1"),
Val("fn(1)", 2),
Val("fn(1.0)", 2),
CErr("fn(1.5)", constantTruncated),
Val("fn(i)", 2),
CErr("fn(u)", opTypes),
CErr("void()+2", opTypes),
CErr("oneTwo()+2", opTypes),
Val("cap(ai)", 2),
Val("cap(&ai)", 2),
Val("cap(aai)", 2),
Val("cap(sli)", 3),
CErr("cap(0)", opTypes),
CErr("cap(i)", opTypes),
CErr("cap(s)", opTypes),
Val("len(s)", 3),
Val("len(ai)", 2),
Val("len(&ai)", 2),
Val("len(ai[0:])", 2),
Val("len(ai[1:])", 1),
Val("len(ai[2:])", 0),
Val("len(aai)", 2),
Val("len(sli)", 2),
Val("len(sli[0:])", 2),
Val("len(sli[1:])", 1),
Val("len(sli[2:])", 0),
// TODO(austin) Test len of map
CErr("len(0)", opTypes),
CErr("len(i)", opTypes),
CErr("*i", opTypes),
Val("*&i", 1),
Val("*&(i)", 1),
CErr("&1", badAddrOf),
CErr("&c", badAddrOf),
Val("*(&ai[0])", 1),
Val("+1", big.NewInt(+1)),
Val("+1.0", big.NewRat(1, 1)),
Val("01.5", big.NewRat(15, 10)),
CErr("+\"x\"", opTypes),
Val("-42", big.NewInt(-42)),
Val("-i", -1),
Val("-f", -1.0),
// 6g bug?
//Val("-(f-1)", -0.0),
CErr("-\"x\"", opTypes),
// TODO(austin) Test unary !
Val("^2", big.NewInt(^2)),
Val("^(-2)", big.NewInt(^(-2))),
CErr("^2.0", opTypes),
CErr("^2.5", opTypes),
Val("^i", ^1),
Val("^u", ^uint(1)),
CErr("^f", opTypes),
Val("1+i", 2),
Val("1+u", uint(2)),
Val("3.0+i", 4),
Val("1+1", big.NewInt(2)),
Val("f+f", 2.0),
Val("1+f", 2.0),
Val("1.0+1", big.NewRat(2, 1)),
Val("\"abc\" + \"def\"", "abcdef"),
CErr("i+u", opTypes),
CErr("-1+u", constantUnderflows),
// TODO(austin) Test named types
Val("2-1", big.NewInt(1)),
Val("2.0-1", big.NewRat(1, 1)),
Val("f-2", -1.0),
Val("-0.0", big.NewRat(0, 1)),
Val("2*2", big.NewInt(4)),
Val("2*i", 2),
Val("3/2", big.NewInt(1)),
Val("3/i", 3),
CErr("1/0", divByZero),
CErr("1.0/0", divByZero),
RErr("i/0", divByZero),
Val("3%2", big.NewInt(1)),
Val("i%2", 1),
CErr("3%0", divByZero),
CErr("3.0%0", opTypes),
RErr("i%0", divByZero),
// Examples from "Arithmetic operators"
Val("5/3", big.NewInt(1)),
Val("(i+4)/(i+2)", 1),
Val("5%3", big.NewInt(2)),
Val("(i+4)%(i+2)", 2),
Val("-5/3", big.NewInt(-1)),
Val("(i-6)/(i+2)", -1),
Val("-5%3", big.NewInt(-2)),
Val("(i-6)%(i+2)", -2),
Val("5/-3", big.NewInt(-1)),
Val("(i+4)/(i-4)", -1),
Val("5%-3", big.NewInt(2)),
Val("(i+4)%(i-4)", 2),
Val("-5/-3", big.NewInt(1)),
Val("(i-6)/(i-4)", 1),
Val("-5%-3", big.NewInt(-2)),
Val("(i-6)%(i-4)", -2),
// Examples from "Arithmetic operators"
Val("11/4", big.NewInt(2)),
Val("(i+10)/4", 2),
Val("11%4", big.NewInt(3)),
Val("(i+10)%4", 3),
Val("11>>2", big.NewInt(2)),
Val("(i+10)>>2", 2),
Val("11&3", big.NewInt(3)),
Val("(i+10)&3", 3),
Val("-11/4", big.NewInt(-2)),
Val("(i-12)/4", -2),
Val("-11%4", big.NewInt(-3)),
Val("(i-12)%4", -3),
Val("-11>>2", big.NewInt(-3)),
Val("(i-12)>>2", -3),
Val("-11&3", big.NewInt(1)),
Val("(i-12)&3", 1),
// TODO(austin) Test bit ops
// For shift, we try nearly every combination of positive
// ideal int, negative ideal int, big ideal int, ideal
// fractional float, ideal non-fractional float, int, uint,
// and float.
Val("2<<2", big.NewInt(2<<2)),
CErr("2<<(-1)", constantUnderflows),
CErr("2<<0x10000000000000000", constantOverflows),
CErr("2<<2.5", constantTruncated),
Val("2<<2.0", big.NewInt(2<<2.0)),
CErr("2<<i", mustBeUnsigned),
Val("2<<u", 2<<1),
CErr("2<<f", opTypes),
Val("-2<<2", big.NewInt(-2<<2)),
CErr("-2<<(-1)", constantUnderflows),
CErr("-2<<0x10000000000000000", constantOverflows),
CErr("-2<<2.5", constantTruncated),
Val("-2<<2.0", big.NewInt(-2<<2.0)),
CErr("-2<<i", mustBeUnsigned),
Val("-2<<u", -2<<1),
CErr("-2<<f", opTypes),
Val("0x10000000000000000<<2", new(big.Int).Lsh(hugeInteger, 2)),
CErr("0x10000000000000000<<(-1)", constantUnderflows),
CErr("0x10000000000000000<<0x10000000000000000", constantOverflows),
CErr("0x10000000000000000<<2.5", constantTruncated),
Val("0x10000000000000000<<2.0", new(big.Int).Lsh(hugeInteger, 2)),
CErr("0x10000000000000000<<i", mustBeUnsigned),
CErr("0x10000000000000000<<u", constantOverflows),
CErr("0x10000000000000000<<f", opTypes),
CErr("2.5<<2", opTypes),
CErr("2.0<<2", opTypes),
Val("i<<2", 1<<2),
CErr("i<<(-1)", constantUnderflows),
CErr("i<<0x10000000000000000", constantOverflows),
CErr("i<<2.5", constantTruncated),
Val("i<<2.0", 1<<2),
CErr("i<<i", mustBeUnsigned),
Val("i<<u", 1<<1),
CErr("i<<f", opTypes),
Val("i<<u", 1<<1),
Val("u<<2", uint(1<<2)),
CErr("u<<(-1)", constantUnderflows),
CErr("u<<0x10000000000000000", constantOverflows),
CErr("u<<2.5", constantTruncated),
Val("u<<2.0", uint(1<<2)),
CErr("u<<i", mustBeUnsigned),
Val("u<<u", uint(1<<1)),
CErr("u<<f", opTypes),
Val("u<<u", uint(1<<1)),
CErr("f<<2", opTypes),
// <, <=, >, >=
Val("1<2", 1 < 2),
Val("1<=2", 1 <= 2),
Val("2<=2", 2 <= 2),
Val("1>2", 1 > 2),
Val("1>=2", 1 >= 2),
Val("2>=2", 2 >= 2),
Val("i<2", 1 < 2),
Val("i<=2", 1 <= 2),
Val("i+1<=2", 2 <= 2),
Val("i>2", 1 > 2),
Val("i>=2", 1 >= 2),
Val("i+1>=2", 2 >= 2),
Val("u<2", 1 < 2),
Val("f<2", 1 < 2),
Val("s<\"b\"", true),
Val("s<\"a\"", false),
Val("s<=\"abc\"", true),
Val("s>\"aa\"", true),
Val("s>\"ac\"", false),
Val("s>=\"abc\"", true),
CErr("i<u", opTypes),
CErr("i<f", opTypes),
CErr("i<s", opTypes),
CErr("&i<&i", opTypes),
CErr("ai<ai", opTypes),
// ==, !=
Val("1==1", true),
Val("1!=1", false),
Val("1==2", false),
Val("1!=2", true),
Val("1.0==1", true),
Val("1.5==1", false),
Val("i==1", true),
Val("i!=1", false),
Val("i==2", false),
Val("i!=2", true),
Val("u==1", true),
Val("f==1", true),
Val("s==\"abc\"", true),
Val("s!=\"abc\"", false),
Val("s==\"abcd\"", false),
Val("s!=\"abcd\"", true),
Val("&i==&i", true),
Val("&i==&i2", false),
Val("fn==fn", true),
Val("fn==func(int)int{return 0}", false),
CErr("i==u", opTypes),
CErr("i==f", opTypes),
CErr("&i==&f", opTypes),
CErr("ai==ai", opTypes),
CErr("t==t", opTypes),
CErr("fn==oneTwo", opTypes),
}
func TestExpr(t *testing.T) { runTests(t, "exprTests", exprTests) }

View File

@ -1,70 +0,0 @@
// 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 eval
import "os"
/*
* Virtual machine
*/
type Thread struct {
abort chan os.Error
pc uint
// The execution frame of this function. This remains the
// same throughout a function invocation.
f *Frame
}
type code []func(*Thread)
func (i code) exec(t *Thread) {
opc := t.pc
t.pc = 0
l := uint(len(i))
for t.pc < l {
pc := t.pc
t.pc++
i[pc](t)
}
t.pc = opc
}
/*
* Code buffer
*/
type codeBuf struct {
instrs code
}
func newCodeBuf() *codeBuf { return &codeBuf{make(code, 0, 16)} }
func (b *codeBuf) push(instr func(*Thread)) {
b.instrs = append(b.instrs, instr)
}
func (b *codeBuf) nextPC() uint { return uint(len(b.instrs)) }
func (b *codeBuf) get() code {
// Freeze this buffer into an array of exactly the right size
a := make(code, len(b.instrs))
copy(a, b.instrs)
return code(a)
}
/*
* User-defined functions
*/
type evalFunc struct {
outer *Frame
frameSize int
code code
}
func (f *evalFunc) NewFrame() *Frame { return f.outer.child(f.frameSize) }
func (f *evalFunc) Call(t *Thread) { f.code.exec(t) }

View File

@ -1,375 +0,0 @@
// 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 main
// generate operator implementations
import (
"log"
"os"
"template"
)
type Op struct {
Name string
Expr string
Body string // overrides Expr
ConstExpr string
AsRightName string
ReturnType string
Types []*Type
}
type Size struct {
Bits int
Sized string
}
type Type struct {
Repr string
Value string
Native string
As string
IsIdeal bool
HasAssign bool
Sizes []Size
}
var (
boolType = &Type{Repr: "*boolType", Value: "BoolValue", Native: "bool", As: "asBool"}
uintType = &Type{Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint",
Sizes: []Size{{8, "uint8"}, {16, "uint16"}, {32, "uint32"}, {64, "uint64"}, {0, "uint"}},
}
intType = &Type{Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt",
Sizes: []Size{{8, "int8"}, {16, "int16"}, {32, "int32"}, {64, "int64"}, {0, "int"}},
}
idealIntType = &Type{Repr: "*idealIntType", Value: "IdealIntValue", Native: "*big.Int", As: "asIdealInt", IsIdeal: true}
floatType = &Type{Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat",
Sizes: []Size{{32, "float32"}, {64, "float64"}},
}
idealFloatType = &Type{Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*big.Rat", As: "asIdealFloat", IsIdeal: true}
stringType = &Type{Repr: "*stringType", Value: "StringValue", Native: "string", As: "asString"}
arrayType = &Type{Repr: "*ArrayType", Value: "ArrayValue", Native: "ArrayValue", As: "asArray", HasAssign: true}
structType = &Type{Repr: "*StructType", Value: "StructValue", Native: "StructValue", As: "asStruct", HasAssign: true}
ptrType = &Type{Repr: "*PtrType", Value: "PtrValue", Native: "Value", As: "asPtr"}
funcType = &Type{Repr: "*FuncType", Value: "FuncValue", Native: "Func", As: "asFunc"}
sliceType = &Type{Repr: "*SliceType", Value: "SliceValue", Native: "Slice", As: "asSlice"}
mapType = &Type{Repr: "*MapType", Value: "MapValue", Native: "Map", As: "asMap"}
all = []*Type{
boolType,
uintType,
intType,
idealIntType,
floatType,
idealFloatType,
stringType,
arrayType,
structType,
ptrType,
funcType,
sliceType,
mapType,
}
bools = all[0:1]
integers = all[1:4]
shiftable = all[1:3]
numbers = all[1:6]
addable = all[1:7]
cmpable = []*Type{
boolType,
uintType,
intType,
idealIntType,
floatType,
idealFloatType,
stringType,
ptrType,
funcType,
mapType,
}
)
var unOps = []Op{
{Name: "Neg", Expr: "-v", ConstExpr: "val.Neg(val)", Types: numbers},
{Name: "Not", Expr: "!v", Types: bools},
{Name: "Xor", Expr: "^v", ConstExpr: "val.Not(val)", Types: integers},
}
var binOps = []Op{
{Name: "Add", Expr: "l + r", ConstExpr: "l.Add(l, r)", Types: addable},
{Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(l, r)", Types: numbers},
{Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(l, r)", Types: numbers},
{Name: "Quo",
Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l / r",
ConstExpr: "l.Quo(l, r)",
Types: numbers,
},
{Name: "Rem",
Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l % r",
ConstExpr: "l.Rem(l, r)",
Types: integers,
},
{Name: "And", Expr: "l & r", ConstExpr: "l.And(l, r)", Types: integers},
{Name: "Or", Expr: "l | r", ConstExpr: "l.Or(l, r)", Types: integers},
{Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(l, r)", Types: integers},
{Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(l, r)", Types: integers},
{Name: "Shl", Expr: "l << r", ConstExpr: "l.Lsh(l, uint(r.Value()))",
AsRightName: "asUint", Types: shiftable,
},
{Name: "Shr", Expr: "l >> r", ConstExpr: "new(big.Int).Rsh(l, uint(r.Value()))",
AsRightName: "asUint", Types: shiftable,
},
{Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable},
{Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable},
{Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable},
{Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable},
{Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable},
{Name: "Neq", Expr: "l != r", ConstExpr: "l.Cmp(r) != 0", ReturnType: "bool", Types: cmpable},
}
type Data struct {
UnaryOps []Op
BinaryOps []Op
Types []*Type
}
var data = Data{
unOps,
binOps,
all,
}
const templateStr = `
// This file is machine generated by gen.go.
// 6g gen.go && 6l gen.6 && ./6.out >expr1.go
package eval
import (
"big"
"log"
)
/*
* "As" functions. These retrieve evaluator functions from an
* expr, panicking if the requested evaluator has the wrong type.
*/
«.repeated section Types»
«.section IsIdeal»
func (a *expr) «As»() (func() «Native») {
return a.eval.(func()(«Native»))
}
«.or»
func (a *expr) «As»() (func(*Thread) «Native») {
return a.eval.(func(*Thread)(«Native»))
}
«.end»
«.end»
func (a *expr) asMulti() (func(*Thread) []Value) {
return a.eval.(func(*Thread)[]Value)
}
func (a *expr) asInterface() (func(*Thread) interface{}) {
switch sf := a.eval.(type) {
«.repeated section Types»
«.section IsIdeal»
case func()«Native»:
return func(*Thread) interface{} { return sf() }
«.or»
case func(t *Thread)«Native»:
return func(t *Thread) interface{} { return sf(t) }
«.end»
«.end»
default:
log.Panicf("unexpected expression node type %T at %v", a.eval, a.pos)
}
panic("fail")
}
/*
* Operator generators.
*/
func (a *expr) genConstant(v Value) {
switch a.t.lit().(type) {
«.repeated section Types»
case «Repr»:
«.section IsIdeal»
val := v.(«Value»).Get()
a.eval = func() «Native» { return val }
«.or»
a.eval = func(t *Thread) «Native» { return v.(«Value»).Get(t) }
«.end»
«.end»
default:
log.Panicf("unexpected constant type %v at %v", a.t, a.pos)
}
}
func (a *expr) genIdentOp(level, index int) {
a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) }
switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
case «Repr»:
a.eval = func(t *Thread) «Native» { return t.f.Get(level, index).(«Value»).Get(t) }
«.end»
«.end»
default:
log.Panicf("unexpected identifier type %v at %v", a.t, a.pos)
}
}
func (a *expr) genFuncCall(call func(t *Thread) []Value) {
a.exec = func(t *Thread) { call(t)}
switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
case «Repr»:
a.eval = func(t *Thread) «Native» { return call(t)[0].(«Value»).Get(t) }
«.end»
«.end»
case *MultiType:
a.eval = func(t *Thread) []Value { return call(t) }
default:
log.Panicf("unexpected result type %v at %v", a.t, a.pos)
}
}
func (a *expr) genValue(vf func(*Thread) Value) {
a.evalAddr = vf
switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
case «Repr»:
a.eval = func(t *Thread) «Native» { return vf(t).(«Value»).Get(t) }
«.end»
«.end»
default:
log.Panicf("unexpected result type %v at %v", a.t, a.pos)
}
}
«.repeated section UnaryOps»
func (a *expr) genUnaryOp«Name»(v *expr) {
switch a.t.lit().(type) {
«.repeated section Types»
case «Repr»:
«.section IsIdeal»
val := v.«As»()()
«ConstExpr»
a.eval = func() «Native» { return val }
«.or»
vf := v.«As»()
a.eval = func(t *Thread) «Native» { v := vf(t); return «Expr» }
«.end»
«.end»
default:
log.Panicf("unexpected type %v at %v", a.t, a.pos)
}
}
«.end»
func (a *expr) genBinOpLogAnd(l, r *expr) {
lf := l.asBool()
rf := r.asBool()
a.eval = func(t *Thread) bool { return lf(t) && rf(t) }
}
func (a *expr) genBinOpLogOr(l, r *expr) {
lf := l.asBool()
rf := r.asBool()
a.eval = func(t *Thread) bool { return lf(t) || rf(t) }
}
«.repeated section BinaryOps»
func (a *expr) genBinOp«Name»(l, r *expr) {
switch t := l.t.lit().(type) {
«.repeated section Types»
case «Repr»:
«.section IsIdeal»
l := l.«As»()()
r := r.«As»()()
val := «ConstExpr»
«.section ReturnType»
a.eval = func(t *Thread) «ReturnType» { return val }
«.or»
a.eval = func() «Native» { return val }
«.end»
«.or»
lf := l.«As»()
rf := r.«.section AsRightName»«@»«.or»«As»«.end»()
«.section ReturnType»
a.eval = func(t *Thread) «@» {
l, r := lf(t), rf(t)
return «Expr»
}
«.or»
«.section Sizes»
switch t.Bits {
«.repeated section @»
case «Bits»:
a.eval = func(t *Thread) «Native» {
l, r := lf(t), rf(t)
var ret «Native»
«.section Body»
«Body»
«.or»
ret = «Expr»
«.end»
return «Native»(«Sized»(ret))
}
«.end»
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
«.or»
a.eval = func(t *Thread) «Native» {
l, r := lf(t), rf(t)
return «Expr»
}
«.end»
«.end»
«.end»
«.end»
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
«.end»
func genAssign(lt Type, r *expr) (func(lv Value, t *Thread)) {
switch lt.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
case «Repr»:
rf := r.«As»()
return func(lv Value, t *Thread) { «.section HasAssign»lv.Assign(t, rf(t))«.or»lv.(«Value»).Set(t, rf(t))«.end» }
«.end»
«.end»
default:
log.Panicf("unexpected left operand type %v at %v", lt, r.pos)
}
panic("fail")
}
`
func main() {
t := template.New(nil)
t.SetDelims("«", "»")
err := t.Parse(templateStr)
if err != nil {
log.Fatal(err)
}
err = t.Execute(os.Stdout, data)
if err != nil {
log.Fatal(err)
}
}

View File

@ -1,93 +0,0 @@
// 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 main
import (
"bufio"
"exp/eval"
"flag"
"go/parser"
"go/scanner"
"go/token"
"io/ioutil"
"os"
)
var fset = token.NewFileSet()
var filename = flag.String("f", "", "file to run")
func main() {
flag.Parse()
w := eval.NewWorld()
if *filename != "" {
data, err := ioutil.ReadFile(*filename)
if err != nil {
println(err.String())
os.Exit(1)
}
file, err := parser.ParseFile(fset, *filename, data, 0)
if err != nil {
println(err.String())
os.Exit(1)
}
code, err := w.CompileDeclList(fset, file.Decls)
if err != nil {
if list, ok := err.(scanner.ErrorList); ok {
for _, e := range list {
println(e.String())
}
} else {
println(err.String())
}
os.Exit(1)
}
_, err = code.Run()
if err != nil {
println(err.String())
os.Exit(1)
}
code, err = w.Compile(fset, "init()")
if code != nil {
_, err := code.Run()
if err != nil {
println(err.String())
os.Exit(1)
}
}
code, err = w.Compile(fset, "main()")
if err != nil {
println(err.String())
os.Exit(1)
}
_, err = code.Run()
if err != nil {
println(err.String())
os.Exit(1)
}
os.Exit(0)
}
r := bufio.NewReader(os.Stdin)
for {
print("; ")
line, err := r.ReadString('\n')
if err != nil {
break
}
code, err := w.Compile(fset, line)
if err != nil {
println(err.String())
continue
}
v, err := code.Run()
if err != nil {
println(err.String())
continue
}
if v != nil {
println(v.String())
}
}
}

View File

@ -1,207 +0,0 @@
// 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 eval
import (
"go/token"
"log"
)
/*
* Blocks and scopes
*/
// A definition can be a *Variable, *Constant, or Type.
type Def interface {
Pos() token.Pos
}
type Variable struct {
VarPos token.Pos
// Index of this variable in the Frame structure
Index int
// Static type of this variable
Type Type
// Value of this variable. This is only used by Scope.NewFrame;
// therefore, it is useful for global scopes but cannot be used
// in function scopes.
Init Value
}
func (v *Variable) Pos() token.Pos {
return v.VarPos
}
type Constant struct {
ConstPos token.Pos
Type Type
Value Value
}
func (c *Constant) Pos() token.Pos {
return c.ConstPos
}
// A block represents a definition block in which a name may not be
// defined more than once.
type block struct {
// The block enclosing this one, including blocks in other
// scopes.
outer *block
// The nested block currently being compiled, or nil.
inner *block
// The Scope containing this block.
scope *Scope
// The Variables, Constants, and Types defined in this block.
defs map[string]Def
// The index of the first variable defined in this block.
// This must be greater than the index of any variable defined
// in any parent of this block within the same Scope at the
// time this block is entered.
offset int
// The number of Variables defined in this block.
numVars int
// If global, do not allocate new vars and consts in
// the frame; assume that the refs will be compiled in
// using defs[name].Init.
global bool
}
// A Scope is the compile-time analogue of a Frame, which captures
// some subtree of blocks.
type Scope struct {
// The root block of this scope.
*block
// The maximum number of variables required at any point in
// this Scope. This determines the number of slots needed in
// Frame's created from this Scope at run-time.
maxVars int
}
func (b *block) enterChild() *block {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before entering another child")
}
sub := &block{
outer: b,
scope: b.scope,
defs: make(map[string]Def),
offset: b.offset + b.numVars,
}
b.inner = sub
return sub
}
func (b *block) exit() {
if b.outer == nil {
log.Panic("Cannot exit top-level block")
}
if b.outer.scope == b.scope {
if b.outer.inner != b {
log.Panic("Already exited block")
}
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Exit of parent block without exit of child block")
}
}
b.outer.inner = nil
}
func (b *block) ChildScope() *Scope {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before entering a child scope")
}
sub := b.enterChild()
sub.offset = 0
sub.scope = &Scope{sub, 0}
return sub.scope
}
func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) {
if prev, ok := b.defs[name]; ok {
return nil, prev
}
v := b.defineSlot(t, false)
v.VarPos = pos
b.defs[name] = v
return v, nil
}
func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) }
func (b *block) defineSlot(t Type, temp bool) *Variable {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before defining variable")
}
index := -1
if !b.global || temp {
index = b.offset + b.numVars
b.numVars++
if index >= b.scope.maxVars {
b.scope.maxVars = index + 1
}
}
v := &Variable{token.NoPos, index, t, nil}
return v
}
func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) {
if prev, ok := b.defs[name]; ok {
return nil, prev
}
c := &Constant{pos, t, v}
b.defs[name] = c
return c, nil
}
func (b *block) DefineType(name string, pos token.Pos, t Type) Type {
if _, ok := b.defs[name]; ok {
return nil
}
nt := &NamedType{pos, name, nil, true, make(map[string]Method)}
if t != nil {
nt.Complete(t)
}
b.defs[name] = nt
return nt
}
func (b *block) Lookup(name string) (bl *block, level int, def Def) {
for b != nil {
if d, ok := b.defs[name]; ok {
return b, level, d
}
if b.outer != nil && b.scope != b.outer.scope {
level++
}
b = b.outer
}
return nil, 0, nil
}
func (s *Scope) NewFrame(outer *Frame) *Frame { return outer.child(s.maxVars) }
/*
* Frames
*/
type Frame struct {
Outer *Frame
Vars []Value
}
func (f *Frame) Get(level int, index int) Value {
for ; level > 0; level-- {
f = f.Outer
}
return f.Vars[index]
}
func (f *Frame) child(numVars int) *Frame {
// TODO(austin) This is probably rather expensive. All values
// require heap allocation and zeroing them when we execute a
// definition typically requires some computation.
return &Frame{f, make([]Value, numVars)}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,343 +0,0 @@
// 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 eval
import "testing"
var atLeastOneDecl = "at least one new variable must be declared"
var stmtTests = []test{
// Short declarations
Val1("x := i", "x", 1),
Val1("x := f", "x", 1.0),
// Type defaulting
Val1("a := 42", "a", 42),
Val1("a := 1.0", "a", 1.0),
// Parallel assignment
Val2("a, b := 1, 2", "a", 1, "b", 2),
Val2("a, i := 1, 2", "a", 1, "i", 2),
CErr("a, i := 1, f", opTypes),
CErr("a, b := 1, 2, 3", "too many"),
CErr("a := 1, 2", "too many"),
CErr("a, b := 1", "not enough"),
// Mixed declarations
CErr("i := 1", atLeastOneDecl),
CErr("i, u := 1, 2", atLeastOneDecl),
Val2("i, x := 2, f", "i", 2, "x", 1.0),
// Various errors
CErr("1 := 2", "expected identifier"),
CErr("c, a := 1, 1", "cannot assign"),
// Unpacking
Val2("x, y := oneTwo()", "x", 1, "y", 2),
CErr("x := oneTwo()", "too many"),
CErr("x, y, z := oneTwo()", "not enough"),
CErr("x, y := oneTwo(), 2", "multi-valued"),
CErr("x := oneTwo()+2", opTypes),
// TOOD(austin) This error message is weird
CErr("x := void()", "not enough"),
// Placeholders
CErr("x := 1+\"x\"; i=x+1", opTypes),
// Assignment
Val1("i = 2", "i", 2),
Val1("(i) = 2", "i", 2),
CErr("1 = 2", "cannot assign"),
CErr("1-1 = 2", "- expression"),
Val1("i = 2.0", "i", 2),
CErr("i = 2.2", constantTruncated),
CErr("u = -2", constantUnderflows),
CErr("i = f", opTypes),
CErr("i, u = 0, f", opTypes),
CErr("i, u = 0, f", "value 2"),
Val2("i, i2 = i2, i", "i", 2, "i2", 1),
CErr("c = 1", "cannot assign"),
Val1("x := &i; *x = 2", "i", 2),
Val1("ai[0] = 42", "ai", varray{42, 2}),
Val1("aai[1] = ai; ai[0] = 42", "aai", varray{varray{1, 2}, varray{1, 2}}),
Val1("aai = aai2", "aai", varray{varray{5, 6}, varray{7, 8}}),
// Assignment conversions
Run("var sl []int; sl = &ai"),
CErr("type ST []int; type AT *[2]int; var x AT = &ai; var y ST = x", opTypes),
Run("type ST []int; var y ST = &ai"),
Run("type AT *[2]int; var x AT = &ai; var y []int = x"),
// Op-assignment
Val1("i += 2", "i", 3),
Val("i", 1),
Val1("f += 2", "f", 3.0),
CErr("2 += 2", "cannot assign"),
CErr("i, j += 2", "cannot be combined"),
CErr("i += 2, 3", "cannot be combined"),
Val2("s2 := s; s += \"def\"", "s2", "abc", "s", "abcdef"),
CErr("s += 1", opTypes),
// Single evaluation
Val2("ai[func()int{i+=1;return 0}()] *= 3; i2 = ai[0]", "i", 2, "i2", 3),
// Type declarations
// Identifiers
Run("type T int"),
CErr("type T x", "undefined"),
CErr("type T c", "constant"),
CErr("type T i", "variable"),
CErr("type T T", "recursive"),
CErr("type T x; type U T; var v U; v = 1", "undefined"),
// Pointer types
Run("type T *int"),
Run("type T *T"),
// Array types
Run("type T [5]int"),
Run("type T [c+42/2]int"),
Run("type T [2.0]int"),
CErr("type T [i]int", "constant expression"),
CErr("type T [2.5]int", constantTruncated),
CErr("type T [-1]int", "negative"),
CErr("type T [2]T", "recursive"),
// Struct types
Run("type T struct { a int; b int }"),
Run("type T struct { a int; int }"),
Run("type T struct { x *T }"),
Run("type T int; type U struct { T }"),
CErr("type T *int; type U struct { T }", "embedded.*pointer"),
CErr("type T *struct { T }", "embedded.*pointer"),
CErr("type T struct { a int; a int }", " a .*redeclared.*:1:17"),
CErr("type T struct { int; int }", "int .*redeclared.*:1:17"),
CErr("type T struct { int int; int }", "int .*redeclared.*:1:17"),
Run("type T struct { x *struct { T } }"),
CErr("type T struct { x struct { T } }", "recursive"),
CErr("type T struct { x }; type U struct { T }", "undefined"),
// Function types
Run("type T func()"),
Run("type T func(a, b int) int"),
Run("type T func(a, b int) (x int, y int)"),
Run("type T func(a, a int) (a int, a int)"),
Run("type T func(a, b int) (x, y int)"),
Run("type T func(int, int) (int, int)"),
CErr("type T func(x); type U T", "undefined"),
CErr("type T func(a T)", "recursive"),
// Interface types
Run("type T interface {x(a, b int) int}"),
Run("type T interface {x(a, b int) int}; type U interface {T; y(c int)}"),
CErr("type T interface {x(a int); x()}", "method x redeclared"),
CErr("type T interface {x()}; type U interface {T; x()}", "method x redeclared"),
CErr("type T int; type U interface {T}", "embedded type"),
// Parens
Run("type T (int)"),
// Variable declarations
Val2("var x int", "i", 1, "x", 0),
Val1("var x = 1", "x", 1),
Val1("var x = 1.0", "x", 1.0),
Val1("var x int = 1.0", "x", 1),
// Placeholders
CErr("var x foo; x = 1", "undefined"),
CErr("var x foo = 1; x = 1", "undefined"),
// Redeclaration
CErr("var i, x int", " i .*redeclared"),
CErr("var x int; var x int", " x .*redeclared.*:1:5"),
// Expression statements
CErr("x := func(){ 1-1 }", "expression statement"),
CErr("x := func(){ 1-1 }", "- expression"),
Val1("fn(2)", "i", 1),
// IncDec statements
Val1("i++", "i", 2),
Val1("i--", "i", 0),
Val1("u++", "u", uint(2)),
Val1("u--", "u", uint(0)),
Val1("f++", "f", 2.0),
Val1("f--", "f", 0.0),
// Single evaluation
Val2("ai[func()int{i+=1;return 0}()]++; i2 = ai[0]", "i", 2, "i2", 2),
// Operand types
CErr("s++", opTypes),
CErr("s++", "'\\+\\+'"),
CErr("2++", "cannot assign"),
CErr("c++", "cannot assign"),
// Function scoping
Val1("fn1 := func() { i=2 }; fn1()", "i", 2),
Val1("fn1 := func() { i:=2 }; fn1()", "i", 1),
Val2("fn1 := func() int { i=2; i:=3; i=4; return i }; x := fn1()", "i", 2, "x", 4),
// Basic returns
CErr("fn1 := func() int {}", "return"),
Run("fn1 := func() {}"),
CErr("fn1 := func() (r int) {}", "return"),
Val1("fn1 := func() (r int) {return}; i = fn1()", "i", 0),
Val1("fn1 := func() (r int) {r = 2; return}; i = fn1()", "i", 2),
Val1("fn1 := func() (r int) {return 2}; i = fn1()", "i", 2),
Val1("fn1 := func(int) int {return 2}; i = fn1(1)", "i", 2),
// Multi-valued returns
Val2("fn1 := func() (bool, int) {return true, 2}; x, y := fn1()", "x", true, "y", 2),
CErr("fn1 := func() int {return}", "not enough values"),
CErr("fn1 := func() int {return 1,2}", "too many values"),
CErr("fn1 := func() {return 1}", "too many values"),
CErr("fn1 := func() (int,int,int) {return 1,2}", "not enough values"),
Val2("fn1 := func() (int, int) {return oneTwo()}; x, y := fn1()", "x", 1, "y", 2),
CErr("fn1 := func() int {return oneTwo()}", "too many values"),
CErr("fn1 := func() (int,int,int) {return oneTwo()}", "not enough values"),
Val1("fn1 := func(x,y int) int {return x+y}; x := fn1(oneTwo())", "x", 3),
// Return control flow
Val2("fn1 := func(x *int) bool { *x = 2; return true; *x = 3; }; x := fn1(&i)", "i", 2, "x", true),
// Break/continue/goto/fallthrough
CErr("break", "outside"),
CErr("break foo", "break.*foo.*not defined"),
CErr("continue", "outside"),
CErr("continue foo", "continue.*foo.*not defined"),
CErr("fallthrough", "outside"),
CErr("goto foo", "foo.*not defined"),
CErr(" foo: foo:;", "foo.*redeclared.*:1:2"),
Val1("i+=2; goto L; i+=4; L: i+=8", "i", 1+2+8),
// Return checking
CErr("fn1 := func() int { goto L; return 1; L: }", "return"),
Run("fn1 := func() int { L: goto L; i = 2 }"),
Run("fn1 := func() int { return 1; L: goto L }"),
// Scope checking
Run("fn1 := func() { { L: x:=1 }; goto L }"),
CErr("fn1 := func() { { x:=1; L: }; goto L }", "into scope"),
CErr("fn1 := func() { goto L; x:=1; L: }", "into scope"),
Run("fn1 := func() { goto L; { L: x:=1 } }"),
CErr("fn1 := func() { goto L; { x:=1; L: } }", "into scope"),
// Blocks
CErr("fn1 := func() int {{}}", "return"),
Val1("fn1 := func() bool { { return true } }; b := fn1()", "b", true),
// If
Val2("if true { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4),
Val2("if false { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4),
Val2("if i == i2 { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4),
// Omit optional parts
Val2("if true { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4),
Val2("if true { i = 2 }; i2 = 4", "i", 2, "i2", 4),
Val2("if false { i = 2 }; i2 = 4", "i", 1, "i2", 4),
// Init
Val2("if x := true; x { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4),
Val2("if x := false; x { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4),
// Statement else
Val2("if true { i = 2 } else i = 3; i2 = 4", "i", 2, "i2", 4),
Val2("if false { i = 2 } else i = 3; i2 = 4", "i", 3, "i2", 4),
// Scoping
Val2("if true { i := 2 } else { i := 3 }; i2 = i", "i", 1, "i2", 1),
Val2("if false { i := 2 } else { i := 3 }; i2 = i", "i", 1, "i2", 1),
Val2("if false { i := 2 } else i := 3; i2 = i", "i", 1, "i2", 1),
CErr("if true { x := 2 }; x = 4", undefined),
Val2("if i := 2; true { i2 = i; i := 3 }", "i", 1, "i2", 2),
Val2("if i := 2; false {} else { i2 = i; i := 3 }", "i", 1, "i2", 2),
// Return checking
Run("fn1 := func() int { if true { return 1 } else { return 2 } }"),
Run("fn1 := func() int { if true { return 1 } else return 2 }"),
CErr("fn1 := func() int { if true { return 1 } else { } }", "return"),
CErr("fn1 := func() int { if true { } else { return 1 } }", "return"),
CErr("fn1 := func() int { if true { } else return 1 }", "return"),
CErr("fn1 := func() int { if true { } else { } }", "return"),
CErr("fn1 := func() int { if true { return 1 } }", "return"),
CErr("fn1 := func() int { if true { } }", "return"),
Run("fn1 := func() int { if true { }; return 1 }"),
CErr("fn1 := func() int { if true { } }", "return"),
CErr("fn1 := func() int { if true { } else { return 2 } }", "return"),
Run("fn1 := func() int { if true { return 1 }; return 0 }"),
Run("fn1 := func() int { if true { return 1 } else { }; return 0 }"),
Run("fn1 := func() int { if true { return 1 } else { }; return 0 }"),
// Switch
Val1("switch { case false: i += 2; case true: i += 4; default: i += 8 }", "i", 1+4),
Val1("switch { default: i += 2; case false: i += 4; case true: i += 8 }", "i", 1+8),
CErr("switch { default: i += 2; default: i += 4 }", "more than one"),
Val1("switch false { case false: i += 2; case true: i += 4; default: i += 8 }", "i", 1+2),
CErr("switch s { case 1: }", opTypes),
CErr("switch ai { case ai: i += 2 }", opTypes),
Val1("switch 1.0 { case 1: i += 2; case 2: i += 4 }", "i", 1+2),
Val1("switch 1.5 { case 1: i += 2; case 2: i += 4 }", "i", 1),
CErr("switch oneTwo() {}", "multi-valued expression"),
Val1("switch 2 { case 1: i += 2; fallthrough; case 2: i += 4; fallthrough; case 3: i += 8; fallthrough }", "i", 1+4+8),
Val1("switch 5 { case 1: i += 2; fallthrough; default: i += 4; fallthrough; case 2: i += 8; fallthrough; case 3: i += 16; fallthrough }", "i", 1+4+8+16),
CErr("switch { case true: fallthrough; i += 2 }", "final statement"),
Val1("switch { case true: i += 2; fallthrough; ; ; case false: i += 4 }", "i", 1+2+4),
Val1("switch 2 { case 0, 1: i += 2; case 2, 3: i += 4 }", "i", 1+4),
Val2("switch func()int{i2++;return 5}() { case 1, 2: i += 2; case 4, 5: i += 4 }", "i", 1+4, "i2", 3),
Run("switch i { case i: }"),
// TODO(austin) Why doesn't this fail?
//CErr("case 1:", "XXX"),
// For
Val2("for x := 1; x < 5; x++ { i+=x }; i2 = 4", "i", 11, "i2", 4),
Val2("for x := 1; x < 5; x++ { i+=x; break; i++ }; i2 = 4", "i", 2, "i2", 4),
Val2("for x := 1; x < 5; x++ { i+=x; continue; i++ }; i2 = 4", "i", 11, "i2", 4),
Val2("for i = 2; false; i = 3 { i = 4 }; i2 = 4", "i", 2, "i2", 4),
Val2("for i < 5 { i++ }; i2 = 4", "i", 5, "i2", 4),
Val2("for i < 0 { i++ }; i2 = 4", "i", 1, "i2", 4),
// Scoping
Val2("for i := 2; true; { i2 = i; i := 3; break }", "i", 1, "i2", 2),
// Labeled break/continue
Val1("L1: for { L2: for { i+=2; break L1; i+=4 }; i+=8 }", "i", 1+2),
Val1("L1: for { L2: for { i+=2; break L2; i+=4 }; i+=8; break; i+=16 }", "i", 1+2+8),
CErr("L1: { for { break L1 } }", "break.*not defined"),
CErr("L1: for {}; for { break L1 }", "break.*not defined"),
CErr("L1:; for { break L1 }", "break.*not defined"),
Val2("L1: for i = 0; i < 2; i++ { L2: for { i2++; continue L1; i2++ } }", "i", 2, "i2", 4),
CErr("L1: { for { continue L1 } }", "continue.*not defined"),
CErr("L1:; for { continue L1 }", "continue.*not defined"),
// Return checking
Run("fn1 := func() int{ for {} }"),
CErr("fn1 := func() int{ for true {} }", "return"),
CErr("fn1 := func() int{ for true {return 1} }", "return"),
CErr("fn1 := func() int{ for {break} }", "return"),
Run("fn1 := func() int{ for { for {break} } }"),
CErr("fn1 := func() int{ L1: for { for {break L1} } }", "return"),
Run("fn1 := func() int{ for true {}; return 1 }"),
// Selectors
Val1("var x struct { a int; b int }; x.a = 42; i = x.a", "i", 42),
Val1("type T struct { x int }; var y struct { T }; y.x = 42; i = y.x", "i", 42),
Val2("type T struct { x int }; var y struct { T; x int }; y.x = 42; i = y.x; i2 = y.T.x", "i", 42, "i2", 0),
Run("type T struct { x int }; var y struct { *T }; a := func(){i=y.x}"),
CErr("type T struct { x int }; var x T; x.y = 42", "no field"),
CErr("type T struct { x int }; type U struct { x int }; var y struct { T; U }; y.x = 42", "ambiguous.*\tT\\.x\n\tU\\.x"),
CErr("type T struct { *T }; var x T; x.foo", "no field"),
Val1("fib := func(int) int{return 0;}; fib = func(v int) int { if v < 2 { return 1 }; return fib(v-1)+fib(v-2) }; i = fib(20)", "i", 10946),
// Make slice
Val2("x := make([]int, 2); x[0] = 42; i, i2 = x[0], x[1]", "i", 42, "i2", 0),
Val2("x := make([]int, 2); x[1] = 42; i, i2 = x[0], x[1]", "i", 0, "i2", 42),
RErr("x := make([]int, 2); x[-i] = 42", "negative index"),
RErr("x := make([]int, 2); x[2] = 42", "index 2 exceeds"),
Val2("x := make([]int, 2, 3); i, i2 = len(x), cap(x)", "i", 2, "i2", 3),
Val2("x := make([]int, 3, 2); i, i2 = len(x), cap(x)", "i", 3, "i2", 3),
RErr("x := make([]int, -i)", "negative length"),
RErr("x := make([]int, 2, -i)", "negative capacity"),
RErr("x := make([]int, 2, 3); x[2] = 42", "index 2 exceeds"),
CErr("x := make([]int, 2, 3, 4)", "too many"),
CErr("x := make([]int)", "not enough"),
// TODO(austin) Test make map
// Maps
Val1("x := make(map[int] int); x[1] = 42; i = x[1]", "i", 42),
Val2("x := make(map[int] int); x[1] = 42; i, y := x[1]", "i", 42, "y", true),
Val2("x := make(map[int] int); x[1] = 42; i, y := x[2]", "i", 0, "y", false),
// Not implemented
//Val1("x := make(map[int] int); x[1] = 42, true; i = x[1]", "i", 42),
//Val2("x := make(map[int] int); x[1] = 42; x[1] = 42, false; i, y := x[1]", "i", 0, "y", false),
Run("var x int; a := make(map[int] int); a[0], x = 1, 2"),
CErr("x := make(map[int] int); (func(a,b int){})(x[0])", "not enough"),
CErr("x := make(map[int] int); x[1] = oneTwo()", "too many"),
RErr("x := make(map[int] int); i = x[1]", "key '1' not found"),
// Functions
Val2("func fib(n int) int { if n <= 2 { return n }; return fib(n-1) + fib(n-2) }", "fib(4)", 5, "fib(10)", 89),
Run("func f1(){}"),
Run2("func f1(){}", "f1()"),
}
func TestStmt(t *testing.T) { runTests(t, "stmtTests", stmtTests) }

View File

@ -1,35 +0,0 @@
#!/usr/bin/env bash
# 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.
# Run the interpreter against all the Go test programs
# that begin with the magic
# // $G $D/$F.go && $L $F.$A && ./$A.out
# line and do not contain imports.
set -e
gomake
6g main.go && 6l main.6
(
for i in $(egrep -l '// \$G (\$D/)?\$F\.go \&\& \$L \$F\.\$A && \./\$A\.out' "$GOROOT"/test/*.go "$GOROOT"/test/*/*.go)
do
if grep '^import' $i >/dev/null 2>&1
then
true
else
if "$GOROOT"/usr/austin/eval/6.out -f $i >/tmp/out 2>&1 && ! test -s /tmp/out
then
echo PASS $i
else
echo FAIL $i
(
echo '>>> ' $i
cat /tmp/out
echo
) 1>&3
fi
fi
done | (tee /dev/fd/2 | awk '{print $1}' | sort | uniq -c) 2>&1
) 3>test.log

File diff suppressed because it is too large Load Diff

View File

@ -1,409 +0,0 @@
// 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 eval
import (
"go/ast"
"go/token"
"log"
)
/*
* Type compiler
*/
type typeCompiler struct {
*compiler
block *block
// Check to be performed after a type declaration is compiled.
//
// TODO(austin) This will probably have to change after we
// eliminate forward declarations.
lateCheck func() bool
}
func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type {
_, _, def := a.block.Lookup(x.Name)
if def == nil {
a.diagAt(x.Pos(), "%s: undefined", x.Name)
return nil
}
switch def := def.(type) {
case *Constant:
a.diagAt(x.Pos(), "constant %v used as type", x.Name)
return nil
case *Variable:
a.diagAt(x.Pos(), "variable %v used as type", x.Name)
return nil
case *NamedType:
if !allowRec && def.incomplete {
a.diagAt(x.Pos(), "illegal recursive type")
return nil
}
if !def.incomplete && def.Def == nil {
// Placeholder type from an earlier error
return nil
}
return def
case Type:
return def
}
log.Panicf("name %s has unknown type %T", x.Name, def)
return nil
}
func (a *typeCompiler) compileArrayType(x *ast.ArrayType, allowRec bool) Type {
// Compile element type
elem := a.compileType(x.Elt, allowRec)
// Compile length expression
if x.Len == nil {
if elem == nil {
return nil
}
return NewSliceType(elem)
}
if _, ok := x.Len.(*ast.Ellipsis); ok {
a.diagAt(x.Len.Pos(), "... array initializers not implemented")
return nil
}
l, ok := a.compileArrayLen(a.block, x.Len)
if !ok {
return nil
}
if l < 0 {
a.diagAt(x.Len.Pos(), "array length must be non-negative")
return nil
}
if elem == nil {
return nil
}
return NewArrayType(l, elem)
}
func (a *typeCompiler) compileFields(fields *ast.FieldList, allowRec bool) ([]Type, []*ast.Ident, []token.Pos, bool) {
n := fields.NumFields()
ts := make([]Type, n)
ns := make([]*ast.Ident, n)
ps := make([]token.Pos, n)
bad := false
if fields != nil {
i := 0
for _, f := range fields.List {
t := a.compileType(f.Type, allowRec)
if t == nil {
bad = true
}
if f.Names == nil {
ns[i] = nil
ts[i] = t
ps[i] = f.Type.Pos()
i++
continue
}
for _, n := range f.Names {
ns[i] = n
ts[i] = t
ps[i] = n.Pos()
i++
}
}
}
return ts, ns, ps, bad
}
func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type {
ts, names, poss, bad := a.compileFields(x.Fields, allowRec)
// XXX(Spec) The spec claims that field identifiers must be
// unique, but 6g only checks this when they are accessed. I
// think the spec is better in this regard: if I write two
// fields with the same name in the same struct type, clearly
// that's a mistake. This definition does *not* descend into
// anonymous fields, so it doesn't matter if those change.
// There's separate language in the spec about checking
// uniqueness of field names inherited from anonymous fields
// at use time.
fields := make([]StructField, len(ts))
nameSet := make(map[string]token.Pos, len(ts))
for i := range fields {
// Compute field name and check anonymous fields
var name string
if names[i] != nil {
name = names[i].Name
} else {
if ts[i] == nil {
continue
}
var nt *NamedType
// [For anonymous fields,] the unqualified
// type name acts as the field identifier.
switch t := ts[i].(type) {
case *NamedType:
name = t.Name
nt = t
case *PtrType:
switch t := t.Elem.(type) {
case *NamedType:
name = t.Name
nt = t
}
}
// [An anonymous field] must be specified as a
// type name T or as a pointer to a type name
// *T, and T itself, may not be a pointer or
// interface type.
if nt == nil {
a.diagAt(poss[i], "embedded type must T or *T, where T is a named type")
bad = true
continue
}
// The check for embedded pointer types must
// be deferred because of things like
// type T *struct { T }
lateCheck := a.lateCheck
a.lateCheck = func() bool {
if _, ok := nt.lit().(*PtrType); ok {
a.diagAt(poss[i], "embedded type %v is a pointer type", nt)
return false
}
return lateCheck()
}
}
// Check name uniqueness
if prev, ok := nameSet[name]; ok {
a.diagAt(poss[i], "field %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev))
bad = true
continue
}
nameSet[name] = poss[i]
// Create field
fields[i].Name = name
fields[i].Type = ts[i]
fields[i].Anonymous = (names[i] == nil)
}
if bad {
return nil
}
return NewStructType(fields)
}
func (a *typeCompiler) compilePtrType(x *ast.StarExpr) Type {
elem := a.compileType(x.X, true)
if elem == nil {
return nil
}
return NewPtrType(elem)
}
func (a *typeCompiler) compileFuncType(x *ast.FuncType, allowRec bool) *FuncDecl {
// TODO(austin) Variadic function types
// The types of parameters and results must be complete.
//
// TODO(austin) It's not clear they actually have to be complete.
in, inNames, _, inBad := a.compileFields(x.Params, allowRec)
out, outNames, _, outBad := a.compileFields(x.Results, allowRec)
if inBad || outBad {
return nil
}
return &FuncDecl{NewFuncType(in, false, out), nil, inNames, outNames}
}
func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) *InterfaceType {
ts, names, poss, bad := a.compileFields(x.Methods, allowRec)
methods := make([]IMethod, len(ts))
nameSet := make(map[string]token.Pos, len(ts))
embeds := make([]*InterfaceType, len(ts))
var nm, ne int
for i := range ts {
if ts[i] == nil {
continue
}
if names[i] != nil {
name := names[i].Name
methods[nm].Name = name
methods[nm].Type = ts[i].(*FuncType)
nm++
if prev, ok := nameSet[name]; ok {
a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev))
bad = true
continue
}
nameSet[name] = poss[i]
} else {
// Embedded interface
it, ok := ts[i].lit().(*InterfaceType)
if !ok {
a.diagAt(poss[i], "embedded type must be an interface")
bad = true
continue
}
embeds[ne] = it
ne++
for _, m := range it.methods {
if prev, ok := nameSet[m.Name]; ok {
a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", m.Name, a.fset.Position(prev))
bad = true
continue
}
nameSet[m.Name] = poss[i]
}
}
}
if bad {
return nil
}
methods = methods[0:nm]
embeds = embeds[0:ne]
return NewInterfaceType(methods, embeds)
}
func (a *typeCompiler) compileMapType(x *ast.MapType) Type {
key := a.compileType(x.Key, true)
val := a.compileType(x.Value, true)
if key == nil || val == nil {
return nil
}
// XXX(Spec) The Map types section explicitly lists all types
// that can be map keys except for function types.
switch key.lit().(type) {
case *StructType:
a.diagAt(x.Pos(), "map key cannot be a struct type")
return nil
case *ArrayType:
a.diagAt(x.Pos(), "map key cannot be an array type")
return nil
case *SliceType:
a.diagAt(x.Pos(), "map key cannot be a slice type")
return nil
}
return NewMapType(key, val)
}
func (a *typeCompiler) compileType(x ast.Expr, allowRec bool) Type {
switch x := x.(type) {
case *ast.BadExpr:
// Error already reported by parser
a.silentErrors++
return nil
case *ast.Ident:
return a.compileIdent(x, allowRec)
case *ast.ArrayType:
return a.compileArrayType(x, allowRec)
case *ast.StructType:
return a.compileStructType(x, allowRec)
case *ast.StarExpr:
return a.compilePtrType(x)
case *ast.FuncType:
fd := a.compileFuncType(x, allowRec)
if fd == nil {
return nil
}
return fd.Type
case *ast.InterfaceType:
return a.compileInterfaceType(x, allowRec)
case *ast.MapType:
return a.compileMapType(x)
case *ast.ChanType:
goto notimpl
case *ast.ParenExpr:
return a.compileType(x.X, allowRec)
case *ast.Ellipsis:
a.diagAt(x.Pos(), "illegal use of ellipsis")
return nil
}
a.diagAt(x.Pos(), "expression used as type")
return nil
notimpl:
a.diagAt(x.Pos(), "compileType: %T not implemented", x)
return nil
}
/*
* Type compiler interface
*/
func noLateCheck() bool { return true }
func (a *compiler) compileType(b *block, typ ast.Expr) Type {
tc := &typeCompiler{a, b, noLateCheck}
t := tc.compileType(typ, false)
if !tc.lateCheck() {
t = nil
}
return t
}
func (a *compiler) compileTypeDecl(b *block, decl *ast.GenDecl) bool {
ok := true
for _, spec := range decl.Specs {
spec := spec.(*ast.TypeSpec)
// Create incomplete type for this type
nt := b.DefineType(spec.Name.Name, spec.Name.Pos(), nil)
if nt != nil {
nt.(*NamedType).incomplete = true
}
// Compile type
tc := &typeCompiler{a, b, noLateCheck}
t := tc.compileType(spec.Type, false)
if t == nil {
// Create a placeholder type
ok = false
}
// Fill incomplete type
if nt != nil {
nt.(*NamedType).Complete(t)
}
// Perform late type checking with complete type
if !tc.lateCheck() {
ok = false
if nt != nil {
// Make the type a placeholder
nt.(*NamedType).Def = nil
}
}
}
return ok
}
func (a *compiler) compileFuncType(b *block, typ *ast.FuncType) *FuncDecl {
tc := &typeCompiler{a, b, noLateCheck}
res := tc.compileFuncType(typ, false)
if res != nil {
if !tc.lateCheck() {
res = nil
}
}
return res
}

View File

@ -1,586 +0,0 @@
// 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 eval
import (
"big"
"fmt"
)
type Value interface {
String() string
// Assign copies another value into this one. It should
// assume that the other value satisfies the same specific
// value interface (BoolValue, etc.), but must not assume
// anything about its specific type.
Assign(t *Thread, o Value)
}
type BoolValue interface {
Value
Get(*Thread) bool
Set(*Thread, bool)
}
type UintValue interface {
Value
Get(*Thread) uint64
Set(*Thread, uint64)
}
type IntValue interface {
Value
Get(*Thread) int64
Set(*Thread, int64)
}
// TODO(austin) IdealIntValue and IdealFloatValue should not exist
// because ideals are not l-values.
type IdealIntValue interface {
Value
Get() *big.Int
}
type FloatValue interface {
Value
Get(*Thread) float64
Set(*Thread, float64)
}
type IdealFloatValue interface {
Value
Get() *big.Rat
}
type StringValue interface {
Value
Get(*Thread) string
Set(*Thread, string)
}
type ArrayValue interface {
Value
// TODO(austin) Get() is here for uniformity, but is
// completely useless. If a lot of other types have similarly
// useless Get methods, just special-case these uses.
Get(*Thread) ArrayValue
Elem(*Thread, int64) Value
// Sub returns an ArrayValue backed by the same array that
// starts from element i and has length len.
Sub(i int64, len int64) ArrayValue
}
type StructValue interface {
Value
// TODO(austin) This is another useless Get()
Get(*Thread) StructValue
Field(*Thread, int) Value
}
type PtrValue interface {
Value
Get(*Thread) Value
Set(*Thread, Value)
}
type Func interface {
NewFrame() *Frame
Call(*Thread)
}
type FuncValue interface {
Value
Get(*Thread) Func
Set(*Thread, Func)
}
type Interface struct {
Type Type
Value Value
}
type InterfaceValue interface {
Value
Get(*Thread) Interface
Set(*Thread, Interface)
}
type Slice struct {
Base ArrayValue
Len, Cap int64
}
type SliceValue interface {
Value
Get(*Thread) Slice
Set(*Thread, Slice)
}
type Map interface {
Len(*Thread) int64
// Retrieve an element from the map, returning nil if it does
// not exist.
Elem(t *Thread, key interface{}) Value
// Set an entry in the map. If val is nil, delete the entry.
SetElem(t *Thread, key interface{}, val Value)
// TODO(austin) Perhaps there should be an iterator interface instead.
Iter(func(key interface{}, val Value) bool)
}
type MapValue interface {
Value
Get(*Thread) Map
Set(*Thread, Map)
}
/*
* Bool
*/
type boolV bool
func (v *boolV) String() string { return fmt.Sprint(*v) }
func (v *boolV) Assign(t *Thread, o Value) { *v = boolV(o.(BoolValue).Get(t)) }
func (v *boolV) Get(*Thread) bool { return bool(*v) }
func (v *boolV) Set(t *Thread, x bool) { *v = boolV(x) }
/*
* Uint
*/
type uint8V uint8
func (v *uint8V) String() string { return fmt.Sprint(*v) }
func (v *uint8V) Assign(t *Thread, o Value) { *v = uint8V(o.(UintValue).Get(t)) }
func (v *uint8V) Get(*Thread) uint64 { return uint64(*v) }
func (v *uint8V) Set(t *Thread, x uint64) { *v = uint8V(x) }
type uint16V uint16
func (v *uint16V) String() string { return fmt.Sprint(*v) }
func (v *uint16V) Assign(t *Thread, o Value) { *v = uint16V(o.(UintValue).Get(t)) }
func (v *uint16V) Get(*Thread) uint64 { return uint64(*v) }
func (v *uint16V) Set(t *Thread, x uint64) { *v = uint16V(x) }
type uint32V uint32
func (v *uint32V) String() string { return fmt.Sprint(*v) }
func (v *uint32V) Assign(t *Thread, o Value) { *v = uint32V(o.(UintValue).Get(t)) }
func (v *uint32V) Get(*Thread) uint64 { return uint64(*v) }
func (v *uint32V) Set(t *Thread, x uint64) { *v = uint32V(x) }
type uint64V uint64
func (v *uint64V) String() string { return fmt.Sprint(*v) }
func (v *uint64V) Assign(t *Thread, o Value) { *v = uint64V(o.(UintValue).Get(t)) }
func (v *uint64V) Get(*Thread) uint64 { return uint64(*v) }
func (v *uint64V) Set(t *Thread, x uint64) { *v = uint64V(x) }
type uintV uint
func (v *uintV) String() string { return fmt.Sprint(*v) }
func (v *uintV) Assign(t *Thread, o Value) { *v = uintV(o.(UintValue).Get(t)) }
func (v *uintV) Get(*Thread) uint64 { return uint64(*v) }
func (v *uintV) Set(t *Thread, x uint64) { *v = uintV(x) }
type uintptrV uintptr
func (v *uintptrV) String() string { return fmt.Sprint(*v) }
func (v *uintptrV) Assign(t *Thread, o Value) { *v = uintptrV(o.(UintValue).Get(t)) }
func (v *uintptrV) Get(*Thread) uint64 { return uint64(*v) }
func (v *uintptrV) Set(t *Thread, x uint64) { *v = uintptrV(x) }
/*
* Int
*/
type int8V int8
func (v *int8V) String() string { return fmt.Sprint(*v) }
func (v *int8V) Assign(t *Thread, o Value) { *v = int8V(o.(IntValue).Get(t)) }
func (v *int8V) Get(*Thread) int64 { return int64(*v) }
func (v *int8V) Set(t *Thread, x int64) { *v = int8V(x) }
type int16V int16
func (v *int16V) String() string { return fmt.Sprint(*v) }
func (v *int16V) Assign(t *Thread, o Value) { *v = int16V(o.(IntValue).Get(t)) }
func (v *int16V) Get(*Thread) int64 { return int64(*v) }
func (v *int16V) Set(t *Thread, x int64) { *v = int16V(x) }
type int32V int32
func (v *int32V) String() string { return fmt.Sprint(*v) }
func (v *int32V) Assign(t *Thread, o Value) { *v = int32V(o.(IntValue).Get(t)) }
func (v *int32V) Get(*Thread) int64 { return int64(*v) }
func (v *int32V) Set(t *Thread, x int64) { *v = int32V(x) }
type int64V int64
func (v *int64V) String() string { return fmt.Sprint(*v) }
func (v *int64V) Assign(t *Thread, o Value) { *v = int64V(o.(IntValue).Get(t)) }
func (v *int64V) Get(*Thread) int64 { return int64(*v) }
func (v *int64V) Set(t *Thread, x int64) { *v = int64V(x) }
type intV int
func (v *intV) String() string { return fmt.Sprint(*v) }
func (v *intV) Assign(t *Thread, o Value) { *v = intV(o.(IntValue).Get(t)) }
func (v *intV) Get(*Thread) int64 { return int64(*v) }
func (v *intV) Set(t *Thread, x int64) { *v = intV(x) }
/*
* Ideal int
*/
type idealIntV struct {
V *big.Int
}
func (v *idealIntV) String() string { return v.V.String() }
func (v *idealIntV) Assign(t *Thread, o Value) {
v.V = o.(IdealIntValue).Get()
}
func (v *idealIntV) Get() *big.Int { return v.V }
/*
* Float
*/
type float32V float32
func (v *float32V) String() string { return fmt.Sprint(*v) }
func (v *float32V) Assign(t *Thread, o Value) { *v = float32V(o.(FloatValue).Get(t)) }
func (v *float32V) Get(*Thread) float64 { return float64(*v) }
func (v *float32V) Set(t *Thread, x float64) { *v = float32V(x) }
type float64V float64
func (v *float64V) String() string { return fmt.Sprint(*v) }
func (v *float64V) Assign(t *Thread, o Value) { *v = float64V(o.(FloatValue).Get(t)) }
func (v *float64V) Get(*Thread) float64 { return float64(*v) }
func (v *float64V) Set(t *Thread, x float64) { *v = float64V(x) }
/*
* Ideal float
*/
type idealFloatV struct {
V *big.Rat
}
func (v *idealFloatV) String() string { return v.V.FloatString(6) }
func (v *idealFloatV) Assign(t *Thread, o Value) {
v.V = o.(IdealFloatValue).Get()
}
func (v *idealFloatV) Get() *big.Rat { return v.V }
/*
* String
*/
type stringV string
func (v *stringV) String() string { return fmt.Sprint(*v) }
func (v *stringV) Assign(t *Thread, o Value) { *v = stringV(o.(StringValue).Get(t)) }
func (v *stringV) Get(*Thread) string { return string(*v) }
func (v *stringV) Set(t *Thread, x string) { *v = stringV(x) }
/*
* Array
*/
type arrayV []Value
func (v *arrayV) String() string {
res := "{"
for i, e := range *v {
if i > 0 {
res += ", "
}
res += e.String()
}
return res + "}"
}
func (v *arrayV) Assign(t *Thread, o Value) {
oa := o.(ArrayValue)
l := int64(len(*v))
for i := int64(0); i < l; i++ {
(*v)[i].Assign(t, oa.Elem(t, i))
}
}
func (v *arrayV) Get(*Thread) ArrayValue { return v }
func (v *arrayV) Elem(t *Thread, i int64) Value {
return (*v)[i]
}
func (v *arrayV) Sub(i int64, len int64) ArrayValue {
res := (*v)[i : i+len]
return &res
}
/*
* Struct
*/
type structV []Value
// TODO(austin) Should these methods (and arrayV's) be on structV
// instead of *structV?
func (v *structV) String() string {
res := "{"
for i, v := range *v {
if i > 0 {
res += ", "
}
res += v.String()
}
return res + "}"
}
func (v *structV) Assign(t *Thread, o Value) {
oa := o.(StructValue)
l := len(*v)
for i := 0; i < l; i++ {
(*v)[i].Assign(t, oa.Field(t, i))
}
}
func (v *structV) Get(*Thread) StructValue { return v }
func (v *structV) Field(t *Thread, i int) Value {
return (*v)[i]
}
/*
* Pointer
*/
type ptrV struct {
// nil if the pointer is nil
target Value
}
func (v *ptrV) String() string {
if v.target == nil {
return "<nil>"
}
return "&" + v.target.String()
}
func (v *ptrV) Assign(t *Thread, o Value) { v.target = o.(PtrValue).Get(t) }
func (v *ptrV) Get(*Thread) Value { return v.target }
func (v *ptrV) Set(t *Thread, x Value) { v.target = x }
/*
* Functions
*/
type funcV struct {
target Func
}
func (v *funcV) String() string {
// TODO(austin) Rob wants to see the definition
return "func {...}"
}
func (v *funcV) Assign(t *Thread, o Value) { v.target = o.(FuncValue).Get(t) }
func (v *funcV) Get(*Thread) Func { return v.target }
func (v *funcV) Set(t *Thread, x Func) { v.target = x }
/*
* Interfaces
*/
type interfaceV struct {
Interface
}
func (v *interfaceV) String() string {
if v.Type == nil || v.Value == nil {
return "<nil>"
}
return v.Value.String()
}
func (v *interfaceV) Assign(t *Thread, o Value) {
v.Interface = o.(InterfaceValue).Get(t)
}
func (v *interfaceV) Get(*Thread) Interface { return v.Interface }
func (v *interfaceV) Set(t *Thread, x Interface) {
v.Interface = x
}
/*
* Slices
*/
type sliceV struct {
Slice
}
func (v *sliceV) String() string {
if v.Base == nil {
return "<nil>"
}
return v.Base.Sub(0, v.Len).String()
}
func (v *sliceV) Assign(t *Thread, o Value) { v.Slice = o.(SliceValue).Get(t) }
func (v *sliceV) Get(*Thread) Slice { return v.Slice }
func (v *sliceV) Set(t *Thread, x Slice) { v.Slice = x }
/*
* Maps
*/
type mapV struct {
target Map
}
func (v *mapV) String() string {
if v.target == nil {
return "<nil>"
}
res := "map["
i := 0
v.target.Iter(func(key interface{}, val Value) bool {
if i > 0 {
res += ", "
}
i++
res += fmt.Sprint(key) + ":" + val.String()
return true
})
return res + "]"
}
func (v *mapV) Assign(t *Thread, o Value) { v.target = o.(MapValue).Get(t) }
func (v *mapV) Get(*Thread) Map { return v.target }
func (v *mapV) Set(t *Thread, x Map) { v.target = x }
type evalMap map[interface{}]Value
func (m evalMap) Len(t *Thread) int64 { return int64(len(m)) }
func (m evalMap) Elem(t *Thread, key interface{}) Value {
return m[key]
}
func (m evalMap) SetElem(t *Thread, key interface{}, val Value) {
if val == nil {
m[key] = nil, false
} else {
m[key] = val
}
}
func (m evalMap) Iter(cb func(key interface{}, val Value) bool) {
for k, v := range m {
if !cb(k, v) {
break
}
}
}
/*
* Multi-values
*/
type multiV []Value
func (v multiV) String() string {
res := "("
for i, v := range v {
if i > 0 {
res += ", "
}
res += v.String()
}
return res + ")"
}
func (v multiV) Assign(t *Thread, o Value) {
omv := o.(multiV)
for i := range v {
v[i].Assign(t, omv[i])
}
}
/*
* Universal constants
*/
func init() {
s := universe
true := boolV(true)
s.DefineConst("true", universePos, BoolType, &true)
false := boolV(false)
s.DefineConst("false", universePos, BoolType, &false)
}

View File

@ -1,188 +0,0 @@
// 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 eval is the beginning of an interpreter for Go.
// It can run simple Go programs but does not implement
// interface values or packages.
package eval
import (
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"os"
)
type World struct {
scope *Scope
frame *Frame
}
func NewWorld() *World {
w := new(World)
w.scope = universe.ChildScope()
w.scope.global = true // this block's vars allocate directly
return w
}
type Code interface {
// The type of the value Run returns, or nil if Run returns nil.
Type() Type
// Run runs the code; if the code is a single expression
// with a value, it returns the value; otherwise it returns nil.
Run() (Value, os.Error)
}
type stmtCode struct {
w *World
code code
}
func (w *World) CompileStmtList(fset *token.FileSet, stmts []ast.Stmt) (Code, os.Error) {
if len(stmts) == 1 {
if s, ok := stmts[0].(*ast.ExprStmt); ok {
return w.CompileExpr(fset, s.X)
}
}
errors := new(scanner.ErrorVector)
cc := &compiler{fset, errors, 0, 0}
cb := newCodeBuf()
fc := &funcCompiler{
compiler: cc,
fnType: nil,
outVarsNamed: false,
codeBuf: cb,
flow: newFlowBuf(cb),
labels: make(map[string]*label),
}
bc := &blockCompiler{
funcCompiler: fc,
block: w.scope.block,
}
nerr := cc.numError()
for _, stmt := range stmts {
bc.compileStmt(stmt)
}
fc.checkLabels()
if nerr != cc.numError() {
return nil, errors.GetError(scanner.Sorted)
}
return &stmtCode{w, fc.get()}, nil
}
func (w *World) CompileDeclList(fset *token.FileSet, decls []ast.Decl) (Code, os.Error) {
stmts := make([]ast.Stmt, len(decls))
for i, d := range decls {
stmts[i] = &ast.DeclStmt{d}
}
return w.CompileStmtList(fset, stmts)
}
func (s *stmtCode) Type() Type { return nil }
func (s *stmtCode) Run() (Value, os.Error) {
t := new(Thread)
t.f = s.w.scope.NewFrame(nil)
return nil, t.Try(func(t *Thread) { s.code.exec(t) })
}
type exprCode struct {
w *World
e *expr
eval func(Value, *Thread)
}
func (w *World) CompileExpr(fset *token.FileSet, e ast.Expr) (Code, os.Error) {
errors := new(scanner.ErrorVector)
cc := &compiler{fset, errors, 0, 0}
ec := cc.compileExpr(w.scope.block, false, e)
if ec == nil {
return nil, errors.GetError(scanner.Sorted)
}
var eval func(Value, *Thread)
switch t := ec.t.(type) {
case *idealIntType:
// nothing
case *idealFloatType:
// nothing
default:
if tm, ok := t.(*MultiType); ok && len(tm.Elems) == 0 {
return &stmtCode{w, code{ec.exec}}, nil
}
eval = genAssign(ec.t, ec)
}
return &exprCode{w, ec, eval}, nil
}
func (e *exprCode) Type() Type { return e.e.t }
func (e *exprCode) Run() (Value, os.Error) {
t := new(Thread)
t.f = e.w.scope.NewFrame(nil)
switch e.e.t.(type) {
case *idealIntType:
return &idealIntV{e.e.asIdealInt()()}, nil
case *idealFloatType:
return &idealFloatV{e.e.asIdealFloat()()}, nil
}
v := e.e.t.Zero()
eval := e.eval
err := t.Try(func(t *Thread) { eval(v, t) })
return v, err
}
func (w *World) Compile(fset *token.FileSet, text string) (Code, os.Error) {
stmts, err := parser.ParseStmtList(fset, "input", text)
if err == nil {
return w.CompileStmtList(fset, stmts)
}
// Otherwise try as DeclList.
decls, err1 := parser.ParseDeclList(fset, "input", text)
if err1 == nil {
return w.CompileDeclList(fset, decls)
}
// Have to pick an error.
// Parsing as statement list admits more forms,
// its error is more likely to be useful.
return nil, err
}
type RedefinitionError struct {
Name string
Prev Def
}
func (e *RedefinitionError) String() string {
res := "identifier " + e.Name + " redeclared"
pos := e.Prev.Pos()
if pos.IsValid() {
// TODO: fix this - currently this code is not reached by the tests
// need to get a file set (fset) from somewhere
//res += "; previous declaration at " + fset.Position(pos).String()
panic(0)
}
return res
}
func (w *World) DefineConst(name string, t Type, val Value) os.Error {
_, prev := w.scope.DefineConst(name, token.NoPos, t, val)
if prev != nil {
return &RedefinitionError{name, prev}
}
return nil
}
func (w *World) DefineVar(name string, t Type, val Value) os.Error {
v, prev := w.scope.DefineVar(name, token.NoPos, t)
if prev != nil {
return &RedefinitionError{name, prev}
}
v.Init = val
return nil
}

View File

@ -1,29 +0,0 @@
# 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.
include ../../../Make.inc
TARG=exp/ogle
GOFILES=\
abort.go\
arch.go\
cmd.go\
event.go\
frame.go\
goroutine.go\
rruntime.go\
rtype.go\
rvalue.go\
process.go\
vars.go\
CLEANFILES+=ogle
include ../../../Make.pkg
main.$O: main.go package
$(GC) -I_obj $<
ogle: main.$O
$(LD) -L_obj -o $@ $<

View File

@ -1,35 +0,0 @@
// 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 (
"os"
"runtime"
)
// An aborter aborts the thread's current computation, usually
// passing the error to a waiting thread.
type aborter interface {
Abort(err os.Error)
}
type ogleAborter chan os.Error
func (a ogleAborter) Abort(err os.Error) {
a <- err
runtime.Goexit()
}
// try executes a computation; if the computation Aborts, try returns
// the error passed to abort.
func try(f func(a aborter)) os.Error {
a := make(ogleAborter)
go func() {
f(a)
a <- nil
}()
err := <-a
return err
}

View File

@ -1,125 +0,0 @@
// 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"
"math"
)
type Arch interface {
// ToWord converts an array of up to 8 bytes in memory order
// to a word.
ToWord(data []byte) proc.Word
// FromWord converts a word to an array of up to 8 bytes in
// memory order.
FromWord(v proc.Word, out []byte)
// ToFloat32 converts a word to a float. The order of this
// word will be the order returned by ToWord on the memory
// representation of a float, and thus may require reversing.
ToFloat32(bits uint32) float32
// FromFloat32 converts a float to a word. This should return
// a word that can be passed to FromWord to get the memory
// representation of a float on this architecture.
FromFloat32(f float32) uint32
// ToFloat64 is to float64 as ToFloat32 is to float32.
ToFloat64(bits uint64) float64
// FromFloat64 is to float64 as FromFloat32 is to float32.
FromFloat64(f float64) uint64
// IntSize returns the number of bytes in an 'int'.
IntSize() int
// PtrSize returns the number of bytes in a 'uintptr'.
PtrSize() int
// FloatSize returns the number of bytes in a 'float'.
FloatSize() int
// Align rounds offset up to the appropriate offset for a
// basic type with the given width.
Align(offset, width int) int
// G returns the current G pointer.
G(regs proc.Regs) proc.Word
// ClosureSize returns the number of bytes expected by
// ParseClosure.
ClosureSize() int
// ParseClosure takes ClosureSize bytes read from a return PC
// in a remote process, determines if the code is a closure,
// and returns the frame size of the closure if it is.
ParseClosure(data []byte) (frame int, ok bool)
}
type ArchLSB struct{}
func (ArchLSB) ToWord(data []byte) proc.Word {
var v proc.Word
for i, b := range data {
v |= proc.Word(b) << (uint(i) * 8)
}
return v
}
func (ArchLSB) FromWord(v proc.Word, out []byte) {
for i := range out {
out[i] = byte(v)
v >>= 8
}
}
func (ArchLSB) ToFloat32(bits uint32) float32 {
// TODO(austin) Do these definitions depend on my current
// architecture?
return math.Float32frombits(bits)
}
func (ArchLSB) FromFloat32(f float32) uint32 { return math.Float32bits(f) }
func (ArchLSB) ToFloat64(bits uint64) float64 { return math.Float64frombits(bits) }
func (ArchLSB) FromFloat64(f float64) uint64 { return math.Float64bits(f) }
type ArchAlignedMultiple struct{}
func (ArchAlignedMultiple) Align(offset, width int) int {
return ((offset - 1) | (width - 1)) + 1
}
type amd64 struct {
ArchLSB
ArchAlignedMultiple
gReg int
}
func (a *amd64) IntSize() int { return 4 }
func (a *amd64) PtrSize() int { return 8 }
func (a *amd64) FloatSize() int { return 4 }
func (a *amd64) G(regs proc.Regs) proc.Word {
// See src/pkg/runtime/mkasmh
if a.gReg == -1 {
ns := regs.Names()
for i, n := range ns {
if n == "r15" {
a.gReg = i
break
}
}
}
return regs.Get(a.gReg)
}
func (a *amd64) ClosureSize() int { return 8 }
func (a *amd64) ParseClosure(data []byte) (int, bool) {
if data[0] == 0x48 && data[1] == 0x81 && data[2] == 0xc4 && data[7] == 0xc3 {
return int(a.ToWord(data[3:7]) + 8), true
}
return 0, false
}
var Amd64 = &amd64{gReg: -1}

View File

@ -1,373 +0,0 @@
// 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 is the beginning of a debugger for Go.
package ogle
import (
"bufio"
"debug/elf"
"debug/proc"
"exp/eval"
"fmt"
"go/scanner"
"go/token"
"os"
"strconv"
"strings"
)
var fset = token.NewFileSet()
var world *eval.World
var curProc *Process
func Main() {
world = eval.NewWorld()
defineFuncs()
r := bufio.NewReader(os.Stdin)
for {
print("; ")
line, err := r.ReadSlice('\n')
if err != nil {
break
}
// Try line as a command
cmd, rest := getCmd(line)
if cmd != nil {
err := cmd.handler(rest)
if err != nil {
scanner.PrintError(os.Stderr, err)
}
continue
}
// Try line as code
code, err := world.Compile(fset, string(line))
if err != nil {
scanner.PrintError(os.Stderr, err)
continue
}
v, err := code.Run()
if err != nil {
fmt.Fprintf(os.Stderr, err.String())
continue
}
if v != nil {
println(v.String())
}
}
}
// newScanner creates a new scanner that scans that given input bytes.
func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) {
sc := new(scanner.Scanner)
ev := new(scanner.ErrorVector)
file := fset.AddFile("input", fset.Base(), len(input))
sc.Init(file, input, ev, 0)
return sc, ev
}
/*
* Commands
*/
// A UsageError occurs when a command is called with illegal arguments.
type UsageError string
func (e UsageError) String() string { return string(e) }
// A cmd represents a single command with a handler.
type cmd struct {
cmd string
handler func([]byte) os.Error
}
var cmds = []cmd{
{"load", cmdLoad},
{"bt", cmdBt},
}
// getCmd attempts to parse an input line as a registered command. If
// successful, it returns the command and the bytes remaining after
// the command, which should be passed to the command.
func getCmd(line []byte) (*cmd, []byte) {
sc, _ := newScanner(line)
pos, tok, lit := sc.Scan()
if sc.ErrorCount != 0 || tok != token.IDENT {
return nil, nil
}
slit := string(lit)
for i := range cmds {
if cmds[i].cmd == slit {
return &cmds[i], line[fset.Position(pos).Offset+len(lit):]
}
}
return nil, nil
}
// cmdLoad starts or attaches to a process. Its form is similar to
// import:
//
// load [sym] "path" [;]
//
// sym specifies the name to give to the process. If not given, the
// name is derived from the path of the process. If ".", then the
// packages from the remote process are defined into the current
// namespace. If given, this symbol is defined as a package
// containing the process' packages.
//
// path gives the path of the process to start or attach to. If it is
// "pid:<num>", then attach to the given PID. Otherwise, treat it as
// a file path and space-separated arguments and start a new process.
//
// load always sets the current process to the loaded process.
func cmdLoad(args []byte) os.Error {
ident, path, err := parseLoad(args)
if err != nil {
return err
}
if curProc != nil {
return UsageError("multiple processes not implemented")
}
if ident != "." {
return UsageError("process identifiers not implemented")
}
// Parse argument and start or attach to process
var fname string
var tproc proc.Process
if len(path) >= 4 && path[0:4] == "pid:" {
pid, err := strconv.Atoi(path[4:])
if err != nil {
return err
}
fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
if err != nil {
return err
}
tproc, err = proc.Attach(pid)
if err != nil {
return err
}
println("Attached to", pid)
} else {
parts := strings.Split(path, " ")
if len(parts) == 0 {
fname = ""
} else {
fname = parts[0]
}
tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
if err != nil {
return err
}
println("Started", path)
// TODO(austin) If we fail after this point, kill tproc
// before detaching.
}
// Get symbols
f, err := os.Open(fname)
if err != nil {
tproc.Detach()
return err
}
defer f.Close()
elf, err := elf.NewFile(f)
if err != nil {
tproc.Detach()
return err
}
curProc, err = NewProcessElf(tproc, elf)
if err != nil {
tproc.Detach()
return err
}
// Prepare new process
curProc.OnGoroutineCreate().AddHandler(EventPrint)
curProc.OnGoroutineExit().AddHandler(EventPrint)
err = curProc.populateWorld(world)
if err != nil {
tproc.Detach()
return err
}
return nil
}
func parseLoad(args []byte) (ident string, path string, err os.Error) {
err = UsageError("Usage: load [sym] \"path\"")
sc, ev := newScanner(args)
var toks [4]token.Token
var lits [4]string
for i := range toks {
_, toks[i], lits[i] = sc.Scan()
}
if sc.ErrorCount != 0 {
err = ev.GetError(scanner.NoMultiples)
return
}
i := 0
switch toks[i] {
case token.PERIOD, token.IDENT:
ident = string(lits[i])
i++
}
if toks[i] != token.STRING {
return
}
path, uerr := strconv.Unquote(string(lits[i]))
if uerr != nil {
err = uerr
return
}
i++
if toks[i] == token.SEMICOLON {
i++
}
if toks[i] != token.EOF {
return
}
return ident, path, nil
}
// cmdBt prints a backtrace for the current goroutine. It takes no
// arguments.
func cmdBt(args []byte) os.Error {
err := parseNoArgs(args, "Usage: bt")
if err != nil {
return err
}
if curProc == nil || curProc.curGoroutine == nil {
return NoCurrentGoroutine{}
}
f := curProc.curGoroutine.frame
if f == nil {
fmt.Println("No frames on stack")
return nil
}
for f.Inner() != nil {
f = f.Inner()
}
for i := 0; i < 100; i++ {
if f == curProc.curGoroutine.frame {
fmt.Printf("=> ")
} else {
fmt.Printf(" ")
}
fmt.Printf("%8x %v\n", f.pc, f)
f, err = f.Outer()
if err != nil {
return err
}
if f == nil {
return nil
}
}
fmt.Println("...")
return nil
}
func parseNoArgs(args []byte, usage string) os.Error {
sc, ev := newScanner(args)
_, tok, _ := sc.Scan()
if sc.ErrorCount != 0 {
return ev.GetError(scanner.NoMultiples)
}
if tok != token.EOF {
return UsageError(usage)
}
return nil
}
/*
* Functions
*/
// defineFuncs populates world with the built-in functions.
func defineFuncs() {
t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig)
world.DefineConst("Out", t, v)
t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig)
world.DefineConst("ContWait", t, v)
t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig)
world.DefineConst("BpSet", t, v)
}
// printCurFrame prints the current stack frame, as it would appear in
// a backtrace.
func printCurFrame() {
if curProc == nil || curProc.curGoroutine == nil {
return
}
f := curProc.curGoroutine.frame
if f == nil {
return
}
fmt.Printf("=> %8x %v\n", f.pc, f)
}
// fnOut moves the current frame to the caller of the current frame.
func fnOutSig() {}
func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) {
if curProc == nil {
t.Abort(NoCurrentGoroutine{})
}
err := curProc.Out()
if err != nil {
t.Abort(err)
}
// TODO(austin) Only in the command form
printCurFrame()
}
// fnContWait continues the current process and waits for a stopping event.
func fnContWaitSig() {}
func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) {
if curProc == nil {
t.Abort(NoCurrentGoroutine{})
}
err := curProc.ContWait()
if err != nil {
t.Abort(err)
}
// TODO(austin) Only in the command form
ev := curProc.Event()
if ev != nil {
fmt.Printf("%v\n", ev)
}
printCurFrame()
}
// fnBpSet sets a breakpoint at the entry to the named function.
func fnBpSetSig(string) {}
func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
// TODO(austin) This probably shouldn't take a symbol name.
// Perhaps it should take an interface that provides PC's.
// Functions and instructions can implement that interface and
// we can have something to translate file:line pairs.
if curProc == nil {
t.Abort(NoCurrentGoroutine{})
}
name := args[0].(eval.StringValue).Get(t)
fn := curProc.syms.LookupFunc(name)
if fn == nil {
t.Abort(UsageError("no such function " + name))
}
curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop)
}

View File

@ -1,280 +0,0 @@
// 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"
"fmt"
"os"
)
/*
* Hooks and events
*/
// An EventHandler is a function that takes an event and returns a
// response to that event and possibly an error. If an event handler
// returns an error, the process stops and no other handlers for that
// event are executed.
type EventHandler func(e Event) (EventAction, os.Error)
// An EventAction is an event handler's response to an event. If all
// of an event's handlers execute without returning errors, their
// results are combined as follows: If any handler returned
// EAContinue, then the process resumes (without returning from
// WaitStop); otherwise, if any handler returned EAStop, the process
// remains stopped; otherwise, if all handlers returned EADefault, the
// process resumes. A handler may return EARemoveSelf bit-wise or'd
// with any other action to indicate that the handler should be
// removed from the hook.
type EventAction int
const (
EARemoveSelf EventAction = 0x100
EADefault EventAction = iota
EAStop
EAContinue
)
// A EventHook allows event handlers to be added and removed.
type EventHook interface {
AddHandler(EventHandler)
RemoveHandler(EventHandler)
NumHandler() int
handle(e Event) (EventAction, os.Error)
String() string
}
// EventHook is almost, but not quite, suitable for user-defined
// events. If we want user-defined events, make EventHook a struct,
// special-case adding and removing handlers in breakpoint hooks, and
// provide a public interface for posting events to hooks.
type Event interface {
Process() *Process
Goroutine() *Goroutine
String() string
}
type commonHook struct {
// Head of handler chain
head *handler
// Number of non-internal handlers
len int
}
type handler struct {
eh EventHandler
// True if this handler must be run before user-defined
// handlers in order to ensure correctness.
internal bool
// True if this handler has been removed from the chain.
removed bool
next *handler
}
func (h *commonHook) AddHandler(eh EventHandler) {
h.addHandler(eh, false)
}
func (h *commonHook) addHandler(eh EventHandler, internal bool) {
// Ensure uniqueness of handlers
h.RemoveHandler(eh)
if !internal {
h.len++
}
// Add internal handlers to the beginning
if internal || h.head == nil {
h.head = &handler{eh, internal, false, h.head}
return
}
// Add handler after internal handlers
// TODO(austin) This should probably go on the end instead
prev := h.head
for prev.next != nil && prev.internal {
prev = prev.next
}
prev.next = &handler{eh, internal, false, prev.next}
}
func (h *commonHook) RemoveHandler(eh EventHandler) {
plink := &h.head
for l := *plink; l != nil; plink, l = &l.next, l.next {
if l.eh == eh {
if !l.internal {
h.len--
}
l.removed = true
*plink = l.next
break
}
}
}
func (h *commonHook) NumHandler() int { return h.len }
func (h *commonHook) handle(e Event) (EventAction, os.Error) {
action := EADefault
plink := &h.head
for l := *plink; l != nil; plink, l = &l.next, l.next {
if l.removed {
continue
}
a, err := l.eh(e)
if a&EARemoveSelf == EARemoveSelf {
if !l.internal {
h.len--
}
l.removed = true
*plink = l.next
a &^= EARemoveSelf
}
if err != nil {
return EAStop, err
}
if a > action {
action = a
}
}
return action, nil
}
type commonEvent struct {
// The process of this event
p *Process
// The goroutine of this event.
t *Goroutine
}
func (e *commonEvent) Process() *Process { return e.p }
func (e *commonEvent) Goroutine() *Goroutine { return e.t }
/*
* Standard event handlers
*/
// EventPrint is a standard event handler that prints events as they
// occur. It will not cause the process to stop.
func EventPrint(ev Event) (EventAction, os.Error) {
// TODO(austin) Include process name here?
fmt.Fprintf(os.Stderr, "*** %v\n", ev.String())
return EADefault, nil
}
// EventStop is a standard event handler that causes the process to stop.
func EventStop(ev Event) (EventAction, os.Error) {
return EAStop, nil
}
/*
* Breakpoints
*/
type breakpointHook struct {
commonHook
p *Process
pc proc.Word
}
// A Breakpoint event occurs when a process reaches a particular
// program counter. When this event is handled, the current goroutine
// will be the goroutine that reached the program counter.
type Breakpoint struct {
commonEvent
osThread proc.Thread
pc proc.Word
}
func (h *breakpointHook) AddHandler(eh EventHandler) {
h.addHandler(eh, false)
}
func (h *breakpointHook) addHandler(eh EventHandler, internal bool) {
// We register breakpoint events lazily to avoid holding
// references to breakpoints without handlers. Be sure to use
// the "canonical" breakpoint if there is one.
if cur, ok := h.p.breakpointHooks[h.pc]; ok {
h = cur
}
oldhead := h.head
h.commonHook.addHandler(eh, internal)
if oldhead == nil && h.head != nil {
h.p.proc.AddBreakpoint(h.pc)
h.p.breakpointHooks[h.pc] = h
}
}
func (h *breakpointHook) RemoveHandler(eh EventHandler) {
oldhead := h.head
h.commonHook.RemoveHandler(eh)
if oldhead != nil && h.head == nil {
h.p.proc.RemoveBreakpoint(h.pc)
h.p.breakpointHooks[h.pc] = nil, false
}
}
func (h *breakpointHook) String() string {
// TODO(austin) Include process name?
// TODO(austin) Use line:pc or at least sym+%#x
return fmt.Sprintf("breakpoint at %#x", h.pc)
}
func (b *Breakpoint) PC() proc.Word { return b.pc }
func (b *Breakpoint) String() string {
// TODO(austin) Include process name and goroutine
// TODO(austin) Use line:pc or at least sym+%#x
return fmt.Sprintf("breakpoint at %#x", b.pc)
}
/*
* Goroutine create/exit
*/
type goroutineCreateHook struct {
commonHook
}
func (h *goroutineCreateHook) String() string { return "goroutine create" }
// A GoroutineCreate event occurs when a process creates a new
// goroutine. When this event is handled, the current goroutine will
// be the newly created goroutine.
type GoroutineCreate struct {
commonEvent
parent *Goroutine
}
// Parent returns the goroutine that created this goroutine. May be
// nil if this event is the creation of the first goroutine.
func (e *GoroutineCreate) Parent() *Goroutine { return e.parent }
func (e *GoroutineCreate) String() string {
// TODO(austin) Include process name
if e.parent == nil {
return fmt.Sprintf("%v created", e.t)
}
return fmt.Sprintf("%v created by %v", e.t, e.parent)
}
type goroutineExitHook struct {
commonHook
}
func (h *goroutineExitHook) String() string { return "goroutine exit" }
// A GoroutineExit event occurs when a Go goroutine exits.
type GoroutineExit struct {
commonEvent
}
func (e *GoroutineExit) String() string {
// TODO(austin) Include process name
//return fmt.Sprintf("%v exited", e.t);
// For debugging purposes
return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base)
}

View File

@ -1,212 +0,0 @@
// 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/gosym"
"debug/proc"
"fmt"
"os"
)
// A Frame represents a single frame on a remote call stack.
type Frame struct {
// pc is the PC of the next instruction that will execute in
// this frame. For lower frames, this is the instruction
// following the CALL instruction.
pc, sp, fp proc.Word
// The runtime.Stktop of the active stack segment
stk remoteStruct
// The function this stack frame is in
fn *gosym.Func
// The path and line of the CALL or current instruction. Note
// that this differs slightly from the meaning of Frame.pc.
path string
line int
// The inner and outer frames of this frame. outer is filled
// in lazily.
inner, outer *Frame
}
// newFrame returns the top-most Frame of the given g's thread.
func newFrame(g remoteStruct) (*Frame, os.Error) {
var f *Frame
err := try(func(a aborter) { f = aNewFrame(a, g) })
return f, err
}
func aNewFrame(a aborter, g remoteStruct) *Frame {
p := g.r.p
var pc, sp proc.Word
// Is this G alive?
switch g.field(p.f.G.Status).(remoteInt).aGet(a) {
case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead:
return nil
}
// Find the OS thread for this G
// TODO(austin) Ideally, we could look at the G's state and
// figure out if it's on an OS thread or not. However, this
// is difficult because the state isn't updated atomically
// with scheduling changes.
for _, t := range p.proc.Threads() {
regs, err := t.Regs()
if err != nil {
// TODO(austin) What to do?
continue
}
thisg := p.G(regs)
if thisg == g.addr().base {
// Found this G's OS thread
pc = regs.PC()
sp = regs.SP()
// If this thread crashed, try to recover it
if pc == 0 {
pc = p.peekUintptr(a, pc)
sp += 8
}
break
}
}
if pc == 0 && sp == 0 {
// G is not mapped to an OS thread. Use the
// scheduler's stored PC and SP.
sched := g.field(p.f.G.Sched).(remoteStruct)
pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a))
sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a))
}
// Get Stktop
stk := g.field(p.f.G.Stackbase).(remotePtr).aGet(a).(remoteStruct)
return prepareFrame(a, pc, sp, stk, nil)
}
// prepareFrame creates a Frame from the PC and SP within that frame,
// as well as the active stack segment. This function takes care of
// traversing stack breaks and unwinding closures.
func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame {
// Based on src/pkg/runtime/amd64/traceback.c:traceback
p := stk.r.p
top := inner == nil
// Get function
var path string
var line int
var fn *gosym.Func
for i := 0; i < 100; i++ {
// Traverse segmented stack breaks
if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) {
// Get stk->gobuf.pc
pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a))
// Get stk->gobuf.sp
sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a))
// Get stk->stackbase
stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct)
continue
}
// Get the PC of the call instruction
callpc := pc
if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) {
callpc--
}
// Look up function
path, line, fn = p.syms.PCToLine(uint64(callpc))
if fn != nil {
break
}
// Closure?
var buf = make([]byte, p.ClosureSize())
if _, err := p.Peek(pc, buf); err != nil {
break
}
spdelta, ok := p.ParseClosure(buf)
if ok {
sp += proc.Word(spdelta)
pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize()))
}
}
if fn == nil {
return nil
}
// Compute frame pointer
var fp proc.Word
if fn.FrameSize < p.PtrSize() {
fp = sp + proc.Word(p.PtrSize())
} else {
fp = sp + proc.Word(fn.FrameSize)
}
// TODO(austin) To really figure out if we're in the prologue,
// we need to disassemble the function and look for the call
// to morestack. For now, just special case the entry point.
//
// TODO(austin) What if we're in the call to morestack in the
// prologue? Then top == false.
if top && pc == proc.Word(fn.Entry) {
// We're in the function prologue, before SP
// has been adjusted for the frame.
fp -= proc.Word(fn.FrameSize - p.PtrSize())
}
return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil}
}
// Outer returns the Frame that called this Frame, or nil if this is
// the outermost frame.
func (f *Frame) Outer() (*Frame, os.Error) {
var fr *Frame
err := try(func(a aborter) { fr = f.aOuter(a) })
return fr, err
}
func (f *Frame) aOuter(a aborter) *Frame {
// Is there a cached outer frame
if f.outer != nil {
return f.outer
}
p := f.stk.r.p
sp := f.fp
if f.fn == p.sys.newproc && f.fn == p.sys.deferproc {
// TODO(rsc) The compiler inserts two push/pop's
// around calls to go and defer. Russ says this
// should get fixed in the compiler, but we account
// for it for now.
sp += proc.Word(2 * p.PtrSize())
}
pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize()))
if pc < 0x1000 {
return nil
}
// TODO(austin) Register this frame for shoot-down.
f.outer = prepareFrame(a, pc, sp, f.stk, f)
return f.outer
}
// Inner returns the Frame called by this Frame, or nil if this is the
// innermost frame.
func (f *Frame) Inner() *Frame { return f.inner }
func (f *Frame) String() string {
res := f.fn.Name
if f.pc > proc.Word(f.fn.Value) {
res += fmt.Sprintf("+%#x", f.pc-proc.Word(f.fn.Entry))
}
return res + fmt.Sprintf(" %s:%d", f.path, f.line)
}

View File

@ -1,117 +0,0 @@
// 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"
"os"
)
// A Goroutine represents a goroutine in a remote process.
type Goroutine struct {
g remoteStruct
frame *Frame
dead bool
}
func (t *Goroutine) String() string {
if t.dead {
return "<dead thread>"
}
// TODO(austin) Give threads friendly ID's, possibly including
// the name of the entry function.
return fmt.Sprintf("thread %#x", t.g.addr().base)
}
// isG0 returns true if this thread if the internal idle thread
func (t *Goroutine) isG0() bool { return t.g.addr().base == t.g.r.p.sys.g0.addr().base }
func (t *Goroutine) resetFrame() (err os.Error) {
// TODO(austin) Reuse any live part of the current frame stack
// so existing references to Frame's keep working.
t.frame, err = newFrame(t.g)
return
}
// Out selects the caller frame of the current frame.
func (t *Goroutine) Out() os.Error {
f, err := t.frame.Outer()
if f != nil {
t.frame = f
}
return err
}
// In selects the frame called by the current frame.
func (t *Goroutine) In() os.Error {
f := t.frame.Inner()
if f != nil {
t.frame = f
}
return nil
}
func readylockedBP(ev Event) (EventAction, os.Error) {
b := ev.(*Breakpoint)
p := b.Process()
// The new g is the only argument to this function, so the
// stack will have the return address, then the G*.
regs, err := b.osThread.Regs()
if err != nil {
return EAStop, err
}
sp := regs.SP()
addr := sp + proc.Word(p.PtrSize())
arg := remotePtr{remote{addr, p}, p.runtime.G}
var gp eval.Value
err = try(func(a aborter) { gp = arg.aGet(a) })
if err != nil {
return EAStop, err
}
if gp == nil {
return EAStop, UnknownGoroutine{b.osThread, 0}
}
gs := gp.(remoteStruct)
g := &Goroutine{gs, nil, false}
p.goroutines[gs.addr().base] = g
// Enqueue goroutine creation event
parent := b.Goroutine()
if parent.isG0() {
parent = nil
}
p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent})
// If we don't have any thread selected, select this one
if p.curGoroutine == nil {
p.curGoroutine = g
}
return EADefault, nil
}
func goexitBP(ev Event) (EventAction, os.Error) {
b := ev.(*Breakpoint)
p := b.Process()
g := b.Goroutine()
g.dead = true
addr := g.g.addr().base
p.goroutines[addr] = nil, false
// Enqueue thread exit event
p.postEvent(&GoroutineExit{commonEvent{p, g}})
// If we just exited our selected goroutine, selected another
if p.curGoroutine == g {
p.selectSomeGoroutine()
}
return EADefault, nil
}

View File

@ -1,9 +0,0 @@
// 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 main
import "exp/ogle"
func main() { ogle.Main() }

View File

@ -1,521 +0,0 @@
// 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/elf"
"debug/gosym"
"debug/proc"
"exp/eval"
"fmt"
"log"
"os"
"reflect"
)
// A FormatError indicates a failure to process information in or
// about a remote process, such as unexpected or missing information
// in the object file or runtime structures.
type FormatError string
func (e FormatError) String() string { return string(e) }
// An UnknownArchitecture occurs when trying to load an object file
// that indicates an architecture not supported by the debugger.
type UnknownArchitecture elf.Machine
func (e UnknownArchitecture) String() string {
return "unknown architecture: " + elf.Machine(e).String()
}
// A ProcessNotStopped error occurs when attempting to read or write
// memory or registers of a process that is not stopped.
type ProcessNotStopped struct{}
func (e ProcessNotStopped) String() string { return "process not stopped" }
// An UnknownGoroutine error is an internal error representing an
// unrecognized G structure pointer.
type UnknownGoroutine struct {
OSThread proc.Thread
Goroutine proc.Word
}
func (e UnknownGoroutine) String() string {
return fmt.Sprintf("internal error: unknown goroutine (G %#x)", e.Goroutine)
}
// A NoCurrentGoroutine error occurs when no goroutine is currently
// selected in a process (or when there are no goroutines in a
// process).
type NoCurrentGoroutine struct{}
func (e NoCurrentGoroutine) String() string { return "no current goroutine" }
// A Process represents a remote attached process.
type Process struct {
Arch
proc proc.Process
// The symbol table of this process
syms *gosym.Table
// A possibly-stopped OS thread, or nil
threadCache proc.Thread
// Types parsed from the remote process
types map[proc.Word]*remoteType
// Types and values from the remote runtime package
runtime runtimeValues
// Runtime field indexes
f runtimeIndexes
// Globals from the sys package (or from no package)
sys struct {
lessstack, goexit, newproc, deferproc, newprocreadylocked *gosym.Func
allg remotePtr
g0 remoteStruct
}
// Event queue
posted []Event
pending []Event
event Event
// Event hooks
breakpointHooks map[proc.Word]*breakpointHook
goroutineCreateHook *goroutineCreateHook
goroutineExitHook *goroutineExitHook
// Current goroutine, or nil if there are no goroutines
curGoroutine *Goroutine
// Goroutines by the address of their G structure
goroutines map[proc.Word]*Goroutine
}
/*
* Process creation
*/
// NewProcess constructs a new remote process around a traced
// process, an architecture, and a symbol table.
func NewProcess(tproc proc.Process, arch Arch, syms *gosym.Table) (*Process, os.Error) {
p := &Process{
Arch: arch,
proc: tproc,
syms: syms,
types: make(map[proc.Word]*remoteType),
breakpointHooks: make(map[proc.Word]*breakpointHook),
goroutineCreateHook: new(goroutineCreateHook),
goroutineExitHook: new(goroutineExitHook),
goroutines: make(map[proc.Word]*Goroutine),
}
// Fill in remote runtime
p.bootstrap()
switch {
case p.sys.allg.addr().base == 0:
return nil, FormatError("failed to find runtime symbol 'allg'")
case p.sys.g0.addr().base == 0:
return nil, FormatError("failed to find runtime symbol 'g0'")
case p.sys.newprocreadylocked == nil:
return nil, FormatError("failed to find runtime symbol 'newprocreadylocked'")
case p.sys.goexit == nil:
return nil, FormatError("failed to find runtime symbol 'sys.goexit'")
}
// Get current goroutines
p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false}
err := try(func(a aborter) {
g := p.sys.allg.aGet(a)
for g != nil {
gs := g.(remoteStruct)
fmt.Printf("*** Found goroutine at %#x\n", gs.addr().base)
p.goroutines[gs.addr().base] = &Goroutine{gs, nil, false}
g = gs.field(p.f.G.Alllink).(remotePtr).aGet(a)
}
})
if err != nil {
return nil, err
}
// Create internal breakpoints to catch new and exited goroutines
p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry)).(*breakpointHook).addHandler(readylockedBP, true)
p.OnBreakpoint(proc.Word(p.sys.goexit.Entry)).(*breakpointHook).addHandler(goexitBP, true)
// Select current frames
for _, g := range p.goroutines {
g.resetFrame()
}
p.selectSomeGoroutine()
return p, nil
}
func elfGoSyms(f *elf.File) (*gosym.Table, os.Error) {
text := f.Section(".text")
symtab := f.Section(".gosymtab")
pclntab := f.Section(".gopclntab")
if text == nil || symtab == nil || pclntab == nil {
return nil, nil
}
symdat, err := symtab.Data()
if err != nil {
return nil, err
}
pclndat, err := pclntab.Data()
if err != nil {
return nil, err
}
pcln := gosym.NewLineTable(pclndat, text.Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
return nil, err
}
return tab, nil
}
// NewProcessElf constructs a new remote process around a traced
// process and the process' ELF object.
func NewProcessElf(tproc proc.Process, f *elf.File) (*Process, os.Error) {
syms, err := elfGoSyms(f)
if err != nil {
return nil, err
}
if syms == nil {
return nil, FormatError("Failed to find symbol table")
}
var arch Arch
switch f.Machine {
case elf.EM_X86_64:
arch = Amd64
default:
return nil, UnknownArchitecture(f.Machine)
}
return NewProcess(tproc, arch, syms)
}
// bootstrap constructs the runtime structure of a remote process.
func (p *Process) bootstrap() {
// Manually construct runtime types
p.runtime.String = newManualType(eval.TypeOfNative(rt1String{}), p.Arch)
p.runtime.Slice = newManualType(eval.TypeOfNative(rt1Slice{}), p.Arch)
p.runtime.Eface = newManualType(eval.TypeOfNative(rt1Eface{}), p.Arch)
p.runtime.Type = newManualType(eval.TypeOfNative(rt1Type{}), p.Arch)
p.runtime.CommonType = newManualType(eval.TypeOfNative(rt1CommonType{}), p.Arch)
p.runtime.UncommonType = newManualType(eval.TypeOfNative(rt1UncommonType{}), p.Arch)
p.runtime.StructField = newManualType(eval.TypeOfNative(rt1StructField{}), p.Arch)
p.runtime.StructType = newManualType(eval.TypeOfNative(rt1StructType{}), p.Arch)
p.runtime.PtrType = newManualType(eval.TypeOfNative(rt1PtrType{}), p.Arch)
p.runtime.ArrayType = newManualType(eval.TypeOfNative(rt1ArrayType{}), p.Arch)
p.runtime.SliceType = newManualType(eval.TypeOfNative(rt1SliceType{}), p.Arch)
p.runtime.Stktop = newManualType(eval.TypeOfNative(rt1Stktop{}), p.Arch)
p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch)
p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch)
// Get addresses of type.*runtime.XType for discrimination.
rtv := reflect.Indirect(reflect.ValueOf(&p.runtime))
rtvt := rtv.Type()
for i := 0; i < rtv.NumField(); i++ {
n := rtvt.Field(i).Name
if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' {
continue
}
sym := p.syms.LookupSym("type.*runtime." + n[1:])
if sym == nil {
continue
}
rtv.Field(i).SetUint(sym.Value)
}
// Get runtime field indexes
fillRuntimeIndexes(&p.runtime, &p.f)
// Fill G status
p.runtime.runtimeGStatus = rt1GStatus
// Get globals
p.sys.lessstack = p.syms.LookupFunc("sys.lessstack")
p.sys.goexit = p.syms.LookupFunc("goexit")
p.sys.newproc = p.syms.LookupFunc("sys.newproc")
p.sys.deferproc = p.syms.LookupFunc("sys.deferproc")
p.sys.newprocreadylocked = p.syms.LookupFunc("newprocreadylocked")
if allg := p.syms.LookupSym("allg"); allg != nil {
p.sys.allg = remotePtr{remote{proc.Word(allg.Value), p}, p.runtime.G}
}
if g0 := p.syms.LookupSym("g0"); g0 != nil {
p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct)
}
}
func (p *Process) selectSomeGoroutine() {
// Once we have friendly goroutine ID's, there might be a more
// reasonable behavior for this.
p.curGoroutine = nil
for _, g := range p.goroutines {
if !g.isG0() && g.frame != nil {
p.curGoroutine = g
return
}
}
}
/*
* Process memory
*/
func (p *Process) someStoppedOSThread() proc.Thread {
if p.threadCache != nil {
if _, err := p.threadCache.Stopped(); err == nil {
return p.threadCache
}
}
for _, t := range p.proc.Threads() {
if _, err := t.Stopped(); err == nil {
p.threadCache = t
return t
}
}
return nil
}
func (p *Process) Peek(addr proc.Word, out []byte) (int, os.Error) {
thr := p.someStoppedOSThread()
if thr == nil {
return 0, ProcessNotStopped{}
}
return thr.Peek(addr, out)
}
func (p *Process) Poke(addr proc.Word, b []byte) (int, os.Error) {
thr := p.someStoppedOSThread()
if thr == nil {
return 0, ProcessNotStopped{}
}
return thr.Poke(addr, b)
}
func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word {
return proc.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a))
}
/*
* Events
*/
// OnBreakpoint returns the hook that is run when the program reaches
// the given program counter.
func (p *Process) OnBreakpoint(pc proc.Word) EventHook {
if bp, ok := p.breakpointHooks[pc]; ok {
return bp
}
// The breakpoint will register itself when a handler is added
return &breakpointHook{commonHook{nil, 0}, p, pc}
}
// OnGoroutineCreate returns the hook that is run when a goroutine is created.
func (p *Process) OnGoroutineCreate() EventHook {
return p.goroutineCreateHook
}
// OnGoroutineExit returns the hook that is run when a goroutine exits.
func (p *Process) OnGoroutineExit() EventHook { return p.goroutineExitHook }
// osThreadToGoroutine looks up the goroutine running on an OS thread.
func (p *Process) osThreadToGoroutine(t proc.Thread) (*Goroutine, os.Error) {
regs, err := t.Regs()
if err != nil {
return nil, err
}
g := p.G(regs)
gt, ok := p.goroutines[g]
if !ok {
return nil, UnknownGoroutine{t, g}
}
return gt, nil
}
// causesToEvents translates the stop causes of the underlying process
// into an event queue.
func (p *Process) causesToEvents() ([]Event, os.Error) {
// Count causes we're interested in
nev := 0
for _, t := range p.proc.Threads() {
if c, err := t.Stopped(); err == nil {
switch c := c.(type) {
case proc.Breakpoint:
nev++
case proc.Signal:
// TODO(austin)
//nev++;
}
}
}
// Translate causes to events
events := make([]Event, nev)
i := 0
for _, t := range p.proc.Threads() {
if c, err := t.Stopped(); err == nil {
switch c := c.(type) {
case proc.Breakpoint:
gt, err := p.osThreadToGoroutine(t)
if err != nil {
return nil, err
}
events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)}
i++
case proc.Signal:
// TODO(austin)
}
}
}
return events, nil
}
// postEvent appends an event to the posted queue. These events will
// be processed before any currently pending events.
func (p *Process) postEvent(ev Event) {
p.posted = append(p.posted, ev)
}
// processEvents processes events in the event queue until no events
// remain, a handler returns EAStop, or a handler returns an error.
// It returns either EAStop or EAContinue and possibly an error.
func (p *Process) processEvents() (EventAction, os.Error) {
var ev Event
for len(p.posted) > 0 {
ev, p.posted = p.posted[0], p.posted[1:]
action, err := p.processEvent(ev)
if action == EAStop {
return action, err
}
}
for len(p.pending) > 0 {
ev, p.pending = p.pending[0], p.pending[1:]
action, err := p.processEvent(ev)
if action == EAStop {
return action, err
}
}
return EAContinue, nil
}
// processEvent processes a single event, without manipulating the
// event queues. It returns either EAStop or EAContinue and possibly
// an error.
func (p *Process) processEvent(ev Event) (EventAction, os.Error) {
p.event = ev
var action EventAction
var err os.Error
switch ev := p.event.(type) {
case *Breakpoint:
hook, ok := p.breakpointHooks[ev.pc]
if !ok {
break
}
p.curGoroutine = ev.Goroutine()
action, err = hook.handle(ev)
case *GoroutineCreate:
p.curGoroutine = ev.Goroutine()
action, err = p.goroutineCreateHook.handle(ev)
case *GoroutineExit:
action, err = p.goroutineExitHook.handle(ev)
default:
log.Panicf("Unknown event type %T in queue", p.event)
}
if err != nil {
return EAStop, err
} else if action == EAStop {
return EAStop, nil
}
return EAContinue, nil
}
// Event returns the last event that caused the process to stop. This
// may return nil if the process has never been stopped by an event.
//
// TODO(austin) Return nil if the user calls p.Stop()?
func (p *Process) Event() Event { return p.event }
/*
* Process control
*/
// TODO(austin) Cont, WaitStop, and Stop. Need to figure out how
// event handling works with these. Originally I did it only in
// WaitStop, but if you Cont and there are pending events, then you
// have to not actually continue and wait until a WaitStop to process
// them, even if the event handlers will tell you to continue. We
// could handle them in both Cont and WaitStop to avoid this problem,
// but it's still weird if an event happens after the Cont and before
// the WaitStop that the handlers say to continue from. Or we could
// handle them on a separate thread. Then obviously you get weird
// asynchronous things, like prints while the user it typing a command,
// but that's not necessarily a bad thing.
// ContWait resumes process execution and waits for an event to occur
// that stops the process.
func (p *Process) ContWait() os.Error {
for {
a, err := p.processEvents()
if err != nil {
return err
} else if a == EAStop {
break
}
err = p.proc.Continue()
if err != nil {
return err
}
err = p.proc.WaitStop()
if err != nil {
return err
}
for _, g := range p.goroutines {
g.resetFrame()
}
p.pending, err = p.causesToEvents()
if err != nil {
return err
}
}
return nil
}
// Out selects the caller frame of the current frame.
func (p *Process) Out() os.Error {
if p.curGoroutine == nil {
return NoCurrentGoroutine{}
}
return p.curGoroutine.Out()
}
// In selects the frame called by the current frame.
func (p *Process) In() os.Error {
if p.curGoroutine == nil {
return NoCurrentGoroutine{}
}
return p.curGoroutine.In()
}

View File

@ -1,271 +0,0 @@
// 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"
"reflect"
)
// This file contains remote runtime definitions. Using reflection,
// we convert all of these to interpreter types and layout their
// remote representations using the architecture rules.
//
// We could get most of these definitions from our own runtime
// package; however, some of them differ in convenient ways, some of
// them are not defined or exported by the runtime, and having our own
// definitions makes it easy to support multiple remote runtime
// versions. This may turn out to be overkill.
//
// All of these structures are prefixed with rt1 to indicate the
// runtime version and to mark them as types used only as templates
// for remote types.
/*
* Runtime data headers
*
* See $GOROOT/src/pkg/runtime/runtime.h
*/
type rt1String struct {
str uintptr
len int
}
type rt1Slice struct {
array uintptr
len int
cap int
}
type rt1Eface struct {
typ uintptr
ptr uintptr
}
/*
* Runtime type structures
*
* See $GOROOT/src/pkg/runtime/type.h and $GOROOT/src/pkg/runtime/type.go
*/
type rt1UncommonType struct {
name *string
pkgPath *string
//methods []method;
}
type rt1CommonType struct {
size uintptr
hash uint32
alg, align, fieldAlign uint8
string *string
uncommonType *rt1UncommonType
}
type rt1Type struct {
// While Type is technically an Eface, treating the
// discriminator as an opaque pointer and taking advantage of
// the commonType prologue on all Type's makes type parsing
// much simpler.
typ uintptr
ptr *rt1CommonType
}
type rt1StructField struct {
name *string
pkgPath *string
typ *rt1Type
tag *string
offset uintptr
}
type rt1StructType struct {
rt1CommonType
fields []rt1StructField
}
type rt1PtrType struct {
rt1CommonType
elem *rt1Type
}
type rt1SliceType struct {
rt1CommonType
elem *rt1Type
}
type rt1ArrayType struct {
rt1CommonType
elem *rt1Type
len uintptr
}
/*
* Runtime scheduler structures
*
* See $GOROOT/src/pkg/runtime/runtime.h
*/
// Fields beginning with _ are only for padding
type rt1Stktop struct {
stackguard uintptr
stackbase *rt1Stktop
gobuf rt1Gobuf
_args uint32
_fp uintptr
}
type rt1Gobuf struct {
sp uintptr
pc uintptr
g *rt1G
r0 uintptr
}
type rt1G struct {
_stackguard uintptr
stackbase *rt1Stktop
_defer uintptr
sched rt1Gobuf
_stack0 uintptr
_entry uintptr
alllink *rt1G
_param uintptr
status int16
// Incomplete
}
var rt1GStatus = runtimeGStatus{
Gidle: 0,
Grunnable: 1,
Grunning: 2,
Gsyscall: 3,
Gwaiting: 4,
Gmoribund: 5,
Gdead: 6,
}
// runtimeIndexes stores the indexes of fields in the runtime
// structures. It is filled in using reflection, so the name of the
// fields must match the names of the remoteType's in runtimeValues
// exactly and the names of the index fields must be the capitalized
// version of the names of the fields in the runtime structures above.
type runtimeIndexes struct {
String struct {
Str, Len int
}
Slice struct {
Array, Len, Cap int
}
Eface struct {
Typ, Ptr int
}
UncommonType struct {
Name, PkgPath int
}
CommonType struct {
Size, Hash, Alg, Align, FieldAlign, String, UncommonType int
}
Type struct {
Typ, Ptr int
}
StructField struct {
Name, PkgPath, Typ, Tag, Offset int
}
StructType struct {
Fields int
}
PtrType struct {
Elem int
}
SliceType struct {
Elem int
}
ArrayType struct {
Elem, Len int
}
Stktop struct {
Stackguard, Stackbase, Gobuf int
}
Gobuf struct {
Sp, Pc, G int
}
G struct {
Stackbase, Sched, Status, Alllink int
}
}
// Values of G status codes
type runtimeGStatus struct {
Gidle, Grunnable, Grunning, Gsyscall, Gwaiting, Gmoribund, Gdead int64
}
// runtimeValues stores the types and values that correspond to those
// in the remote runtime package.
type runtimeValues struct {
// Runtime data headers
String, Slice, Eface *remoteType
// Runtime type structures
Type, CommonType, UncommonType, StructField, StructType, PtrType,
ArrayType, SliceType *remoteType
// Runtime scheduler structures
Stktop, Gobuf, G *remoteType
// Addresses of *runtime.XType types. These are the
// discriminators on the runtime.Type interface. We use local
// reflection to fill these in from the remote symbol table,
// so the names must match the runtime names.
PBoolType,
PUint8Type, PUint16Type, PUint32Type, PUint64Type, PUintType, PUintptrType,
PInt8Type, PInt16Type, PInt32Type, PInt64Type, PIntType,
PFloat32Type, PFloat64Type, PFloatType,
PArrayType, PStringType, PStructType, PPtrType, PFuncType,
PInterfaceType, PSliceType, PMapType, PChanType,
PDotDotDotType, PUnsafePointerType proc.Word
// G status values
runtimeGStatus
}
// fillRuntimeIndexes fills a runtimeIndexes structure will the field
// indexes gathered from the remoteTypes recorded in a runtimeValues
// structure.
func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) {
outv := reflect.Indirect(reflect.ValueOf(out))
outt := outv.Type()
runtimev := reflect.Indirect(reflect.ValueOf(runtime))
// out contains fields corresponding to each runtime type
for i := 0; i < outt.NumField(); i++ {
// Find the interpreter type for this runtime type
name := outt.Field(i).Name
et := runtimev.FieldByName(name).Interface().(*remoteType).Type.(*eval.StructType)
// Get the field indexes of the interpreter struct type
indexes := make(map[string]int, len(et.Elems))
for j, f := range et.Elems {
if f.Anonymous {
continue
}
name := f.Name
if name[0] >= 'a' && name[0] <= 'z' {
name = string(name[0]+'A'-'a') + name[1:]
}
indexes[name] = j
}
// Fill this field of out
outStructv := outv.Field(i)
outStructt := outStructv.Type()
for j := 0; j < outStructt.NumField(); j++ {
f := outStructv.Field(j)
name := outStructt.Field(j).Name
f.SetInt(int64(indexes[name]))
}
}
}

View File

@ -1,288 +0,0 @@
// 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"
"log"
)
const debugParseRemoteType = false
// A remoteType is the local representation of a type in a remote process.
type remoteType struct {
eval.Type
// The size of values of this type in bytes.
size int
// The field alignment of this type. Only used for
// manually-constructed types.
fieldAlign int
// The maker function to turn a remote address of a value of
// this type into an interpreter Value.
mk maker
}
var manualTypes = make(map[Arch]map[eval.Type]*remoteType)
// newManualType constructs a remote type from an interpreter Type
// using the size and alignment properties of the given architecture.
// Most types are parsed directly out of the remote process, but to do
// so we need to layout the structures that describe those types ourselves.
func newManualType(t eval.Type, arch Arch) *remoteType {
if nt, ok := t.(*eval.NamedType); ok {
t = nt.Def
}
// Get the type map for this architecture
typeMap := manualTypes[arch]
if typeMap == nil {
typeMap = make(map[eval.Type]*remoteType)
manualTypes[arch] = typeMap
// Construct basic types for this architecture
basicType := func(t eval.Type, mk maker, size int, fieldAlign int) {
t = t.(*eval.NamedType).Def
if fieldAlign == 0 {
fieldAlign = size
}
typeMap[t] = &remoteType{t, size, fieldAlign, mk}
}
basicType(eval.Uint8Type, mkUint8, 1, 0)
basicType(eval.Uint32Type, mkUint32, 4, 0)
basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0)
basicType(eval.Int16Type, mkInt16, 2, 0)
basicType(eval.Int32Type, mkInt32, 4, 0)
basicType(eval.IntType, mkInt, arch.IntSize(), 0)
basicType(eval.StringType, mkString, arch.PtrSize()+arch.IntSize(), arch.PtrSize())
}
if rt, ok := typeMap[t]; ok {
return rt
}
var rt *remoteType
switch t := t.(type) {
case *eval.PtrType:
var elem *remoteType
mk := func(r remote) eval.Value { return remotePtr{r, elem} }
rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk}
// Construct the element type after registering the
// type to break cycles.
typeMap[eval.Type(t)] = rt
elem = newManualType(t.Elem, arch)
case *eval.ArrayType:
elem := newManualType(t.Elem, arch)
mk := func(r remote) eval.Value { return remoteArray{r, t.Len, elem} }
rt = &remoteType{t, elem.size * int(t.Len), elem.fieldAlign, mk}
case *eval.SliceType:
elem := newManualType(t.Elem, arch)
mk := func(r remote) eval.Value { return remoteSlice{r, elem} }
rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk}
case *eval.StructType:
layout := make([]remoteStructField, len(t.Elems))
offset := 0
fieldAlign := 0
for i, f := range t.Elems {
elem := newManualType(f.Type, arch)
if fieldAlign == 0 {
fieldAlign = elem.fieldAlign
}
offset = arch.Align(offset, elem.fieldAlign)
layout[i].offset = offset
layout[i].fieldType = elem
offset += elem.size
}
mk := func(r remote) eval.Value { return remoteStruct{r, layout} }
rt = &remoteType{t, offset, fieldAlign, mk}
default:
log.Panicf("cannot manually construct type %T", t)
}
typeMap[t] = rt
return rt
}
var prtIndent = ""
// parseRemoteType parses a Type structure in a remote process to
// construct the corresponding interpreter type and remote type.
func parseRemoteType(a aborter, rs remoteStruct) *remoteType {
addr := rs.addr().base
p := rs.addr().p
// We deal with circular types by discovering cycles at
// NamedTypes. If a type cycles back to something other than
// a named type, we're guaranteed that there will be a named
// type somewhere in that cycle. Thus, we continue down,
// re-parsing types until we reach the named type in the
// cycle. In order to still create one remoteType per remote
// type, we insert an empty remoteType in the type map the
// first time we encounter the type and re-use that structure
// the second time we encounter it.
rt, ok := p.types[addr]
if ok && rt.Type != nil {
return rt
} else if !ok {
rt = &remoteType{}
p.types[addr] = rt
}
if debugParseRemoteType {
sym := p.syms.SymByAddr(uint64(addr))
name := "<unknown>"
if sym != nil {
name = sym.Name
}
log.Printf("%sParsing type at %#x (%s)", prtIndent, addr, name)
prtIndent += " "
defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }()
}
// Get Type header
itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a))
typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct)
// Is this a named type?
var nt *eval.NamedType
uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a)
if uncommon != nil {
name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a)
if name != nil {
// TODO(austin) Declare type in appropriate remote package
nt = eval.NewNamedType(name.(remoteString).aGet(a))
rt.Type = nt
}
}
// Create type
var t eval.Type
var mk maker
switch itype {
case p.runtime.PBoolType:
t = eval.BoolType
mk = mkBool
case p.runtime.PUint8Type:
t = eval.Uint8Type
mk = mkUint8
case p.runtime.PUint16Type:
t = eval.Uint16Type
mk = mkUint16
case p.runtime.PUint32Type:
t = eval.Uint32Type
mk = mkUint32
case p.runtime.PUint64Type:
t = eval.Uint64Type
mk = mkUint64
case p.runtime.PUintType:
t = eval.UintType
mk = mkUint
case p.runtime.PUintptrType:
t = eval.UintptrType
mk = mkUintptr
case p.runtime.PInt8Type:
t = eval.Int8Type
mk = mkInt8
case p.runtime.PInt16Type:
t = eval.Int16Type
mk = mkInt16
case p.runtime.PInt32Type:
t = eval.Int32Type
mk = mkInt32
case p.runtime.PInt64Type:
t = eval.Int64Type
mk = mkInt64
case p.runtime.PIntType:
t = eval.IntType
mk = mkInt
case p.runtime.PFloat32Type:
t = eval.Float32Type
mk = mkFloat32
case p.runtime.PFloat64Type:
t = eval.Float64Type
mk = mkFloat64
case p.runtime.PStringType:
t = eval.StringType
mk = mkString
case p.runtime.PArrayType:
// Cast to an ArrayType
typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct)
len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a))
elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct))
t = eval.NewArrayType(len, elem.Type)
mk = func(r remote) eval.Value { return remoteArray{r, len, elem} }
case p.runtime.PStructType:
// Cast to a StructType
typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct)
fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a)
fields := make([]eval.StructField, fs.Len)
layout := make([]remoteStructField, fs.Len)
for i := range fields {
f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct)
elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct)
elem := parseRemoteType(a, elemrs)
fields[i].Type = elem.Type
name := f.field(p.f.StructField.Name).(remotePtr).aGet(a)
if name == nil {
fields[i].Anonymous = true
} else {
fields[i].Name = name.(remoteString).aGet(a)
}
layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a))
layout[i].fieldType = elem
}
t = eval.NewStructType(fields)
mk = func(r remote) eval.Value { return remoteStruct{r, layout} }
case p.runtime.PPtrType:
// Cast to a PtrType
typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct)
elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct))
t = eval.NewPtrType(elem.Type)
mk = func(r remote) eval.Value { return remotePtr{r, elem} }
case p.runtime.PSliceType:
// Cast to a SliceType
typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct)
elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct))
t = eval.NewSliceType(elem.Type)
mk = func(r remote) eval.Value { return remoteSlice{r, elem} }
case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType:
// TODO(austin)
t = eval.UintptrType
mk = mkUintptr
default:
sym := p.syms.SymByAddr(uint64(itype))
name := "<unknown symbol>"
if sym != nil {
name = sym.Name
}
err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name)
a.Abort(FormatError(err))
}
// Fill in the remote type
if nt != nil {
nt.Complete(t)
} else {
rt.Type = t
}
rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a))
rt.mk = mk
return rt
}

View File

@ -1,515 +0,0 @@
// 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")
}
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.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)
}

View File

@ -1,272 +0,0 @@
// 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/gosym"
"debug/proc"
"exp/eval"
"log"
"os"
)
/*
* Remote frame pointers
*/
// A NotOnStack error occurs when attempting to access a variable in a
// remote frame where that remote frame is not on the current stack.
type NotOnStack struct {
Fn *gosym.Func
Goroutine *Goroutine
}
func (e NotOnStack) String() string {
return "function " + e.Fn.Name + " not on " + e.Goroutine.String() + "'s stack"
}
// A remoteFramePtr is an implementation of eval.PtrValue that
// represents a pointer to a function frame in a remote process. When
// accessed, this locates the function on the current goroutine's
// stack and returns a structure containing the local variables of
// that function.
type remoteFramePtr struct {
p *Process
fn *gosym.Func
rt *remoteType
}
func (v remoteFramePtr) String() string {
// TODO(austin): This could be a really awesome string method
return "<remote frame>"
}
func (v remoteFramePtr) Assign(t *eval.Thread, o eval.Value) {
v.Set(t, o.(eval.PtrValue).Get(t))
}
func (v remoteFramePtr) Get(t *eval.Thread) eval.Value {
g := v.p.curGoroutine
if g == nil || g.frame == nil {
t.Abort(NoCurrentGoroutine{})
}
for f := g.frame; f != nil; f = f.aOuter(t) {
if f.fn != v.fn {
continue
}
// TODO(austin): Register for shootdown with f
return v.rt.mk(remote{f.fp, v.p})
}
t.Abort(NotOnStack{v.fn, g})
panic("fail")
}
func (v remoteFramePtr) Set(t *eval.Thread, x eval.Value) {
// Theoretically this could be a static error. If remote
// packages were packages, remote frames could just be defined
// as constants.
t.Abort(ReadOnlyError("remote frames cannot be assigned to"))
}
/*
* Remote packages
*/
// TODO(austin): Remote packages are implemented as structs right now,
// which has some weird consequences. You can attempt to assign to a
// remote package. It also produces terrible error messages.
// Ideally, these would actually be packages, but somehow first-class
// so they could be assigned to other names.
// A remotePackage is an implementation of eval.StructValue that
// represents a package in a remote process. It's essentially a
// regular struct, except it cannot be assigned to.
type remotePackage struct {
defs []eval.Value
}
func (v remotePackage) String() string { return "<remote package>" }
func (v remotePackage) Assign(t *eval.Thread, o eval.Value) {
t.Abort(ReadOnlyError("remote packages cannot be assigned to"))
}
func (v remotePackage) Get(t *eval.Thread) eval.StructValue {
return v
}
func (v remotePackage) Field(t *eval.Thread, i int) eval.Value {
return v.defs[i]
}
/*
* Remote variables
*/
// populateWorld defines constants in the given world for each package
// in this process. These packages are structs that, in turn, contain
// fields for each global and function in that package.
func (p *Process) populateWorld(w *eval.World) os.Error {
type def struct {
t eval.Type
v eval.Value
}
packages := make(map[string]map[string]def)
for _, s := range p.syms.Syms {
if s.ReceiverName() != "" {
// TODO(austin)
continue
}
// Package
pkgName := s.PackageName()
switch pkgName {
case "", "type", "extratype", "string", "go":
// "go" is really "go.string"
continue
}
pkg, ok := packages[pkgName]
if !ok {
pkg = make(map[string]def)
packages[pkgName] = pkg
}
// Symbol name
name := s.BaseName()
if _, ok := pkg[name]; ok {
log.Printf("Multiple definitions of symbol %s", s.Name)
continue
}
// Symbol type
rt, err := p.typeOfSym(&s)
if err != nil {
return err
}
// Definition
switch s.Type {
case 'D', 'd', 'B', 'b':
// Global variable
if rt == nil {
continue
}
pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})}
case 'T', 't', 'L', 'l':
// Function
s := s.Func
// TODO(austin): Ideally, this would *also* be
// callable. How does that interact with type
// conversion syntax?
rt, err := p.makeFrameType(s)
if err != nil {
return err
}
pkg[name] = def{eval.NewPtrType(rt.Type), remoteFramePtr{p, s, rt}}
}
}
// TODO(austin): Define remote types
// Define packages
for pkgName, defs := range packages {
fields := make([]eval.StructField, len(defs))
vals := make([]eval.Value, len(defs))
i := 0
for name, def := range defs {
fields[i].Name = name
fields[i].Type = def.t
vals[i] = def.v
i++
}
pkgType := eval.NewStructType(fields)
pkgVal := remotePackage{vals}
err := w.DefineConst(pkgName, pkgType, pkgVal)
if err != nil {
log.Printf("while defining package %s: %v", pkgName, err)
}
}
return nil
}
// typeOfSym returns the type associated with a symbol. If the symbol
// has no type, returns nil.
func (p *Process) typeOfSym(s *gosym.Sym) (*remoteType, os.Error) {
if s.GoType == 0 {
return nil, nil
}
addr := proc.Word(s.GoType)
var rt *remoteType
err := try(func(a aborter) { rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct)) })
if err != nil {
return nil, err
}
return rt, nil
}
// makeFrameType constructs a struct type for the frame of a function.
// The offsets in this struct type are such that the struct can be
// instantiated at this function's frame pointer.
func (p *Process) makeFrameType(s *gosym.Func) (*remoteType, os.Error) {
n := len(s.Params) + len(s.Locals)
fields := make([]eval.StructField, n)
layout := make([]remoteStructField, n)
i := 0
// TODO(austin): There can be multiple locals/parameters with
// the same name. We probably need liveness information to do
// anything about this. Once we have that, perhaps we give
// such fields interface{} type? Or perhaps we disambiguate
// the names with numbers. Disambiguation is annoying for
// things like "i", where there's an obvious right answer.
for _, param := range s.Params {
rt, err := p.typeOfSym(param)
if err != nil {
return nil, err
}
if rt == nil {
//fmt.Printf(" (no type)\n");
continue
}
// TODO(austin): Why do local variables carry their
// package name?
fields[i].Name = param.BaseName()
fields[i].Type = rt.Type
// Parameters have positive offsets from FP
layout[i].offset = int(param.Value)
layout[i].fieldType = rt
i++
}
for _, local := range s.Locals {
rt, err := p.typeOfSym(local)
if err != nil {
return nil, err
}
if rt == nil {
continue
}
fields[i].Name = local.BaseName()
fields[i].Type = rt.Type
// Locals have negative offsets from FP - PtrSize
layout[i].offset = -int(local.Value) - p.PtrSize()
layout[i].fieldType = rt
i++
}
fields = fields[0:i]
layout = layout[0:i]
t := eval.NewStructType(fields)
mk := func(r remote) eval.Value { return remoteStruct{r, layout} }
return &remoteType{t, 0, 0, mk}, nil
}

View File

@ -69,11 +69,6 @@ gomake clean
gotest
) || exit $?
(xcd pkg/exp/ogle
gomake clean
time gomake ogle
) || exit $?
(xcd ../doc/progs
time ./run
) || exit $?