Commit 9d517ba3 authored by Austin Clements's avatar Austin Clements

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
parent 636cdc76
...@@ -6,6 +6,8 @@ include $(GOROOT)/src/Make.$(GOARCH) ...@@ -6,6 +6,8 @@ include $(GOROOT)/src/Make.$(GOARCH)
TARG=eval TARG=eval
GOFILES=\ GOFILES=\
abort.go\
bridge.go\
compiler.go\ compiler.go\
decls.go\ decls.go\
expr.go\ expr.go\
......
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package eval
import (
"fmt";
"os";
"runtime";
)
// 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);
}
...@@ -990,8 +990,12 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { ...@@ -990,8 +990,12 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
switch _ := r.t.lit().(type) { switch _ := r.t.lit().(type) {
case *idealIntType: case *idealIntType:
val := r.asIdealInt()(); val := r.asIdealInt()();
if val.IsNeg() || (maxIndex != -1 && val.Cmp(bignum.Int(maxIndex)) >= 0) { if val.IsNeg() {
a.diag("array index out of bounds"); 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; return nil;
} }
r = r.convertTo(IntType); r = r.convertTo(IntType);
...@@ -1022,36 +1026,45 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { ...@@ -1022,36 +1026,45 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// Compile // Compile
switch lt := l.t.lit().(type) { switch lt := l.t.lit().(type) {
case *ArrayType: case *ArrayType:
// TODO(austin) Bounds check
expr.genIndexArray(l, r);
lf := l.asArray(); lf := l.asArray();
rf := r.asInt(); rf := r.asInt();
expr.evalAddr = func(f *Frame) Value { bound := lt.Len;
return lf(f).Elem(rf(f)); 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: case *SliceType:
// TODO(austin) Bounds check
// TODO(austin) Can this be done with genValue?
expr.genIndexSlice(l, r);
lf := l.asSlice(); lf := l.asSlice();
rf := r.asInt(); rf := r.asInt();
expr.evalAddr = func(f *Frame) Value { expr.genValue(func(f *Frame) Value {
return lf(f).Base.Elem(rf(f)); 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: case *stringType:
// TODO(austin) Bounds check
lf := l.asString(); lf := l.asString();
rf := r.asInt(); rf := r.asInt();
// TODO(austin) This pulls over the whole string in a // TODO(austin) This pulls over the whole string in a
// remote setting, instead of just the one character. // remote setting, instead of just the one character.
expr.evalUint = func(f *Frame) uint64 { 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: case *MapType:
// TODO(austin) Bounds check
lf := l.asMap(); lf := l.asMap();
rf := r.asInterface(); rf := r.asInterface();
expr.genValue(func(f *Frame) Value { expr.genValue(func(f *Frame) Value {
...@@ -1059,8 +1072,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { ...@@ -1059,8 +1072,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
k := rf(f); k := rf(f);
e := m.Elem(k); e := m.Elem(k);
if e == nil { if e == nil {
// TODO(austin) Use an exception Abort(KeyNotFound{k});
panic("key ", k, " not found in map");
} }
return e; return e;
}); });
...@@ -1068,6 +1080,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { ...@@ -1068,6 +1080,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// aren't addressable. // aren't addressable.
expr.evalAddr = nil; expr.evalAddr = nil;
expr.evalMapValue = func(f *Frame) (Map, interface{}) { expr.evalMapValue = func(f *Frame) (Map, interface{}) {
// TODO(austin) Key check?
return lf(f), rf(f); return lf(f), rf(f);
}; };
...@@ -1153,8 +1166,14 @@ func (a *exprInfo) compileStarExpr(v *expr) *expr { ...@@ -1153,8 +1166,14 @@ func (a *exprInfo) compileStarExpr(v *expr) *expr {
switch vt := v.t.lit().(type) { switch vt := v.t.lit().(type) {
case *PtrType: case *PtrType:
expr := a.newExpr(vt.Elem, "indirect expression"); expr := a.newExpr(vt.Elem, "indirect expression");
// TODO(austin) Deal with nil pointers vf := v.asPtr();
expr.genValue(v.asPtr()); expr.genValue(func(f *Frame) Value {
v := vf(f);
if v == nil {
Abort(NilPointer{});
}
return v;
});
return expr; return expr;
} }
...@@ -1496,6 +1515,18 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { ...@@ -1496,6 +1515,18 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
binOpDescs[op] = desc; 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 // Compile
expr := a.newExpr(t, desc); expr := a.newExpr(t, desc);
switch op { switch op {
...@@ -1509,13 +1540,11 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { ...@@ -1509,13 +1540,11 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
expr.genBinOpMul(l, r); expr.genBinOpMul(l, r);
case token.QUO: case token.QUO:
// TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have // TODO(austin) Clear higher bits that may have
// accumulated in our temporary. // accumulated in our temporary.
expr.genBinOpQuo(l, r); expr.genBinOpQuo(l, r);
case token.REM: case token.REM:
// TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have // TODO(austin) Clear higher bits that may have
// accumulated in our temporary. // accumulated in our temporary.
expr.genBinOpRem(l, r); expr.genBinOpRem(l, r);
...@@ -1697,10 +1726,10 @@ type Expr struct { ...@@ -1697,10 +1726,10 @@ type Expr struct {
f func(f *Frame, out Value); 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(); v := expr.t.Zero();
expr.f(f, v); err := Try(func() {expr.f(f, v)});
return v; return v, err;
} }
func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) { func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) {
...@@ -1820,68 +1849,6 @@ func (a *expr) genIdentOp(level int, index int) { ...@@ -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) { func (a *expr) genFuncCall(call func(f *Frame) []Value) {
a.exec = func(f *Frame) { call(f) }; a.exec = func(f *Frame) { call(f) };
switch _ := a.t.lit().(type) { switch _ := a.t.lit().(type) {
...@@ -2091,11 +2058,11 @@ func (a *expr) genBinOpQuo(l, r *expr) { ...@@ -2091,11 +2058,11 @@ func (a *expr) genBinOpQuo(l, r *expr) {
case *uintType: case *uintType:
lf := l.asUint(); lf := l.asUint();
rf := r.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: case *intType:
lf := l.asInt(); lf := l.asInt();
rf := r.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: case *idealIntType:
lf := l.asIdealInt(); lf := l.asIdealInt();
rf := r.asIdealInt(); rf := r.asIdealInt();
...@@ -2104,7 +2071,7 @@ func (a *expr) genBinOpQuo(l, r *expr) { ...@@ -2104,7 +2071,7 @@ func (a *expr) genBinOpQuo(l, r *expr) {
case *floatType: case *floatType:
lf := l.asFloat(); lf := l.asFloat();
rf := r.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: case *idealFloatType:
lf := l.asIdealFloat(); lf := l.asIdealFloat();
rf := r.asIdealFloat(); rf := r.asIdealFloat();
...@@ -2120,11 +2087,11 @@ func (a *expr) genBinOpRem(l, r *expr) { ...@@ -2120,11 +2087,11 @@ func (a *expr) genBinOpRem(l, r *expr) {
case *uintType: case *uintType:
lf := l.asUint(); lf := l.asUint();
rf := r.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: case *intType:
lf := l.asInt(); lf := l.asInt();
rf := r.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: case *idealIntType:
lf := l.asIdealInt(); lf := l.asIdealInt();
rf := r.asIdealInt(); rf := r.asIdealInt();
......
...@@ -1281,8 +1281,8 @@ type Stmt struct { ...@@ -1281,8 +1281,8 @@ type Stmt struct {
f func (f *Frame); f func (f *Frame);
} }
func (s *Stmt) Exec(f *Frame) { func (s *Stmt) Exec(f *Frame) os.Error {
s.f(f); return Try(func() {s.f(f)});
} }
func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) { func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment