simplified hawk::call().

fixed a bug in copying a value for a reference upon returning from a function in hawk_rtx_evalcall(). it was unable to handle the case where a reference variable is not updated in the called function
This commit is contained in:
hyung-hwan 2020-04-13 08:41:16 +00:00
parent f5da432bb4
commit a8afcca04e
4 changed files with 81 additions and 52 deletions

View File

@ -27,8 +27,13 @@
#include "mod-hawk.h"
#include "hawk-prv.h"
/*
* function abc(a, b, c) { return a + b + c; }
* BEGIN { print hawk::call("abc", 10, 20, 30); }
*/
struct pafs_t
{
hawk_fun_t* fun;
hawk_oow_t stack_base;
hawk_oow_t start_index;
hawk_oow_t end_index;
@ -37,7 +42,7 @@ struct pafs_t
static hawk_oow_t push_args_from_stack (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, void* data)
{
struct pafs_t* pasf = (struct pafs_t*)data;
hawk_oow_t org_stack_base, i;
hawk_oow_t org_stack_base, spec_len, i, j;
hawk_val_t* v;
if (HAWK_RTX_STACK_AVAIL(rtx) < pasf->end_index - pasf->start_index + 1)
@ -46,13 +51,21 @@ static hawk_oow_t push_args_from_stack (hawk_rtx_t* rtx, hawk_nde_fncall_t* call
return (hawk_oow_t)-1;
}
spec_len = pasf->fun->argspec? hawk_count_oocstr(pasf->fun->argspec): 0;
org_stack_base = rtx->stack_base;
for (i = pasf->start_index; i <= pasf->end_index; i++)
for (i = pasf->start_index, j = 0; i <= pasf->end_index; i++, j++)
{
rtx->stack_base = pasf->stack_base;
v = HAWK_RTX_STACK_ARG(rtx, i);
rtx->stack_base = org_stack_base;
if (j < spec_len && pasf->fun->argspec[j] == 'r' && HAWK_RTX_GETVALTYPE(rtx, v) != HAWK_VAL_REF)
{
hawk_rtx_seterrnum (rtx, &call->loc, HAWK_ENOTREF);
return (hawk_oow_t)-1;
}
HAWK_RTX_STACK_PUSH (rtx, v);
hawk_rtx_refupval (rtx, v);
}
@ -66,45 +79,36 @@ static int fnc_call (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
hawk_oow_t nargs;
hawk_nde_fncall_t call;
struct pafs_t pafs;
hawk_val_t* v, * a0;
int rx = -1;
hawk_val_t* v;
/* this function is similar to hawk_rtx_callfun() but it is more simplified */
a0 = hawk_rtx_getarg(rtx, 0);
fun = hawk_rtx_valtofun(rtx, hawk_rtx_getarg(rtx, 1));
if (!fun) goto done;
fun = hawk_rtx_valtofun(rtx, hawk_rtx_getarg(rtx, 0));
if (!fun) return -1; /* hard failure */
nargs = hawk_rtx_getnargs(rtx);
if (nargs - 2 > fun->nargs)
if (nargs - 1 > fun->nargs)
{
/*hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EARGTM);
return HAWK_NULL;*/
goto done;
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EARGTM);
return -1; /* hard failure */
}
HAWK_MEMSET (&call, 0, HAWK_SIZEOF(call));
call.type = HAWK_NDE_FNCALL_FUN;
call.u.fun.name = fun->name;
call.nargs = nargs - 2;
call.nargs = nargs - 1;
/* keep HAWK_NULL in call.args so that hawk_rtx_evalcall() knows it's a fake call structure */
call.arg_base = rtx->stack_base + 6; /* 6 = 4(stack frame prologue) + 2(the first two arguments to hawk::call()) */
call.arg_base = rtx->stack_base + 5; /* 5 = 4(stack frame prologue) + 1(the first argument to hawk::call()) */
pafs.fun = fun;
pafs.stack_base = rtx->stack_base;
pafs.start_index = 2;
pafs.start_index = 1;
pafs.end_index = nargs - 1;
v = hawk_rtx_evalcall(rtx, &call, fun, push_args_from_stack, (void*)&pafs, HAWK_NULL, HAWK_NULL);
if (HAWK_LIKELY(v))
{
hawk_rtx_refupval (rtx, v);
rx = hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)a0, v);
hawk_rtx_refdownval (rtx, v);
}
if (HAWK_UNLIKELY(!v)) return -1; /* hard failure */
done:
hawk_rtx_setretval (rtx, hawk_rtx_makeintval(rtx, rx));
hawk_rtx_setretval (rtx, v);
return 0;
}
@ -188,7 +192,7 @@ struct inttab_t
static fnctab_t fnctab[] =
{
/* keep this table sorted for binary search in query(). */
{ HAWK_T("call"), { { 2, A_MAX, HAWK_T("rvR") }, fnc_call, 0 } },
{ HAWK_T("call"), { { 1, A_MAX, HAWK_T("vR") }, fnc_call, 0 } },
{ HAWK_T("gc"), { { 0, 1, HAWK_NULL }, fnc_gc, 0 } },
{ HAWK_T("gc_get_threshold"), { { 1, 1, HAWK_NULL }, fnc_gc_get_threshold, 0 } },
{ HAWK_T("gc_set_threshold"), { { 2, 2, HAWK_NULL }, fnc_gc_set_threshold, 0 } }

View File

@ -1454,14 +1454,13 @@ static hawk_nde_t* parse_function (hawk_t* awk)
nargs = HAWK_ARR_SIZE(awk->parse.params);
if (nargs >= argspeccapa)
{
hawk_oow_t i, newcapa = HAWK_ALIGN_POW2(nargs + 1, 64);
hawk_oow_t i, newcapa = HAWK_ALIGN_POW2(nargs + 2, 64);
argspec = hawk_reallocmem(awk, argspec, newcapa * HAWK_SIZEOF(*argspec));
if (!argspec) goto oops;
for (i = argspeccapa; i < newcapa; i++) argspec[i] = HAWK_T(' ');
argspeccapa = newcapa;
}
argspec[nargs] = 'r';
argspec[nargs + 1] = '\0';
if (get_token(awk) <= -1) goto oops;
}
@ -1522,6 +1521,7 @@ static hawk_nde_t* parse_function (hawk_t* awk)
while (MATCH(awk,TOK_NEWLINE));
}
if (argspec) argspec[nargs + 1] = '\0';
if (get_token(awk) <= -1) goto oops;
}

