src/builtin.lisp: Bind `me' around slot initializers, and define the order.
[sod] / src / builtin.lisp
index 1dde0d5..ffd8451 100644 (file)
@@ -90,8 +90,8 @@
   (format nil "~A__imprint" class)
   (let ((ilayout (sod-class-ilayout class)))
     (format stream "~&~:
-/* Imprint raw memory with instance structure. */
-static void *~A__imprint(void *p)
+/* Imprint raw memory with class `~A' instance structure. */
+static void *~:*~A__imprint(void *p)
 {
   struct ~A *sod__obj = p;
 
@@ -109,59 +109,6 @@ static void *~A__imprint(void *p)
                              (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))
-       (used nil))
-    (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
-                    (unless used
-                      (format stream
-                              "  struct ~A *sod__obj = ~A__imprint(p);~2%"
-                              (ilayout-struct-tag class) class)
-                      (setf used t))
-                    (format stream "  {~%    ~A ~A ="
-                            (sod-slot-type dslot)
-                            *sod-tmp-val*)
-                    (ecase (sod-initializer-value-kind init)
-                      (:simple (write (sod-initializer-value-form init)
-                                      :stream stream
-                                      :pretty nil :escape nil)
-                               (format stream ";~%"))
-                      (:compound (format stream " {")
-                                 (write (sod-initializer-value-form init)
-                                        :stream stream
-                                        :pretty nil :escape nil)
-                                 (format stream "    };~%")))
-                    (format stream "    ~A.~A = ~A;~%  }~%"
-                            isl (sod-slot-name dslot)
-                            *sod-tmp-val*))))))))))
-    (unless used
-      (format stream "  ~A__imprint(p);~%" class))
-    (format stream "~&~:
-  return (p);
-}~2%")))
-
 ;;;--------------------------------------------------------------------------
 ;;; Superclass structure.
 
@@ -257,6 +204,134 @@ static const SodClass *const ~A__cpl[] = {
       "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))))
+
+;; Utilities.
+
+(defun declare-me (codegen class)
+  "Emit, to CODEGEN, a declaration of `me' as a pointer to CLASS.
+
+   The pointer refers to a part of the prevailing `sod__obj' object, which is
+   assumed to be a pointer to an appropriate `ilayout' structure."
+  (emit-decl codegen (make-var-inst "me" (c-type (* (class class)))
+                                   (format nil "&sod__obj->~A.~A"
+                                           (sod-class-nickname
+                                            (sod-class-chain-head class))
+                                           (sod-class-nickname class)))))
+
+;; 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)))
+
+      ;; Initialize the structure defined by the various superclasses, in
+      ;; reverse precedence order.
+      (dolist (super (reverse (sod-class-precedence-list class)))
+       (let* ((ichain (find (sod-class-chain-head super)
+                            (ilayout-ichains ilayout)
+                            :key #'ichain-head))
+              (islots (find super (ichain-body ichain)
+                            :test (lambda (class item)
+                                    (and (typep item 'islots)
+                                         (eq (islots-class item) class)))))
+              (this-class-focussed-p nil)
+              (isl (format nil "me->~A" (sod-class-nickname super))))
+
+         (flet ((focus-this-class ()
+                  ;; Delayed initial preparation.  Don't bother defining the
+                  ;; `me' pointer if there's actually nothing to do.
+                  (unless this-class-focussed-p
+                    (emit-banner codegen
+                                 "Initialization for class `~A'." super)
+                    (codegen-push codegen)
+                    (declare-me codegen super)
+                    (setf this-class-focussed-p t))))
+
+           ;; Work through each slot in turn.
+           (dolist (slot (and islots (islots-slots islots)))
+             (let ((dslot (effective-slot-direct-slot slot))
+                   (init (effective-slot-initializer slot)))
+               (when init
+                 (focus-this-class)
+                 (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)))))
+
+           ;; If we opened a block to initialize this class then close it
+           ;; again.
+           (when this-class-focussed-p
+             (emit-inst codegen (codegen-pop-block codegen)))))))
+
+    ;; 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.
 
 (defun bootstrap-classes (module)
@@ -271,6 +346,12 @@ static const SodClass *const ~A__cpl[] = {
                                    (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)