Compare commits
2
Commits
586094ae8f
...
4b4238dc50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b4238dc50 | ||
|
|
175e666954 |
@@ -979,12 +979,16 @@ int main (int argc, char* argv[])
|
|||||||
|
|
||||||
{
|
{
|
||||||
hak_oow_t tab_size;
|
hak_oow_t tab_size;
|
||||||
tab_size = 5000;
|
tab_size = HAK_DFL_SYMTAB_SIZE;
|
||||||
hak_setoption (hak, HAK_SYMTAB_SIZE, &tab_size);
|
hak_setoption (hak, HAK_SYMTAB_SIZE, &tab_size);
|
||||||
tab_size = 5000;
|
tab_size = HAK_DFL_SYSDIC_SIZE;
|
||||||
hak_setoption (hak, HAK_SYSDIC_SIZE, &tab_size);
|
hak_setoption (hak, HAK_SYSDIC_SIZE, &tab_size);
|
||||||
tab_size = 600; /* TODO: choose a better stack size or make this user specifiable */
|
tab_size = HAK_DFL_PROCSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
|
||||||
hak_setoption (hak, HAK_PROCSTK_SIZE, &tab_size);
|
hak_setoption (hak, HAK_PROCSTK_SIZE, &tab_size);
|
||||||
|
tab_size = HAK_DFL_EXSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
|
||||||
|
hak_setoption (hak, HAK_EXSTK_SIZE, &tab_size);
|
||||||
|
tab_size = HAK_DFL_CLSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
|
||||||
|
hak_setoption (hak, HAK_CLSTK_SIZE, &tab_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
+69
-60
@@ -1134,6 +1134,7 @@ static int push_ctlblk (hak_t* hak, const hak_loc_t* errloc, hak_ctlblk_type_t t
|
|||||||
|
|
||||||
HAK_MEMSET(&hak->c->ctlblk.info[new_depth], 0, HAK_SIZEOF(hak->c->ctlblk.info[new_depth]));
|
HAK_MEMSET(&hak->c->ctlblk.info[new_depth], 0, HAK_SIZEOF(hak->c->ctlblk.info[new_depth]));
|
||||||
hak->c->ctlblk.info[new_depth]._type = type;
|
hak->c->ctlblk.info[new_depth]._type = type;
|
||||||
|
/*hak->c->ctlblk.info[new_depth].in_catch = 0; not needed for memset above */
|
||||||
hak->c->ctlblk.depth = new_depth;
|
hak->c->ctlblk.depth = new_depth;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -2090,12 +2091,60 @@ static HAK_INLINE int emit_plus (hak_t* hak)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ========================================================================= */
|
/* ========================================================================= */
|
||||||
|
static int emit_ctlblk_unwind (hak_t* hak, hak_cnode_t* src, int stop_at_loop)
|
||||||
|
{
|
||||||
|
hak_ooi_t i;
|
||||||
|
|
||||||
|
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
|
||||||
|
{
|
||||||
|
switch (hak->c->ctlblk.info[i]._type)
|
||||||
|
{
|
||||||
|
case HAK_CTLBLK_TYPE_LOOP:
|
||||||
|
/* a loop block needs no unwinding instruction */
|
||||||
|
if (stop_at_loop) return 1; /* for break/continue inside loop */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HAK_CTLBLK_TYPE_TRY:
|
||||||
|
/* emit an instruction to exit from the try loop. when it is in the catch side. don't emit anything */
|
||||||
|
if (!hak->c->ctlblk.info[i].in_catch &&
|
||||||
|
emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HAK_CTLBLK_TYPE_CLASS:
|
||||||
|
/* emit an instruction to exit from the class definition scope being defined */
|
||||||
|
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_try_ctlblk_for_catch (hak_t* hak)
|
||||||
|
{
|
||||||
|
hak_ooi_t i;
|
||||||
|
|
||||||
|
/* find the innermost try block info and mark that it's now in the catch block side */
|
||||||
|
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
|
||||||
|
{
|
||||||
|
if (hak->c->ctlblk.info[i]._type == HAK_CTLBLK_TYPE_TRY)
|
||||||
|
{
|
||||||
|
hak->c->ctlblk.info[i].in_catch = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this must not happend. compile_catch() must not be invoked without
|
||||||
|
* going through the enclosing TRY block first. */
|
||||||
|
HAK_ASSERT(hak, !"internal error - this must never happen");
|
||||||
|
}
|
||||||
|
|
||||||
static int compile_break (hak_t* hak, hak_cnode_t* src)
|
static int compile_break (hak_t* hak, hak_cnode_t* src)
|
||||||
{
|
{
|
||||||
/* (break) */
|
/* (break) */
|
||||||
hak_cnode_t* cmd, * obj;
|
hak_cnode_t* cmd, * obj;
|
||||||
hak_ooi_t i;
|
hak_ooi_t i;
|
||||||
|
int n;
|
||||||
|
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_BREAK));
|
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_BREAK));
|
||||||
@@ -2123,30 +2172,15 @@ static int compile_break (hak_t* hak, hak_cnode_t* src)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
|
n = emit_ctlblk_unwind(hak, cmd, 1);
|
||||||
|
if (n <= -1) return -1;
|
||||||
|
if (n == 0)
|
||||||
{
|
{
|
||||||
switch (hak->c->ctlblk.info[i]._type)
|
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(cmd),
|
||||||
{
|
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
|
||||||
case HAK_CTLBLK_TYPE_LOOP:
|
return -1;
|
||||||
goto inside_loop;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_TRY:
|
|
||||||
/* emit an instruction to exit from the try loop. */
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_CLASS:
|
|
||||||
/* emit an instruction to exit from the class definition scope being defined */
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(src),
|
|
||||||
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
inside_loop:
|
|
||||||
for (i = hak->c->cfs.top; i >= 0; --i)
|
for (i = hak->c->cfs.top; i >= 0; --i)
|
||||||
{
|
{
|
||||||
const hak_cframe_t* tcf;
|
const hak_cframe_t* tcf;
|
||||||
@@ -2217,6 +2251,7 @@ static int compile_continue (hak_t* hak, hak_cnode_t* src)
|
|||||||
/* (continue) */
|
/* (continue) */
|
||||||
hak_cnode_t* cmd, * obj;
|
hak_cnode_t* cmd, * obj;
|
||||||
hak_ooi_t i;
|
hak_ooi_t i;
|
||||||
|
int n;
|
||||||
|
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_CONTINUE));
|
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_CONTINUE));
|
||||||
@@ -2244,29 +2279,15 @@ static int compile_continue (hak_t* hak, hak_cnode_t* src)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
|
n = emit_ctlblk_unwind(hak, cmd, 1);
|
||||||
|
if (n <= -1) return -1;
|
||||||
|
if (n == 0)
|
||||||
{
|
{
|
||||||
switch (hak->c->ctlblk.info[i]._type)
|
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(cmd),
|
||||||
{
|
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
|
||||||
case HAK_CTLBLK_TYPE_LOOP:
|
return -1;
|
||||||
goto inside_loop;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_TRY:
|
|
||||||
/*must emit an instruction to exit from the try loop.*/
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_CLASS:
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(src),
|
|
||||||
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
inside_loop:
|
|
||||||
for (i = hak->c->cfs.top; i >= 0; --i)
|
for (i = hak->c->cfs.top; i >= 0; --i)
|
||||||
{
|
{
|
||||||
const hak_cframe_t* tcf;
|
const hak_cframe_t* tcf;
|
||||||
@@ -3968,7 +3989,6 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
|
|||||||
hak_cnode_t* obj, * val;
|
hak_cnode_t* obj, * val;
|
||||||
hak_cframe_t* cf;
|
hak_cframe_t* cf;
|
||||||
hak_funblk_info_t* fbi;
|
hak_funblk_info_t* fbi;
|
||||||
hak_ooi_t i;
|
|
||||||
|
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
|
||||||
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_RETURN) ||
|
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_RETURN) ||
|
||||||
@@ -3977,24 +3997,6 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
|
|||||||
fbi = &hak->c->funblk.info[hak->c->funblk.depth];
|
fbi = &hak->c->funblk.info[hak->c->funblk.depth];
|
||||||
obj = HAK_CNODE_CONS_CDR(src);
|
obj = HAK_CNODE_CONS_CDR(src);
|
||||||
|
|
||||||
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
|
|
||||||
{
|
|
||||||
switch (hak->c->ctlblk.info[i]._type)
|
|
||||||
{
|
|
||||||
case HAK_CTLBLK_TYPE_LOOP:
|
|
||||||
/* do nothing */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_TRY:
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HAK_CTLBLK_TYPE_CLASS:
|
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fbi->tmpr_nrvars > 0)
|
if (fbi->tmpr_nrvars > 0)
|
||||||
{
|
{
|
||||||
hak_cnode_t* tmp = HAK_CNODE_CONS_CAR(src);
|
hak_cnode_t* tmp = HAK_CNODE_CONS_CAR(src);
|
||||||
@@ -4016,6 +4018,9 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* emit unwinding expression just before emitting the actual return instruction */
|
||||||
|
if (emit_ctlblk_unwind(hak, src, 0) <= -1) return -1;
|
||||||
|
|
||||||
/* TODO: pop stack if this is not the first statement... */
|
/* TODO: pop stack if this is not the first statement... */
|
||||||
if (emit_byte_instruction(hak, HAK_CODE_PUSH_RETURN_R, HAK_CNODE_GET_LOC(tmp)) <= -1) return -1;
|
if (emit_byte_instruction(hak, HAK_CODE_PUSH_RETURN_R, HAK_CNODE_GET_LOC(tmp)) <= -1) return -1;
|
||||||
POP_CFRAME(hak);
|
POP_CFRAME(hak);
|
||||||
@@ -4502,6 +4507,7 @@ static HAK_INLINE int compile_catch (hak_t* hak)
|
|||||||
/* produce an instruction to store the exception value to an exception variable pushed by the 'throw' instruction */
|
/* produce an instruction to store the exception value to an exception variable pushed by the 'throw' instruction */
|
||||||
if (emit_variable_access(hak, VAR_ACCESS_POP, &vi, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
if (emit_variable_access(hak, VAR_ACCESS_POP, &vi, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
|
||||||
|
|
||||||
|
update_try_ctlblk_for_catch(hak);
|
||||||
SWITCH_TOP_CFRAME(hak, COP_COMPILE_OBJECT_LIST, obj);
|
SWITCH_TOP_CFRAME(hak, COP_COMPILE_OBJECT_LIST, obj);
|
||||||
|
|
||||||
PUSH_SUBCFRAME(hak, COP_POST_CATCH, cmd);
|
PUSH_SUBCFRAME(hak, COP_POST_CATCH, cmd);
|
||||||
@@ -7001,6 +7007,9 @@ static HAK_INLINE int emit_return (hak_t* hak)
|
|||||||
HAK_ASSERT(hak, cf->opcode == COP_EMIT_RETURN);
|
HAK_ASSERT(hak, cf->opcode == COP_EMIT_RETURN);
|
||||||
HAK_ASSERT(hak, cf->operand != HAK_NULL);
|
HAK_ASSERT(hak, cf->operand != HAK_NULL);
|
||||||
|
|
||||||
|
/* emit unwinding expression just before emitting the actual return instruction */
|
||||||
|
if (emit_ctlblk_unwind(hak, cf->operand, 0) <= -1) return -1;
|
||||||
|
|
||||||
n = emit_byte_instruction(hak, (cf->u._return.from_home? HAK_CODE_RETURN_STACKTOP: HAK_CODE_RETURN_FROM_BLOCK), HAK_CNODE_GET_LOC(cf->operand));
|
n = emit_byte_instruction(hak, (cf->u._return.from_home? HAK_CODE_RETURN_STACKTOP: HAK_CODE_RETURN_FROM_BLOCK), HAK_CNODE_GET_LOC(cf->operand));
|
||||||
|
|
||||||
POP_CFRAME(hak);
|
POP_CFRAME(hak);
|
||||||
|
|||||||
+13
-1
@@ -180,6 +180,8 @@ static void terminate_all_processes (hak_t* hak);
|
|||||||
ap->exsp = HAK_SMOOI_TO_OOP(exsp); \
|
ap->exsp = HAK_SMOOI_TO_OOP(exsp); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/* normal stack top is the base of exstack */
|
||||||
|
#define HAK_EXSTACK_GET_BASE(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->st)
|
||||||
#define HAK_EXSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exst)
|
#define HAK_EXSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exst)
|
||||||
#define HAK_EXSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exsp)
|
#define HAK_EXSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exsp)
|
||||||
|
|
||||||
@@ -235,6 +237,8 @@ static void terminate_all_processes (hak_t* hak);
|
|||||||
|
|
||||||
#define HAK_CLSTACK_CHOP(hak, clsp_) ((hak)->processor->active->clsp = HAK_SMOOI_TO_OOP(clsp_))
|
#define HAK_CLSTACK_CHOP(hak, clsp_) ((hak)->processor->active->clsp = HAK_SMOOI_TO_OOP(clsp_))
|
||||||
|
|
||||||
|
/* exstack top is the base of clstack */
|
||||||
|
#define HAK_CLSTACK_GET_BASE(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exst)
|
||||||
#define HAK_CLSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clst)
|
#define HAK_CLSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clst)
|
||||||
#define HAK_CLSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clsp)
|
#define HAK_CLSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clsp)
|
||||||
|
|
||||||
@@ -4364,7 +4368,15 @@ static int execute (hak_t* hak)
|
|||||||
|
|
||||||
case HAK_CODE_TRY_EXIT:
|
case HAK_CODE_TRY_EXIT:
|
||||||
LOG_INST_0(hak, "try_exit");
|
LOG_INST_0(hak, "try_exit");
|
||||||
/* TODO: stack underflow check? */
|
/* writing the condition this way would be more explicit.
|
||||||
|
* if (HAK_EXSTACK_GET_SP(hak) - HAK_EXSTACK_GET_BASE(hak) < 4)
|
||||||
|
* it's simpler to use HAK_EXSTACK_IS_EMPTY() base PUSH, POP, POP_TO all move
|
||||||
|
* exsp by exactly 4. */
|
||||||
|
if (HAK_EXSTACK_IS_EMPTY(hak))
|
||||||
|
{
|
||||||
|
hak_seterrbfmt(hak, HAK_ESTKUNDFLW, "exception stack underflow");
|
||||||
|
goto oops_with_errmsg_supplement;
|
||||||
|
}
|
||||||
HAK_EXSTACK_POP(hak);
|
HAK_EXSTACK_POP(hak);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -855,6 +855,7 @@ typedef enum hak_ctlblk_type_t hak_ctlblk_type_t;
|
|||||||
struct hak_ctlblk_info_t
|
struct hak_ctlblk_info_t
|
||||||
{
|
{
|
||||||
hak_ctlblk_type_t _type;
|
hak_ctlblk_type_t _type;
|
||||||
|
int in_catch; /* used for HAK_CTLBLK_TYPE_TRY only */
|
||||||
};
|
};
|
||||||
typedef struct hak_ctlblk_info_t hak_ctlblk_info_t;
|
typedef struct hak_ctlblk_info_t hak_ctlblk_info_t;
|
||||||
|
|
||||||
|
|||||||
@@ -279,11 +279,20 @@ typedef enum hak_option_t hak_option_t;
|
|||||||
enum hak_option_dflval_t
|
enum hak_option_dflval_t
|
||||||
{
|
{
|
||||||
HAK_DFL_LOG_MAXCAPA = HAK_LOG_CAPA_ALIGN * 16,
|
HAK_DFL_LOG_MAXCAPA = HAK_LOG_CAPA_ALIGN * 16,
|
||||||
HAK_DFL_SYMTAB_SIZE = 5000,
|
|
||||||
HAK_DFL_SYSDIC_SIZE = 5000,
|
#if defined(HAK_SMALL_MEMORY_FOOTPRINT)
|
||||||
HAK_DFL_PROCSTK_SIZE = 5000,
|
HAK_DFL_SYMTAB_SIZE = 1024,
|
||||||
HAK_DFL_EXSTK_SIZE = 128,
|
HAK_DFL_SYSDIC_SIZE = 1024
|
||||||
HAK_DFL_CLSTK_SIZE = 64
|
HAK_DFL_PROCSTK_SIZE = 1024, /* -> fstk 1024, the FSTK floor */
|
||||||
|
HAK_DFL_EXSTK_SIZE = 256, /* 4 slots/level x 59 levels = 236 */
|
||||||
|
HAK_DFL_CLSTK_SIZE = 16, /* = HAK_MIN_CLSTK_SIZE */
|
||||||
|
#else
|
||||||
|
HAK_DFL_SYMTAB_SIZE = 8192,
|
||||||
|
HAK_DFL_SYSDIC_SIZE = 8192,
|
||||||
|
HAK_DFL_PROCSTK_SIZE = 8192,
|
||||||
|
HAK_DFL_EXSTK_SIZE = 2048,
|
||||||
|
HAK_DFL_CLSTK_SIZE = 64
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
typedef enum hak_option_dflval_t hak_option_dflval_t;
|
typedef enum hak_option_dflval_t hak_option_dflval_t;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ check_SCRIPTS = \
|
|||||||
sysproc-01.hak \
|
sysproc-01.hak \
|
||||||
sysproc-02.hak \
|
sysproc-02.hak \
|
||||||
tick-01.hak \
|
tick-01.hak \
|
||||||
|
try-01.hak \
|
||||||
va-01.hak \
|
va-01.hak \
|
||||||
var-01.hak \
|
var-01.hak \
|
||||||
var-02.hak \
|
var-02.hak \
|
||||||
|
|||||||
@@ -577,6 +577,7 @@ check_SCRIPTS = \
|
|||||||
sysproc-01.hak \
|
sysproc-01.hak \
|
||||||
sysproc-02.hak \
|
sysproc-02.hak \
|
||||||
tick-01.hak \
|
tick-01.hak \
|
||||||
|
try-01.hak \
|
||||||
va-01.hak \
|
va-01.hak \
|
||||||
var-01.hak \
|
var-01.hak \
|
||||||
var-02.hak \
|
var-02.hak \
|
||||||
|
|||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
## Two related defects in how 'return' interacts with try/catch. Both are
|
||||||
|
## fixed; this checks both, because each hid the other while writing the test.
|
||||||
|
##
|
||||||
|
## 1. compile_return() emitted TRY_EXIT/CLASS_EXIT before the return value was
|
||||||
|
## compiled, so the value expression ran with every enclosing handler in the
|
||||||
|
## function already popped - not just the innermost one. The unwinding now
|
||||||
|
## comes from emit_return(), after the value is on the operand stack.
|
||||||
|
## Only the syntactic form used to matter: try { x := (f) } caught what f
|
||||||
|
## threw while try { return (f) } did not, so both forms appear below.
|
||||||
|
##
|
||||||
|
## 2. A 'return', 'break' or 'continue' inside a catch body emitted a TRY_EXIT
|
||||||
|
## for the very try whose handler it sat in - but 'throw' has already
|
||||||
|
## unwound that frame, so the second pop drove the exception stack below its
|
||||||
|
## base. HAK_EXSTACK_IS_EMPTY tests 'exsp <= st', so an underflowed stack
|
||||||
|
## reads as empty from then on and EVERY later try in the process reported
|
||||||
|
## 'exception not handled'. The catch body is now compiled with its try
|
||||||
|
## block marked in_catch, which suppresses the instruction.
|
||||||
|
##
|
||||||
|
## That corruption was never visible in the function that caused it, so the
|
||||||
|
## assertions that actually guard it are the ones exercising a try AFTER a
|
||||||
|
## catch that returned, broke or continued.
|
||||||
|
|
||||||
|
fun chk(ok msg) {
|
||||||
|
if ok { printf "OK: %s\n" msg } \
|
||||||
|
else { printf "ERROR: %s\n" msg }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun boom() { throw 99 }
|
||||||
|
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
## 1. a throw inside a return value
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
|
||||||
|
fun c1() {
|
||||||
|
try { return (boom) } catch (e) { return (+ e 1) }
|
||||||
|
}
|
||||||
|
chk (= (c1) 100) "a throw in a return value is caught by the enclosing try"
|
||||||
|
|
||||||
|
## the catch need not return for the handler to run
|
||||||
|
seen := 0
|
||||||
|
fun c2() {
|
||||||
|
try { return (boom) } catch (e) { seen := e }
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
chk (= (c2) 7) "execution resumes after the try when the catch falls through"
|
||||||
|
chk (= seen 99) "the catch body ran"
|
||||||
|
|
||||||
|
fun c3() {
|
||||||
|
try { return (boom) } catch (e) { }
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
chk (= (c3) 8) "an empty catch body still handles the throw"
|
||||||
|
|
||||||
|
## nesting: the innermost handler wins and the outer one stays out of it.
|
||||||
|
## the old code unwound every enclosing try, so neither fired.
|
||||||
|
inner := 0
|
||||||
|
outer := 0
|
||||||
|
fun c4() {
|
||||||
|
try {
|
||||||
|
try { return (boom) } catch (e) { inner := (+ inner 1) }
|
||||||
|
} catch (e2) { outer := (+ outer 1) }
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
chk (= (c4) 9) "a nested try returns normally after handling"
|
||||||
|
chk (= inner 1) "the innermost try catches"
|
||||||
|
chk (= outer 0) "the outer try is left out of it"
|
||||||
|
|
||||||
|
## across a call boundary the callee's own handler wins; the old code let the
|
||||||
|
## throw escape to the caller's handler instead
|
||||||
|
g_seen := 0
|
||||||
|
h_seen := 0
|
||||||
|
fun g() { try { return (boom) } catch (e) { g_seen := 1 ; return -1 } }
|
||||||
|
fun h() { try { return (g) } catch (e) { h_seen := 1 ; return -2 } }
|
||||||
|
chk (= (h) -1) "the callee handles its own throw"
|
||||||
|
chk (= g_seen 1) "the callee's catch ran"
|
||||||
|
chk (= h_seen 0) "the caller's catch did not"
|
||||||
|
|
||||||
|
## the form that always worked must keep working
|
||||||
|
fun c5() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := e }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (c5) 99) "a throw in an assignment is still caught"
|
||||||
|
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
## 2. leaving a catch body by return, break or continue
|
||||||
|
##
|
||||||
|
## each case is followed by a fresh try: that is where an underflowed
|
||||||
|
## exception stack shows up, never in the function that caused it.
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
|
||||||
|
## a try that must still work after the cases above, which all returned from
|
||||||
|
## inside a catch
|
||||||
|
fun still1() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := 21 }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (still1) 21) "a try still works after earlier catch bodies returned"
|
||||||
|
|
||||||
|
## break out of a loop from inside a catch
|
||||||
|
bj := 0
|
||||||
|
while (< bj 9) {
|
||||||
|
bj := (+ bj 1)
|
||||||
|
try { y := (boom) } catch (e) { break }
|
||||||
|
}
|
||||||
|
chk (= bj 1) "break inside a catch leaves the loop"
|
||||||
|
|
||||||
|
fun still2() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := 22 }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (still2) 22) "a try still works after a break from inside a catch"
|
||||||
|
|
||||||
|
## continue to the next iteration from inside a catch
|
||||||
|
ci := 0
|
||||||
|
ch := 0
|
||||||
|
while (< ci 3) {
|
||||||
|
ci := (+ ci 1)
|
||||||
|
try { z := (boom) } catch (e) { ch := (+ ch 1) ; continue }
|
||||||
|
ch := 100
|
||||||
|
}
|
||||||
|
chk (= ci 3) "continue inside a catch keeps iterating"
|
||||||
|
chk (= ch 3) "continue inside a catch skips the rest of the loop body"
|
||||||
|
|
||||||
|
fun still3() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := 23 }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (still3) 23) "a try still works after a continue from inside a catch"
|
||||||
|
|
||||||
|
## the in_catch mark must suppress only the block whose handler we are in. the
|
||||||
|
## return below sits in the inner catch, so the inner frame is already gone,
|
||||||
|
## but the outer try is still live and has to be unwound on the way out. a
|
||||||
|
## frame left behind there would misdirect the next throw instead.
|
||||||
|
fun nest2() {
|
||||||
|
try {
|
||||||
|
try { w := (boom) } catch (e) { return 11 }
|
||||||
|
} catch (e2) { }
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
chk (= (nest2) 11) "a return from an inner catch still unwinds the outer try"
|
||||||
|
|
||||||
|
fun still4() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := 24 }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (still4) 24) "a try still works after returning from a nested catch"
|
||||||
|
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
## 3. break and continue in a try BODY still unwind
|
||||||
|
##
|
||||||
|
## these share emit_ctlblk_unwind with return, and here the frame is live and
|
||||||
|
## must be popped. a leaked frame per iteration would exhaust the exception
|
||||||
|
## stack long before these loops finish.
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
n := 0
|
||||||
|
while (< i 2000) {
|
||||||
|
i := (+ i 1)
|
||||||
|
try { if (= (mod i 2) 0) { continue } ; n := (+ n 1) } catch (e) { n := -1 }
|
||||||
|
}
|
||||||
|
chk (= n 1000) "continue inside a try body unwinds without leaking a handler"
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
while (< i 2000) {
|
||||||
|
i := (+ i 1)
|
||||||
|
try { if (= i 7) { break } } catch (e) { }
|
||||||
|
}
|
||||||
|
chk (= i 7) "break inside a try body unwinds and leaves the loop"
|
||||||
|
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
## 4. a handler stays live across a whole call chain
|
||||||
|
## ------------------------------------------------------------------
|
||||||
|
|
||||||
|
hits := 0
|
||||||
|
caught := 0
|
||||||
|
fun rec(k) {
|
||||||
|
if (<= k 0) { throw 55 }
|
||||||
|
try { return (rec (- k 1)) } catch (e) { hits := (+ hits 1) ; caught := e ; return e }
|
||||||
|
}
|
||||||
|
chk (= (rec 20) 55) "recursion with a try per frame propagates the thrown value"
|
||||||
|
chk (= caught 55) "the thrown value arrives intact"
|
||||||
|
chk (= hits 1) "only the frame nearest the throw handles it"
|
||||||
|
|
||||||
|
fun still5() {
|
||||||
|
| v |
|
||||||
|
v := 0
|
||||||
|
try { v := (boom) } catch (e) { v := 25 }
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
chk (= (still5) 25) "a try still works after deep try-per-frame recursion"
|
||||||
Reference in New Issue
Block a user