View File

@ -6124,27 +6124,44 @@ hawk_val_t* hawk_rtx_evalcall (
{
hawk_val_t** ref;
hawk_val_ref_t refv;
hawk_val_t* av;
int r;
/* if an argument passed is a local variable or a parameter to the previous caller,
* the argument node information stored is relative to the previous stack frame.
* i revert rtx->stack_base to the previous stack frame base before calling
* get_reference() and restors it back to the current base. this tactic
* is very ugly because the assumptions for this is depecallnt on get_reference()
* implementation */
rtx->stack_base = prev_stack_base; /* UGLY */
r = get_reference(rtx, p, &ref);
rtx->stack_base = cur_stack_base; /* UGLY */
/* if argspec is 'r', get_reference() must succeed all the time.
* if argspec is 'R', it may fail. if it happens, don't copy the value */
if (HAWK_LIKELY(r >= 0))
av = HAWK_RTX_STACK_ARG(rtx, i);
if (HAWK_RTX_GETVALTYPE(rtx, av) == HAWK_VAL_REF)
{
HAWK_RTX_INIT_REF_VAL (&refv, p->type - HAWK_NDE_NAMED, ref, 9); /* initialize a fake reference variable. 9 chosen randomly */
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, &refv, HAWK_RTX_STACK_ARG(rtx, i)) <= -1))
/* the argument still has the reference type.
* this means, the argument has not been set.
*
* function f1(&a, &b) { b = 20 }
*
* since a is not set in f1, the value for a is still the pushed value which is a reference
*/
/* ---- DO NOTHING ---- */
}
else
{
/* if an argument passed is a local variable or a parameter to the previous caller,
* the argument node information stored is relative to the previous stack frame.
* i revert rtx->stack_base to the previous stack frame base before calling
* get_reference() and restors it back to the current base. this tactic
* is very ugly because the assumptions for this is depecallnt on get_reference()
* implementation */
rtx->stack_base = prev_stack_base; /* UGLY */
r = get_reference(rtx, p, &ref);
rtx->stack_base = cur_stack_base; /* UGLY */
/* if argspec is 'r', get_reference() must succeed all the time.
* if argspec is 'R', it may fail. if it happens, don't copy the value */
if (HAWK_LIKELY(r >= 0))
{
n = -1;
ADJERR_LOC (rtx, &call->loc);
HAWK_RTX_INIT_REF_VAL (&refv, p->type - HAWK_NDE_NAMED, ref, 9); /* initialize a fake reference variable. 9 chosen randomly */
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, &refv, av) <= -1))
{
n = -1;
ADJERR_LOC (rtx, &call->loc);
}
}
}
}
@ -6157,8 +6174,8 @@ hawk_val_t* hawk_rtx_evalcall (
{
/*
* function f1(a,&b) { b *= 20; }
* BEGIN { q = 4; hawk::call(r, "f1", 20, q); print q;
* }
* BEGIN { q = 4; hawk::call(r, "f1", 20, q); print q; }
*
* the fourth argument to hawk::call() must map to the second argument to f1().
* hawk::call() accepts the third to the last arguments as reference if possible.
* this function attempts to copy back the pass-by-reference values to
@ -6171,15 +6188,23 @@ hawk_val_t* hawk_rtx_evalcall (
{
if (n >= 0 && (fun->argspec[i] == 'r' || fun->argspec[i] == 'R'))
{
hawk_val_t* v;
hawk_val_t* v, * av;
v = rtx->stack[call->arg_base + i]; /* UGLY */
if (HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_REF)
av = HAWK_RTX_STACK_ARG(rtx, i);
if (HAWK_RTX_GETVALTYPE(rtx, av) == HAWK_VAL_REF)
{
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)v, HAWK_RTX_STACK_ARG(rtx, i)) <= -1))
/* ---- DO NOTHING ---- */
}
else
{
v = rtx->stack[call->arg_base + i]; /* UGLY */
if (HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_REF)
{
n = -1;
ADJERR_LOC (rtx, &call->loc);
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)v, av) <= -1))
{
n = -1;
ADJERR_LOC (rtx, &call->loc);
}
}
}
}

View File

@ -2465,7 +2465,7 @@ hawk_val_t* hawk_rtx_getrefval (hawk_rtx_t* rtx, hawk_val_ref_t* ref)
int hawk_rtx_setrefval (hawk_rtx_t* rtx, hawk_val_ref_t* ref, hawk_val_t* val)
{
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE (rtx, val);
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);
if (vtype == HAWK_VAL_REX || vtype == HAWK_VAL_REF)
{