diff options
| author | Russ Cox <rsc@golang.org> | 2009-10-14 18:10:43 -0700 | 
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2009-10-14 18:10:43 -0700 | 
| commit | 8f49b30671161bc5425c4f2f56cee96ad11befeb (patch) | |
| tree | a545d9b275fbbc2346c40d8ea51b2c741ba9faa2 /src | |
| parent | 9615b0b1cfb8bbfc2b963b471bf9ac2afb7c8662 (diff) | |
| download | golang-8f49b30671161bc5425c4f2f56cee96ad11befeb.tar.gz | |
move austin/eval and austin/ogle to exp/eval and exp/ogle
R=r
OCL=35736
CL=35746
Diffstat (limited to 'src')
34 files changed, 12992 insertions, 3 deletions
| diff --git a/src/pkg/exp/eval/Makefile b/src/pkg/exp/eval/Makefile new file mode 100644 index 000000000..65bedf7ba --- /dev/null +++ b/src/pkg/exp/eval/Makefile @@ -0,0 +1,23 @@ +# 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 $(GOROOT)/src/Make.$(GOARCH) + +TARG=exp/eval +GOFILES=\ +	abort.go\ +	bridge.go\ +	compiler.go\ +	expr.go\ +	expr1.go\ +	func.go\ +	scope.go\ +	stmt.go\ +	type.go\ +	typec.go\ +	util.go\ +	value.go\ +	world.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/exp/eval/abort.go b/src/pkg/exp/eval/abort.go new file mode 100644 index 000000000..38ad2bf62 --- /dev/null +++ b/src/pkg/exp/eval/abort.go @@ -0,0 +1,91 @@ +// 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 { +		panicln("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); +} diff --git a/src/pkg/exp/eval/bridge.go b/src/pkg/exp/eval/bridge.go new file mode 100644 index 000000000..da2dd52a9 --- /dev/null +++ b/src/pkg/exp/eval/bridge.go @@ -0,0 +1,170 @@ +// 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.Position{}, name, nil, true, make(map[string] Method)}; +		evalTypes[t] = nt; +	} + +	var et Type; +	switch t := t.(type) { +	case *reflect.BoolType: +		et = BoolType; +	case *reflect.Float32Type: +		et = Float32Type; +	case *reflect.Float64Type: +		et = Float64Type; +	case *reflect.FloatType: +		et = FloatType; +	case *reflect.Int16Type: +		et = Int16Type; +	case *reflect.Int32Type: +		et = Int32Type; +	case *reflect.Int64Type: +		et = Int64Type; +	case *reflect.Int8Type: +		et = Int8Type; +	case *reflect.IntType: +		et = IntType; +	case *reflect.StringType: +		et = StringType; +	case *reflect.Uint16Type: +		et = Uint16Type; +	case *reflect.Uint32Type: +		et = Uint32Type; +	case *reflect.Uint64Type: +		et = Uint64Type; +	case *reflect.Uint8Type: +		et = Uint8Type; +	case *reflect.UintType: +		et = UintType; +	case *reflect.UintptrType: +		et = UintptrType; + +	case *reflect.ArrayType: +		et = NewArrayType(int64(t.Len()), TypeFromNative(t.Elem())); +	case *reflect.ChanType: +		log.Crashf("%T not implemented", t); +	case *reflect.FuncType: +		nin := t.NumIn(); +		// Variadic functions have DotDotDotType at the end +		varidic := false; +		if nin > 0 { +			if _, ok := t.In(nin - 1).(*reflect.DotDotDotType); ok { +				varidic = true; +				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, varidic, out); +	case *reflect.InterfaceType: +		log.Crashf("%T not implemented", t); +	case *reflect.MapType: +		log.Crashf("%T not implemented", t); +	case *reflect.PtrType: +		et = NewPtrType(TypeFromNative(t.Elem())); +	case *reflect.SliceType: +		et = NewSliceType(TypeFromNative(t.Elem())); +	case *reflect.StructType: +		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.UnsafePointerType: +		log.Crashf("%T not implemented", t); +	default: +		log.Crashf("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); +} diff --git a/src/pkg/exp/eval/compiler.go b/src/pkg/exp/eval/compiler.go new file mode 100644 index 000000000..f3c962c2b --- /dev/null +++ b/src/pkg/exp/eval/compiler.go @@ -0,0 +1,97 @@ +// 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"; +) + + +type positioned interface { +	Pos() token.Position; +} + + +// 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 { +	errors scanner.ErrorHandler; +	numErrors int; +	silentErrors int; +} + +func (a *compiler) diagAt(pos positioned, format string, args ...) { +	a.errors.Error(pos.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.Position; +	// The position where this label was first jumped to. +	used token.Position; +} + +// 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; +} diff --git a/src/pkg/exp/eval/eval_test.go b/src/pkg/exp/eval/eval_test.go new file mode 100644 index 000000000..192a2e782 --- /dev/null +++ b/src/pkg/exp/eval/eval_test.go @@ -0,0 +1,270 @@ +// 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 ( +	"bignum"; +	"flag"; +	"fmt"; +	"log"; +	"os"; +	"reflect"; +	"testing"; +) + +// 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) { +	for i, test := range tests { +		name := fmt.Sprintf("%s[%d]", baseName, i); +		test.run(t, name); +	} +} + +func (a test) run(t *testing.T, name string) { +	w := newTestWorld(); +	for _, j := range a { +		src := j.code; +		if noisy { +			println("code:", src); +		} + +		code, err := w.Compile(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, errstr := testing.MatchString(pat, err.String()); +	if errstr != "" { +		t.Fatalf("compile regexp %s: %v", pat, errstr); +	} +	return ok; +} + + +/* + * Test constructors + */ + +// Expression compile error +func CErr(expr string, cerr string) test { +	return test([]job{job{code: expr, cerr: cerr}}) +} + +// Expression runtime error +func RErr(expr string, rterr string) test { +	return test([]job{job{code: expr, rterr: rterr}}) +} + +// Expression value +func Val(expr string, val interface{}) test { +	return test([]job{job{code: expr, val: toValue(val)}}) +} + +// Statement runs without error +func Run(stmts string) test { +	return test([]job{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{job{code: stmt1, noval: true}, job{code: stmt2, noval: true}}) +} + +// Statement runs and test one expression's value +func Val1(stmts string, expr1 string, val1 interface{}) test { +	return test([]job{ +		job{code: stmts, noval: true}, +		job{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{ +		job{code: stmts, noval: true}, +		job{code: expr1, val: toValue(val1)}, +		job{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 *bignum.Integer: +		return &idealIntV{val}; +	case float: +		r := floatV(val); +		return &r; +	case *bignum.Rational: +		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.Crashf("toValue(%T) not implemented", val); +	panic(); +} + +/* + * Default test scope + */ + +type testFunc struct {}; + +func (*testFunc) NewFrame() *Frame { +	return &Frame{nil, &[2]Value {}}; +} + +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, &[2]Value {}}; +} + +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(bignum.Int(1))); +	def("i", IntType, 1); +	def("i2", IntType, 2); +	def("u", UintType, uint(1)); +	def("f", FloatType, 1.0); +	def("s", StringType, "abc"); +	def("t", NewStructType([]StructField {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; +} diff --git a/src/pkg/exp/eval/expr.go b/src/pkg/exp/eval/expr.go new file mode 100644 index 000000000..ea4fc082b --- /dev/null +++ b/src/pkg/exp/eval/expr.go @@ -0,0 +1,2007 @@ +// 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 ( +	"bignum"; +	"go/ast"; +	"go/token"; +	"log"; +	"strconv"; +	"strings"; +	"os"; +) + +// 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.Position; +} + +func (a *exprInfo) newExpr(t Type, desc string) *expr { +	return &expr{exprInfo: a, t: t, desc: desc}; +} + +func (a *exprInfo) diag(format string, args ...) { +	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.Crashf("attempted to convert from %v, expected ideal", a.t); +	} + +	var rat *bignum.Rational; + +	// 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", ratToString(rat)); +			return nil; +		} +	case IdealIntType: +		i := a.asIdealInt()(); +		rat = bignum.MakeRat(i, bignum.Nat(1)); +	default: +		log.Crashf("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", ratToString(rat), t); +			return nil; +		} +		if rat.Cmp(t.maxVal()) > 0 { +			a.diag("constant %v overflows %v", ratToString(rat), t); +			return nil; +		} +	} + +	// Convert rat to type t. +	res := a.newExpr(t, a.desc); +	switch t := t.lit().(type) { +	case *uintType: +		n, d := rat.Value(); +		f := n.Quo(bignum.MakeInt(false, d)); +		v := f.Abs().Value(); +		res.eval = func(*Thread) uint64 { return v }; +	case *intType: +		n, d := rat.Value(); +		f := n.Quo(bignum.MakeInt(false, d)); +		v := f.Value(); +		res.eval = func(*Thread) int64 { return v }; +	case *idealIntType: +		n, d := rat.Value(); +		f := n.Quo(bignum.MakeInt(false, d)); +		res.eval = func() *bignum.Integer { return f }; +	case *floatType: +		n, d := rat.Value(); +		v := float64(n.Value())/float64(d.Value()); +		res.eval = func(*Thread) float64 { return v }; +	case *idealFloatType: +		res.eval = func() *bignum.Rational { return rat }; +	default: +		log.Crashf("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.IsNeg() { +			a.diag("negative %s: %s", negErr, val); +			return nil; +		} +		bound := max; +		if negErr == "slice" { +			bound++; +		} +		if max != -1 && val.Cmp(bignum.Int(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.Crashf("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.Position; +	// 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.Position, 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 { +			panicln("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.Crashf("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.Position, 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.Crashf("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, "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, "function call in constant context"); +			return nil; +		} + +		if l.valType != nil { +			a.diagAt(x, "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.Value); + +	case *ast.IndexExpr: +		if x.End != nil { +			arr := a.compile(x.X, false); +			lo := a.compile(x.Index, false); +			hi := a.compile(x.End, false); +			if arr == nil || lo == nil || hi == nil { +				return nil; +			} +			return ei.compileSliceExpr(arr, lo, hi); +		} +		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.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.Value); + +	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.StringList: +		strings := make([]*expr, len(x.Strings)); +		bad := false; +		for i, s := range x.Strings { +			strings[i] = a.compile(s, false); +			if strings[i] == nil { +				bad = true; +			} +		} +		if bad { +			return nil; +		} +		return ei.compileStringList(strings); + +	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.Crashf("unexpected ast node type %T", x); +	panic(); + +typeexpr: +	if !callCtx { +		a.diagAt(x, "type used as expression"); +		return nil; +	} +	return ei.exprFromType(a.compileType(a.block, x)); + +notimpl: +	a.diagAt(x, "%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.Crashf("name %s has unknown type %T", name, def); +	panic(); +} + +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 *bignum.Integer, desc string) *expr { +	expr := a.newExpr(IdealIntType, desc); +	expr.eval = func() *bignum.Integer { return i }; +	return expr; +} + +func (a *exprInfo) compileIntLit(lit string) *expr { +	i, _, _ := bignum.IntFromString(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:len(lit)], '\''); +	if err != nil || tail != "'" { +		// Caught by parser +		a.silentErrors++; +		return nil; +	} +	return a.compileIdealInt(bignum.Int(int64(v)), "character literal"); +} + +func (a *exprInfo) compileFloatLit(lit string) *expr { +	f, _, n := bignum.RatFromString(lit, 0); +	if n != len(lit) { +		log.Crashf("malformed float literal %s at %v passed parser", lit, a.pos); +	} +	expr := a.newExpr(IdealFloatType, "float literal"); +	expr.eval = func() *bignum.Rational { 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.Crashf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth); +		} +		amberr += "\n\t" + pathName[1:len(pathName)]; +	}; + +	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 _, ok := visited[t]; ok { +			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.Crash("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.Crashf("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.Crashf("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); +	for i, t := range lt.In { +		vts[i] = t; +	} +	for i, t := range lt.Out { +		vts[i+nin] = t; +	} + +	// 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 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, paniclnType, printType, printlnType: +		evals := make([]func(*Thread)interface{}, len(as)); +		for i, x := range as { +			evals[i] = x.asInterface(); +		} +		spaces := ft == paniclnType || 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 || ft == paniclnType { +			expr.exec = func(t *Thread) { +				printer(t); +				t.Abort(os.NewError("panic")); +			} +		} +		return expr; +	} + +	log.Crashf("unexpected built-in function '%s'", ft.builtin); +	panic(); +} + +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.Crashf("Unary op %v not implemented", op); + +	default: +		log.Crashf("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.Crashf("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.Crashf("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.Crash("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 waht 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.Crashf("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()().IsZero()) || +				(r.t.isFloat() && r.asIdealFloat()().IsZero()) { +				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(bignum.Int(maxShift)) > 0 { +				a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift); +				expr.t = nil; +				return nil; +			} +			val := lv.Shl(uint(rv.Value())); +			expr.eval = func() *bignum.Integer { return val }; +		} else { +			expr.genBinOpShl(l, r); +		} + +	case token.SHR: +		if l.t.isIdeal() { +			lv := l.asIdealInt()(); +			rv := r.asIdealInt()(); +			val := lv.Shr(uint(rv.Value())); +			expr.eval = func() *bignum.Integer { 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.Crashf("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, "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.Crashf("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.Crashf("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 = FloatType; +		default: +			log.Crashf("unexpected ideal type %v", tempType); +		} +	} +	temp := b.DefineTemp(tempType); +	tempIdx := temp.Index; + +	// Create "temp := rhs" +	assign := ac.compile(b, tempType); +	if assign == nil { +		log.Crashf("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; +} diff --git a/src/pkg/exp/eval/expr1.go b/src/pkg/exp/eval/expr1.go new file mode 100644 index 000000000..7787a2112 --- /dev/null +++ b/src/pkg/exp/eval/expr1.go @@ -0,0 +1,1836 @@ +// This file is machine generated by gen.go. +// 6g gen.go && 6l gen.6 && ./6.out >expr1.go + +package eval + +import ( +	"bignum"; +	"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() *bignum.Integer) { +	return a.eval.(func()(*bignum.Integer)) +} +func (a *expr) asFloat() (func(*Thread) float64) { +	return a.eval.(func(*Thread)(float64)) +} +func (a *expr) asIdealFloat() (func() *bignum.Rational) { +	return a.eval.(func()(*bignum.Rational)) +} +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()*bignum.Integer: +		return func(*Thread) interface{} { return sf() } +	case func(t *Thread)float64: +		return func(t *Thread) interface{} { return sf(t) } +	case func()*bignum.Rational: +		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.Crashf("unexpected expression node type %T at %v", a.eval, a.pos); +	} +	panic(); +} + +/* + * 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() *bignum.Integer { return val } +	case *floatType: +		a.eval = func(t *Thread) float64 { return v.(FloatValue).Get(t) } +	case *idealFloatType: +		val := v.(IdealFloatValue).Get(); +		a.eval = func() *bignum.Rational { 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.Crashf("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.Crashf("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.Crashf("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.Crashf("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: +		v := v.asIdealInt()(); +		val := v.Neg(); +		a.eval = func() *bignum.Integer { return val } +	case *floatType: +		vf := v.asFloat(); +		a.eval = func(t *Thread) float64 { v := vf(t); return -v } +	case *idealFloatType: +		v := v.asIdealFloat()(); +		val := v.Neg(); +		a.eval = func() *bignum.Rational { return val } +	default: +		log.Crashf("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.Crashf("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: +		v := v.asIdealInt()(); +		val := v.Neg().Sub(bignum.Int(1)); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Add(r); +		a.eval = func() *bignum.Integer { 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)) +			} +		case 0: +			a.eval = func(t *Thread) float64 { +				l, r := lf(t), rf(t); +				var ret float64; +				ret = l + r; +				return float64(float(ret)) +			} +		default: +			log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealFloatType: +		l := l.asIdealFloat()(); +		r := r.asIdealFloat()(); +		val := l.Add(r); +		a.eval = func() *bignum.Rational { 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.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Sub(r); +		a.eval = func() *bignum.Integer { 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)) +			} +		case 0: +			a.eval = func(t *Thread) float64 { +				l, r := lf(t), rf(t); +				var ret float64; +				ret = l - r; +				return float64(float(ret)) +			} +		default: +			log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealFloatType: +		l := l.asIdealFloat()(); +		r := r.asIdealFloat()(); +		val := l.Sub(r); +		a.eval = func() *bignum.Rational { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Mul(r); +		a.eval = func() *bignum.Integer { 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)) +			} +		case 0: +			a.eval = func(t *Thread) float64 { +				l, r := lf(t), rf(t); +				var ret float64; +				ret = l * r; +				return float64(float(ret)) +			} +		default: +			log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealFloatType: +		l := l.asIdealFloat()(); +		r := r.asIdealFloat()(); +		val := l.Mul(r); +		a.eval = func() *bignum.Rational { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Quo(r); +		a.eval = func() *bignum.Integer { 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)) +			} +		case 0: +			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(float(ret)) +			} +		default: +			log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealFloatType: +		l := l.asIdealFloat()(); +		r := r.asIdealFloat()(); +		val := l.Quo(r); +		a.eval = func() *bignum.Rational { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Rem(r); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.And(r); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Or(r); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.Xor(r); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	case *idealIntType: +		l := l.asIdealInt()(); +		r := r.asIdealInt()(); +		val := l.AndNot(r); +		a.eval = func() *bignum.Integer { return val } +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	default: +		log.Crashf("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.Crashf("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.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos); +		} +	default: +		log.Crashf("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.Crashf("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.Crashf("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.Crashf("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.Crashf("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.Crashf("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.Crashf("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.Crashf("unexpected left operand type %v at %v", lt, r.pos); +	} +	panic(); +} diff --git a/src/pkg/exp/eval/expr_test.go b/src/pkg/exp/eval/expr_test.go new file mode 100644 index 000000000..ea11cf69d --- /dev/null +++ b/src/pkg/exp/eval/expr_test.go @@ -0,0 +1,345 @@ +// 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 ( +	"bignum"; +	"testing"; +) + +var undefined = "undefined" +var typeAsExpr = "type .* used as expression" +var badCharLit = "character literal" +var illegalEscape = "illegal char escape" +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 = bignum.Int(1).Shl(64); + +var exprTests = []test { +	Val("i", 1), +	CErr("zzz", undefined), +	// TODO(austin) Test variable in constant context +	//CErr("t", typeAsExpr), + +	Val("'a'", bignum.Int('a')), +	Val("'\\uffff'", bignum.Int('\uffff')), +	Val("'\\n'", bignum.Int('\n')), +	CErr("''+x", badCharLit), +	// Produces two parse errors +	//CErr("'''", ""), +	CErr("'\n'", badCharLit), +	CErr("'\\z'", illegalEscape), +	CErr("'ab'", badCharLit), + +	Val("1.0", bignum.Rat(1, 1)), +	Val("1.", bignum.Rat(1, 1)), +	Val(".1", bignum.Rat(1, 10)), +	Val("1e2", bignum.Rat(100, 1)), + +	Val("\"abc\"", "abc"), +	Val("\"\"", ""), +	Val("\"\\n\\\"\"", "\n\""), +	CErr("\"\\z\"", illegalEscape), +	CErr("\"abc", "string not terminated"), + +	Val("\"abc\" \"def\"", "abcdef"), +	CErr("\"abc\" \"\\z\"", illegalEscape), + +	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"), + +	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(aai)", 2), +	Val("len(sli)", 2), +	// 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", bignum.Int(+1)), +	Val("+1.0", bignum.Rat(1, 1)), +	CErr("+\"x\"", opTypes), + +	Val("-42", bignum.Int(-42)), +	Val("-i", -1), +	Val("-f", -1.0), +	// 6g bug? +	//Val("-(f-1)", -0.0), +	CErr("-\"x\"", opTypes), + +	// TODO(austin) Test unary ! + +	Val("^2", bignum.Int(^2)), +	Val("^(-2)", bignum.Int(^(-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", bignum.Int(2)), +	Val("f+f", 2.0), +	Val("1+f", 2.0), +	Val("1.0+1", bignum.Rat(2, 1)), +	Val("\"abc\" + \"def\"", "abcdef"), +	CErr("i+u", opTypes), +	CErr("-1+u", constantUnderflows), +	// TODO(austin) Test named types + +	Val("2-1", bignum.Int(1)), +	Val("2.0-1", bignum.Rat(1, 1)), +	Val("f-2", -1.0), +	// TOOD(austin) bignum can't do negative 0? +	//Val("-0.0", XXX), +	Val("2*2", bignum.Int(4)), +	Val("2*i", 2), +	Val("3/2", bignum.Int(1)), +	Val("3/i", 3), +	CErr("1/0", divByZero), +	CErr("1.0/0", divByZero), +	RErr("i/0", divByZero), +	Val("3%2", bignum.Int(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", bignum.Int(1)), +	Val("(i+4)/(i+2)", 1), +	Val("5%3", bignum.Int(2)), +	Val("(i+4)%(i+2)", 2), +	Val("-5/3", bignum.Int(-1)), +	Val("(i-6)/(i+2)", -1), +	Val("-5%3", bignum.Int(-2)), +	Val("(i-6)%(i+2)", -2), +	Val("5/-3", bignum.Int(-1)), +	Val("(i+4)/(i-4)", -1), +	Val("5%-3", bignum.Int(2)), +	Val("(i+4)%(i-4)", 2), +	Val("-5/-3", bignum.Int(1)), +	Val("(i-6)/(i-4)", 1), +	Val("-5%-3", bignum.Int(-2)), +	Val("(i-6)%(i-4)", -2), + +	// Examples from "Arithmetic operators" +	Val("11/4", bignum.Int(2)), +	Val("(i+10)/4", 2), +	Val("11%4", bignum.Int(3)), +	Val("(i+10)%4", 3), +	Val("11>>2", bignum.Int(2)), +	Val("(i+10)>>2", 2), +	Val("11&3", bignum.Int(3)), +	Val("(i+10)&3", 3), +	Val("-11/4", bignum.Int(-2)), +	Val("(i-12)/4", -2), +	Val("-11%4", bignum.Int(-3)), +	Val("(i-12)%4", -3), +	Val("-11>>2", bignum.Int(-3)), +	Val("(i-12)>>2", -3), +	Val("-11&3", bignum.Int(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", bignum.Int(2<<2)), +	CErr("2<<(-1)", constantUnderflows), +	CErr("2<<0x10000000000000000", constantOverflows), +	CErr("2<<2.5", constantTruncated), +	Val("2<<2.0", bignum.Int(2<<2.0)), +	CErr("2<<i", mustBeUnsigned), +	Val("2<<u", 2<<1), +	CErr("2<<f", opTypes), + +	Val("-2<<2", bignum.Int(-2<<2)), +	CErr("-2<<(-1)", constantUnderflows), +	CErr("-2<<0x10000000000000000", constantOverflows), +	CErr("-2<<2.5", constantTruncated), +	Val("-2<<2.0", bignum.Int(-2<<2.0)), +	CErr("-2<<i", mustBeUnsigned), +	Val("-2<<u", -2<<1), +	CErr("-2<<f", opTypes), + +	Val("0x10000000000000000<<2", hugeInteger.Shl(2)), +	CErr("0x10000000000000000<<(-1)", constantUnderflows), +	CErr("0x10000000000000000<<0x10000000000000000", constantOverflows), +	CErr("0x10000000000000000<<2.5", constantTruncated), +	Val("0x10000000000000000<<2.0", hugeInteger.Shl(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); +} diff --git a/src/pkg/exp/eval/func.go b/src/pkg/exp/eval/func.go new file mode 100644 index 000000000..3bf52871d --- /dev/null +++ b/src/pkg/exp/eval/func.go @@ -0,0 +1,89 @@ +// 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)) { +	n := len(b.instrs); +	if n >= cap(b.instrs) { +		a := make(code, n, n*2); +		for i := range b.instrs { +			a[i] = b.instrs[i]; +		} +		b.instrs = a; +	} +	b.instrs = b.instrs[0:n+1]; +	b.instrs[n] = 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)); +	for i := range b.instrs { +		a[i] = b.instrs[i]; +	} +	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); +} diff --git a/src/pkg/exp/eval/gen.go b/src/pkg/exp/eval/gen.go new file mode 100644 index 000000000..06939e58c --- /dev/null +++ b/src/pkg/exp/eval/gen.go @@ -0,0 +1,375 @@ +// 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{ Size{8, "uint8"}, Size{16, "uint16"}, Size{32, "uint32"}, Size{64, "uint64"}, Size{0, "uint"}} +	}; +	intType = &Type{ Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt", +		Sizes: []Size{Size{8, "int8"}, Size{16, "int16"}, Size{32, "int32"}, Size{64, "int64"}, Size{0, "int"}} +	}; +	idealIntType = &Type{ Repr: "*idealIntType", Value: "IdealIntValue", Native: "*bignum.Integer", As: "asIdealInt", IsIdeal: true }; +	floatType = &Type{ Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat", +		Sizes: []Size{Size{32, "float32"}, Size{64, "float64"}, Size{0, "float"}} +	}; +	idealFloatType = &Type{ Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*bignum.Rational", 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{ +	Op{ Name: "Neg", Expr: "-v", ConstExpr: "v.Neg()", Types: numbers }, +	Op{ Name: "Not", Expr: "!v", Types: bools }, +	Op{ Name: "Xor", Expr: "^v", ConstExpr: "v.Neg().Sub(bignum.Int(1))", Types: integers }, +} + +var binOps = []Op{ +	Op{ Name: "Add", Expr: "l + r", ConstExpr: "l.Add(r)", Types: addable }, +	Op{ Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(r)", Types: numbers }, +	Op{ Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(r)", Types: numbers }, +	Op{ Name: "Quo", +		Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret =  l / r", +		ConstExpr: "l.Quo(r)", +		Types: numbers, +	}, +	Op{ Name: "Rem", +		Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret = l % r", +		ConstExpr: "l.Rem(r)", +		Types: integers, +	}, +	Op{ Name: "And", Expr: "l & r", ConstExpr: "l.And(r)", Types: integers }, +	Op{ Name: "Or", Expr: "l | r", ConstExpr: "l.Or(r)", Types: integers }, +	Op{ Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(r)", Types: integers }, +	Op{ Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(r)", Types: integers }, +	Op{ Name: "Shl", Expr: "l << r", ConstExpr: "l.Shl(uint(r.Value()))", +		AsRightName: "asUint", Types: shiftable +	}, +	Op{ Name: "Shr", Expr: "l >> r", ConstExpr: "l.Shr(uint(r.Value()))", +		AsRightName: "asUint", Types: shiftable +	}, +	Op{ Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable }, +	Op{ Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable }, +	Op{ Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable }, +	Op{ Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable }, +	Op{ Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable }, +	Op{ 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 ( +	"bignum"; +	"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.Crashf("unexpected expression node type %T at %v", a.eval, a.pos); +	} +	panic(); +} + +/* + * 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.Crashf("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.Crashf("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.Crashf("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.Crashf("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ยป +		v := v.ยซAsยป()(); +		val := ยซ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.Crashf("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.Crashf("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.Crashf("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.Crashf("unexpected left operand type %v at %v", lt, r.pos); +	} +	panic(); +} +` + +func main() { +	t := template.New(nil); +	t.SetDelims("ยซ", "ยป"); +	err := t.Parse(templateStr); +	if err != nil { +		log.Exit(err); +	} +	err = t.Execute(data, os.Stdout); +	if err != nil { +		log.Exit(err); +	} +} diff --git a/src/pkg/exp/eval/main.go b/src/pkg/exp/eval/main.go new file mode 100644 index 000000000..2a6d94845 --- /dev/null +++ b/src/pkg/exp/eval/main.go @@ -0,0 +1,92 @@ +// 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 ( +	"./_obj/eval"; +	"bufio"; +	"flag"; +	"go/parser"; +	"go/scanner"; +	"io"; +	"os"; +) + +var filename = flag.String("f", "", "file to run"); + +func main() { +	flag.Parse(); +	w := eval.NewWorld(); +	if *filename != "" { +		data, err := io.ReadFile(*filename); +		if err != nil { +			println(err.String()); +			os.Exit(1); +		} +		file, err := parser.ParseFile(*filename, data, 0); +		if err != nil { +			println(err.String()); +			os.Exit(1); +		} +		code, err := w.CompileDeclList(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("init()"); +		if code != nil { +			_, err := code.Run(); +			if err != nil { +				println(err.String()); +				os.Exit(1); +			} +		} +		code, err = w.Compile("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(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()); +		} +	} +} + diff --git a/src/pkg/exp/eval/scope.go b/src/pkg/exp/eval/scope.go new file mode 100644 index 000000000..7ee4a8915 --- /dev/null +++ b/src/pkg/exp/eval/scope.go @@ -0,0 +1,203 @@ +// 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.Position; +} + +type Variable struct { +	token.Position; +	// 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; +} + +type Constant struct { +	token.Position; +	Type Type; +	Value Value; +} + +// 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.Crash("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.Crash("Cannot exit top-level block"); +	} +	if b.outer.scope == b.scope { +		if b.outer.inner != b { +			log.Crash("Already exited block"); +		} +		if b.inner != nil && b.inner.scope == b.scope { +			log.Crash("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.Crash("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.Position, t Type) (*Variable, Def) { +	if prev, ok := b.defs[name]; ok { +		return nil, prev; +	} +	v := b.defineSlot(t, false); +	v.Position = 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.Crash("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.Position{}, index, t, nil}; +	return v; +} + +func (b *block) DefineConst(name string, pos token.Position, 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.Position, 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)}; +} diff --git a/src/pkg/exp/eval/stmt.go b/src/pkg/exp/eval/stmt.go new file mode 100644 index 000000000..9ec6fb83d --- /dev/null +++ b/src/pkg/exp/eval/stmt.go @@ -0,0 +1,1313 @@ +// 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 ( +	"bignum"; +	"log"; +	"go/ast"; +	"go/token"; +) + +const ( +	returnPC = ^uint(0); +	badPC = ^uint(1); +) + +/* + * Statement compiler + */ + +type stmtCompiler struct { +	*blockCompiler; +	pos token.Position; +	// This statement's label, or nil if it is not labeled. +	stmtLabel *label; +} + +func (a *stmtCompiler) diag(format string, args ...) { +	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.Position] *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 mutliple 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.Position] *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.Crashf("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.Position, 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.Crashf("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:len(numVars)]; +		} +		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.Value, ident.Pos(), t); +	if prev != nil { +		// TODO(austin) It's silly that we have to capture +		// Pos() in a variable. +		pos := prev.Pos(); +		if pos.IsValid() { +			a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Value, &pos); +		} else { +			a.diagAt(ident, "variable %s redeclared in this block", ident.Value); +		} +		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.Crash("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.TypeCaseClause: +		notimpl = true; + +	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.Crashf("unexpected ast node type %T", s); +	} + +	if notimpl { +		a.diag("%T statment node not implemented", s); +	} + +	if a.block.inner != nil { +		log.Crash("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.Crash("FuncDecl at statement level"); +		} + +	case *ast.GenDecl: +		if decl.Tok == token.IMPORT && !a.block.global { +			log.Crash("import at statement level"); +		} + +	default: +		log.Crashf("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.Crash("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.Value, a.pos, decl.Type, decl.Type.Zero()); +		if prev != nil { +			pos := prev.Pos(); +			if pos.IsValid() { +				a.diagAt(d.Name, "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Value, &pos); +			} else { +				a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Value); +			} +		} +		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.Crashf("%v not implemented", d.Tok); +		case token.CONST: +			log.Crashf("%v not implemented", d.Tok); +		case token.TYPE: +			a.compileTypeDecl(a.block, d); +		case token.VAR: +			a.compileVarDecl(d); +		} + +	default: +		log.Crashf("Unexpected Decl type %T", decl); +	} +} + +func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) { +	// Define label +	l, ok := a.labels[s.Label.Value]; +	if ok { +		if l.resolved.IsValid() { +			a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Value, &l.resolved); +		} +	} else { +		pc := badPC; +		l = &label{name: s.Label.Value, 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.Crashf("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() *bignum.Integer { return bignum.Int(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.Crashf("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, "left side of := must be a name"); +				// Suppress new defitions errors +				nDefs++; +				continue; +			} + +			// Is this simply an assignment? +			if _, ok := a.block.defs[ident.Value]; 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 = FloatType; +				default: +					log.Crashf("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.Crashf("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.Value { +			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.Value); +	} +	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.Value]; +		if !ok { +			pc := badPC; +			l = &label{name: s.Label.Value, 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.Crash("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, "switch statement must contain case clauses"); +			continue; +		} +		if clause.Values == nil { +			if hasDefault { +				a.diagAt(clause, "switch statement contains more than one default case"); +			} +			hasDefault = true; +		} else { +			ncases += len(clause.Values); +		} +	} + +	// 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.Values { +			e := condbc.compileExpr(condbc.block, false, v); +			switch { +			case e == nil: +				// Error reported by compileExpr +			case cond == nil && !e.t.isBoolean(): +				a.diagAt(v, "'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.Values != nil { +			for _ = range clause.Values { +				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:len(clause.Body)] { +					// XXX(Spec) 6g also considers +					// empty blocks to be empty +					// statements. +					if _, ok := s2.(*ast.EmptyStmt); !ok { +						a.diagAt(s, "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].Value, 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].Value, 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); +} diff --git a/src/pkg/exp/eval/stmt_test.go b/src/pkg/exp/eval/stmt_test.go new file mode 100644 index 000000000..e94adfb60 --- /dev/null +++ b/src/pkg/exp/eval/stmt_test.go @@ -0,0 +1,343 @@ +// 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), +	// TODO(austin) The parser produces an error message for this +	// one that's inconsistent with the errors I give for other +	// things +	//CErr("a, b := 1, 2, 3", "too many"), +	CErr("a, b := 1, 2, 3", "arity"), +	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", "left side of := must be a name"), +	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"), +	// 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 { 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 { } }", "return"), +	CErr("fn1 := func() int { if { } else { return 2 } }", "return"), +	Run("fn1 := func() int { if { return 1 } }"), +	Run("fn1 := func() int { if { return 1 } else { } }"), +	Run("fn1 := func() int { if { return 1 } else { } }"), + +	// 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); +} diff --git a/src/pkg/exp/eval/test.bash b/src/pkg/exp/eval/test.bash new file mode 100755 index 000000000..81f4c3dd1 --- /dev/null +++ b/src/pkg/exp/eval/test.bash @@ -0,0 +1,34 @@ +#!/bin/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 +make +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 diff --git a/src/pkg/exp/eval/type.go b/src/pkg/exp/eval/type.go new file mode 100644 index 000000000..b73f92163 --- /dev/null +++ b/src/pkg/exp/eval/type.go @@ -0,0 +1,1174 @@ +// 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 ( +	"bignum"; +	"go/ast"; +	"go/token"; +	"log"; +	"reflect"; +	"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.Position; +} + +type BoundedType interface { +	Type; +	// minVal returns the smallest value of this type. +	minVal() *bignum.Rational; +	// maxVal returns the largest value of this type. +	maxVal() *bignum.Rational; +} + +var universePos = token.Position{"<universe>", 0, 0, 0}; + +/* + * 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.NewValue(t).(*reflect.PtrValue).Get(); +		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.Position { +	return token.Position{}; +} + +/* + * 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: ", t.Bits); +} + +func (t *uintType) minVal() *bignum.Rational { +	return bignum.Rat(0, 1); +} + +func (t *uintType) maxVal() *bignum.Rational { +	bits := t.Bits; +	if bits == 0 { +		if t.Ptr { +			bits = uint(8 * unsafe.Sizeof(uintptr(0))); +		} else { +			bits = uint(8 * unsafe.Sizeof(uint(0))); +		} +	} +	return bignum.MakeRat(bignum.Int(1).Shl(bits).Add(bignum.Int(-1)), bignum.Nat(1)); +} + +/* + * 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: ", t.Bits); +} + +func (t *intType) minVal() *bignum.Rational { +	bits := t.Bits; +	if bits == 0 { +		bits = uint(8 * unsafe.Sizeof(int(0))); +	} +	return bignum.MakeRat(bignum.Int(-1).Shl(bits - 1), bignum.Nat(1)); +} + +func (t *intType) maxVal() *bignum.Rational { +	bits := t.Bits; +	if bits == 0 { +		bits = uint(8 * unsafe.Sizeof(int(0))); +	} +	return bignum.MakeRat(bignum.Int(1).Shl(bits - 1).Add(bignum.Int(-1)), bignum.Nat(1)); +} + +/* + * 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{bignum.Int(0)}; +} + +/* + * 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"}); +	FloatType   = universe.DefineType("float",   universePos, &floatType{commonType{}, 0,  "float"}); +) + +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; +	case 0: +		res := floatV(0); +		return &res; +	} +	panic("unexpected float bit count: ", t.Bits); +} + +var maxFloat32Val = bignum.MakeRat(bignum.Int(0xffffff).Shl(127-23), bignum.Nat(1)) +var maxFloat64Val = bignum.MakeRat(bignum.Int(0x1fffffffffffff).Shl(1023-52), bignum.Nat(1)) +var minFloat32Val = maxFloat32Val.Neg() +var minFloat64Val = maxFloat64Val.Neg() + +func (t *floatType) minVal() *bignum.Rational { +	bits := t.Bits; +	if bits == 0 { +		bits = uint(8 * unsafe.Sizeof(float(0))); +	} +	switch bits { +	case 32: +		return minFloat32Val; +	case 64: +		return minFloat64Val; +	} +	log.Crashf("unexpected floating point bit count: %d", bits); +	panic(); +} + +func (t *floatType) maxVal() *bignum.Rational { +	bits := t.Bits; +	if bits == 0 { +		bits = uint(8 * unsafe.Sizeof(float(0))); +	} +	switch bits { +	case 32: +		return maxFloat32Val; +	case 64: +		return maxFloat64Val; +	} +	log.Crashf("unexpected floating point bit count: %d", bits); +	panic(); +} + +/* + * 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{bignum.Rat(1, 0)}; +} + +/* + * 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"}; +	paniclnType = &FuncType{builtin: "panicln"}; +	printType   = &FuncType{builtin: "print"}; +	printlnType = &FuncType{builtin: "println"}; +) + +// 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].Value + " "; +		} +		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 { +	args := typeListString(t.Type.In, t.InNames); +	if t.Type.Variadic { +		if len(args) > 0 { +			args += ", "; +		} +		args += "..."; +	} +	s := "func"; +	if t.Name != nil { +		s += " " + t.Name.Value; +	} +	s += "(" + args + ")"; +	if len(t.Type.Out) > 0 { +		s += " (" + typeListString(t.Type.Out, t.OutNames) + ")"; +	} +	return s; +} + +/* +type InterfaceType struct { +	// TODO(austin) +} +*/ + +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 { +	token.Position; +	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.Position{}, name, nil, true, make(map[string] Method)}; +} + +func (t *NamedType) Complete(def Type) { +	if !t.incomplete { +		log.Crashf("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() { +	// 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("len", universePos, lenType, nil); +	universe.DefineConst("make", universePos, makeType, nil); +	universe.DefineConst("new", universePos, newType, nil); +	universe.DefineConst("panic", universePos, panicType, nil); +	universe.DefineConst("panicln", universePos, paniclnType, nil); +	universe.DefineConst("print", universePos, printType, nil); +	universe.DefineConst("println", universePos, printlnType, nil); +} diff --git a/src/pkg/exp/eval/typec.go b/src/pkg/exp/eval/typec.go new file mode 100644 index 000000000..bdbe98c4c --- /dev/null +++ b/src/pkg/exp/eval/typec.go @@ -0,0 +1,366 @@ +// 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.Value); +	if def == nil { +		a.diagAt(x, "%s: undefined", x.Value); +		return nil; +	} +	switch def := def.(type) { +	case *Constant: +		a.diagAt(x, "constant %v used as type", x.Value); +		return nil; +	case *Variable: +		a.diagAt(x, "variable %v used as type", x.Value); +		return nil; +	case *NamedType: +		if !allowRec && def.incomplete { +			a.diagAt(x, "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.Crashf("name %s has unknown type %T", x.Value, 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, "... array initailizers not implemented"); +		return nil; +	} +	l, ok := a.compileArrayLen(a.block, x.Len); +	if !ok { +		return nil; +	} +	if l < 0 { +		a.diagAt(x.Len, "array length must be non-negative"); +		return nil; +	} +	if elem == nil { +		return nil; +	} + +	return NewArrayType(l, elem); +} + +func countFields(fs []*ast.Field) int { +	n := 0; +	for _, f := range fs { +		if f.Names == nil { +			n++; +		} else { +			n += len(f.Names); +		} +	} +	return n; +} + +func (a *typeCompiler) compileFields(fs []*ast.Field, allowRec bool) ([]Type, []*ast.Ident, []token.Position, bool) { +	n := countFields(fs); +	ts := make([]Type, n); +	ns := make([]*ast.Ident, n); +	ps := make([]token.Position, n); + +	bad := false; +	i := 0; +	for _, f := range fs { +		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.Position, len(ts)); +	for i := range fields { +		// Compute field name and check anonymous fields +		var name string; +		if names[i] != nil { +			name = names[i].Value; +		} 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, &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) 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, "map key cannot be a struct type"); +		return nil; +	case *ArrayType: +		a.diagAt(x, "map key cannot be an array type"); +		return nil; +	case *SliceType: +		a.diagAt(x, "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: +		goto notimpl; + +	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, "illegal use of ellipsis"); +		return nil; +	} +	a.diagAt(x, "expression used as type"); +	return nil; + +notimpl: +	a.diagAt(x, "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.Value, 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; +} diff --git a/src/pkg/exp/eval/util.go b/src/pkg/exp/eval/util.go new file mode 100644 index 000000000..9cdf23722 --- /dev/null +++ b/src/pkg/exp/eval/util.go @@ -0,0 +1,39 @@ +// 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 ( +	"bignum"; +) + +// TODO(austin): Maybe add to bignum in more general form +func ratToString(rat *bignum.Rational) string { +	n, dnat := rat.Value(); +	d := bignum.MakeInt(false, dnat); +	w, frac := n.QuoRem(d); +	out := w.String(); +	if frac.IsZero() { +		return out; +	} + +	r := frac.Abs(); +	r = r.Mul(bignum.Nat(1e6)); +	dec, tail := r.DivMod(dnat); +	// Round last digit +	if tail.Cmp(dnat.Div(bignum.Nat(2))) >= 0 { +		dec = dec.Add(bignum.Nat(1)); +	} +	// Strip zeros +	ten := bignum.Nat(10); +	for !dec.IsZero() { +		dec2, r2 := dec.DivMod(ten); +		if !r2.IsZero() { +			break; +		} +		dec = dec2; +	} +	out += "." + dec.String(); +	return out; +} diff --git a/src/pkg/exp/eval/value.go b/src/pkg/exp/eval/value.go new file mode 100644 index 000000000..1a64a6d96 --- /dev/null +++ b/src/pkg/exp/eval/value.go @@ -0,0 +1,731 @@ +// 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 ( +	"bignum"; +	"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() *bignum.Integer; +} + +type FloatValue interface { +	Value; +	Get(*Thread) float64; +	Set(*Thread, float64); +} + +type IdealFloatValue interface { +	Value; +	Get() *bignum.Rational; +} + +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 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 *bignum.Integer; +} + +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() *bignum.Integer { +	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); +} + +type floatV float + +func (v *floatV) String() string { +	return fmt.Sprint(*v); +} + +func (v *floatV) Assign(t *Thread, o Value) { +	*v = floatV(o.(FloatValue).Get(t)); +} + +func (v *floatV) Get(*Thread) float64 { +	return float64(*v); +} + +func (v *floatV) Set(t *Thread, x float64) { +	*v = floatV(x); +} + +/* + * Ideal float + */ + +type idealFloatV struct { +	V *bignum.Rational; +} + +func (v *idealFloatV) String() string { +	return ratToString(v.V); +} + +func (v *idealFloatV) Assign(t *Thread, o Value) { +	v.V = o.(IdealFloatValue).Get(); +} + +func (v *idealFloatV) Get() *bignum.Rational { +	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; +} + +/* + * 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 { +	if v, ok := m[key]; ok { +		return v; +	} +	return nil; +} + +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 + */ + +// TODO(austin) Nothing complains if I accidentally define init with +// arguments.  Is this intentional? +func init() { +	s := universe; + +	true := boolV(true); +	s.DefineConst("true", universePos, BoolType, &true); +	false := boolV(false); +	s.DefineConst("false", universePos, BoolType, &false); +} diff --git a/src/pkg/exp/eval/world.go b/src/pkg/exp/eval/world.go new file mode 100644 index 000000000..a5e4e6092 --- /dev/null +++ b/src/pkg/exp/eval/world.go @@ -0,0 +1,192 @@ +// 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. + +// This package 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(stmts []ast.Stmt) (Code, os.Error) { +	if len(stmts) == 1 { +		if s, ok := stmts[0].(*ast.ExprStmt); ok { +			return w.CompileExpr(s.X); +		} +	} +	errors := scanner.NewErrorVector(); +	cc := &compiler{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(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(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(e ast.Expr) (Code, os.Error) { +	errors := scanner.NewErrorVector(); +	cc := &compiler{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 +	case *MultiType: +		if len(t.Elems) == 0 { +			return &stmtCode{w, code{ec.exec}}, nil; +		} +		fallthrough; +	default: +		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(text string) (Code, os.Error) { +	stmts, err := parser.ParseStmtList("input", text); +	if err == nil { +		return w.CompileStmtList(stmts); +	} + +	// Otherwise try as DeclList. +	decls, err1 := parser.ParseDeclList("input", text); +	if err1 == nil { +		return w.CompileDeclList(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() { +		res += "; previous declaration at " + pos.String(); +	} +	return res; +} + +func (w *World) DefineConst(name string, t Type, val Value) os.Error { +	_, prev := w.scope.DefineConst(name, token.Position{}, 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.Position{}, t); +	if prev != nil { +		return &RedefinitionError{name, prev}; +	} +	v.Init = val; +	return nil; +} + diff --git a/src/pkg/exp/ogle/Makefile b/src/pkg/exp/ogle/Makefile new file mode 100644 index 000000000..31bb9df5c --- /dev/null +++ b/src/pkg/exp/ogle/Makefile @@ -0,0 +1,29 @@ +# 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 $(GOROOT)/src/Make.$(GOARCH) + +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 $(GOROOT)/src/Make.pkg + +main.$O: main.go package +	$(GC) -I_obj $< + +ogle: main.$O +	$(LD) -L_obj -o $@ $< diff --git a/src/pkg/exp/ogle/abort.go b/src/pkg/exp/ogle/abort.go new file mode 100644 index 000000000..087c57b5f --- /dev/null +++ b/src/pkg/exp/ogle/abort.go @@ -0,0 +1,35 @@ +// 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 compututation, 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; +} diff --git a/src/pkg/exp/ogle/arch.go b/src/pkg/exp/ogle/arch.go new file mode 100644 index 000000000..30a2bcf58 --- /dev/null +++ b/src/pkg/exp/ogle/arch.go @@ -0,0 +1,139 @@ +// 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}; diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go new file mode 100644 index 000000000..f60621343 --- /dev/null +++ b/src/pkg/exp/ogle/cmd.go @@ -0,0 +1,375 @@ +// 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. + +// 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 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(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); +	ev.Init(); +	sc.Init("input", 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 { +	cmd{"load", cmdLoad}, +	cmd{"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[pos.Offset + len(lit):len(line)]; +		} +	} +	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:len(path)]); +		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, " ", 0); +		if len(parts) == 0 { +			fname = ""; +		} else { +			fname = parts[0]; +		} +		tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*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, os.O_RDONLY, 0); +	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][]byte; +	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); +} diff --git a/src/pkg/exp/ogle/event.go b/src/pkg/exp/ogle/event.go new file mode 100644 index 000000000..9dc7a8445 --- /dev/null +++ b/src/pkg/exp/ogle/event.go @@ -0,0 +1,294 @@ +// 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); +} diff --git a/src/pkg/exp/ogle/frame.go b/src/pkg/exp/ogle/frame.go new file mode 100644 index 000000000..bf2b39134 --- /dev/null +++ b/src/pkg/exp/ogle/frame.go @@ -0,0 +1,214 @@ +// 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); +} diff --git a/src/pkg/exp/ogle/goroutine.go b/src/pkg/exp/ogle/goroutine.go new file mode 100644 index 000000000..4d458c561 --- /dev/null +++ b/src/pkg/exp/ogle/goroutine.go @@ -0,0 +1,119 @@ +// 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; +} diff --git a/src/pkg/exp/ogle/main.go b/src/pkg/exp/ogle/main.go new file mode 100644 index 000000000..88265624a --- /dev/null +++ b/src/pkg/exp/ogle/main.go @@ -0,0 +1,11 @@ +// 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(); +} diff --git a/src/pkg/exp/ogle/process.go b/src/pkg/exp/ogle/process.go new file mode 100644 index 000000000..984364f23 --- /dev/null +++ b/src/pkg/exp/ogle/process.go @@ -0,0 +1,541 @@ +// 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.NewValue(&p.runtime)).(*reflect.StructValue); +	rtvt := rtv.Type().(*reflect.StructType); +	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:len(n)]); +		if sym == nil { +			continue; +		} +		rtv.Field(i).(*reflect.Uint64Value).Set(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) { +	n := len(p.posted); +	m := n*2; +	if m == 0 { +		m = 4; +	} +	posted := make([]Event, n+1, m); +	for i, p := range p.posted { +		posted[i] = p; +	} +	posted[n] = ev; +	p.posted = posted; +} + +// 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:len(p.posted)]; +		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:len(p.pending)]; +		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.Crashf("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 +// asynchrony 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(); +} diff --git a/src/pkg/exp/ogle/rruntime.go b/src/pkg/exp/ogle/rruntime.go new file mode 100644 index 000000000..2af636926 --- /dev/null +++ b/src/pkg/exp/ogle/rruntime.go @@ -0,0 +1,271 @@ +// 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.NewValue(out)).(*reflect.StructValue); +	outt := outv.Type().(*reflect.StructType); +	runtimev := reflect.Indirect(reflect.NewValue(runtime)).(*reflect.StructValue); + +	// 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:len(name)]; +			} +			indexes[name] = j; +		} + +		// Fill this field of out +		outStructv := outv.Field(i).(*reflect.StructValue); +		outStructt := outStructv.Type().(*reflect.StructType); +		for j := 0; j < outStructt.NumField(); j++ { +			f := outStructv.Field(j).(*reflect.IntValue); +			name := outStructt.Field(j).Name; +			f.Set(indexes[name]); +		} +	} +} diff --git a/src/pkg/exp/ogle/rtype.go b/src/pkg/exp/ogle/rtype.go new file mode 100644 index 000000000..f70b918c0 --- /dev/null +++ b/src/pkg/exp/ogle/rtype.go @@ -0,0 +1,307 @@ +// 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.Crashf("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.Stderrf("%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.PFloatType: +		t = eval.FloatType; +		mk = mkFloat; +	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; +} diff --git a/src/pkg/exp/ogle/rvalue.go b/src/pkg/exp/ogle/rvalue.go new file mode 100644 index 000000000..9077e238b --- /dev/null +++ b/src/pkg/exp/ogle/rvalue.go @@ -0,0 +1,579 @@ +// Copyright 2009 The Go Authors.  All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ogle + +import ( +	"debug/proc"; +	"exp/eval"; +	"fmt"; +) + +// A RemoteMismatchError occurs when an operation that requires two +// identical remote processes is given different process.  For +// example, this occurs when trying to set a pointer in one process to +// point to something in another process. +type RemoteMismatchError string + +func (e RemoteMismatchError) String() string { +	return string(e); +} + +// A ReadOnlyError occurs when attempting to set or assign to a +// read-only value. +type ReadOnlyError string + +func (e ReadOnlyError) String() string { +	return string(e); +} + +// A maker is a function that converts a remote address into an +// interpreter Value. +type maker func(remote) eval.Value + +type remoteValue interface { +	addr() remote; +} + +// remote represents an address in a remote process. +type remote struct { +	base proc.Word; +	p *Process; +} + +func (v remote) Get(a aborter, size int) uint64 { +	// TODO(austin) This variable might temporarily be in a +	// register.  We could trace the assembly back from the +	// current PC, looking for the beginning of the function or a +	// call (both of which guarantee that the variable is in +	// memory), or an instruction that loads the variable into a +	// register. +	// +	// TODO(austin) If this is a local variable, it might not be +	// live at this PC.  In fact, because the compiler reuses +	// slots, there might even be a different local variable at +	// this location right now.  A simple solution to both +	// problems is to include the range of PC's over which a local +	// variable is live in the symbol table. +	// +	// TODO(austin) We need to prevent the remote garbage +	// collector from collecting objects out from under us. +	var arr [8]byte; +	buf := arr[0:size]; +	_, err := v.p.Peek(v.base, buf); +	if err != nil { +		a.Abort(err); +	} +	return uint64(v.p.ToWord(buf)); +} + +func (v remote) Set(a aborter, size int, x uint64) { +	var arr [8]byte; +	buf := arr[0:size]; +	v.p.FromWord(proc.Word(x), buf); +	_, err := v.p.Poke(v.base, buf); +	if err != nil { +		a.Abort(err); +	} +} + +func (v remote) plus(x proc.Word) remote { +	return remote{v.base + x, v.p}; +} + +func tryRVString(f func(a aborter) string) string { +	var s string; +	err := try(func(a aborter) { s = f(a) }); +	if err != nil { +		return fmt.Sprintf("<error: %v>", err); +	} +	return s; +} + +/* + * Bool + */ + +type remoteBool struct { +	r remote; +} + +func (v remoteBool) String() string { +	return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteBool) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.BoolValue).Get(t)); +} + +func (v remoteBool) Get(t *eval.Thread) bool { +	return v.aGet(t); +} + +func (v remoteBool) aGet(a aborter) bool { +	return v.r.Get(a, 1) != 0; +} + +func (v remoteBool) Set(t *eval.Thread, x bool) { +	v.aSet(t, x); +} + +func (v remoteBool) aSet(a aborter, x bool) { +	if x { +		v.r.Set(a, 1, 1); +	} else { +		v.r.Set(a, 1, 0); +	} +} + +func (v remoteBool) addr() remote { +	return v.r; +} + +func mkBool(r remote) eval.Value { +	return remoteBool{r}; +} + +/* + * Uint + */ + +type remoteUint struct { +	r remote; +	size int; +} + +func (v remoteUint) String() string { +	return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteUint) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.UintValue).Get(t)); +} + +func (v remoteUint) Get(t *eval.Thread) uint64 { +	return v.aGet(t); +} + +func (v remoteUint) aGet(a aborter) uint64 { +	return v.r.Get(a, v.size); +} + +func (v remoteUint) Set(t *eval.Thread, x uint64) { +	v.aSet(t, x); +} + +func (v remoteUint) aSet(a aborter, x uint64) { +	v.r.Set(a, v.size, x); +} + +func (v remoteUint) addr() remote { +	return v.r; +} + +func mkUint8(r remote) eval.Value { +	return remoteUint{r, 1}; +} + +func mkUint16(r remote) eval.Value { +	return remoteUint{r, 2}; +} + +func mkUint32(r remote) eval.Value { +	return remoteUint{r, 4}; +} + +func mkUint64(r remote) eval.Value { +	return remoteUint{r, 8}; +} + +func mkUint(r remote) eval.Value { +	return remoteUint{r, r.p.IntSize()}; +} + +func mkUintptr(r remote) eval.Value { +	return remoteUint{r, r.p.PtrSize()}; +} + +/* + * Int + */ + +type remoteInt struct { +	r remote; +	size int; +} + +func (v remoteInt) String() string { +	return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteInt) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.IntValue).Get(t)); +} + +func (v remoteInt) Get(t *eval.Thread) int64 { +	return v.aGet(t); +} + +func (v remoteInt) aGet(a aborter) int64 { +	return int64(v.r.Get(a, v.size)); +} + +func (v remoteInt) Set(t *eval.Thread, x int64) { +	v.aSet(t, x); +} + +func (v remoteInt) aSet(a aborter, x int64) { +	v.r.Set(a, v.size, uint64(x)); +} + +func (v remoteInt) addr() remote { +	return v.r; +} + +func mkInt8(r remote) eval.Value { +	return remoteInt{r, 1}; +} + +func mkInt16(r remote) eval.Value { +	return remoteInt{r, 2}; +} + +func mkInt32(r remote) eval.Value { +	return remoteInt{r, 4}; +} + +func mkInt64(r remote) eval.Value { +	return remoteInt{r, 8}; +} + +func mkInt(r remote) eval.Value { +	return remoteInt{r, r.p.IntSize()}; +} + +/* + * Float + */ + +type remoteFloat struct { +	r remote; +	size int; +} + +func (v remoteFloat) String() string { +	return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }); +} + +func (v remoteFloat) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.FloatValue).Get(t)); +} + +func (v remoteFloat) Get(t *eval.Thread) float64 { +	return v.aGet(t); +} + +func (v remoteFloat) aGet(a aborter) float64 { +	bits := v.r.Get(a, v.size); +	switch v.size { +	case 4: +		return float64(v.r.p.ToFloat32(uint32(bits))); +	case 8: +		return v.r.p.ToFloat64(bits); +	} +	panic("Unexpected float size ", v.size); +} + +func (v remoteFloat) Set(t *eval.Thread, x float64) { +	v.aSet(t, x); +} + +func (v remoteFloat) aSet(a aborter, x float64) { +	var bits uint64; +	switch v.size{ +	case 4: +		bits = uint64(v.r.p.FromFloat32(float32(x))); +	case 8: +		bits = v.r.p.FromFloat64(x); +	default: +		panic("Unexpected float size ", v.size); +	} +	v.r.Set(a, v.size, bits); +} + +func (v remoteFloat) addr() remote { +	return v.r; +} + +func mkFloat32(r remote) eval.Value { +	return remoteFloat{r, 4}; +} + +func mkFloat64(r remote) eval.Value { +	return remoteFloat{r, 8}; +} + +func mkFloat(r remote) eval.Value { +	return remoteFloat{r, r.p.FloatSize()}; +} + +/* + * String + */ + +type remoteString struct { +	r remote; +} + +func (v remoteString) String() string { +	return tryRVString(func(a aborter) string { return v.aGet(a) }); +} + +func (v remoteString) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.StringValue).Get(t)); +} + +func (v remoteString) Get(t *eval.Thread) string { +	return v.aGet(t); +} + +func (v remoteString) aGet(a aborter) string { +	rs := v.r.p.runtime.String.mk(v.r).(remoteStruct); +	str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)); +	len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a); + +	bytes := make([]uint8, len); +	_, err := v.r.p.Peek(str, bytes); +	if err != nil { +		a.Abort(err); +	} +	return string(bytes); +} + +func (v remoteString) Set(t *eval.Thread, x string) { +	v.aSet(t, x); +} + +func (v remoteString) aSet(a aborter, x string) { +	// TODO(austin) This isn't generally possible without the +	// ability to allocate remote memory. +	a.Abort(ReadOnlyError("remote strings cannot be assigned to")); +} + +func mkString(r remote) eval.Value { +	return remoteString{r}; +} + +/* + * Array + */ + +type remoteArray struct { +	r remote; +	len int64; +	elemType *remoteType; +} + +func (v remoteArray) String() string { +	res := "{"; +	for i := int64(0); i < v.len; i++ { +		if i > 0 { +			res += ", "; +		} +		res += v.elem(i).String(); +	} +	return res + "}"; +} + +func (v remoteArray) Assign(t *eval.Thread, o eval.Value) { + 	// TODO(austin) Could do a bigger memcpy if o is a +	// remoteArray in the same Process. +	oa := o.(eval.ArrayValue); +	for i := int64(0); i < v.len; i++ { +		v.Elem(t, i).Assign(t, oa.Elem(t, i)); +	} +} + +func (v remoteArray) Get(t *eval.Thread) eval.ArrayValue { +	return v; +} + +func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { +	return v.elem(i); +} + +func (v remoteArray) elem(i int64) eval.Value { +	return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))); +} + +func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { +	return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType}; +} + +/* + * Struct + */ + +type remoteStruct struct { +	r remote; +	layout []remoteStructField; +} + +type remoteStructField struct { +	offset int; +	fieldType *remoteType; +} + +func (v remoteStruct) String() string { +	res := "{"; +	for i := range v.layout { +		if i > 0 { +			res += ", "; +		} +		res += v.field(i).String(); +	} +	return res + "}"; +} + +func (v remoteStruct) Assign(t *eval.Thread, o eval.Value) { +	// TODO(austin) Could do a bigger memcpy. +	oa := o.(eval.StructValue); +	l := len(v.layout); +	for i := 0; i < l; i++ { +		v.Field(t, i).Assign(t, oa.Field(t, i)); +	} +} + +func (v remoteStruct) Get(t *eval.Thread) eval.StructValue { +	return v; +} + +func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { +	return v.field(i); +} + +func (v remoteStruct) field(i int) eval.Value { +	f := &v.layout[i]; +	return f.fieldType.mk(v.r.plus(proc.Word(f.offset))); +} + +func (v remoteStruct) addr() remote { +	return v.r; +} + +/* + * Pointer + */ + +// TODO(austin) Comparing two remote pointers for equality in the +// interpreter will crash it because the Value's returned from +// remotePtr.Get() will be structs. + +type remotePtr struct { +	r remote; +	elemType *remoteType; +} + +func (v remotePtr) String() string { +	return tryRVString(func(a aborter) string { +		e := v.aGet(a); +		if e == nil { +			return "<nil>"; +		} +		return "&" + e.String(); +	}); +} + +func (v remotePtr) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.PtrValue).Get(t)); +} + +func (v remotePtr) Get(t *eval.Thread) eval.Value { +	return v.aGet(t); +} + +func (v remotePtr) aGet(a aborter) eval.Value { +	addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())); +	if addr == 0 { +		return nil; +	} +	return v.elemType.mk(remote{addr, v.r.p}); +} + +func (v remotePtr) Set(t *eval.Thread, x eval.Value) { +	v.aSet(t, x); +} + +func (v remotePtr) aSet(a aborter, x eval.Value) { +	if x == nil { +		v.r.Set(a, v.r.p.PtrSize(), 0); +		return; +	} +	xr, ok := x.(remoteValue); +	if !ok || v.r.p != xr.addr().p { +		a.Abort(RemoteMismatchError("remote pointer must point within the same process")); +	} +	v.r.Set(a, v.r.p.PtrSize(), uint64(xr.addr().base)); +} + +func (v remotePtr) addr() remote { +	return v.r; +} + +/* + * Slice + */ + +type remoteSlice struct { +	r remote; +	elemType *remoteType; +} + +func (v remoteSlice) String() string { +	return tryRVString(func(a aborter) string { +		b := v.aGet(a).Base; +		if b == nil { +			return "<nil>"; +		} +		return b.String(); +	}); +} + +func (v remoteSlice) Assign(t *eval.Thread, o eval.Value) { +	v.Set(t, o.(eval.SliceValue).Get(t)); +} + +func (v remoteSlice) Get(t *eval.Thread) eval.Slice { +	return v.aGet(t); +} + +func (v remoteSlice) aGet(a aborter) eval.Slice { +	rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); +	base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)); +	nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a); +	cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a); +	if base == 0 { +		return eval.Slice{nil, nel, cap}; +	} +	return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap}; +} + +func (v remoteSlice) Set(t *eval.Thread, x eval.Slice) { +	v.aSet(t, x); +} + +func (v remoteSlice) aSet(a aborter, x eval.Slice) { +	rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct); +	if x.Base == nil { +		rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, 0); +	} else { +		ar, ok := x.Base.(remoteArray); +		if !ok || v.r.p != ar.r.p { +			a.Abort(RemoteMismatchError("remote slice must point within the same process")); +		} +		rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, uint64(ar.r.base)); +	} +	rs.field(v.r.p.f.Slice.Len).(remoteInt).aSet(a, x.Len); +	rs.field(v.r.p.f.Slice.Cap).(remoteInt).aSet(a, x.Cap); +} diff --git a/src/pkg/exp/ogle/vars.go b/src/pkg/exp/ogle/vars.go new file mode 100644 index 000000000..539b5b345 --- /dev/null +++ b/src/pkg/exp/ogle/vars.go @@ -0,0 +1,276 @@ +// 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(); +} + +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.Stderrf("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.Stderrf("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; +} diff --git a/src/run.bash b/src/run.bash index 07f2774a1..619ba9b7d 100755 --- a/src/run.bash +++ b/src/run.bash @@ -26,12 +26,10 @@ maketest() {  maketest \  	pkg \ -	../usr/austin/eval \  # all of these are subtly different  # from what maketest does. -  (xcd pkg/sync;  make clean;  time make @@ -55,7 +53,18 @@ make clean  ./test.bash  ) || exit $? -(xcd ../usr/austin/ogle +(xcd ../usr/r/rpc +make clean +time make +./chanrun +) || exit $? + +(xcd ../usr/dsymonds/iterable +make clean +time make test +) || exit $? + +(xcd pkg/exp/ogle  make clean  time make ogle  ) || exit $? | 
