Aul: Gracefully handle errors in codegen (#1840)

By continuing to generate bytecode even after an error is found, we're
able to find more syntax errors and will also be able to keep the value
stack at the expected height.
directional-lights
Nicolas Hake 2016-11-13 10:57:18 +01:00
parent ff0325dfac
commit eda6cc9c7f
2 changed files with 231 additions and 106 deletions

View File

@ -151,6 +151,7 @@ public:
virtual void visit(const ::aul::ast::ParExpr *n) override; virtual void visit(const ::aul::ast::ParExpr *n) override;
virtual void visit(const ::aul::ast::AppendtoPragma *n) override; virtual void visit(const ::aul::ast::AppendtoPragma *n) override;
virtual void visit(const ::aul::ast::IncludePragma *n) override; virtual void visit(const ::aul::ast::IncludePragma *n) override;
virtual void visit(const ::aul::ast::Script *n) override;
}; };
class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor
@ -212,6 +213,49 @@ class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor
void RemoveLastBCC(); void RemoveLastBCC();
C4AulBCC MakeSetter(const char *SPos, bool fLeaveValue); C4AulBCC MakeSetter(const char *SPos, bool fLeaveValue);
void HandleError(const C4AulError &e)
{
AddBCC(nullptr, AB_ERR, (intptr_t)::Strings.RegString(e.what()));
target_host->Engine->ErrorHandler->OnError(e.what());
}
template<class T>
bool SafeVisit(const T &node)
{
// Swallows exceptions during evaluation of node. Use if you want to
// keep doing syntax checks for subsequent children. (Generated code
// will cause a runtime error if executed.)
try
{
node->accept(this);
return true;
}
catch (C4AulParseError &e)
{
HandleError(e);
return false;
}
}
class StackGuard
{
// Ensures that the Aul value stack ends up at the expected height
CodegenAstVisitor *parent;
const int32_t target_stack_height;
public:
explicit StackGuard(CodegenAstVisitor *parent, int32_t offset = 0) : parent(parent), target_stack_height(parent->stack_height + offset)
{}
~StackGuard()
{
assert(parent->stack_height == target_stack_height);
if (parent->stack_height != target_stack_height)
{
parent->HandleError(Error(parent->target_host, parent->host, nullptr, parent->Fn, "internal error: value stack left unbalanced"));
parent->AddBCC(nullptr, AB_STACK, target_stack_height - parent->stack_height);
}
}
};
public: public:
CodegenAstVisitor(C4ScriptHost *host, C4ScriptHost *source_host) : target_host(host), host(source_host) {} CodegenAstVisitor(C4ScriptHost *host, C4ScriptHost *source_host) : target_host(host), host(source_host) {}
explicit CodegenAstVisitor(C4AulScriptFunc *func) : Fn(func), target_host(func->pOrgScript), host(target_host) {} explicit CodegenAstVisitor(C4AulScriptFunc *func) : Fn(func), target_host(func->pOrgScript), host(target_host) {}
@ -502,7 +546,7 @@ void C4AulCompiler::PreparseAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
try try
{ {
DefaultRecursiveVisitor::visit(n); DefaultRecursiveVisitor::visit(n);
Fn = nullptr; Fn = nullptr;
} }
catch (...) catch (...)
@ -544,6 +588,21 @@ void C4AulCompiler::PreparseAstVisitor::visit(const ::aul::ast::IncludePragma *n
host->Includes.emplace_back(n->what.c_str()); host->Includes.emplace_back(n->what.c_str());
} }
void C4AulCompiler::PreparseAstVisitor::visit(const::aul::ast::Script * n)
{
for (const auto &d : n->declarations)
{
try
{
d->accept(this);
}
catch (C4AulParseError &e)
{
target_host->Engine->GetErrorHandler()->OnError(e.what());
}
}
}
int C4AulCompiler::CodegenAstVisitor::GetStackValue(C4AulBCCType eType, intptr_t X) int C4AulCompiler::CodegenAstVisitor::GetStackValue(C4AulBCCType eType, intptr_t X)
{ {
switch (eType) switch (eType)
@ -894,27 +953,31 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Noop *) {}
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::StringLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::StringLit *n)
{ {
StackGuard g(this, 1);
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(n->value.c_str())); AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(n->value.c_str()));
type_of_stack_top = C4V_String; type_of_stack_top = C4V_String;
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::IntLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::IntLit *n)
{ {
StackGuard g(this, 1);
AddBCC(n->loc, AB_INT, n->value); AddBCC(n->loc, AB_INT, n->value);
type_of_stack_top = C4V_Int; type_of_stack_top = C4V_Int;
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BoolLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BoolLit *n)
{ {
StackGuard g(this, 1);
AddBCC(n->loc, AB_BOOL, n->value); AddBCC(n->loc, AB_BOOL, n->value);
type_of_stack_top = C4V_Bool; type_of_stack_top = C4V_Bool;
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n)
{ {
StackGuard g(this, 1);
for (const auto &e : n->values) for (const auto &e : n->values)
{ {
e->accept(this); SafeVisit(e);
} }
AddBCC(n->loc, AB_NEW_ARRAY, n->values.size()); AddBCC(n->loc, AB_NEW_ARRAY, n->values.size());
type_of_stack_top = C4V_Array; type_of_stack_top = C4V_Array;
@ -922,10 +985,12 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n)
{ {
StackGuard g(this, 1);
for (const auto &e : n->values) for (const auto &e : n->values)
{ {
StackGuard g(this, 2);
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(e.first.c_str())); AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(e.first.c_str()));
e.second->accept(this); SafeVisit(e.second);
} }
AddBCC(n->loc, AB_NEW_PROPLIST, n->values.size()); AddBCC(n->loc, AB_NEW_PROPLIST, n->values.size());
type_of_stack_top = C4V_PropList; type_of_stack_top = C4V_PropList;
@ -933,18 +998,21 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::NilLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::NilLit *n)
{ {
StackGuard g(this, 1);
AddBCC(n->loc, AB_NIL); AddBCC(n->loc, AB_NIL);
type_of_stack_top = C4V_Nil; type_of_stack_top = C4V_Nil;
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ThisLit *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ThisLit *n)
{ {
StackGuard g(this, 1);
AddBCC(n->loc, AB_THIS); AddBCC(n->loc, AB_THIS);
type_of_stack_top = C4V_PropList; type_of_stack_top = C4V_PropList;
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n)
{ {
StackGuard g(this, 1);
assert(Fn); assert(Fn);
C4Value dummy; C4Value dummy;
const char *cname = n->identifier.c_str(); const char *cname = n->identifier.c_str();
@ -1004,18 +1072,22 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n)
case C4V_Function: case C4V_Function:
AddBCC(n->loc, AB_CFUNCTION, reinterpret_cast<intptr_t>(v._getFunction())); AddBCC(n->loc, AB_CFUNCTION, reinterpret_cast<intptr_t>(v._getFunction()));
default: default:
AddBCC(n->loc, AB_NIL);
throw Error(target_host, host, n, Fn, "internal error: global constant of unexpected type: %s (of type %s)", cname, v.GetTypeName()); throw Error(target_host, host, n, Fn, "internal error: global constant of unexpected type: %s (of type %s)", cname, v.GetTypeName());
} }
type_of_stack_top = v.GetType(); type_of_stack_top = v.GetType();
} }
else else
{ {
AddBCC(n->loc, AB_NIL);
throw Error(target_host, host, n, Fn, "symbol not found in any symbol table: %s", cname); throw Error(target_host, host, n, Fn, "symbol not found in any symbol table: %s", cname);
} }
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n)
{ {
StackGuard g(this, 1);
n->operand->accept(this); n->operand->accept(this);
const auto &op = C4ScriptOpMap[n->op]; const auto &op = C4ScriptOpMap[n->op];
if (op.Changer) if (op.Changer)
@ -1038,7 +1110,9 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
{ {
n->lhs->accept(this); StackGuard g(this, 1);
SafeVisit(n->lhs);
const auto &op = C4ScriptOpMap[n->op]; const auto &op = C4ScriptOpMap[n->op];
if (op.Code == AB_JUMPAND || op.Code == AB_JUMPOR || op.Code == AB_JUMPNNIL) if (op.Code == AB_JUMPAND || op.Code == AB_JUMPOR || op.Code == AB_JUMPNNIL)
@ -1047,19 +1121,26 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
// because we don't want to evaluate their rhs operand when the // because we don't want to evaluate their rhs operand when the
// lhs one already decided the result // lhs one already decided the result
int jump = AddBCC(n->loc, op.Code); int jump = AddBCC(n->loc, op.Code);
n->rhs->accept(this); SafeVisit(n->rhs);
UpdateJump(jump, AddJumpTarget()); UpdateJump(jump, AddJumpTarget());
} }
else if (op.Changer) else if (op.Changer)
{ {
C4AulBCC setter = MakeSetter(n->loc, true); try
n->rhs->accept(this); {
AddBCC(n->loc, op.Code); C4AulBCC setter = MakeSetter(n->loc, true);
AddBCC(n->loc, setter); SafeVisit(n->rhs);
AddBCC(n->loc, op.Code);
AddBCC(n->loc, setter);
}
catch (C4AulParseError &e)
{
HandleError(e);
}
} }
else else
{ {
n->rhs->accept(this); SafeVisit(n->rhs);
AddBCC(n->loc, op.Code, 0); AddBCC(n->loc, op.Code, 0);
} }
@ -1068,18 +1149,26 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::AssignmentExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::AssignmentExpr *n)
{ {
n->lhs->accept(this); StackGuard g(this, 1);
C4AulBCC setter = MakeSetter(n->loc, false); SafeVisit(n->lhs);
n->rhs->accept(this); try
AddBCC(n->loc, setter); {
C4AulBCC setter = MakeSetter(n->loc, false);
SafeVisit(n->rhs);
AddBCC(n->loc, setter);
}
catch (C4AulParseError &e)
{
HandleError(e);
}
// Assignment does not change the type of the variable // Assignment does not change the type of the variable
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n)
{ {
n->object->accept(this); StackGuard g(this, 1);
n->index->accept(this); SafeVisit(n->object);
SafeVisit(n->index);
AddBCC(n->loc, AB_ARRAYA); AddBCC(n->loc, AB_ARRAYA);
// FIXME: Check if the subscripted object is a literal and if so, retrieve type // FIXME: Check if the subscripted object is a literal and if so, retrieve type
@ -1088,9 +1177,10 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SliceExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SliceExpr *n)
{ {
n->object->accept(this); StackGuard g(this, 1);
n->start->accept(this); SafeVisit(n->object);
n->end->accept(this); SafeVisit(n->start);
SafeVisit(n->end);
AddBCC(n->loc, AB_ARRAY_SLICE); AddBCC(n->loc, AB_ARRAY_SLICE);
type_of_stack_top = C4V_Array; type_of_stack_top = C4V_Array;
@ -1114,17 +1204,31 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
return; return;
} }
if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited)
{
// inherited can only be called within the same context
if (n->context)
{
throw Error(target_host, host, n, Fn, "\"%s\" can't be called in a different context", cname);
}
}
if (n->callee == C4AUL_Inherited && !Fn->OwnerOverloaded)
{
throw Error(target_host, host, n, Fn, "inherited function not found (use " C4AUL_SafeInherited " to disable this message)");
}
const auto pre_call_stack = stack_height; const auto pre_call_stack = stack_height;
if (n->context) if (n->context)
n->context->accept(this); SafeVisit(n->context);
std::vector<C4V_Type> known_par_types; std::vector<C4V_Type> known_par_types;
known_par_types.reserve(n->args.size()); known_par_types.reserve(n->args.size());
for (const auto &arg : n->args) for (const auto &arg : n->args)
{ {
arg->accept(this); SafeVisit(arg);
known_par_types.push_back(type_of_stack_top); known_par_types.push_back(type_of_stack_top);
} }
@ -1133,28 +1237,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
// Special handling for the overload chain // Special handling for the overload chain
if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited) if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited)
{ {
if (n->context)
{
throw Error(target_host, host, n, Fn, "\"%s\" can't be called in a different context", cname);
}
callee = Fn->OwnerOverloaded; callee = Fn->OwnerOverloaded;
if (!callee)
{
if (n->callee == C4AUL_SafeInherited)
{
// pop all args off the stack
if (!n->args.empty())
AddBCC(n->loc, AB_STACK, -(intptr_t)n->args.size());
// and "return" nil
AddBCC(n->loc, AB_NIL);
type_of_stack_top = C4V_Nil;
return;
}
else
{
throw Error(target_host, host, n, Fn, "inherited function not found (use " C4AUL_SafeInherited " to disable this message)");
}
}
} }
size_t fn_argc = C4AUL_MAX_Par; size_t fn_argc = C4AUL_MAX_Par;
@ -1167,9 +1250,24 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
callee = target_host->Engine->GetFunc(cname); callee = target_host->Engine->GetFunc(cname);
if (callee) if (callee)
{
fn_argc = callee->GetParCount(); fn_argc = callee->GetParCount();
}
else else
throw Error(target_host, host, n, Fn, "called function not found: %s", cname); {
// pop all args off the stack
if (!n->args.empty())
AddBCC(n->loc, AB_STACK, -(intptr_t)n->args.size());
// and "return" nil
AddBCC(n->loc, AB_NIL);
type_of_stack_top = C4V_Nil;
if (n->callee != C4AUL_SafeInherited)
{
HandleError(Error(target_host, host, n, Fn, "called function not found: %s", cname));
}
return;
}
} }
if (n->args.size() > fn_argc) if (n->args.size() > fn_argc)
@ -1266,7 +1364,9 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ParExpr *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ParExpr *n)
{ {
n->arg->accept(this); StackGuard g(this, 1);
SafeVisit(n->arg);
AddBCC(n->loc, AB_PAR); AddBCC(n->loc, AB_PAR);
type_of_stack_top = C4V_Any; type_of_stack_top = C4V_Any;
} }
@ -1275,17 +1375,20 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Block *n)
{ {
for (const auto &s : n->children) for (const auto &s : n->children)
{ {
const auto pre_statement_stack = stack_height; StackGuard g(this, 0);
s->accept(this); if (SafeVisit(s))
// If the statement has left a stack value, pop it off {
MaybePopValueOf(s); // If the statement has left a stack value, pop it off
assert(pre_statement_stack == stack_height); MaybePopValueOf(s);
}
} }
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Return *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Return *n)
{ {
n->value->accept(this); StackGuard g(this, 0);
SafeVisit(n->value);
AddBCC(n->loc, AB_RETURN); AddBCC(n->loc, AB_RETURN);
} }
@ -1356,28 +1459,29 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ForLoop *n)
if (n->init) if (n->init)
{ {
n->init->accept(this); if (SafeVisit(n->init))
MaybePopValueOf(n->init); MaybePopValueOf(n->init);
} }
PushLoop(); PushLoop();
int cond = AddJumpTarget(); int cond = AddJumpTarget();
if (n->cond) if (n->cond)
{ {
n->cond->accept(this); SafeVisit(n->cond);
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN)); active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
} }
int incr = cond; int incr = cond;
if (n->incr) if (n->incr)
{ {
int cond_jump = AddBCC(n->loc, AB_JUMP); int cond_jump = AddBCC(n->loc, AB_JUMP);
incr = AddJumpTarget(); incr = AddJumpTarget();
n->incr->accept(this); if (SafeVisit(n->incr))
MaybePopValueOf(n->incr); MaybePopValueOf(n->incr);
AddJumpTo(n->loc, AB_JUMP, cond); AddJumpTo(n->loc, AB_JUMP, cond);
UpdateJump(cond_jump, AddJumpTarget()); UpdateJump(cond_jump, AddJumpTarget());
} }
n->body->accept(this); if (SafeVisit(n->body))
MaybePopValueOf(n->body); MaybePopValueOf(n->body);
AddJumpTo(n->loc, AB_JUMP, incr); AddJumpTo(n->loc, AB_JUMP, incr);
PopLoop(incr); PopLoop(incr);
#endif #endif
@ -1403,16 +1507,17 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::RangeLoop *n)
if (var_id == -1) if (var_id == -1)
throw Error(target_host, host, n, Fn, "internal error: unable to find variable in foreach: %s", cname); throw Error(target_host, host, n, Fn, "internal error: unable to find variable in foreach: %s", cname);
// Emit code for array // Emit code for array
n->cond->accept(this); SafeVisit(n->cond);
// Emit code for iteration // Emit code for iteration
AddBCC(n->loc, AB_INT, 0); AddBCC(n->loc, AB_INT, 0);
int cond = AddJumpTarget(); int cond = AddJumpTarget();
PushLoop(); PushLoop();
AddVarAccess(n->loc, AB_FOREACH_NEXT, var_id); AddVarAccess(n->loc, AB_FOREACH_NEXT, var_id);
AddLoopControl(n->loc, Loop::Control::Break); // Will be skipped by AB_FOREACH_NEXT as long as more entries exist AddLoopControl(n->loc, Loop::Control::Break); // Will be skipped by AB_FOREACH_NEXT as long as more entries exist
// Emit body
n->body->accept(this); // Emit body
MaybePopValueOf(n->body); if (SafeVisit(n->body))
MaybePopValueOf(n->body);
// continue starts the next iteration of the loop // continue starts the next iteration of the loop
AddLoopControl(n->loc, Loop::Control::Continue); AddLoopControl(n->loc, Loop::Control::Continue);
PopLoop(cond); PopLoop(cond);
@ -1460,41 +1565,25 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::DoLoop *n)
{ {
int body = AddJumpTarget(); int body = AddJumpTarget();
PushLoop(); PushLoop();
try if (SafeVisit(n->body))
{
n->body->accept(this);
MaybePopValueOf(n->body); MaybePopValueOf(n->body);
int cond = AddJumpTarget(); int cond = AddJumpTarget();
n->cond->accept(this); SafeVisit(n->cond);
AddJumpTo(n->loc, AB_COND, body); AddJumpTo(n->loc, AB_COND, body);
PopLoop(cond); PopLoop(cond);
}
catch (...)
{
PopLoop(AddJumpTarget());
throw;
}
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::WhileLoop *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::WhileLoop *n)
{ {
int cond = AddJumpTarget(); int cond = AddJumpTarget();
PushLoop(); PushLoop();
try SafeVisit(n->cond);
{ active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
n->cond->accept(this); if (SafeVisit(n->body))
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
n->body->accept(this);
MaybePopValueOf(n->body); MaybePopValueOf(n->body);
// continue starts the next iteration of the loop // continue starts the next iteration of the loop
AddLoopControl(n->loc, Loop::Control::Continue); AddLoopControl(n->loc, Loop::Control::Continue);
PopLoop(cond); PopLoop(cond);
}
catch (...)
{
PopLoop(AddJumpTarget());
throw;
}
} }
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Break *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Break *n)
@ -1511,17 +1600,17 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Continue *n)
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::If *n) void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::If *n)
{ {
n->cond->accept(this); SafeVisit(n->cond);
int jump = AddBCC(n->loc, AB_CONDN); int jump = AddBCC(n->loc, AB_CONDN);
n->iftrue->accept(this); if (SafeVisit(n->iftrue))
MaybePopValueOf(n->iftrue); MaybePopValueOf(n->iftrue);
if (n->iffalse) if (n->iffalse)
{ {
int jumpout = AddBCC(n->loc, AB_JUMP); int jumpout = AddBCC(n->loc, AB_JUMP);
UpdateJump(jump, AddJumpTarget()); UpdateJump(jump, AddJumpTarget());
jump = jumpout; jump = jumpout;
n->iffalse->accept(this); if (SafeVisit(n->iffalse))
MaybePopValueOf(n->iffalse); MaybePopValueOf(n->iffalse);
} }
UpdateJump(jump, AddJumpTarget()); UpdateJump(jump, AddJumpTarget());
} }
@ -1537,7 +1626,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarDecl *n)
if (dec.init) if (dec.init)
{ {
// Emit code for the initializer // Emit code for the initializer
dec.init->accept(this); SafeVisit(dec.init);
int var_idx = Fn->VarNamed.GetItemNr(cname); int var_idx = Fn->VarNamed.GetItemNr(cname);
assert(var_idx >= 0 && "CodegenAstVisitor: var not found in variable table"); assert(var_idx >= 0 && "CodegenAstVisitor: var not found in variable table");
if (var_idx < 0) if (var_idx < 0)
@ -1598,7 +1687,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
try try
{ {
EmitFunctionCode(n); EmitFunctionCode(n);
Fn = nullptr; Fn = nullptr;
} }
catch (...) catch (...)
@ -1610,6 +1699,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::FunctionExpr * n) void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::FunctionExpr * n)
{ {
AddBCC(n->loc, AB_NIL);
throw Error(target_host, host, n, Fn, "can't define a function in a function-scoped proplist"); throw Error(target_host, host, n, Fn, "can't define a function in a function-scoped proplist");
} }
@ -1617,16 +1707,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::Script * n)
{ {
for (const auto &d : n->declarations) for (const auto &d : n->declarations)
{ {
try SafeVisit(d);
{
d->accept(this);
}
catch (C4AulParseError &e)
{
// Inform handler
host->Engine->ErrorHandler->OnError(e.what());
// try next declaration
}
} }
} }

View File

@ -136,6 +136,50 @@ for (var i in a) {
} }
return b; return b;
)")); )"));
// Test syntax errors inside loops
{
// Syntax error in for loop initializer
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("for (var i = missing();;) break;"), C4AulExecError);
}
{
// Syntax error in for loop condition
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("for (; missing();) break;"), C4AulExecError);
}
{
// Syntax error in for loop incrementor
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("for (;; missing()) continue;"), C4AulExecError);
}
{
// Syntax error in for loop body
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("for (;;) missing();"), C4AulExecError);
}
{
// Syntax error in while loop condition
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("while (missing()) break;"), C4AulExecError);
}
{
// Syntax error in while loop body
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("while (1) missing();"), C4AulExecError);
}
{
// Syntax error in for-in loop body
ErrorHandler errh;
EXPECT_CALL(errh, OnError(::testing::_));
EXPECT_THROW(RunCode("for (var i in [1]) { missing(); }"), C4AulExecError);
}
} }
TEST_F(AulTest, Locals) TEST_F(AulTest, Locals)