From a142609c5dc2a7c3df02497235881beaf47088bf Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Tue, 15 Dec 2015 19:15:23 +0000 Subject: [PATCH] Replace the `init' class-slot function with an `init' message. Organization: Straylight/Edgeware From: Mark Wooding The `SodObject' class now defines an `init' message. This message takes keywords, which can be collected by methods and used to set up objects in interesting ways. The `init' message's default behaviour (unless overridden by a primary or around method) is to initialize the instance's slots using the most specific applicable initializers, just as the old `init' function used to do. Obviously, one can send an `init' message to an instance before it's been imprinted, so that has to be done as a separate step now. The `SOD_DECL' macro has been adjusted to cope. It also takes an additional argument containing keyword arguments to be passed with the `init' message (which constitutes an additional compatibility break). The library has grown a new pair of functions `sod_init' and `sod_initv' which wrap up the initialization protocol, and a macro `SOD_INIT' which improves type safety a little. (Some of the new code in `src/builtin.lisp' is a little odd. This is intended to accommodate future changes better.) --- doc/SYMBOLS | 3 + doc/concepts.tex | 46 ++++++++++----- doc/runtime.tex | 38 +++++++++++- doc/structures.tex | 62 ++++++++++++++------ lib/sod-structs.3 | 63 +++++++++++++++----- lib/sod.3 | 68 ++++++++++++++++++++++ lib/sod.c | 39 +++++++++++++ lib/sod.h | 49 +++++++++++++--- src/builtin.lisp | 142 +++++++++++++++++++++++++++++++++------------ src/sod.asd.in | 2 +- test/chimaera.sod | 8 +-- test/test.sod | 4 +- 12 files changed, 423 insertions(+), 101 deletions(-) diff --git a/doc/SYMBOLS b/doc/SYMBOLS index 06e2002..2acfd60 100644 --- a/doc/SYMBOLS +++ b/doc/SYMBOLS @@ -879,6 +879,7 @@ effective-method-function-name effective-method-keywords effective-method effective-method-live-p + sod::lifecycle-effective-method simple-effective-method effective-method-message effective-method @@ -1310,6 +1311,7 @@ cl:shared-initialize sod-token-scanner t simple-method-body aggregating-effective-method t t + sod::lifecycle-effective-method t t standard-effective-method t t cl:slot-unbound t basic-direct-method (eql sod::function-type) @@ -1381,6 +1383,7 @@ sod-message-combination aggregating-message sod-message-effective-method-class aggregating-message + sod::initialization-message standard-message sod-message-kernel-function aggregating-message diff --git a/doc/concepts.tex b/doc/concepts.tex index 1663939..39cccb8 100644 --- a/doc/concepts.tex +++ b/doc/concepts.tex @@ -388,7 +388,8 @@ Keyword arguments can be provided in three ways. Keyword arguments are provided as a general feature for C functions. However, Sod has special support for messages which accept keyword arguments -(\xref{sec:concepts.methods.keywords}). +(\xref{sec:concepts.methods.keywords}); and they play an essential role in +the instance construction protocol (\xref{sec:concepts.lifecycle.birth}). %%%-------------------------------------------------------------------------- \section{Messages and methods} \label{sec:concepts.methods} @@ -654,8 +655,10 @@ Construction of a new instance of a class involves three steps. necessary. \end{enumerate} The \descref{SOD_DECL}[macro]{mac} handles constructing instances with -automatic storage duration (`on the stack'). Currently, there is no built-in -support for constructing dynamically-allocated instances. +automatic storage duration (`on the stack'). Programmers can add support for +other allocation strategies by using the \descref{SOD_INIT}[macro]{mac} and +the \descref{sod_init}{fun} and \descref{sod_initv}{fun} functions, which +package up imprinting and initialization. \subsubsection{Allocation} Instances of most classes (specifically including those classes defined by @@ -724,31 +727,42 @@ Details of initialization are necessarily class-specific, but typically it involves setting the instance's slots to appropriate values, and possibly linking it into some larger data structure to keep track of it. -Classes can declare initial values for their slots. A class object's @|init| -slot points to a function which will establish the appropriate initial values -for a new instance's slots. Slots are not initialized in any particularly -useful order. - -The provided initialization protocol is extremely simplistic; most notably, -it's not possible to pass parameters into the initialization process. -Classes which have more complex requirements will need to define and -implement their own additional (or alternative) protocols. +Initialization is performed by sending the imprinted instance an @|init| +message, defined by the @|SodObject| class. This message uses a nonstandard +method combination which works like the standard combination, except that the +\emph{default behaviour}, if there is no overriding method, is to initialize +the instance's slots using the initializers defined in the class and its +superclasses. This default behaviour may be invoked multiple times if some +method calls on its @|next_method| more than once, unless some other method +takes steps to prevent this. + +The recommended way to add new initialization behaviour is to define @|after| +methods on the @|init| message. These will be run after the slot +initializers have been applied, in reverse precedence order. + +Initialization is \emph{parametrized}, so the caller may select from a space +of possible initial states for the new instance, or to inform the new +instance about some other objects known to the caller. Specifically, the +@|init| message accepts keyword arguments (\xref{sec:concepts.keywords}) +which can be defined and used by methods defined on it. \subsubsection{Example} The following is a simple function, with syntactic-sugar macro, which allocate storage for an instance of a class, imprints and initializes it, and returns a pointer to the new instance. \begin{prog} - void *make_instance(const SodClass *c) \\ + void *make_instance(const SodClass *c, @|\dots|) \\ \{ \\ \ind + va_list ap; void *p = malloc(c@->cls.initsz); \\ if (!p) return (0); \\ - c@->cls.imprint(p); \\ - c@->cls.init(p); \\ + va_start(ap, c); \\ + sod_initv(c, p, ap); \\ + va_end(ap); \\ return (p); \- \\ \} \\+ - \#define MAKE(cls) (cls *)make_instance(cls\#\#__class) + \#define MAKE(cls, keys) (cls *)make_instance(cls\#\#__class, keys) \end{prog} diff --git a/doc/runtime.tex b/doc/runtime.tex index 0a44f91..6a1941d 100644 --- a/doc/runtime.tex +++ b/doc/runtime.tex @@ -732,10 +732,42 @@ instance. The following macros and functions manage the standard steps along an instance's lifecycle. +\subsubsection{Low-level operations} +The following macros and functions are agnostic with respect to storage +allocation strategies. They don't concern themselves with allocation or +deallocation, and applications are free to use any suitable mechanism. + +\begin{describe*} + {\dhead[SOD_INIT]{mac} + {@ *SOD_INIT(@, void *@

, @);} + \dhead[sod_init]{fun} + {void *sod_init(const SodClass *@, void *@

, \dots);} + \dhead[sod_initv]{fun} + {void *sod_initv(const SodClass *@, void *@

, va_list @);}} + Imprints and initializes an instance of a class @ in the storage + starting at address~@

