feat: implement simple infinite loop checker
This commit is contained in:
@@ -6,42 +6,214 @@
|
|||||||
#include <compiler/utils/fzl_symtable.h>
|
#include <compiler/utils/fzl_symtable.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
ssize_t check_top;
|
||||||
|
fzl_nodetype_t check_tree[256];
|
||||||
|
|
||||||
|
void ghost_walk_changed(fzl_node_t *node, bool *changed, fzl_sym_t *sym)
|
||||||
|
{
|
||||||
|
if (!node || !changed || *changed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
check_tree[check_top++] = node->type;
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case FZL_NODETYPE_DECLARATION:
|
||||||
|
case FZL_NODETYPE_CONSTRUCT:
|
||||||
|
case FZL_NODETYPE_CLASS:
|
||||||
|
case FZL_NODETYPE_FUNCTION:
|
||||||
|
case FZL_NODETYPE_LITERAL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_UNARY_PLUS:
|
||||||
|
case FZL_NODETYPE_UNARY_MINUS:
|
||||||
|
case FZL_NODETYPE_NOT:
|
||||||
|
ghost_walk_changed(node->unary_op.operand, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_ADD:
|
||||||
|
case FZL_NODETYPE_SUB:
|
||||||
|
case FZL_NODETYPE_MUL:
|
||||||
|
case FZL_NODETYPE_DIV:
|
||||||
|
case FZL_NODETYPE_POW:
|
||||||
|
case FZL_NODETYPE_MOD:
|
||||||
|
case FZL_NODETYPE_OR:
|
||||||
|
case FZL_NODETYPE_AND:
|
||||||
|
case FZL_NODETYPE_EQUAL:
|
||||||
|
case FZL_NODETYPE_NEQUAL:
|
||||||
|
case FZL_NODETYPE_LESS:
|
||||||
|
case FZL_NODETYPE_LESS_EQUAL:
|
||||||
|
case FZL_NODETYPE_GREATER:
|
||||||
|
case FZL_NODETYPE_GREATER_EQUAL:
|
||||||
|
case FZL_NODETYPE_ASSIGN:
|
||||||
|
ghost_walk_changed(node->binary_op.left, changed, sym);
|
||||||
|
ghost_walk_changed(node->binary_op.right, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_FIELD:
|
||||||
|
ghost_walk_changed(node->memb.parent, changed, sym);
|
||||||
|
ghost_walk_changed(node->memb.field, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_RETURN:
|
||||||
|
ghost_walk_changed(node->ret.value, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_CONDITIONAL:
|
||||||
|
ghost_walk_changed(node->cond.condition, changed, sym);
|
||||||
|
ghost_walk_changed(node->cond.true_block, changed, sym);
|
||||||
|
ghost_walk_changed(node->cond.else_block, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_LOOP:
|
||||||
|
ghost_walk_changed(node->loop.cond, changed, sym);
|
||||||
|
ghost_walk_changed(node->loop.step, changed, sym);
|
||||||
|
ghost_walk_changed(node->loop.body, changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_BLOCK:
|
||||||
|
for (size_t i = 0; i < node->list.entry_count; i++)
|
||||||
|
ghost_walk_changed(node->list.entries[i], changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_CALL:
|
||||||
|
ghost_walk_changed(node->call.callee, changed, sym);
|
||||||
|
for (size_t i = 0; i < node->call.args_count; i++)
|
||||||
|
ghost_walk_changed(node->call.args[i], changed, sym);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_IDENTIFIER:
|
||||||
|
if (strcmp(node->token->value, sym->name) == 0)
|
||||||
|
{
|
||||||
|
*changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*changed)
|
||||||
|
{
|
||||||
|
*changed = false;
|
||||||
|
for (ssize_t i = check_top - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
fzl_nodetype_t cur_t = check_tree[i];
|
||||||
|
if (cur_t == FZL_NODETYPE_ADD || cur_t == FZL_NODETYPE_SUB ||
|
||||||
|
cur_t == FZL_NODETYPE_MUL || cur_t == FZL_NODETYPE_DIV ||
|
||||||
|
cur_t == FZL_NODETYPE_POW || cur_t == FZL_NODETYPE_MOD ||
|
||||||
|
cur_t == FZL_NODETYPE_UNARY_MINUS)
|
||||||
|
{
|
||||||
|
*changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_tree[--check_top] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether value changed in loop body
|
||||||
|
bool value_is_changed(fzl_node_t *node, fzl_sym_t *sym)
|
||||||
|
{
|
||||||
|
if (!node || !sym)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
check_top = 0;
|
||||||
|
memset(check_tree, 0, sizeof(check_tree));
|
||||||
|
ghost_walk_changed(node, &changed, sym);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find symbol in loop condition node
|
||||||
|
bool retrieve_cond_sym(fzl_walker_t *walker, fzl_node_t *node, fzl_sym_t *sym)
|
||||||
|
{
|
||||||
|
if (!walker || !node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case FZL_NODETYPE_BLOCK:
|
||||||
|
case FZL_NODETYPE_CALL:
|
||||||
|
case FZL_NODETYPE_LITERAL:
|
||||||
|
case FZL_NODETYPE_CONDITIONAL:
|
||||||
|
case FZL_NODETYPE_FUNCTION:
|
||||||
|
case FZL_NODETYPE_RETURN:
|
||||||
|
case FZL_NODETYPE_CLASS:
|
||||||
|
case FZL_NODETYPE_FIELD:
|
||||||
|
case FZL_NODETYPE_CONSTRUCT:
|
||||||
|
case FZL_NODETYPE_LOOP:
|
||||||
|
case FZL_NODETYPE_ASSIGN:
|
||||||
|
case FZL_NODETYPE_DECLARATION:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_UNARY_MINUS:
|
||||||
|
case FZL_NODETYPE_UNARY_PLUS:
|
||||||
|
case FZL_NODETYPE_NOT:
|
||||||
|
return retrieve_cond_sym(walker, node->unary_op.operand, sym);
|
||||||
|
|
||||||
|
case FZL_NODETYPE_ADD:
|
||||||
|
case FZL_NODETYPE_SUB:
|
||||||
|
case FZL_NODETYPE_MUL:
|
||||||
|
case FZL_NODETYPE_DIV:
|
||||||
|
case FZL_NODETYPE_POW:
|
||||||
|
case FZL_NODETYPE_MOD:
|
||||||
|
case FZL_NODETYPE_AND:
|
||||||
|
case FZL_NODETYPE_OR:
|
||||||
|
case FZL_NODETYPE_LESS:
|
||||||
|
case FZL_NODETYPE_LESS_EQUAL:
|
||||||
|
case FZL_NODETYPE_GREATER:
|
||||||
|
case FZL_NODETYPE_GREATER_EQUAL:
|
||||||
|
case FZL_NODETYPE_EQUAL:
|
||||||
|
case FZL_NODETYPE_NEQUAL:
|
||||||
|
if (retrieve_cond_sym(walker, node->binary_op.left, sym))
|
||||||
|
return true;
|
||||||
|
if (retrieve_cond_sym(walker, node->binary_op.right, sym))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case FZL_NODETYPE_IDENTIFIER:
|
||||||
|
{
|
||||||
|
bool exists;
|
||||||
|
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if ((exists = fzl_symt_get(walker->syms[i], (char *)node->token->value,
|
||||||
|
sym)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for infinite loop
|
// Check for infinite loop
|
||||||
bool is_infinite_loop(fzl_walker_t *walker, size_t start_ip, size_t end_ip)
|
bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t start_ip,
|
||||||
|
size_t end_ip)
|
||||||
{
|
{
|
||||||
if (!walker)
|
if (!walker)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
end_ip = (end_ip >= walker->op_count) ? (walker->op_count - 1) : end_ip;
|
end_ip = (end_ip >= walker->op_count) ? (walker->op_count - 1) : end_ip;
|
||||||
|
|
||||||
// Can we determine an init block?
|
|
||||||
size_t maybe_init_start = start_ip + 1;
|
|
||||||
size_t maybe_init_end = start_ip + 6;
|
|
||||||
fzl_const_t *declared = NULL;
|
|
||||||
for (size_t i = maybe_init_start; i <= maybe_init_end; i++)
|
|
||||||
{
|
|
||||||
if (walker->ops[i] != FZL_OPCODE_STORE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
declared = &walker->consts->array[walker->ops[i - 1]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (declared)
|
|
||||||
{
|
|
||||||
fzl_sym_t sym;
|
fzl_sym_t sym;
|
||||||
bool found = false;
|
bool sym_exists = retrieve_cond_sym(walker, node->loop.cond, &sym);
|
||||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
if (sym_exists)
|
||||||
if ((found = fzl_symt_get(walker->syms[i],
|
{
|
||||||
(char *)declared->as.string.chars, &sym)))
|
// fzl_sym_t sym;
|
||||||
break;
|
// bool found = false;
|
||||||
|
// for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||||
if (!found)
|
// if ((found = fzl_symt_get(walker->syms[i],
|
||||||
return false; // suspicious
|
// (char *)declared->as.string.chars, &sym)))
|
||||||
|
// break;
|
||||||
|
|
||||||
if (sym.type >= FZL_TYPE_INT8)
|
if (sym.type >= FZL_TYPE_INT8)
|
||||||
{
|
{
|
||||||
|
bool value_changed = value_is_changed(node, &sym);
|
||||||
|
if (value_changed)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
extern bool is_infinite_loop(fzl_walker_t *walker, size_t ls, size_t le);
|
extern bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t ls,
|
||||||
|
size_t le);
|
||||||
|
|
||||||
char *null_name = "<null>";
|
char *null_name = "<null>";
|
||||||
char wlast_err[256];
|
char wlast_err[256];
|
||||||
@@ -221,6 +222,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
|
|
||||||
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
||||||
walker->syms[walker->sym_top++] = fzl_mksymt();
|
walker->syms[walker->sym_top++] = fzl_mksymt();
|
||||||
|
size_t loop_begin_ip = walker->op_count;
|
||||||
|
|
||||||
if (node->loop.init)
|
if (node->loop.init)
|
||||||
{
|
{
|
||||||
@@ -274,9 +276,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
|||||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||||
|
|
||||||
bool is_infinite =
|
bool is_infinite =
|
||||||
is_infinite_loop(walker, start_ip, walker->op_count - 2);
|
is_infinite_loop(walker, node, loop_begin_ip, walker->op_count - 2);
|
||||||
if (is_infinite)
|
if (is_infinite)
|
||||||
{
|
{
|
||||||
|
free(newctx->breaks);
|
||||||
snprintf(wlast_err, sizeof(wlast_err),
|
snprintf(wlast_err, sizeof(wlast_err),
|
||||||
"'%s': infinite loop detected at %ld:%ld",
|
"'%s': infinite loop detected at %ld:%ld",
|
||||||
node->token->filename, node->token->ln, node->token->cn);
|
node->token->filename, node->token->ln, node->token->cn);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
for (int i = 0; i < 5; i = i - 1)
|
for (int i = 0; i < 5; i = i + 0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user