summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Clements <aclements@csail.mit.edu>2009-08-28 10:39:57 -0700
committerAustin Clements <aclements@csail.mit.edu>2009-08-28 10:39:57 -0700
commitdb2c4b8dcf4b0a04a60d2acb2c1f7b6151541f76 (patch)
tree621b26e8f65ad78dcd1edf3bb8c5473a24029f47
parentd820612026ad44252988b00bc5fcfa3e350dbc2a (diff)
downloadgolang-db2c4b8dcf4b0a04a60d2acb2c1f7b6151541f76.tar.gz
Implement runtime errors, divide-by-zero checking, nil pointer
checking, bounds checking, and map key checking. R=rsc APPROVED=rsc DELTA=202 (108 added, 72 deleted, 22 changed) OCL=33981 CL=34031
-rw-r--r--usr/austin/eval/Makefile2
-rw-r--r--usr/austin/eval/abort.go71
-rw-r--r--usr/austin/eval/expr.go151
-rw-r--r--usr/austin/eval/stmt.go4
4 files changed, 134 insertions, 94 deletions
diff --git a/usr/austin/eval/Makefile b/usr/austin/eval/Makefile
index 3a477710b..fb870c478 100644
--- a/usr/austin/eval/Makefile
+++ b/usr/austin/eval/Makefile
@@ -6,6 +6,8 @@ include $(GOROOT)/src/Make.$(GOARCH)
TARG=eval
GOFILES=\
+ abort.go\
+ bridge.go\
compiler.go\
decls.go\
expr.go\
diff --git a/usr/austin/eval/abort.go b/usr/austin/eval/abort.go
new file mode 100644
index 000000000..bee290421
--- /dev/null
+++ b/usr/austin/eval/abort.go
@@ -0,0 +1,71 @@
+// 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";
+)
+
+// TODO(austin) This is not thread-safe. We could include the abort
+// channel in the Frame structure, but then the Value methods need to
+// take the Frame. However, passing something to the Value methods
+// might be necessary to generate back traces.
+var abortChan = make(chan os.Error)
+
+// Abort aborts the current computation. If this is called within the
+// extent of a Try call, this immediately returns to the Try with the
+// given error. If not, then this panic's.
+func Abort(e os.Error) {
+ if abortChan == nil {
+ panic("Abort: " + e.String());
+ }
+ abortChan <- e;
+ runtime.Goexit();
+}
+
+// Try executes a computation with the ability to Abort.
+func Try(f func()) os.Error {
+ abortChan = make(chan os.Error);
+ go func() {
+ f();
+ abortChan <- nil;
+ }();
+ res := <-abortChan;
+ abortChan = nil;
+ return res;
+}
+
+type DivByZero struct {}
+
+func (DivByZero) String() string {
+ return "divide by zero";
+}
+
+type NilPointer struct {}
+
+func (NilPointer) String() string {
+ return "nil pointer dereference";
+}
+
+type IndexOutOfBounds struct {
+ Idx, Len int64;
+}
+
+func (e IndexOutOfBounds) 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 KeyNotFound struct {
+ Key interface {};
+}
+
+func (e KeyNotFound) String() string {
+ return fmt.Sprintf("key %s not found in map", e.Key);
+}
diff --git a/usr/austin/eval/expr.go b/usr/austin/eval/expr.go
index 43ec54781..c6650729a 100644
--- a/usr/austin/eval/expr.go
+++ b/usr/austin/eval/expr.go
@@ -990,8 +990,12 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
switch _ := r.t.lit().(type) {
case *idealIntType:
val := r.asIdealInt()();
- if val.IsNeg() || (maxIndex != -1 && val.Cmp(bignum.Int(maxIndex)) >= 0) {
- a.diag("array index out of bounds");
+ if val.IsNeg() {
+ a.diag("negative index: %s", val);
+ return nil;
+ }
+ if maxIndex != -1 && val.Cmp(bignum.Int(maxIndex)) >= 0 {
+ a.diag("index %s exceeds length %d", val, maxIndex);
return nil;
}
r = r.convertTo(IntType);
@@ -1022,36 +1026,45 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// Compile
switch lt := l.t.lit().(type) {
case *ArrayType:
- // TODO(austin) Bounds check
- expr.genIndexArray(l, r);
lf := l.asArray();
rf := r.asInt();
- expr.evalAddr = func(f *Frame) Value {
- return lf(f).Elem(rf(f));
- };
+ bound := lt.Len;
+ expr.genValue(func(f *Frame) Value {
+ l, r := lf(f), rf(f);
+ if r < 0 || r >= bound {
+ Abort(IndexOutOfBounds{r, bound});
+ }
+ return l.Elem(r);
+ });
case *SliceType:
- // TODO(austin) Bounds check
- // TODO(austin) Can this be done with genValue?
- expr.genIndexSlice(l, r);
lf := l.asSlice();
rf := r.asInt();
- expr.evalAddr = func(f *Frame) Value {
- return lf(f).Base.Elem(rf(f));
- };
+ expr.genValue(func(f *Frame) Value {
+ l, r := lf(f), rf(f);
+ if l.Base == nil {
+ Abort(NilPointer{});
+ }
+ if r < 0 || r >= l.Len {
+ Abort(IndexOutOfBounds{r, l.Len});
+ }
+ return l.Base.Elem(r);
+ });
case *stringType:
- // TODO(austin) Bounds check
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.evalUint = func(f *Frame) uint64 {
- return uint64(lf(f)[rf(f)]);
+ l, r := lf(f), rf(f);
+ if r < 0 || r >= int64(len(l)) {
+ Abort(IndexOutOfBounds{r, int64(len(l))});
+ }
+ return uint64(l[r]);
}
case *MapType:
- // TODO(austin) Bounds check
lf := l.asMap();
rf := r.asInterface();
expr.genValue(func(f *Frame) Value {
@@ -1059,8 +1072,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
k := rf(f);
e := m.Elem(k);
if e == nil {
- // TODO(austin) Use an exception
- panic("key ", k, " not found in map");
+ Abort(KeyNotFound{k});
}
return e;
});
@@ -1068,6 +1080,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// aren't addressable.
expr.evalAddr = nil;
expr.evalMapValue = func(f *Frame) (Map, interface{}) {
+ // TODO(austin) Key check?
return lf(f), rf(f);
};
@@ -1153,8 +1166,14 @@ func (a *exprInfo) compileStarExpr(v *expr) *expr {
switch vt := v.t.lit().(type) {
case *PtrType:
expr := a.newExpr(vt.Elem, "indirect expression");
- // TODO(austin) Deal with nil pointers
- expr.genValue(v.asPtr());
+ vf := v.asPtr();
+ expr.genValue(func(f *Frame) Value {
+ v := vf(f);
+ if v == nil {
+ Abort(NilPointer{});
+ }
+ return v;
+ });
return expr;
}
@@ -1496,6 +1515,18 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
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 {
@@ -1509,13 +1540,11 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
expr.genBinOpMul(l, r);
case token.QUO:
- // TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
expr.genBinOpQuo(l, r);
case token.REM:
- // TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
expr.genBinOpRem(l, r);
@@ -1697,10 +1726,10 @@ type Expr struct {
f func(f *Frame, out Value);
}
-func (expr *Expr) Eval(f *Frame) Value {
+func (expr *Expr) Eval(f *Frame) (Value, os.Error) {
v := expr.t.Zero();
- expr.f(f, v);
- return v;
+ err := Try(func() {expr.f(f, v)});
+ return v, err;
}
func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) {
@@ -1820,68 +1849,6 @@ func (a *expr) genIdentOp(level int, index int) {
}
}
-func (a *expr) genIndexArray(l, r *expr) {
- lf := l.asArray();
- rf := r.asInt();
- switch _ := a.t.lit().(type) {
- case *boolType:
- a.evalBool = func(f *Frame) bool { return lf(f).Elem(rf(f)).(BoolValue).Get() };
- case *uintType:
- a.evalUint = func(f *Frame) uint64 { return lf(f).Elem(rf(f)).(UintValue).Get() };
- case *intType:
- a.evalInt = func(f *Frame) int64 { return lf(f).Elem(rf(f)).(IntValue).Get() };
- case *floatType:
- a.evalFloat = func(f *Frame) float64 { return lf(f).Elem(rf(f)).(FloatValue).Get() };
- case *stringType:
- a.evalString = func(f *Frame) string { return lf(f).Elem(rf(f)).(StringValue).Get() };
- case *ArrayType:
- a.evalArray = func(f *Frame) ArrayValue { return lf(f).Elem(rf(f)).(ArrayValue).Get() };
- case *StructType:
- a.evalStruct = func(f *Frame) StructValue { return lf(f).Elem(rf(f)).(StructValue).Get() };
- case *PtrType:
- a.evalPtr = func(f *Frame) Value { return lf(f).Elem(rf(f)).(PtrValue).Get() };
- case *FuncType:
- a.evalFunc = func(f *Frame) Func { return lf(f).Elem(rf(f)).(FuncValue).Get() };
- case *SliceType:
- a.evalSlice = func(f *Frame) Slice { return lf(f).Elem(rf(f)).(SliceValue).Get() };
- case *MapType:
- a.evalMap = func(f *Frame) Map { return lf(f).Elem(rf(f)).(MapValue).Get() };
- default:
- log.Crashf("unexpected result type %v at %v", a.t, a.pos);
- }
-}
-
-func (a *expr) genIndexSlice(l, r *expr) {
- lf := l.asSlice();
- rf := r.asInt();
- switch _ := a.t.lit().(type) {
- case *boolType:
- a.evalBool = func(f *Frame) bool { return lf(f).Base.Elem(rf(f)).(BoolValue).Get() };
- case *uintType:
- a.evalUint = func(f *Frame) uint64 { return lf(f).Base.Elem(rf(f)).(UintValue).Get() };
- case *intType:
- a.evalInt = func(f *Frame) int64 { return lf(f).Base.Elem(rf(f)).(IntValue).Get() };
- case *floatType:
- a.evalFloat = func(f *Frame) float64 { return lf(f).Base.Elem(rf(f)).(FloatValue).Get() };
- case *stringType:
- a.evalString = func(f *Frame) string { return lf(f).Base.Elem(rf(f)).(StringValue).Get() };
- case *ArrayType:
- a.evalArray = func(f *Frame) ArrayValue { return lf(f).Base.Elem(rf(f)).(ArrayValue).Get() };
- case *StructType:
- a.evalStruct = func(f *Frame) StructValue { return lf(f).Base.Elem(rf(f)).(StructValue).Get() };
- case *PtrType:
- a.evalPtr = func(f *Frame) Value { return lf(f).Base.Elem(rf(f)).(PtrValue).Get() };
- case *FuncType:
- a.evalFunc = func(f *Frame) Func { return lf(f).Base.Elem(rf(f)).(FuncValue).Get() };
- case *SliceType:
- a.evalSlice = func(f *Frame) Slice { return lf(f).Base.Elem(rf(f)).(SliceValue).Get() };
- case *MapType:
- a.evalMap = func(f *Frame) Map { return lf(f).Base.Elem(rf(f)).(MapValue).Get() };
- default:
- log.Crashf("unexpected result type %v at %v", a.t, a.pos);
- }
-}
-
func (a *expr) genFuncCall(call func(f *Frame) []Value) {
a.exec = func(f *Frame) { call(f) };
switch _ := a.t.lit().(type) {
@@ -2091,11 +2058,11 @@ func (a *expr) genBinOpQuo(l, r *expr) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
- a.evalUint = func(f *Frame) uint64 { return lf(f) / rf(f) };
+ a.evalUint = func(f *Frame) uint64 { l, r := lf(f), rf(f); if r == 0 { Abort(DivByZero{}) }; return l / r };
case *intType:
lf := l.asInt();
rf := r.asInt();
- a.evalInt = func(f *Frame) int64 { return lf(f) / rf(f) };
+ a.evalInt = func(f *Frame) int64 { l, r := lf(f), rf(f); if r == 0 { Abort(DivByZero{}) }; return l / r };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
@@ -2104,7 +2071,7 @@ func (a *expr) genBinOpQuo(l, r *expr) {
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
- a.evalFloat = func(f *Frame) float64 { return lf(f) / rf(f) };
+ a.evalFloat = func(f *Frame) float64 { l, r := lf(f), rf(f); if r == 0 { Abort(DivByZero{}) }; return l / r };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
@@ -2120,11 +2087,11 @@ func (a *expr) genBinOpRem(l, r *expr) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
- a.evalUint = func(f *Frame) uint64 { return lf(f) % rf(f) };
+ a.evalUint = func(f *Frame) uint64 { l, r := lf(f), rf(f); if r == 0 { Abort(DivByZero{}) }; return l % r };
case *intType:
lf := l.asInt();
rf := r.asInt();
- a.evalInt = func(f *Frame) int64 { return lf(f) % rf(f) };
+ a.evalInt = func(f *Frame) int64 { l, r := lf(f), rf(f); if r == 0 { Abort(DivByZero{}) }; return l % r };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
diff --git a/usr/austin/eval/stmt.go b/usr/austin/eval/stmt.go
index 4c90da0aa..b6e471dee 100644
--- a/usr/austin/eval/stmt.go
+++ b/usr/austin/eval/stmt.go
@@ -1281,8 +1281,8 @@ type Stmt struct {
f func (f *Frame);
}
-func (s *Stmt) Exec(f *Frame) {
- s.f(f);
+func (s *Stmt) Exec(f *Frame) os.Error {
+ return Try(func() {s.f(f)});
}
func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) {