. + + The direct class for the new instance is specified as a class name to + @|SOD_INIT|, or a pointer to a class object to the functions. + + Keyword arguments for the initialization message may be provided. The + @|SOD_INIT| macro expects a single preprocessor-time argument which is + a use of one of \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}; the + @|sod_init| function expects the keywords as a variable-length argument + tail; and @|sod_initv| expects the keywords to be passed indirectly, + through the captured argument-tail cursor @. + + The return value is an instance pointer for the class @; the + @|SOD_INIT| macro will have converted it to the correct type, so it should + probably be used where possible. In fact, this is guaranteed to be equal + to @

by the layout rules described in + \xref{sec:structures.layout.instance}. +\end{describe*} + \subsubsection{Automatic storage duration} The following macro constructs an instance with automatic storage duration. -\begin{describe}[SOD_DECL]{mac}{SOD_DECL(@, @);} +\begin{describe}[SOD_DECL]{mac}{SOD_DECL(@, @, @);} Declares and initializes an instance with automatic storage duration. Given a class name @ and an identifier @, @|SOD_DECL| declares @@ -744,6 +776,10 @@ The following macro constructs an instance with automatic storage duration. up, and slots for which initializers are defined are set to the appropriate initial values. + Keyword arguments for the initialization message may be provided. The + macro expects a single preprocessor-time argument which is a use of one of + \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}. + The instance has automatic storage duration: pointers to it will become invalid when control exits the scope of the declaration. \end{describe} diff --git a/doc/structures.tex b/doc/structures.tex index c8d9daf..51e71f9 100644 --- a/doc/structures.tex +++ b/doc/structures.tex @@ -105,7 +105,11 @@ recommended. \begin{nprog} struct SodObject__vt_obj \{ \\ \ind const SodClass *_class; \\ - size_t _base; \- \\ + size_t _base; \\ + struct SodObject__vtmsgs_obj \{ \\ \ind + void (*init)(SodObject *me, ...); \\ + void (*init__v)(SodObject *me, va_list); \- \\ + \} obj; \- \\ \}; \end{nprog} \\ \end{tabular} @@ -115,19 +119,48 @@ recommended. \begin{describe}[SodObject]{cls} {[nick = obj, metaclass = SodClass, lisp_metaclass = sod_class] \\ - class SodObject \{ \}} + class SodObject \{ \\ \ind + void init(?); + \}} - The @|SodObject| class defines no slots or messages. Because @|SodObject| - has no direct superclasses, there is only one chain, and no inherited - slots or messages, so the single chain contains only a vtable pointer. + The @|SodObject| class defines no slots. Because @|SodObject| has no + direct superclasses, there is only one chain, and no inherited slots or + messages, so the single chain contains only a vtable pointer. - Since there are no messages, and @|SodClass| also has only one chain, the - vtable contains only the standard class pointer and offset-to-base members. - In a direct instance of @|SodObject| (why would you want one?) the class - pointer contains the address of @|SodObject__class| and the offset is zero. + Since @|SodClass| also has only one chain, the vtable contains only the + standard class pointer and offset-to-base members. In a direct instance of + @|SodObject| (why would you want one?) the class pointer contains the + address of @|SodObject__class| and the offset is zero. The instance and vtable layout of @|SodObject| is shown in \xref{fig:structures.root.sodobject}. + + The following message is defined. + + \begin{describe}[obj.init]{msg}{void init(?);} + Initialize a newly allocated instance. + + This message uses a custom method combination which works like the + standard method combination except that default behaviour specific to the + receiver's direct class is invoked if no primary or around method + overrides. This default behaviour may be invoked multiple times if some + method calls on its @|next_method| function more than once. + + This default behaviour is to initialize the instance's slots using the + defined slot initializers: each slot is initialized using the most + specific applicable initializer, if any. Slots without an initializer + are left uninitialized. + + There are no standard keyword arguments; methods on subclasses are free + to introduce their own in the usual way. + + It is usual to provide complex initialization behaviour as @|after| + methods. This ensures that slots have been initialized as necessary + before the method executes. + + For more details on instance construction, see + \xref{sec:concepts.lifecycle.birth}. + \end{describe} \end{describe} @@ -140,7 +173,6 @@ recommended. const char *nick; \\ size_t initsz; \\ void *(*imprint)(void *@

); \\ - void *(*init)(void *@

); \\ size_t n_supers; \\ const SodClass *const *supers; \\ size_t n_cpl; \\ @@ -154,9 +186,9 @@ recommended. size_t islotsz; \- \\ \}} - The @|SodClass| class defines no messages, but there are a number of slots. - Its only direct superclass is @|SodObject| and so (like its superclass) its - vtable is trivial. + The @|SodClass| class defines no additional messages , but there are a + number of slots. Its only direct superclass is @|SodObject| and so (like + its superclass) its vtable is simple. The slots defined are as follows. \begin{description} \let\makelabel\code @@ -173,10 +205,6 @@ recommended. the vtable and class pointers are properly initialized, but the slots are left untouched. The function returns its argument @

. - \item[init] A pointer to a function: given a pointer @

to an imprinted - instance, initialize all of its slots for which initializers are defined. - Other slots are left untouched. The function returns its argument @

. - \item[n_supers] The number of direct superclasses. (This is zero exactly in the case of @|SodObject|.) diff --git a/lib/sod-structs.3 b/lib/sod-structs.3 index 2d4ecd1..6cfbb75 100644 --- a/lib/sod-structs.3 +++ b/lib/sod-structs.3 @@ -69,6 +69,10 @@ struct sod_vtable { struct SodObject__vt_obj { \h'2n'const SodClass *_class; \h'2n'size_t _base; +\h'2n'struct SodObject__vtmsgs_obj { +\h'4n'void (*init)(SodObject *\fIme\fB, ...); +\h'4n'void (*init__v)(SodObject *\fIme\fB, va_list); +\h'2n'} obj; }; struct SodObject__ilayout { @@ -85,6 +89,10 @@ extern const struct SodClass__ilayout SodObject__classobj; struct SodClass__vt_obj { \h'2n'const SodClass *_class; \h'2n'size_t _base; +\h'2n'struct SodClass__vtmsgs_obj { +\h'4n'void (*init)(SodClass *\fIme\fB, ...); +\h'4n'void (*init__v)(SodClass *\fIme\fB, va_list); +\h'2n'} obj; }; struct SodObject__ilayout { @@ -96,7 +104,6 @@ struct SodObject__ilayout { \h'8n'const char *nick; \h'8n'size_t initsz; \h'8n'void *(*imprint)(void *\fIp\fB); -\h'8n'void *(*init)(void *\fIp\fB); \h'8n'size_t n_supers; \h'8n'const SodClass *const *supers; \h'8n'size_t n_cpl; @@ -212,7 +219,7 @@ and not really to be recommended. .SS The SodObject class The .B SodObject -class defines no slots or messages. +class defines no slots. Because .B SodObject has no direct superclasses, @@ -220,8 +227,7 @@ there is only one chain, and no inherited slots or messages, so the single chain contains only a vtable pointer. .PP -Since there are no messages, -and +Since .B SodClass also has only one chain, the vtable contains only the standard class pointer and offset-to-base @@ -232,6 +238,45 @@ In an actual instance of the class pointer contains the address of .B SodObject__class and the offset is zero. +.PP +The +.B init +message is used to initialize a newly allocated instance. +.PP +This message uses a custom method combination +which works like the standard method combination +except that default behaviour +specific to the receiver's direct class +is invoked if no primary or around method overrides. +This default behaviour may be invoked multiple times +if some method calls on its +.B next_method +function more than once. +.PP +This default behaviour is to initialize the instance's slots +using the defined slot initializers: +each slot is initialized +using the most specific applicable initializer, +if any. +Slots without an initializer +are left uninitialized. +.PP +Slots are initialized in reverse-precedence order +of their defining classes; +i.e., slots defined by a less specific superclass are initialized +earlier than slots defined by a more specific superclass. +Slots defined by the same class are initialized in the order in which +they appear in the class definition. +.PP +There are no standard keyword arguments; +methods on subclasses are free to +introduce their own in the usual way. +.PP +It is usual to provide complex initialization behaviour as +.B after +methods. +This ensures that slots have been initialized as necessary +before the method executes. . .SS The SodClass class The @@ -267,16 +312,6 @@ but the slots are left untouched. The function returns its argument .IR p . .TP -.BI "void *(*init)(void *" p ); -A pointer to a function: -given a pointer -.I p -to an imprinted instance, -initialize all of its slots for which initializers are defined. -Other slots are left untouched. -The function returns its argument -.IR p . -.TP .B size_t n_supers; The number of direct superclasses. (This is zero exactly in the case of diff --git a/lib/sod.3 b/lib/sod.3 index 5908eb7..b713d57 100644 --- a/lib/sod.3 +++ b/lib/sod.3 @@ -92,6 +92,24 @@ sod \- Sensible Object Design runtime library .BI "const SodClass *" cls , .BI "const void *" obj ); .PP +.IB cls " *" \c +.B SOD_INIT(\c +.IB cls , +.BI "void *" p , +.IB keywords ); +.br +.B void *\c +.B sod_init(\c +.BI "const SodClass *" cls , +.BI "void *" p , +.B ...); +.br +.B void *\c +.B sod_initv(\c +.BI "const SodClass *" cls , +.BI "void *" p , +.BI "va_list " ap ); +.br .B SOD_DECL(\c .IB cls , .IB var ); @@ -336,6 +354,55 @@ manage the standard steps along an instance's lifecycle. .PP The +.B SOD_INIT +macro, +and the +.B sod_init +and +.B sod_initv +functions, +imprint and initialize an instance of a class +.I cls +in the storage starting at address +.IR p . +.PP +The direct class for the new instance is specified as +a class name to +.BR SOD_INIT , +or a pointer to a class object to the functions. +.PP +Keyword arguments for the initialization message may be provided. +The +.B SOD_INIT +macro expects a single preprocessor-time argument +which is a use of one of +.B KWARGS +or +.B NO_KWARGS +(see +.BR keyword (3)); +the +.B sod_init +function expects the keywords as +a variable-length argument tail; +and +.B sod_initv +expects the keywords to be passed indirectly, +through the captured argument-tail cursor +.IR ap . +.PP +The return value is an instance pointer for the class +.IR cls ; +the +.B SOD_INIT +macro will have converted it to the correct type, +so it should probably be used where possible. +In fact, this is guaranteed to be equal to +.I p +by the layout rules described in +.BR sod-structs (3). +.PP +The .B SOD_DECL macro declares and initializes an instance with automatic storage duration. @@ -360,6 +427,7 @@ exits the scope of the declaration. . .\"-------------------------------------------------------------------------- .SH SEE ALSO +.BR keyword (3), .BR sod (1), .BR sod-structs (3). . diff --git a/lib/sod.c b/lib/sod.c index 1e8d061..aee28d5 100644 --- a/lib/sod.c +++ b/lib/sod.c @@ -120,4 +120,43 @@ void *sod_convert(const SodClass *cls, const void *obj) return ((char *)obj - vt->_base + chain->off_ichain); } +/* --- @sod_init@, @sod_initv@ --- * + * + * Arguments: @const SodClass *cls@ = class object for new instance + * @void *p@ = pointer to storage for new instance + * @va_list ap, ...@ = initialization keyword arguments + * + * Returns: Pointer to the initialized instance. + * + * Use: Initializes an instance in pre-allocated storage, and returns + * a pointer to it. + * + * This function will imprint the storage, and then send an + * `initialize' message to the fresh instance containing the + * provided keyword arguments. + * + * It's usually convenient to use the macro @SOD_INIT@ rather + * than calling @sod_init@ directly. + */ + +void *sod_init(const SodClass *cls, void *p, ...) +{ + va_list ap; + + va_start(ap, p); + sod_initv(cls, p, ap); + va_end(ap); + return (p); +} + +void *sod_initv(const SodClass *cls, void *p, va_list ap) +{ + SodObject *obj; + + cls->cls.imprint(p); + obj = SOD_CONVERT(SodObject, p); + SodObject_init__v(obj, ap); + return (p); +} + /*----- That's all, folks -------------------------------------------------*/ diff --git a/lib/sod.h b/lib/sod.h index e0e9193..185c6fb 100644 --- a/lib/sod.h +++ b/lib/sod.h @@ -227,19 +227,30 @@ struct sod_chain { #define SOD_CONVERT(cls, obj) ((cls *)sod_convert(cls##__class, (obj))) +/* --- @SOD_INIT@ --- * + * + * Arguments: @cls@ = a class type name + * @p@ = pointer to storage to initialize + * @keys@ = a @KWARGS(...)@ keyword argument sequence + * + * Use: Initializes raw storage as an instance of @cls@. + */ + +#define SOD_INIT(cls, p, keys) ((cls *)sod_init(cls##__class, (p), keys)) + /* --- @SOD_DECL@ --- * * - * Arguments: @cls_@ = a class type name - * @var_@ = a variable name + * Arguments: @cls@ = a class type name + * @var@ = a variable name + * @keys@ = a @KWARGS(...)@ keyword argument sequence * - * Use: Declare @var_@ as a pointer to an initialized instance of - * @cls_@ with automatic lifetime. + * Use: Declare @var@ as a pointer to an initialized instance of + * @cls@ with automatic lifetime. */ -#define SOD_DECL(cls_, var_) \ - struct cls_##__ilayout var_##__layout; \ - cls_ *var_ = \ - cls_##__class->cls.init(cls_##__class->cls.imprint(&var_##__layout)) +#define SOD_DECL(cls, var, keys) \ + struct cls##__ilayout var##__layout; \ + cls *var = (cls *)sod_init(cls##__class, &var##__layout, keys) /*----- Functions provided ------------------------------------------------*/ @@ -280,6 +291,28 @@ extern int sod_subclassp(const SodClass */*sub*/, const SodClass */*super*/); extern void *sod_convert(const SodClass */*cls*/, const void */*obj*/); +/* --- @sod_init@, @sod_initv@ --- * + * + * Arguments: @const SodClass *cls@ = class object for new instance + * @void *p@ = pointer to storage for new instance + * @va_list ap, ...@ = initialization keyword arguments + * + * Returns: Pointer to the initialized instance. + * + * Use: Initializes an instance in pre-allocated storage, and returns + * a pointer to it. + * + * This function will imprint the storage, and then send an + * `initialize' message to the fresh instance containing the + * provided keyword arguments. + * + * It's usually convenient to use the macro @SOD_INIT@ rather + * than calling @sod_init@ directly. + */ + +extern KWCALL void *sod_init(const SodClass */*cls*/, void */*p*/, ...); +extern void *sod_initv(const SodClass */*cls*/, void */*p*/, va_list /*ap*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/src/builtin.lisp b/src/builtin.lisp index 1073cae..6374e6d 100644 --- a/src/builtin.lisp +++ b/src/builtin.lisp @@ -109,44 +109,6 @@ (define-class-slot "imprint" (class stream) (sod-class-nickname tail)))) (ilayout-ichains ilayout))))) -(define-class-slot "init" (class stream) - (* (fun (* void) ("/*p*/" (* void)))) - (format nil "~A__init" class) - - ;; FIXME this needs a metaobject protocol - (let ((ilayout (sod-class-ilayout class))) - (format stream "~&~: -/* Provide initial values for an instance's slots. */ -static void *~A__init(void *p)~%{~%" class) - (dolist (ichain (ilayout-ichains ilayout)) - (let ((ich (format nil "sod__obj->~A.~A" - (sod-class-nickname (ichain-head ichain)) - (sod-class-nickname (ichain-tail ichain))))) - (dolist (item (ichain-body ichain)) - (etypecase item - (vtable-pointer - nil) - (islots - (let ((isl (format nil "~A.~A" - ich - (sod-class-nickname (islots-class item))))) - (dolist (slot (islots-slots item)) - (let ((dslot (effective-slot-direct-slot slot)) - (init (effective-slot-initializer slot))) - (when init - (format stream " {~% ") - (pprint-c-type (sod-slot-type dslot) stream - *sod-tmp-val*) - (format stream " = ~A;~% ~ - ~A.~A = ~A;~% ~ - }~%" - (sod-initializer-value init) - isl (sod-slot-name dslot) - *sod-tmp-val*)))))))))) - (format stream "~&~: - return (p); -}~2%"))) - ;;;-------------------------------------------------------------------------- ;;; Superclass structure. @@ -241,6 +203,104 @@ (define-class-slot "islotsz" (class) size-t (islots-struct-tag class)) "0")) +;;;-------------------------------------------------------------------------- +;;; Built-in methods. + +;; Common protocol. + +(defclass lifecycle-message (standard-message) + ()) + +(defclass lifecycle-effective-method (standard-effective-method) + ()) + +(defmethod effective-method-live-p ((method lifecycle-effective-method)) + t) + +(defgeneric lifecycle-method-kernel (method codegen target) + (:documentation + "Compute (into CODEGEN) the class-specific part of the METHOD. + + The result, if any, needs to find its way to the TARGET, as usual.")) + +(defmethod simple-method-body + ((method lifecycle-effective-method) codegen target) + (invoke-delegation-chain codegen target + (effective-method-basic-argument-names method) + (effective-method-primary-methods method) + (lambda (target) + (lifecycle-method-kernel method + codegen + target)))) + +;; Initialization. + +(defclass initialization-message (lifecycle-message) + ()) + +(defclass initialization-effective-method (lifecycle-effective-method) + ()) + +(defmethod sod-message-effective-method-class + ((message initialization-message)) + 'initialization-effective-method) + +(defmethod lifecycle-method-kernel + ((method initialization-effective-method) codegen target) + (let* ((class (effective-method-class method)) + (ilayout (sod-class-ilayout class)) + (obj-tag (ilayout-struct-tag class)) + (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag)))))) + (func-name (format nil "~A__init" class))) + + ;; Start building the initialization function. + (codegen-push codegen) + + (labels ((set-from-initializer (var type init) + ;; Store the value of INIT, which has the given TYPE, in VAR. + ;; INIT has the syntax of an initializer: declare and + ;; initialize a temporary, and then copy the result. + ;; Compilers seem to optimize this properly. Return the + ;; resulting code as an instruction. + (codegen-push codegen) + (emit-decl codegen (make-var-inst *sod-tmp-val* type init)) + (deliver-expr codegen var *sod-tmp-val*) + (codegen-pop-block codegen))) + + ;; Loop over the instance layout emitting initializers as we find them. + (dolist (ichain (ilayout-ichains ilayout)) + (let ((ich (format nil "sod__obj->~A.~A" + (sod-class-nickname (ichain-head ichain)) + (sod-class-nickname (ichain-tail ichain))))) + (dolist (item (ichain-body ichain)) + (etypecase item + (vtable-pointer + nil) + (islots + (let ((isl (format nil "~A.~A" + ich + (sod-class-nickname (islots-class item))))) + (dolist (slot (islots-slots item)) + (let ((dslot (effective-slot-direct-slot slot)) + (init (effective-slot-initializer slot))) + (when init + (let* ((slot-type (sod-slot-type dslot)) + (slot-default (sod-initializer-value init)) + (target (format nil "~A.~A" + isl (sod-slot-name dslot))) + (initinst (set-from-initializer target + slot-type + slot-default))) + (emit-inst codegen initinst)))))))))))) + + ;; Done making the initialization function. + (codegen-pop-function codegen func-name func-type + "Instance initialization function ~:_~ + for class `~A'." + class) + + (deliver-call codegen :void func-name "sod__obj"))) + ;;;-------------------------------------------------------------------------- ;;; Bootstrapping the class graph. @@ -256,6 +316,12 @@ (defun bootstrap-classes (module) (make-property-set :nick 'cls))) (classes (list sod-object sod-class))) + ;; Attach the built-in messages. + (make-sod-message sod-object "init" + (c-type (fun void :keys)) + (make-property-set + :message-class 'initialization-message)) + ;; Sort out the recursion. (setf (slot-value sod-class 'chain-link) sod-object) (dolist (class classes) diff --git a/src/sod.asd.in b/src/sod.asd.in index c09a622..9f36e1c 100644 --- a/src/sod.asd.in +++ b/src/sod.asd.in @@ -131,7 +131,7 @@ ("module-proto" "pset-proto" "c-types-class-impl" "builtin")) (:file "builtin" :depends-on ("module-proto" "pset-proto" "c-types-impl" "c-types-class-impl" - "classes" "class-layout-proto")) + "classes" "class-layout-proto" "method-proto")) (:file "module-parse" :depends-on ("class-make-proto" "class-finalize-proto" "fragment-parse" "lexer-proto" "module-impl")) diff --git a/test/chimaera.sod b/test/chimaera.sod index 0723ae1..976c727 100644 --- a/test/chimaera.sod +++ b/test/chimaera.sod @@ -82,25 +82,25 @@ static void provoke_serpent(Serpent *s) int main(void) { { - SOD_DECL(Lion, l); + SOD_DECL(Lion, l, NO_KWARGS); provoke_lion(l); tickle_animal(LION__CONV_NML(l)); } { - SOD_DECL(Goat, g); + SOD_DECL(Goat, g, NO_KWARGS); provoke_goat(g); tickle_animal(GOAT__CONV_NML(g)); } { - SOD_DECL(Serpent, s); + SOD_DECL(Serpent, s, NO_KWARGS); provoke_serpent(s); tickle_animal(SERPENT__CONV_NML(s)); } { - SOD_DECL(Chimaera, c); + SOD_DECL(Chimaera, c, NO_KWARGS); provoke_lion(CHIMAERA__CONV_LION(c)); provoke_goat(CHIMAERA__CONV_GOAT(c)); provoke_serpent(CHIMAERA__CONV_SERPENT(c)); diff --git a/test/test.sod b/test/test.sod index e5025b5..71fdd68 100644 --- a/test/test.sod +++ b/test/test.sod @@ -169,7 +169,7 @@ class T1Sub : T1Base { code c : tests { prepare("aggregate, base"); - { SOD_DECL(T1Base, t1); + { SOD_DECL(T1Base, t1, NO_KWARGS); struct item *l; struct vec v; STEP(0); T1Base_aprogn(t1); /* 1 */ @@ -185,7 +185,7 @@ code c : tests { DONE(7); } prepare("aggregate, sub"); - { SOD_DECL(T1Sub, t1); + { SOD_DECL(T1Sub, t1, NO_KWARGS); struct item *l; struct vec v; T1Base_aprogn(t1); /* 0, 1 */ -- [mdw]