mirror of
https://github.com/golang/go.git
synced 2024-09-30 23:07:22 +00:00
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:
parent
050d839df0
commit
4c986d86b1
@ -79,7 +79,6 @@ DIRS=\
|
||||
encoding/pem\
|
||||
exec\
|
||||
exp/datafmt\
|
||||
exp/eval\
|
||||
exp/gui\
|
||||
exp/gui/x11\
|
||||
exp/regexp/syntax\
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
@ -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) }
|
@ -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) }
|
@ -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)
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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) }
|
@ -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
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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 $@ $<
|
@ -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
|
||||
}
|
@ -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}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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() }
|
@ -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()
|
||||
}
|
@ -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]))
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -69,11 +69,6 @@ gomake clean
|
||||
gotest
|
||||
) || exit $?
|
||||
|
||||
(xcd pkg/exp/ogle
|
||||
gomake clean
|
||||
time gomake ogle
|
||||
) || exit $?
|
||||
|
||||
(xcd ../doc/progs
|
||||
time ./run
|
||||
) || exit $?
|
||||
|
Loading…
Reference in New Issue
Block a user