GOOPS cosmetics
[bpt/guile.git] / module / oop / goops.scm
index fd1b9ff..172839a 100644 (file)
@@ -1,6 +1,6 @@
-;;; installed-scm-file
-
-;;;; Copyright (C) 1998,1999,2000-2003,2006,2009-2011,2013-2015 Free Software Foundation, Inc.
+;;;; goops.scm -- The Guile Object-Oriented Programming System
+;;;;
+;;;; Copyright (C) 1998-2003,2006,2009-2011,2013-2015 Free Software Foundation, Inc.
 ;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
 ;;;;
 ;;;; This library is free software; you can redistribute it and/or
             method-specializers method-formals
             primitive-generic-generic enable-primitive-generic!
             method-procedure accessor-method-slot-definition
-            make find-method get-keyword)
-  #:no-backtrace)
+            make find-method get-keyword))
 
 
 ;;;
 ;;; class-index-flags.  `vtable-flag-vtable' indicates that instances of
 ;;; a vtable are themselves vtables, and `vtable-flag-validated'
 ;;; indicates that the struct's layout has been validated.  goops.c
-;;; defines a couple of additional flags: one to indicate that a vtable
-;;; is actually a class, and one to indicate that the class is "valid",
-;;; meaning that it hasn't been redefined.
+;;; defines a few additional flags: one to indicate that a vtable is
+;;; actually a class, one to indicate that the class is "valid" (meaning
+;;; that it hasn't been redefined), and one to indicate that instances
+;;; of a class are slot definition objects (<slot> instances).
 ;;;
 (define vtable-flag-goops-metaclass
   (logior vtable-flag-vtable vtable-flag-goops-class))
 (define-inlinable (class? obj)
   (class-has-flags? (struct-vtable obj) vtable-flag-goops-metaclass))
 
+(define-inlinable (slot? obj)
+  (and (struct? obj)
+       (class-has-flags? (struct-vtable obj) vtable-flag-goops-slot)))
+
 (define-inlinable (instance? obj)
   (class-has-flags? (struct-vtable obj) vtable-flag-goops-class))
 
@@ -377,7 +381,6 @@ subclasses of @var{c}."
 ;;; more of the protocol.  Again, the CPL and class hierarchy slots
 ;;; remain uninitialized.
 ;;;
-
 (define* (get-keyword key l #:optional default)
   "Determine an associated value for the keyword @var{key} from the list
 @var{l}.  The list @var{l} has to consist of an even number of elements,
@@ -426,70 +429,65 @@ followed by its associated value.  If @var{l} does not hold a value for
       (struct-set! <slot> class-index-redefined #f)
       <slot>)))
 
-(define-inlinable (slot? obj)
-  (and (struct? obj)
-       (class-has-flags? (struct-vtable obj) vtable-flag-goops-slot)))
-
-(define-syntax-rule (define-slot-accessor name docstring field)
-  (define (name obj)
-    docstring
-    (let ((val obj))
-      (unless (slot? val)
+;;; Access to slot objects is performance-sensitive for slot-ref, so in
+;;; addition to the type-checking accessors that we export, we also
+;;; define some internal inlined helpers that just do an unchecked
+;;; struct-ref in cases where we know the object must be a slot, as
+;;; when accessing class-slots.
+;;;
+(define-syntax-rule (define-slot-accessor name docstring %name field)
+  (begin
+    (define-syntax-rule (%name obj)
+      (struct-ref obj field))
+    (define (name obj)
+      docstring
+      (unless (slot? obj)
         (scm-error 'wrong-type-arg #f "Not a slot: ~S"
-                   (list val) #f))
-      (struct-ref val field))))
+                   (list obj) #f))
+      (%name obj))))
 
 (define-slot-accessor slot-definition-name
   "Return the name of @var{obj}."
-  slot-index-name)
+   %slot-definition-name slot-index-name)
 (define-slot-accessor slot-definition-allocation
   "Return the allocation of the slot @var{obj}."
-  slot-index-allocation)
+   %slot-definition-allocation slot-index-allocation)
 (define-slot-accessor slot-definition-init-keyword
   "Return the init keyword of the slot @var{obj}, or @code{#f}."
-  slot-index-init-keyword)
+   %slot-definition-init-keyword slot-index-init-keyword)
 (define-slot-accessor slot-definition-init-form
   "Return the init form of the slot @var{obj}, or the unbound value"
-  slot-index-init-form)
+   %slot-definition-init-form slot-index-init-form)
 (define-slot-accessor slot-definition-init-value
   "Return the init value of the slot @var{obj}, or the unbound value."
-  slot-index-init-value)
+   %slot-definition-init-value slot-index-init-value)
 (define-slot-accessor slot-definition-init-thunk
   "Return the init thunk of the slot @var{obj}, or @code{#f}."
-  slot-index-init-thunk)
+   %slot-definition-init-thunk slot-index-init-thunk)
 (define-slot-accessor slot-definition-options
   "Return the initargs given when creating the slot @var{obj}."
-  slot-index-options)
+   %slot-definition-options slot-index-options)
 (define-slot-accessor slot-definition-getter
   "Return the getter of the slot @var{obj}, or @code{#f}."
-  slot-index-getter)
+   %slot-definition-getter slot-index-getter)
 (define-slot-accessor slot-definition-setter
   "Return the setter of the slot @var{obj}, or @code{#f}."
-  slot-index-setter)
+   %slot-definition-setter slot-index-setter)
 (define-slot-accessor slot-definition-accessor
   "Return the accessor of the slot @var{obj}, or @code{#f}."
-  slot-index-accessor)
+   %slot-definition-accessor slot-index-accessor)
 (define-slot-accessor slot-definition-slot-ref
   "Return the slot-ref procedure of the slot @var{obj}, or @code{#f}."
-  slot-index-slot-ref)
+   %slot-definition-slot-ref slot-index-slot-ref)
 (define-slot-accessor slot-definition-slot-set!
   "Return the slot-set! procedure of the slot @var{obj}, or @code{#f}."
-  slot-index-slot-set!)
+   %slot-definition-slot-set! slot-index-slot-set!)
 (define-slot-accessor slot-definition-index
   "Return the allocated struct offset of the slot @var{obj}, or @code{#f}."
-  slot-index-index)
+   %slot-definition-index slot-index-index)
 (define-slot-accessor slot-definition-size
   "Return the number fields used by the slot @var{obj}, or @code{#f}."
-  slot-index-size)
-
-(define (class-slot-definition class slot-name)
-  (let lp ((slots (class-slots class)))
-    (match slots
-      (() #f)
-      ((slot . slots)
-       (if (eq? (struct-ref slot slot-index-name) slot-name)
-           slot
-           (lp slots))))))
+   %slot-definition-size slot-index-size)
 
 ;; Boot definition.
 (define (direct-slot-definition-class class initargs)
@@ -507,7 +505,7 @@ followed by its associated value.  If @var{l} does not hold a value for
     (init-slot slot-index-init-value #:init-value *unbound*)
     (struct-set! slot slot-index-init-thunk
                  (or (get-keyword #:init-thunk initargs #f)
-                     (let ((val (struct-ref slot slot-index-init-value)))
+                     (let ((val (%slot-definition-init-value slot)))
                        (if (unbound? val)
                            #f
                            (lambda () val)))))
@@ -628,12 +626,12 @@ followed by its associated value.  If @var{l} does not hold a value for
 
 (define (build-slots-list dslots cpl)
   (define (slot-memq slot slots)
-    (let ((name (slot-definition-name slot)))
+    (let ((name (%slot-definition-name slot)))
       (let lp ((slots slots))
         (match slots
           (() #f)
           ((slot . slots)
-           (or (eq? (slot-definition-name slot) name) (lp slots)))))))
+           (or (eq? (%slot-definition-name slot) name) (lp slots)))))))
   (define (check-cpl slots static-slots)
     (when (or-map (lambda (slot) (slot-memq slot slots)) static-slots)
       (scm-error 'misc-error #f
@@ -644,7 +642,7 @@ followed by its associated value.  If @var{l} does not hold a value for
       (match slots
         (() res)
         ((slot . slots)
-         (let ((name (slot-definition-name slot)))
+         (let ((name (%slot-definition-name slot)))
            (if (memq name seen)
                (lp slots res seen)
                (lp slots (cons slot res) (cons name seen))))))))
@@ -714,7 +712,7 @@ slots as we go."
   (define (slot-protection-and-kind slot)
     (define (subclass? class parent)
       (memq parent (class-precedence-list class)))
-    (let ((type (kw-arg-ref (struct-ref slot slot-index-options) #:class)))
+    (let ((type (kw-arg-ref (%slot-definition-options slot) #:class)))
       (if (and type (subclass? type <foreign-slot>))
           (values (cond
                    ((subclass? type <self-slot>) #\s)
@@ -737,10 +735,10 @@ slots as we go."
                (error "bad layout for class"))))
          layout)
         ((slot . slots)
-         (unless (= n (slot-definition-index slot)) (error "bad allocation"))
+         (unless (= n (%slot-definition-index slot)) (error "bad allocation"))
          (call-with-values (lambda () (slot-protection-and-kind slot))
            (lambda (protection kind)
-             (let init ((n n) (size (slot-definition-size slot)))
+             (let init ((n n) (size (%slot-definition-size slot)))
                (cond
                 ((zero? size) (lp n slots))
                 (else
@@ -1049,33 +1047,6 @@ function."
 
 ;;;
 ;;; Slot access.
-;;;
-(define (get-slot-value-using-name class obj slot-name)
-  (cond
-   ((class-slot-definition class slot-name)
-    => (lambda (slot)
-         (cond
-          ((slot-definition-slot-ref slot)
-           => (lambda (slot-ref) (slot-ref obj)))
-          (else
-           (struct-ref obj (slot-definition-index slot))))))
-   (else (slot-missing class obj slot-name))))
-
-(define (set-slot-value-using-name! class obj slot-name value)
-  (cond
-   ((class-slot-definition class slot-name)
-    => (lambda (slot)
-         (cond
-          ((slot-definition-slot-set! slot)
-           => (lambda (slot-set!) (slot-set! obj value)))
-          (else
-           (struct-set! obj (slot-definition-index slot) value)))))
-   (else (slot-missing class obj slot-name))))
-
-(define (test-slot-existence class obj slot-name)
-  (and (class-slot-definition class slot-name)
-       #t))
-
 ;;;
 ;;; Before we go on, some notes about class redefinition.  In GOOPS,
 ;;; classes can be redefined.  Redefinition of a class marks the class
@@ -1089,38 +1060,93 @@ function."
 ;;; here though as the { class, object data } pair needs to be accessed
 ;;; atomically, not the { class, object } pair.
 ;;;
+(define-inlinable (%class-slot-definition class slot-name kt kf)
+  (let lp ((slots (struct-ref class class-index-slots)))
+    (match slots
+      ((slot . slots)
+       (if (eq? (%slot-definition-name slot) slot-name)
+           (kt slot)
+           (lp slots)))
+      (_ (kf)))))
+
+(define (class-slot-definition class slot-name)
+  (unless (class? class)
+    (scm-error 'wrong-type-arg #f "Not a class: ~S" (list class) #f))
+  (%class-slot-definition class slot-name
+                          (lambda (slot) slot)
+                          (lambda () #f)))
 
 (define (slot-ref obj slot-name)
   "Return the value from @var{obj}'s slot with the nam var{slot_name}."
-  (unless (symbol? slot-name)
-    (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
-               (list slot-name) #f))
-  (let* ((class (class-of obj))
-         (val (get-slot-value-using-name class obj slot-name)))
-    (if (unbound? val)
-        (slot-unbound class obj slot-name)
-        val)))
+  (let ((class (class-of obj)))
+    (define (slot-value slot)
+      (cond
+       ((%slot-definition-slot-ref slot)
+        => (lambda (slot-ref) (slot-ref obj)))
+       (else
+        (struct-ref obj (%slot-definition-index slot)))))
+    (define (have-slot slot)
+      (let ((val (slot-value slot)))
+        (if (unbound? val)
+            (slot-unbound class obj slot-name)
+            val)))
+    (define (no-slot)
+      (unless (symbol? slot-name)
+        (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
+                   (list slot-name) #f))
+      (let ((val (slot-missing class obj slot-name)))
+        (if (unbound? val)
+            (slot-unbound class obj slot-name)
+            val)))
+    (%class-slot-definition class slot-name have-slot no-slot)))
 
 (define (slot-set! obj slot-name value)
   "Set the slot named @var{slot_name} of @var{obj} to @var{value}."
-  (unless (symbol? slot-name)
-    (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
-               (list slot-name) #f))
-  (set-slot-value-using-name! (class-of obj) obj slot-name value))
+  (let ((class (class-of obj)))
+    (define (have-slot slot)
+      (cond
+       ((%slot-definition-slot-set! slot)
+        => (lambda (slot-set!) (slot-set! obj value)))
+       (else
+        (struct-set! obj (%slot-definition-index slot) value))))
+    (define (no-slot)
+      (unless (symbol? slot-name)
+        (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
+                   (list slot-name) #f))
+      (slot-missing class obj slot-name value))
+
+    (%class-slot-definition class slot-name have-slot no-slot)))
 
 (define (slot-bound? obj slot-name)
   "Return the value from @var{obj}'s slot with the nam var{slot_name}."
-  (unless (symbol? slot-name)
-    (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
-               (list slot-name) #f))
-  (not (unbound? (get-slot-value-using-name (class-of obj) obj slot-name))))
+  (let ((class (class-of obj)))
+    (define (slot-value slot)
+      (cond
+       ((%slot-definition-slot-ref slot)
+        => (lambda (slot-ref) (slot-ref obj)))
+       (else
+        (struct-ref obj (%slot-definition-index slot)))))
+    (define (have-slot slot)
+      (not (unbound? (slot-value slot))))
+    (define (no-slot)
+      (unless (symbol? slot-name)
+        (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
+                   (list slot-name) #f))
+      (let ((val (slot-missing class obj slot-name)))
+        (if (unbound? val)
+            (slot-unbound class obj slot-name)
+            val)))
+    (%class-slot-definition class slot-name have-slot no-slot)))
 
 (define (slot-exists? obj slot-name)
   "Return @code{#t} if @var{obj} has a slot named @var{slot_name}."
-  (unless (symbol? slot-name)
-    (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
-               (list slot-name) #f))
-  (test-slot-existence (class-of obj) obj slot-name))
+  (define (have-slot slot) #t)
+  (define (no-slot)
+    (unless (symbol? slot-name)
+      (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
+                 (list slot-name) #f))
+    #f)
+  (%class-slot-definition (class-of obj) slot-name have-slot no-slot))
 
 (begin-deprecated
  (define (check-slot-args class obj slot-name)
@@ -1531,7 +1557,7 @@ function."
     (match slot-spec
       (((? symbol? name) . args) name)
       ;; We can get here when redefining classes.
-      ((? slot? slot) (slot-definition-name slot))))
+      ((? slot? slot) (%slot-definition-name slot))))
 
   (let* ((name (get-keyword #:name options *unbound*))
          (supers (if (not (or-map (lambda (class)
@@ -2161,8 +2187,8 @@ function."
 ;;; Slots
 ;;;
 (define (slot-init-function class slot-name)
-  (slot-definition-init-thunk (or (class-slot-definition class slot-name)
-                                  (error "slot not found" slot-name))))
+  (%slot-definition-init-thunk (or (class-slot-definition class slot-name)
+                                   (error "slot not found" slot-name))))
 
 (define (accessor-method-slot-definition obj)
   "Return the slot definition of the accessor @var{obj}."
@@ -2224,7 +2250,7 @@ function."
           (display "#<" file)
           (display (class-name class) file)
           (display #\space file)
-          (display (slot-definition-name slot) file)
+          (display (%slot-definition-name slot) file)
           (display #\space file)
           (display-address slot file)
           (display #\> file))
@@ -2372,18 +2398,18 @@ function."
 
 (define (class-slot-ref class slot-name)
   (let ((slot (class-slot-definition class slot-name)))
-    (unless (memq (slot-definition-allocation slot) '(#:class #:each-subclass))
+    (unless (memq (%slot-definition-allocation slot) '(#:class #:each-subclass))
       (slot-missing class slot-name))
-    (let ((x ((slot-definition-slot-ref slot) #f)))
+    (let ((x ((%slot-definition-slot-ref slot) #f)))
       (if (unbound? x)
           (slot-unbound class slot-name)
           x))))
 
 (define (class-slot-set! class slot-name value)
   (let ((slot (class-slot-definition class slot-name)))
-    (unless (memq (slot-definition-allocation slot) '(#:class #:each-subclass))
+    (unless (memq (%slot-definition-allocation slot) '(#:class #:each-subclass))
       (slot-missing class slot-name))
-    ((slot-definition-slot-set! slot) #f value)))
+    ((%slot-definition-slot-set! slot) #f value)))
 
 (define-method (slot-unbound (c <class>) (o <object>) s)
   (goops-error "Slot `~S' is unbound in object ~S" s o))
@@ -2562,10 +2588,10 @@ function."
 (define (compute-slot-accessors class slots)
   (for-each
    (lambda (slot)
-     (let ((getter (slot-definition-getter slot))
-           (setter (slot-definition-setter slot))
+     (let ((getter (%slot-definition-getter slot))
+           (setter (%slot-definition-setter slot))
            (accessor-setter setter)
-           (accessor (slot-definition-accessor slot)))
+           (accessor (%slot-definition-accessor slot)))
        (when getter
          (add-method! getter (compute-getter-method class slot)))
        (when setter
@@ -2715,30 +2741,49 @@ function."
 (define (%initialize-object obj initargs)
   "Initialize the object @var{obj} with the given arguments
 var{initargs}."
+  (define (valid-initargs? initargs)
+    (match initargs
+      (() #t)
+      (((? keyword?) _ . initargs) (valid-initargs? initargs))
+      (_ #f)))
   (unless (instance? obj)
     (scm-error 'wrong-type-arg #f "Not an object: ~S"
                (list obj) #f))
-  (unless (even? (length initargs))
-    (scm-error 'wrong-type-arg #f "Initargs has odd length: ~S"
+  (unless (valid-initargs? initargs)
+    (scm-error 'wrong-type-arg #f "Invalid initargs: ~S"
                (list initargs) #f))
   (let ((class (class-of obj)))
     (define (get-initarg kw)
       (if kw
-          (get-keyword kw initargs *unbound*)
+          ;; Inlined get-keyword to avoid checking initargs for validity
+          ;; each time.
+          (let lp ((initargs initargs))
+            (match initargs
+              ((kw* val . initargs)
+               (if (eq? kw* kw)
+                   val
+                   (lp initargs)))
+              (_ *unbound*)))
           *unbound*))
     (let lp ((slots (struct-ref class class-index-slots)))
       (match slots
         (() obj)
         ((slot . slots)
-         (let ((initarg (get-initarg (slot-definition-init-keyword slot))))
+         (define (initialize-slot! value)
+           (cond
+            ((%slot-definition-slot-set! slot)
+             => (lambda (slot-set!) (slot-set! obj value)))
+            (else
+             (struct-set! obj (%slot-definition-index slot) value))))
+         (let ((initarg (get-initarg (%slot-definition-init-keyword slot))))
            (cond
             ((not (unbound? initarg))
-             (slot-set! obj (slot-definition-name slot) initarg))
-            ((slot-definition-init-thunk slot)
+             (initialize-slot! initarg))
+            ((%slot-definition-init-thunk slot)
              => (lambda (init-thunk)
                   (unless (memq (slot-definition-allocation slot)
                                 '(#:class #:each-subclass))
-                    (slot-set! obj (slot-definition-name slot) (init-thunk)))))))
+                    (initialize-slot! (init-thunk)))))))
          (lp slots))))))
 
 (define-method (initialize (object <object>) initargs)
@@ -2747,11 +2792,11 @@ var{initargs}."
 (define-method (initialize (slot <slot>) initargs)
   (next-method)
   (struct-set! slot slot-index-options initargs)
-  (let ((init-thunk (struct-ref slot slot-index-init-thunk)))
+  (let ((init-thunk (%slot-definition-init-thunk slot)))
     (when init-thunk
       (unless (thunk? init-thunk)
         (goops-error "Bad init-thunk for slot `~S': ~S"
-                     (slot-definition-name slot) init-thunk)))))
+                     (%slot-definition-name slot) init-thunk)))))
 
 (define-method (initialize (class <class>) initargs)
   (define (make-direct-slot-definition dslot)
@@ -2852,7 +2897,7 @@ var{initargs}."
     (for-each
      (lambda (slot)
        (if (and (slot-exists? old-instance slot)
-                (eq? (slot-definition-allocation
+                (eq? (%slot-definition-allocation
                       (class-slot-definition old-class slot))
                      #:instance)
                 (slot-bound? old-instance slot))