Commit 22721316 authored by Noam Postavsky's avatar Noam Postavsky

Add lisp watchpoints

This allows calling a function whenever a symbol-value is changed.

* src/lisp.h (lisp_h_SYMBOL_TRAPPED_WRITE_P):
(SYMBOL_TRAPPED_WRITE_P): New function/macro.
(lisp_h_SYMBOL_CONSTANT_P): Check for SYMBOL_NOWRITE specifically.
(enum symbol_trapped_write): New enumeration.
(struct Lisp_Symbol): Rename field constant to trapped_write.
(make_symbol_constant): New function.

* src/data.c (Fadd_variable_watcher, Fremove_variable_watcher):
(set_symbol_trapped_write, restore_symbol_trapped_write):
(harmonize_variable_watchers, notify_variable_watchers): New functions.

* src/data.c (Fset_default): Call `notify_variable_watchers' for trapped
symbols.
(set_internal): Change bool argument BIND to 3-value enum and call
`notify_variable_watchers' for trapped symbols.

* src/data.c (syms_of_data):
* src/data.c (syms_of_data):
* src/font.c (syms_of_font):
* src/lread.c (intern_sym, init_obarray):
* src/buffer.c (syms_of_buffer): Use make_symbol_constant.

* src/alloc.c (init_symbol):
* src/bytecode.c (exec_byte_code): Use SYMBOL_TRAPPED_WRITE_P.
* src/data.c (Fmake_variable_buffer_local, Fmake_local_variable):
(Fmake_variable_frame_local):
* src/eval.c (Fdefvaralias, specbind): Refer to Lisp_Symbol's
trapped_write instead of constant.
(Ffuncall): Move subr calling code into separate function.
(funcall_subr): New function.
parent 0fc4761c
......@@ -3567,7 +3567,7 @@ init_symbol (Lisp_Object val, Lisp_Object name)
set_symbol_next (val, NULL);
p->gcmarkbit = false;
p->interned = SYMBOL_UNINTERNED;
p->constant = 0;
p->trapped_write = SYMBOL_UNTRAPPED_WRITE;
p->declared_special = false;
p->pinned = false;
}
......
......@@ -984,40 +984,54 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too)
bset_local_var_alist (b, Qnil);
else
{
Lisp_Object tmp, prop, last = Qnil;
Lisp_Object tmp, last = Qnil;
for (tmp = BVAR (b, local_var_alist); CONSP (tmp); tmp = XCDR (tmp))
if (!NILP (prop = Fget (XCAR (XCAR (tmp)), Qpermanent_local)))
{
/* If permanent-local, keep it. */
last = tmp;
if (EQ (prop, Qpermanent_local_hook))
{
/* This is a partially permanent hook variable.
Preserve only the elements that want to be preserved. */
Lisp_Object list, newlist;
list = XCDR (XCAR (tmp));
if (!CONSP (list))
newlist = list;
else
for (newlist = Qnil; CONSP (list); list = XCDR (list))
{
Lisp_Object elt = XCAR (list);
/* Preserve element ELT if it's t,
if it is a function with a `permanent-local-hook' property,
or if it's not a symbol. */
if (! SYMBOLP (elt)
|| EQ (elt, Qt)
|| !NILP (Fget (elt, Qpermanent_local_hook)))
newlist = Fcons (elt, newlist);
}
XSETCDR (XCAR (tmp), Fnreverse (newlist));
}
}
/* Delete this local variable. */
else if (NILP (last))
bset_local_var_alist (b, XCDR (tmp));
else
XSETCDR (last, XCDR (tmp));
{
Lisp_Object local_var = XCAR (XCAR (tmp));
Lisp_Object prop = Fget (local_var, Qpermanent_local);
if (!NILP (prop))
{
/* If permanent-local, keep it. */
last = tmp;
if (EQ (prop, Qpermanent_local_hook))
{
/* This is a partially permanent hook variable.
Preserve only the elements that want to be preserved. */
Lisp_Object list, newlist;
list = XCDR (XCAR (tmp));
if (!CONSP (list))
newlist = list;
else
for (newlist = Qnil; CONSP (list); list = XCDR (list))
{
Lisp_Object elt = XCAR (list);
/* Preserve element ELT if it's t,
if it is a function with a `permanent-local-hook' property,
or if it's not a symbol. */
if (! SYMBOLP (elt)
|| EQ (elt, Qt)
|| !NILP (Fget (elt, Qpermanent_local_hook)))
newlist = Fcons (elt, newlist);
}
newlist = Fnreverse (newlist);
if (XSYMBOL (local_var)->trapped_write == SYMBOL_TRAPPED_WRITE)
notify_variable_watchers (local_var, newlist,
Qmakunbound, Fcurrent_buffer ());
XSETCDR (XCAR (tmp), newlist);
continue; /* Don't do variable write trapping twice. */
}
}
/* Delete this local variable. */
else if (NILP (last))
bset_local_var_alist (b, XCDR (tmp));
else
XSETCDR (last, XCDR (tmp));
if (XSYMBOL (local_var)->trapped_write == SYMBOL_TRAPPED_WRITE)
notify_variable_watchers (local_var, Qnil,
Qmakunbound, Fcurrent_buffer ());
}
}
for (i = 0; i < last_per_buffer_idx; ++i)
......@@ -5541,7 +5555,7 @@ file I/O and the behavior of various editing commands.
This variable is buffer-local but you cannot set it directly;
use the function `set-buffer-multibyte' to change a buffer's representation.
See also Info node `(elisp)Text Representations'. */);
XSYMBOL (intern_c_string ("enable-multibyte-characters"))->constant = 1;
make_symbol_constant (intern_c_string ("enable-multibyte-characters"));
DEFVAR_PER_BUFFER ("buffer-file-coding-system",
&BVAR (current_buffer, buffer_file_coding_system), Qnil,
......
......@@ -569,10 +569,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
if (SYMBOLP (sym)
&& !EQ (val, Qunbound)
&& !XSYMBOL (sym)->redirect
&& !SYMBOL_CONSTANT_P (sym))
&& !SYMBOL_TRAPPED_WRITE_P (sym))
SET_SYMBOL_VAL (XSYMBOL (sym), val);
else
set_internal (sym, val, Qnil, false);
set_internal (sym, val, Qnil, SET_INTERNAL_SET);
}
NEXT;
......
......@@ -1225,7 +1225,7 @@ DEFUN ("set", Fset, Sset, 2, 2, 0,
doc: /* Set SYMBOL's value to NEWVAL, and return NEWVAL. */)
(register Lisp_Object symbol, Lisp_Object newval)
{
set_internal (symbol, newval, Qnil, 0);
set_internal (symbol, newval, Qnil, SET_INTERNAL_SET);
return newval;
}
......@@ -1233,13 +1233,14 @@ DEFUN ("set", Fset, Sset, 2, 2, 0,
If buffer/frame-locality is an issue, WHERE specifies which context to use.
(nil stands for the current buffer/frame).
If BINDFLAG is false, then if this symbol is supposed to become
local in every buffer where it is set, then we make it local.
If BINDFLAG is true, we don't do that. */
If BINDFLAG is SET_INTERNAL_SET, then if this symbol is supposed to
become local in every buffer where it is set, then we make it
local. If BINDFLAG is SET_INTERNAL_BIND or SET_INTERNAL_UNBIND, we
don't do that. */
void
set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
bool bindflag)
enum Set_Internal_Bind bindflag)
{
bool voide = EQ (newval, Qunbound);
struct Lisp_Symbol *sym;
......@@ -1250,18 +1251,31 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
return; */
CHECK_SYMBOL (symbol);
if (SYMBOL_CONSTANT_P (symbol))
sym = XSYMBOL (symbol);
switch (sym->trapped_write)
{
case SYMBOL_NOWRITE:
if (NILP (Fkeywordp (symbol))
|| !EQ (newval, Fsymbol_value (symbol)))
xsignal1 (Qsetting_constant, symbol);
|| !EQ (newval, Fsymbol_value (symbol)))
xsignal1 (Qsetting_constant, symbol);
else
/* Allow setting keywords to their own value. */
return;
/* Allow setting keywords to their own value. */
return;
case SYMBOL_TRAPPED_WRITE:
notify_variable_watchers (symbol, voide? Qnil : newval,
(bindflag == SET_INTERNAL_BIND? Qlet :
bindflag == SET_INTERNAL_UNBIND? Qunlet :
voide? Qmakunbound : Qset),
where);
/* FALLTHROUGH! */
case SYMBOL_UNTRAPPED_WRITE:
break;
default: emacs_abort ();
}
maybe_set_redisplay (symbol);
sym = XSYMBOL (symbol);
start:
switch (sym->redirect)
......@@ -1385,6 +1399,111 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where,
}
return;
}
static void
set_symbol_trapped_write (Lisp_Object symbol, enum symbol_trapped_write trap)
{
struct Lisp_Symbol* sym = XSYMBOL (symbol);
if (sym->trapped_write == SYMBOL_NOWRITE)
xsignal1 (Qtrapping_constant, symbol);
else if (sym->redirect == SYMBOL_LOCALIZED
&& SYMBOL_BLV (sym)->frame_local)
xsignal1 (Qtrapping_frame_local, symbol);
sym->trapped_write = trap;
}
static void
restore_symbol_trapped_write (Lisp_Object symbol)
{
set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE);
}
static void
harmonize_variable_watchers (Lisp_Object alias, Lisp_Object base_variable)
{
if (!EQ (base_variable, alias)
&& EQ (base_variable, Findirect_variable (alias)))
set_symbol_trapped_write
(alias, XSYMBOL (base_variable)->trapped_write);
}
DEFUN ("add-variable-watcher", Fadd_variable_watcher, Sadd_variable_watcher,
2, 2, 0,
doc: /* Cause WATCH-FUNCTION to be called when SYMBOL is set.
All writes to aliases of SYMBOL will call WATCH-FUNCTION too. */)
(Lisp_Object symbol, Lisp_Object watch_function)
{
symbol = Findirect_variable (symbol);
set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE);
map_obarray (Vobarray, harmonize_variable_watchers, symbol);
Lisp_Object watchers = Fget (symbol, Qwatchers);
Lisp_Object member = Fmember (watch_function, watchers);
if (NILP (member))
Fput (symbol, Qwatchers, Fcons (watch_function, watchers));
return Qnil;
}
DEFUN ("remove-variable-watcher", Fremove_variable_watcher, Sremove_variable_watcher,
2, 2, 0,
doc: /* Undo the effect of `add-variable-watcher'.
Remove WATCH-FUNCTION from the list of functions to be called when
SYMBOL (or its aliases) are set. */)
(Lisp_Object symbol, Lisp_Object watch_function)
{
symbol = Findirect_variable (symbol);
Lisp_Object watchers = Fget (symbol, Qwatchers);
watchers = Fdelete (watch_function, watchers);
if (NILP (watchers))
{
set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE);
map_obarray (Vobarray, harmonize_variable_watchers, symbol);
}
Fput (symbol, Qwatchers, watchers);
return Qnil;
}
void
notify_variable_watchers (Lisp_Object symbol,
Lisp_Object newval,
Lisp_Object operation,
Lisp_Object where)
{
symbol = Findirect_variable (symbol);
ptrdiff_t count = SPECPDL_INDEX ();
record_unwind_protect (restore_symbol_trapped_write, symbol);
/* Avoid recursion. */
set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE);
if (NILP (where)
&& !EQ (operation, Qset_default) && !EQ (operation, Qmakunbound)
&& !NILP (Flocal_variable_if_set_p (symbol, Fcurrent_buffer ())))
{
XSETBUFFER (where, current_buffer);
}
if (EQ (operation, Qset_default))
operation = Qset;
for (Lisp_Object watchers = Fget (symbol, Qwatchers);
CONSP (watchers);
watchers = XCDR (watchers))
{
Lisp_Object watcher = XCAR (watchers);
/* Call subr directly to avoid gc. */
if (SUBRP (watcher))
{
Lisp_Object args[] = { symbol, newval, operation, where };
funcall_subr (XSUBR (watcher), ARRAYELTS (args), args);
}
else
CALLN (Ffuncall, watcher, symbol, newval, operation, where);
}
unbind_to (count, Qnil);
}
/* Access or set a buffer-local symbol's default value. */
......@@ -1471,16 +1590,27 @@ for this variable. */)
struct Lisp_Symbol *sym;
CHECK_SYMBOL (symbol);
if (SYMBOL_CONSTANT_P (symbol))
sym = XSYMBOL (symbol);
switch (sym->trapped_write)
{
case SYMBOL_NOWRITE:
if (NILP (Fkeywordp (symbol))
|| !EQ (value, Fdefault_value (symbol)))
xsignal1 (Qsetting_constant, symbol);
|| !EQ (value, Fsymbol_value (symbol)))
xsignal1 (Qsetting_constant, symbol);
else
/* Allow setting keywords to their own value. */
return value;
/* Allow setting keywords to their own value. */
return value;
case SYMBOL_TRAPPED_WRITE:
/* Don't notify here if we're going to call Fset anyway. */
if (sym->redirect != SYMBOL_PLAINVAL)
notify_variable_watchers (symbol, value, Qset_default, Qnil);
/* FALLTHROUGH! */
case SYMBOL_UNTRAPPED_WRITE:
break;
default: emacs_abort ();
}
sym = XSYMBOL (symbol);
start:
switch (sym->redirect)
......@@ -1651,7 +1781,7 @@ The function `default-value' gets the default value and `set-default' sets it.
default: emacs_abort ();
}
if (sym->constant)
if (SYMBOL_CONSTANT_P (variable))
error ("Symbol %s may not be buffer-local", SDATA (SYMBOL_NAME (variable)));
if (!blv)
......@@ -1726,7 +1856,7 @@ Instead, use `add-hook' and specify t for the LOCAL argument. */)
default: emacs_abort ();
}
if (sym->constant)
if (sym->trapped_write == SYMBOL_NOWRITE)
error ("Symbol %s may not be buffer-local",
SDATA (SYMBOL_NAME (variable)));
......@@ -1838,6 +1968,9 @@ From now on the default value will apply in this buffer. Return VARIABLE. */)
default: emacs_abort ();
}
if (sym->trapped_write == SYMBOL_TRAPPED_WRITE)
notify_variable_watchers (variable, Qnil, Qmakunbound, Fcurrent_buffer ());
/* Get rid of this buffer's alist element, if any. */
XSETSYMBOL (variable, sym); /* Propagate variable indirection. */
tem = Fassq (variable, BVAR (current_buffer, local_var_alist));
......@@ -1920,7 +2053,7 @@ frame-local bindings). */)
default: emacs_abort ();
}
if (sym->constant)
if (SYMBOL_TRAPPED_WRITE_P (variable))
error ("Symbol %s may not be frame-local", SDATA (SYMBOL_NAME (variable)));
blv = make_blv (sym, forwarded, valcontents);
......@@ -3465,6 +3598,8 @@ syms_of_data (void)
DEFSYM (Qcyclic_variable_indirection, "cyclic-variable-indirection");
DEFSYM (Qvoid_variable, "void-variable");
DEFSYM (Qsetting_constant, "setting-constant");
DEFSYM (Qtrapping_constant, "trapping-constant");
DEFSYM (Qtrapping_frame_local, "trapping-frame-local");
DEFSYM (Qinvalid_read_syntax, "invalid-read-syntax");
DEFSYM (Qinvalid_function, "invalid-function");
......@@ -3543,6 +3678,10 @@ syms_of_data (void)
PUT_ERROR (Qvoid_variable, error_tail, "Symbol's value as variable is void");
PUT_ERROR (Qsetting_constant, error_tail,
"Attempt to set a constant symbol");
PUT_ERROR (Qtrapping_constant, error_tail,
"Attempt to trap writes to a constant symbol");
PUT_ERROR (Qtrapping_frame_local, error_tail,
"Attempt to trap writes to a frame local variable");
PUT_ERROR (Qinvalid_read_syntax, error_tail, "Invalid read syntax");
PUT_ERROR (Qinvalid_function, error_tail, "Invalid function");
PUT_ERROR (Qwrong_number_of_arguments, error_tail,
......@@ -3721,10 +3860,18 @@ syms_of_data (void)
DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum,
doc: /* The largest value that is representable in a Lisp integer. */);
Vmost_positive_fixnum = make_number (MOST_POSITIVE_FIXNUM);
XSYMBOL (intern_c_string ("most-positive-fixnum"))->constant = 1;
make_symbol_constant (intern_c_string ("most-positive-fixnum"));
DEFVAR_LISP ("most-negative-fixnum", Vmost_negative_fixnum,
doc: /* The smallest value that is representable in a Lisp integer. */);
Vmost_negative_fixnum = make_number (MOST_NEGATIVE_FIXNUM);
XSYMBOL (intern_c_string ("most-negative-fixnum"))->constant = 1;
make_symbol_constant (intern_c_string ("most-negative-fixnum"));
DEFSYM (Qwatchers, "watchers");
DEFSYM (Qmakunbound, "makunbound");
DEFSYM (Qunlet, "unlet");
DEFSYM (Qset, "set");
DEFSYM (Qset_default, "set-default");
defsubr (&Sadd_variable_watcher);
defsubr (&Sremove_variable_watcher);
}
......@@ -593,12 +593,12 @@ The return value is BASE-VARIABLE. */)
CHECK_SYMBOL (new_alias);
CHECK_SYMBOL (base_variable);
sym = XSYMBOL (new_alias);
if (sym->constant)
/* Not sure why, but why not? */
if (SYMBOL_CONSTANT_P (new_alias))
/* Making it an alias effectively changes its value. */
error ("Cannot make a constant an alias");
sym = XSYMBOL (new_alias);
switch (sym->redirect)
{
case SYMBOL_FORWARDED:
......@@ -617,8 +617,8 @@ The return value is BASE-VARIABLE. */)
so that old-code that affects n_a before the aliasing is setup
still works. */
if (NILP (Fboundp (base_variable)))
set_internal (base_variable, find_symbol_value (new_alias), Qnil, 1);
set_internal (base_variable, find_symbol_value (new_alias),
Qnil, SET_INTERNAL_BIND);
{
union specbinding *p;
......@@ -628,11 +628,14 @@ The return value is BASE-VARIABLE. */)
error ("Don't know how to make a let-bound variable an alias");
}
if (sym->trapped_write == SYMBOL_TRAPPED_WRITE)
notify_variable_watchers (new_alias, base_variable, Qdefvaralias, Qnil);
sym->declared_special = 1;
XSYMBOL (base_variable)->declared_special = 1;
sym->redirect = SYMBOL_VARALIAS;
SET_SYMBOL_ALIAS (sym, XSYMBOL (base_variable));
sym->constant = SYMBOL_CONSTANT_P (base_variable);
sym->trapped_write = XSYMBOL (base_variable)->trapped_write;
LOADHIST_ATTACH (new_alias);
/* Even if docstring is nil: remove old docstring. */
Fput (new_alias, Qvariable_documentation, docstring);
......@@ -2645,9 +2648,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
Lisp_Object fun, original_fun;
Lisp_Object funcar;
ptrdiff_t numargs = nargs - 1;
Lisp_Object lisp_numargs;
Lisp_Object val;
Lisp_Object *internal_args;
ptrdiff_t count;
QUIT;
......@@ -2680,86 +2681,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
fun = indirect_function (fun);
if (SUBRP (fun))
{
if (numargs < XSUBR (fun)->min_args
|| (XSUBR (fun)->max_args >= 0 && XSUBR (fun)->max_args < numargs))
{
XSETFASTINT (lisp_numargs, numargs);
xsignal2 (Qwrong_number_of_arguments, original_fun, lisp_numargs);
}
else if (XSUBR (fun)->max_args == UNEVALLED)
xsignal1 (Qinvalid_function, original_fun);
else if (XSUBR (fun)->max_args == MANY)
val = (XSUBR (fun)->function.aMANY) (numargs, args + 1);
else
{
Lisp_Object internal_argbuf[8];
if (XSUBR (fun)->max_args > numargs)
{
eassert (XSUBR (fun)->max_args <= ARRAYELTS (internal_argbuf));
internal_args = internal_argbuf;
memcpy (internal_args, args + 1, numargs * word_size);
memclear (internal_args + numargs,
(XSUBR (fun)->max_args - numargs) * word_size);
}
else
internal_args = args + 1;
switch (XSUBR (fun)->max_args)
{
case 0:
val = (XSUBR (fun)->function.a0 ());
break;
case 1:
val = (XSUBR (fun)->function.a1 (internal_args[0]));
break;
case 2:
val = (XSUBR (fun)->function.a2
(internal_args[0], internal_args[1]));
break;
case 3:
val = (XSUBR (fun)->function.a3
(internal_args[0], internal_args[1], internal_args[2]));
break;
case 4:
val = (XSUBR (fun)->function.a4
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3]));
break;
case 5:
val = (XSUBR (fun)->function.a5
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4]));
break;
case 6:
val = (XSUBR (fun)->function.a6
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5]));
break;
case 7:
val = (XSUBR (fun)->function.a7
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5],
internal_args[6]));
break;
case 8:
val = (XSUBR (fun)->function.a8
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5],
internal_args[6], internal_args[7]));
break;
default:
/* If a subr takes more than 8 arguments without using MANY
or UNEVALLED, we need to extend this function to support it.
Until this is done, there is no way to call the function. */
emacs_abort ();
}
}
}
val = funcall_subr (XSUBR (fun), numargs, args + 1);
else if (COMPILEDP (fun))
val = funcall_lambda (fun, numargs, args + 1);
else
......@@ -2791,6 +2713,89 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
return val;
}
/* Apply a C subroutine SUBR to the NUMARGS evaluated arguments in ARG_VECTOR
and return the result of evaluation. */
Lisp_Object
funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *args)
{
if (numargs < subr->min_args
|| (subr->max_args >= 0 && subr->max_args < numargs))
{
Lisp_Object fun;
XSETSUBR (fun, subr);
xsignal2 (Qwrong_number_of_arguments, fun, make_number (numargs));
}
else if (subr->max_args == UNEVALLED)
{
Lisp_Object fun;
XSETSUBR (fun, subr);
xsignal1 (Qinvalid_function, fun);
}
else if (subr->max_args == MANY)
return (subr->function.aMANY) (numargs, args);
else
{
Lisp_Object internal_argbuf[8];
Lisp_Object *internal_args;
if (subr->max_args > numargs)
{
eassert (subr->max_args <= ARRAYELTS (internal_argbuf));
internal_args = internal_argbuf;
memcpy (internal_args, args, numargs * word_size);
memclear (internal_args + numargs,
(subr->max_args - numargs) * word_size);
}
else
internal_args = args;
switch (subr->max_args)
{
case 0:
return (subr->function.a0 ());
case 1:
return (subr->function.a1 (internal_args[0]));
case 2:
return (subr->function.a2
(internal_args[0], internal_args[1]));
case 3:
return (subr->function.a3
(internal_args[0], internal_args[1], internal_args[2]));
case 4:
return (subr->function.a4
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3]));
case 5:
return (subr->function.a5
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4]));
case 6:
return (subr->function.a6
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5]));
case 7:
return (subr->function.a7
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5],
internal_args[6]));
case 8:
return (subr->function.a8
(internal_args[0], internal_args[1], internal_args[2],
internal_args[3], internal_args[4], internal_args[5],
internal_args[6], internal_args[7]));