Commit 4c986d86 authored by Robert Griesemer's avatar Robert Griesemer

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
parent 050d839d
...@@ -79,7 +79,6 @@ DIRS=\ ...@@ -79,7 +79,6 @@ DIRS=\
encoding/pem\ encoding/pem\
exec\ exec\
exp/datafmt\ exp/datafmt\
exp/eval\
exp/gui\ exp/gui\
exp/gui/x11\ exp/gui/x11\
exp/regexp/syntax\ exp/regexp/syntax\
......
# 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
// 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)
}
// 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)
}
// 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
}
// 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
}
// 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"
"go/ast"
"go/token"
"log"
"strconv"
"strings"
"os"
)
var (
idealZero = big.NewInt(0)
idealOne = big.NewInt(1)
)
// An expr is the result of compiling an expression. It stores the
// type of the expression and its evaluator function.
type expr struct {
*exprInfo
t Type
// Evaluate this node as the given type.
eval interface{}
// Map index expressions permit special forms of assignment,
// for which we need to know the Map and key.
evalMapValue func(t *Thread) (Map, interface{})
// Evaluate to the "address of" this value; that is, the
// settable Value object. nil for expressions whose address
// cannot be taken.
evalAddr func(t *Thread) Value
// Execute this expression as a statement. Only expressions
// that are valid expression statements should set this.
exec func(t *Thread)
// If this expression is a type, this is its compiled type.
// This is only permitted in the function position of a call
// expression. In this case, t should be nil.
valType Type
// A short string describing this expression for error
// messages.
desc string
}
// exprInfo stores information needed to compile any expression node.
// Each expr also stores its exprInfo so further expressions can be
// compiled from it.
type exprInfo struct {
*compiler
pos token.Pos
}
func (a *exprInfo) newExpr(t Type, desc string) *expr {
return &expr{exprInfo: a, t: t, desc: desc}
}
func (a *exprInfo) diag(format string, args ...interface{}) {
a.diagAt(a.pos, format, args...)
}
func (a *exprInfo) diagOpType(op token.Token, vt Type) {
a.diag("illegal operand type for '%v' operator\n\t%v", op, vt)
}
func (a *exprInfo) diagOpTypes(op token.Token, lt Type, rt Type) {
a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt)
}
/*
* Common expression manipulations
*/
// a.convertTo(t) converts the value of the analyzed expression a,
// which must be a constant, ideal number, to a new analyzed
// expression with a constant value of type t.
//
// TODO(austin) Rename to resolveIdeal or something?
func (a *expr) convertTo(t Type) *expr {
if !a.t.isIdeal() {
log.Panicf("attempted to convert from %v, expected ideal", a.t)
}
var rat *big.Rat
// XXX(Spec) The spec says "It is erroneous".
//
// It is an error to assign a value with a non-zero fractional
// part to an integer, or if the assignment would overflow or
// underflow, or in general if the value cannot be represented
// by the type of the variable.
switch a.t {
case IdealFloatType:
rat = a.asIdealFloat()()
if t.isInteger() && !rat.IsInt() {
a.diag("constant %v truncated to integer", rat.FloatString(6))
return nil
}
case IdealIntType:
i := a.asIdealInt()()
rat = new(big.Rat).SetInt(i)
default:
log.Panicf("unexpected ideal type %v", a.t)
}
// Check bounds
if t, ok := t.lit().(BoundedType); ok {
if rat.Cmp(t.minVal()) < 0 {
a.diag("constant %v underflows %v", rat.FloatString(6), t)
return nil
}
if rat.Cmp(t.maxVal()) > 0 {
a.diag("constant %v overflows %v", rat.FloatString(6), t)
return nil
}
}
// Convert rat to type t.
res := a.newExpr(t, a.desc)
switch t := t.lit().(type) {
case *uintType:
n, d := rat.Num(), rat.Denom()
f := new(big.Int).Quo(n, d)
f = f.Abs(f)
v := uint64(f.Int64())
res.eval = func(*Thread) uint64 { return v }
case *intType:
n, d := rat.Num(), rat.Denom()
f := new(big.Int).Quo(n, d)
v := f.Int64()
res.eval = func(*Thread) int64 { return v }
case *idealIntType:
n, d := rat.Num(), rat.Denom()
f := new(big.Int).Quo(n, d)
res.eval = func() *big.Int { return f }
case *floatType:
n, d := rat.Num(), rat.Denom()
v := float64(n.Int64()) / float64(d.Int64())
res.eval = func(*Thread) float64 { return v }
case *idealFloatType:
res.eval = func() *big.Rat { return rat }
default:
log.Panicf("cannot convert to type %T", t)
}
return res
}
// convertToInt converts this expression to an integer, if possible,
// or produces an error if not. This accepts ideal ints, uints, and
// ints. If max is not -1, produces an error if possible if the value
// exceeds max. If negErr is not "", produces an error if possible if
// the value is negative.
func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr {
switch a.t.lit().(type) {
case *idealIntType:
val := a.asIdealInt()()
if negErr != "" && val.Sign() < 0 {
a.diag("negative %s: %s", negErr, val)
return nil
}
bound := max
if negErr == "slice" {
bound++
}
if max != -1 && val.Cmp(big.NewInt(bound)) >= 0 {
a.diag("index %s exceeds length %d", val, max)
return nil
}
return a.convertTo(IntType)
case *uintType:
// Convert to int
na := a.newExpr(IntType, a.desc)
af := a.asUint()
na.eval = func(t *Thread) int64 { return int64(af(t)) }
return na
case *intType:
// Good as is
return a
}
a.diag("illegal operand type for %s\n\t%v", errOp, a.t)
return nil
}
// derefArray returns an expression of array type if the given
// expression is a *array type. Otherwise, returns the given
// expression.
func (a *expr) derefArray() *expr {
if pt, ok := a.t.lit().(*PtrType); ok {
if _, ok := pt.Elem.lit().(*ArrayType); ok {
deref := a.compileStarExpr(a)
if deref == nil {
log.Panicf("failed to dereference *array")
}
return deref
}
}
return a
}
/*
* Assignments
*/
// An assignCompiler compiles assignment operations. Anything other
// than short declarations should use the compileAssign wrapper.
//
// There are three valid types of assignment:
// 1) T = T
// Assigning a single expression with single-valued type to a
// single-valued type.
// 2) MT = T, T, ...
// Assigning multiple expressions with single-valued types to a
// multi-valued type.
// 3) MT = MT
// Assigning a single expression with multi-valued type to a
// multi-valued type.
type assignCompiler struct {
*compiler
pos token.Pos
// The RHS expressions. This may include nil's for
// expressions that failed to compile.
rs []*expr
// The (possibly unary) MultiType of the RHS.
rmt *MultiType
// Whether this is an unpack assignment (case 3).
isUnpack bool
// Whether map special assignment forms are allowed.
allowMap bool
// Whether this is a "r, ok = a[x]" assignment.
isMapUnpack bool
// The operation name to use in error messages, such as
// "assignment" or "function call".
errOp string
// The name to use for positions in error messages, such as
// "argument".
errPosName string
}
// Type check the RHS of an assignment, returning a new assignCompiler
// and indicating if the type check succeeded. This always returns an
// assignCompiler with rmt set, but if type checking fails, slots in
// the MultiType may be nil. If rs contains nil's, type checking will
// fail and these expressions given a nil type.
func (a *compiler) checkAssign(pos token.Pos, rs []*expr, errOp, errPosName string) (*assignCompiler, bool) {
c := &assignCompiler{
compiler: a,
pos: pos,
rs: rs,
errOp: errOp,
errPosName: errPosName,
}
// Is this an unpack?
if len(rs) == 1 && rs[0] != nil {
if rmt, isUnpack := rs[0].t.(*MultiType); isUnpack {
c.rmt = rmt
c.isUnpack = true
return c, true
}
}
// Create MultiType for RHS and check that all RHS expressions
// are single-valued.
rts := make([]Type, len(rs))
ok := true
for i, r := range rs {
if r == nil {
ok = false
continue
}
if _, isMT := r.t.(*MultiType); isMT {
r.diag("multi-valued expression not allowed in %s", errOp)
ok = false
continue
}
rts[i] = r.t
}
c.rmt = NewMultiType(rts)
return c, ok
}
func (a *assignCompiler) allowMapForms(nls int) {
a.allowMap = true
// Update unpacking info if this is r, ok = a[x]
if nls == 2 && len(a.rs) == 1 && a.rs[0] != nil && a.rs[0].evalMapValue != nil {
a.isUnpack = true
a.rmt = NewMultiType([]Type{a.rs[0].t, BoolType})
a.isMapUnpack = true
}
}
// compile type checks and compiles an assignment operation, returning
// a function that expects an l-value and the frame in which to
// evaluate the RHS expressions. The l-value must have exactly the
// type given by lt. Returns nil if type checking fails.
func (a *assignCompiler) compile(b *block, lt Type) func(Value, *Thread) {
lmt, isMT := lt.(*MultiType)
rmt, isUnpack := a.rmt, a.isUnpack
// Create unary MultiType for single LHS
if !isMT {
lmt = NewMultiType([]Type{lt})
}
// Check that the assignment count matches
lcount := len(lmt.Elems)
rcount := len(rmt.Elems)
if lcount != rcount {
msg := "not enough"
pos := a.pos
if rcount > lcount {
msg = "too many"
if lcount > 0 {
pos = a.rs[lcount-1].pos
}
}
a.diagAt(pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt)
return nil
}
bad := false
// If this is an unpack, create a temporary to store the
// multi-value and replace the RHS with expressions to pull
// out values from the temporary. Technically, this is only
// necessary when we need to perform assignment conversions.
var effect func(*Thread)
if isUnpack {
// This leaks a slot, but is definitely safe.
temp := b.DefineTemp(a.rmt)
tempIdx := temp.Index
if tempIdx < 0 {
panic(fmt.Sprintln("tempidx", tempIdx))
}
if a.isMapUnpack {
rf := a.rs[0].evalMapValue
vt := a.rmt.Elems[0]
effect = func(t *Thread) {
m, k := rf(t)
v := m.Elem(t, k)
found := boolV(true)
if v == nil {
found = boolV(false)
v = vt.Zero()
}
t.f.Vars[tempIdx] = multiV([]Value{v, &found})
}
} else {
rf := a.rs[0].asMulti()
effect = func(t *Thread) { t.f.Vars[tempIdx] = multiV(rf(t)) }
}
orig := a.rs[0]
a.rs = make([]*expr, len(a.rmt.Elems))
for i, t := range a.rmt.Elems {
if t.isIdeal() {
log.Panicf("Right side of unpack contains ideal: %s", rmt)
}
a.rs[i] = orig.newExpr(t, orig.desc)
index := i
a.rs[i].genValue(func(t *Thread) Value { return t.f.Vars[tempIdx].(multiV)[index] })
}
}
// Now len(a.rs) == len(a.rmt) and we've reduced any unpacking
// to multi-assignment.
// TODO(austin) Deal with assignment special cases.
// Values of any type may always be assigned to variables of
// compatible static type.
for i, lt := range lmt.Elems {
rt := rmt.Elems[i]
// When [an ideal is] (used in an expression) assigned
// to a variable or typed constant, the destination
// must be able to represent the assigned value.
if rt.isIdeal() {
a.rs[i] = a.rs[i].convertTo(lmt.Elems[i])
if a.rs[i] == nil {
bad = true
continue
}
rt = a.rs[i].t
}
// A pointer p to an array can be assigned to a slice
// variable v with compatible element type if the type
// of p or v is unnamed.
if rpt, ok := rt.lit().(*PtrType); ok {
if at, ok := rpt.Elem.lit().(*ArrayType); ok {
if lst, ok := lt.lit().(*SliceType); ok {
if lst.Elem.compat(at.Elem, false) && (rt.lit() == Type(rt) || lt.lit() == Type(lt)) {
rf := a.rs[i].asPtr()
a.rs[i] = a.rs[i].newExpr(lt, a.rs[i].desc)
len := at.Len
a.rs[i].eval = func(t *Thread) Slice { return Slice{rf(t).(ArrayValue), len, len} }
rt = a.rs[i].t
}
}
}
}
if !lt.compat(rt, false) {
if len(a.rs) == 1 {
a.rs[0].diag("illegal operand types for %s\n\t%v\n\t%v", a.errOp, lt, rt)
} else {
a.rs[i].diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", a.errPosName, i+1, a.errOp, lt, rt)
}
bad = true
}
}
if bad {
return nil
}
// Compile
if !isMT {
// Case 1
return genAssign(lt, a.rs[0])
}
// Case 2 or 3
as := make([]func(lv Value, t *Thread), len(a.rs))
for i, r := range a.rs {
as[i] = genAssign(lmt.Elems[i], r)
}
return func(lv Value, t *Thread) {
if effect != nil {
effect(t)
}
lmv := lv.(multiV)
for i, a := range as {
a(lmv[i], t)
}
}
}
// compileAssign compiles an assignment operation without the full
// generality of an assignCompiler. See assignCompiler for a
// description of the arguments.
func (a *compiler) compileAssign(pos token.Pos, b *block, lt Type, rs []*expr, errOp, errPosName string) func(Value, *Thread) {
ac, ok := a.checkAssign(pos, rs, errOp, errPosName)
if !ok {
return nil
}
return ac.compile(b, lt)
}
/*
* Expression compiler
*/
// An exprCompiler stores information used throughout the compilation
// of a single expression. It does not embed funcCompiler because
// expressions can appear at top level.
type exprCompiler struct {
*compiler
// The block this expression is being compiled in.
block *block
// Whether this expression is used in a constant context.
constant bool
}
// compile compiles an expression AST. callCtx should be true if this
// AST is in the function position of a function call node; it allows
// the returned expression to be a type or a built-in function (which
// otherwise result in errors).
func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
ei := &exprInfo{a.compiler, x.Pos()}
switch x := x.(type) {
// Literals
case *ast.BasicLit:
switch x.Kind {
case token.INT:
return ei.compileIntLit(string(x.Value))
case token.FLOAT:
return ei.compileFloatLit(string(x.Value))
case token.CHAR:
return ei.compileCharLit(string(x.Value))
case token.STRING:
return ei.compileStringLit(string(x.Value))
default:
log.Panicf("unexpected basic literal type %v", x.Kind)
}
case *ast.CompositeLit:
goto notimpl
case *ast.FuncLit:
decl := ei.compileFuncType(a.block, x.Type)
if decl == nil {
// TODO(austin) Try compiling the body,
// perhaps with dummy argument definitions
return nil
}
fn := ei.compileFunc(a.block, decl, x.Body)
if fn == nil {
return nil
}
if a.constant {
a.diagAt(x.Pos(), "function literal used in constant expression")
return nil
}
return ei.compileFuncLit(decl, fn)
// Types
case *ast.ArrayType:
// TODO(austin) Use a multi-type case
goto typeexpr
case *ast.ChanType:
goto typeexpr
case *ast.Ellipsis:
goto typeexpr
case *ast.FuncType:
goto typeexpr
case *ast.InterfaceType:
goto typeexpr
case *ast.MapType:
goto typeexpr
// Remaining expressions
case *ast.BadExpr:
// Error already reported by parser
a.silentErrors++
return nil
case *ast.BinaryExpr:
l, r := a.compile(x.X, false), a.compile(x.Y, false)
if l == nil || r == nil {
return nil
}
return ei.compileBinaryExpr(x.Op, l, r)
case *ast.CallExpr:
l := a.compile(x.Fun, true)
args := make([]*expr, len(x.Args))
bad := false
for i, arg := range x.Args {
if i == 0 && l != nil && (l.t == Type(makeType) || l.t == Type(newType)) {
argei := &exprInfo{a.compiler, arg.Pos()}
args[i] = argei.exprFromType(a.compileType(a.block, arg))
} else {
args[i] = a.compile(arg, false)
}
if args[i] == nil {
bad = true
}
}
if bad || l == nil {
return nil
}
if a.constant {
a.diagAt(x.Pos(), "function call in constant context")
return nil
}
if l.valType != nil {
a.diagAt(x.Pos(), "type conversions not implemented")
return nil
} else if ft, ok := l.t.(*FuncType); ok && ft.builtin != "" {
return ei.compileBuiltinCallExpr(a.block, ft, args)
} else {
return ei.compileCallExpr(a.block, l, args)
}
case *ast.Ident:
return ei.compileIdent(a.block, a.constant, callCtx, x.Name)
case *ast.IndexExpr:
l, r := a.compile(x.X, false), a.compile(x.Index, false)
if l == nil || r == nil {
return nil
}
return ei.compileIndexExpr(l, r)
case *ast.SliceExpr:
var lo, hi *expr
arr := a.compile(x.X, false)
if x.Low == nil {
// beginning was omitted, so we need to provide it
ei := &exprInfo{a.compiler, x.Pos()}
lo = ei.compileIntLit("0")
} else {
lo = a.compile(x.Low, false)
}
if x.High == nil {
// End was omitted, so we need to compute len(x.X)
ei := &exprInfo{a.compiler, x.Pos()}
hi = ei.compileBuiltinCallExpr(a.block, lenType, []*expr{arr})
} else {
hi = a.compile(x.High, false)
}
if arr == nil || lo == nil || hi == nil {
return nil
}
return ei.compileSliceExpr(arr, lo, hi)
case *ast.KeyValueExpr:
goto notimpl
case *ast.ParenExpr:
return a.compile(x.X, callCtx)
case *ast.SelectorExpr:
v := a.compile(x.X, false)
if v == nil {
return nil
}
return ei.compileSelectorExpr(v, x.Sel.Name)
case *ast.StarExpr:
// We pass down our call context because this could be
// a pointer type (and thus a type conversion)
v := a.compile(x.X, callCtx)
if v == nil {
return nil
}
if v.valType != nil {
// Turns out this was a pointer type, not a dereference
return ei.exprFromType(NewPtrType(v.valType))
}
return ei.compileStarExpr(v)
case *ast.StructType:
goto notimpl
case *ast.TypeAssertExpr:
goto notimpl
case *ast.UnaryExpr:
v := a.compile(x.X, false)
if v == nil {
return nil
}
return ei.compileUnaryExpr(x.Op, v)
}
log.Panicf("unexpected ast node type %T", x)
panic("unreachable")
typeexpr:
if !callCtx {
a.diagAt(x.Pos(), "type used as expression")
return nil
}
return ei.exprFromType(a.compileType(a.block, x))
notimpl:
a.diagAt(x.Pos(), "%T expression node not implemented", x)
return nil
}
func (a *exprInfo) exprFromType(t Type) *expr {
if t == nil {
return nil
}
expr := a.newExpr(nil, "type")
expr.valType = t
return expr
}
func (a *exprInfo) compileIdent(b *block, constant bool, callCtx bool, name string) *expr {
bl, level, def := b.Lookup(name)
if def == nil {
a.diag("%s: undefined", name)
return nil
}
switch def := def.(type) {
case *Constant:
expr := a.newExpr(def.Type, "constant")
if ft, ok := def.Type.(*FuncType); ok && ft.builtin != "" {
// XXX(Spec) I don't think anything says that
// built-in functions can't be used as values.
if !callCtx {
a.diag("built-in function %s cannot be used as a value", ft.builtin)
return nil
}
// Otherwise, we leave the evaluators empty
// because this is handled specially
} else {
expr.genConstant(def.Value)
}
return expr
case *Variable:
if constant {
a.diag("variable %s used in constant expression", name)
return nil
}
if bl.global {
return a.compileGlobalVariable(def)
}
return a.compileVariable(level, def)
case Type:
if callCtx {
return a.exprFromType(def)
}
a.diag("type %v used as expression", name)
return nil
}
log.Panicf("name %s has unknown type %T", name, def)
panic("unreachable")
}
func (a *exprInfo) compileVariable(level int, v *Variable) *expr {
if v.Type == nil {
// Placeholder definition from an earlier error
a.silentErrors++
return nil
}
expr := a.newExpr(v.Type, "variable")
expr.genIdentOp(level, v.Index)
return expr
}
func (a *exprInfo) compileGlobalVariable(v *Variable) *expr {
if v.Type == nil {
// Placeholder definition from an earlier error
a.silentErrors++
return nil
}
if v.Init == nil {
v.Init = v.Type.Zero()
}
expr := a.newExpr(v.Type, "variable")
val := v.Init
expr.genValue(func(t *Thread) Value { return val })
return expr
}
func (a *exprInfo) compileIdealInt(i *big.Int, desc string) *expr {
expr := a.newExpr(IdealIntType, desc)
expr.eval = func() *big.Int { return i }
return expr
}
func (a *exprInfo) compileIntLit(lit string) *expr {
i, _ := new(big.Int).SetString(lit, 0)
return a.compileIdealInt(i, "integer literal")
}
func (a *exprInfo) compileCharLit(lit string) *expr {
if lit[0] != '\'' {
// Caught by parser
a.silentErrors++
return nil
}
v, _, tail, err := strconv.UnquoteChar(lit[1:], '\'')
if err != nil || tail != "'" {
// Caught by parser
a.silentErrors++
return nil
}
return a.compileIdealInt(big.NewInt(int64(v)), "character literal")
}
func (a *exprInfo) compileFloatLit(lit string) *expr {
f, ok := new(big.Rat).SetString(lit)
if !ok {
log.Panicf("malformed float literal %s at %v passed parser", lit, a.pos)
}
expr := a.newExpr(IdealFloatType, "float literal")
expr.eval = func() *big.Rat { return f }
return expr
}
func (a *exprInfo) compileString(s string) *expr {
// Ideal strings don't have a named type but they are
// compatible with type string.
// TODO(austin) Use unnamed string type.
expr := a.newExpr(StringType, "string literal")
expr.eval = func(*Thread) string { return s }
return expr
}
func (a *exprInfo) compileStringLit(lit string) *expr {
s, err := strconv.Unquote(lit)
if err != nil {
a.diag("illegal string literal, %v", err)
return nil
}
return a.compileString(s)
}
func (a *exprInfo) compileStringList(list []*expr) *expr {
ss := make([]string, len(list))
for i, s := range list {
ss[i] = s.asString()(nil)
}
return a.compileString(strings.Join(ss, ""))
}
func (a *exprInfo) compileFuncLit(decl *FuncDecl, fn func(*Thread) Func) *expr {
expr := a.newExpr(decl.Type, "function literal")
expr.eval = fn
return expr
}
func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr {
// mark marks a field that matches the selector name. It
// tracks the best depth found so far and whether more than
// one field has been found at that depth.
bestDepth := -1
ambig := false
amberr := ""
mark := func(depth int, pathName string) {
switch {
case bestDepth == -1 || depth < bestDepth:
bestDepth = depth
ambig = false
amberr = ""
case depth == bestDepth:
ambig = true
default:
log.Panicf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth)
}
amberr += "\n\t" + pathName[1:]
}
visited := make(map[Type]bool)
// find recursively searches for the named field, starting at
// type t. If it finds the named field, it returns a function
// which takes an expr that represents a value of type 't' and
// returns an expr that retrieves the named field. We delay
// expr construction to avoid producing lots of useless expr's
// as we search.
//
// TODO(austin) Now that the expression compiler works on
// semantic values instead of AST's, there should be a much
// better way of doing this.
var find func(Type, int, string) func(*expr) *expr
find = func(t Type, depth int, pathName string) func(*expr) *expr {
// Don't bother looking if we've found something shallower
if bestDepth != -1 && bestDepth < depth {
return nil
}
// Don't check the same type twice and avoid loops
if visited[t] {
return nil
}
visited[t] = true
// Implicit dereference
deref := false
if ti, ok := t.(*PtrType); ok {
deref = true
t = ti.Elem
}
// If it's a named type, look for methods
if ti, ok := t.(*NamedType); ok {
_, ok := ti.methods[name]
if ok {
mark(depth, pathName+"."+name)
log.Panic("Methods not implemented")
}
t = ti.Def
}
// If it's a struct type, check fields and embedded types
var builder func(*expr) *expr
if t, ok := t.(*StructType); ok {
for i, f := range t.Elems {
var sub func(*expr) *expr
switch {
case f.Name == name:
mark(depth, pathName+"."+name)
sub = func(e *expr) *expr { return e }
case f.Anonymous:
sub = find(f.Type, depth+1, pathName+"."+f.Name)
if sub == nil {
continue
}
default:
continue
}
// We found something. Create a
// builder for accessing this field.
ft := f.Type
index := i
builder = func(parent *expr) *expr {
if deref {
parent = a.compileStarExpr(parent)
}
expr := a.newExpr(ft, "selector expression")
pf := parent.asStruct()
evalAddr := func(t *Thread) Value { return pf(t).Field(t, index) }
expr.genValue(evalAddr)
return sub(expr)
}
}
}
return builder
}
builder := find(v.t, 0, "")
if builder == nil {
a.diag("type %v has no field or method %s", v.t, name)
return nil
}
if ambig {
a.diag("field %s is ambiguous in type %v%s", name, v.t, amberr)
return nil
}
return builder(v)
}
func (a *exprInfo) compileSliceExpr(arr, lo, hi *expr) *expr {
// Type check object
arr = arr.derefArray()
var at Type
var maxIndex int64 = -1
switch lt := arr.t.lit().(type) {
case *ArrayType:
at = NewSliceType(lt.Elem)
maxIndex = lt.Len
case *SliceType:
at = lt
case *stringType:
at = lt
default:
a.diag("cannot slice %v", arr.t)
return nil
}
// Type check index and convert to int
// XXX(Spec) It's unclear if ideal floats with no
// fractional part are allowed here. 6g allows it. I
// believe that's wrong.
lo = lo.convertToInt(maxIndex, "slice", "slice")
hi = hi.convertToInt(maxIndex, "slice", "slice")
if lo == nil || hi == nil {
return nil
}
expr := a.newExpr(at, "slice expression")
// Compile
lof := lo.asInt()
hif := hi.asInt()
switch lt := arr.t.lit().(type) {
case *ArrayType:
arrf := arr.asArray()
bound := lt.Len
expr.eval = func(t *Thread) Slice {
arr, lo, hi := arrf(t), lof(t), hif(t)
if lo > hi || hi > bound || lo < 0 {
t.Abort(SliceError{lo, hi, bound})
}
return Slice{arr.Sub(lo, bound-lo), hi - lo, bound - lo}
}
case *SliceType:
arrf := arr.asSlice()
expr.eval = func(t *Thread) Slice {
arr, lo, hi := arrf(t), lof(t), hif(t)
if lo > hi || hi > arr.Cap || lo < 0 {
t.Abort(SliceError{lo, hi, arr.Cap})
}
return Slice{arr.Base.Sub(lo, arr.Cap-lo), hi - lo, arr.Cap - lo}
}
case *stringType:
arrf := arr.asString()
// TODO(austin) This pulls over the whole string in a
// remote setting, instead of creating a substring backed
// by remote memory.
expr.eval = func(t *Thread) string {
arr, lo, hi := arrf(t), lof(t), hif(t)
if lo > hi || hi > int64(len(arr)) || lo < 0 {
t.Abort(SliceError{lo, hi, int64(len(arr))})
}
return arr[lo:hi]
}
default:
log.Panicf("unexpected left operand type %T", arr.t.lit())
}
return expr
}
func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// Type check object
l = l.derefArray()
var at Type
intIndex := false
var maxIndex int64 = -1
switch lt := l.t.lit().(type) {
case *ArrayType:
at = lt.Elem
intIndex = true
maxIndex = lt.Len
case *SliceType:
at = lt.Elem
intIndex = true
case *stringType:
at = Uint8Type
intIndex = true
case *MapType:
at = lt.Elem
if r.t.isIdeal() {
r = r.convertTo(lt.Key)
if r == nil {
return nil
}
}
if !lt.Key.compat(r.t, false) {
a.diag("cannot use %s as index into %s", r.t, lt)
return nil
}
default:
a.diag("cannot index into %v", l.t)
return nil
}
// Type check index and convert to int if necessary
if intIndex {
// XXX(Spec) It's unclear if ideal floats with no
// fractional part are allowed here. 6g allows it. I
// believe that's wrong.
r = r.convertToInt(maxIndex, "index", "index")
if r == nil {
return nil
}
}
expr := a.newExpr(at, "index expression")
// Compile
switch lt := l.t.lit().(type) {
case *ArrayType:
lf := l.asArray()
rf := r.asInt()
bound := lt.Len
expr.genValue(func(t *Thread) Value {
l, r := lf(t), rf(t)
if r < 0 || r >= bound {
t.Abort(IndexError{r, bound})
}
return l.Elem(t, r)
})
case *SliceType:
lf := l.asSlice()
rf := r.asInt()
expr.genValue(func(t *Thread) Value {
l, r := lf(t), rf(t)
if l.Base == nil {
t.Abort(NilPointerError{})
}
if r < 0 || r >= l.Len {
t.Abort(IndexError{r, l.Len})
}
return l.Base.Elem(t, r)
})
case *stringType:
lf := l.asString()
rf := r.asInt()
// TODO(austin) This pulls over the whole string in a
// remote setting, instead of just the one character.
expr.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
if r < 0 || r >= int64(len(l)) {
t.Abort(IndexError{r, int64(len(l))})
}
return uint64(l[r])
}
case *MapType:
lf := l.asMap()
rf := r.asInterface()
expr.genValue(func(t *Thread) Value {
m := lf(t)
k := rf(t)
if m == nil {
t.Abort(NilPointerError{})
}
e := m.Elem(t, k)
if e == nil {
t.Abort(KeyError{k})
}
return e
})
// genValue makes things addressable, but map values
// aren't addressable.
expr.evalAddr = nil
expr.evalMapValue = func(t *Thread) (Map, interface{}) {
// TODO(austin) Key check? nil check?
return lf(t), rf(t)
}
default:
log.Panicf("unexpected left operand type %T", l.t.lit())
}
return expr
}
func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr {
// TODO(austin) Variadic functions.
// Type check
// XXX(Spec) Calling a named function type is okay. I really
// think there needs to be a general discussion of named
// types. A named type creates a new, distinct type, but the
// type of that type is still whatever it's defined to. Thus,
// in "type Foo int", Foo is still an integer type and in
// "type Foo func()", Foo is a function type.
lt, ok := l.t.lit().(*FuncType)
if !ok {
a.diag("cannot call non-function type %v", l.t)
return nil
}
// The arguments must be single-valued expressions assignment
// compatible with the parameters of F.
//
// XXX(Spec) The spec is wrong. It can also be a single
// multi-valued expression.
nin := len(lt.In)
assign := a.compileAssign(a.pos, b, NewMultiType(lt.In), as, "function call", "argument")
if assign == nil {
return nil
}
var t Type
nout := len(lt.Out)
switch nout {
case 0:
t = EmptyType
case 1:
t = lt.Out[0]
default:
t = NewMultiType(lt.Out)
}
expr := a.newExpr(t, "function call")
// Gather argument and out types to initialize frame variables
vts := make([]Type, nin+nout)
copy(vts, lt.In)
copy(vts[nin:], lt.Out)
// Compile
lf := l.asFunc()
call := func(t *Thread) []Value {
fun := lf(t)
fr := fun.NewFrame()
for i, t := range vts {
fr.Vars[i] = t.Zero()
}
assign(multiV(fr.Vars[0:nin]), t)
oldf := t.f
t.f = fr
fun.Call(t)
t.f = oldf
return fr.Vars[nin : nin+nout]
}
expr.genFuncCall(call)
return expr
}
func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *expr {
checkCount := func(min, max int) bool {
if len(as) < min {
a.diag("not enough arguments to %s", ft.builtin)
return false
} else if len(as) > max {
a.diag("too many arguments to %s", ft.builtin)
return false
}
return true
}
switch ft {
case capType:
if !checkCount(1, 1) {
return nil
}
arg := as[0].derefArray()
expr := a.newExpr(IntType, "function call")
switch t := arg.t.lit().(type) {
case *ArrayType:
// TODO(austin) It would be nice if this could
// be a constant int.
v := t.Len
expr.eval = func(t *Thread) int64 { return v }
case *SliceType:
vf := arg.asSlice()
expr.eval = func(t *Thread) int64 { return vf(t).Cap }
//case *ChanType:
default:
a.diag("illegal argument type for cap function\n\t%v", arg.t)
return nil
}
return expr
case copyType:
if !checkCount(2, 2) {
return nil
}
src := as[1]
dst := as[0]
if src.t != dst.t {
a.diag("arguments to built-in function 'copy' must have same type\nsrc: %s\ndst: %s\n", src.t, dst.t)
return nil
}
if _, ok := src.t.lit().(*SliceType); !ok {
a.diag("src argument to 'copy' must be a slice (got: %s)", src.t)
return nil
}
if _, ok := dst.t.lit().(*SliceType); !ok {
a.diag("dst argument to 'copy' must be a slice (got: %s)", dst.t)
return nil
}
expr := a.newExpr(IntType, "function call")
srcf := src.asSlice()
dstf := dst.asSlice()
expr.eval = func(t *Thread) int64 {
src, dst := srcf(t), dstf(t)
nelems := src.Len
if nelems > dst.Len {
nelems = dst.Len
}
dst.Base.Sub(0, nelems).Assign(t, src.Base.Sub(0, nelems))
return nelems
}
return expr
case lenType:
if !checkCount(1, 1) {
return nil
}
arg := as[0].derefArray()
expr := a.newExpr(IntType, "function call")
switch t := arg.t.lit().(type) {
case *stringType:
vf := arg.asString()
expr.eval = func(t *Thread) int64 { return int64(len(vf(t))) }
case *ArrayType:
// TODO(austin) It would be nice if this could
// be a constant int.
v := t.Len
expr.eval = func(t *Thread) int64 { return v }
case *SliceType:
vf := arg.asSlice()
expr.eval = func(t *Thread) int64 { return vf(t).Len }
case *MapType:
vf := arg.asMap()
expr.eval = func(t *Thread) int64 {
// XXX(Spec) What's the len of an
// uninitialized map?
m := vf(t)
if m == nil {
return 0
}
return m.Len(t)
}
//case *ChanType:
default:
a.diag("illegal argument type for len function\n\t%v", arg.t)
return nil
}
return expr
case makeType:
if !checkCount(1, 3) {
return nil
}
// XXX(Spec) What are the types of the
// arguments? Do they have to be ints? 6g
// accepts any integral type.
var lenexpr, capexpr *expr
var lenf, capf func(*Thread) int64
if len(as) > 1 {
lenexpr = as[1].convertToInt(-1, "length", "make function")
if lenexpr == nil {
return nil
}
lenf = lenexpr.asInt()
}
if len(as) > 2 {
capexpr = as[2].convertToInt(-1, "capacity", "make function")
if capexpr == nil {
return nil
}
capf = capexpr.asInt()
}
switch t := as[0].valType.lit().(type) {
case *SliceType:
// A new, initialized slice value for a given
// element type T is made using the built-in
// function make, which takes a slice type and
// parameters specifying the length and
// optionally the capacity.
if !checkCount(2, 3) {
return nil
}
et := t.Elem
expr := a.newExpr(t, "function call")
expr.eval = func(t *Thread) Slice {
l := lenf(t)
// XXX(Spec) What if len or cap is
// negative? The runtime panics.
if l < 0 {
t.Abort(NegativeLengthError{l})
}
c := l
if capf != nil {
c = capf(t)
if c < 0 {
t.Abort(NegativeCapacityError{c})
}
// XXX(Spec) What happens if
// len > cap? The runtime
// sets cap to len.
if l > c {
c = l
}
}
base := arrayV(make([]Value, c))
for i := int64(0); i < c; i++ {
base[i] = et.Zero()
}
return Slice{&base, l, c}
}
return expr
case *MapType:
// A new, empty map value is made using the
// built-in function make, which takes the map
// type and an optional capacity hint as
// arguments.
if !checkCount(1, 2) {
return nil
}
expr := a.newExpr(t, "function call")
expr.eval = func(t *Thread) Map {
if lenf == nil {
return make(evalMap)
}
l := lenf(t)
return make(evalMap, l)
}
return expr
//case *ChanType:
default:
a.diag("illegal argument type for make function\n\t%v", as[0].valType)
return nil
}
case closeType, closedType:
a.diag("built-in function %s not implemented", ft.builtin)
return nil
case newType:
if !checkCount(1, 1) {
return nil
}
t := as[0].valType
expr := a.newExpr(NewPtrType(t), "new")
expr.eval = func(*Thread) Value { return t.Zero() }
return expr
case panicType, printType, printlnType:
evals := make([]func(*Thread) interface{}, len(as))
for i, x := range as {
evals[i] = x.asInterface()
}
spaces := ft == printlnType
newline := ft != printType
printer := func(t *Thread) {
for i, eval := range evals {
if i > 0 && spaces {
print(" ")
}
v := eval(t)
type stringer interface {
String() string
}
switch v1 := v.(type) {
case bool:
print(v1)
case uint64:
print(v1)
case int64:
print(v1)
case float64:
print(v1)
case string:
print(v1)
case stringer:
print(v1.String())
default:
print("???")
}
}
if newline {
print("\n")
}
}
expr := a.newExpr(EmptyType, "print")
expr.exec = printer
if ft == panicType {
expr.exec = func(t *Thread) {
printer(t)
t.Abort(os.NewError("panic"))
}
}
return expr
}
log.Panicf("unexpected built-in function '%s'", ft.builtin)
panic("unreachable")
}
func (a *exprInfo) compileStarExpr(v *expr) *expr {
switch vt := v.t.lit().(type) {
case *PtrType:
expr := a.newExpr(vt.Elem, "indirect expression")
vf := v.asPtr()
expr.genValue(func(t *Thread) Value {
v := vf(t)
if v == nil {
t.Abort(NilPointerError{})
}
return v
})
return expr
}
a.diagOpType(token.MUL, v.t)
return nil
}
var unaryOpDescs = make(map[token.Token]string)
func (a *exprInfo) compileUnaryExpr(op token.Token, v *expr) *expr {
// Type check
var t Type
switch op {
case token.ADD, token.SUB:
if !v.t.isInteger() && !v.t.isFloat() {
a.diagOpType(op, v.t)
return nil
}
t = v.t
case token.NOT:
if !v.t.isBoolean() {
a.diagOpType(op, v.t)
return nil
}
t = BoolType
case token.XOR:
if !v.t.isInteger() {
a.diagOpType(op, v.t)
return nil
}
t = v.t
case token.AND:
// The unary prefix address-of operator & generates
// the address of its operand, which must be a
// variable, pointer indirection, field selector, or
// array or slice indexing operation.
if v.evalAddr == nil {
a.diag("cannot take the address of %s", v.desc)
return nil
}
// TODO(austin) Implement "It is illegal to take the
// address of a function result variable" once I have
// function result variables.
t = NewPtrType(v.t)
case token.ARROW:
log.Panicf("Unary op %v not implemented", op)
default:
log.Panicf("unknown unary operator %v", op)
}
desc, ok := unaryOpDescs[op]
if !ok {
desc = "unary " + op.String() + " expression"
unaryOpDescs[op] = desc
}
// Compile
expr := a.newExpr(t, desc)
switch op {
case token.ADD:
// Just compile it out
expr = v
expr.desc = desc
case token.SUB:
expr.genUnaryOpNeg(v)
case token.NOT:
expr.genUnaryOpNot(v)
case token.XOR:
expr.genUnaryOpXor(v)
case token.AND:
vf := v.evalAddr
expr.eval = func(t *Thread) Value { return vf(t) }
default:
log.Panicf("Compilation of unary op %v not implemented", op)
}
return expr
}
var binOpDescs = make(map[token.Token]string)
func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
// Save the original types of l.t and r.t for error messages.
origlt := l.t
origrt := r.t
// XXX(Spec) What is the exact definition of a "named type"?
// XXX(Spec) Arithmetic operators: "Integer types" apparently
// means all types compatible with basic integer types, though
// this is never explained. Likewise for float types, etc.
// This relates to the missing explanation of named types.
// XXX(Spec) Operators: "If both operands are ideal numbers,
// the conversion is to ideal floats if one of the operands is
// an ideal float (relevant for / and %)." How is that
// relevant only for / and %? If I add an ideal int and an
// ideal float, I get an ideal float.
if op != token.SHL && op != token.SHR {
// Except in shift expressions, if one operand has
// numeric type and the other operand is an ideal
// number, the ideal number is converted to match the
// type of the other operand.
if (l.t.isInteger() || l.t.isFloat()) && !l.t.isIdeal() && r.t.isIdeal() {
r = r.convertTo(l.t)
} else if (r.t.isInteger() || r.t.isFloat()) && !r.t.isIdeal() && l.t.isIdeal() {
l = l.convertTo(r.t)
}
if l == nil || r == nil {
return nil
}
// Except in shift expressions, if both operands are
// ideal numbers and one is an ideal float, the other
// is converted to ideal float.
if l.t.isIdeal() && r.t.isIdeal() {
if l.t.isInteger() && r.t.isFloat() {
l = l.convertTo(r.t)
} else if l.t.isFloat() && r.t.isInteger() {
r = r.convertTo(l.t)
}
if l == nil || r == nil {
return nil
}
}
}
// Useful type predicates
// TODO(austin) CL 33668 mandates identical types except for comparisons.
compat := func() bool { return l.t.compat(r.t, false) }
integers := func() bool { return l.t.isInteger() && r.t.isInteger() }
floats := func() bool { return l.t.isFloat() && r.t.isFloat() }
strings := func() bool {
// TODO(austin) Deal with named types
return l.t == StringType && r.t == StringType
}
booleans := func() bool { return l.t.isBoolean() && r.t.isBoolean() }
// Type check
var t Type
switch op {
case token.ADD:
if !compat() || (!integers() && !floats() && !strings()) {
a.diagOpTypes(op, origlt, origrt)
return nil
}
t = l.t
case token.SUB, token.MUL, token.QUO:
if !compat() || (!integers() && !floats()) {
a.diagOpTypes(op, origlt, origrt)
return nil
}
t = l.t
case token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
if !compat() || !integers() {
a.diagOpTypes(op, origlt, origrt)
return nil
}
t = l.t
case token.SHL, token.SHR:
// XXX(Spec) Is it okay for the right operand to be an
// ideal float with no fractional part? "The right
// operand in a shift operation must be always be of
// unsigned integer type or an ideal number that can
// be safely converted into an unsigned integer type
// (§Arithmetic operators)" suggests so and 6g agrees.
if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) {
a.diagOpTypes(op, origlt, origrt)
return nil
}
// The right operand in a shift operation must be
// always be of unsigned integer type or an ideal
// number that can be safely converted into an
// unsigned integer type.
if r.t.isIdeal() {
r2 := r.convertTo(UintType)
if r2 == nil {
return nil
}
// If the left operand is not ideal, convert
// the right to not ideal.
if !l.t.isIdeal() {
r = r2
}
// If both are ideal, but the right side isn't
// an ideal int, convert it to simplify things.
if l.t.isIdeal() && !r.t.isInteger() {
r = r.convertTo(IdealIntType)
if r == nil {
log.Panicf("conversion to uintType succeeded, but conversion to idealIntType failed")
}
}
} else if _, ok := r.t.lit().(*uintType); !ok {
a.diag("right operand of shift must be unsigned")
return nil
}
if l.t.isIdeal() && !r.t.isIdeal() {
// XXX(Spec) What is the meaning of "ideal >>
// non-ideal"? Russ says the ideal should be
// converted to an int. 6g propagates the
// type down from assignments as a hint.
l = l.convertTo(IntType)
if l == nil {
return nil
}
}
// At this point, we should have one of three cases:
// 1) uint SHIFT uint
// 2) int SHIFT uint
// 3) ideal int SHIFT ideal int
t = l.t
case token.LOR, token.LAND:
if !booleans() {
return nil
}
// XXX(Spec) There's no mention of *which* boolean
// type the logical operators return. From poking at
// 6g, it appears to be the named boolean type, NOT
// the type of the left operand, and NOT an unnamed
// boolean type.
t = BoolType
case token.ARROW:
// The operands in channel sends differ in type: one
// is always a channel and the other is a variable or
// value of the channel's element type.
log.Panic("Binary op <- not implemented")
t = BoolType
case token.LSS, token.GTR, token.LEQ, token.GEQ:
// XXX(Spec) It's really unclear what types which
// comparison operators apply to. I feel like the
// text is trying to paint a Venn diagram for me,
// which it's really pretty simple: <, <=, >, >= apply
// only to numeric types and strings. == and != apply
// to everything except arrays and structs, and there
// are some restrictions on when it applies to slices.
if !compat() || (!integers() && !floats() && !strings()) {
a.diagOpTypes(op, origlt, origrt)
return nil
}
t = BoolType
case token.EQL, token.NEQ:
// XXX(Spec) The rules for type checking comparison
// operators are spread across three places that all
// partially overlap with each other: the Comparison
// Compatibility section, the Operators section, and
// the Comparison Operators section. The Operators
// section should just say that operators require
// identical types (as it does currently) except that
// there a few special cases for comparison, which are
// described in section X. Currently it includes just
// one of the four special cases. The Comparison
// Compatibility section and the Comparison Operators
// section should either be merged, or at least the
// Comparison Compatibility section should be
// exclusively about type checking and the Comparison
// Operators section should be exclusively about
// semantics.
// XXX(Spec) Comparison operators: "All comparison
// operators apply to basic types except bools." This
// is very difficult to parse. It's explained much
// better in the Comparison Compatibility section.
// XXX(Spec) Comparison compatibility: "Function
// values are equal if they refer to the same
// function." is rather vague. It should probably be
// similar to the way the rule for map values is
// written: Function values are equal if they were
// created by the same execution of a function literal
// or refer to the same function declaration. This is
// *almost* but not quite what 6g implements. If a
// function literals does not capture any variables,
// then multiple executions of it will result in the
// same closure. Russ says he'll change that.
// TODO(austin) Deal with remaining special cases
if !compat() {
a.diagOpTypes(op, origlt, origrt)
return nil
}
// Arrays and structs may not be compared to anything.
switch l.t.(type) {
case *ArrayType, *StructType:
a.diagOpTypes(op, origlt, origrt)
return nil
}
t = BoolType
default:
log.Panicf("unknown binary operator %v", op)
}
desc, ok := binOpDescs[op]
if !ok {
desc = op.String() + " expression"
binOpDescs[op] = desc
}
// Check for ideal divide by zero
switch op {
case token.QUO, token.REM:
if r.t.isIdeal() {
if (r.t.isInteger() && r.asIdealInt()().Sign() == 0) ||
(r.t.isFloat() && r.asIdealFloat()().Sign() == 0) {
a.diag("divide by zero")
return nil
}
}
}
// Compile
expr := a.newExpr(t, desc)
switch op {
case token.ADD:
expr.genBinOpAdd(l, r)
case token.SUB:
expr.genBinOpSub(l, r)
case token.MUL:
expr.genBinOpMul(l, r)
case token.QUO:
expr.genBinOpQuo(l, r)
case token.REM:
expr.genBinOpRem(l, r)
case token.AND:
expr.genBinOpAnd(l, r)
case token.OR:
expr.genBinOpOr(l, r)
case token.XOR:
expr.genBinOpXor(l, r)
case token.AND_NOT:
expr.genBinOpAndNot(l, r)
case token.SHL:
if l.t.isIdeal() {
lv := l.asIdealInt()()
rv := r.asIdealInt()()
const maxShift = 99999
if rv.Cmp(big.NewInt(maxShift)) > 0 {
a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift)
expr.t = nil
return nil
}
val := new(big.Int).Lsh(lv, uint(rv.Int64()))
expr.eval = func() *big.Int { return val }
} else {
expr.genBinOpShl(l, r)
}
case token.SHR:
if l.t.isIdeal() {
lv := l.asIdealInt()()
rv := r.asIdealInt()()
val := new(big.Int).Rsh(lv, uint(rv.Int64()))
expr.eval = func() *big.Int { return val }
} else {
expr.genBinOpShr(l, r)
}
case token.LSS:
expr.genBinOpLss(l, r)
case token.GTR:
expr.genBinOpGtr(l, r)
case token.LEQ:
expr.genBinOpLeq(l, r)
case token.GEQ:
expr.genBinOpGeq(l, r)
case token.EQL:
expr.genBinOpEql(l, r)
case token.NEQ:
expr.genBinOpNeq(l, r)
case token.LAND:
expr.genBinOpLogAnd(l, r)
case token.LOR:
expr.genBinOpLogOr(l, r)
default:
log.Panicf("Compilation of binary op %v not implemented", op)
}
return expr
}
// TODO(austin) This is a hack to eliminate a circular dependency
// between type.go and expr.go
func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) {
lenExpr := a.compileExpr(b, true, expr)
if lenExpr == nil {
return 0, false
}
// XXX(Spec) Are ideal floats with no fractional part okay?
if lenExpr.t.isIdeal() {
lenExpr = lenExpr.convertTo(IntType)
if lenExpr == nil {
return 0, false
}
}
if !lenExpr.t.isInteger() {
a.diagAt(expr.Pos(), "array size must be an integer")
return 0, false
}
switch lenExpr.t.lit().(type) {
case *intType:
return lenExpr.asInt()(nil), true
case *uintType:
return int64(lenExpr.asUint()(nil)), true
}
log.Panicf("unexpected integer type %T", lenExpr.t)
return 0, false
}
func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr {
ec := &exprCompiler{a, b, constant}
nerr := a.numError()
e := ec.compile(expr, false)
if e == nil && nerr == a.numError() {
log.Panicf("expression compilation failed without reporting errors")
}
return e
}
// extractEffect separates out any effects that the expression may
// have, returning a function that will perform those effects and a
// new exprCompiler that is guaranteed to be side-effect free. These
// are the moral equivalents of "temp := expr" and "temp" (or "temp :=
// &expr" and "*temp" for addressable exprs). Because this creates a
// temporary variable, the caller should create a temporary block for
// the compilation of this expression and the evaluation of the
// results.
func (a *expr) extractEffect(b *block, errOp string) (func(*Thread), *expr) {
// Create "&a" if a is addressable
rhs := a
if a.evalAddr != nil {
rhs = a.compileUnaryExpr(token.AND, rhs)
}
// Create temp
ac, ok := a.checkAssign(a.pos, []*expr{rhs}, errOp, "")
if !ok {
return nil, nil
}
if len(ac.rmt.Elems) != 1 {
a.diag("multi-valued expression not allowed in %s", errOp)
return nil, nil
}
tempType := ac.rmt.Elems[0]
if tempType.isIdeal() {
// It's too bad we have to duplicate this rule.
switch {
case tempType.isInteger():
tempType = IntType
case tempType.isFloat():
tempType = Float64Type
default:
log.Panicf("unexpected ideal type %v", tempType)
}
}
temp := b.DefineTemp(tempType)
tempIdx := temp.Index
// Create "temp := rhs"
assign := ac.compile(b, tempType)
if assign == nil {
log.Panicf("compileAssign type check failed")
}
effect := func(t *Thread) {
tempVal := tempType.Zero()
t.f.Vars[tempIdx] = tempVal
assign(tempVal, t)
}
// Generate "temp" or "*temp"
getTemp := a.compileVariable(0, temp)
if a.evalAddr == nil {
return effect, getTemp
}
deref := a.compileStarExpr(getTemp)
if deref == nil {
return nil, nil
}
return effect, deref
}
// 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.
*/
func (a *expr) asBool() func(*Thread) bool {
return a.eval.(func(*Thread) bool)
}
func (a *expr) asUint() func(*Thread) uint64 {
return a.eval.(func(*Thread) uint64)
}
func (a *expr) asInt() func(*Thread) int64 {
return a.eval.(func(*Thread) int64)
}
func (a *expr) asIdealInt() func() *big.Int {
return a.eval.(func() *big.Int)
}
func (a *expr) asFloat() func(*Thread) float64 {
return a.eval.(func(*Thread) float64)
}
func (a *expr) asIdealFloat() func() *big.Rat {
return a.eval.(func() *big.Rat)
}
func (a *expr) asString() func(*Thread) string {
return a.eval.(func(*Thread) string)
}
func (a *expr) asArray() func(*Thread) ArrayValue {
return a.eval.(func(*Thread) ArrayValue)
}
func (a *expr) asStruct() func(*Thread) StructValue {
return a.eval.(func(*Thread) StructValue)
}
func (a *expr) asPtr() func(*Thread) Value {
return a.eval.(func(*Thread) Value)
}
func (a *expr) asFunc() func(*Thread) Func {
return a.eval.(func(*Thread) Func)
}
func (a *expr) asSlice() func(*Thread) Slice {
return a.eval.(func(*Thread) Slice)
}
func (a *expr) asMap() func(*Thread) Map {
return a.eval.(func(*Thread) Map)
}
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) {
case func(t *Thread) bool:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) uint64:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) int64:
return func(t *Thread) interface{} { return sf(t) }
case func() *big.Int:
return func(*Thread) interface{} { return sf() }
case func(t *Thread) float64:
return func(t *Thread) interface{} { return sf(t) }
case func() *big.Rat:
return func(*Thread) interface{} { return sf() }
case func(t *Thread) string:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) ArrayValue:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) StructValue:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) Value:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) Func:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) Slice:
return func(t *Thread) interface{} { return sf(t) }
case func(t *Thread) Map:
return func(t *Thread) interface{} { return sf(t) }
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) {
case *boolType:
a.eval = func(t *Thread) bool { return v.(BoolValue).Get(t) }
case *uintType:
a.eval = func(t *Thread) uint64 { return v.(UintValue).Get(t) }
case *intType:
a.eval = func(t *Thread) int64 { return v.(IntValue).Get(t) }
case *idealIntType:
val := v.(IdealIntValue).Get()
a.eval = func() *big.Int { return val }
case *floatType:
a.eval = func(t *Thread) float64 { return v.(FloatValue).Get(t) }
case *idealFloatType:
val := v.(IdealFloatValue).Get()
a.eval = func() *big.Rat { return val }
case *stringType:
a.eval = func(t *Thread) string { return v.(StringValue).Get(t) }
case *ArrayType:
a.eval = func(t *Thread) ArrayValue { return v.(ArrayValue).Get(t) }
case *StructType:
a.eval = func(t *Thread) StructValue { return v.(StructValue).Get(t) }
case *PtrType:
a.eval = func(t *Thread) Value { return v.(PtrValue).Get(t) }
case *FuncType:
a.eval = func(t *Thread) Func { return v.(FuncValue).Get(t) }
case *SliceType:
a.eval = func(t *Thread) Slice { return v.(SliceValue).Get(t) }
case *MapType:
a.eval = func(t *Thread) Map { return v.(MapValue).Get(t) }
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) {
case *boolType:
a.eval = func(t *Thread) bool { return t.f.Get(level, index).(BoolValue).Get(t) }
case *uintType:
a.eval = func(t *Thread) uint64 { return t.f.Get(level, index).(UintValue).Get(t) }
case *intType:
a.eval = func(t *Thread) int64 { return t.f.Get(level, index).(IntValue).Get(t) }
case *floatType:
a.eval = func(t *Thread) float64 { return t.f.Get(level, index).(FloatValue).Get(t) }
case *stringType:
a.eval = func(t *Thread) string { return t.f.Get(level, index).(StringValue).Get(t) }
case *ArrayType:
a.eval = func(t *Thread) ArrayValue { return t.f.Get(level, index).(ArrayValue).Get(t) }
case *StructType:
a.eval = func(t *Thread) StructValue { return t.f.Get(level, index).(StructValue).Get(t) }
case *PtrType:
a.eval = func(t *Thread) Value { return t.f.Get(level, index).(PtrValue).Get(t) }
case *FuncType:
a.eval = func(t *Thread) Func { return t.f.Get(level, index).(FuncValue).Get(t) }
case *SliceType:
a.eval = func(t *Thread) Slice { return t.f.Get(level, index).(SliceValue).Get(t) }
case *MapType:
a.eval = func(t *Thread) Map { return t.f.Get(level, index).(MapValue).Get(t) }
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) {
case *boolType:
a.eval = func(t *Thread) bool { return call(t)[0].(BoolValue).Get(t) }
case *uintType:
a.eval = func(t *Thread) uint64 { return call(t)[0].(UintValue).Get(t) }
case *intType:
a.eval = func(t *Thread) int64 { return call(t)[0].(IntValue).Get(t) }
case *floatType:
a.eval = func(t *Thread) float64 { return call(t)[0].(FloatValue).Get(t) }
case *stringType:
a.eval = func(t *Thread) string { return call(t)[0].(StringValue).Get(t) }
case *ArrayType:
a.eval = func(t *Thread) ArrayValue { return call(t)[0].(ArrayValue).Get(t) }
case *StructType:
a.eval = func(t *Thread) StructValue { return call(t)[0].(StructValue).Get(t) }
case *PtrType:
a.eval = func(t *Thread) Value { return call(t)[0].(PtrValue).Get(t) }
case *FuncType:
a.eval = func(t *Thread) Func { return call(t)[0].(FuncValue).Get(t) }
case *SliceType:
a.eval = func(t *Thread) Slice { return call(t)[0].(SliceValue).Get(t) }
case *MapType:
a.eval = func(t *Thread) Map { return call(t)[0].(MapValue).Get(t) }
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) {
case *boolType:
a.eval = func(t *Thread) bool { return vf(t).(BoolValue).Get(t) }
case *uintType:
a.eval = func(t *Thread) uint64 { return vf(t).(UintValue).Get(t) }
case *intType:
a.eval = func(t *Thread) int64 { return vf(t).(IntValue).Get(t) }
case *floatType:
a.eval = func(t *Thread) float64 { return vf(t).(FloatValue).Get(t) }
case *stringType:
a.eval = func(t *Thread) string { return vf(t).(StringValue).Get(t) }
case *ArrayType:
a.eval = func(t *Thread) ArrayValue { return vf(t).(ArrayValue).Get(t) }
case *StructType:
a.eval = func(t *Thread) StructValue { return vf(t).(StructValue).Get(t) }
case *PtrType:
a.eval = func(t *Thread) Value { return vf(t).(PtrValue).Get(t) }
case *FuncType:
a.eval = func(t *Thread) Func { return vf(t).(FuncValue).Get(t) }
case *SliceType:
a.eval = func(t *Thread) Slice { return vf(t).(SliceValue).Get(t) }
case *MapType:
a.eval = func(t *Thread) Map { return vf(t).(MapValue).Get(t) }
default:
log.Panicf("unexpected result type %v at %v", a.t, a.pos)
}
}
func (a *expr) genUnaryOpNeg(v *expr) {
switch a.t.lit().(type) {
case *uintType:
vf := v.asUint()
a.eval = func(t *Thread) uint64 { v := vf(t); return -v }
case *intType:
vf := v.asInt()
a.eval = func(t *Thread) int64 { v := vf(t); return -v }
case *idealIntType:
val := v.asIdealInt()()
val.Neg(val)
a.eval = func() *big.Int { return val }
case *floatType:
vf := v.asFloat()
a.eval = func(t *Thread) float64 { v := vf(t); return -v }
case *idealFloatType:
val := v.asIdealFloat()()
val.Neg(val)
a.eval = func() *big.Rat { return val }
default:
log.Panicf("unexpected type %v at %v", a.t, a.pos)
}
}
func (a *expr) genUnaryOpNot(v *expr) {
switch a.t.lit().(type) {
case *boolType:
vf := v.asBool()
a.eval = func(t *Thread) bool { v := vf(t); return !v }
default:
log.Panicf("unexpected type %v at %v", a.t, a.pos)
}
}
func (a *expr) genUnaryOpXor(v *expr) {
switch a.t.lit().(type) {
case *uintType:
vf := v.asUint()
a.eval = func(t *Thread) uint64 { v := vf(t); return ^v }
case *intType:
vf := v.asInt()
a.eval = func(t *Thread) int64 { v := vf(t); return ^v }
case *idealIntType:
val := v.asIdealInt()()
val.Not(val)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", a.t, a.pos)
}
}
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) }
}
func (a *expr) genBinOpAdd(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l + r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l + r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l + r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l + r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l + r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l + r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l + r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l + r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l + r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l + r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Add(l, r)
a.eval = func() *big.Int { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
switch t.Bits {
case 32:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l + r
return float64(float32(ret))
}
case 64:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l + r
return float64(float64(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Add(l, r)
a.eval = func() *big.Rat { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) string {
l, r := lf(t), rf(t)
return l + r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpSub(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l - r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l - r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l - r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l - r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l - r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l - r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l - r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l - r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l - r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l - r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Sub(l, r)
a.eval = func() *big.Int { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
switch t.Bits {
case 32:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l - r
return float64(float32(ret))
}
case 64:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l - r
return float64(float64(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Sub(l, r)
a.eval = func() *big.Rat { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpMul(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l * r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l * r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l * r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l * r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l * r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l * r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l * r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l * r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l * r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l * r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Mul(l, r)
a.eval = func() *big.Int { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
switch t.Bits {
case 32:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l * r
return float64(float32(ret))
}
case 64:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
ret = l * r
return float64(float64(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Mul(l, r)
a.eval = func() *big.Rat { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpQuo(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Quo(l, r)
a.eval = func() *big.Int { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
switch t.Bits {
case 32:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return float64(float32(ret))
}
case 64:
a.eval = func(t *Thread) float64 {
l, r := lf(t), rf(t)
var ret float64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l / r
return float64(float64(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Quo(l, r)
a.eval = func() *big.Rat { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpRem(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
if r == 0 {
t.Abort(DivByZeroError{})
}
ret = l % r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Rem(l, r)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpAnd(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l & r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l & r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l & r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l & r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l & r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l & r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l & r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l & r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l & r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l & r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.And(l, r)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpOr(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l | r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l | r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l | r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l | r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l | r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l | r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l | r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l | r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l | r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l | r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Or(l, r)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpXor(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l ^ r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l ^ r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l ^ r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l ^ r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l ^ r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l ^ r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l ^ r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l ^ r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l ^ r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l ^ r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Xor(l, r)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpAndNot(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l &^ r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l &^ r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l &^ r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l &^ r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l &^ r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asInt()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l &^ r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l &^ r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l &^ r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l &^ r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l &^ r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.AndNot(l, r)
a.eval = func() *big.Int { return val }
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpShl(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l << r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l << r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l << r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l << r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l << r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l << r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l << r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l << r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l << r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l << r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpShr(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l >> r
return uint64(uint8(ret))
}
case 16:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l >> r
return uint64(uint16(ret))
}
case 32:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l >> r
return uint64(uint32(ret))
}
case 64:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l >> r
return uint64(uint64(ret))
}
case 0:
a.eval = func(t *Thread) uint64 {
l, r := lf(t), rf(t)
var ret uint64
ret = l >> r
return uint64(uint(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
case *intType:
lf := l.asInt()
rf := r.asUint()
switch t.Bits {
case 8:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l >> r
return int64(int8(ret))
}
case 16:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l >> r
return int64(int16(ret))
}
case 32:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l >> r
return int64(int32(ret))
}
case 64:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l >> r
return int64(int64(ret))
}
case 0:
a.eval = func(t *Thread) int64 {
l, r := lf(t), rf(t)
var ret int64
ret = l >> r
return int64(int(ret))
}
default:
log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos)
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpLss(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l < r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l < r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) < 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l < r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) < 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l < r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpGtr(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l > r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l > r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) > 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l > r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) > 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l > r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpLeq(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l <= r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l <= r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) <= 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l <= r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) <= 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l <= r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpGeq(l, r *expr) {
switch t := l.t.lit().(type) {
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l >= r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l >= r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) >= 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l >= r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) >= 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l >= r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpEql(l, r *expr) {
switch t := l.t.lit().(type) {
case *boolType:
lf := l.asBool()
rf := r.asBool()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) == 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) == 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *PtrType:
lf := l.asPtr()
rf := r.asPtr()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *FuncType:
lf := l.asFunc()
rf := r.asFunc()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
case *MapType:
lf := l.asMap()
rf := r.asMap()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l == r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func (a *expr) genBinOpNeq(l, r *expr) {
switch t := l.t.lit().(type) {
case *boolType:
lf := l.asBool()
rf := r.asBool()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *uintType:
lf := l.asUint()
rf := r.asUint()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *intType:
lf := l.asInt()
rf := r.asInt()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *idealIntType:
l := l.asIdealInt()()
r := r.asIdealInt()()
val := l.Cmp(r) != 0
a.eval = func(t *Thread) bool { return val }
case *floatType:
lf := l.asFloat()
rf := r.asFloat()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *idealFloatType:
l := l.asIdealFloat()()
r := r.asIdealFloat()()
val := l.Cmp(r) != 0
a.eval = func(t *Thread) bool { return val }
case *stringType:
lf := l.asString()
rf := r.asString()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *PtrType:
lf := l.asPtr()
rf := r.asPtr()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *FuncType:
lf := l.asFunc()
rf := r.asFunc()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
case *MapType:
lf := l.asMap()
rf := r.asMap()
a.eval = func(t *Thread) bool {
l, r := lf(t), rf(t)
return l != r
}
default:
log.Panicf("unexpected type %v at %v", l.t, a.pos)
}
}
func genAssign(lt Type, r *expr) func(lv Value, t *Thread) {
switch lt.lit().(type) {
case *boolType:
rf := r.asBool()
return func(lv Value, t *Thread) { lv.(BoolValue).Set(t, rf(t)) }
case *uintType:
rf := r.asUint()
return func(lv Value, t *Thread) { lv.(UintValue).Set(t, rf(t)) }
case *intType:
rf := r.asInt()
return func(lv Value, t *Thread) { lv.(IntValue).Set(t, rf(t)) }
case *floatType:
rf := r.asFloat()
return func(lv Value, t *Thread) { lv.(FloatValue).Set(t, rf(t)) }
case *stringType:
rf := r.asString()
return func(lv Value, t *Thread) { lv.(StringValue).Set(t, rf(t)) }
case *ArrayType:
rf := r.asArray()
return func(lv Value, t *Thread) { lv.Assign(t, rf(t)) }
case *StructType:
rf := r.asStruct()
return func(lv Value, t *Thread) { lv.Assign(t, rf(t)) }
case *PtrType:
rf := r.asPtr()
return func(lv Value, t *Thread) { lv.(PtrValue).Set(t, rf(t)) }
case *FuncType:
rf := r.asFunc()
return func(lv Value, t *Thread) { lv.(FuncValue).Set(t, rf(t)) }
case *SliceType:
rf := r.asSlice()
return func(lv Value, t *Thread) { lv.(SliceValue).Set(t, rf(t)) }
case *MapType:
rf := r.asMap()
return func(lv Value, t *Thread) { lv.(MapValue).Set(t, rf(t)) }
default:
log.Panicf("unexpected left operand type %v at %v", lt, r.pos)
}
panic("fail")
}
// 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) }
// 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) }
// 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)
}
}
// 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())
}
}
}
// 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)}
}
// 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"
"log"
"go/ast"
"go/token"
)
const (
returnPC = ^uint(0)
badPC = ^uint(1)
)
/*
* Statement compiler
*/
type stmtCompiler struct {
*blockCompiler
pos token.Pos
// This statement's label, or nil if it is not labeled.
stmtLabel *label
}
func (a *stmtCompiler) diag(format string, args ...interface{}) {
a.diagAt(a.pos, format, args...)
}
/*
* Flow checker
*/
type flowEnt struct {
// Whether this flow entry is conditional. If true, flow can
// continue to the next PC.
cond bool
// True if this will terminate flow (e.g., a return statement).
// cond must be false and jumps must be nil if this is true.
term bool
// PC's that can be reached from this flow entry.
jumps []*uint
// Whether this flow entry has been visited by reachesEnd.
visited bool
}
type flowBlock struct {
// If this is a goto, the target label.
target string
// The inner-most block containing definitions.
block *block
// The numVars from each block leading to the root of the
// scope, starting at block.
numVars []int
}
type flowBuf struct {
cb *codeBuf
// ents is a map from PC's to flow entries. Any PC missing
// from this map is assumed to reach only PC+1.
ents map[uint]*flowEnt
// gotos is a map from goto positions to information on the
// block at the point of the goto.
gotos map[token.Pos]*flowBlock
// labels is a map from label name to information on the block
// at the point of the label. labels are tracked by name,
// since multiple labels at the same PC can have different
// blocks.
labels map[string]*flowBlock
}
func newFlowBuf(cb *codeBuf) *flowBuf {
return &flowBuf{cb, make(map[uint]*flowEnt), make(map[token.Pos]*flowBlock), make(map[string]*flowBlock)}
}
// put creates a flow control point for the next PC in the code buffer.
// This should be done before pushing the instruction into the code buffer.
func (f *flowBuf) put(cond bool, term bool, jumps []*uint) {
pc := f.cb.nextPC()
if ent, ok := f.ents[pc]; ok {
log.Panicf("Flow entry already exists at PC %d: %+v", pc, ent)
}
f.ents[pc] = &flowEnt{cond, term, jumps, false}
}
// putTerm creates a flow control point at the next PC that
// unconditionally terminates execution.
func (f *flowBuf) putTerm() { f.put(false, true, nil) }
// put1 creates a flow control point at the next PC that jumps to one
// PC and, if cond is true, can also continue to the PC following the
// next PC.
func (f *flowBuf) put1(cond bool, jumpPC *uint) {
f.put(cond, false, []*uint{jumpPC})
}
func newFlowBlock(target string, b *block) *flowBlock {
// Find the inner-most block containing definitions
for b.numVars == 0 && b.outer != nil && b.outer.scope == b.scope {
b = b.outer
}
// Count parents leading to the root of the scope
n := 0
for bp := b; bp.scope == b.scope; bp = bp.outer {
n++
}
// Capture numVars from each block to the root of the scope
numVars := make([]int, n)
i := 0
for bp := b; i < n; bp = bp.outer {
numVars[i] = bp.numVars
i++
}
return &flowBlock{target, b, numVars}
}
// putGoto captures the block at a goto statement. This should be
// called in addition to putting a flow control point.
func (f *flowBuf) putGoto(pos token.Pos, target string, b *block) {
f.gotos[pos] = newFlowBlock(target, b)
}
// putLabel captures the block at a label.
func (f *flowBuf) putLabel(name string, b *block) {
f.labels[name] = newFlowBlock("", b)
}
// reachesEnd returns true if the end of f's code buffer can be
// reached from the given program counter. Error reporting is the
// caller's responsibility.
func (f *flowBuf) reachesEnd(pc uint) bool {
endPC := f.cb.nextPC()
if pc > endPC {
log.Panicf("Reached bad PC %d past end PC %d", pc, endPC)
}
for ; pc < endPC; pc++ {
ent, ok := f.ents[pc]
if !ok {
continue
}
if ent.visited {
return false
}
ent.visited = true
if ent.term {
return false
}
// If anything can reach the end, we can reach the end
// from pc.
for _, j := range ent.jumps {
if f.reachesEnd(*j) {
return true
}
}
// If the jump was conditional, we can reach the next
// PC, so try reaching the end from it.
if ent.cond {
continue
}
return false
}
return true
}
// gotosObeyScopes returns true if no goto statement causes any
// variables to come into scope that were not in scope at the point of
// the goto. Reports any errors using the given compiler.
func (f *flowBuf) gotosObeyScopes(a *compiler) {
for pos, src := range f.gotos {
tgt := f.labels[src.target]
// The target block must be a parent of this block
numVars := src.numVars
b := src.block
for len(numVars) > 0 && b != tgt.block {
b = b.outer
numVars = numVars[1:]
}
if b != tgt.block {
// We jumped into a deeper block
a.diagAt(pos, "goto causes variables to come into scope")
return
}
// There must be no variables in the target block that
// did not exist at the jump
tgtNumVars := tgt.numVars
for i := range numVars {
if tgtNumVars[i] > numVars[i] {
a.diagAt(pos, "goto causes variables to come into scope")
return
}
}
}
}
/*
* Statement generation helpers
*/
func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable {
v, prev := a.block.DefineVar(ident.Name, ident.Pos(), t)
if prev != nil {
if prev.Pos().IsValid() {
a.diagAt(ident.Pos(), "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name, a.fset.Position(prev.Pos()))
} else {
a.diagAt(ident.Pos(), "variable %s redeclared in this block", ident.Name)
}
return nil
}
// Initialize the variable
index := v.Index
if v.Index >= 0 {
a.push(func(v *Thread) { v.f.Vars[index] = t.Zero() })
}
return v
}
// TODO(austin) Move doAssign to here
/*
* Statement compiler
*/
func (a *stmtCompiler) compile(s ast.Stmt) {
if a.block.inner != nil {
log.Panic("Child scope still entered")
}
notimpl := false
switch s := s.(type) {
case *ast.BadStmt:
// Error already reported by parser.
a.silentErrors++
case *ast.DeclStmt:
a.compileDeclStmt(s)
case *ast.EmptyStmt:
// Do nothing.
case *ast.LabeledStmt:
a.compileLabeledStmt(s)
case *ast.ExprStmt:
a.compileExprStmt(s)
case *ast.IncDecStmt:
a.compileIncDecStmt(s)
case *ast.AssignStmt:
a.compileAssignStmt(s)
case *ast.GoStmt:
notimpl = true
case *ast.DeferStmt:
notimpl = true
case *ast.ReturnStmt:
a.compileReturnStmt(s)
case *ast.BranchStmt:
a.compileBranchStmt(s)
case *ast.BlockStmt:
a.compileBlockStmt(s)
case *ast.IfStmt:
a.compileIfStmt(s)
case *ast.CaseClause:
a.diag("case clause outside switch")
case *ast.SwitchStmt:
a.compileSwitchStmt(s)
case *ast.TypeSwitchStmt:
notimpl = true
case *ast.CommClause:
notimpl = true
case *ast.SelectStmt:
notimpl = true
case *ast.ForStmt:
a.compileForStmt(s)
case *ast.RangeStmt:
notimpl = true
default:
log.Panicf("unexpected ast node type %T", s)
}
if notimpl {
a.diag("%T statement node not implemented", s)
}
if a.block.inner != nil {
log.Panic("Forgot to exit child scope")
}
}
func (a *stmtCompiler) compileDeclStmt(s *ast.DeclStmt) {
switch decl := s.Decl.(type) {
case *ast.BadDecl:
// Do nothing. Already reported by parser.
a.silentErrors++
case *ast.FuncDecl:
if !a.block.global {
log.Panic("FuncDecl at statement level")
}
case *ast.GenDecl:
if decl.Tok == token.IMPORT && !a.block.global {
log.Panic("import at statement level")
}
default:
log.Panicf("Unexpected Decl type %T", s.Decl)
}
a.compileDecl(s.Decl)
}
func (a *stmtCompiler) compileVarDecl(decl *ast.GenDecl) {
for _, spec := range decl.Specs {
spec := spec.(*ast.ValueSpec)
if spec.Values == nil {
// Declaration without assignment
if spec.Type == nil {
// Parser should have caught
log.Panic("Type and Values nil")
}
t := a.compileType(a.block, spec.Type)
// Define placeholders even if type compile failed
for _, n := range spec.Names {
a.defineVar(n, t)
}
} else {
// Declaration with assignment
lhs := make([]ast.Expr, len(spec.Names))
for i, n := range spec.Names {
lhs[i] = n
}
a.doAssign(lhs, spec.Values, decl.Tok, spec.Type)
}
}
}
func (a *stmtCompiler) compileDecl(decl ast.Decl) {
switch d := decl.(type) {
case *ast.BadDecl:
// Do nothing. Already reported by parser.
a.silentErrors++
case *ast.FuncDecl:
decl := a.compileFuncType(a.block, d.Type)
if decl == nil {
return
}
// Declare and initialize v before compiling func
// so that body can refer to itself.
c, prev := a.block.DefineConst(d.Name.Name, a.pos, decl.Type, decl.Type.Zero())
if prev != nil {
pos := prev.Pos()
if pos.IsValid() {
a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name, a.fset.Position(pos))
} else {
a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block", d.Name.Name)
}
}
fn := a.compileFunc(a.block, decl, d.Body)
if c == nil || fn == nil {
return
}
var zeroThread Thread
c.Value.(FuncValue).Set(nil, fn(&zeroThread))
case *ast.GenDecl:
switch d.Tok {
case token.IMPORT:
log.Panicf("%v not implemented", d.Tok)
case token.CONST:
log.Panicf("%v not implemented", d.Tok)
case token.TYPE:
a.compileTypeDecl(a.block, d)
case token.VAR:
a.compileVarDecl(d)
}
default:
log.Panicf("Unexpected Decl type %T", decl)
}
}
func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) {
// Define label
l, ok := a.labels[s.Label.Name]
if ok {
if l.resolved.IsValid() {
a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name, a.fset.Position(l.resolved))
}
} else {
pc := badPC
l = &label{name: s.Label.Name, gotoPC: &pc}
a.labels[l.name] = l
}
l.desc = "regular label"
l.resolved = s.Pos()
// Set goto PC
*l.gotoPC = a.nextPC()
// Define flow entry so we can check for jumps over declarations.
a.flow.putLabel(l.name, a.block)
// Compile the statement. Reuse our stmtCompiler for simplicity.
sc := &stmtCompiler{a.blockCompiler, s.Stmt.Pos(), l}
sc.compile(s.Stmt)
}
func (a *stmtCompiler) compileExprStmt(s *ast.ExprStmt) {
bc := a.enterChild()
defer bc.exit()
e := a.compileExpr(bc.block, false, s.X)
if e == nil {
return
}
if e.exec == nil {
a.diag("%s cannot be used as expression statement", e.desc)
return
}
a.push(e.exec)
}
func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) {
// Create temporary block for extractEffect
bc := a.enterChild()
defer bc.exit()
l := a.compileExpr(bc.block, false, s.X)
if l == nil {
return
}
if l.evalAddr == nil {
l.diag("cannot assign to %s", l.desc)
return
}
if !(l.t.isInteger() || l.t.isFloat()) {
l.diagOpType(s.Tok, l.t)
return
}
var op token.Token
var desc string
switch s.Tok {
case token.INC:
op = token.ADD
desc = "increment statement"
case token.DEC:
op = token.SUB
desc = "decrement statement"
default:
log.Panicf("Unexpected IncDec token %v", s.Tok)
}
effect, l := l.extractEffect(bc.block, desc)
one := l.newExpr(IdealIntType, "constant")
one.pos = s.Pos()
one.eval = func() *big.Int { return big.NewInt(1) }
binop := l.compileBinaryExpr(op, l, one)
if binop == nil {
return
}
assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "", "")
if assign == nil {
log.Panicf("compileAssign type check failed")
}
lf := l.evalAddr
a.push(func(v *Thread) {
effect(v)
assign(lf(v), v)
})
}
func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, declTypeExpr ast.Expr) {
nerr := a.numError()
// Compile right side first so we have the types when
// compiling the left side and so we don't see definitions
// made on the left side.
rs := make([]*expr, len(rhs))
for i, re := range rhs {
rs[i] = a.compileExpr(a.block, false, re)
}
errOp := "assignment"
if tok == token.DEFINE || tok == token.VAR {
errOp = "declaration"
}
ac, ok := a.checkAssign(a.pos, rs, errOp, "value")
ac.allowMapForms(len(lhs))
// If this is a definition and the LHS is too big, we won't be
// able to produce the usual error message because we can't
// begin to infer the types of the LHS.
if (tok == token.DEFINE || tok == token.VAR) && len(lhs) > len(ac.rmt.Elems) {
a.diag("not enough values for definition")
}
// Compile left type if there is one
var declType Type
if declTypeExpr != nil {
declType = a.compileType(a.block, declTypeExpr)
}
// Compile left side
ls := make([]*expr, len(lhs))
nDefs := 0
for i, le := range lhs {
// If this is a definition, get the identifier and its type
var ident *ast.Ident
var lt Type
switch tok {
case token.DEFINE:
// Check that it's an identifier
ident, ok = le.(*ast.Ident)
if !ok {
a.diagAt(le.Pos(), "left side of := must be a name")
// Suppress new definitions errors
nDefs++
continue
}
// Is this simply an assignment?
if _, ok := a.block.defs[ident.Name]; ok {
ident = nil
break
}
nDefs++
case token.VAR:
ident = le.(*ast.Ident)
}
// If it's a definition, get or infer its type.
if ident != nil {
// Compute the identifier's type from the RHS
// type. We use the computed MultiType so we
// don't have to worry about unpacking.
switch {
case declTypeExpr != nil:
// We have a declaration type, use it.
// If declType is nil, we gave an
// error when we compiled it.
lt = declType
case i >= len(ac.rmt.Elems):
// Define a placeholder. We already
// gave the "not enough" error above.
lt = nil
case ac.rmt.Elems[i] == nil:
// We gave the error when we compiled
// the RHS.
lt = nil
case ac.rmt.Elems[i].isIdeal():
// If the type is absent and the
// corresponding expression is a
// constant expression of ideal
// integer or ideal float type, the
// type of the declared variable is
// int or float respectively.
switch {
case ac.rmt.Elems[i].isInteger():
lt = IntType
case ac.rmt.Elems[i].isFloat():
lt = Float64Type
default:
log.Panicf("unexpected ideal type %v", rs[i].t)
}
default:
lt = ac.rmt.Elems[i]
}
}
// If it's a definition, define the identifier
if ident != nil {
if a.defineVar(ident, lt) == nil {
continue
}
}
// Compile LHS
ls[i] = a.compileExpr(a.block, false, le)
if ls[i] == nil {
continue
}
if ls[i].evalMapValue != nil {
// Map indexes are not generally addressable,
// but they are assignable.
//
// TODO(austin) Now that the expression
// compiler uses semantic values, this might
// be easier to implement as a function call.
sub := ls[i]
ls[i] = ls[i].newExpr(sub.t, sub.desc)
ls[i].evalMapValue = sub.evalMapValue
mvf := sub.evalMapValue
et := sub.t
ls[i].evalAddr = func(t *Thread) Value {
m, k := mvf(t)
e := m.Elem(t, k)
if e == nil {
e = et.Zero()
m.SetElem(t, k, e)
}
return e
}
} else if ls[i].evalAddr == nil {
ls[i].diag("cannot assign to %s", ls[i].desc)
continue
}
}
// A short variable declaration may redeclare variables
// provided they were originally declared in the same block
// with the same type, and at least one of the variables is
// new.
if tok == token.DEFINE && nDefs == 0 {
a.diag("at least one new variable must be declared")
return
}
// If there have been errors, our arrays are full of nil's so
// get out of here now.
if nerr != a.numError() {
return
}
// Check for 'a[x] = r, ok'
if len(ls) == 1 && len(rs) == 2 && ls[0].evalMapValue != nil {
a.diag("a[x] = r, ok form not implemented")
return
}
// Create assigner
var lt Type
n := len(lhs)
if n == 1 {
lt = ls[0].t
} else {
lts := make([]Type, len(ls))
for i, l := range ls {
if l != nil {
lts[i] = l.t
}
}
lt = NewMultiType(lts)
}
bc := a.enterChild()
defer bc.exit()
assign := ac.compile(bc.block, lt)
if assign == nil {
return
}
// Compile
if n == 1 {
// Don't need temporaries and can avoid []Value.
lf := ls[0].evalAddr
a.push(func(t *Thread) { assign(lf(t), t) })
} else if tok == token.VAR || (tok == token.DEFINE && nDefs == n) {
// Don't need temporaries
lfs := make([]func(*Thread) Value, n)
for i, l := range ls {
lfs[i] = l.evalAddr
}
a.push(func(t *Thread) {
dest := make([]Value, n)
for i, lf := range lfs {
dest[i] = lf(t)
}
assign(multiV(dest), t)
})
} else {
// Need temporaries
lmt := lt.(*MultiType)
lfs := make([]func(*Thread) Value, n)
for i, l := range ls {
lfs[i] = l.evalAddr
}
a.push(func(t *Thread) {
temp := lmt.Zero().(multiV)
assign(temp, t)
// Copy to destination
for i := 0; i < n; i++ {
// TODO(austin) Need to evaluate LHS
// before RHS
lfs[i](t).Assign(t, temp[i])
}
})
}
}
var assignOpToOp = map[token.Token]token.Token{
token.ADD_ASSIGN: token.ADD,
token.SUB_ASSIGN: token.SUB,
token.MUL_ASSIGN: token.MUL,
token.QUO_ASSIGN: token.QUO,
token.REM_ASSIGN: token.REM,
token.AND_ASSIGN: token.AND,
token.OR_ASSIGN: token.OR,
token.XOR_ASSIGN: token.XOR,
token.SHL_ASSIGN: token.SHL,
token.SHR_ASSIGN: token.SHR,
token.AND_NOT_ASSIGN: token.AND_NOT,
}
func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) {
if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
a.diag("tuple assignment cannot be combined with an arithmetic operation")
return
}
// Create temporary block for extractEffect
bc := a.enterChild()
defer bc.exit()
l := a.compileExpr(bc.block, false, s.Lhs[0])
r := a.compileExpr(bc.block, false, s.Rhs[0])
if l == nil || r == nil {
return
}
if l.evalAddr == nil {
l.diag("cannot assign to %s", l.desc)
return
}
effect, l := l.extractEffect(bc.block, "operator-assignment")
binop := r.compileBinaryExpr(assignOpToOp[s.Tok], l, r)
if binop == nil {
return
}
assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "assignment", "value")
if assign == nil {
log.Panicf("compileAssign type check failed")
}
lf := l.evalAddr
a.push(func(t *Thread) {
effect(t)
assign(lf(t), t)
})
}
func (a *stmtCompiler) compileAssignStmt(s *ast.AssignStmt) {
switch s.Tok {
case token.ASSIGN, token.DEFINE:
a.doAssign(s.Lhs, s.Rhs, s.Tok, nil)
default:
a.doAssignOp(s)
}
}
func (a *stmtCompiler) compileReturnStmt(s *ast.ReturnStmt) {
if a.fnType == nil {
a.diag("cannot return at the top level")
return
}
if len(s.Results) == 0 && (len(a.fnType.Out) == 0 || a.outVarsNamed) {
// Simple case. Simply exit from the function.
a.flow.putTerm()
a.push(func(v *Thread) { v.pc = returnPC })
return
}
bc := a.enterChild()
defer bc.exit()
// Compile expressions
bad := false
rs := make([]*expr, len(s.Results))
for i, re := range s.Results {
rs[i] = a.compileExpr(bc.block, false, re)
if rs[i] == nil {
bad = true
}
}
if bad {
return
}
// Create assigner
// However, if the expression list in the "return" statement
// is a single call to a multi-valued function, the values
// returned from the called function will be returned from
// this one.
assign := a.compileAssign(s.Pos(), bc.block, NewMultiType(a.fnType.Out), rs, "return", "value")
// XXX(Spec) "The result types of the current function and the
// called function must match." Match is fuzzy. It should
// say that they must be assignment compatible.
// Compile
start := len(a.fnType.In)
nout := len(a.fnType.Out)
a.flow.putTerm()
a.push(func(t *Thread) {
assign(multiV(t.f.Vars[start:start+nout]), t)
t.pc = returnPC
})
}
func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, errOp, errCtx string) *label {
bc := a.blockCompiler
for ; bc != nil; bc = bc.parent {
if bc.label == nil {
continue
}
l := bc.label
if name == nil && pred(l) {
return l
}
if name != nil && l.name == name.Name {
if !pred(l) {
a.diag("cannot %s to %s %s", errOp, l.desc, l.name)
return nil
}
return l
}
}
if name == nil {
a.diag("%s outside %s", errOp, errCtx)
} else {
a.diag("%s label %s not defined", errOp, name.Name)
}
return nil
}
func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) {
var pc *uint
switch s.Tok {
case token.BREAK:
l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.breakPC != nil }, "break", "for loop, switch, or select")
if l == nil {
return
}
pc = l.breakPC
case token.CONTINUE:
l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.continuePC != nil }, "continue", "for loop")
if l == nil {
return
}
pc = l.continuePC
case token.GOTO:
l, ok := a.labels[s.Label.Name]
if !ok {
pc := badPC
l = &label{name: s.Label.Name, desc: "unresolved label", gotoPC: &pc, used: s.Pos()}
a.labels[l.name] = l
}
pc = l.gotoPC
a.flow.putGoto(s.Pos(), l.name, a.block)
case token.FALLTHROUGH:
a.diag("fallthrough outside switch")
return
default:
log.Panicf("Unexpected branch token %v", s.Tok)
}
a.flow.put1(false, pc)
a.push(func(v *Thread) { v.pc = *pc })
}
func (a *stmtCompiler) compileBlockStmt(s *ast.BlockStmt) {
bc := a.enterChild()
bc.compileStmts(s)
bc.exit()
}
func (a *stmtCompiler) compileIfStmt(s *ast.IfStmt) {
// The scope of any variables declared by [the init] statement
// extends to the end of the "if" statement and the variables
// are initialized once before the statement is entered.
//
// XXX(Spec) What this really wants to say is that there's an
// implicit scope wrapping every if, for, and switch
// statement. This is subtly different from what it actually
// says when there's a non-block else clause, because that
// else claus has to execute in a scope that is *not* the
// surrounding scope.
bc := a.enterChild()
defer bc.exit()
// Compile init statement, if any
if s.Init != nil {
bc.compileStmt(s.Init)
}
elsePC := badPC
endPC := badPC
// Compile condition, if any. If there is no condition, we
// fall through to the body.
if s.Cond != nil {
e := bc.compileExpr(bc.block, false, s.Cond)
switch {
case e == nil:
// Error reported by compileExpr
case !e.t.isBoolean():
e.diag("'if' condition must be boolean\n\t%v", e.t)
default:
eval := e.asBool()
a.flow.put1(true, &elsePC)
a.push(func(t *Thread) {
if !eval(t) {
t.pc = elsePC
}
})
}
}
// Compile body
body := bc.enterChild()
body.compileStmts(s.Body)
body.exit()
// Compile else
if s.Else != nil {
// Skip over else if we executed the body
a.flow.put1(false, &endPC)
a.push(func(v *Thread) { v.pc = endPC })
elsePC = a.nextPC()
bc.compileStmt(s.Else)
} else {
elsePC = a.nextPC()
}
endPC = a.nextPC()
}
func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
// Create implicit scope around switch
bc := a.enterChild()
defer bc.exit()
// Compile init statement, if any
if s.Init != nil {
bc.compileStmt(s.Init)
}
// Compile condition, if any, and extract its effects
var cond *expr
condbc := bc.enterChild()
if s.Tag != nil {
e := condbc.compileExpr(condbc.block, false, s.Tag)
if e != nil {
var effect func(*Thread)
effect, cond = e.extractEffect(condbc.block, "switch")
a.push(effect)
}
}
// Count cases
ncases := 0
hasDefault := false
for _, c := range s.Body.List {
clause, ok := c.(*ast.CaseClause)
if !ok {
a.diagAt(clause.Pos(), "switch statement must contain case clauses")
continue
}
if clause.List == nil {
if hasDefault {
a.diagAt(clause.Pos(), "switch statement contains more than one default case")
}
hasDefault = true
} else {
ncases += len(clause.List)
}
}
// Compile case expressions
cases := make([]func(*Thread) bool, ncases)
i := 0
for _, c := range s.Body.List {
clause, ok := c.(*ast.CaseClause)
if !ok {
continue
}
for _, v := range clause.List {
e := condbc.compileExpr(condbc.block, false, v)
switch {
case e == nil:
// Error reported by compileExpr
case cond == nil && !e.t.isBoolean():
a.diagAt(v.Pos(), "'case' condition must be boolean")
case cond == nil:
cases[i] = e.asBool()
case cond != nil:
// Create comparison
// TOOD(austin) This produces bad error messages
compare := e.compileBinaryExpr(token.EQL, cond, e)
if compare != nil {
cases[i] = compare.asBool()
}
}
i++
}
}
// Emit condition
casePCs := make([]*uint, ncases+1)
endPC := badPC
a.flow.put(false, false, casePCs)
a.push(func(t *Thread) {
for i, c := range cases {
if c(t) {
t.pc = *casePCs[i]
return
}
}
t.pc = *casePCs[ncases]
})
condbc.exit()
// Compile cases
i = 0
for _, c := range s.Body.List {
clause, ok := c.(*ast.CaseClause)
if !ok {
continue
}
// Save jump PC's
pc := a.nextPC()
if clause.List != nil {
for _ = range clause.List {
casePCs[i] = &pc
i++
}
} else {
// Default clause
casePCs[ncases] = &pc
}
// Compile body
fall := false
for j, s := range clause.Body {
if br, ok := s.(*ast.BranchStmt); ok && br.Tok == token.FALLTHROUGH {
// println("Found fallthrough");
// It may be used only as the final
// non-empty statement in a case or
// default clause in an expression
// "switch" statement.
for _, s2 := range clause.Body[j+1:] {
// XXX(Spec) 6g also considers
// empty blocks to be empty
// statements.
if _, ok := s2.(*ast.EmptyStmt); !ok {
a.diagAt(s.Pos(), "fallthrough statement must be final statement in case")
break
}
}
fall = true
} else {
bc.compileStmt(s)
}
}
// Jump out of switch, unless there was a fallthrough
if !fall {
a.flow.put1(false, &endPC)
a.push(func(v *Thread) { v.pc = endPC })
}
}
// Get end PC
endPC = a.nextPC()
if !hasDefault {
casePCs[ncases] = &endPC
}
}
func (a *stmtCompiler) compileForStmt(s *ast.ForStmt) {
// Wrap the entire for in a block.
bc := a.enterChild()
defer bc.exit()
// Compile init statement, if any
if s.Init != nil {
bc.compileStmt(s.Init)
}
bodyPC := badPC
postPC := badPC
checkPC := badPC
endPC := badPC
// Jump to condition check. We generate slightly less code by
// placing the condition check after the body.
a.flow.put1(false, &checkPC)
a.push(func(v *Thread) { v.pc = checkPC })
// Compile body
bodyPC = a.nextPC()
body := bc.enterChild()
if a.stmtLabel != nil {
body.label = a.stmtLabel
} else {
body.label = &label{resolved: s.Pos()}
}
body.label.desc = "for loop"
body.label.breakPC = &endPC
body.label.continuePC = &postPC
body.compileStmts(s.Body)
body.exit()
// Compile post, if any
postPC = a.nextPC()
if s.Post != nil {
// TODO(austin) Does the parser disallow short
// declarations in s.Post?
bc.compileStmt(s.Post)
}
// Compile condition check, if any
checkPC = a.nextPC()
if s.Cond == nil {
// If the condition is absent, it is equivalent to true.
a.flow.put1(false, &bodyPC)
a.push(func(v *Thread) { v.pc = bodyPC })
} else {
e := bc.compileExpr(bc.block, false, s.Cond)
switch {
case e == nil:
// Error reported by compileExpr
case !e.t.isBoolean():
a.diag("'for' condition must be boolean\n\t%v", e.t)
default:
eval := e.asBool()
a.flow.put1(true, &bodyPC)
a.push(func(t *Thread) {
if eval(t) {
t.pc = bodyPC
}
})
}
}
endPC = a.nextPC()
}
/*
* Block compiler
*/
func (a *blockCompiler) compileStmt(s ast.Stmt) {
sc := &stmtCompiler{a, s.Pos(), nil}
sc.compile(s)
}
func (a *blockCompiler) compileStmts(block *ast.BlockStmt) {
for _, sub := range block.List {
a.compileStmt(sub)
}
}
func (a *blockCompiler) enterChild() *blockCompiler {
block := a.block.enterChild()
return &blockCompiler{
funcCompiler: a.funcCompiler,
block: block,
parent: a,
}
}
func (a *blockCompiler) exit() { a.block.exit() }
/*
* Function compiler
*/
func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) func(*Thread) Func {
// Create body scope
//
// The scope of a parameter or result is the body of the
// corresponding function.
bodyScope := b.ChildScope()
defer bodyScope.exit()
for i, t := range decl.Type.In {
if decl.InNames[i] != nil {
bodyScope.DefineVar(decl.InNames[i].Name, decl.InNames[i].Pos(), t)
} else {
bodyScope.DefineTemp(t)
}
}
for i, t := range decl.Type.Out {
if decl.OutNames[i] != nil {
bodyScope.DefineVar(decl.OutNames[i].Name, decl.OutNames[i].Pos(), t)
} else {
bodyScope.DefineTemp(t)
}
}
// Create block context
cb := newCodeBuf()
fc := &funcCompiler{
compiler: a,
fnType: decl.Type,
outVarsNamed: len(decl.OutNames) > 0 && decl.OutNames[0] != nil,
codeBuf: cb,
flow: newFlowBuf(cb),
labels: make(map[string]*label),
}
bc := &blockCompiler{
funcCompiler: fc,
block: bodyScope.block,
}
// Compile body
nerr := a.numError()
bc.compileStmts(body)
fc.checkLabels()
if nerr != a.numError() {
return nil
}
// Check that the body returned if necessary. We only check
// this if there were no errors compiling the body.
if len(decl.Type.Out) > 0 && fc.flow.reachesEnd(0) {
// XXX(Spec) Not specified.
a.diagAt(body.Rbrace, "function ends without a return statement")
return nil
}
code := fc.get()
maxVars := bodyScope.maxVars
return func(t *Thread) Func { return &evalFunc{t.f, maxVars, code} }
}
// Checks that labels were resolved and that all jumps obey scoping
// rules. Reports an error and set fc.err if any check fails.
func (a *funcCompiler) checkLabels() {
nerr := a.numError()
for _, l := range a.labels {
if !l.resolved.IsValid() {
a.diagAt(l.used, "label %s not defined", l.name)
}
}
if nerr != a.numError() {
// Don't check scopes if we have unresolved labels
return
}
// Executing the "goto" statement must not cause any variables
// to come into scope that were not already in scope at the
// point of the goto.
a.flow.gotosObeyScopes(a.compiler)
}
// 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) }
#!/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
// 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"
"go/ast"
"go/token"
"log"
"reflect"
"sort"
"unsafe" // For Sizeof
)
// XXX(Spec) The type compatibility section is very confusing because
// it makes it seem like there are three distinct types of
// compatibility: plain compatibility, assignment compatibility, and
// comparison compatibility. As I understand it, there's really only
// assignment compatibility and comparison and conversion have some
// restrictions and have special meaning in some cases where the types
// are not otherwise assignment compatible. The comparison
// compatibility section is almost all about the semantics of
// comparison, not the type checking of it, so it would make much more
// sense in the comparison operators section. The compatibility and
// assignment compatibility sections should be rolled into one.
type Type interface {
// compat returns whether this type is compatible with another
// type. If conv is false, this is normal compatibility,
// where two named types are compatible only if they are the
// same named type. If conv if true, this is conversion
// compatibility, where two named types are conversion
// compatible if their definitions are conversion compatible.
//
// TODO(austin) Deal with recursive types
compat(o Type, conv bool) bool
// lit returns this type's literal. If this is a named type,
// this is the unnamed underlying type. Otherwise, this is an
// identity operation.
lit() Type
// isBoolean returns true if this is a boolean type.
isBoolean() bool
// isInteger returns true if this is an integer type.
isInteger() bool
// isFloat returns true if this is a floating type.
isFloat() bool
// isIdeal returns true if this is an ideal int or float.
isIdeal() bool
// Zero returns a new zero value of this type.
Zero() Value
// String returns the string representation of this type.
String() string
// The position where this type was defined, if any.
Pos() token.Pos
}
type BoundedType interface {
Type
// minVal returns the smallest value of this type.
minVal() *big.Rat
// maxVal returns the largest value of this type.
maxVal() *big.Rat
}
var universePos = token.NoPos
/*
* Type array maps. These are used to memoize composite types.
*/
type typeArrayMapEntry struct {
key []Type
v interface{}
next *typeArrayMapEntry
}
type typeArrayMap map[uintptr]*typeArrayMapEntry
func hashTypeArray(key []Type) uintptr {
hash := uintptr(0)
for _, t := range key {
hash = hash * 33
if t == nil {
continue
}
addr := reflect.ValueOf(t).Pointer()
hash ^= addr
}
return hash
}
func newTypeArrayMap() typeArrayMap { return make(map[uintptr]*typeArrayMapEntry) }
func (m typeArrayMap) Get(key []Type) interface{} {
ent, ok := m[hashTypeArray(key)]
if !ok {
return nil
}
nextEnt:
for ; ent != nil; ent = ent.next {
if len(key) != len(ent.key) {
continue
}
for i := 0; i < len(key); i++ {
if key[i] != ent.key[i] {
continue nextEnt
}
}
// Found it
return ent.v
}
return nil
}
func (m typeArrayMap) Put(key []Type, v interface{}) interface{} {
hash := hashTypeArray(key)
ent := m[hash]
new := &typeArrayMapEntry{key, v, ent}
m[hash] = new
return v
}
/*
* Common type
*/
type commonType struct{}
func (commonType) isBoolean() bool { return false }
func (commonType) isInteger() bool { return false }
func (commonType) isFloat() bool { return false }
func (commonType) isIdeal() bool { return false }
func (commonType) Pos() token.Pos { return token.NoPos }
/*
* Bool
*/
type boolType struct {
commonType
}
var BoolType = universe.DefineType("bool", universePos, &boolType{})
func (t *boolType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*boolType)
return ok
}
func (t *boolType) lit() Type { return t }
func (t *boolType) isBoolean() bool { return true }
func (boolType) String() string {
// Use angle brackets as a convention for printing the
// underlying, unnamed type. This should only show up in
// debug output.
return "<bool>"
}
func (t *boolType) Zero() Value {
res := boolV(false)
return &res
}
/*
* Uint
*/
type uintType struct {
commonType
// 0 for architecture-dependent types
Bits uint
// true for uintptr, false for all others
Ptr bool
name string
}
var (
Uint8Type = universe.DefineType("uint8", universePos, &uintType{commonType{}, 8, false, "uint8"})
Uint16Type = universe.DefineType("uint16", universePos, &uintType{commonType{}, 16, false, "uint16"})
Uint32Type = universe.DefineType("uint32", universePos, &uintType{commonType{}, 32, false, "uint32"})
Uint64Type = universe.DefineType("uint64", universePos, &uintType{commonType{}, 64, false, "uint64"})
UintType = universe.DefineType("uint", universePos, &uintType{commonType{}, 0, false, "uint"})
UintptrType = universe.DefineType("uintptr", universePos, &uintType{commonType{}, 0, true, "uintptr"})
)
func (t *uintType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*uintType)
return ok && t == t2
}
func (t *uintType) lit() Type { return t }
func (t *uintType) isInteger() bool { return true }
func (t *uintType) String() string { return "<" + t.name + ">" }
func (t *uintType) Zero() Value {
switch t.Bits {
case 0:
if t.Ptr {
res := uintptrV(0)
return &res
} else {
res := uintV(0)
return &res
}
case 8:
res := uint8V(0)
return &res
case 16:
res := uint16V(0)
return &res
case 32:
res := uint32V(0)
return &res
case 64:
res := uint64V(0)
return &res
}
panic("unexpected uint bit count")
}
func (t *uintType) minVal() *big.Rat { return big.NewRat(0, 1) }
func (t *uintType) maxVal() *big.Rat {
bits := t.Bits
if bits == 0 {
if t.Ptr {
bits = uint(8 * unsafe.Sizeof(uintptr(0)))
} else {
bits = uint(8 * unsafe.Sizeof(uint(0)))
}
}
numer := big.NewInt(1)
numer.Lsh(numer, bits)
numer.Sub(numer, idealOne)
return new(big.Rat).SetInt(numer)
}
/*
* Int
*/
type intType struct {
commonType
// XXX(Spec) Numeric types: "There is also a set of
// architecture-independent basic numeric types whose size
// depends on the architecture." Should that be
// architecture-dependent?
// 0 for architecture-dependent types
Bits uint
name string
}
var (
Int8Type = universe.DefineType("int8", universePos, &intType{commonType{}, 8, "int8"})
Int16Type = universe.DefineType("int16", universePos, &intType{commonType{}, 16, "int16"})
Int32Type = universe.DefineType("int32", universePos, &intType{commonType{}, 32, "int32"})
Int64Type = universe.DefineType("int64", universePos, &intType{commonType{}, 64, "int64"})
IntType = universe.DefineType("int", universePos, &intType{commonType{}, 0, "int"})
)
func (t *intType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*intType)
return ok && t == t2
}
func (t *intType) lit() Type { return t }
func (t *intType) isInteger() bool { return true }
func (t *intType) String() string { return "<" + t.name + ">" }
func (t *intType) Zero() Value {
switch t.Bits {
case 8:
res := int8V(0)
return &res
case 16:
res := int16V(0)
return &res
case 32:
res := int32V(0)
return &res
case 64:
res := int64V(0)
return &res
case 0:
res := intV(0)
return &res
}
panic("unexpected int bit count")
}
func (t *intType) minVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(int(0)))
}
numer := big.NewInt(-1)
numer.Lsh(numer, bits-1)
return new(big.Rat).SetInt(numer)
}
func (t *intType) maxVal() *big.Rat {
bits := t.Bits
if bits == 0 {
bits = uint(8 * unsafe.Sizeof(int(0)))
}
numer := big.NewInt(1)
numer.Lsh(numer, bits-1)
numer.Sub(numer, idealOne)
return new(big.Rat).SetInt(numer)
}
/*
* Ideal int
*/
type idealIntType struct {
commonType
}
var IdealIntType Type = &idealIntType{}
func (t *idealIntType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*idealIntType)
return ok
}
func (t *idealIntType) lit() Type { return t }
func (t *idealIntType) isInteger() bool { return true }
func (t *idealIntType) isIdeal() bool { return true }
func (t *idealIntType) String() string { return "ideal integer" }
func (t *idealIntType) Zero() Value { return &idealIntV{idealZero} }
/*
* Float
*/
type floatType struct {
commonType
// 0 for architecture-dependent type
Bits uint
name string
}
var (
Float32Type = universe.DefineType("float32", universePos, &floatType{commonType{}, 32, "float32"})
Float64Type = universe.DefineType("float64", universePos, &floatType{commonType{}, 64, "float64"})
)
func (t *floatType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*floatType)
return ok && t == t2
}
func (t *floatType) lit() Type { return t }
func (t *floatType) isFloat() bool { return true }
func (t *floatType) String() string { return "<" + t.name + ">" }
func (t *floatType) Zero() Value {
switch t.Bits {
case 32:
res := float32V(0)
return &res
case 64:
res := float64V(0)
return &res
}
panic("unexpected float bit count")
}
var maxFloat32Val *big.Rat
var maxFloat64Val *big.Rat
var minFloat32Val *big.Rat
var minFloat64Val *big.Rat
func (t *floatType) minVal() *big.Rat {
bits := t.Bits
switch bits {
case 32:
return minFloat32Val
case 64:
return minFloat64Val
}
log.Panicf("unexpected floating point bit count: %d", bits)
panic("unreachable")
}
func (t *floatType) maxVal() *big.Rat {
bits := t.Bits
switch bits {
case 32:
return maxFloat32Val
case 64:
return maxFloat64Val
}
log.Panicf("unexpected floating point bit count: %d", bits)
panic("unreachable")
}
/*
* Ideal float
*/
type idealFloatType struct {
commonType
}
var IdealFloatType Type = &idealFloatType{}
func (t *idealFloatType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*idealFloatType)
return ok
}
func (t *idealFloatType) lit() Type { return t }
func (t *idealFloatType) isFloat() bool { return true }
func (t *idealFloatType) isIdeal() bool { return true }
func (t *idealFloatType) String() string { return "ideal float" }
func (t *idealFloatType) Zero() Value { return &idealFloatV{big.NewRat(0, 1)} }
/*
* String
*/
type stringType struct {
commonType
}
var StringType = universe.DefineType("string", universePos, &stringType{})
func (t *stringType) compat(o Type, conv bool) bool {
_, ok := o.lit().(*stringType)
return ok
}
func (t *stringType) lit() Type { return t }
func (t *stringType) String() string { return "<string>" }
func (t *stringType) Zero() Value {
res := stringV("")
return &res
}
/*
* Array
*/
type ArrayType struct {
commonType
Len int64
Elem Type
}
var arrayTypes = make(map[int64]map[Type]*ArrayType)
// Two array types are identical if they have identical element types
// and the same array length.
func NewArrayType(len int64, elem Type) *ArrayType {
ts, ok := arrayTypes[len]
if !ok {
ts = make(map[Type]*ArrayType)
arrayTypes[len] = ts
}
t, ok := ts[elem]
if !ok {
t = &ArrayType{commonType{}, len, elem}
ts[elem] = t
}
return t
}
func (t *ArrayType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*ArrayType)
if !ok {
return false
}
return t.Len == t2.Len && t.Elem.compat(t2.Elem, conv)
}
func (t *ArrayType) lit() Type { return t }
func (t *ArrayType) String() string { return "[]" + t.Elem.String() }
func (t *ArrayType) Zero() Value {
res := arrayV(make([]Value, t.Len))
// TODO(austin) It's unfortunate that each element is
// separately heap allocated. We could add ZeroArray to
// everything, though that doesn't help with multidimensional
// arrays. Or we could do something unsafe. We'll have this
// same problem with structs.
for i := int64(0); i < t.Len; i++ {
res[i] = t.Elem.Zero()
}
return &res
}
/*
* Struct
*/
type StructField struct {
Name string
Type Type
Anonymous bool
}
type StructType struct {
commonType
Elems []StructField
}
var structTypes = newTypeArrayMap()
// Two struct types are identical if they have the same sequence of
// fields, and if corresponding fields have the same names and
// identical types. Two anonymous fields are considered to have the
// same name.
func NewStructType(fields []StructField) *StructType {
// Start by looking up just the types
fts := make([]Type, len(fields))
for i, f := range fields {
fts[i] = f.Type
}
tMapI := structTypes.Get(fts)
if tMapI == nil {
tMapI = structTypes.Put(fts, make(map[string]*StructType))
}
tMap := tMapI.(map[string]*StructType)
// Construct key for field names
key := ""
for _, f := range fields {
// XXX(Spec) It's not clear if struct { T } and struct
// { T T } are either identical or compatible. The
// "Struct Types" section says that the name of that
// field is "T", which suggests that they are
// identical, but it really means that it's the name
// for the purpose of selector expressions and nothing
// else. We decided that they should be neither
// identical or compatible.
if f.Anonymous {
key += "!"
}
key += f.Name + " "
}
// XXX(Spec) Do the tags also have to be identical for the
// types to be identical? I certainly hope so, because
// otherwise, this is the only case where two distinct type
// objects can represent identical types.
t, ok := tMap[key]
if !ok {
// Create new struct type
t = &StructType{commonType{}, fields}
tMap[key] = t
}
return t
}
func (t *StructType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*StructType)
if !ok {
return false
}
if len(t.Elems) != len(t2.Elems) {
return false
}
for i, e := range t.Elems {
e2 := t2.Elems[i]
// XXX(Spec) An anonymous and a non-anonymous field
// are neither identical nor compatible.
if e.Anonymous != e2.Anonymous ||
(!e.Anonymous && e.Name != e2.Name) ||
!e.Type.compat(e2.Type, conv) {
return false
}
}
return true
}
func (t *StructType) lit() Type { return t }
func (t *StructType) String() string {
s := "struct {"
for i, f := range t.Elems {
if i > 0 {
s += "; "
}
if !f.Anonymous {
s += f.Name + " "
}
s += f.Type.String()
}
return s + "}"
}
func (t *StructType) Zero() Value {
res := structV(make([]Value, len(t.Elems)))
for i, f := range t.Elems {
res[i] = f.Type.Zero()
}
return &res
}
/*
* Pointer
*/
type PtrType struct {
commonType
Elem Type
}
var ptrTypes = make(map[Type]*PtrType)
// Two pointer types are identical if they have identical base types.
func NewPtrType(elem Type) *PtrType {
t, ok := ptrTypes[elem]
if !ok {
t = &PtrType{commonType{}, elem}
ptrTypes[elem] = t
}
return t
}
func (t *PtrType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*PtrType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv)
}
func (t *PtrType) lit() Type { return t }
func (t *PtrType) String() string { return "*" + t.Elem.String() }
func (t *PtrType) Zero() Value { return &ptrV{nil} }
/*
* Function
*/
type FuncType struct {
commonType
// TODO(austin) Separate receiver Type for methods?
In []Type
Variadic bool
Out []Type
builtin string
}
var funcTypes = newTypeArrayMap()
var variadicFuncTypes = newTypeArrayMap()
// Create singleton function types for magic built-in functions
var (
capType = &FuncType{builtin: "cap"}
closeType = &FuncType{builtin: "close"}
closedType = &FuncType{builtin: "closed"}
lenType = &FuncType{builtin: "len"}
makeType = &FuncType{builtin: "make"}
newType = &FuncType{builtin: "new"}
panicType = &FuncType{builtin: "panic"}
printType = &FuncType{builtin: "print"}
printlnType = &FuncType{builtin: "println"}
copyType = &FuncType{builtin: "copy"}
)
// Two function types are identical if they have the same number of
// parameters and result values and if corresponding parameter and
// result types are identical. All "..." parameters have identical
// type. Parameter and result names are not required to match.
func NewFuncType(in []Type, variadic bool, out []Type) *FuncType {
inMap := funcTypes
if variadic {
inMap = variadicFuncTypes
}
outMapI := inMap.Get(in)
if outMapI == nil {
outMapI = inMap.Put(in, newTypeArrayMap())
}
outMap := outMapI.(typeArrayMap)
tI := outMap.Get(out)
if tI != nil {
return tI.(*FuncType)
}
t := &FuncType{commonType{}, in, variadic, out, ""}
outMap.Put(out, t)
return t
}
func (t *FuncType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*FuncType)
if !ok {
return false
}
if len(t.In) != len(t2.In) || t.Variadic != t2.Variadic || len(t.Out) != len(t2.Out) {
return false
}
for i := range t.In {
if !t.In[i].compat(t2.In[i], conv) {
return false
}
}
for i := range t.Out {
if !t.Out[i].compat(t2.Out[i], conv) {
return false
}
}
return true
}
func (t *FuncType) lit() Type { return t }
func typeListString(ts []Type, ns []*ast.Ident) string {
s := ""
for i, t := range ts {
if i > 0 {
s += ", "
}
if ns != nil && ns[i] != nil {
s += ns[i].Name + " "
}
if t == nil {
// Some places use nil types to represent errors
s += "<none>"
} else {
s += t.String()
}
}
return s
}
func (t *FuncType) String() string {
if t.builtin != "" {
return "built-in function " + t.builtin
}
args := typeListString(t.In, nil)
if t.Variadic {
if len(args) > 0 {
args += ", "
}
args += "..."
}
s := "func(" + args + ")"
if len(t.Out) > 0 {
s += " (" + typeListString(t.Out, nil) + ")"
}
return s
}
func (t *FuncType) Zero() Value { return &funcV{nil} }
type FuncDecl struct {
Type *FuncType
Name *ast.Ident // nil for function literals
// InNames will be one longer than Type.In if this function is
// variadic.
InNames []*ast.Ident
OutNames []*ast.Ident
}
func (t *FuncDecl) String() string {
s := "func"
if t.Name != nil {
s += " " + t.Name.Name
}
s += funcTypeString(t.Type, t.InNames, t.OutNames)
return s
}
func funcTypeString(ft *FuncType, ins []*ast.Ident, outs []*ast.Ident) string {
s := "("
s += typeListString(ft.In, ins)
if ft.Variadic {
if len(ft.In) > 0 {
s += ", "
}
s += "..."
}
s += ")"
if len(ft.Out) > 0 {
s += " (" + typeListString(ft.Out, outs) + ")"
}
return s
}
/*
* Interface
*/
// TODO(austin) Interface values, types, and type compilation are
// implemented, but none of the type checking or semantics of
// interfaces are.
type InterfaceType struct {
commonType
// TODO(austin) This should be a map from names to
// *FuncType's. We only need the sorted list for generating
// the type map key. It's detrimental for everything else.
methods []IMethod
}
type IMethod struct {
Name string
Type *FuncType
}
var interfaceTypes = newTypeArrayMap()
func NewInterfaceType(methods []IMethod, embeds []*InterfaceType) *InterfaceType {
// Count methods of embedded interfaces
nMethods := len(methods)
for _, e := range embeds {
nMethods += len(e.methods)
}
// Combine methods
allMethods := make([]IMethod, nMethods)
copy(allMethods, methods)
n := len(methods)
for _, e := range embeds {
for _, m := range e.methods {
allMethods[n] = m
n++
}
}
// Sort methods
sort.Sort(iMethodSorter(allMethods))
mts := make([]Type, len(allMethods))
for i, m := range methods {
mts[i] = m.Type
}
tMapI := interfaceTypes.Get(mts)
if tMapI == nil {
tMapI = interfaceTypes.Put(mts, make(map[string]*InterfaceType))
}
tMap := tMapI.(map[string]*InterfaceType)
key := ""
for _, m := range allMethods {
key += m.Name + " "
}
t, ok := tMap[key]
if !ok {
t = &InterfaceType{commonType{}, allMethods}
tMap[key] = t
}
return t
}
type iMethodSorter []IMethod
func (s iMethodSorter) Less(a, b int) bool { return s[a].Name < s[b].Name }
func (s iMethodSorter) Swap(a, b int) { s[a], s[b] = s[b], s[a] }
func (s iMethodSorter) Len() int { return len(s) }
func (t *InterfaceType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*InterfaceType)
if !ok {
return false
}
if len(t.methods) != len(t2.methods) {
return false
}
for i, e := range t.methods {
e2 := t2.methods[i]
if e.Name != e2.Name || !e.Type.compat(e2.Type, conv) {
return false
}
}
return true
}
func (t *InterfaceType) lit() Type { return t }
func (t *InterfaceType) String() string {
// TODO(austin) Instead of showing embedded interfaces, this
// shows their methods.
s := "interface {"
for i, m := range t.methods {
if i > 0 {
s += "; "
}
s += m.Name + funcTypeString(m.Type, nil, nil)
}
return s + "}"
}
// implementedBy tests if o implements t, returning nil, true if it does.
// Otherwise, it returns a method of t that o is missing and false.
func (t *InterfaceType) implementedBy(o Type) (*IMethod, bool) {
if len(t.methods) == 0 {
return nil, true
}
// The methods of a named interface types are those of the
// underlying type.
if it, ok := o.lit().(*InterfaceType); ok {
o = it
}
// XXX(Spec) Interface types: "A type implements any interface
// comprising any subset of its methods" It's unclear if
// methods must have identical or compatible types. 6g
// requires identical types.
switch o := o.(type) {
case *NamedType:
for _, tm := range t.methods {
sm, ok := o.methods[tm.Name]
if !ok || sm.decl.Type != tm.Type {
return &tm, false
}
}
return nil, true
case *InterfaceType:
var ti, oi int
for ti < len(t.methods) && oi < len(o.methods) {
tm, om := &t.methods[ti], &o.methods[oi]
switch {
case tm.Name == om.Name:
if tm.Type != om.Type {
return tm, false
}
ti++
oi++
case tm.Name > om.Name:
oi++
default:
return tm, false
}
}
if ti < len(t.methods) {
return &t.methods[ti], false
}
return nil, true
}
return &t.methods[0], false
}
func (t *InterfaceType) Zero() Value { return &interfaceV{} }
/*
* Slice
*/
type SliceType struct {
commonType
Elem Type
}
var sliceTypes = make(map[Type]*SliceType)
// Two slice types are identical if they have identical element types.
func NewSliceType(elem Type) *SliceType {
t, ok := sliceTypes[elem]
if !ok {
t = &SliceType{commonType{}, elem}
sliceTypes[elem] = t
}
return t
}
func (t *SliceType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*SliceType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv)
}
func (t *SliceType) lit() Type { return t }
func (t *SliceType) String() string { return "[]" + t.Elem.String() }
func (t *SliceType) Zero() Value {
// The value of an uninitialized slice is nil. The length and
// capacity of a nil slice are 0.
return &sliceV{Slice{nil, 0, 0}}
}
/*
* Map type
*/
type MapType struct {
commonType
Key Type
Elem Type
}
var mapTypes = make(map[Type]map[Type]*MapType)
func NewMapType(key Type, elem Type) *MapType {
ts, ok := mapTypes[key]
if !ok {
ts = make(map[Type]*MapType)
mapTypes[key] = ts
}
t, ok := ts[elem]
if !ok {
t = &MapType{commonType{}, key, elem}
ts[elem] = t
}
return t
}
func (t *MapType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*MapType)
if !ok {
return false
}
return t.Elem.compat(t2.Elem, conv) && t.Key.compat(t2.Key, conv)
}
func (t *MapType) lit() Type { return t }
func (t *MapType) String() string { return "map[" + t.Key.String() + "] " + t.Elem.String() }
func (t *MapType) Zero() Value {
// The value of an uninitialized map is nil.
return &mapV{nil}
}
/*
type ChanType struct {
// TODO(austin)
}
*/
/*
* Named types
*/
type Method struct {
decl *FuncDecl
fn Func
}
type NamedType struct {
NamePos token.Pos
Name string
// Underlying type. If incomplete is true, this will be nil.
// If incomplete is false and this is still nil, then this is
// a placeholder type representing an error.
Def Type
// True while this type is being defined.
incomplete bool
methods map[string]Method
}
// TODO(austin) This is temporarily needed by the debugger's remote
// type parser. This should only be possible with block.DefineType.
func NewNamedType(name string) *NamedType {
return &NamedType{token.NoPos, name, nil, true, make(map[string]Method)}
}
func (t *NamedType) Pos() token.Pos {
return t.NamePos
}
func (t *NamedType) Complete(def Type) {
if !t.incomplete {
log.Panicf("cannot complete already completed NamedType %+v", *t)
}
// We strip the name from def because multiple levels of
// naming are useless.
if ndef, ok := def.(*NamedType); ok {
def = ndef.Def
}
t.Def = def
t.incomplete = false
}
func (t *NamedType) compat(o Type, conv bool) bool {
t2, ok := o.(*NamedType)
if ok {
if conv {
// Two named types are conversion compatible
// if their literals are conversion
// compatible.
return t.Def.compat(t2.Def, conv)
} else {
// Two named types are compatible if their
// type names originate in the same type
// declaration.
return t == t2
}
}
// A named and an unnamed type are compatible if the
// respective type literals are compatible.
return o.compat(t.Def, conv)
}
func (t *NamedType) lit() Type { return t.Def.lit() }
func (t *NamedType) isBoolean() bool { return t.Def.isBoolean() }
func (t *NamedType) isInteger() bool { return t.Def.isInteger() }
func (t *NamedType) isFloat() bool { return t.Def.isFloat() }
func (t *NamedType) isIdeal() bool { return false }
func (t *NamedType) String() string { return t.Name }
func (t *NamedType) Zero() Value { return t.Def.Zero() }
/*
* Multi-valued type
*/
// MultiType is a special type used for multi-valued expressions, akin
// to a tuple type. It's not generally accessible within the
// language.
type MultiType struct {
commonType
Elems []Type
}
var multiTypes = newTypeArrayMap()
func NewMultiType(elems []Type) *MultiType {
if t := multiTypes.Get(elems); t != nil {
return t.(*MultiType)
}
t := &MultiType{commonType{}, elems}
multiTypes.Put(elems, t)
return t
}
func (t *MultiType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*MultiType)
if !ok {
return false
}
if len(t.Elems) != len(t2.Elems) {
return false
}
for i := range t.Elems {
if !t.Elems[i].compat(t2.Elems[i], conv) {
return false
}
}
return true
}
var EmptyType Type = NewMultiType([]Type{})
func (t *MultiType) lit() Type { return t }
func (t *MultiType) String() string {
if len(t.Elems) == 0 {
return "<none>"
}
return typeListString(t.Elems, nil)
}
func (t *MultiType) Zero() Value {
res := make([]Value, len(t.Elems))
for i, t := range t.Elems {
res[i] = t.Zero()
}
return multiV(res)
}
/*
* Initialize the universe
*/
func init() {
numer := big.NewInt(0xffffff)
numer.Lsh(numer, 127-23)
maxFloat32Val = new(big.Rat).SetInt(numer)
numer.SetInt64(0x1fffffffffffff)
numer.Lsh(numer, 1023-52)
maxFloat64Val = new(big.Rat).SetInt(numer)
minFloat32Val = new(big.Rat).Neg(maxFloat32Val)
minFloat64Val = new(big.Rat).Neg(maxFloat64Val)
// To avoid portability issues all numeric types are distinct
// except byte, which is an alias for uint8.
// Make byte an alias for the named type uint8. Type aliases
// are otherwise impossible in Go, so just hack it here.
universe.defs["byte"] = universe.defs["uint8"]
// Built-in functions
universe.DefineConst("cap", universePos, capType, nil)
universe.DefineConst("close", universePos, closeType, nil)
universe.DefineConst("closed", universePos, closedType, nil)
universe.DefineConst("copy", universePos, copyType, nil)
universe.DefineConst("len", universePos, lenType, nil)
universe.DefineConst("make", universePos, makeType, nil)
universe.DefineConst("new", universePos, newType, nil)
universe.DefineConst("panic", universePos, panicType, nil)
universe.DefineConst("print", universePos, printType, nil)
universe.DefineConst("println", universePos, printlnType, nil)
}
// 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
}
// 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)
}
// 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
}
# 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 $@ $<
// 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
}
// 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}
// 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)
}
// 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)
}
// 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)
}
// 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
}
// 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() }
// 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()
}
// 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]))
}
}
}
// 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
}
// 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)
}
// 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 ...@@ -69,11 +69,6 @@ gomake clean
gotest gotest
) || exit $? ) || exit $?
(xcd pkg/exp/ogle
gomake clean
time gomake ogle
) || exit $?
(xcd ../doc/progs (xcd ../doc/progs
time ./run time ./run
) || exit $? ) || exit $?
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment