ecl/src/c/eval.d
Daniel Kochmański e9e97815bc stacks: make stack frame state consistent based on its operators
Previously we did not perform necessary checks for whether we should update the
frame size and stack pointer or whether we should resize the stack. This commit
fixes these functions and adds a missing function to API ecl_stack_frame_pop.
2025-05-29 14:05:17 +02:00

259 lines
6.3 KiB
C

/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* vim: set filetype=c tabstop=2 shiftwidth=2 expandtab: */
/*
* eval.d - evaluation
*
* Copyright (c) 1984 Taiichi Yuasa and Masami Hagiya
* Copyright (c) 1990 Giuseppe Attardi
* Copyright (c) 2001 Juan Jose Garcia Ripoll
*
* See file 'LICENSE' for the copyright details.
*
*/
#include <ecl/ecl.h>
#include <ecl/ecl-inl.h>
#include <ecl/internal.h>
cl_object *
_ecl_va_sp(cl_narg narg)
{
return ECL_STACK_FRAME_PTR(ecl_process_env()->stack_frame) + narg;
}
/* Calling conventions:
* Compiled C code calls lisp function supplying #args, and args.
* Linking function performs check_args, gets jmp_buf with _setjmp, then
* if cfun then stores C code address into function link location
* and transfers to jmp_buf at cf_self
* if cclosure then replaces #args with cc_env and calls cc_self
* otherwise, it emulates funcall.
*/
cl_object
ecl_apply_from_stack_frame(cl_object frame, cl_object x)
{
cl_object *sp = ECL_STACK_FRAME_PTR(frame);
cl_index narg = frame->frame.size;
cl_object fun = x;
cl_object ret;
frame->frame.env->stack_frame = frame;
AGAIN:
frame->frame.env->function = fun;
if (ecl_unlikely(fun == ECL_NIL))
FEundefined_function(x);
switch (ecl_t_of(fun)) {
case t_cfunfixed:
if (ecl_unlikely(narg != (cl_index)fun->cfun.narg))
FEwrong_num_arguments(fun);
ret = APPLY_fixed(narg, fun->cfunfixed.entry_fixed, sp);
break;
case t_cfun:
ret = APPLY(narg, fun->cfun.entry, sp);
break;
case t_cclosure:
ret = APPLY(narg, fun->cclosure.entry, sp);
break;
case t_instance:
switch (fun->instance.isgf) {
case ECL_STANDARD_DISPATCH:
case ECL_RESTRICTED_DISPATCH:
ret = _ecl_standard_dispatch(frame, fun);
break;
case ECL_USER_DISPATCH:
fun = fun->instance.slots[fun->instance.length - 1];
goto AGAIN;
case ECL_READER_DISPATCH:
case ECL_WRITER_DISPATCH:
ret = APPLY(narg, fun->instance.entry, sp);
break;
default:
FEinvalid_function(fun);
}
break;
case t_symbol:
if (ecl_unlikely(!ECL_FBOUNDP(fun)))
FEundefined_function(fun);
fun = ECL_SYM_FUN(fun);
goto AGAIN;
case t_bytecodes:
ret = ecl_interpret(frame, ECL_NIL, fun);
break;
case t_bclosure:
ret = ecl_interpret(frame, fun->bclosure.lex, fun->bclosure.code);
break;
default:
FEinvalid_function(x);
}
frame->frame.env->stack_frame = NULL; /* for gc's sake */
return ret;
}
cl_objectfn
ecl_function_dispatch(cl_env_ptr env, cl_object x)
{
cl_object fun = x;
if (ecl_unlikely(fun == ECL_NIL))
FEundefined_function(x);
switch (ecl_t_of(fun)) {
case t_cfunfixed:
env->function = fun;
return fun->cfunfixed.entry;
case t_cfun:
env->function = fun;
return fun->cfun.entry;
case t_cclosure:
env->function = fun;
return fun->cclosure.entry;
case t_instance:
env->function = fun;
return fun->instance.entry;
case t_symbol:
fun = ECL_SYM_FUN(fun);
env->function = fun;
return fun->cfun.entry;
case t_bytecodes:
env->function = fun;
return fun->bytecodes.entry;
case t_bclosure:
env->function = fun;
return fun->bclosure.entry;
default:
FEinvalid_function(x);
}
}
cl_object
cl_funcall(cl_narg narg, cl_object function, ...)
{
cl_object output;
--narg;
{
ECL_STACK_FRAME_VARARGS_BEGIN(narg, function, frame);
output = ecl_apply_from_stack_frame(frame, function);
ECL_STACK_FRAME_VARARGS_END(frame);
}
return output;
}
@(defun apply (fun lastarg &rest args)
@ {
if (narg == 2 && ecl_t_of(lastarg) == t_frame) {
return ecl_apply_from_stack_frame(lastarg, fun);
} else {
cl_object out;
cl_index i;
struct ecl_stack_frame frame_aux;
const cl_object frame = ecl_stack_frame_open(the_env,
(cl_object)&frame_aux,
narg -= 2);
for (i = 0; i < narg; i++) {
ecl_stack_frame_push(frame, lastarg);
lastarg = ecl_va_arg(args);
}
if (ecl_t_of(lastarg) == t_frame) {
/* This could be replaced with a memcpy() */
for (i = 0; i < lastarg->frame.size; i++) {
ecl_stack_frame_push(frame, ECL_STACK_FRAME_REF(lastarg, i));
}
} else loop_for_in (lastarg) {
if (ecl_unlikely(i >= ECL_CALL_ARGUMENTS_LIMIT)) {
ecl_stack_frame_close(frame);
FEprogram_error("CALL-ARGUMENTS-LIMIT exceeded",0);
}
ecl_stack_frame_push(frame, CAR(lastarg));
i++;
} end_loop_for_in;
out = ecl_apply_from_stack_frame(frame, fun);
ecl_stack_frame_close(frame);
return out;
}
}@)
cl_object
cl_eval(cl_object form)
{
return si_eval_with_env(1, form);
}
@(defun constantp (arg &optional env)
@
return _ecl_funcall3(@'ext::constantp-inner', arg, env);
@)
@(defun ext::constantp-inner (form &optional env)
cl_object value;
@ {
AGAIN:
switch (ecl_t_of(form)) {
case t_list:
if (Null(form)) {
value = ECL_T;
break;
}
if (ECL_CONS_CAR(form) == @'quote') {
value = ECL_T;
break;
}
/*
value = cl_macroexpand(2, form, env);
if (value != form) {
form = value;
goto AGAIN;
}
*/
value = ECL_NIL;
break;
case t_symbol:
value = cl_macroexpand(2, form, env);
if (value != form) {
form = value;
goto AGAIN;
}
if (!(form->symbol.stype & ecl_stp_constant)) {
value = ECL_NIL;
break;
}
default:
value = ECL_T;
}
ecl_return1(the_env, value);
} @)
@(defun ext::constant-form-value (form &optional env)
cl_object value;
@ {
AGAIN:
switch (ecl_t_of(form)) {
case t_list:
if (Null(form)) {
value = ECL_NIL;
break;
}
if (ECL_CONS_CAR(form) == @'quote') {
return cl_second(form);
}
FEerror("EXT:CONSTANT-FORM-VALUE invoked with a non-constant form ~A",
0, form);
break;
case t_symbol:
value = cl_macroexpand(2, form, env);
if (value != form) {
form = value;
goto AGAIN;
}
value = ECL_SYM_VAL(the_env, value);
break;
default:
value = form;
}
@(return value);
} @)
cl_object
ecl_undefined_function_entry(cl_narg narg, ...)
{
FEundefined_function(ecl_process_env()->function); /* see object.h */
}