mirror of
https://gitlab.com/embeddable-common-lisp/ecl.git
synced 2026-01-11 11:42:51 -08:00
250 lines
6.1 KiB
D
250 lines
6.1 KiB
D
/*
|
|
gfun.c -- Dispatch for generic functions.
|
|
*/
|
|
/*
|
|
Copyright (c) 1990, Giuseppe Attardi.
|
|
|
|
ECL is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
See file '../Copyright' for full details.
|
|
*/
|
|
|
|
#include "ecl.h"
|
|
|
|
cl_object
|
|
si_allocate_gfun(cl_object name, cl_object arg_no, cl_object ht)
|
|
{
|
|
cl_object x;
|
|
int n, i;
|
|
|
|
if (type_of(ht) != t_hashtable)
|
|
FEwrong_type_argument(@'hash-table', ht);
|
|
|
|
x = cl_alloc_object(t_gfun);
|
|
x->gfun.specializers = NULL; /* for GC sake */
|
|
x->gfun.name = name;
|
|
x->gfun.method_hash = ht;
|
|
n = fixnnint(arg_no);
|
|
x->gfun.arg_no = n;
|
|
x->gfun.specializers = (cl_object *)cl_alloc_align(sizeof(cl_object)*n, sizeof(cl_object));
|
|
for (i = 0; i < n; i++)
|
|
x->gfun.specializers[i] = OBJNULL;
|
|
x->gfun.instance = Cnil;
|
|
@(return x)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_name(cl_object x)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
@(return x->gfun.name)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_name_set(cl_object x, cl_object name)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
x->gfun.name = name;
|
|
@(return x)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_method_ht(cl_object x)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
@(return x->gfun.method_hash)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_method_ht_set(cl_object x, cl_object y)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
if (type_of(y) != t_hashtable)
|
|
FEwrong_type_argument(@'hash-table', y);
|
|
x->gfun.method_hash = y;
|
|
@(return x)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_spec_how_ref(cl_object x, cl_object y)
|
|
{
|
|
int i;
|
|
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
if (!FIXNUMP(y) ||
|
|
(i = fix(y)) < 0 || i >= x->gfun.arg_no)
|
|
FEerror("~S is an illegal spec_how index.", 1, y);
|
|
@(return x->gfun.specializers[i])
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_spec_how_set(cl_object x, cl_object y, cl_object spec)
|
|
{
|
|
int i;
|
|
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
if (!FIXNUMP(y) || (i = fix(y)) >= x->gfun.arg_no)
|
|
FEerror("~S is an illegal spec_how index.", 1, y);
|
|
x->gfun.specializers[i] = spec;
|
|
@(return spec)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_instance(cl_object x)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
@(return x->gfun.instance)
|
|
}
|
|
|
|
cl_object
|
|
si_gfun_instance_set(cl_object x, cl_object y)
|
|
{
|
|
if (type_of(x) != t_gfun)
|
|
FEwrong_type_argument(@'dispatch-function', x);
|
|
if (type_of(y) != t_instance)
|
|
FEwrong_type_argument(@'instance', y);
|
|
x->gfun.instance = y;
|
|
@(return x)
|
|
}
|
|
|
|
cl_object
|
|
si_gfunp(cl_object x)
|
|
{
|
|
@(return ((type_of(x) == t_gfun)? Ct : Cnil))
|
|
}
|
|
|
|
/*
|
|
* variation of gethash from hash.d, which takes an array of objects as key
|
|
* It also assumes that entries are never removed except by clrhash.
|
|
*/
|
|
|
|
static struct hashtable_entry *
|
|
get_meth_hash(cl_object *keys, int argno, cl_object hashtable)
|
|
{
|
|
int hsize;
|
|
struct hashtable_entry *e, *htable;
|
|
cl_object hkey, tlist;
|
|
register int i = 0;
|
|
int k, n; /* k added by chou */
|
|
bool b = 1;
|
|
|
|
hsize = hashtable->hash.size;
|
|
htable = hashtable->hash.data;
|
|
for (n = 0; n < argno; n++)
|
|
i += (int)keys[n] / 4; /* instead of:
|
|
i += hash_eql(keys[n]);
|
|
i += hash_eql(Cnil);
|
|
*/
|
|
for (i %= hsize, k = 0; k < hsize; i = (i + 1) % hsize, k++) {
|
|
e = &htable[i];
|
|
hkey = e->key;
|
|
if (hkey == OBJNULL)
|
|
return(e);
|
|
for (n = 0, tlist = hkey; b && (n < argno);
|
|
n++, tlist = CDR(tlist))
|
|
b &= (keys[n] == CAR(tlist));
|
|
if (b)
|
|
return(&htable[i]);
|
|
}
|
|
internal_error("get_meth_hash");
|
|
}
|
|
|
|
cl_object
|
|
si_method_ht_get(cl_object keylist, cl_object table)
|
|
{
|
|
struct hashtable_entry *e;
|
|
|
|
{ int i, argn = length(keylist);
|
|
cl_object keys[argn]; /* __GNUC__ */
|
|
|
|
for (i = 0; i < argn; i++, keylist = CDR(keylist))
|
|
keys[i] = CAR(keylist);
|
|
e = get_meth_hash(keys, argn, table);
|
|
}
|
|
@(return ((e->key == OBJNULL)? Cnil : e->value))
|
|
}
|
|
|
|
static void
|
|
set_meth_hash(cl_object *keys, int argno, cl_object hashtable, cl_object value)
|
|
{
|
|
struct hashtable_entry *e;
|
|
cl_object keylist, *p;
|
|
|
|
if (hashtable->hash.entries + 1 >= fix(hashtable->hash.threshold))
|
|
extend_hashtable(hashtable);
|
|
e = get_meth_hash(keys, argno, hashtable);
|
|
if (e->key == OBJNULL)
|
|
hashtable->hash.entries++;
|
|
keylist = Cnil;
|
|
for (p = keys + argno; p > keys; p--) keylist = CONS(p[-1], keylist);
|
|
e->key = keylist;
|
|
e->value = value;
|
|
}
|
|
|
|
cl_object
|
|
compute_method(int narg, cl_object fun, cl_object *args)
|
|
{
|
|
cl_object func;
|
|
|
|
{ int i, spec_no;
|
|
struct hashtable_entry *e;
|
|
cl_object *spec_how = fun->gfun.specializers;
|
|
cl_object argtype[narg]; /* __GNUC__ */
|
|
|
|
if (narg < fun->gfun.arg_no)
|
|
FEerror("Generic function ~S requires more than ~R argument~:p.",
|
|
2, fun->gfun.name, MAKE_FIXNUM(narg));
|
|
for (i = 0, spec_no = 0; i < fun->gfun.arg_no; i++, spec_how++) {
|
|
if (*spec_how != Cnil)
|
|
argtype[spec_no++] = (ATOM(*spec_how) ||
|
|
!member_eq(args[i], *spec_how)) ?
|
|
cl_type_of(args[i]) :
|
|
args[i];
|
|
}
|
|
|
|
e = get_meth_hash(argtype, spec_no, fun->gfun.method_hash);
|
|
|
|
if (e->key == OBJNULL) {
|
|
/* method not cached */
|
|
register cl_object gf = fun->gfun.instance;
|
|
cl_object methods, meth_comb, meth_args, arglist = Cnil;
|
|
|
|
i = narg;
|
|
while (i-- > 0)
|
|
arglist = CONS(args[i], arglist);
|
|
methods = funcall(3, @'si::compute-applicable-methods', gf, arglist);
|
|
meth_comb = funcall(2, @'si::generic-function-method-combination', gf);
|
|
meth_args = funcall(2, @'si::generic-function-method-combination-args', gf);
|
|
func = funcall(5, @'si::compute-effective-method', gf, methods,
|
|
meth_comb, meth_args);
|
|
|
|
/* update cache */
|
|
set_meth_hash(argtype, spec_no, fun->gfun.method_hash, func);
|
|
} else
|
|
/* method is already cached */
|
|
func = e->value;
|
|
}
|
|
return func;
|
|
}
|
|
|
|
cl_object
|
|
si_set_compiled_function_name(cl_object fn, cl_object new_name)
|
|
{
|
|
cl_type t = type_of(fn);
|
|
|
|
if (t == t_cfun)
|
|
@(return (fn->cfun.name = new_name))
|
|
if (t == t_bytecodes)
|
|
@(return (fn->bytecodes.data[0] = new_name))
|
|
FEerror("~S is not a compiled-function.", 1, fn);
|
|
}
|