mirror of
https://github.com/zeek/zeek.git
synced 2025-10-12 03:28:19 +00:00
GH-211: improve consistency of how scripting errors are handled
Scripting errors/mistakes now consistently generate a runtime error which have the behavior of unwinding the call stack all the way out of the current event handler. Before, such errors were not treated consistently and either aborted the process entirely or emitted a message while continuing to execute subsequent statements without well-defined behavior (possibly causing a cascade of errors). The previous behavior also would only unwind out of the current function (if within a function body), not out the current event handler, which is especially problematic for functions that return a value: the caller is essentially left a mess with no way to deal with it. This also changes the behavior of the startup/initialization process to abort if there's errors during bro_init() rather than continue one to the main run loop. The `allow_init_errors` option may change this new, default behavior.
This commit is contained in:
parent
49a30d61cf
commit
67484a90fa
49 changed files with 374 additions and 144 deletions
102
src/Expr.cc
102
src/Expr.cc
|
@ -181,6 +181,27 @@ void Expr::ExprError(const char msg[])
|
|||
SetError();
|
||||
}
|
||||
|
||||
void Expr::RuntimeError(const std::string& msg) const
|
||||
{
|
||||
reporter->ExprRuntimeError(this, "%s", msg.data());
|
||||
}
|
||||
|
||||
void Expr::RuntimeErrorWithCallStack(const std::string& msg) const
|
||||
{
|
||||
auto rcs = render_call_stack();
|
||||
|
||||
if ( rcs.empty() )
|
||||
reporter->ExprRuntimeError(this, "%s", msg.data());
|
||||
else
|
||||
{
|
||||
ODesc d;
|
||||
d.SetShort();
|
||||
Describe(&d);
|
||||
reporter->RuntimeError(GetLocationInfo(), "%s, expression: %s, call stack: %s",
|
||||
msg.data(), d.Description(), rcs.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool Expr::Serialize(SerialInfo* info) const
|
||||
{
|
||||
return SerialObj::Serialize(info);
|
||||
|
@ -272,7 +293,7 @@ Val* NameExpr::Eval(Frame* f) const
|
|||
return v->Ref();
|
||||
else
|
||||
{
|
||||
Error("value used but not set");
|
||||
RuntimeError("value used but not set");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +601,7 @@ Val* BinaryExpr::Eval(Frame* f) const
|
|||
|
||||
if ( v_op1->Size() != v_op2->Size() )
|
||||
{
|
||||
Error("vector operands are of different sizes");
|
||||
RuntimeError("vector operands are of different sizes");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -707,7 +728,7 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
d2 = v2->InternalDouble();
|
||||
}
|
||||
else
|
||||
Internal("bad type in BinaryExpr::Fold");
|
||||
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
||||
|
||||
switch ( tag ) {
|
||||
#define DO_INT_FOLD(op) \
|
||||
|
@ -716,13 +737,13 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
else if ( is_unsigned ) \
|
||||
u3 = u1 op u2; \
|
||||
else \
|
||||
Internal("bad type in BinaryExpr::Fold");
|
||||
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
||||
|
||||
#define DO_UINT_FOLD(op) \
|
||||
if ( is_unsigned ) \
|
||||
u3 = u1 op u2; \
|
||||
else \
|
||||
Internal("bad type in BinaryExpr::Fold");
|
||||
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
||||
|
||||
#define DO_FOLD(op) \
|
||||
if ( is_integral ) \
|
||||
|
@ -750,7 +771,7 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
if ( is_integral )
|
||||
{
|
||||
if ( i2 == 0 )
|
||||
reporter->ExprRuntimeError(this, "division by zero");
|
||||
RuntimeError("division by zero");
|
||||
|
||||
i3 = i1 / i2;
|
||||
}
|
||||
|
@ -758,14 +779,14 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
else if ( is_unsigned )
|
||||
{
|
||||
if ( u2 == 0 )
|
||||
reporter->ExprRuntimeError(this, "division by zero");
|
||||
RuntimeError("division by zero");
|
||||
|
||||
u3 = u1 / u2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( d2 == 0 )
|
||||
reporter->ExprRuntimeError(this, "division by zero");
|
||||
RuntimeError("division by zero");
|
||||
|
||||
d3 = d1 / d2;
|
||||
}
|
||||
|
@ -778,7 +799,7 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
if ( is_integral )
|
||||
{
|
||||
if ( i2 == 0 )
|
||||
reporter->ExprRuntimeError(this, "modulo by zero");
|
||||
RuntimeError("modulo by zero");
|
||||
|
||||
i3 = i1 % i2;
|
||||
}
|
||||
|
@ -786,13 +807,13 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
|
|||
else if ( is_unsigned )
|
||||
{
|
||||
if ( u2 == 0 )
|
||||
reporter->ExprRuntimeError(this, "modulo by zero");
|
||||
RuntimeError("modulo by zero");
|
||||
|
||||
u3 = u1 % u2;
|
||||
}
|
||||
|
||||
else
|
||||
Internal("bad type in BinaryExpr::Fold");
|
||||
RuntimeErrorWithCallStack("bad type in BinaryExpr::Fold");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1122,7 +1143,7 @@ Val* IncrExpr::DoSingleEval(Frame* f, Val* v) const
|
|||
|
||||
if ( k < 0 &&
|
||||
v->Type()->InternalType() == TYPE_INTERNAL_UNSIGNED )
|
||||
Error("count underflow");
|
||||
RuntimeError("count underflow");
|
||||
}
|
||||
|
||||
BroType* ret_type = Type();
|
||||
|
@ -1522,7 +1543,7 @@ Val* AddToExpr::Eval(Frame* f) const
|
|||
{
|
||||
VectorVal* vv = v1->AsVectorVal();
|
||||
if ( ! vv->Assign(vv->Size(), v2) )
|
||||
reporter->Error("type-checking failed in vector append");
|
||||
RuntimeError("type-checking failed in vector append");
|
||||
return v1;
|
||||
}
|
||||
|
||||
|
@ -1959,7 +1980,7 @@ Val* BoolExpr::Eval(Frame* f) const
|
|||
|
||||
if ( vec_v1->Size() != vec_v2->Size() )
|
||||
{
|
||||
Error("vector operands have different sizes");
|
||||
RuntimeError("vector operands have different sizes");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2344,7 +2365,7 @@ Val* CondExpr::Eval(Frame* f) const
|
|||
|
||||
if ( cond->Size() != a->Size() || a->Size() != b->Size() )
|
||||
{
|
||||
Error("vectors in conditional expression have different sizes");
|
||||
RuntimeError("vectors in conditional expression have different sizes");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2669,7 +2690,7 @@ Val* AssignExpr::Eval(Frame* f) const
|
|||
{
|
||||
if ( is_init )
|
||||
{
|
||||
Error("illegal assignment in initialization");
|
||||
RuntimeError("illegal assignment in initialization");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2709,7 +2730,7 @@ void AssignExpr::EvalIntoAggregate(const BroType* t, Val* aggr, Frame* f) const
|
|||
{
|
||||
if ( t->Tag() != TYPE_RECORD )
|
||||
{
|
||||
Error("not a record initializer", t);
|
||||
RuntimeError("not a record initializer");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2718,7 +2739,7 @@ void AssignExpr::EvalIntoAggregate(const BroType* t, Val* aggr, Frame* f) const
|
|||
|
||||
if ( field < 0 )
|
||||
{
|
||||
Error("no such field");
|
||||
RuntimeError("no such field");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2732,7 +2753,7 @@ void AssignExpr::EvalIntoAggregate(const BroType* t, Val* aggr, Frame* f) const
|
|||
}
|
||||
|
||||
if ( op1->Tag() != EXPR_LIST )
|
||||
Error("bad table insertion");
|
||||
RuntimeError("bad table insertion");
|
||||
|
||||
TableVal* tv = aggr->AsTableVal();
|
||||
|
||||
|
@ -2742,7 +2763,7 @@ void AssignExpr::EvalIntoAggregate(const BroType* t, Val* aggr, Frame* f) const
|
|||
return;
|
||||
|
||||
if ( ! tv->Assign(index, v) )
|
||||
Error("type clash in table assignment");
|
||||
RuntimeError("type clash in table assignment");
|
||||
|
||||
Unref(index);
|
||||
}
|
||||
|
@ -3008,7 +3029,7 @@ Val* IndexExpr::Eval(Frame* f) const
|
|||
{
|
||||
if ( v_v1->Size() != v_v2->Size() )
|
||||
{
|
||||
Error("size mismatch, boolean index and vector");
|
||||
RuntimeError("size mismatch, boolean index and vector");
|
||||
Unref(v_result);
|
||||
return 0;
|
||||
}
|
||||
|
@ -3096,14 +3117,14 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const
|
|||
}
|
||||
|
||||
default:
|
||||
Error("type cannot be indexed");
|
||||
RuntimeError("type cannot be indexed");
|
||||
break;
|
||||
}
|
||||
|
||||
if ( v )
|
||||
return v->Ref();
|
||||
|
||||
Error("no such index");
|
||||
RuntimeError("no such index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3136,12 +3157,12 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
|||
auto vt = v->Type();
|
||||
auto vtt = vt->Tag();
|
||||
std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt);
|
||||
Internal(fmt(
|
||||
RuntimeErrorWithCallStack(fmt(
|
||||
"vector index assignment failed for invalid type '%s', value: %s",
|
||||
tn.data(), d.Description()));
|
||||
}
|
||||
else
|
||||
Internal("assignment failed with null value");
|
||||
RuntimeErrorWithCallStack("assignment failed with null value");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3155,21 +3176,21 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
|||
auto vt = v->Type();
|
||||
auto vtt = vt->Tag();
|
||||
std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt);
|
||||
Internal(fmt(
|
||||
RuntimeErrorWithCallStack(fmt(
|
||||
"table index assignment failed for invalid type '%s', value: %s",
|
||||
tn.data(), d.Description()));
|
||||
}
|
||||
else
|
||||
Internal("assignment failed with null value");
|
||||
RuntimeErrorWithCallStack("assignment failed with null value");
|
||||
}
|
||||
break;
|
||||
|
||||
case TYPE_STRING:
|
||||
Internal("assignment via string index accessor not allowed");
|
||||
RuntimeErrorWithCallStack("assignment via string index accessor not allowed");
|
||||
break;
|
||||
|
||||
default:
|
||||
Internal("bad index expression type in assignment");
|
||||
RuntimeErrorWithCallStack("bad index expression type in assignment");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3269,9 +3290,6 @@ void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
|||
if ( IsError() )
|
||||
return;
|
||||
|
||||
if ( field < 0 )
|
||||
ExprError("no such field in record");
|
||||
|
||||
Val* op_v = op->Eval(f);
|
||||
if ( op_v )
|
||||
{
|
||||
|
@ -3298,7 +3316,7 @@ Val* FieldExpr::Fold(Val* v) const
|
|||
return def_attr->AttrExpr()->Eval(0);
|
||||
else
|
||||
{
|
||||
reporter->ExprRuntimeError(this, "field value missing");
|
||||
RuntimeError("field value missing");
|
||||
assert(false);
|
||||
return 0; // Will never get here, but compiler can't tell.
|
||||
}
|
||||
|
@ -3483,7 +3501,7 @@ Val* RecordConstructorExpr::Fold(Val* v) const
|
|||
RecordType* rt = type->AsRecordType();
|
||||
|
||||
if ( lv->Length() != rt->NumFields() )
|
||||
Internal("inconsistency evaluating record constructor");
|
||||
RuntimeErrorWithCallStack("inconsistency evaluating record constructor");
|
||||
|
||||
RecordVal* rv = new RecordVal(rt);
|
||||
|
||||
|
@ -3838,7 +3856,7 @@ Val* VectorConstructorExpr::Eval(Frame* f) const
|
|||
Val* v = e->Eval(f);
|
||||
if ( ! vec->Assign(i, v) )
|
||||
{
|
||||
Error(fmt("type mismatch at index %d", i), e);
|
||||
RuntimeError(fmt("type mismatch at index %d", i));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -3997,7 +4015,7 @@ Val* ArithCoerceExpr::FoldSingleVal(Val* v, InternalTypeTag t) const
|
|||
return val_mgr->GetCount(v->CoerceToUnsigned());
|
||||
|
||||
default:
|
||||
Internal("bad type in CoerceExpr::Fold");
|
||||
RuntimeErrorWithCallStack("bad type in CoerceExpr::Fold");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -4305,7 +4323,7 @@ Val* TableCoerceExpr::Fold(Val* v) const
|
|||
TableVal* tv = v->AsTableVal();
|
||||
|
||||
if ( tv->Size() > 0 )
|
||||
Internal("coercion of non-empty table/set");
|
||||
RuntimeErrorWithCallStack("coercion of non-empty table/set");
|
||||
|
||||
return new TableVal(Type()->AsTableType(), tv->Attrs());
|
||||
}
|
||||
|
@ -4349,7 +4367,7 @@ Val* VectorCoerceExpr::Fold(Val* v) const
|
|||
VectorVal* vv = v->AsVectorVal();
|
||||
|
||||
if ( vv->Size() > 0 )
|
||||
Internal("coercion of non-empty vector");
|
||||
RuntimeErrorWithCallStack("coercion of non-empty vector");
|
||||
|
||||
return new VectorVal(Type()->Ref()->AsVectorType());
|
||||
}
|
||||
|
@ -4410,7 +4428,7 @@ Val* FlattenExpr::Fold(Val* v) const
|
|||
l->Append(fa->AttrExpr()->Eval(0));
|
||||
|
||||
else
|
||||
reporter->ExprRuntimeError(this, "missing field value");
|
||||
RuntimeError("missing field value");
|
||||
}
|
||||
|
||||
return l;
|
||||
|
@ -5070,7 +5088,7 @@ Val* ListExpr::Eval(Frame* f) const
|
|||
Val* ev = exprs[i]->Eval(f);
|
||||
if ( ! ev )
|
||||
{
|
||||
Error("uninitialized list value");
|
||||
RuntimeError("uninitialized list value");
|
||||
Unref(v);
|
||||
return 0;
|
||||
}
|
||||
|
@ -5367,7 +5385,7 @@ void ListExpr::Assign(Frame* f, Val* v, Opcode op)
|
|||
ListVal* lv = v->AsListVal();
|
||||
|
||||
if ( exprs.length() != lv->Vals()->length() )
|
||||
ExprError("mismatch in list lengths");
|
||||
RuntimeError("mismatch in list lengths");
|
||||
|
||||
loop_over_list(exprs, i)
|
||||
exprs[i]->Assign(f, (*lv->Vals())[i]->Ref(), op);
|
||||
|
@ -5542,7 +5560,7 @@ Val* CastExpr::Eval(Frame* f) const
|
|||
d.Add(" (nil $data field)");
|
||||
|
||||
Unref(v);
|
||||
reporter->ExprRuntimeError(this, "%s", d.Description());
|
||||
RuntimeError(d.Description());
|
||||
return 0; // not reached.
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue