1 /*
2  * class.c - class metaobject implementation
3  *
4  *   Copyright (c) 2000-2020  Shiro Kawai  <shiro@acm.org>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   3. Neither the name of the authors nor the names of its contributors
18  *      may be used to endorse or promote products derived from this
19  *      software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #define LIBGAUCHE_BODY
35 #include "gauche.h"
36 #include "gauche/class.h"
37 #include "gauche/code.h"
38 #include "gauche/priv/classP.h"
39 #include "gauche/priv/builtin-syms.h"
40 #include "gauche/priv/macroP.h"
41 #include "gauche/priv/pairP.h"
42 #include "gauche/priv/writerP.h"
43 #include "gauche/priv/dispatchP.h"
44 #include "gauche/priv/stringP.h"
45 
46 /* Some routines uses small array on stack to keep data about
47    arguments to dispatch.  If the # of args used for dispach is bigger
48    than this, the routine allocates an array in heap. */
49 #define PREALLOC_SIZE  32
50 
51 
52 /*===================================================================
53  * Built-in classes
54  */
55 
56 static void class_print(ScmObj, ScmPort *, ScmWriteContext*);
57 static void generic_print(ScmObj, ScmPort *, ScmWriteContext*);
58 static void method_print(ScmObj, ScmPort *, ScmWriteContext*);
59 static void next_method_print(ScmObj, ScmPort *, ScmWriteContext*);
60 static void slot_accessor_print(ScmObj, ScmPort *, ScmWriteContext*);
61 static void accessor_method_print(ScmObj, ScmPort *, ScmWriteContext*);
62 
63 static ScmObj class_allocate(ScmClass *klass, ScmObj initargs);
64 static ScmObj generic_allocate(ScmClass *klass, ScmObj initargs);
65 static ScmObj method_allocate(ScmClass *klass, ScmObj initargs);
66 static ScmObj slot_accessor_allocate(ScmClass *klass, ScmObj initargs);
67 static void   initialize_builtin_cpl(ScmClass *klass, ScmObj supers);
68 
69 static ScmObj instance_class_redefinition(ScmObj obj, ScmClass *old);
70 static ScmObj slot_set_using_accessor(ScmObj obj, ScmSlotAccessor *sa,
71                                       ScmObj val);
72 static ScmObj instance_allocate(ScmClass *klass, ScmObj initargs);
73 
74 static ScmObj fallback_compare(ScmObj *, int, ScmGeneric *);
75 
76 static ScmObj builtin_initialize(ScmObj *, int, ScmGeneric *);
77 
78 ScmClass *Scm_DefaultCPL[] = {
79     SCM_CLASS_STATIC_PTR(Scm_TopClass),
80     NULL
81 };
82 
83 ScmClass *Scm_ObjectCPL[] = {
84     SCM_CLASS_STATIC_PTR(Scm_ObjectClass),
85     SCM_CLASS_STATIC_PTR(Scm_TopClass),
86     NULL
87 };
88 
89 static ScmClass *Scm_MethodCPL[] = {
90     SCM_CLASS_STATIC_PTR(Scm_MethodClass),
91     SCM_CLASS_STATIC_PTR(Scm_ObjectClass),
92     SCM_CLASS_STATIC_PTR(Scm_TopClass),
93     NULL
94 };
95 
96 /* Class <top> is the superclass of all classes.  The class initialization
97    routine ensures that the class precedence list always terminates by <top>.
98    Class <bottom> is the subclass of all classes.  It won't appear in the
99    class precedence list, but Scm_SubTypeP treats it specially and answers
100    yes to Scm_SubTypeP(<bottom>, <any-class>). */
101 SCM_DEFINE_ABSTRACT_CLASS(Scm_TopClass, NULL);
102 SCM_DEFINE_ABSTRACT_CLASS(Scm_BottomClass, NULL);
103 
104 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_BoolClass, NULL);
105 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_CharClass, NULL);
106 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_UnknownClass, NULL);
107 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_EOFObjectClass, NULL);
108 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_UndefinedObjectClass, NULL);
109 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_ForeignPointerClass, NULL);
110 
111 SCM_DEFINE_BASE_CLASS(Scm_ObjectClass, ScmInstance,
112                       NULL, NULL, NULL, instance_allocate,
113                       SCM_CLASS_DEFAULT_CPL);
114 
115 /* Basic metaobjects */
116 SCM_DEFINE_BASE_CLASS(Scm_ClassClass, ScmClass,
117                       class_print, NULL, NULL, class_allocate,
118                       SCM_CLASS_OBJECT_CPL);
119 SCM_DEFINE_BASE_CLASS(Scm_GenericClass, ScmGeneric,
120                       generic_print, NULL, NULL, generic_allocate,
121                       SCM_CLASS_OBJECT_CPL);
122 SCM_DEFINE_BASE_CLASS(Scm_MethodClass, ScmMethod,
123                       method_print, NULL, NULL, method_allocate,
124                       SCM_CLASS_OBJECT_CPL);
125 
126 /* Internally used classes */
127 SCM_DEFINE_BUILTIN_CLASS(Scm_SlotAccessorClass,
128                          slot_accessor_print, NULL, NULL,
129                          slot_accessor_allocate,
130                          SCM_CLASS_DEFAULT_CPL);
131 SCM_DEFINE_BUILTIN_CLASS(Scm_AccessorMethodClass,
132                          accessor_method_print, NULL, NULL,
133                          method_allocate,
134                          Scm_MethodCPL);
135 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_NextMethodClass, next_method_print);
136 
137 /* Builtin generic functions */
138 SCM_DEFINE_GENERIC(Scm_GenericMake, Scm_NoNextMethod, NULL);
139 SCM_DEFINE_GENERIC(Scm_GenericAllocate, Scm_NoNextMethod, NULL);
140 SCM_DEFINE_GENERIC(Scm_GenericInitialize, builtin_initialize, NULL);
141 SCM_DEFINE_GENERIC(Scm_GenericAddMethod, Scm_NoNextMethod, NULL);
142 SCM_DEFINE_GENERIC(Scm_GenericDeleteMethod, Scm_NoNextMethod, NULL);
143 SCM_DEFINE_GENERIC(Scm_GenericComputeCPL, Scm_NoNextMethod, NULL);
144 SCM_DEFINE_GENERIC(Scm_GenericComputeSlots, Scm_NoNextMethod, NULL);
145 SCM_DEFINE_GENERIC(Scm_GenericComputeGetNSet, Scm_NoNextMethod, NULL);
146 SCM_DEFINE_GENERIC(Scm_GenericComputeApplicableMethods, Scm_NoNextMethod, NULL);
147 SCM_DEFINE_GENERIC(Scm_GenericUpdateDirectMethod, Scm_NoNextMethod, NULL);
148 SCM_DEFINE_GENERIC(Scm_GenericApplyGeneric, Scm_NoNextMethod, NULL);
149 SCM_DEFINE_GENERIC(Scm_GenericMethodMoreSpecificP, Scm_NoNextMethod, NULL);
150 SCM_DEFINE_GENERIC(Scm_GenericSlotMissing, Scm_NoNextMethod, NULL);
151 SCM_DEFINE_GENERIC(Scm_GenericSlotUnbound, Scm_NoNextMethod, NULL);
152 SCM_DEFINE_GENERIC(Scm_GenericSlotRefUsingClass, Scm_NoNextMethod, NULL);
153 SCM_DEFINE_GENERIC(Scm_GenericSlotSetUsingClass, Scm_NoNextMethod, NULL);
154 SCM_DEFINE_GENERIC(Scm_GenericSlotBoundUsingClassP, Scm_NoNextMethod, NULL);
155 SCM_DEFINE_GENERIC(Scm_GenericObjectEqualP, fallback_compare, NULL);
156 SCM_DEFINE_GENERIC(Scm_GenericObjectCompare, fallback_compare, NULL);
157 SCM_DEFINE_GENERIC(Scm_GenericObjectHash, Scm_NoNextMethod, NULL);
158 SCM_DEFINE_GENERIC(Scm_GenericObjectApply, Scm_InvalidApply, NULL);
159 SCM_DEFINE_GENERIC(Scm_GenericObjectSetter, Scm_InvalidApply, NULL);
160 SCM_DEFINE_GENERIC(Scm_GenericChangeClass, Scm_NoNextMethod, NULL);
161 
162 /* Some frequently-used pointers */
163 static ScmObj key_allocation     = SCM_FALSE;
164 static ScmObj key_slot_accessor  = SCM_FALSE;
165 static ScmObj key_builtin        = SCM_FALSE;
166 static ScmObj key_name           = SCM_FALSE;
167 static ScmObj key_lambda_list    = SCM_FALSE;
168 static ScmObj key_method_locked  = SCM_FALSE;
169 static ScmObj key_generic        = SCM_FALSE;
170 static ScmObj key_specializers   = SCM_FALSE;
171 static ScmObj key_body           = SCM_FALSE;
172 
173 /* TRANSIENT: Global flag to disable generic dispatcher.  This is an escape
174    pod to fall back the default mechanism when we find a serious bug in
175    dispatch accelerator.  Can be turned on with a environment variable. */
176 static int disable_generic_dispatcher = FALSE;
177 
178 /* A global lock to serialize class redefinition.  We need it since
179    class redefinition is not a local effect---it propagates through
180    its subclasses.  So it is pretty difficult to guarantee consistency
181    if two threads enter the class redefinition, even if they redefine
182    different classes.
183    This lock works as a recursive lock.  Scm_StartClassRedefinition
184    increments the lock count, and Scm_CommitClassRedefinition decrements it.
185 */
186 static struct {
187     ScmVM             *owner;   /* thread that grabs the lock, or NULL */
188     int               count;
189     ScmInternalMutex  mutex;
190     ScmInternalCond   cv;
191 } class_redefinition_lock;
192 
193 /* Imporant slots in <class> metaboject can be modified only when the
194    class is in 'malleable' state.   Here's the check. */
195 #define CHECK_MALLEABLE(k, who)                         \
196     if (!SCM_CLASS_MALLEABLE_P(k)) {                    \
197         Scm_Error("%s: class is not malleable: %S",     \
198                   who, SCM_OBJ(k));                     \
199     }
200 
201 /*=====================================================================
202  * Auxiliary utilities
203  */
204 
class_list_to_array(ScmObj classes,int len)205 static ScmClass **class_list_to_array(ScmObj classes, int len)
206 {
207     ScmObj cp;
208     ScmClass **v, **vp;
209     v = vp = SCM_NEW_ARRAY(ScmClass*, len+1);
210     SCM_FOR_EACH(cp, classes) {
211         if (!Scm_TypeP(SCM_CAR(cp), SCM_CLASS_CLASS))
212             Scm_Error("list of classes required, but found non-class object"
213                       " %S in %S", SCM_CAR(cp), classes);
214         *vp++ = SCM_CLASS(SCM_CAR(cp));
215     }
216     *vp = NULL;
217     return v;
218 }
219 
class_array_to_list(ScmClass ** array,int len)220 static ScmObj class_array_to_list(ScmClass **array, int len)
221 {
222     ScmObj h = SCM_NIL, t = SCM_NIL;
223     if (array) while (len-- > 0) SCM_APPEND1(h, t, SCM_OBJ(*array++));
224     return h;
225 }
226 
class_array_to_names(ScmClass ** array,int len)227 static ScmObj class_array_to_names(ScmClass **array, int len)
228 {
229     ScmObj h = SCM_NIL, t = SCM_NIL;
230     for (int i=0; i<len; i++, array++) SCM_APPEND1(h, t, (*array)->name);
231     return h;
232 }
233 
234 /* If the class name has brackets '<' and '>', as in Gauche's convention,
235    returns a string without those brackets.  Otherwise returns the class
236    name in a string.  This is used by some print method.  Always returns
237    a string. */
Scm_ShortClassName(ScmClass * klass)238 ScmObj Scm_ShortClassName(ScmClass *klass)
239 {
240     ScmObj name = klass->name;
241 
242     if (SCM_SYMBOLP(name)) {
243         const ScmStringBody *b = SCM_STRING_BODY(SCM_SYMBOL_NAME(name));
244         int size;
245         if (((size = SCM_STRING_BODY_SIZE(b)) > 2)
246             && SCM_STRING_BODY_START(b)[0] == '<'
247             && SCM_STRING_BODY_START(b)[size-1] == '>') {
248             return Scm_Substring(SCM_SYMBOL_NAME(name), 1,
249                                  SCM_STRING_BODY_LENGTH(b)-1, FALSE);
250         } else {
251             return SCM_OBJ(SCM_SYMBOL_NAME(name));
252         }
253     }
254     /* Fallback.  At this moment we don't have unnamed classes,
255        so this is an ad hoc code.  We may need better handling
256        (like write-to-string) later. */
257     return SCM_MAKE_STR("(unnamed class)");
258 }
259 
260 #if GAUCHE_API_VERSION < 1000
261 /* TRANSIENT: For the backward compatibility.  Remove this on 1.0. */
Scm__InternalClassName(ScmClass * klass)262 ScmObj Scm__InternalClassName(ScmClass *klass)
263 {
264     return Scm_ShortClassName(klass);
265 }
266 #endif /*GAUCHE_API_VERSION < 1000*/
267 
268 /*=====================================================================
269  * Class metaobject
270  */
271 
272 /* One of the design goals of Gauche object system is to make Scheme-defined
273  * class easily accessible from C code, and vice versa.
274  *
275  * Class is implemented in two layers; Scheme layer and C layer.  The two
276  * layers work together to realize efficient MOP.   In the following
277  * description, (FOOBAR baz) indicates Scheme call where FooBar(baz) indicates
278  * C call.
279  *
280  * Class instantiation is handled as follows.
281  *
282  *  (MAKE class . initargs)
283  *    If class is a descendant of <class> eventually this calls
284  *    a method (MAKE <class> . initargs).
285  *
286  *  (MAKE <class> . initargs)
287  *    Defined in lib/gauche/object.scm.  This calls
288  *    (ALLOCATE-INSTANCE <class> initargs), then
289  *    (INITIALIZE obj initargs).
290  *
291  *  (ALLOCATE-INSTANCE <class> <list>)
292  *    This is a C-defined method, and calls allocate() below.
293  *
294  *  static ScmObj allocate(ScmNextMethod *, ScmObj *, int, void*)
295  *    The default allocation dispatcher.   This calls class->allocate().
296  *    Some builtin function doesn't allow instantiation from Scheme and
297  *    sets class->allocate() to NULL; an error is raised in such case.
298  *
299  *    The class->allocate() function usually allocates the instance
300  *    (Scm*** structure) and initializes its slots with reasonable values.
301  *    For example, if class is <class>, class->allocate allocates
302  *    ScmClass structure.  If the class allows subclassing, class->allocate
303  *    must allocate extra storage for as many slots as class->numInstanceSlots.
304  *
305  *    The allocated and set up structure is returned as ScmObj, which
306  *    eventually retured by (ALLOCATE-INSTANCE ...) method, and passed to
307  *    (INITIALIZE obj initargs) structure.
308  *
309  *  (INITIALIZE obj initargs)
310  *    In most cases this method is defined in Scheme, if ever defined.
311  *    The Scheme method does whatever it want, but it must call
312  *    (NEXT-METHOD) in it, and it eventually calls the C-defined fallback
313  *    method buildin_initialize().
314  *
315  *  ScmObj builtin_initialize(ScmObj *, int, ScmGeneric*)
316  *    This function traverses the slot accessors, and if the slot has
317  *    not been initialized, initialize it as specified in initargs or
318  *    slot options.
319  */
320 
321 /* Defining builtin class in C.
322  *
323  *    Defining classes in C is divided in two steps.  First, you have to
324  *    define the static part of the class; it is done by one of the
325  *    SCM_DEFINE_***_CLASS macros provided in gauche.h, and it defines
326  *    static instance of ScmClass structure.  Then, in the initialization
327  *    phase, you have to call Scm_InitStaticClass to initialize the dynamic
328  *    part of the structure.
329  *
330  *      void Scm_InitStaticClass(ScmClass *klass, const char *name,
331  *                               ScmModule *mod,
332  *                               ScmClassStaticSlotSpec *slots,
333  *                               int flags)
334  *
335  *         This function fills the ScmClass structure that can't be
336  *         defined statically, and inserts the binding from the named
337  *         symbol to the class object in the specified module.
338  *         The 'flags' arg is reserved for future use, and must be 0
339  *         for the time being.
340  *
341  *    See comments in gauche.h (around "Class categories") about
342  *    the categories of C-defined classes.
343  */
344 
345 /*
346  * Built-in protocols
347  *
348  *  ScmObj klass->allocate(ScmClass *klass, ScmObj initargs)
349  *     Called at the bottom of the chain of allocate-instance method.
350  *     Besides allocating the required space, it must initialize
351  *     members of the C-specific part of the instance, including SCM_HEADER.
352  *     This protocol can be NULL for core base classes; if so, attempt
353  *     to "make" such class reports an error.
354  *
355  *  void klass->print(ScmObj obj, ScmPort *sink, ScmWriteContext *ctx)
356  *     OBJ is an instance of klass (you can safely assume it).  This
357  *     function should print OBJ into SINK.  See write.c about the
358  *     details of the context.
359  *     If this function pointer is not set, a default print method
360  *     is used.
361  *
362  *  int klass->compare(ScmObj x, ScmObj y, int equalp)
363  *     X and Y are instances of klass.  If equalp is FALSE,
364  *     return -1, 0, or 1, when X < Y, X == Y or X > Y, respectively.
365  *     In case if klass is not orderable, it can signal an error.
366  *     If equalp is TRUE, just test the equality: return -1 if X != Y
367  *     and 0 if X == Y.
368  *
369  *  int klass->serialize(ScmObj obj, ScmPort *sink, ScmObj table)
370  *     OBJ is an instance of klass.  This method is only called when OBJ
371  *     has not been output in the current serializing session.
372  */
373 
374 /* A note on the 'data' member of ScmClass
375  *
376  *   It can be used to hang an opaque data to a specific class.  So far,
377  *   we use it only for <simple> class mechanism.  Its use is highly
378  *   controversial; I mean, The Right Thing is to define a metaclass
379  *   which defines an extra member, and allocate <simple> class as an
380  *   instance of it.  However, creating metaclass from C is messy now,
381  *   so I chose to hack.  In future we may have a nice C API to create
382  *   a metaclass, and then we may remove this 'data' member.  So DO NOT
383  *   RELY ON ITS EXISTENCE.
384  */
385 
386 /*
387  * Class metaobject protocol implementation
388  */
389 
390 /* Allocate class structure.  klass is a metaclass. */
class_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)391 static ScmObj class_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
392 {
393     ScmClass *instance = SCM_NEW_INSTANCE(ScmClass, klass);
394     instance->allocate = NULL;  /* will be set when CPL is set */
395     instance->print = NULL;
396     instance->compare = Scm_ObjectCompare;
397     instance->hash = NULL;
398     instance->cpa = NULL;
399     instance->numInstanceSlots = 0; /* will be adjusted in class init */
400     instance->coreSize = 0;     /* will be set when CPL is set */
401     instance->flags = SCM_CLASS_SCHEME|SCM_CLASS_MALLEABLE; /* default */
402     instance->name = SCM_FALSE;
403     instance->directSupers = SCM_NIL;
404     instance->accessors = SCM_NIL;
405     instance->cpl = SCM_NIL;
406     instance->directSlots = SCM_NIL;
407     instance->slots = SCM_NIL;
408     instance->directSubclasses = SCM_NIL;
409     instance->directMethods = SCM_NIL;
410     instance->initargs = SCM_NIL;
411     instance->modules = SCM_NIL;
412     instance->redefined = SCM_FALSE;
413     (void)SCM_INTERNAL_MUTEX_INIT(instance->mutex);
414     (void)SCM_INTERNAL_COND_INIT(instance->cv);
415     instance->data = NULL;      /* see the above note on the 'data' member */
416     return SCM_OBJ(instance);
417 }
418 
class_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)419 static void class_print(ScmObj obj, ScmPort *port,
420                         ScmWriteContext *ctx SCM_UNUSED)
421 {
422     Scm_Printf(port, "#<class %A%s>",
423                SCM_CLASS(obj)->name,
424                (SCM_FALSEP(SCM_CLASS(obj)->redefined)? "" : " (redefined)"));
425 }
426 
427 /*
428  * (make <class> ...)   - default method to make a class instance.
429  */
430 
431 /* defined in Scheme */
432 
433 /*
434  * (allocate-instance <class> initargs)
435  */
Scm_Allocate(ScmClass * c,ScmObj initargs)436 ScmObj Scm_Allocate(ScmClass *c, ScmObj initargs)
437 {
438     if (c->allocate == NULL) {
439         Scm_Error("built-in class can't be allocated via allocate-instance: %S",
440                   SCM_OBJ(c));
441     }
442     return c->allocate(c, initargs);
443 }
444 
allocate(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * d SCM_UNUSED)445 static ScmObj allocate(ScmNextMethod *nm SCM_UNUSED,
446                        ScmObj *argv,
447                        int argc SCM_UNUSED,
448                        void *d SCM_UNUSED)
449 {
450     return Scm_Allocate(SCM_CLASS(argv[0]), argv[1]);
451 }
452 
453 static ScmClass *class_allocate_SPEC[] = {
454     SCM_CLASS_STATIC_PTR(Scm_ClassClass), SCM_CLASS_STATIC_PTR(Scm_ListClass)
455 };
456 static SCM_DEFINE_METHOD(class_allocate_rec, &Scm_GenericAllocate,
457                          2, 0, class_allocate_SPEC, allocate, NULL);
458 
459 /*
460  * (compute-cpl <class>)
461  */
class_compute_cpl(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * d SCM_UNUSED)462 static ScmObj class_compute_cpl(ScmNextMethod *nm SCM_UNUSED,
463                                 ScmObj *argv,
464                                 int argc SCM_UNUSED,
465                                 void *d SCM_UNUSED)
466 {
467     ScmClass *c = SCM_CLASS(argv[0]);
468     return Scm_ComputeCPL(c);
469 }
470 
471 static ScmClass *class_compute_cpl_SPEC[] = {
472     SCM_CLASS_STATIC_PTR(Scm_ClassClass)
473 };
474 static SCM_DEFINE_METHOD(class_compute_cpl_rec, &Scm_GenericComputeCPL,
475                          1, 0, class_compute_cpl_SPEC,
476                          class_compute_cpl, NULL);
477 
478 /*
479  * (class-of obj)
480  */
481 
Scm_ClassOf(ScmObj obj)482 ScmClass *Scm_ClassOf(ScmObj obj)
483 {
484     if (!SCM_PTRP(obj)) {
485         if (SCM_TRUEP(obj) || SCM_FALSEP(obj)) return SCM_CLASS_BOOL;
486         if (SCM_NULLP(obj)) return SCM_CLASS_NULL;
487         if (SCM_CHARP(obj)) return SCM_CLASS_CHAR;
488         if (SCM_INTP(obj))  return SCM_CLASS_INTEGER;
489         if (SCM_EOFP(obj))  return SCM_CLASS_EOF_OBJECT;
490         if (SCM_UNDEFINEDP(obj)) return SCM_CLASS_UNDEFINED_OBJECT;
491         if (SCM_STRING_CURSOR_SMALL_P(obj)) return SCM_CLASS_STRING_CURSOR;
492         else return SCM_CLASS_UNKNOWN;
493     }
494     if (SCM_FLONUMP(obj)) return SCM_CLASS_REAL;
495     /* check lazy pair first, so that we won't trigger forcing. */
496     if (SCM_LAZY_PAIR_P(obj)) return SCM_CLASS_PAIR;
497     if (SCM_PAIRP(obj)) {
498         ScmExtendedPairDescriptor *d = Scm__GetExtendedPairDescriptor(obj);
499         if (d) {
500             return d->klass;
501         } else {
502             return SCM_CLASS_PAIR;
503         }
504     }
505 
506     return SCM_CLASS_OF(obj);
507 }
508 
509 /* Returns the pointer of the first base class found in the given
510    class's CPA.  If the class is pure abstract or builtin, NULL is
511    returned. */
Scm_BaseClassOf(ScmClass * klass)512 ScmClass *Scm_BaseClassOf(ScmClass *klass)
513 {
514     ScmClass **cp = klass->cpa;
515     ScmClass *k;
516     while ((k = *cp++) != NULL) {
517         if (SCM_CLASS_CATEGORY(k) == SCM_CLASS_BASE) return k;
518     }
519     return NULL;
520 }
521 
522 /*
523  * (class-of obj class)
524  *   - if obj's class is redefined, first updates obj.
525  */
class_of_cc(ScmObj result,void ** data SCM_UNUSED)526 ScmObj class_of_cc(ScmObj result, void **data SCM_UNUSED)
527 {
528     return Scm_VMClassOf(result);
529 }
530 
Scm_VMClassOf(ScmObj obj)531 ScmObj Scm_VMClassOf(ScmObj obj)
532 {
533     ScmClass *k = Scm_ClassOf(obj);
534     if (!SCM_FALSEP(k->redefined)) {
535         Scm_VMPushCC(class_of_cc, NULL, 0);
536         return instance_class_redefinition(obj, k);
537     }
538     return SCM_OBJ(k);
539 }
540 
541 /*
542  * (is-a? obj class)
543  *   - if obj's class is redefined, first updates obj.
544  */
is_a_cc(ScmObj result SCM_UNUSED,void ** data)545 ScmObj is_a_cc(ScmObj result SCM_UNUSED, void **data)
546 {
547     return Scm_VMIsA(SCM_OBJ(data[0]), SCM_CLASS(data[1]));
548 }
549 
Scm_VMIsA(ScmObj obj,ScmClass * klass)550 ScmObj Scm_VMIsA(ScmObj obj, ScmClass *klass)
551 {
552     ScmClass *k = Scm_ClassOf(obj);
553     if (!SCM_FALSEP(k->redefined)) {
554         void *data[2];
555         data[0] = obj;
556         data[1] = klass;
557         Scm_VMPushCC(is_a_cc, data, 2);
558         return instance_class_redefinition(obj, k);
559     }
560     return SCM_MAKE_BOOL(Scm_TypeP(obj, klass));
561 }
562 
563 /*
564  * Setting/resetting SCM_CLASS_MALLEABLE flag
565  */
Scm_ClassMalleableSet(ScmClass * klass,int flag)566 void Scm_ClassMalleableSet(ScmClass *klass, int flag)
567 {
568     if (SCM_CLASS_CATEGORY(klass) != SCM_CLASS_SCHEME) {
569         Scm_Error("You cannot modify malleable flag of a class not defined in Scheme: %S", SCM_OBJ(klass));
570     }
571     if (flag) {
572         klass->flags |= SCM_CLASS_MALLEABLE;
573     } else {
574         klass->flags &= ~SCM_CLASS_MALLEABLE;
575     }
576 }
577 
578 /*--------------------------------------------------------------
579  * Metainformation accessors
580  */
581 
582 /* TODO: disable modification of system-builtin classes */
583 
class_name(ScmClass * klass)584 static ScmObj class_name(ScmClass *klass)
585 {
586     return klass->name;
587 }
588 
class_name_set(ScmClass * klass,ScmObj val)589 static void class_name_set(ScmClass *klass, ScmObj val)
590 {
591     CHECK_MALLEABLE(klass, "(setter name)");
592     klass->name = val;
593 }
594 
class_cpl(ScmClass * klass)595 static ScmObj class_cpl(ScmClass *klass)
596 {
597     return klass->cpl;
598 }
599 
600 /* Subroutine for class_cpl_set.  Scans KLASS's CPL and find out the
601    suitable allocator function, C-struct core size, and some flags.
602    If KLASS inherits more than one C-defined classes (BASEs), they must
603    form a single inheritance chain. */
find_core_allocator(ScmClass * klass)604 static void find_core_allocator(ScmClass *klass)
605 {
606     ScmClass *b = NULL; /* the base class klass gets the allocate func */
607     int object_inherited = FALSE;
608 
609     klass->allocate = NULL;
610     for (ScmClass **p = klass->cpa; *p; p++) {
611         if (SCM_CLASS_CATEGORY(*p) == SCM_CLASS_BUILTIN) {
612             Scm_Error("class '%S' attempted to inherit from a builtin class "
613                       "%S; you cannot subclass a builtin class.",
614                       klass->name, *p);
615         }
616 
617         if ((*p)->allocate == instance_allocate) {
618             /* Check if we certainly inherited <object> */
619             object_inherited = TRUE;
620             continue;
621         }
622 
623         if ((*p)->flags & SCM_CLASS_APPLICABLE) {
624             klass->flags |= SCM_CLASS_APPLICABLE;
625         }
626 
627         if (b
628             && SCM_CLASS_CATEGORY(*p) == SCM_CLASS_BASE
629             && b->allocate != (*p)->allocate) {
630             /* found different C-defined class.  check to see if (*p) is
631                superclass of b.  If not, the single-inheritance rule is
632                violated. */
633             ScmClass **bp = b->cpa;
634             for (; *bp; bp++) {
635                 if (*bp == *p) break;
636             }
637             if (!*bp) {
638                 Scm_Error("class '%S' attempted to inherit multiple C-defined "
639                           "base class (%S and %S) which are not in a "
640                           "superclass-subclass relathionship.",
641                           klass->name, b, *p);
642             }
643             continue;
644         }
645         if (!b) {
646             /* Here we found the closest C-base class.  Get the allocator
647                from it. */
648             b = *p;
649             klass->allocate = b->allocate;
650             klass->coreSize = b->coreSize;
651         }
652     }
653 
654     if (!object_inherited) {
655         Scm_Error("class %S's precedence list doesn't have a base class: %S",
656                   klass->name, klass->cpl);
657     }
658     if (!klass->allocate) {
659         klass->allocate = instance_allocate;
660         klass->coreSize = sizeof(ScmInstance);
661     }
662 }
663 
class_cpl_set(ScmClass * klass,ScmObj val)664 static void class_cpl_set(ScmClass *klass, ScmObj val)
665 {
666     CHECK_MALLEABLE(klass, "(setter cpl)");
667 
668     /* We make sure things are consistent. */
669     if (!SCM_PAIRP(val)) goto err;
670     /* check if the CPL begins with the class itself. */
671     if (SCM_CAR(val) != SCM_OBJ(klass)) goto err;
672 
673     /* set up the cpa */
674     ScmObj cp = SCM_CDR(val);
675     int len = Scm_Length(cp);
676     if (len < 0) goto err;
677     klass->cpa = class_list_to_array(cp, len);
678     for (int i=0; i<len; i++) {
679         /* sanity check */
680         if (klass->cpa[i] == SCM_CLASS_BOTTOM) goto err;
681     }
682     if (klass->cpa[len-1] != SCM_CLASS_TOP) goto err;
683     klass->cpl = Scm_CopyList(val);
684     /* find correct allocation method */
685     find_core_allocator(klass);
686     return;
687   err:
688     Scm_Error("class precedence list must be a proper list of class "
689               "metaobject, beginning from the class itself owing the list, "
690               "and ending by the class <top>, and must not include <bottom>: "
691               "%S", val);
692 }
693 
class_direct_supers(ScmClass * klass)694 static ScmObj class_direct_supers(ScmClass *klass)
695 {
696     return klass->directSupers;
697 }
698 
class_direct_supers_set(ScmClass * klass,ScmObj val)699 static void class_direct_supers_set(ScmClass *klass, ScmObj val)
700 {
701     CHECK_MALLEABLE(klass, "(setter direct-supers)")
702     ScmObj vp;
703     SCM_FOR_EACH(vp, val) {
704         if (!Scm_TypeP(SCM_CAR(vp), SCM_CLASS_CLASS))
705             Scm_Error("non-class object found in direct superclass list: %S",
706                       SCM_CAR(vp));
707     }
708     klass->directSupers = val;
709 }
710 
class_direct_slots(ScmClass * klass)711 static ScmObj class_direct_slots(ScmClass *klass)
712 {
713     return klass->directSlots;
714 }
715 
class_direct_slots_set(ScmClass * klass,ScmObj val)716 static void class_direct_slots_set(ScmClass *klass, ScmObj val)
717 {
718     CHECK_MALLEABLE(klass, "(setter direct-slots)");
719     ScmObj vp;
720     SCM_FOR_EACH(vp, val) {
721         if (!SCM_PAIRP(SCM_CAR(vp)))
722             Scm_Error("bad slot spec found in direct slot list: %S",
723                       SCM_CAR(vp));
724     }
725     klass->directSlots = val;
726 }
727 
class_slots_ref(ScmClass * klass)728 static ScmObj class_slots_ref(ScmClass *klass)
729 {
730     return klass->slots;
731 }
732 
class_slots_set(ScmClass * klass,ScmObj val)733 static void class_slots_set(ScmClass *klass, ScmObj val)
734 {
735     CHECK_MALLEABLE(klass, "(setter slots)");
736     ScmObj vp;
737     SCM_FOR_EACH(vp, val) {
738         if (!SCM_PAIRP(SCM_CAR(vp)))
739             Scm_Error("bad slot spec found in slot list: %S",
740                       SCM_CAR(vp));
741     }
742     klass->slots = val;
743 }
744 
class_accessors(ScmClass * klass)745 static ScmObj class_accessors(ScmClass *klass)
746 {
747     return klass->accessors;
748 }
749 
class_accessors_set(ScmClass * klass,ScmObj val)750 static void class_accessors_set(ScmClass *klass, ScmObj val)
751 {
752     CHECK_MALLEABLE(klass, "(setter accessors)");
753     ScmObj vp;
754     SCM_FOR_EACH(vp, val) {
755         if (!SCM_PAIRP(SCM_CAR(vp))
756             || !SCM_SLOT_ACCESSOR_P(SCM_CDAR(vp)))
757             Scm_Error("slot accessor list must be an assoc-list of slot name and slot accessor object, but found: %S",
758                       SCM_CAR(vp));
759     }
760     klass->accessors = val;
761 }
762 
class_numislots(ScmClass * klass)763 static ScmObj class_numislots(ScmClass *klass)
764 {
765     return Scm_MakeInteger(klass->numInstanceSlots);
766 }
767 
class_numislots_set(ScmClass * klass,ScmObj snf)768 static void class_numislots_set(ScmClass *klass, ScmObj snf)
769 {
770     CHECK_MALLEABLE(klass, "(setter num-instance-slots)");
771     int nf = 0;
772     if (!SCM_INTP(snf) || (nf = SCM_INT_VALUE(snf)) < 0) {
773         Scm_Error("invalid argument: %S", snf);
774         /*NOTREACHED*/
775     }
776     klass->numInstanceSlots = nf;
777 }
778 
class_category(ScmClass * klass)779 static ScmObj class_category(ScmClass *klass)
780 {
781     switch (SCM_CLASS_CATEGORY(klass)) {
782     case SCM_CLASS_BUILTIN:  return SCM_SYM_BUILTIN;
783     case SCM_CLASS_ABSTRACT: return SCM_SYM_ABSTRACT;
784     case SCM_CLASS_BASE:     return SCM_SYM_BASE;
785     default:                 return SCM_SYM_SCHEME;
786     }
787 }
788 
class_initargs(ScmClass * klass)789 static ScmObj class_initargs(ScmClass *klass)
790 {
791     return klass->initargs;
792 }
793 
class_initargs_set(ScmClass * klass,ScmObj val)794 static void class_initargs_set(ScmClass *klass, ScmObj val)
795 {
796     CHECK_MALLEABLE(klass, "(setter initargs)");
797     int len = Scm_Length(val);
798     if (len < 0 || len%2 != 0) {
799         Scm_Error("class-initargs must be a list of even number of elements, but got %S", val);
800     }
801     klass->initargs = val;
802 }
803 
class_defined_modules(ScmClass * klass)804 static ScmObj class_defined_modules(ScmClass *klass)
805 {
806     return klass->modules;
807 }
808 
class_defined_modules_set(ScmClass * klass,ScmObj val)809 static void class_defined_modules_set(ScmClass *klass, ScmObj val)
810 {
811     CHECK_MALLEABLE(klass, "(setter defined-modules)");
812     ScmObj cp;
813     SCM_FOR_EACH(cp, val) {
814         if (!SCM_MODULEP(SCM_CAR(cp))) goto err;
815     }
816     if (!SCM_NULLP(cp)) goto err;
817     klass->modules = val;
818     return;
819   err:
820     Scm_Error("list of modules required, bot got %S", val);
821 }
822 
823 /*
824  * The following slots should only be modified by a special MT-safe procedures.
825  */
class_direct_subclasses(ScmClass * klass)826 static ScmObj class_direct_subclasses(ScmClass *klass)
827 {
828     return klass->directSubclasses;
829 }
830 
class_direct_methods(ScmClass * klass)831 static ScmObj class_direct_methods(ScmClass *klass)
832 {
833     return klass->directMethods;
834 }
835 
class_redefined(ScmClass * klass)836 static ScmObj class_redefined(ScmClass *klass)
837 {
838     int abandoned = FALSE;
839 
840     /* If this class is being redefined by other thread, you should wait */
841     (void)SCM_INTERNAL_MUTEX_LOCK(klass->mutex);
842     while (SCM_VMP(klass->redefined)) {
843         if (SCM_VM(klass->redefined)->state == SCM_VM_TERMINATED) {
844             /* TODO: this means redefinition of klass has been abandoned,
845                so the state of klass may be inconsistent.  Should we do
846                something to it? */
847             abandoned = TRUE;
848             klass->redefined = SCM_FALSE;
849         } else {
850             (void)SCM_INTERNAL_COND_WAIT(klass->cv, klass->mutex);
851         }
852     }
853     ScmObj r = klass->redefined;
854     (void)SCM_INTERNAL_MUTEX_UNLOCK(klass->mutex);
855     if (abandoned) {
856         Scm_Warn("redefinition of class %S has been abandoned", klass);
857     }
858     return r;
859 }
860 
861 /*--------------------------------------------------------------
862  * Implicit metaclass
863  */
864 /* This function does the equivalent to
865  *  (make <class> :name NAME :supers (list <class>))
866  */
867 
make_implicit_meta(const char * name,ScmClass ** cpa,ScmModule * mod)868 static ScmClass *make_implicit_meta(const char *name,
869                                     ScmClass **cpa,
870                                     ScmModule *mod)
871 {
872     ScmClass *meta = (ScmClass*)class_allocate(SCM_CLASS_CLASS, SCM_NIL);
873     ScmObj s = SCM_INTERN(name);
874     static ScmClass *metacpa[] = { SCM_CLASS_CLASS, SCM_CLASS_OBJECT, SCM_CLASS_TOP, NULL };
875     ScmClass **metas = metacpa;
876 
877     /* check to see if parent class has also metaclass, and if so,
878        adds it to the CPA.  We know all the builtin classes use
879        single inheritance, so the CPA calculation should be straightforward.
880        Note that this assumes the parent classes are already initialized.
881     */
882     {
883         ScmClass **parent;
884         int numExtraMetas = 0, i;
885         for (parent = cpa; *parent; parent++) {
886             if (SCM_CLASS_OF(*parent) != SCM_CLASS_CLASS) {
887                 numExtraMetas++;
888             }
889         }
890         if (numExtraMetas) {
891             metas = SCM_NEW_ARRAY(ScmClass*, numExtraMetas+4);
892             for (i = 0, parent = cpa; *parent; parent++) {
893                 if (SCM_CLASS_OF(*parent) != SCM_CLASS_CLASS) {
894                     metas[i++] = SCM_CLASS_OF(*parent);
895                 }
896             }
897             metas[i++] = SCM_CLASS_CLASS;
898             metas[i++] = SCM_CLASS_OBJECT;
899             metas[i++] = SCM_CLASS_TOP;
900             metas[i] = NULL;
901         }
902     }
903 
904     meta->name = s;
905     meta->allocate = class_allocate;
906     meta->print = class_print;
907     meta->cpa = metas;
908     meta->flags = SCM_CLASS_ABSTRACT;
909     initialize_builtin_cpl(meta, SCM_FALSE);
910     Scm_Define(mod, SCM_SYMBOL(s), SCM_OBJ(meta));
911     meta->slots = Scm_ClassClass.slots;
912     meta->accessors = Scm_ClassClass.accessors;
913     return meta;
914 }
915 
916 /*--------------------------------------------------------------
917  * External interface
918  */
919 
Scm_SubtypeP(ScmClass * sub,ScmClass * type)920 int Scm_SubtypeP(ScmClass *sub, ScmClass *type)
921 {
922     if (sub == type) return TRUE;
923     if (sub == SCM_CLASS_BOTTOM) return TRUE;
924 
925     ScmClass **p = sub->cpa;
926     while (*p) {
927         if (*p++ == type) return TRUE;
928     }
929     return FALSE;
930 }
931 
Scm_TypeP(ScmObj obj,ScmClass * type)932 int Scm_TypeP(ScmObj obj, ScmClass *type)
933 {
934     return Scm_SubtypeP(Scm_ClassOf(obj), type);
935 }
936 
937 /*
938  * compute-cpl
939  */
Scm_ComputeCPL(ScmClass * klass)940 ScmObj Scm_ComputeCPL(ScmClass *klass)
941 {
942     ScmObj seqh = SCM_NIL, seqt = SCM_NIL;
943 
944     /* a trick to ensure we have <object> <top> at the end of CPL. */
945     ScmObj ds = Scm_Delete(SCM_OBJ(SCM_CLASS_OBJECT), klass->directSupers,
946                            SCM_CMP_EQ);
947     ds = Scm_Delete(SCM_OBJ(SCM_CLASS_TOP), ds, SCM_CMP_EQ);
948     ds = Scm_Append2(ds, SCM_LIST1(SCM_OBJ(SCM_CLASS_OBJECT)));
949 
950     ScmObj dp;
951     SCM_FOR_EACH(dp, klass->directSupers) {
952         if (!Scm_TypeP(SCM_CAR(dp), SCM_CLASS_CLASS))
953             Scm_Error("non-class found in direct superclass list: %S",
954                       klass->directSupers);
955         if (SCM_CAR(dp) == SCM_OBJ(SCM_CLASS_OBJECT)
956             || SCM_CAR(dp) == SCM_OBJ(SCM_CLASS_TOP))
957             continue;
958         SCM_APPEND1(seqh, seqt, SCM_CLASS(SCM_CAR(dp))->cpl);
959     }
960     SCM_APPEND1(seqh, seqt, Scm_ObjectClass.cpl);
961 
962     SCM_APPEND1(seqh, seqt, ds);
963 
964     ScmObj result = Scm_MonotonicMerge1(seqh);
965     if (SCM_FALSEP(result))
966         Scm_Error("discrepancy found in class precedence lists of the superclasses: %S",
967                   klass->directSupers);
968     return Scm_Cons(SCM_OBJ(klass), result);
969 }
970 
971 /*
972  * Internal procedures for class redefinition
973  */
974 
975 /* global lock manipulation */
lock_class_redefinition(ScmVM * vm)976 static void lock_class_redefinition(ScmVM *vm)
977 {
978     ScmVM *stolefrom = NULL;
979     if (class_redefinition_lock.owner == vm) {
980         class_redefinition_lock.count++;
981     } else {
982         (void)SCM_INTERNAL_MUTEX_LOCK(class_redefinition_lock.mutex);
983         while (class_redefinition_lock.owner != vm) {
984             if (class_redefinition_lock.owner == NULL) {
985                 class_redefinition_lock.owner = vm;
986             } else if (class_redefinition_lock.owner->state
987                        == SCM_VM_TERMINATED) {
988                 stolefrom = class_redefinition_lock.owner;
989                 class_redefinition_lock.owner = vm;
990             } else {
991                 (void)SCM_INTERNAL_COND_WAIT(class_redefinition_lock.cv,
992                                              class_redefinition_lock.mutex);
993             }
994         }
995         (void)SCM_INTERNAL_MUTEX_UNLOCK(class_redefinition_lock.mutex);
996         if (stolefrom) {
997             Scm_Warn("a thread holding class redefinition lock has been terminated: %S", stolefrom);
998         }
999         class_redefinition_lock.count = 1;
1000     }
1001 }
1002 
unlock_class_redefinition(ScmVM * vm)1003 static void unlock_class_redefinition(ScmVM *vm)
1004 {
1005     if (class_redefinition_lock.owner != vm) return;
1006     if (--class_redefinition_lock.count <= 0) {
1007         (void)SCM_INTERNAL_MUTEX_LOCK(class_redefinition_lock.mutex);
1008         (void)SCM_INTERNAL_COND_BROADCAST(class_redefinition_lock.cv);
1009         class_redefinition_lock.owner = NULL;
1010         (void)SCM_INTERNAL_MUTEX_UNLOCK(class_redefinition_lock.mutex);
1011     }
1012 }
1013 
1014 /* %start-class-redefinition klass */
Scm_StartClassRedefinition(ScmClass * klass)1015 void Scm_StartClassRedefinition(ScmClass *klass)
1016 {
1017     if (SCM_CLASS_CATEGORY(klass) != SCM_CLASS_SCHEME) {
1018         Scm_Error("cannot redefine class %S, which is not a Scheme-defined class", klass);
1019     }
1020     ScmVM *vm = Scm_VM();
1021 
1022     /* First, acquire the global lock. */
1023     lock_class_redefinition(vm);
1024 
1025     /* Mark this class to be redefined. */
1026     int success = FALSE;
1027     (void)SCM_INTERNAL_MUTEX_LOCK(klass->mutex);
1028     if (SCM_FALSEP(klass->redefined)) {
1029         klass->redefined = SCM_OBJ(vm);
1030         success = TRUE;
1031     }
1032     (void)SCM_INTERNAL_MUTEX_UNLOCK(klass->mutex);
1033 
1034     if (!success) {
1035         unlock_class_redefinition(vm);
1036         Scm_Error("class %S seems abandoned during class redefinition", klass);
1037     }
1038 
1039     /* Allow modification of important slots */
1040     Scm_ClassMalleableSet(klass, TRUE);
1041 }
1042 
1043 /* %commit-class-redefinition klass newklass */
Scm_CommitClassRedefinition(ScmClass * klass,ScmObj newklass)1044 void Scm_CommitClassRedefinition(ScmClass *klass, ScmObj newklass)
1045 {
1046     if (SCM_CLASS_CATEGORY(klass) != SCM_CLASS_SCHEME) return;
1047     if (!SCM_FALSEP(newklass)&&!SCM_CLASSP(newklass)) {
1048         Scm_Error("class or #f required, but got %S", newklass);
1049     }
1050 
1051     ScmVM *vm = Scm_VM();
1052 
1053     /* Release the lock of the class.
1054        We execute this regardless of class_redefinition_lock.owner.
1055        Theoretically, this procedure shouldn't be called unless the thread
1056        owns global class_redefinition_lock.  However, we don't require it,
1057        so that this procedure can be used for a program to exit from
1058        obscure state. */
1059     (void)SCM_INTERNAL_MUTEX_LOCK(klass->mutex);
1060     if (SCM_EQ(klass->redefined, SCM_OBJ(vm))) {
1061         Scm_ClassMalleableSet(klass, FALSE); /* disable modification */
1062         klass->redefined = newklass;
1063         (void)SCM_INTERNAL_COND_BROADCAST(klass->cv);
1064     }
1065     (void)SCM_INTERNAL_MUTEX_UNLOCK(klass->mutex);
1066 
1067     /* Decrement the recursive global lock. */
1068     unlock_class_redefinition(vm);
1069 }
1070 
1071 /* %check-class-binding name module
1072    See the bindings of name in module, and iff it is bound to a class,
1073    returns the class; otherwise returns #f. */
Scm_CheckClassBinding(ScmObj name,ScmModule * module)1074 ScmObj Scm_CheckClassBinding(ScmObj name, ScmModule *module)
1075 {
1076     if (!SCM_SYMBOLP(name)) return SCM_FALSE;
1077     ScmObj v = Scm_GlobalVariableRef(module, SCM_SYMBOL(name), 0);
1078     return SCM_CLASSP(v) ? v : SCM_FALSE;
1079 }
1080 
1081 /* %replace-class-binding! klass newklass
1082    Called when a descendant of klass is redefined.  If klass has a global
1083    binding, replace it to newklass. */
Scm_ReplaceClassBinding(ScmClass * klass,ScmClass * newklass)1084 void Scm_ReplaceClassBinding(ScmClass *klass, ScmClass *newklass)
1085 {
1086     if (!SCM_SYMBOLP(klass->name)) return;
1087     ScmObj cp;
1088     SCM_FOR_EACH(cp, klass->modules) {
1089         if (!SCM_MODULEP(SCM_CAR(cp))) continue;
1090         Scm_Define(SCM_MODULE(SCM_CAR(cp)),
1091                    SCM_SYMBOL(klass->name),
1092                    SCM_OBJ(newklass));
1093     }
1094 }
1095 
1096 /* %add-direct-subclass! super sub */
Scm_AddDirectSubclass(ScmClass * super,ScmClass * sub)1097 void Scm_AddDirectSubclass(ScmClass *super, ScmClass *sub)
1098 {
1099     if (SCM_CLASS_CATEGORY(super) == SCM_CLASS_SCHEME) {
1100         ScmObj p = Scm_Cons(SCM_OBJ(sub), SCM_NIL);
1101         (void)SCM_INTERNAL_MUTEX_LOCK(super->mutex);
1102         /* avoid duplication */
1103         if (SCM_FALSEP(Scm_Memq(super->directSubclasses, SCM_OBJ(sub)))) {
1104             SCM_SET_CDR_UNCHECKED(p, super->directSubclasses);
1105             super->directSubclasses = p;
1106         }
1107         (void)SCM_INTERNAL_MUTEX_UNLOCK(super->mutex);
1108     }
1109 }
1110 
1111 /* %delete-direct-subclass! super sub */
Scm_DeleteDirectSubclass(ScmClass * super,ScmClass * sub)1112 void Scm_DeleteDirectSubclass(ScmClass *super, ScmClass *sub)
1113 {
1114     if (SCM_CLASS_CATEGORY(super) == SCM_CLASS_SCHEME) {
1115         (void)SCM_INTERNAL_MUTEX_LOCK(super->mutex);
1116         super->directSubclasses =
1117             Scm_DeleteX(SCM_OBJ(sub), super->directSubclasses, SCM_CMP_EQ);
1118         (void)SCM_INTERNAL_MUTEX_UNLOCK(super->mutex);
1119     }
1120 }
1121 
1122 /* %add-direct-method! super method
1123    method can be added or deleted freely, so we don't check malleablility. */
Scm_AddDirectMethod(ScmClass * super,ScmMethod * m)1124 void Scm_AddDirectMethod(ScmClass *super, ScmMethod *m)
1125 {
1126     if (SCM_CLASS_CATEGORY(super) == SCM_CLASS_SCHEME) {
1127         ScmObj p = Scm_Cons(SCM_OBJ(m), SCM_NIL);
1128         (void)SCM_INTERNAL_MUTEX_LOCK(super->mutex);
1129         /* avoid duplication */
1130         if (SCM_FALSEP(Scm_Memq(super->directMethods, SCM_OBJ(m)))) {
1131             SCM_SET_CDR_UNCHECKED(p, super->directMethods);
1132             super->directMethods = p;
1133         }
1134         (void)SCM_INTERNAL_MUTEX_UNLOCK(super->mutex);
1135     }
1136 }
1137 
1138 /* %delete-direct-method! super method
1139    method can be added or deleted freely, so we don't check malleablility. */
Scm_DeleteDirectMethod(ScmClass * super,ScmMethod * m)1140 void Scm_DeleteDirectMethod(ScmClass *super, ScmMethod *m)
1141 {
1142     if (SCM_CLASS_CATEGORY(super) == SCM_CLASS_SCHEME) {
1143         (void)SCM_INTERNAL_MUTEX_LOCK(super->mutex);
1144         super->directMethods =
1145             Scm_DeleteX(SCM_OBJ(m), super->directMethods, SCM_CMP_EQ);
1146         (void)SCM_INTERNAL_MUTEX_UNLOCK(super->mutex);
1147     }
1148 }
1149 
1150 /* %transplant-instance! src dst */
1151 /* Copies the contents of the core structure pointed by src over
1152    the contents of dst.  The contents of dst is destroyed.  This
1153    astonishingly dangerous operation has to be done at the last stage
1154    of change-class, in order to keep the identity of the instance
1155    being updated.
1156 
1157    Note that this procedure doesn't overwrite the Scheme slot
1158    vectors. */
Scm_TransplantInstance(ScmObj src,ScmObj dst)1159 void Scm_TransplantInstance(ScmObj src, ScmObj dst)
1160 {
1161     ScmClass *srcklass = Scm_ClassOf(src);
1162     ScmClass *dstklass = Scm_ClassOf(dst);
1163     ScmClass *base;
1164 
1165     /* Extra check.  We can't transplant the contents to different
1166        an instance that has different base class. */
1167     if ((base = Scm_BaseClassOf(srcklass)) == NULL
1168         || !SCM_EQ(base, Scm_BaseClassOf(dstklass))) {
1169         Scm_Error("%%transplant-instance: classes are incompatible between %S and %S",
1170                   src, dst);
1171     }
1172     if (base->coreSize < (int)sizeof(ScmInstance)) {
1173         Scm_Error("%%transplant-instance: baseclass is too small (implementation error?)");
1174     }
1175     memcpy(dst, src, base->coreSize);
1176 }
1177 
1178 /* touch-instance! obj
1179  * If obj's class is redefined, update obj.  Otherwise it does nothing.
1180  * Handy to ensure obj is in the newest state.  Returns obj.
1181  */
Scm_VMTouchInstance(ScmObj obj)1182 ScmObj Scm_VMTouchInstance(ScmObj obj)
1183 {
1184     ScmClass *klass = Scm_ClassOf(obj);
1185     if (!SCM_FALSEP(klass->redefined)) {
1186         return instance_class_redefinition(obj, klass);
1187     }
1188     return obj;
1189 }
1190 
1191 /*=====================================================================
1192  * Scheme slot access
1193  */
1194 
1195 /* Scheme slots are stored in ScmObj array pointed by slots field
1196  * of ScmInstance.  This one-level indirection allows an instance
1197  * to be redefined.
1198  */
1199 
1200 /* Unbound slot: if the slot value yields either SCM_UNBOUND or
1201  * SCM_UNDEFINED, a generic function slot-unbound is called.
1202  * We count SCM_UNDEFINED as unbound so that a Scheme program can
1203  * make slot unbound, especially needed for procedural slots.
1204  */
1205 
1206 /* A common routine to be used to allocate object.
1207    Coresize should be a size of base C structure in bytes.
1208    Klass may be a subclass.   If klass is inheritable by Scheme
1209    (i.e. it's category is either SCM_CLASS_BASE or SCM_CLASS_SCHEME),
1210    This routine also allocates a slot vector, and initializes the
1211    slot vector with SCM_UNBOUND.
1212    We don't care class redefinition at this point.  If the class is
1213    redefined simultaneously, it will be handled by the subsequent initialize
1214    method.
1215 */
Scm_NewInstance(ScmClass * klass,int coresize)1216 ScmObj Scm_NewInstance(ScmClass *klass, int coresize)
1217 {
1218     ScmObj obj = SCM_NEW2(ScmObj, coresize);
1219 
1220     if (SCM_CLASS_CATEGORY(klass) == SCM_CLASS_BASE
1221         || SCM_CLASS_CATEGORY(klass) == SCM_CLASS_SCHEME) {
1222         ScmObj *slots = SCM_NEW_ARRAY(ScmObj, klass->numInstanceSlots);
1223 
1224         /* NB: actually, for Scheme instances, 'coresize' argument is
1225            redundant since klass->coreSize has it.  There's a historical
1226            confusion in the class protocol.  We should clear it out someday.
1227         */
1228         if (coresize != klass->coreSize) {
1229             Scm_Printf(SCM_CURERR, "WARNING: allocating instance of class %S: coresize argument %d doesn't match the class definition's (%d)\n", klass, coresize, klass->coreSize);
1230         }
1231 
1232         for (int i=0; i<klass->numInstanceSlots; i++) {
1233             slots[i] = SCM_UNBOUND;
1234         }
1235         SCM_INSTANCE(obj)->slots = slots;
1236     }
1237     SCM_SET_CLASS(obj, klass);
1238     return obj;
1239 }
1240 
1241 #if GAUCHE_API_VERSION < 1000
1242 /* TRANSIENT: For the binary compatibility.  Will go on 1.0. */
Scm_AllocateInstance(ScmClass * klass,int coresize)1243 ScmObj Scm_AllocateInstance(ScmClass *klass, int coresize)
1244 {
1245     return Scm_NewInstance(klass, coresize);
1246 }
1247 #endif /*GAUCHE_API_VERSION < 1000*/
1248 
1249 
1250 /* A special procedure that shortcuts allocate-instance and initialize
1251  * slots directly.
1252  * This is mainly used for fast construction of records.  This bypasses
1253  * normal MOP initialization steps, so this shouldn't be used casually.
1254  */
Scm__AllocateAndInitializeInstance(ScmClass * klass,ScmObj * inits,int numInits,u_long flags SCM_UNUSED)1255 ScmObj Scm__AllocateAndInitializeInstance(ScmClass *klass,
1256                                           ScmObj *inits, int numInits,
1257                                           u_long flags SCM_UNUSED /*reserved*/)
1258 {
1259     if (SCM_CLASS_CATEGORY(klass) != SCM_CLASS_BASE
1260         && SCM_CLASS_CATEGORY(klass) != SCM_CLASS_SCHEME) {
1261         Scm_Error("Scm_AllocateAndInitializeInstance can't be called on "
1262                   "this class: %S", SCM_OBJ(klass));
1263     }
1264 
1265     /* We allocate an instance and a slot vector at once for speed.
1266        It is reasonable optimization, since record classes won't be redefined,
1267        and the instances' slot vector will never be replaced.
1268        We may provide an another mode to allocate the slot vector separately,
1269        using FLAGS argument. */
1270     int corewords = (klass->coreSize + sizeof(ScmObj)-1)/sizeof(ScmObj);
1271     ScmObj obj = SCM_NEW2(ScmObj, (corewords+klass->numInstanceSlots)*sizeof(ScmObj));
1272     SCM_SET_CLASS(obj, klass);
1273     ScmObj *slots = ((ScmObj*)obj) + corewords;
1274     for (int i=0; i<klass->numInstanceSlots; i++) {
1275         if (i < numInits) slots[i] = inits[i];
1276         else slots[i] = SCM_UNBOUND;
1277     }
1278     SCM_INSTANCE(obj)->slots = slots;
1279     return obj;
1280 }
1281 
1282 /* Invoke class redefinition method */
instance_class_redefinition(ScmObj obj,ScmClass * old)1283 static ScmObj instance_class_redefinition(ScmObj obj, ScmClass *old)
1284 {
1285     (void)SCM_INTERNAL_MUTEX_LOCK(old->mutex);
1286     while (!SCM_ISA(old->redefined, SCM_CLASS_CLASS)) {
1287         (void)SCM_INTERNAL_COND_WAIT(old->cv, old->mutex);
1288     }
1289     ScmObj newc = old->redefined;
1290     (void)SCM_INTERNAL_MUTEX_UNLOCK(old->mutex);
1291     if (SCM_CLASSP(newc)) {
1292         return Scm_VMApply2(SCM_OBJ(&Scm_GenericChangeClass), obj, newc);
1293     } else {
1294         return SCM_OBJ(old);
1295     }
1296 }
1297 
1298 /* most primitive internal accessor */
scheme_slot_ref(ScmObj obj,ScmSmallInt number)1299 static inline ScmObj scheme_slot_ref(ScmObj obj, ScmSmallInt number)
1300 {
1301     ScmClass *k = Scm_ClassOf(obj);
1302     if (number < 0 || number >= k->numInstanceSlots)
1303         Scm_Error("instance slot index %ld out of bounds for %S", number, obj);
1304     return SCM_INSTANCE_SLOTS(obj)[number];
1305 }
1306 
scheme_slot_set(ScmObj obj,ScmSmallInt number,ScmObj val)1307 static inline void scheme_slot_set(ScmObj obj, ScmSmallInt number, ScmObj val)
1308 {
1309     ScmClass *k = Scm_ClassOf(obj);
1310     if (number < 0 || number >= k->numInstanceSlots)
1311         Scm_Error("instance slot index %ld out of bounds for %S", number, obj);
1312     SCM_INSTANCE_SLOTS(obj)[number] = val;
1313 }
1314 
1315 /* These three are exposed to Scheme to do some nasty things.
1316    We shouldn't do class redefinition check here, since the slot number
1317    is calculated based on the old class, if the class is ever redefined.
1318 */
1319 #if GAUCHE_API_VERSION < 1000
1320 /* OBSOLETED, for the backward compatibility */
Scm_InstanceSlotRef(ScmObj obj,ScmSmallInt number)1321 ScmObj Scm_InstanceSlotRef(ScmObj obj, ScmSmallInt number)
1322 {
1323     return scheme_slot_ref(obj, number);
1324 }
1325 
1326 /* TRANSIENT: we'll rename this to Scm_InstanceSlotRef() in 1.0. */
Scm_InstanceSlotRef3(ScmObj obj,ScmSmallInt number,ScmObj fallback)1327 ScmObj Scm_InstanceSlotRef3(ScmObj obj, ScmSmallInt number, ScmObj fallback)
1328 #else  /*GAUCHE_API_VERSION >= 1000*/
1329 ScmObj Scm_InstanceSlotRef(ScmObj obj, ScmSmallInt number, ScmObj fallback)
1330 #endif /*GAUCHE_API_VERSION*/
1331 {
1332     ScmObj v = scheme_slot_ref(obj, number);
1333     if (SCM_UNBOUNDP(v)) {
1334         if (SCM_UNBOUNDP(fallback)) {
1335             Scm_Error("Slot #%d of object of class %S is unbound.",
1336                       number, SCM_OBJ(Scm_ClassOf(obj)));
1337         }
1338         return fallback;
1339     }
1340     return v;
1341 }
1342 
Scm_InstanceSlotSet(ScmObj obj,ScmSmallInt number,ScmObj val)1343 void Scm_InstanceSlotSet(ScmObj obj, ScmSmallInt number, ScmObj val)
1344 {
1345     scheme_slot_set(obj, number, val);
1346 }
1347 
1348 /* Initialize a slot according to its accessor spec
1349    TODO: class redefintion check
1350 */
slot_initialize_cc(ScmObj result,void ** data)1351 static ScmObj slot_initialize_cc(ScmObj result, void **data)
1352 {
1353     ScmObj obj = data[0];
1354     ScmSlotAccessor *sa = SCM_SLOT_ACCESSOR(data[1]);
1355     return slot_set_using_accessor(obj, sa, result);
1356 }
1357 
Scm_VMSlotInitializeUsingAccessor(ScmObj obj,ScmSlotAccessor * sa,ScmObj initargs)1358 ScmObj Scm_VMSlotInitializeUsingAccessor(ScmObj obj,
1359                                          ScmSlotAccessor *sa,
1360                                          ScmObj initargs)
1361 {
1362     /* (1) see if we have init-keyword */
1363     if (SCM_KEYWORDP(sa->initKeyword)) {
1364         ScmObj v = Scm_GetKeyword(sa->initKeyword, initargs, SCM_UNDEFINED);
1365         if (!SCM_UNDEFINEDP(v)) {
1366             return slot_set_using_accessor(obj, sa, v);
1367         }
1368     }
1369     /* (2) use init-value or init-thunk, if this slot is initializable. */
1370     if (sa->initializable) {
1371         if (!SCM_UNBOUNDP(sa->initValue)) {
1372             return slot_set_using_accessor(obj, sa, sa->initValue);
1373         }
1374         if (SCM_PROCEDUREP(sa->initThunk)) {
1375             void *data[2];
1376             data[0] = (void*)obj;
1377             data[1] = (void*)sa;
1378             Scm_VMPushCC(slot_initialize_cc, data, 2);
1379             return Scm_VMApply(sa->initThunk, SCM_NIL);
1380         }
1381     }
1382     return SCM_UNDEFINED;
1383 }
1384 
1385 /*-------------------------------------------------------------------
1386  * slot-ref, slot-set! and families
1387  */
1388 
1389 /* helper macros */
1390 #define SLOT_UNBOUND(klass, obj, slot)                  \
1391     Scm_VMApply(SCM_OBJ(&Scm_GenericSlotUnbound),       \
1392                 SCM_LIST3(SCM_OBJ(klass), obj, slot))
1393 
1394 #define SLOT_MISSING3(klass, obj, slot)                 \
1395     Scm_VMApply(SCM_OBJ(&Scm_GenericSlotMissing),       \
1396                 SCM_LIST3(SCM_OBJ(klass), obj, slot))
1397 
1398 #define SLOT_MISSING4(klass, obj, slot, val)            \
1399     Scm_VMApply(SCM_OBJ(&Scm_GenericSlotMissing),       \
1400                 SCM_LIST4(SCM_OBJ(klass), obj, slot, val))
1401 
1402 /* GET-SLOT-ACCESSOR
1403  *
1404  * (define (get-slot-accessor class slot)
1405  *   (cond ((assq slot (ref class 'accessors)) => cdr)
1406  *         (else (error !!!))))
1407  */
Scm_GetSlotAccessor(ScmClass * klass,ScmObj slot)1408 ScmSlotAccessor *Scm_GetSlotAccessor(ScmClass *klass, ScmObj slot)
1409 {
1410     ScmObj p = Scm_Assq(slot, klass->accessors);
1411     if (!SCM_PAIRP(p)) return NULL;
1412     if (!SCM_XTYPEP(SCM_CDR(p), SCM_CLASS_SLOT_ACCESSOR))
1413         Scm_Error("slot accessor information of class %S, slot %S is screwed up.",
1414                   SCM_OBJ(klass), slot);
1415     return SCM_SLOT_ACCESSOR(SCM_CDR(p));
1416 }
1417 
1418 /* (internal) slot-ref-using-accessor
1419  *
1420  * - assumes accessor belongs to the proper class.
1421  * - no class redefinition check is done
1422  */
slot_ref_using_accessor_cc(ScmObj result,void ** data)1423 static ScmObj slot_ref_using_accessor_cc(ScmObj result, void **data)
1424 {
1425     ScmObj obj = data[0];
1426     ScmObj slot = data[1];
1427     int boundp = (data[2] != NULL);
1428 
1429     if (SCM_UNBOUNDP(result) || SCM_UNDEFINEDP(result)) {
1430         if (boundp) {
1431             return SCM_FALSE;
1432         } else {
1433             return SLOT_UNBOUND(Scm_ClassOf(obj), obj, slot);
1434         }
1435     } else {
1436         if (boundp) return SCM_TRUE;
1437         else        return result;
1438     }
1439 }
1440 
slot_boundp_using_accessor_cc(ScmObj result,void ** data SCM_UNUSED)1441 static ScmObj slot_boundp_using_accessor_cc(ScmObj result,
1442                                             void **data SCM_UNUSED)
1443 {
1444     return SCM_FALSEP(result)? SCM_FALSE:SCM_TRUE;
1445 }
1446 
slot_ref_using_accessor(ScmObj obj,ScmSlotAccessor * sa,int boundp)1447 static ScmObj slot_ref_using_accessor(ScmObj obj,
1448                                       ScmSlotAccessor *sa,
1449                                       int boundp)
1450 {
1451     ScmObj val = SCM_UNBOUND;
1452     if (sa->getter) {
1453         val = sa->getter(obj);
1454     } else if (sa->slotNumber >= 0) {
1455         val = scheme_slot_ref(obj, sa->slotNumber);
1456     } else if (boundp && SCM_PROCEDUREP(sa->schemeBoundp)) {
1457         void *data[3];
1458         data[0] = obj;
1459         data[1] = sa->name;
1460         data[2] = (void*)(intptr_t)boundp;
1461         Scm_VMPushCC(slot_boundp_using_accessor_cc, data, 3);
1462         return Scm_VMApply(sa->schemeBoundp, SCM_LIST1(obj));
1463     } else if (SCM_PROCEDUREP(sa->schemeGetter)) {
1464         void *data[3];
1465         data[0] = obj;
1466         data[1] = sa->name;
1467         data[2] = (void*)(intptr_t)boundp;
1468         Scm_VMPushCC(slot_ref_using_accessor_cc, data, 3);
1469         return Scm_VMApply(sa->schemeGetter, SCM_LIST1(obj));
1470     } else {
1471         Scm_Error("don't know how to retrieve value of slot %S of object %S (MOP error?)",
1472                   sa->name, obj);
1473     }
1474     if (boundp) {
1475         return SCM_MAKE_BOOL(!(SCM_UNBOUNDP(val) || SCM_UNDEFINEDP(val)));
1476     } else {
1477         if (SCM_UNBOUNDP(val) || SCM_UNDEFINEDP(val)) {
1478             return SLOT_UNBOUND(Scm_ClassOf(obj), obj, sa->name);
1479         } else {
1480             return val;
1481         }
1482     }
1483 }
1484 
1485 /* SLOT-REF
1486  *
1487  *(define (slot-ref obj slot bound-check?)
1488  *   (%check-class-redefined (class-of obj))
1489  *   (let ((sa (get-slot-accessor (class-of obj) slot)))
1490  *     (if sa
1491  *         (%internal-slot-ref-using-accessor obj sa bound-check?)
1492  *         (slot-missing (class-of obj) obj slot))))
1493  */
slot_ref_cc(ScmObj result SCM_UNUSED,void ** data)1494 static ScmObj slot_ref_cc(ScmObj result SCM_UNUSED, void **data)
1495 {
1496     return Scm_VMSlotRef(SCM_OBJ(data[0]), SCM_OBJ(data[1]), (int)(intptr_t)data[2]);
1497 }
1498 
Scm_VMSlotRef(ScmObj obj,ScmObj slot,int boundp)1499 ScmObj Scm_VMSlotRef(ScmObj obj, ScmObj slot, int boundp)
1500 {
1501     ScmClass *klass = Scm_ClassOf(obj);
1502 
1503     if (!SCM_FALSEP(klass->redefined)) {
1504         void *data[3];
1505         data[0] = obj;
1506         data[1] = slot;
1507         data[2] = (void*)(intptr_t)boundp;
1508         Scm_VMPushCC(slot_ref_cc, data, 3);
1509         return instance_class_redefinition(obj, klass);
1510     }
1511     ScmSlotAccessor *sa = Scm_GetSlotAccessor(klass, slot);
1512     if (sa == NULL) return SLOT_MISSING3(klass, obj, slot);
1513     else            return slot_ref_using_accessor(obj, sa, boundp);
1514 }
1515 
1516 /* SLOT-REF-USING-ACCESSOR
1517  *
1518  * (define (slot-ref-using-accessor obj sa bound-check?)
1519  *   (%check-if-sa-is-valid-for-object obj sa)
1520  *   (%internal-slot-ref-using-accessor obj sa bound-check?))
1521  *
1522  * - no class redefinition check is performed.  if obj isn't updated
1523  *   for the new class, sa must come from the old class.
1524  */
Scm_VMSlotRefUsingAccessor(ScmObj obj,ScmSlotAccessor * sa,int boundp)1525 ScmObj Scm_VMSlotRefUsingAccessor(ScmObj obj, ScmSlotAccessor *sa, int boundp)
1526 {
1527     ScmClass *klass = Scm_ClassOf(obj);
1528     if (klass != sa->klass) {
1529         Scm_Error("attempt to use a slot accessor %S on the object of different class: %S",
1530                   SCM_OBJ(sa), obj);
1531     }
1532     return slot_ref_using_accessor(obj, sa, boundp);
1533 }
1534 
1535 /* SLOT-REF-USING-CLASS
1536  *
1537  * (define-method slot-ref-using-class
1538  *      ((class <class>) (obj <object>) slot)
1539  *   (unless (eq? (class-of obj) class) (error !!!))
1540  *   (let ((sa (get-slot-accessor class slot)))
1541  *     (if sa
1542  *         (%internal-slot-ref-using-accessor obj sa #f)
1543  *         (slot-missing class obj slot))))
1544  *
1545  * - no class redefinition check is performed.  if obj isn't updated,
1546  *   and class is an old class, then it can access to the old instance's
1547  *   slot value.
1548  */
slot_ref_using_class(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * d SCM_UNUSED)1549 static ScmObj slot_ref_using_class(ScmNextMethod *nm SCM_UNUSED,
1550                                    ScmObj *argv,
1551                                    int argc SCM_UNUSED,
1552                                    void *d SCM_UNUSED)
1553 {
1554     ScmClass *klass = SCM_CLASS(argv[0]);
1555     ScmObj obj = argv[1];
1556     ScmObj slot = argv[2];
1557 
1558     if (!SCM_EQ(SCM_OBJ(klass), SCM_OBJ(Scm_ClassOf(obj)))) {
1559         Scm_Error("slot-ref-using-class: class %S is not the class of object %S", klass, obj);
1560     }
1561     ScmSlotAccessor *sa = Scm_GetSlotAccessor(klass, slot);
1562     if (sa == NULL) return SLOT_MISSING3(klass, obj, slot);
1563     else            return slot_ref_using_accessor(obj, sa, FALSE);
1564 }
1565 
1566 static ScmClass *slot_ref_using_class_SPEC[] = {
1567     SCM_CLASS_STATIC_PTR(Scm_ClassClass),
1568     SCM_CLASS_STATIC_PTR(Scm_ObjectClass),
1569     SCM_CLASS_STATIC_PTR(Scm_TopClass)
1570 };
1571 static SCM_DEFINE_METHOD(slot_ref_using_class_rec,
1572                          &Scm_GenericSlotRefUsingClass,
1573                          3, 0, slot_ref_using_class_SPEC,
1574                          slot_ref_using_class, NULL);
1575 
1576 /* (internal) SLOT-SET-USING-ACCESSOR
1577  *
1578  * - assumes accessor belongs to the proper class.
1579  * - no class redefinition check is done
1580  */
slot_set_using_accessor(ScmObj obj,ScmSlotAccessor * sa,ScmObj val)1581 ScmObj slot_set_using_accessor(ScmObj obj,
1582                                ScmSlotAccessor *sa,
1583                                ScmObj val)
1584 {
1585     if (sa->setter) {
1586         sa->setter(obj, val);
1587     } else if (sa->slotNumber >= 0) {
1588         scheme_slot_set(obj, sa->slotNumber, val);
1589     } else if (SCM_PROCEDUREP(sa->schemeSetter)) {
1590         return Scm_VMApply(sa->schemeSetter, SCM_LIST2(obj, val));
1591     } else {
1592         Scm_Error("slot %S of class %S is read-only", sa->name,
1593                   SCM_OBJ(Scm_ClassOf(obj)));
1594     }
1595     return SCM_UNDEFINED;
1596 }
1597 
1598 /* (internal) SLOT-ACCESSOR-SETTABLE
1599  * must be in sync with slot_set_using_accessor.
1600  * this won't detect the case when slot mutation is rejected by
1601  * the setter procedure.
1602  */
slot_accessor_settable_p(ScmSlotAccessor * sa)1603 static int slot_accessor_settable_p(ScmSlotAccessor *sa)
1604 {
1605     if (sa->setter
1606         || sa->slotNumber >= 0
1607         || SCM_PROCEDUREP(sa->schemeSetter))
1608         return TRUE;
1609     else
1610         return FALSE;
1611 }
1612 
1613 /* SLOT-SET!
1614  *
1615  * (define (slot-set! obj slot val)
1616  *   (%check-class-redefined (class-of obj))
1617  *   (let ((sa (get-slot-accessor (class-of obj) slot)))
1618  *     (if sa
1619  *         (%internal-slot-set-using-accessor obj sa val)
1620  *         (slot-missing (class-of obj) obj slot val))))
1621  */
slot_set_cc(ScmObj result SCM_UNUSED,void ** data)1622 static ScmObj slot_set_cc(ScmObj result SCM_UNUSED, void **data)
1623 {
1624     return Scm_VMSlotSet(SCM_OBJ(data[0]), SCM_OBJ(data[1]), SCM_OBJ(data[2]));
1625 }
1626 
Scm_VMSlotSet(ScmObj obj,ScmObj slot,ScmObj val)1627 ScmObj Scm_VMSlotSet(ScmObj obj, ScmObj slot, ScmObj val)
1628 {
1629     ScmClass *klass = Scm_ClassOf(obj);
1630     if (!SCM_FALSEP(klass->redefined)) {
1631         void *data[3];
1632         data[0] = obj;
1633         data[1] = slot;
1634         data[2] = val;
1635         Scm_VMPushCC(slot_set_cc, data, 3);
1636         return instance_class_redefinition(obj, klass);
1637     }
1638     ScmSlotAccessor *sa = Scm_GetSlotAccessor(klass, slot);
1639     if (sa == NULL) return SLOT_MISSING4(klass, obj, slot, val);
1640     else            return slot_set_using_accessor(obj, sa, val);
1641 }
1642 
1643 /* SLOT-SET-USING-ACCESSOR
1644  *
1645  * (define (slot-set-using-accessor obj sa val)
1646  *   (%check-if-sa-is-valid-for-object obj sa)
1647  *   (%internal-slot-set-using-accessor obj sa val))
1648  *
1649  * - no class redefinition check is performed.  if obj isn't updated
1650  *   for the new class, sa must come from the old class.
1651  */
Scm_VMSlotSetUsingAccessor(ScmObj obj,ScmSlotAccessor * sa,ScmObj val)1652 ScmObj Scm_VMSlotSetUsingAccessor(ScmObj obj, ScmSlotAccessor *sa, ScmObj val)
1653 {
1654     ScmClass *klass = Scm_ClassOf(obj);
1655     if (klass != sa->klass) {
1656         Scm_Error("attempt to use a slot accessor %S on the object of different class: %S",
1657                   SCM_OBJ(sa), obj);
1658     }
1659     return slot_set_using_accessor(obj, sa, val);
1660 }
1661 
1662 /* SLOT-SET-USING-CLASS
1663  *
1664  * (define-method slot-set-using-class
1665  *      ((class <class>) (obj <object>) slot val)
1666  *   (unless (eq? (class-of obj) class) (error !!!))
1667  *   (let ((sa (get-slot-accessor class slot)))
1668  *     (if sa
1669  *         (%internal-slot-set-using-accessor obj sa val)
1670  *         (slot-missing class obj slot val))))
1671  *
1672  * - no class redefinition check is performed.  if obj isn't updated,
1673  *   and class is an old class, then it can access to the old instance's
1674  *   slot value.
1675  */
slot_set_using_class(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * d SCM_UNUSED)1676 static ScmObj slot_set_using_class(ScmNextMethod *nm SCM_UNUSED,
1677                                    ScmObj *argv,
1678                                    int argc SCM_UNUSED,
1679                                    void *d SCM_UNUSED)
1680 {
1681     ScmClass *klass = SCM_CLASS(argv[0]);
1682     ScmObj obj = argv[1];
1683     ScmObj slot = argv[2];
1684     ScmObj val = argv[3];
1685 
1686     if (!SCM_EQ(SCM_OBJ(klass), SCM_OBJ(Scm_ClassOf(obj)))) {
1687         Scm_Error("slot-ref-using-class: class %S is not the class of object %S", klass, obj);
1688     }
1689     ScmSlotAccessor *sa = Scm_GetSlotAccessor(klass, slot);
1690     if (sa == NULL) return SLOT_MISSING4(klass, obj, slot, val);
1691     else            return slot_set_using_accessor(obj, sa, val);
1692 }
1693 
1694 static ScmClass *slot_set_using_class_SPEC[] = {
1695     SCM_CLASS_STATIC_PTR(Scm_ClassClass),
1696     SCM_CLASS_STATIC_PTR(Scm_ObjectClass),
1697     SCM_CLASS_STATIC_PTR(Scm_TopClass),
1698     SCM_CLASS_STATIC_PTR(Scm_TopClass)
1699 };
1700 static SCM_DEFINE_METHOD(slot_set_using_class_rec,
1701                          &Scm_GenericSlotSetUsingClass,
1702                          4, 0, slot_set_using_class_SPEC,
1703                          slot_set_using_class, NULL);
1704 
1705 /* SLOT-BOUND?
1706  *
1707  * (define (slot-bound? obj slot)
1708  *   (%check-class-redefined (class-of obj))
1709  *   (slot-bound-using-class (class-of obj) obj slot))
1710  */
slot_boundp_cc(ScmObj result SCM_UNUSED,void ** data)1711 static ScmObj slot_boundp_cc(ScmObj result SCM_UNUSED, void **data)
1712 {
1713     ScmObj obj = SCM_OBJ(data[0]);
1714     ScmObj slot = SCM_OBJ(data[1]);
1715     return Scm_VMSlotBoundP(obj, slot);
1716 }
1717 
Scm_VMSlotBoundP(ScmObj obj,ScmObj slot)1718 ScmObj Scm_VMSlotBoundP(ScmObj obj, ScmObj slot)
1719 {
1720     ScmClass *klass = Scm_ClassOf(obj);
1721 
1722     if (!SCM_FALSEP(klass->redefined)) {
1723         void *data[2];
1724         data[0] = obj;
1725         data[1] = slot;
1726         Scm_VMPushCC(slot_boundp_cc, data, 2);
1727         return instance_class_redefinition(obj, Scm_ClassOf(obj));
1728     }
1729     return Scm_VMApply(SCM_OBJ(&Scm_GenericSlotBoundUsingClassP),
1730                        SCM_LIST3(SCM_OBJ(klass), obj, slot));
1731 }
1732 
1733 /* SLOT-BOUND-USING-CLASS?
1734  *
1735  * (define-method slot-bound-using-class? ((class <class>)
1736  *                                         (obj <obj>)
1737  *                                         slot)
1738  *   (unless (eq? class (class-of obj)) (error !!!))
1739  *   (let ((sa (get-slot-accessor class slot)))
1740  *     (if sa
1741  *         (%internal-slot-ref-using-accessor obj sa #t)
1742  *         (slot-missing class obj slot)))
1743  *
1744  * - no redefinition check!
1745  */
slot_bound_using_class_p(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)1746 static ScmObj slot_bound_using_class_p(ScmNextMethod *nm SCM_UNUSED,
1747                                        ScmObj *argv,
1748                                        int argc SCM_UNUSED,
1749                                        void *data SCM_UNUSED)
1750 {
1751     ScmClass *klass = SCM_CLASS(argv[0]);
1752     ScmObj obj = argv[1];
1753     ScmObj slot = argv[2];
1754 
1755     if (!SCM_EQ(SCM_OBJ(klass), SCM_OBJ(Scm_ClassOf(obj)))) {
1756         Scm_Error("slot-bound-using-class?: class %S is not the class of object %S", klass, obj);
1757     }
1758     ScmSlotAccessor *sa =Scm_GetSlotAccessor(klass, slot);
1759     if (sa == NULL) return SLOT_MISSING3(klass, obj, slot);
1760     else            return slot_ref_using_accessor(obj, sa, TRUE);
1761 }
1762 
1763 static ScmClass *slot_bound_using_class_p_SPEC[] = {
1764     SCM_CLASS_STATIC_PTR(Scm_ClassClass),
1765     SCM_CLASS_STATIC_PTR(Scm_TopClass),
1766     SCM_CLASS_STATIC_PTR(Scm_TopClass)
1767 };
1768 static SCM_DEFINE_METHOD(slot_bound_using_class_p_rec,
1769                          &Scm_GenericSlotBoundUsingClassP,
1770                          3, 0,
1771                          slot_bound_using_class_p_SPEC,
1772                          slot_bound_using_class_p, NULL);
1773 
1774 /*
1775  * Builtin object initializer
1776  * This is the fallback method of generic initialize.  Since all the
1777  * Scheme-defined objects will be initialized by object_initialize,
1778  * this method is called only for built-in classes.
1779  */
builtin_initialize(ScmObj * argv,int argc,ScmGeneric * gf SCM_UNUSED)1780 static ScmObj builtin_initialize(ScmObj *argv, int argc,
1781                                  ScmGeneric *gf SCM_UNUSED)
1782 {
1783     SCM_ASSERT(argc == 2);
1784     ScmObj instance = argv[0];
1785     ScmObj initargs = argv[1];
1786     if (Scm_Length(initargs) % 2) {
1787         Scm_Error("initializer list is not even: %S", initargs);
1788     }
1789     ScmClass *klass = Scm_ClassOf(instance);
1790     ScmObj ap;
1791     SCM_FOR_EACH(ap, klass->accessors) {
1792         ScmSlotAccessor *acc = SCM_SLOT_ACCESSOR(SCM_CDAR(ap));
1793         if (acc->setter && SCM_KEYWORDP(acc->initKeyword)) {
1794             ScmObj val = Scm_GetKeyword(acc->initKeyword, initargs, SCM_UNDEFINED);
1795             if (!SCM_UNDEFINEDP(val)) {
1796                 acc->setter(instance, val);
1797             }
1798         }
1799     }
1800     return instance;
1801 }
1802 
1803 /*--------------------------------------------------------------
1804  * Slot accessor object
1805  */
1806 
1807 /* we initialize fields appropriately here. */
slot_accessor_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)1808 static ScmObj slot_accessor_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
1809 {
1810     ScmSlotAccessor *sa = SCM_NEW(ScmSlotAccessor);
1811 
1812     SCM_SET_CLASS(sa, klass);
1813     sa->name = SCM_FALSE;
1814     sa->getter = NULL;
1815     sa->setter = NULL;
1816     sa->initValue = SCM_UNBOUND;
1817     sa->initKeyword = SCM_FALSE;
1818     sa->initThunk = SCM_FALSE;
1819     sa->initializable = FALSE;
1820     sa->slotNumber = -1;
1821     sa->schemeGetter = SCM_FALSE;
1822     sa->schemeSetter = SCM_FALSE;
1823     sa->schemeBoundp = SCM_FALSE;
1824     return SCM_OBJ(sa);
1825 }
1826 
slot_accessor_print(ScmObj obj,ScmPort * out,ScmWriteContext * ctx SCM_UNUSED)1827 static void slot_accessor_print(ScmObj obj, ScmPort *out,
1828                                 ScmWriteContext *ctx SCM_UNUSED)
1829 {
1830     ScmSlotAccessor *sa = SCM_SLOT_ACCESSOR(obj);
1831 
1832     Scm_Printf(out, "#<slot-accessor %S.%S ",
1833                (sa->klass? sa->klass->name : SCM_FALSE),
1834                sa->name);
1835     if (sa->getter) Scm_Printf(out, "native");
1836     else if (!SCM_FALSEP(sa->schemeGetter)) Scm_Printf(out, "proc");
1837     else if (sa->slotNumber >= 0) Scm_Printf(out, "%d", sa->slotNumber);
1838     else Scm_Printf(out, "unknown");
1839     if (!SCM_FALSEP(sa->initKeyword))
1840         Scm_Printf(out, " %S", sa->initKeyword);
1841     Scm_Printf(out, ">");
1842 }
1843 
1844 /* some information is visible from Scheme world */
slot_accessor_class(ScmSlotAccessor * sa)1845 static ScmObj slot_accessor_class(ScmSlotAccessor *sa)
1846 {
1847     return SCM_OBJ(sa->klass);
1848 }
1849 
slot_accessor_class_set(ScmSlotAccessor * sa,ScmObj v)1850 static void slot_accessor_class_set(ScmSlotAccessor *sa, ScmObj v)
1851 {
1852     if (!Scm_TypeP(v, SCM_CLASS_CLASS)) {
1853         Scm_Error(":class argument must be a class metaobject, but got %S", v);
1854     }
1855     sa->klass = SCM_CLASS(v);
1856 }
1857 
slot_accessor_name(ScmSlotAccessor * sa)1858 static ScmObj slot_accessor_name(ScmSlotAccessor *sa)
1859 {
1860     return sa->name;
1861 }
1862 
slot_accessor_name_set(ScmSlotAccessor * sa,ScmObj v)1863 static void slot_accessor_name_set(ScmSlotAccessor *sa, ScmObj v)
1864 {
1865     sa->name = v;
1866 }
1867 
slot_accessor_init_value(ScmSlotAccessor * sa)1868 static ScmObj slot_accessor_init_value(ScmSlotAccessor *sa)
1869 {
1870     return sa->initValue;
1871 }
1872 
slot_accessor_init_value_set(ScmSlotAccessor * sa,ScmObj v)1873 static void slot_accessor_init_value_set(ScmSlotAccessor *sa, ScmObj v)
1874 {
1875     sa->initValue = v;
1876 }
1877 
slot_accessor_init_keyword(ScmSlotAccessor * sa)1878 static ScmObj slot_accessor_init_keyword(ScmSlotAccessor *sa)
1879 {
1880     return sa->initKeyword;
1881 }
1882 
slot_accessor_init_keyword_set(ScmSlotAccessor * sa,ScmObj v)1883 static void slot_accessor_init_keyword_set(ScmSlotAccessor *sa, ScmObj v)
1884 {
1885     sa->initKeyword = v;
1886 }
1887 
slot_accessor_init_thunk(ScmSlotAccessor * sa)1888 static ScmObj slot_accessor_init_thunk(ScmSlotAccessor *sa)
1889 {
1890     return sa->initThunk;
1891 }
1892 
slot_accessor_init_thunk_set(ScmSlotAccessor * sa,ScmObj v)1893 static void slot_accessor_init_thunk_set(ScmSlotAccessor *sa, ScmObj v)
1894 {
1895     sa->initThunk = v;
1896 }
1897 
slot_accessor_slot_number(ScmSlotAccessor * sa)1898 static ScmObj slot_accessor_slot_number(ScmSlotAccessor *sa)
1899 {
1900     return SCM_MAKE_INT(sa->slotNumber);
1901 }
1902 
slot_accessor_slot_number_set(ScmSlotAccessor * sa,ScmObj val)1903 static void slot_accessor_slot_number_set(ScmSlotAccessor *sa, ScmObj val)
1904 {
1905     int n = 0;
1906     if (!SCM_INTP(val) || ((n = SCM_INT_VALUE(val)) < 0))
1907         Scm_Error("small positive integer required, but got %S", val);
1908     sa->slotNumber = n;
1909 }
1910 
slot_accessor_initializable(ScmSlotAccessor * sa)1911 static ScmObj slot_accessor_initializable(ScmSlotAccessor *sa)
1912 {
1913     return SCM_MAKE_BOOL(sa->initializable);
1914 }
1915 
slot_accessor_initializable_set(ScmSlotAccessor * sa,ScmObj v)1916 static void slot_accessor_initializable_set(ScmSlotAccessor *sa, ScmObj v)
1917 {
1918     sa->initializable = SCM_FALSEP(v)? FALSE : TRUE;
1919 }
1920 
slot_accessor_settable(ScmSlotAccessor * sa)1921 static ScmObj slot_accessor_settable(ScmSlotAccessor *sa)
1922 {
1923     return SCM_MAKE_BOOL(slot_accessor_settable_p(sa));
1924 }
1925 
slot_accessor_scheme_getter(ScmSlotAccessor * sa)1926 static ScmObj slot_accessor_scheme_getter(ScmSlotAccessor *sa)
1927 {
1928     return sa->schemeGetter;
1929 }
1930 
slot_accessor_scheme_getter_set(ScmSlotAccessor * sa,ScmObj p)1931 static void slot_accessor_scheme_getter_set(ScmSlotAccessor *sa, ScmObj p)
1932 {
1933     /* TODO: check */
1934     sa->schemeGetter = p;
1935 }
1936 
slot_accessor_scheme_setter(ScmSlotAccessor * sa)1937 static ScmObj slot_accessor_scheme_setter(ScmSlotAccessor *sa)
1938 {
1939     return sa->schemeSetter;
1940 }
1941 
slot_accessor_scheme_setter_set(ScmSlotAccessor * sa,ScmObj p)1942 static void slot_accessor_scheme_setter_set(ScmSlotAccessor *sa, ScmObj p)
1943 {
1944     /* TODO: check */
1945     sa->schemeSetter = p;
1946 }
1947 
slot_accessor_scheme_boundp(ScmSlotAccessor * sa)1948 static ScmObj slot_accessor_scheme_boundp(ScmSlotAccessor *sa)
1949 {
1950     return sa->schemeBoundp;
1951 }
1952 
slot_accessor_scheme_boundp_set(ScmSlotAccessor * sa,ScmObj p)1953 static void slot_accessor_scheme_boundp_set(ScmSlotAccessor *sa, ScmObj p)
1954 {
1955     /* TODO: check */
1956     sa->schemeBoundp = p;
1957 }
1958 
1959 /*=====================================================================
1960  * <object> class initialization
1961  */
1962 
instance_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)1963 static ScmObj instance_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
1964 {
1965     return SCM_OBJ(SCM_NEW_INSTANCE(ScmInstance, klass));
1966 }
1967 
1968 #if GAUCHE_API_VERSION < 1000
1969 /* TRANSIENT: For the binary compatibility during 0.9 series.  Remove
1970    this on 1.0 */
Scm_ObjectAllocate(ScmClass * klass,ScmObj initargs)1971 ScmObj Scm_ObjectAllocate(ScmClass *klass, ScmObj initargs)
1972 {
1973     return instance_allocate(klass, initargs);
1974 }
1975 #endif /*GAUCHE_API_VERSION < 1000*/
1976 
1977 /* (initialize <object> initargs) */
1978 static ScmObj object_initialize_cc(ScmObj result, void **data);
1979 
object_initialize1(ScmObj obj,ScmObj accs,ScmObj initargs)1980 static ScmObj object_initialize1(ScmObj obj, ScmObj accs, ScmObj initargs)
1981 {
1982     if (SCM_NULLP(accs)) return obj;
1983     SCM_ASSERT(SCM_PAIRP(SCM_CAR(accs))
1984                && SCM_SLOT_ACCESSOR_P(SCM_CDAR(accs)));
1985     void *next[3];
1986     next[0] = obj;
1987     next[1] = SCM_CDR(accs);
1988     next[2] = initargs;
1989     Scm_VMPushCC(object_initialize_cc, next, 3);
1990     return Scm_VMSlotInitializeUsingAccessor(obj,
1991                                              SCM_SLOT_ACCESSOR(SCM_CDAR(accs)),
1992                                              initargs);
1993 }
1994 
object_initialize_cc(ScmObj result SCM_UNUSED,void ** data)1995 static ScmObj object_initialize_cc(ScmObj result SCM_UNUSED, void **data)
1996 {
1997     ScmObj obj = SCM_OBJ(data[0]);
1998     ScmObj accs = SCM_OBJ(data[1]);
1999     ScmObj initargs = SCM_OBJ(data[2]);
2000     return object_initialize1(obj, accs, initargs);
2001 }
2002 
object_initialize(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2003 static ScmObj object_initialize(ScmNextMethod *nm SCM_UNUSED,
2004                                 ScmObj *argv,
2005                                 int argc SCM_UNUSED,
2006                                 void *data SCM_UNUSED)
2007 {
2008     ScmObj obj = argv[0];
2009     ScmObj initargs = argv[1];
2010     ScmObj accs = Scm_ClassOf(obj)->accessors;
2011     if (SCM_NULLP(accs)) return obj;
2012     return object_initialize1(obj, accs, initargs);
2013 }
2014 
2015 static ScmClass *object_initialize_SPEC[] = {
2016     SCM_CLASS_STATIC_PTR(Scm_ObjectClass), SCM_CLASS_STATIC_PTR(Scm_ListClass)
2017 };
2018 static SCM_DEFINE_METHOD(object_initialize_rec,
2019                          &Scm_GenericInitialize,
2020                          2, 0,
2021                          object_initialize_SPEC,
2022                          object_initialize, NULL);
2023 
2024 /* Default equal? delegates compare action to generic function object-equal?.
2025    We can't use VMApply here */
Scm_ObjectCompare(ScmObj x,ScmObj y,int equalp)2026 int Scm_ObjectCompare(ScmObj x, ScmObj y, int equalp)
2027 {
2028     ScmObj r;
2029     if (equalp) {
2030         r = Scm_ApplyRec2(SCM_OBJ(&Scm_GenericObjectEqualP), x, y);
2031         return (SCM_FALSEP(r)? -1 : 0);
2032     } else {
2033         r = Scm_ApplyRec2(SCM_OBJ(&Scm_GenericObjectCompare), x, y);
2034         if (SCM_INTP(r)) {
2035             int ri = SCM_INT_VALUE(r);
2036             if (ri < 0) return -1;
2037             if (ri > 0) return 1;
2038             else return 0;
2039         }
2040         Scm_Error("object %S and %S can't be ordered", x, y);
2041         return 0;               /* dummy */
2042     }
2043 }
2044 
2045 /* Fallback of object-equal? and object-compare.
2046    We return #f for fallback of object-compare, which means two objects
2047    can't be ordered.
2048  */
fallback_compare(ScmObj * argv,int argc,ScmGeneric * gf)2049 static ScmObj fallback_compare(ScmObj *argv, int argc, ScmGeneric *gf)
2050 {
2051     if (argc == 2) return SCM_FALSE;
2052     else return Scm_NoNextMethod(argv, argc, gf);
2053 }
2054 
2055 
2056 /*=====================================================================
2057  * Generic function
2058  */
2059 
generic_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)2060 static ScmObj generic_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
2061 {
2062     ScmGeneric *gf = SCM_NEW_INSTANCE(ScmGeneric, klass);
2063     SCM_PROCEDURE_INIT(gf, 0, 0, SCM_PROC_GENERIC, SCM_FALSE);
2064     gf->methods = SCM_NIL;
2065     gf->dispatcher = NULL;
2066     gf->fallback = Scm_NoNextMethod;
2067     gf->data = NULL;
2068     gf->maxReqargs = 0;
2069     (void)SCM_INTERNAL_MUTEX_INIT(gf->lock);
2070     return SCM_OBJ(gf);
2071 }
2072 
generic_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)2073 static void generic_print(ScmObj obj, ScmPort *port,
2074                           ScmWriteContext *ctx SCM_UNUSED)
2075 {
2076 #if 0  /* enable this to show maxReqargs */
2077     Scm_Printf(port, "#<generic %S (%d:%d)>",
2078                SCM_GENERIC(obj)->common.info,
2079                Scm_Length(SCM_GENERIC(obj)->methods),
2080                SCM_GENERIC(obj)->maxReqargs);
2081 #else
2082     Scm_Printf(port, "#<generic %S (%d)>",
2083                SCM_GENERIC(obj)->common.info,
2084                Scm_Length(SCM_GENERIC(obj)->methods));
2085 #endif
2086 }
2087 
2088 /*
2089  * Accessors
2090  */
generic_name(ScmGeneric * gf)2091 static ScmObj generic_name(ScmGeneric *gf)
2092 {
2093     return gf->common.info;
2094 }
2095 
generic_name_set(ScmGeneric * gf,ScmObj val)2096 static void generic_name_set(ScmGeneric *gf, ScmObj val)
2097 {
2098     gf->common.info = val;
2099 }
2100 
generic_sealed(ScmGeneric * gf)2101 static ScmObj generic_sealed(ScmGeneric *gf)
2102 {
2103     return SCM_GENERIC_SEALED_P(gf)? SCM_TRUE:SCM_FALSE;
2104 }
2105 
generic_methods(ScmGeneric * gf)2106 static ScmObj generic_methods(ScmGeneric *gf)
2107 {
2108     return gf->methods;
2109 }
2110 
generic_methods_set(ScmGeneric * gf,ScmObj val)2111 static void generic_methods_set(ScmGeneric *gf, ScmObj val)
2112 {
2113     int reqs = 0;
2114     ScmObj cp;
2115     SCM_FOR_EACH(cp, val) {
2116         if (!SCM_METHODP(SCM_CAR(cp))) {
2117             Scm_Error("The methods slot of <generic> must be a list of method, but given: %S", val);
2118         }
2119         if (SCM_PROCEDURE_REQUIRED(SCM_CAR(cp)) > reqs) {
2120             reqs = SCM_PROCEDURE_REQUIRED(SCM_CAR(cp));
2121         }
2122     }
2123     if (!SCM_NULLP(cp)) {
2124         Scm_Error("The methods slot of <generic> cannot contain an improper list: %S", val);
2125     }
2126     (void)SCM_INTERNAL_MUTEX_LOCK(gf->lock);
2127     gf->methods = val;
2128     gf->maxReqargs = reqs;
2129     (void)SCM_INTERNAL_MUTEX_UNLOCK(gf->lock);
2130 }
2131 
2132 /* Make base generic function from C */
Scm_MakeBaseGeneric(ScmObj name,ScmObj (* fallback)(ScmObj *,int,ScmGeneric *),void * data)2133 ScmObj Scm_MakeBaseGeneric(ScmObj name,
2134                            ScmObj (*fallback)(ScmObj *, int, ScmGeneric*),
2135                            void *data)
2136 {
2137     ScmGeneric *gf = SCM_GENERIC(generic_allocate(SCM_CLASS_GENERIC, SCM_NIL));
2138     gf->common.info = name;
2139     if (fallback) {
2140         gf->fallback = fallback;
2141         gf->data = data;
2142     }
2143     return SCM_OBJ(gf);
2144 }
2145 
2146 /* default "default method" */
Scm_NoNextMethod(ScmObj * argv,int argc,ScmGeneric * gf)2147 ScmObj Scm_NoNextMethod(ScmObj *argv, int argc, ScmGeneric *gf)
2148 {
2149     Scm_Error("no applicable method for %S with arguments %S",
2150               SCM_OBJ(gf), Scm_ArrayToList(argv, argc));
2151     return SCM_UNDEFINED;       /* dummy */
2152 }
2153 
2154 /* another handy "default method", which does nothing. */
Scm_NoOperation(ScmObj * argv SCM_UNUSED,int argc SCM_UNUSED,ScmGeneric * gf SCM_UNUSED)2155 ScmObj Scm_NoOperation(ScmObj *argv SCM_UNUSED,
2156                        int argc SCM_UNUSED,
2157                        ScmGeneric *gf SCM_UNUSED)
2158 {
2159     return SCM_UNDEFINED;
2160 }
2161 
2162 /* fallback of object-apply */
Scm_InvalidApply(ScmObj * argv,int argc,ScmGeneric * gf SCM_UNUSED)2163 ScmObj Scm_InvalidApply(ScmObj *argv, int argc, ScmGeneric *gf SCM_UNUSED)
2164 {
2165     Scm_Error("invalid application: %S", Scm_ArrayToList(argv, argc));
2166     return SCM_UNDEFINED;
2167 }
2168 
2169 /* method-appliable-for-classes?
2170    NB: This may need to be redesigned once we support eqv specializer.
2171  */
Scm_MethodApplicableForClasses(ScmMethod * m,ScmClass * types[],int nargs)2172 int Scm_MethodApplicableForClasses(ScmMethod *m, ScmClass *types[], int nargs)
2173 {
2174     if (nargs < m->common.required
2175         || (!m->common.optional && nargs != m->common.required)) {
2176         return FALSE;
2177     } else {
2178         ScmClass **sp = m->specializers;
2179         int n = 0;
2180         for (; n < m->common.required; n++) {
2181             if (SCM_EQ(sp[n], SCM_CLASS_TOP)) continue;
2182             if (!Scm_SubtypeP(types[n], sp[n])) return FALSE;
2183         }
2184     }
2185     return TRUE;
2186 }
2187 
2188 /* compute-applicable-methods */
Scm_ComputeApplicableMethods(ScmGeneric * gf,ScmObj * argv,int argc,int applyargs)2189 ScmObj Scm_ComputeApplicableMethods(ScmGeneric *gf, ScmObj *argv, int argc,
2190                                     int applyargs)
2191 {
2192     ScmObj methods = gf->methods, mp, ap;
2193     ScmObj h = SCM_NIL, t = SCM_NIL;
2194     ScmClass *typev_s[PREALLOC_SIZE], **typev = typev_s;
2195     int i, nsel;
2196 
2197     if (SCM_NULLP(methods)) return SCM_NIL;
2198 
2199     if (gf->maxReqargs > PREALLOC_SIZE) {
2200         typev = SCM_NEW_ATOMIC_ARRAY(ScmClass*, gf->maxReqargs);
2201     }
2202     nsel = gf->maxReqargs;
2203     if (applyargs) argc--;
2204     for (i = 0; i < argc && nsel >= 0; i++, nsel--) {
2205         typev[i] = Scm_ClassOf(argv[i]);
2206     }
2207     if (applyargs && nsel) {
2208         SCM_FOR_EACH(ap, argv[argc]) {
2209             if (--nsel >= 0) typev[i++] = Scm_ClassOf(SCM_CAR(ap));
2210             argc++;
2211         }
2212     }
2213 
2214     if (gf->dispatcher
2215         && argc <= SCM_DISPATCHER_MAX_NARGS
2216         && argc >= 1) {
2217         ScmMethodDispatcher *dis = (ScmMethodDispatcher*)gf->dispatcher;
2218         ScmObj p = Scm__MethodDispatcherLookup(dis, typev, argc);
2219         if (SCM_PAIRP(p)) methods = p;
2220     }
2221 
2222     SCM_ASSERT(SCM_PAIRP(methods));
2223     if (SCM_NULLP(SCM_CDR(methods))) {
2224         /* We have only one method, so just check its applicability
2225            and retrun the list without allocation if possible. */
2226         if (Scm_MethodApplicableForClasses(SCM_METHOD(SCM_CAR(methods)),
2227                                            typev, argc)) {
2228             return methods;
2229         } else {
2230             return SCM_NIL;
2231         }
2232     } else {
2233         SCM_FOR_EACH(mp, methods) {
2234             ScmObj m = SCM_CAR(mp);
2235             SCM_ASSERT(SCM_METHODP(m));
2236             if (Scm_MethodApplicableForClasses(SCM_METHOD(m), typev, argc)) {
2237                 SCM_APPEND1(h, t, SCM_OBJ(m));
2238             }
2239         }
2240         return h;
2241     }
2242 }
2243 
compute_applicable_methods(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2244 static ScmObj compute_applicable_methods(ScmNextMethod *nm SCM_UNUSED,
2245                                          ScmObj *argv,
2246                                          int argc SCM_UNUSED,
2247                                          void *data SCM_UNUSED)
2248 {
2249     ScmGeneric *gf = SCM_GENERIC(argv[0]);
2250     ScmObj arglist = argv[1];
2251     int n = Scm_Length(arglist);
2252     if (n < 0) Scm_Error("bad argument list: %S", arglist);
2253 
2254     return Scm_ComputeApplicableMethods(gf, &arglist, 1, TRUE);
2255 }
2256 
2257 static ScmClass *compute_applicable_methods_SPEC[] = {
2258     SCM_CLASS_STATIC_PTR(Scm_GenericClass), SCM_CLASS_STATIC_PTR(Scm_ListClass)
2259 };
2260 static SCM_DEFINE_METHOD(compute_applicable_methods_rec,
2261                          &Scm_GenericComputeApplicableMethods,
2262                          2, 0,
2263                          compute_applicable_methods_SPEC,
2264                          compute_applicable_methods, NULL);
2265 
2266 /* method-more-specific? */
method_more_specific(ScmMethod * x,ScmMethod * y,ScmClass ** targv,int argc SCM_UNUSED)2267 static inline int method_more_specific(ScmMethod *x, ScmMethod *y,
2268                                        ScmClass **targv, int argc SCM_UNUSED)
2269 {
2270     ScmClass **xs = x->specializers;
2271     ScmClass **ys = y->specializers;
2272     int xreq = SCM_PROCEDURE_REQUIRED(x), yreq = SCM_PROCEDURE_REQUIRED(y);
2273 
2274     for (int i=0; i<xreq && i<yreq; i++) {
2275         if (xs[i] != ys[i]) {
2276             ScmClass *ac = targv[i];
2277             if (xs[i] == ac) return TRUE;
2278             if (ys[i] == ac) return FALSE;
2279             for (ScmClass **acpl = ac->cpa; *acpl; acpl++) {
2280                 if (xs[i] == *acpl) return TRUE;
2281                 if (ys[i] == *acpl) return FALSE;
2282             }
2283             /* If we're here, two methods are not orderable. */
2284             Scm_Error("Couldn't determine which method is more specific:"
2285                       " %S and %S: Check if compute-applicable-methods is "
2286                       "working properly.", SCM_OBJ(x), SCM_OBJ(y));
2287         }
2288     }
2289     if (xreq > yreq) return TRUE;
2290     if (xreq < yreq) return FALSE;
2291 
2292     /* all specializers match.  the one without optional arg is more special.*/
2293     if (SCM_PROCEDURE_OPTIONAL(y)) return TRUE;
2294     else return FALSE;
2295 }
2296 
method_more_specific_p(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2297 static ScmObj method_more_specific_p(ScmNextMethod *nm SCM_UNUSED,
2298                                      ScmObj *argv,
2299                                      int argc SCM_UNUSED,
2300                                      void *data SCM_UNUSED)
2301 {
2302     ScmMethod *x = SCM_METHOD(argv[0]);
2303     ScmMethod *y = SCM_METHOD(argv[1]);
2304     ScmObj targlist = argv[2];
2305     ScmClass *targv_s[PREALLOC_SIZE], **targv = targv_s;
2306     int targc = Scm_Length(targlist);
2307     if (targc < 0) Scm_Error("bad targ list: %S", targlist);
2308     if (targc > PREALLOC_SIZE) {
2309         targv = SCM_NEW_ARRAY(ScmClass*, targc);
2310     }
2311     int i = 0;
2312     ScmObj tp;
2313     SCM_FOR_EACH(tp, targlist) {
2314         if (!Scm_TypeP(SCM_CAR(tp), SCM_CLASS_CLASS))
2315             Scm_Error("bad class object in type list: %S", SCM_CAR(tp));
2316         targv[i++] = SCM_CLASS(SCM_CAR(tp));
2317     }
2318     return SCM_MAKE_BOOL(method_more_specific(x, y, targv, targc));
2319 }
2320 static ScmClass *method_more_specific_p_SPEC[] = {
2321     SCM_CLASS_STATIC_PTR(Scm_MethodClass),
2322     SCM_CLASS_STATIC_PTR(Scm_MethodClass),
2323     SCM_CLASS_STATIC_PTR(Scm_ListClass)
2324 };
2325 static SCM_DEFINE_METHOD(method_more_specific_p_rec,
2326                          &Scm_GenericMethodMoreSpecificP,
2327                          3, 0,
2328                          method_more_specific_p_SPEC,
2329                          method_more_specific_p, NULL);
2330 
2331 /* sort-methods
2332  *  This is a naive implementation just to make things work.
2333  *
2334  *  Argv/argc is used to create an array of classes used to order methods.
2335  *  We never need the arguments more than the maximum number of required
2336  *  arguments among the given methods; when VM calls Scm_SortMethods, it
2337  *  only puts as many args as gf->maxReqargs.
2338  *
2339  *  TODO: can't we carry around the method list in array
2340  *  instead of list, at least internally?
2341  */
Scm_SortMethods(ScmObj methods,ScmObj * argv,int argc)2342 ScmObj Scm_SortMethods(ScmObj methods, ScmObj *argv, int argc)
2343 {
2344     ScmObj array_s[PREALLOC_SIZE], *array = array_s;
2345     ScmClass *targv_s[PREALLOC_SIZE], **targv = targv_s;
2346     int cnt = 0, len = Scm_Length(methods);
2347 
2348     if (len >= PREALLOC_SIZE)  array = SCM_NEW_ARRAY(ScmObj, len);
2349     if (argc >= PREALLOC_SIZE) targv = SCM_NEW_ARRAY(ScmClass*, argc);
2350 
2351     ScmObj mp;
2352     SCM_FOR_EACH(mp, methods) {
2353         if (!Scm_TypeP(SCM_CAR(mp), SCM_CLASS_METHOD))
2354             Scm_Error("bad method in applicable method list: %S", SCM_CAR(mp));
2355         array[cnt] = SCM_CAR(mp);
2356         cnt++;
2357     }
2358     for (int i=0; i<argc; i++) targv[i] = Scm_ClassOf(argv[i]);
2359 
2360     for (int step = len/2; step > 0; step /= 2) {
2361         for (int i=step; i<len; i++) {
2362             for (int j=i-step; j >= 0; j -= step) {
2363                 if (method_more_specific(SCM_METHOD(array[j]),
2364                                          SCM_METHOD(array[j+step]),
2365                                          targv, argc)) {
2366                     break;
2367                 } else {
2368                     ScmObj tmp = array[j+step];
2369                     array[j+step] = array[j];
2370                     array[j] = tmp;
2371                 }
2372             }
2373         }
2374     }
2375     return Scm_ArrayToList(array, len);
2376 }
2377 
2378 
2379 /* Developer API.  Accessible from Scheme via generic-build-dispatcher!
2380    If axis is out of range, we do nothing and returns #f.
2381  */
Scm__GenericBuildDispatcher(ScmGeneric * gf,int axis)2382 ScmObj Scm__GenericBuildDispatcher(ScmGeneric *gf, int axis)
2383 {
2384     if (!disable_generic_dispatcher
2385         && axis >= 0 && axis < SCM_DISPATCHER_MAX_NARGS) {
2386         (void)SCM_INTERNAL_MUTEX_LOCK(gf->lock);
2387         gf->dispatcher = Scm__BuildMethodDispatcher(gf->methods, axis);
2388         (void)SCM_INTERNAL_MUTEX_UNLOCK(gf->lock);
2389         return SCM_TRUE;
2390     } else {
2391         return SCM_FALSE;
2392     }
2393 }
2394 
2395 /* Developer API */
Scm__GenericInvalidateDispatcher(ScmGeneric * gf)2396 void Scm__GenericInvalidateDispatcher(ScmGeneric *gf)
2397 {
2398     (void)SCM_INTERNAL_MUTEX_LOCK(gf->lock);
2399     gf->dispatcher = NULL;
2400     (void)SCM_INTERNAL_MUTEX_UNLOCK(gf->lock);
2401 }
2402 
2403 /* Developer API */
Scm__GenericDispatcherInfo(ScmGeneric * gf)2404 ScmObj Scm__GenericDispatcherInfo(ScmGeneric *gf)
2405 {
2406     if (gf->dispatcher) {
2407         return Scm__MethodDispatcherInfo(gf->dispatcher);
2408     } else {
2409         return SCM_FALSE;
2410     }
2411 }
2412 
2413 /* Developer API */
Scm__GenericDispatcherDump(ScmGeneric * gf,ScmPort * port)2414 void Scm__GenericDispatcherDump(ScmGeneric *gf, ScmPort *port)
2415 {
2416     if (gf->dispatcher) {
2417         Scm_Printf(port, "%S's dispatcher:\n", gf);
2418         Scm__MethodDispatcherDump((ScmMethodDispatcher*)gf->dispatcher, port);
2419     } else {
2420         Scm_Printf(port, "%S doesn't have a dispatcher.\n", gf);
2421     }
2422 }
2423 
2424 /*=====================================================================
2425  * Method
2426  */
2427 
method_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)2428 static ScmObj method_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
2429 {
2430     ScmMethod *instance = SCM_NEW_INSTANCE(ScmMethod, klass);
2431     SCM_PROCEDURE_INIT(instance, 0, 0, SCM_PROC_METHOD, SCM_FALSE);
2432     instance->generic = NULL;
2433     instance->specializers = NULL;
2434     instance->func = NULL;
2435     return SCM_OBJ(instance);
2436 }
2437 
method_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)2438 static void method_print(ScmObj obj, ScmPort *port,
2439                          ScmWriteContext *ctx SCM_UNUSED)
2440 {
2441     Scm_Printf(port, "#<method %S>", SCM_METHOD(obj)->common.info);
2442 }
2443 
2444 /* See if this method doesn't use 'next-method' in its body. */
method_leaf_p(ScmClosure * body)2445 static int method_leaf_p(ScmClosure *body)
2446 {
2447     ScmCompiledCode *code = SCM_COMPILED_CODE(SCM_CLOSURE_CODE(body));
2448     if (!SCM_PAIRP(code->signatureInfo)
2449         || !SCM_PAIRP(SCM_CAR(code->signatureInfo))) return FALSE;
2450     ScmObj attr = Scm_PairAttrGet(SCM_PAIR(SCM_CAR(code->signatureInfo)),
2451                                   SCM_SYM_UNUSED_ARGS,
2452                                   SCM_NIL);
2453     return !SCM_FALSEP(Scm_Memq(SCM_SYM_NEXT_METHOD, attr));
2454 }
2455 
2456 /*
2457  * (initialize <method> (&key lamdba-list generic specializers body method-locked))
2458  *    Method initialization.   This needs to be hardcoded, since
2459  *    we can't call Scheme version of initialize to initialize the
2460  *    "initialize" method (chicken-and-egg circularity).
2461  */
method_initialize(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2462 static ScmObj method_initialize(ScmNextMethod *nm SCM_UNUSED,
2463                                 ScmObj *argv,
2464                                 int argc SCM_UNUSED,
2465                                 void *data SCM_UNUSED)
2466 {
2467     ScmMethod *m = SCM_METHOD(argv[0]);
2468     ScmObj initargs = argv[1];
2469     ScmObj llist = Scm_GetKeyword(key_lambda_list, initargs, SCM_FALSE);
2470     ScmObj generic = Scm_GetKeyword(key_generic, initargs, SCM_FALSE);
2471     ScmObj specs = Scm_GetKeyword(key_specializers, initargs, SCM_FALSE);
2472     ScmObj body = Scm_GetKeyword(key_body, initargs, SCM_FALSE);
2473     ScmObj locked = Scm_GetKeyword(key_method_locked, initargs, SCM_FALSE);
2474     ScmObj lp, h, t;
2475     int speclen = 0, req = 0, opt = 0;
2476 
2477     if (!Scm_TypeP(generic, SCM_CLASS_GENERIC))
2478         Scm_Error("generic function required for :generic argument: %S",
2479                   generic);
2480     ScmGeneric *g = SCM_GENERIC(generic);
2481     if (!SCM_CLOSUREP(body))
2482         Scm_Error("closure required for :body argument: %S", body);
2483     if ((speclen = Scm_Length(specs)) < 0)
2484         Scm_Error("invalid specializers list: %S", specs);
2485     ScmClass **specarray = class_list_to_array(specs, speclen);
2486 
2487     /* find out # of args from lambda list */
2488     SCM_FOR_EACH(lp, llist) req++;
2489     if (!SCM_NULLP(lp)) opt++;
2490 
2491     if (SCM_PROCEDURE_REQUIRED(body) != req + opt + 1)
2492         Scm_Error("method body %S doesn't match with lambda list %S",
2493                   body, llist);
2494     if (speclen != req)
2495         Scm_Error("specializer list doesn't match with lambda list: %S",specs);
2496 
2497     m->common.required = req;
2498     m->common.optional = opt;
2499     m->common.info = Scm_Cons(g->common.info,
2500                               class_array_to_names(specarray, speclen));
2501     m->common.leaf = method_leaf_p(SCM_CLOSURE(body));
2502     m->generic = g;
2503     m->specializers = specarray;
2504     m->func = NULL;
2505     m->data = SCM_CLOSURE_CODE(body);
2506     m->env = SCM_CLOSURE_ENV(body);
2507 
2508     SCM_METHOD_LOCKED(m) = SCM_BOOL_VALUE(locked);
2509 
2510     /* NB: for comprehensive debugging & profiling information, we modify
2511        the 'name' field of the compiled code to contain
2512        (generic-name specializer-class-names ...).  It may be a hazard if
2513        some existing named closure is given as BODY; as far as the standard
2514        macro is used, though, altering it should be OK. */
2515     h = t = SCM_NIL;
2516     for (int i=0; i<speclen; i++) {
2517         SCM_APPEND1(h, t, specarray[i]->name);
2518     }
2519     SCM_COMPILED_CODE(m->data)->name = Scm_Cons(SCM_PROCEDURE_INFO(g), h);
2520 
2521     /* Register this method to all classes in the specializers.
2522        This has to come after the part that may throw an error. */
2523     for (int i=0; i<speclen; i++) {
2524         Scm_AddDirectMethod(specarray[i], m);
2525     }
2526     return SCM_OBJ(m);
2527 }
2528 
2529 static ScmClass *method_initialize_SPEC[] = {
2530     SCM_CLASS_STATIC_PTR(Scm_MethodClass),
2531     SCM_CLASS_STATIC_PTR(Scm_ListClass)
2532 };
2533 static SCM_DEFINE_METHOD(method_initialize_rec,
2534                          &Scm_GenericInitialize,
2535                          2, 0,
2536                          method_initialize_SPEC,
2537                          method_initialize, NULL);
2538 
2539 /*
2540  * Accessors
2541  */
method_required(ScmMethod * m)2542 static ScmObj method_required(ScmMethod *m)
2543 {
2544     return SCM_MAKE_INT(m->common.required);
2545 }
2546 
method_optional(ScmMethod * m)2547 static ScmObj method_optional(ScmMethod *m)
2548 {
2549     return SCM_MAKE_BOOL(m->common.optional);
2550 }
2551 
method_locked(ScmMethod * m)2552 static ScmObj method_locked(ScmMethod *m)
2553 {
2554     return SCM_MAKE_BOOL(SCM_METHOD_LOCKED(m));
2555 }
2556 
method_leaf(ScmMethod * m)2557 static ScmObj method_leaf(ScmMethod *m)
2558 {
2559     return SCM_MAKE_BOOL(SCM_METHOD_LEAF_P(m));
2560 }
2561 
method_generic(ScmMethod * m)2562 static ScmObj method_generic(ScmMethod *m)
2563 {
2564     return m->generic ? SCM_OBJ(m->generic) : SCM_FALSE;
2565 }
2566 
method_generic_set(ScmMethod * m,ScmObj val)2567 static void method_generic_set(ScmMethod *m, ScmObj val)
2568 {
2569     if (SCM_GENERICP(val))
2570         m->generic = SCM_GENERIC(val);
2571     else
2572         Scm_Error("generic function required, but got %S", val);
2573 }
2574 
method_specializers(ScmMethod * m)2575 static ScmObj method_specializers(ScmMethod *m)
2576 {
2577     if (m->specializers) {
2578         return class_array_to_list(m->specializers, m->common.required);
2579     } else {
2580         return SCM_NIL;
2581     }
2582 }
2583 
method_specializers_set(ScmMethod * m,ScmObj val)2584 static void method_specializers_set(ScmMethod *m, ScmObj val)
2585 {
2586     int len = Scm_Length(val);
2587     if (len != m->common.required)
2588         Scm_Error("specializer list doesn't match body's lambda list: %S", val);
2589     if (len == 0)
2590         m->specializers = NULL;
2591     else
2592         m->specializers = class_list_to_array(val, len);
2593 }
2594 
2595 /* update-direct-method! method old-class new-class
2596  *   To be called during class redefinition, and swaps reference of
2597  *   old-class for new-class.
2598  *
2599  *   This procedure swaps the pointer "in-place", so as far as the pointer
2600  *   arithmetic is atomic, we won't have a race condition.  Class
2601  *   redefinition is serialized inside class-redefinition, so we won't
2602  *   have the case that more than one thread call this procedure with
2603  *   the same OLD pointer.  It is possible that more than one thread call
2604  *   this procedure on the same method simultaneously, but the OLD pointer
2605  *   should differ, and it won't do any harm for them to run concurrently.
2606  *
2607  *   Note that if we implement this in Scheme, we need a mutex to lock the
2608  *   specializer array.
2609  */
Scm_UpdateDirectMethod(ScmMethod * m,ScmClass * old,ScmClass * newc)2610 ScmObj Scm_UpdateDirectMethod(ScmMethod *m, ScmClass *old, ScmClass *newc)
2611 {
2612     int rec = SCM_PROCEDURE_REQUIRED(m);
2613     ScmClass **sp = m->specializers;
2614     for (int i=0; i<rec; i++) {
2615         if (sp[i] == old) sp[i] = newc;
2616     }
2617     if (SCM_FALSEP(Scm_Memq(SCM_OBJ(m), newc->directMethods))) {
2618         newc->directMethods = Scm_Cons(SCM_OBJ(m), newc->directMethods);
2619     }
2620     /* NB: For now, we just invalidate dispatcher.  Redefining class may
2621        trigger massive update-direct-method! and it's inefficient to rebuild
2622        dispatcher table for every invocation of it.
2623      */
2624     Scm__GenericInvalidateDispatcher(m->generic);
2625     return SCM_OBJ(m);
2626 }
2627 
generic_updatedirectmethod(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2628 static ScmObj generic_updatedirectmethod(ScmNextMethod *nm SCM_UNUSED,
2629                                          ScmObj *argv,
2630                                          int argc SCM_UNUSED,
2631                                          void *data SCM_UNUSED)
2632 {
2633     return Scm_UpdateDirectMethod(SCM_METHOD(argv[0]),
2634                                   SCM_CLASS(argv[1]),
2635                                   SCM_CLASS(argv[2]));
2636 }
2637 
2638 static ScmClass *generic_updatedirectmethod_SPEC[] = {
2639     SCM_CLASS_STATIC_PTR(Scm_MethodClass),
2640     SCM_CLASS_STATIC_PTR(Scm_ClassClass),
2641     SCM_CLASS_STATIC_PTR(Scm_ClassClass)
2642 };
2643 static SCM_DEFINE_METHOD(generic_updatedirectmethod_rec,
2644                          &Scm_GenericUpdateDirectMethod, 3, 0,
2645                          generic_updatedirectmethod_SPEC,
2646                          generic_updatedirectmethod, NULL);
2647 
2648 /*
2649  * ADD-METHOD, and it's default method version.
2650  */
Scm_AddMethod(ScmGeneric * gf,ScmMethod * method)2651 ScmObj Scm_AddMethod(ScmGeneric *gf, ScmMethod *method)
2652 {
2653     if (method->generic && method->generic != gf)
2654         Scm_Error("method %S already added to a generic function %S",
2655                   method, method->generic);
2656     if (!SCM_FALSEP(Scm_Memq(SCM_OBJ(method), gf->methods)))
2657         Scm_Error("method %S already appears in a method list of generic %S"
2658                   " something wrong in MOP implementation?",
2659                   method, gf);
2660     if (SCM_GENERIC_SEALED_P(gf)) {
2661         Scm_Warn("Attempt to add a method to a sealed generic %S. "
2662                  "You may need to recompile code that calls it.", gf);
2663     }
2664 
2665     int reqs = gf->maxReqargs;  /* # of maximum required args */
2666     method->generic = gf;
2667     /* pre-allocate cons pair to avoid triggering GC in the critical region */
2668     ScmObj pair = Scm_Cons(SCM_OBJ(method), gf->methods);
2669     if (SCM_PROCEDURE_REQUIRED(method) > reqs) {
2670         reqs = SCM_PROCEDURE_REQUIRED(method);
2671     }
2672 
2673     /* Check if a method with the same signature exists.
2674        If so, we replace the method instead of adding it.  */
2675     ScmMethod *replaced = NULL;
2676     ScmMethod *method_locked = NULL;
2677     (void)SCM_INTERNAL_MUTEX_LOCK(gf->lock);
2678     ScmObj mp;
2679     SCM_FOR_EACH(mp, gf->methods) {
2680         ScmMethod *mm = SCM_METHOD(SCM_CAR(mp));
2681         if (SCM_PROCEDURE_REQUIRED(method) == SCM_PROCEDURE_REQUIRED(mm)
2682             && SCM_PROCEDURE_OPTIONAL(method) == SCM_PROCEDURE_OPTIONAL(mm)) {
2683             ScmClass **sp1 = method->specializers;
2684             ScmClass **sp2 = mm->specializers;
2685             int i;
2686             for (i=0; i<SCM_PROCEDURE_REQUIRED(method); i++) {
2687                 if (sp1[i] != sp2[i]) break;
2688             }
2689             if (i == SCM_PROCEDURE_REQUIRED(method)) {
2690                 if (SCM_METHOD_LOCKED(mm)) {
2691                     /* We'll throw an error */
2692                     method_locked = mm;
2693                 } else {
2694                     replaced = mm;
2695                     Scm_SetCar(mp, SCM_OBJ(method));
2696                 }
2697                 break;
2698             }
2699         }
2700     }
2701     if (!replaced && (method_locked == NULL)) {
2702         gf->methods = pair;
2703         gf->maxReqargs = reqs;
2704     }
2705     if (gf->dispatcher && (method_locked == NULL)) {
2706         ScmMethodDispatcher *dis = (ScmMethodDispatcher*)gf->dispatcher;
2707         if (replaced) Scm__MethodDispatcherDelete(dis, replaced);
2708         Scm__MethodDispatcherAdd(dis, method);
2709     }
2710     (void)SCM_INTERNAL_MUTEX_UNLOCK(gf->lock);
2711 
2712     if (method_locked != NULL) {
2713         Scm_Error("Attempt to replace a locked method %S",
2714                   SCM_OBJ(method_locked));
2715     }
2716     return SCM_UNDEFINED;
2717 }
2718 
generic_addmethod(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2719 static ScmObj generic_addmethod(ScmNextMethod *nm SCM_UNUSED,
2720                                 ScmObj *argv,
2721                                 int argc SCM_UNUSED,
2722                                 void *data SCM_UNUSED)
2723 {
2724     return Scm_AddMethod(SCM_GENERIC(argv[0]), SCM_METHOD(argv[1]));
2725 }
2726 
2727 static ScmClass *generic_addmethod_SPEC[] = {
2728     SCM_CLASS_STATIC_PTR(Scm_GenericClass),
2729     SCM_CLASS_STATIC_PTR(Scm_MethodClass)
2730 };
2731 static SCM_DEFINE_METHOD(generic_addmethod_rec,
2732                          &Scm_GenericAddMethod, 2, 0,
2733                          generic_addmethod_SPEC,
2734                          generic_addmethod, NULL);
2735 
2736 /*
2737  * DELETE-METHOD, and it's default method version.
2738  */
Scm_DeleteMethod(ScmGeneric * gf,ScmMethod * method)2739 ScmObj Scm_DeleteMethod(ScmGeneric *gf, ScmMethod *method)
2740 {
2741     if (!method->generic || method->generic != gf) return SCM_UNDEFINED;
2742 
2743     (void)SCM_INTERNAL_MUTEX_LOCK(gf->lock);
2744     ScmObj mp = gf->methods;
2745     if (SCM_PAIRP(mp)) {
2746         if (SCM_EQ(SCM_CAR(mp), SCM_OBJ(method))) {
2747             gf->methods = SCM_CDR(mp);
2748             method->generic = NULL;
2749         } else {
2750             while (SCM_PAIRP(SCM_CDR(mp))) {
2751                 if (SCM_EQ(SCM_CADR(mp), SCM_OBJ(method))) {
2752                     SCM_CDR(mp) = SCM_CDDR(mp);
2753                     method->generic = NULL;
2754                     break;
2755                 }
2756                 mp = SCM_CDR(mp);
2757             }
2758         }
2759     }
2760     if (gf->dispatcher) {
2761         Scm__MethodDispatcherDelete((ScmMethodDispatcher*)gf->dispatcher,
2762                                     method);
2763     }
2764     SCM_FOR_EACH(mp, gf->methods) {
2765         /* sync # of required selector */
2766         if (SCM_PROCEDURE_REQUIRED(SCM_CAR(mp)) > gf->maxReqargs) {
2767             gf->maxReqargs = SCM_PROCEDURE_REQUIRED(SCM_CAR(mp));
2768         }
2769     }
2770     (void)SCM_INTERNAL_MUTEX_UNLOCK(gf->lock);
2771     return SCM_UNDEFINED;
2772 }
2773 
generic_deletemethod(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2774 static ScmObj generic_deletemethod(ScmNextMethod *nm SCM_UNUSED,
2775                                    ScmObj *argv,
2776                                    int argc SCM_UNUSED,
2777                                    void *data SCM_UNUSED)
2778 {
2779     return Scm_DeleteMethod(SCM_GENERIC(argv[0]), SCM_METHOD(argv[1]));
2780 }
2781 
2782 static ScmClass *generic_deletemethod_SPEC[] = {
2783     SCM_CLASS_STATIC_PTR(Scm_GenericClass),
2784     SCM_CLASS_STATIC_PTR(Scm_MethodClass)
2785 };
2786 static SCM_DEFINE_METHOD(generic_deletemethod_rec,
2787                          &Scm_GenericDeleteMethod, 2, 0,
2788                          generic_deletemethod_SPEC,
2789                          generic_deletemethod, NULL);
2790 
2791 /*=====================================================================
2792  * Next Method
2793  */
2794 
Scm_MakeNextMethod(ScmGeneric * gf,ScmObj methods,ScmObj * argv,int argc,int copyargs,int applyargs)2795 ScmObj Scm_MakeNextMethod(ScmGeneric *gf, ScmObj methods,
2796                           ScmObj *argv, int argc, int copyargs, int applyargs)
2797 {
2798     ScmNextMethod *nm = SCM_NEW(ScmNextMethod);
2799     SCM_SET_CLASS(nm, SCM_CLASS_NEXT_METHOD);
2800     SCM_PROCEDURE_INIT(nm, 0, 0, SCM_PROC_NEXT_METHOD, SCM_FALSE);
2801     nm->generic = gf;
2802     nm->methods = methods;
2803     if (copyargs) {
2804         nm->argv = SCM_NEW_ARRAY(ScmObj, argc);
2805         memcpy(nm->argv, argv, sizeof(ScmObj)*argc);
2806     } else {
2807         nm->argv = argv;
2808     }
2809     nm->argc = argc;
2810     nm->applyargs = applyargs;
2811     return SCM_OBJ(nm);
2812 }
2813 
next_method_print(ScmObj obj,ScmPort * out,ScmWriteContext * ctx SCM_UNUSED)2814 static void next_method_print(ScmObj obj, ScmPort *out,
2815                               ScmWriteContext *ctx SCM_UNUSED)
2816 {
2817     ScmNextMethod *nm = SCM_NEXT_METHOD(obj);
2818     ScmObj args = Scm_ArrayToList(nm->argv, nm->argc);
2819     Scm_Printf(out, "#<next-method %S%d %S>", nm->methods, nm->applyargs, args);
2820 }
2821 
2822 /*=====================================================================
2823  * Accessor Method
2824  */
2825 
accessor_method_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)2826 static void accessor_method_print(ScmObj obj, ScmPort *port,
2827                                   ScmWriteContext *ctx SCM_UNUSED)
2828 {
2829     Scm_Printf(port, "#<accessor-method %S>", SCM_METHOD(obj)->common.info);
2830 }
2831 
accessor_get_proc(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2832 static ScmObj accessor_get_proc(ScmNextMethod *nm SCM_UNUSED,
2833                                 ScmObj *argv,
2834                                 int argc SCM_UNUSED,
2835                                 void *data SCM_UNUSED)
2836 {
2837     ScmObj obj = argv[0];
2838     ScmSlotAccessor *ca = (ScmSlotAccessor*)data;
2839     /* NB: we need this extra check, in case if the getter method of parent
2840        class and the one of subclass don't share the generic function, and
2841        the getter method of parent class is called on the subclass's instance.
2842        See test/object.scm "module and accessor" section for a concrete
2843        example. */
2844     if (!SCM_EQ(Scm_ClassOf(obj), ca->klass)) {
2845         /* fallback to a normal protocol */
2846         return Scm_VMSlotRef(obj, ca->name, FALSE);
2847     }
2848     /* Standard path.  We can skip searching the slot, so it is faster. */
2849     return slot_ref_using_accessor(obj, ca, FALSE);
2850 }
2851 
accessor_set_proc(ScmNextMethod * nm SCM_UNUSED,ScmObj * argv,int argc SCM_UNUSED,void * data SCM_UNUSED)2852 static ScmObj accessor_set_proc(ScmNextMethod *nm SCM_UNUSED,
2853                                 ScmObj *argv,
2854                                 int argc SCM_UNUSED,
2855                                 void *data SCM_UNUSED)
2856 {
2857     ScmObj obj = argv[0];
2858     ScmObj val = argv[1];
2859     ScmSlotAccessor *ca = (ScmSlotAccessor*)data;
2860     /* See the comment in accessor_get_proc above about this check. */
2861     if (!SCM_EQ(Scm_ClassOf(obj), ca->klass)) {
2862         return Scm_VMSlotSet(obj, ca->name, val);
2863     }
2864     return slot_set_using_accessor(obj, ca, val);
2865 }
2866 
2867 /* Accessor method can be just created by usual allocate/initialize
2868    sequence.  But it requires :slot-accessor initarg.  The method body
2869    is overridden by C function, and the closure given to :body doesn't
2870    have an effect.  */
accessor_method_initialize(ScmNextMethod * nm,ScmObj * argv,int argc,void * data)2871 static ScmObj accessor_method_initialize(ScmNextMethod *nm, ScmObj *argv,
2872                                          int argc, void *data)
2873 {
2874     ScmMethod *m = SCM_METHOD(method_initialize(nm, argv, argc, data));
2875     ScmObj initargs = argv[1];
2876     ScmObj sa = Scm_GetKeyword(key_slot_accessor, initargs, SCM_FALSE);
2877 
2878     if (!SCM_SLOT_ACCESSOR_P(sa)) {
2879         Scm_Error("slot accessor required for :slot-accessor argument: %S",
2880                   sa);
2881     }
2882 
2883     m->data = sa;
2884     switch (SCM_PROCEDURE_REQUIRED(m)) {
2885     case 1: /* accessor <obj> - this is a getter */
2886         m->func = accessor_get_proc;
2887         break;
2888     case 2: /* accessor <obj> <val> - this is a setter */
2889         m->func = accessor_set_proc;
2890         break;
2891     default:
2892         Scm_Error("bad initialization parameter for accessor method %S", m);
2893     }
2894     return SCM_OBJ(m);
2895 }
2896 
2897 static ScmClass *accessor_method_initialize_SPEC[] = {
2898     SCM_CLASS_STATIC_PTR(Scm_AccessorMethodClass),
2899     SCM_CLASS_STATIC_PTR(Scm_ListClass)
2900 };
2901 static SCM_DEFINE_METHOD(accessor_method_initialize_rec,
2902                          &Scm_GenericInitialize,
2903                          2, 0,
2904                          accessor_method_initialize_SPEC,
2905                          accessor_method_initialize, NULL);
2906 
accessor_method_slot_accessor(ScmAccessorMethod * m)2907 static ScmObj accessor_method_slot_accessor(ScmAccessorMethod *m)
2908 {
2909     SCM_ASSERT(SCM_SLOT_ACCESSOR_P(m->data));
2910     return SCM_OBJ(m->data);
2911 }
2912 
accessor_method_slot_accessor_set(ScmAccessorMethod * m,ScmObj v)2913 static void accessor_method_slot_accessor_set(ScmAccessorMethod *m, ScmObj v)
2914 {
2915     if (!SCM_SLOT_ACCESSOR_P(v)) {
2916         Scm_Error("slot accessor required, but got %S", v);
2917     }
2918     m->data = v;
2919 }
2920 
2921 /*=====================================================================
2922  * Foreign pointer mechanism
2923  */
2924 
2925 /* foreign pointer instance flags */
2926 enum {
2927     SCM_FOREIGN_POINTER_INVALID = (1L<<0) /* The pointer is no longer valid. */
2928 };
2929 
2930 struct foreign_data_rec {
2931     int flags;
2932     ScmForeignCleanupProc cleanup;
2933     ScmInternalMutex attr_mutex;     /* lock for updating foreign pointer's
2934                                         "attribute" slot.  we use one-per-class
2935                                         mutex, assuming the mutation of attrs
2936                                         is rare and saving space per foreign
2937                                         pointer is more important. */
2938     ScmHashCore *identity_map;       /* for KEEP_IDENTITY */
2939     ScmInternalMutex identity_mutex; /* lock for identity_map */
2940 };
2941 
Scm_MakeForeignPointerClass(ScmModule * mod,const char * name,ScmClassPrintProc print_proc,ScmForeignCleanupProc cleanup_proc,int flags)2942 ScmClass *Scm_MakeForeignPointerClass(ScmModule *mod,
2943                                       const char *name,
2944                                       ScmClassPrintProc print_proc,
2945                                       ScmForeignCleanupProc cleanup_proc,
2946                                       int flags)
2947 {
2948     ScmClass *fp = (ScmClass*)class_allocate(SCM_CLASS_CLASS, SCM_NIL);
2949     ScmObj s = SCM_INTERN(name);
2950     struct foreign_data_rec *data = SCM_NEW(struct foreign_data_rec);
2951     /* NB: here we don't need to use SCM_CLASS_STATIC_PTR, since we only
2952        refer intra-dll classes, and we don't go through init_class.
2953        If we ever find the need to go through init_class, do not forget
2954        to change fpcpa initializers as well, to make it work on windows. */
2955     static ScmClass *fpcpa[] = { SCM_CLASS_FOREIGN_POINTER,
2956                                  SCM_CLASS_TOP,
2957                                  NULL };
2958     fp->name = s;
2959     fp->allocate = NULL;
2960     fp->print = print_proc;
2961     fp->cpa = fpcpa;
2962     fp->flags = SCM_CLASS_BUILTIN;
2963     initialize_builtin_cpl(fp, SCM_FALSE);
2964     Scm_Define(mod, SCM_SYMBOL(s), SCM_OBJ(fp));
2965     fp->slots = SCM_NIL;
2966     fp->accessors = SCM_NIL;
2967     data->flags = flags;
2968     data->cleanup = cleanup_proc;
2969     (void)SCM_INTERNAL_MUTEX_INIT(data->attr_mutex);
2970     if (flags & SCM_FOREIGN_POINTER_KEEP_IDENTITY) {
2971         (void)SCM_INTERNAL_MUTEX_INIT(data->identity_mutex);
2972         data->identity_map = SCM_NEW(ScmHashCore);
2973         Scm_HashCoreInitSimple(data->identity_map, SCM_HASH_WORD, 256, NULL);
2974     } else {
2975         data->identity_map = NULL;
2976     }
2977     fp->data = (void*)data; /* see the note above class_allocate() */
2978     return fp;
2979 }
2980 
fp_finalize(ScmObj obj,void * data)2981 static void fp_finalize(ScmObj obj, void *data)
2982 {
2983     void (*cleanup)(ScmObj) = (void (*)(ScmObj))data;
2984     cleanup(obj);
2985 }
2986 
2987 /* This shouldn't raise an error. */
make_foreign_int(ScmClass * klass,void * ptr,ScmObj attr,struct foreign_data_rec * data)2988 static ScmForeignPointer *make_foreign_int(ScmClass *klass, void *ptr,
2989                                            ScmObj attr,
2990                                            struct foreign_data_rec *data)
2991 {
2992     ScmForeignPointer *obj = SCM_NEW(ScmForeignPointer);
2993     SCM_SET_CLASS(obj, klass);
2994     obj->ptr = ptr;
2995     obj->attributes = attr;
2996     obj->flags = 0;
2997     if (data->cleanup) {
2998         Scm_RegisterFinalizer(SCM_OBJ(obj), fp_finalize, data->cleanup);
2999     }
3000     return obj;
3001 }
3002 
3003 /* Note for future API: Scm_MakeForeignPointer should take attr argument.
3004    We add *WithAttr only to keep ABI compatibility. */
Scm_MakeForeignPointer(ScmClass * klass,void * ptr)3005 ScmObj Scm_MakeForeignPointer(ScmClass *klass, void *ptr)
3006 {
3007     return Scm_MakeForeignPointerWithAttr(klass, ptr, SCM_NIL);
3008 }
3009 
Scm_MakeForeignPointerWithAttr(ScmClass * klass,void * ptr,ScmObj attr)3010 ScmObj Scm_MakeForeignPointerWithAttr(ScmClass *klass, void *ptr, ScmObj attr)
3011 {
3012     ScmForeignPointer *obj;
3013     struct foreign_data_rec *data = (struct foreign_data_rec *)klass->data;
3014 
3015     if (!klass) {               /* for extra safety */
3016         Scm_Error("NULL pointer passed to Scm_MakeForeignPointer");
3017     }
3018     if (!Scm_SubtypeP(klass, SCM_CLASS_FOREIGN_POINTER)) {
3019         Scm_Error("attempt to instantiate non-foreign-pointer class %S via Scm_MakeForeignPointer", klass);
3020     }
3021 
3022     if (ptr == NULL && (data->flags & SCM_FOREIGN_POINTER_MAP_NULL)) {
3023         return SCM_FALSE;
3024     }
3025 
3026     if (data->identity_map) {
3027         (void)SCM_INTERNAL_MUTEX_LOCK(data->identity_mutex);
3028         ScmDictEntry *e = Scm_HashCoreSearch(data->identity_map,
3029                                              (intptr_t)ptr, SCM_DICT_CREATE);
3030         if (e->value) {
3031             if (Scm_WeakBoxEmptyP((ScmWeakBox*)e->value)) {
3032                 obj = make_foreign_int(klass, ptr, attr, data);
3033                 Scm_WeakBoxSet((ScmWeakBox*)e->value, obj);
3034             } else {
3035                 obj = (ScmForeignPointer*)Scm_WeakBoxRef((ScmWeakBox*)e->value);
3036             }
3037         } else {
3038             obj = make_foreign_int(klass, ptr, attr, data);
3039             e->value = (intptr_t)Scm_MakeWeakBox(obj);
3040         }
3041         (void)SCM_INTERNAL_MUTEX_UNLOCK(data->identity_mutex);
3042     } else {
3043         obj = make_foreign_int(klass, ptr, attr, data);
3044     }
3045     return SCM_OBJ(obj);
3046 }
3047 
Scm_ForeignPointerRef(ScmForeignPointer * fp)3048 void *Scm_ForeignPointerRef(ScmForeignPointer *fp)
3049 {
3050     if (Scm_ForeignPointerInvalidP(fp)) {
3051         Scm_Error("attempt to dereference a foreign pointer "
3052                   "that is no longer valid: %S", SCM_OBJ(fp));
3053 
3054     }
3055     return fp->ptr;
3056 }
3057 
Scm_ForeignPointerInvalidP(ScmForeignPointer * fp)3058 int Scm_ForeignPointerInvalidP(ScmForeignPointer *fp)
3059 {
3060     return (fp->flags & SCM_FOREIGN_POINTER_INVALID);
3061 }
3062 
Scm_ForeignPointerInvalidate(ScmForeignPointer * fp)3063 void Scm_ForeignPointerInvalidate(ScmForeignPointer *fp)
3064 {
3065     fp->flags |= SCM_FOREIGN_POINTER_INVALID;
3066 }
3067 
Scm_ForeignPointerAttr(ScmForeignPointer * fp)3068 ScmObj Scm_ForeignPointerAttr(ScmForeignPointer *fp)
3069 {
3070     return fp->attributes;
3071 }
3072 
Scm_ForeignPointerAttrGet(ScmForeignPointer * fp,ScmObj key,ScmObj fallback)3073 ScmObj Scm_ForeignPointerAttrGet(ScmForeignPointer *fp,
3074                                  ScmObj key, ScmObj fallback)
3075 {
3076     /* no need to lock, for AttrSet won't make fp->attributes inconsisnent
3077        at any moment. */
3078     ScmObj p = Scm_Assq(key, fp->attributes);
3079     if (SCM_PAIRP(p)) return SCM_CDR(p);
3080     if (SCM_UNBOUNDP(fallback)) {
3081         Scm_Error("No value associated with key %S in a foreign pointer %S",
3082                   key, SCM_OBJ(fp));
3083     }
3084     return fallback;
3085 }
3086 
Scm_ForeignPointerAttrSet(ScmForeignPointer * fp,ScmObj key,ScmObj value)3087 ScmObj Scm_ForeignPointerAttrSet(ScmForeignPointer *fp,
3088                                  ScmObj key, ScmObj value)
3089 {
3090     struct foreign_data_rec *data
3091         = (struct foreign_data_rec*)(SCM_CLASS_OF(fp)->data);
3092 
3093     /* NB: We presume mutating foreign pointer attributes is rare operation,
3094        so we don't try hard to make it efficient.   Particularly, we use
3095        one mutex shared among all instances of the same class, in order to
3096        keep the size of each foreign pointer instance small.  We'll reconsider
3097        the design if the performance ever becomes a problem.  */
3098     (void)SCM_INTERNAL_MUTEX_LOCK(data->attr_mutex);
3099     ScmObj r = SCM_UNDEFINED;
3100     ScmObj p = Scm_Assq(key, fp->attributes);
3101     if (SCM_PAIRP(p)) {
3102         SCM_SET_CDR_UNCHECKED(p, value);
3103         r = value;
3104     } else {
3105         fp->attributes = Scm_Acons(key, value, fp->attributes);
3106     }
3107     (void)SCM_INTERNAL_MUTEX_UNLOCK(data->attr_mutex);
3108     return r;
3109 }
3110 
3111 /*=====================================================================
3112  * Class initialization
3113  */
3114 
3115 /* TODO: need a cleaner way! */
3116 /* static declaration of some structures */
3117 
3118 static ScmClassStaticSlotSpec class_slots[] = {
3119     SCM_CLASS_SLOT_SPEC("name", class_name, class_name_set),
3120     SCM_CLASS_SLOT_SPEC("cpl",  class_cpl, class_cpl_set),
3121     SCM_CLASS_SLOT_SPEC("direct-supers",  class_direct_supers, class_direct_supers_set),
3122     SCM_CLASS_SLOT_SPEC("accessors", class_accessors, class_accessors_set),
3123     SCM_CLASS_SLOT_SPEC("slots", class_slots_ref, class_slots_set),
3124     SCM_CLASS_SLOT_SPEC("direct-slots", class_direct_slots, class_direct_slots_set),
3125     SCM_CLASS_SLOT_SPEC("num-instance-slots", class_numislots, class_numislots_set),
3126     SCM_CLASS_SLOT_SPEC("direct-subclasses", class_direct_subclasses, NULL),
3127     SCM_CLASS_SLOT_SPEC("direct-methods", class_direct_methods, NULL),
3128     SCM_CLASS_SLOT_SPEC("initargs", class_initargs, class_initargs_set),
3129     SCM_CLASS_SLOT_SPEC("defined-modules", class_defined_modules, class_defined_modules_set),
3130     SCM_CLASS_SLOT_SPEC("redefined", class_redefined, NULL),
3131     SCM_CLASS_SLOT_SPEC("category", class_category, NULL),
3132     SCM_CLASS_SLOT_SPEC_END()
3133 };
3134 
3135 static ScmClassStaticSlotSpec generic_slots[] = {
3136     SCM_CLASS_SLOT_SPEC("name", generic_name, generic_name_set),
3137     SCM_CLASS_SLOT_SPEC("sealed", generic_sealed, NULL),
3138     SCM_CLASS_SLOT_SPEC("methods", generic_methods, generic_methods_set),
3139     SCM_CLASS_SLOT_SPEC_END()
3140 };
3141 
3142 static ScmClassStaticSlotSpec method_slots[] = {
3143     SCM_CLASS_SLOT_SPEC("required", method_required, NULL),
3144     SCM_CLASS_SLOT_SPEC("optional", method_optional, NULL),
3145     SCM_CLASS_SLOT_SPEC("method-locked", method_locked, NULL),
3146     SCM_CLASS_SLOT_SPEC("leaf?", method_leaf, NULL),
3147     SCM_CLASS_SLOT_SPEC("generic", method_generic, method_generic_set),
3148     SCM_CLASS_SLOT_SPEC("specializers", method_specializers, method_specializers_set),
3149     SCM_CLASS_SLOT_SPEC_END()
3150 };
3151 
3152 static ScmClassStaticSlotSpec accessor_method_slots[] = {
3153     SCM_CLASS_SLOT_SPEC("required", method_required, NULL),
3154     SCM_CLASS_SLOT_SPEC("optional", method_optional, NULL),
3155     SCM_CLASS_SLOT_SPEC("method-locked", method_locked, NULL),
3156     SCM_CLASS_SLOT_SPEC("leaf?", method_leaf, NULL),
3157     SCM_CLASS_SLOT_SPEC("generic", method_generic, method_generic_set),
3158     SCM_CLASS_SLOT_SPEC("specializers", method_specializers, method_specializers_set),
3159     SCM_CLASS_SLOT_SPEC("slot-accessor", accessor_method_slot_accessor, accessor_method_slot_accessor_set),
3160     SCM_CLASS_SLOT_SPEC_END()
3161 };
3162 
3163 static ScmClassStaticSlotSpec slot_accessor_slots[] = {
3164     SCM_CLASS_SLOT_SPEC("class", slot_accessor_class,
3165                         slot_accessor_class_set),
3166     SCM_CLASS_SLOT_SPEC("name", slot_accessor_name,
3167                         slot_accessor_name_set),
3168     SCM_CLASS_SLOT_SPEC("init-value", slot_accessor_init_value,
3169                         slot_accessor_init_value_set),
3170     SCM_CLASS_SLOT_SPEC("init-keyword", slot_accessor_init_keyword,
3171                         slot_accessor_init_keyword_set),
3172     SCM_CLASS_SLOT_SPEC("init-thunk", slot_accessor_init_thunk,
3173                         slot_accessor_init_thunk_set),
3174     SCM_CLASS_SLOT_SPEC("initializable", slot_accessor_initializable,
3175                         slot_accessor_initializable_set),
3176     SCM_CLASS_SLOT_SPEC("settable", slot_accessor_settable,
3177                         NULL),
3178     SCM_CLASS_SLOT_SPEC("slot-number", slot_accessor_slot_number,
3179                         slot_accessor_slot_number_set),
3180     SCM_CLASS_SLOT_SPEC("getter", slot_accessor_scheme_getter,
3181                         slot_accessor_scheme_getter_set),
3182     SCM_CLASS_SLOT_SPEC("setter", slot_accessor_scheme_setter,
3183                         slot_accessor_scheme_setter_set),
3184     SCM_CLASS_SLOT_SPEC("bound?", slot_accessor_scheme_boundp,
3185                         slot_accessor_scheme_boundp_set),
3186     SCM_CLASS_SLOT_SPEC_END()
3187 };
3188 
3189 /*
3190  * Sets up CPL from CPA
3191  */
initialize_builtin_cpl(ScmClass * klass,ScmObj supers)3192 static void initialize_builtin_cpl(ScmClass *klass, ScmObj supers)
3193 {
3194     ScmObj h = SCM_NIL, t = SCM_NIL;
3195 
3196     SCM_APPEND1(h, t, SCM_OBJ(klass));
3197     for (ScmClass **p = klass->cpa; *p; p++) SCM_APPEND1(h, t, SCM_OBJ(*p));
3198     klass->cpl = h;
3199     if (SCM_PAIRP(supers)) {
3200         /* Check validity of the given supers. */
3201         ScmObj cp, sp = supers;
3202         SCM_FOR_EACH(cp, klass->cpl) {
3203             if (SCM_EQ(SCM_CAR(cp), SCM_CAR(sp))) {
3204                 sp = SCM_CDR(sp);
3205                 if (SCM_NULLP(sp)) break;
3206             }
3207         }
3208         if (!SCM_NULLP(sp)) {
3209             /* NB: At this point we may not have initialized error handing
3210                mechanism, so we have no option but quit. */
3211             const char *cname = "(unnamed class)";
3212             if (SCM_STRINGP(klass->name)) {
3213                 cname = Scm_GetStringConst(SCM_STRING(klass->name));
3214             }
3215             Scm_Panic("Class %s is being initialized with inconsistent super class list.  Must be an implementation error.  Report to the author.", cname);
3216         }
3217         klass->directSupers = supers;
3218     } else if (SCM_PAIRP(SCM_CDR(h))) {
3219         /* Default: take the next class of CPL as the only direct super */
3220         klass->directSupers = SCM_LIST1(SCM_CADR(h));
3221     } else {
3222         /* Should this happen? */
3223         klass->directSupers = SCM_NIL;
3224     }
3225 }
3226 
3227 /*
3228  * A common part for builtin class initialization
3229  */
init_class(ScmClass * klass,const char * name,ScmModule * mod,ScmObj supers,ScmClassStaticSlotSpec * specs,int flags SCM_UNUSED)3230 static void init_class(ScmClass *klass,
3231                        const char *name,
3232                        ScmModule *mod,
3233                        ScmObj supers,  /* SCM_FALSE if using default */
3234                        ScmClassStaticSlotSpec *specs,
3235                        int flags SCM_UNUSED)  /* reserved */
3236 {
3237     ScmObj slots = SCM_NIL, t = SCM_NIL, acc = SCM_NIL;
3238 
3239     /* initialize CPL and directSupers */
3240     if (klass->cpa == NULL) {
3241         klass->cpa = SCM_CLASS_DEFAULT_CPL;
3242     }
3243 
3244     klass->name = SCM_INTERN(name);
3245     initialize_builtin_cpl(klass, supers);
3246 
3247     /* On Windows, mutex and cv must be initialized at runtime. */
3248     SCM_INTERNAL_MUTEX_INIT(klass->mutex);
3249     SCM_INTERNAL_COND_INIT(klass->cv);
3250 
3251     /* insert binding */
3252     Scm_Define(mod, SCM_SYMBOL(klass->name), SCM_OBJ(klass));
3253 
3254     /* initialize direct slots */
3255     if (specs) {
3256         for (;specs->name; specs++) {
3257             ScmObj snam = SCM_INTERN(specs->name);
3258             specs->accessor.klass = klass;
3259             specs->accessor.name = snam;
3260             acc = Scm_Acons(snam, SCM_OBJ(&specs->accessor), acc);
3261             specs->accessor.initKeyword = SCM_MAKE_KEYWORD(specs->name);
3262             SCM_APPEND1(slots, t,
3263                         Scm_List(snam,
3264                                  key_allocation, key_builtin,
3265                                  key_slot_accessor, SCM_OBJ(&specs->accessor),
3266                                  NULL));
3267         }
3268     }
3269     klass->directSlots = slots;
3270 
3271     /* compute other slots inherited from supers */
3272     for (ScmClass **super = klass->cpa; *super; super++) {
3273         ScmObj sp;
3274         SCM_FOR_EACH(sp, (*super)->directSlots) {
3275             ScmObj slot = SCM_CAR(sp), snam, p, a;
3276             SCM_ASSERT(SCM_PAIRP(slot));
3277             snam = SCM_CAR(slot);
3278             p = Scm_Assq(snam, slots);
3279             if (SCM_FALSEP(p)) {
3280                 slots = Scm_Cons(Scm_CopyList(slot), slots);
3281                 a = Scm_GetKeyword(key_slot_accessor, SCM_CDR(slot), SCM_FALSE);
3282                 SCM_ASSERT(SCM_HOBJP(a));
3283                 SCM_ASSERT(SCM_SLOT_ACCESSOR_P(a));
3284                 acc = Scm_Acons(snam, a, acc);
3285             }
3286         }
3287     }
3288     klass->slots = slots;
3289     klass->accessors = acc;
3290 }
3291 
3292 /*
3293  * Inter-module API
3294  */
3295 
3296 /* The most standard way to initialize a class. */
Scm_InitStaticClass(ScmClass * klass,const char * name,ScmModule * mod,ScmClassStaticSlotSpec * specs,int flags)3297 void Scm_InitStaticClass(ScmClass *klass,
3298                          const char *name,
3299                          ScmModule *mod,
3300                          ScmClassStaticSlotSpec *specs,
3301                          int flags) /* reserved */
3302 {
3303     init_class(klass, name, mod, SCM_FALSE, specs, flags);
3304 }
3305 
3306 /* If the builtin class needs multiple inheritance... */
Scm_InitStaticClassWithSupers(ScmClass * klass,const char * name,ScmModule * mod,ScmObj supers,ScmClassStaticSlotSpec * specs,int flags)3307 void Scm_InitStaticClassWithSupers(ScmClass *klass,
3308                                    const char *name,
3309                                    ScmModule *mod,
3310                                    ScmObj supers,
3311                                    ScmClassStaticSlotSpec *specs,
3312                                    int flags) /* reserved */
3313 {
3314     init_class(klass, name, mod, supers, specs, flags);
3315 }
3316 
3317 /* A special initialization for some of builtin classes.
3318    Sets klass's metaclass to META.  If META is NULL, a new metaclass
3319    (whose name has "-meta" after the original class name except brackets)
3320    is created automatically.  This procedure should be used only if
3321    metaclass is absolutely required (e.g. all condition classes should
3322    be an instance of <condition-meta>).   The older version of Gauche
3323    has metaclasses for many builtin classes, which is a compensation of
3324    lack of eqv-method specializer; such use of metaclass is deprecated
3325    and will be removed in future. */
Scm_InitStaticClassWithMeta(ScmClass * klass,const char * name,ScmModule * mod,ScmClass * meta,ScmObj supers,ScmClassStaticSlotSpec * specs,int flags)3326 void Scm_InitStaticClassWithMeta(ScmClass *klass,
3327                                  const char *name,
3328                                  ScmModule *mod,
3329                                  ScmClass *meta,
3330                                  ScmObj supers,
3331                                  ScmClassStaticSlotSpec *specs,
3332                                  int flags)
3333 {
3334     init_class(klass, name, mod, supers, specs, flags);
3335 
3336     if (meta) {
3337         SCM_SET_CLASS(klass, meta);
3338     } else {
3339         int nlen = (int)strlen(name);
3340         char *metaname = SCM_NEW_ATOMIC2(char *, nlen + 6);
3341 
3342         if (name[nlen - 1] == '>') {
3343             strncpy(metaname, name, nlen-1);
3344             strcpy(metaname+nlen-1, "-meta>");
3345         } else {
3346             strcpy(metaname, name);
3347             strcat(metaname, "-meta");
3348         }
3349         SCM_SET_CLASS(klass, make_implicit_meta(metaname, klass->cpa, mod));
3350     }
3351 }
3352 
3353 /* The old API - deprecated.  We keep this around for a while
3354    for backward compatibility. */
Scm_InitBuiltinClass(ScmClass * klass,const char * name,ScmClassStaticSlotSpec * specs,int withMeta,ScmModule * mod)3355 void Scm_InitBuiltinClass(ScmClass *klass, const char *name,
3356                           ScmClassStaticSlotSpec *specs,
3357                           int withMeta, ScmModule *mod)
3358 {
3359     if (withMeta) {
3360         Scm_InitStaticClassWithMeta(klass, name, mod, NULL, SCM_FALSE, specs, 0);
3361     } else {
3362         Scm_InitStaticClass(klass, name, mod, specs, 0);
3363     }
3364 }
3365 
Scm_InitBuiltinGeneric(ScmGeneric * gf,const char * name,ScmModule * mod)3366 void Scm_InitBuiltinGeneric(ScmGeneric *gf, const char *name, ScmModule *mod)
3367 {
3368     ScmObj s = SCM_INTERN(name);
3369     gf->common.info = s;
3370     if (gf->fallback == NULL) {
3371         gf->fallback = Scm_NoNextMethod;
3372     }
3373     (void)SCM_INTERNAL_MUTEX_INIT(gf->lock);
3374     Scm_Define(mod, SCM_SYMBOL(s), SCM_OBJ(gf));
3375 }
3376 
Scm_InitBuiltinMethod(ScmMethod * m)3377 void Scm_InitBuiltinMethod(ScmMethod *m)
3378 {
3379     m->common.info = Scm_Cons(m->generic->common.info,
3380                               class_array_to_names(m->specializers,
3381                                                    m->common.required));
3382     Scm_AddMethod(m->generic, m);
3383 }
3384 
Scm__InitClass(void)3385 void Scm__InitClass(void)
3386 {
3387     ScmModule *mod = Scm_GaucheModule();
3388     static ScmClass *nullcpa[1] = {NULL}; /* for <top> */
3389 
3390     key_allocation = SCM_MAKE_KEYWORD("allocation");
3391     key_builtin = SCM_MAKE_KEYWORD("builtin");
3392     key_slot_accessor = SCM_MAKE_KEYWORD("slot-accessor");
3393     key_name = SCM_MAKE_KEYWORD("name");
3394     key_lambda_list = SCM_MAKE_KEYWORD("lambda-list");
3395     key_generic = SCM_MAKE_KEYWORD("generic");
3396     key_method_locked = SCM_MAKE_KEYWORD("method-locked");
3397     key_specializers = SCM_MAKE_KEYWORD("specializers");
3398     key_body = SCM_MAKE_KEYWORD("body");
3399 
3400     (void)SCM_INTERNAL_MUTEX_INIT(class_redefinition_lock.mutex);
3401     (void)SCM_INTERNAL_COND_INIT(class_redefinition_lock.cv);
3402 
3403     if (Scm_GetEnv("GAUCHE_DISABLE_GENERIC_DISPATCHER") != NULL) {
3404         disable_generic_dispatcher = TRUE;
3405     }
3406 
3407     /* booting class metaobject */
3408     Scm_TopClass.cpa = nullcpa;
3409 
3410 #define BINIT(cl, nam, slots) \
3411     Scm_InitStaticClass(cl, nam, mod, slots, 0)
3412 
3413 #define CINIT(cl, nam) \
3414     Scm_InitStaticClassWithMeta(cl, nam, mod, NULL, SCM_FALSE, NULL, 0)
3415 
3416     /* Need to initialize these first */
3417     BINIT(SCM_CLASS_CLASS,  "<class>", class_slots);
3418     BINIT(SCM_CLASS_TOP,    "<top>",     NULL);
3419     BINIT(SCM_CLASS_BOTTOM, "<bottom>",  NULL);
3420 
3421     /* box.c */
3422     CINIT(SCM_CLASS_BOX,    "<box>");
3423     CINIT(SCM_CLASS_MVBOX,  "<mv-box>");
3424 
3425     /* class.c */
3426     CINIT(SCM_CLASS_BOOL,   "<boolean>");
3427     CINIT(SCM_CLASS_CHAR,   "<char>");
3428     BINIT(SCM_CLASS_EOF_OBJECT,"<eof-object>", NULL);
3429     BINIT(SCM_CLASS_UNDEFINED_OBJECT,"<undefined-object>", NULL);
3430     BINIT(SCM_CLASS_UNKNOWN,"<unknown>", NULL);
3431     BINIT(SCM_CLASS_OBJECT, "<object>",  NULL);
3432     BINIT(SCM_CLASS_GENERIC,"<generic>", generic_slots);
3433     Scm_GenericClass.flags |= SCM_CLASS_APPLICABLE;
3434     BINIT(SCM_CLASS_METHOD, "<method>",  method_slots);
3435     Scm_MethodClass.flags |= SCM_CLASS_APPLICABLE;
3436     BINIT(SCM_CLASS_NEXT_METHOD, "<next-method>", NULL);
3437     Scm_NextMethodClass.flags |= SCM_CLASS_APPLICABLE;
3438     BINIT(SCM_CLASS_ACCESSOR_METHOD, "<accessor-method>", accessor_method_slots);
3439     Scm_AccessorMethodClass.flags |= SCM_CLASS_APPLICABLE;
3440     BINIT(SCM_CLASS_SLOT_ACCESSOR,"<slot-accessor>", slot_accessor_slots);
3441     BINIT(SCM_CLASS_FOREIGN_POINTER, "<foreign-pointer>", NULL);
3442 
3443     /* char.c */
3444     CINIT(SCM_CLASS_CHAR_SET,         "<char-set>");
3445 
3446     /* connection.c */
3447     BINIT(SCM_CLASS_CONNECTION, "<connection>", NULL);
3448 
3449     /* comparator.c */
3450     /* initialized in Scm__InitComparator */
3451 
3452     /* compile.c */
3453     /* initialized in Scm__InitCompiler */
3454 
3455     /* error.c */
3456     /* initialized in Scm__InitExceptions */
3457 
3458     /* hash.c */
3459     CINIT(SCM_CLASS_HASH_TABLE,       "<hash-table>");
3460 
3461     /* list.c */
3462     CINIT(SCM_CLASS_LIST,             "<list>");
3463     CINIT(SCM_CLASS_PAIR,             "<pair>");
3464     CINIT(SCM_CLASS_NULL,             "<null>");
3465 
3466     /* load.c */
3467     CINIT(SCM_CLASS_AUTOLOAD,         "<autoload>");
3468 
3469     /* macro.c */
3470     CINIT(SCM_CLASS_SYNTAX,           "<syntax>");
3471     CINIT(SCM_CLASS_MACRO,            "<macro>");
3472     CINIT(SCM_CLASS_SYNTAX_RULES,     "<syntax-rules>");
3473 
3474     /* module.c */
3475     /* class initialized in libmod.scm */
3476 
3477     /* number.c */
3478     CINIT(SCM_CLASS_NUMBER,           "<number>");
3479     CINIT(SCM_CLASS_COMPLEX,          "<complex>");
3480     CINIT(SCM_CLASS_REAL,             "<real>");
3481     CINIT(SCM_CLASS_RATIONAL,         "<rational>");
3482     CINIT(SCM_CLASS_INTEGER,          "<integer>");
3483 
3484     /* parameter.c */
3485     BINIT(SCM_CLASS_PRIMITIVE_PARAMETER, "<primitive-parameter>", NULL);
3486 
3487     /* port.c */
3488     /* initialized in Scm__InitPort */
3489 
3490     /* proc.c */
3491     /* initialized in Scm__InitProc */
3492 
3493     /* promise.c */
3494     CINIT(SCM_CLASS_PROMISE,          "<promise>");
3495     CINIT(SCM_CLASS_LAZY_PAIR,        "<lazy-pair>");
3496 
3497     /* read.c */
3498     BINIT(SCM_CLASS_READ_CONTEXT,     "<read-context>", NULL);
3499     BINIT(SCM_CLASS_READ_REFERENCE,   "<read-reference>", NULL);
3500 
3501     /* regexp.c */
3502     CINIT(SCM_CLASS_REGEXP,           "<regexp>");
3503     CINIT(SCM_CLASS_REGMATCH,         "<regmatch>");
3504 
3505     /* string.c */
3506     CINIT(SCM_CLASS_STRING,           "<string>");
3507     BINIT(SCM_CLASS_STRING_CURSOR,    "<string-cursor>", NULL);
3508     BINIT(SCM_CLASS_STRING_CURSOR_LARGE, "<string-cursor-large>", NULL);
3509 #if GAUCHE_STRING_POINTER
3510     CINIT(SCM_CLASS_STRING_POINTER,   "<string-pointer>");
3511 #endif
3512 
3513     /* symbol.c */
3514     CINIT(SCM_CLASS_SYMBOL,           "<symbol>");
3515     CINIT(SCM_CLASS_GLOC,             "<gloc>");
3516     CINIT(SCM_CLASS_KEYWORD,          "<keyword>");
3517 
3518     /* system.c */
3519     /* initialized in Scm__InitSystem */
3520 
3521     /* treemap.c */
3522     CINIT(SCM_CLASS_TREE_MAP,         "<tree-map>");
3523 
3524     /* vector.c */
3525     CINIT(SCM_CLASS_VECTOR,           "<vector>");
3526     CINIT(SCM_CLASS_UVECTOR,          "<uvector>");
3527     CINIT(SCM_CLASS_S8VECTOR,         "<s8vector>");
3528     CINIT(SCM_CLASS_U8VECTOR,         "<u8vector>");
3529     CINIT(SCM_CLASS_S16VECTOR,        "<s16vector>");
3530     CINIT(SCM_CLASS_U16VECTOR,        "<u16vector>");
3531     CINIT(SCM_CLASS_S32VECTOR,        "<s32vector>");
3532     CINIT(SCM_CLASS_U32VECTOR,        "<u32vector>");
3533     CINIT(SCM_CLASS_S64VECTOR,        "<s64vector>");
3534     CINIT(SCM_CLASS_U64VECTOR,        "<u64vector>");
3535     CINIT(SCM_CLASS_F16VECTOR,        "<f16vector>");
3536     CINIT(SCM_CLASS_F32VECTOR,        "<f32vector>");
3537     CINIT(SCM_CLASS_F64VECTOR,        "<f64vector>");
3538     CINIT(SCM_CLASS_C32VECTOR,        "<c32vector>");
3539     CINIT(SCM_CLASS_C64VECTOR,        "<c64vector>");
3540     CINIT(SCM_CLASS_C128VECTOR,       "<c128vector>");
3541     CINIT(SCM_CLASS_BITVECTOR,        "<bitvector>");
3542 
3543     /* weak.c */
3544     CINIT(SCM_CLASS_WEAK_VECTOR,      "<weak-vector>");
3545     CINIT(SCM_CLASS_WEAK_HASH_TABLE,  "<weak-hash-table>");
3546 
3547     /* write.c */
3548     BINIT(SCM_CLASS_WRITE_STATE,      "<write-state>", NULL);
3549 
3550 #define GINIT(gf, nam) \
3551     Scm_InitBuiltinGeneric(gf, nam, mod);
3552 
3553     GINIT(&Scm_GenericMake, "make");
3554     GINIT(&Scm_GenericAllocate, "allocate-instance");
3555     GINIT(&Scm_GenericInitialize, "initialize");
3556     GINIT(&Scm_GenericAddMethod, "add-method!");
3557     GINIT(&Scm_GenericDeleteMethod, "delete-method!");
3558     GINIT(&Scm_GenericComputeCPL, "compute-cpl");
3559     GINIT(&Scm_GenericComputeSlots, "compute-slots");
3560     GINIT(&Scm_GenericComputeGetNSet, "compute-get-n-set");
3561     GINIT(&Scm_GenericComputeApplicableMethods, "compute-applicable-methods");
3562     GINIT(&Scm_GenericUpdateDirectMethod, "update-direct-method!");
3563     GINIT(&Scm_GenericMethodMoreSpecificP, "method-more-specific?");
3564     GINIT(&Scm_GenericApplyGeneric, "apply-generic");
3565     GINIT(&Scm_GenericSlotMissing, "slot-missing");
3566     GINIT(&Scm_GenericSlotUnbound, "slot-unbound");
3567     GINIT(&Scm_GenericSlotRefUsingClass, "slot-ref-using-class");
3568     GINIT(&Scm_GenericSlotSetUsingClass, "slot-set-using-class!");
3569     GINIT(&Scm_GenericSlotBoundUsingClassP, "slot-bound-using-class?");
3570     GINIT(&Scm_GenericObjectEqualP, "object-equal?");
3571     GINIT(&Scm_GenericObjectCompare, "object-compare");
3572     GINIT(&Scm_GenericObjectHash, "object-hash");
3573     GINIT(&Scm_GenericObjectApply, "object-apply");
3574     GINIT(&Scm_GenericObjectSetter, "setter of object-apply");
3575     GINIT(&Scm_GenericChangeClass, "change-class");
3576 
3577     Scm_SetterSet(SCM_PROCEDURE(&Scm_GenericObjectApply),
3578                   SCM_PROCEDURE(&Scm_GenericObjectSetter),
3579                   TRUE);
3580 
3581     Scm_InitBuiltinMethod(&class_allocate_rec);
3582     Scm_InitBuiltinMethod(&class_compute_cpl_rec);
3583     Scm_InitBuiltinMethod(&slot_ref_using_class_rec);
3584     Scm_InitBuiltinMethod(&slot_set_using_class_rec);
3585     Scm_InitBuiltinMethod(&slot_bound_using_class_p_rec);
3586     Scm_InitBuiltinMethod(&object_initialize_rec);
3587     Scm_InitBuiltinMethod(&generic_addmethod_rec);
3588     Scm_InitBuiltinMethod(&generic_deletemethod_rec);
3589     Scm_InitBuiltinMethod(&method_initialize_rec);
3590     Scm_InitBuiltinMethod(&accessor_method_initialize_rec);
3591     Scm_InitBuiltinMethod(&compute_applicable_methods_rec);
3592     Scm_InitBuiltinMethod(&generic_updatedirectmethod_rec);
3593     Scm_InitBuiltinMethod(&method_more_specific_p_rec);
3594 }
3595