347 lines
9.8 KiB
C
347 lines
9.8 KiB
C
#define SUPPRESS_COMPILER_INLINES
|
|
#include "std.h"
|
|
#include "lpc_incl.h"
|
|
#include "eoperators.h"
|
|
#include "compiler.h"
|
|
#include "replace_program.h"
|
|
|
|
INLINE void
|
|
dealloc_funp (funptr_t * fp)
|
|
{
|
|
program_t *prog = 0;
|
|
|
|
switch (fp->hdr.type) {
|
|
case FP_LOCAL | FP_NOT_BINDABLE:
|
|
if (fp->hdr.owner)
|
|
prog = fp->hdr.owner->prog;
|
|
break;
|
|
case FP_FUNCTIONAL:
|
|
case FP_FUNCTIONAL | FP_NOT_BINDABLE:
|
|
prog = fp->f.functional.prog;
|
|
break;
|
|
}
|
|
|
|
if (fp->hdr.owner)
|
|
free_object(&fp->hdr.owner, "free_funp");
|
|
if (fp->hdr.args)
|
|
free_array(fp->hdr.args);
|
|
|
|
if (prog) {
|
|
prog->func_ref--;
|
|
debug(d_flag, ("subtr func ref /%s: now %i\n",
|
|
prog->filename, prog->func_ref));
|
|
if (!prog->func_ref && !prog->ref)
|
|
deallocate_program(prog);
|
|
}
|
|
|
|
FREE(fp);
|
|
}
|
|
|
|
INLINE void
|
|
free_funp (funptr_t * fp)
|
|
{
|
|
fp->hdr.ref--;
|
|
if (fp->hdr.ref > 0) {
|
|
return;
|
|
}
|
|
dealloc_funp(fp);
|
|
}
|
|
|
|
INLINE void
|
|
push_refed_funp (funptr_t * fp)
|
|
{
|
|
STACK_INC;
|
|
sp->type = T_FUNCTION;
|
|
sp->u.fp = fp;
|
|
}
|
|
|
|
INLINE void
|
|
push_funp (funptr_t * fp)
|
|
{
|
|
STACK_INC;
|
|
sp->type = T_FUNCTION;
|
|
sp->u.fp = fp;
|
|
fp->hdr.ref++;
|
|
}
|
|
|
|
/* num_arg args are on the stack, and the args from the array vec should be
|
|
* put in front of them. This is so that the order of arguments is logical.
|
|
*
|
|
* evaluate( (: f, a :), b) -> f(a,b) and not f(b, a) which would happen
|
|
* if we simply pushed the args from vec at this point. (Note that the
|
|
* old function pointers are broken in this regard)
|
|
*/
|
|
int merge_arg_lists (int num_arg, array_t * arr, int start) {
|
|
int num_arr_arg = arr->size - start;
|
|
svalue_t *sptr;
|
|
|
|
if (num_arr_arg) {
|
|
CHECK_STACK_OVERFLOW(num_arr_arg);
|
|
sptr = (sp += num_arr_arg);
|
|
if (num_arg) {
|
|
/* We need to do some stack movement so that the order
|
|
of arguments is logical */
|
|
while (num_arg--) {
|
|
*sptr = *(sptr - num_arr_arg);
|
|
sptr--;
|
|
}
|
|
}
|
|
num_arg = arr->size;
|
|
while (--num_arg >= start)
|
|
assign_svalue_no_free(sptr--, &arr->item[num_arg]);
|
|
/* could just return num_arr_arg if num_arg is 0 but .... -Sym */
|
|
return (sp - sptr);
|
|
}
|
|
return num_arg;
|
|
}
|
|
|
|
INLINE funptr_t *
|
|
make_efun_funp (int opcode, svalue_t * args)
|
|
{
|
|
funptr_t *fp;
|
|
|
|
fp = (funptr_t *)DXALLOC(sizeof(funptr_t),
|
|
TAG_FUNP, "make_efun_funp");
|
|
fp->hdr.owner = current_object;
|
|
add_ref( current_object, "make_efun_funp" );
|
|
fp->hdr.type = FP_EFUN;
|
|
|
|
fp->f.efun.index = opcode;
|
|
|
|
if (args->type == T_ARRAY) {
|
|
fp->hdr.args = args->u.arr;
|
|
args->u.arr->ref++;
|
|
} else
|
|
fp->hdr.args = 0;
|
|
|
|
fp->hdr.ref = 1;
|
|
return fp;
|
|
}
|
|
|
|
INLINE funptr_t *
|
|
make_lfun_funp (int index, svalue_t * args)
|
|
{
|
|
funptr_t *fp;
|
|
int newindex;
|
|
|
|
if (replace_program_pending(current_object))
|
|
error("cannot bind an lfun fp to an object with a pending replace_program()\n");
|
|
|
|
fp = (funptr_t *)DXALLOC(sizeof(funptr_hdr_t) + sizeof(local_ptr_t),
|
|
TAG_FUNP, "make_lfun_funp");
|
|
fp->hdr.owner = current_object;
|
|
add_ref( current_object, "make_lfun_funp" );
|
|
fp->hdr.type = FP_LOCAL | FP_NOT_BINDABLE;
|
|
|
|
fp->hdr.owner->prog->func_ref++;
|
|
debug(d_flag, ("add func ref /%s: now %i\n",
|
|
fp->hdr.owner->prog->filename,
|
|
fp->hdr.owner->prog->func_ref));
|
|
|
|
newindex = index + function_index_offset;
|
|
if (current_object->prog->function_flags[newindex] & FUNC_ALIAS)
|
|
newindex = current_object->prog->function_flags[newindex] & ~FUNC_ALIAS;
|
|
fp->f.local.index = newindex;
|
|
|
|
if (args->type == T_ARRAY) {
|
|
fp->hdr.args = args->u.arr;
|
|
args->u.arr->ref++;
|
|
} else
|
|
fp->hdr.args = 0;
|
|
|
|
fp->hdr.ref = 1;
|
|
return fp;
|
|
}
|
|
|
|
INLINE funptr_t *
|
|
make_simul_funp (int index, svalue_t * args)
|
|
{
|
|
funptr_t *fp;
|
|
|
|
fp = (funptr_t *)DXALLOC(sizeof(funptr_hdr_t) + sizeof(simul_ptr_t),
|
|
TAG_FUNP, "make_simul_funp");
|
|
fp->hdr.owner = current_object;
|
|
add_ref( current_object, "make_simul_funp" );
|
|
fp->hdr.type = FP_SIMUL;
|
|
|
|
fp->f.simul.index = index;
|
|
|
|
if (args->type == T_ARRAY) {
|
|
fp->hdr.args = args->u.arr;
|
|
args->u.arr->ref++;
|
|
} else
|
|
fp->hdr.args = 0;
|
|
|
|
fp->hdr.ref = 1;
|
|
return fp;
|
|
}
|
|
|
|
INLINE funptr_t *
|
|
make_functional_funp (short num_arg, short num_local, short len, svalue_t * args, int flag)
|
|
{
|
|
funptr_t *fp;
|
|
|
|
if (replace_program_pending(current_object))
|
|
error("cannot bind a functional to an object with a pending replace_program()\n");
|
|
|
|
fp = (funptr_t *)DXALLOC(sizeof(funptr_hdr_t) + sizeof(functional_t),
|
|
TAG_FUNP, "make_functional_funp");
|
|
fp->hdr.owner = current_object;
|
|
add_ref( current_object, "make_functional_funp" );
|
|
fp->hdr.type = FP_FUNCTIONAL + flag;
|
|
|
|
current_prog->func_ref++;
|
|
debug(d_flag, ("add func ref /%s: now %i\n",
|
|
current_prog->filename,
|
|
current_prog->func_ref));
|
|
|
|
fp->f.functional.prog = current_prog;
|
|
fp->f.functional.offset = pc - current_prog->program;
|
|
fp->f.functional.num_arg = num_arg;
|
|
fp->f.functional.num_local = num_local;
|
|
fp->f.functional.fio = function_index_offset;
|
|
fp->f.functional.vio = variable_index_offset;
|
|
pc += len;
|
|
|
|
if (args && args->type == T_ARRAY) {
|
|
fp->hdr.args = args->u.arr;
|
|
args->u.arr->ref++;
|
|
fp->f.functional.num_arg += args->u.arr->size;
|
|
} else
|
|
fp->hdr.args = 0;
|
|
|
|
fp->hdr.ref = 1;
|
|
return fp;
|
|
}
|
|
|
|
typedef void (*func_t) (void);
|
|
extern func_t efun_table[];
|
|
|
|
svalue_t *
|
|
call_function_pointer (funptr_t * funp, int num_arg)
|
|
{
|
|
static func_t *oefun_table = efun_table - BASE;
|
|
array_t *v;
|
|
int extfr = 1;
|
|
|
|
if (!funp->hdr.owner || (funp->hdr.owner->flags & O_DESTRUCTED))
|
|
error("Owner (/%s) of function pointer is destructed.\n",
|
|
(funp->hdr.owner ? funp->hdr.owner->obname : "(null)"));
|
|
setup_fake_frame(funp);
|
|
if ((v=funp->hdr.args)) {
|
|
check_for_destr(v);
|
|
num_arg = merge_arg_lists(num_arg, v, 0);
|
|
}
|
|
|
|
switch (funp->hdr.type) {
|
|
case FP_SIMUL:
|
|
call_simul_efun(funp->f.simul.index, num_arg);
|
|
break;
|
|
case FP_EFUN:
|
|
{
|
|
int i, def;
|
|
fp = sp - num_arg + 1;
|
|
|
|
i = funp->f.efun.index;
|
|
if (num_arg == instrs[i].min_arg - 1 &&
|
|
((def = instrs[i].Default) != DEFAULT_NONE)) {
|
|
if (def == DEFAULT_THIS_OBJECT) {
|
|
push_object(current_object);
|
|
} else {
|
|
push_number(def);
|
|
}
|
|
num_arg++;
|
|
} else
|
|
if (num_arg < instrs[i].min_arg) {
|
|
error("Too few arguments to efun %s in efun pointer.\n", query_instr_name(i));
|
|
} else if (num_arg > instrs[i].max_arg && instrs[i].max_arg != -1) {
|
|
error("Too many arguments to efun %s in efun pointer.\n", query_instr_name(i));
|
|
}
|
|
/* possibly we should add TRACE, OPC, etc here;
|
|
also on eval_cost here, which is ok for just 1 efun */
|
|
{
|
|
int j, n = num_arg;
|
|
st_num_arg = num_arg;
|
|
|
|
if (n >= 4 || instrs[i].max_arg == -1)
|
|
n = instrs[i].min_arg;
|
|
|
|
for (j = 0; j < n; j++) {
|
|
CHECK_TYPES(sp - num_arg + j + 1, instrs[i].type[j], j + 1, i);
|
|
}
|
|
(*oefun_table[i])();
|
|
|
|
free_svalue(&apply_ret_value, "call_function_pointer");
|
|
if (instrs[i].ret_type == TYPE_NOVALUE)
|
|
apply_ret_value = const0;
|
|
else
|
|
apply_ret_value = *sp--;
|
|
remove_fake_frame();
|
|
return &apply_ret_value;
|
|
}
|
|
}
|
|
case FP_LOCAL | FP_NOT_BINDABLE: {
|
|
function_t *func;
|
|
|
|
fp = sp - num_arg + 1;
|
|
|
|
if (current_object->prog->function_flags[funp->f.local.index] & (FUNC_PROTOTYPE|FUNC_UNDEFINED))
|
|
error("Undefined lfun pointer called: %s\n", function_name(current_object->prog, funp->f.local.index));
|
|
push_control_stack(FRAME_FUNCTION);
|
|
current_prog = funp->hdr.owner->prog;
|
|
|
|
caller_type = ORIGIN_LOCAL;
|
|
|
|
csp->num_local_variables = num_arg;
|
|
func = setup_new_frame(funp->f.local.index);
|
|
|
|
call_program(current_prog, func->address);
|
|
break;
|
|
}
|
|
case FP_FUNCTIONAL:
|
|
case FP_FUNCTIONAL | FP_NOT_BINDABLE: {
|
|
|
|
fp = sp - num_arg + 1;
|
|
push_control_stack(FRAME_FUNP);
|
|
current_prog = funp->f.functional.prog;
|
|
csp->fr.funp = funp;
|
|
|
|
caller_type = ORIGIN_FUNCTIONAL;
|
|
|
|
setup_variables(num_arg, funp->f.functional.num_local,
|
|
funp->f.functional.num_arg);
|
|
|
|
function_index_offset = funp->f.functional.fio;
|
|
variable_index_offset = funp->f.functional.vio;
|
|
call_program(funp->f.functional.prog, funp->f.functional.offset);
|
|
break;
|
|
}
|
|
default:
|
|
error("Unsupported function pointer type.\n");
|
|
}
|
|
free_svalue(&apply_ret_value, "call_function_pointer");
|
|
apply_ret_value = *sp--;
|
|
remove_fake_frame();
|
|
return &apply_ret_value;
|
|
}
|
|
|
|
svalue_t *
|
|
safe_call_function_pointer (funptr_t * funp, int num_arg)
|
|
{
|
|
error_context_t econ;
|
|
svalue_t *ret;
|
|
|
|
if (!save_context(&econ))
|
|
return 0;
|
|
if (!SETJMP(econ.context)) {
|
|
ret = call_function_pointer(funp, num_arg);
|
|
} else {
|
|
restore_context(&econ);
|
|
/* condition was restored to where it was when we came in */
|
|
pop_n_elems(num_arg);
|
|
ret = 0;
|
|
}
|
|
pop_context(&econ);
|
|
return ret;
|
|
}
|