diff --git a/src/clos/boot.lsp b/src/clos/boot.lsp index bf5f3173b..b2bc492e0 100644 --- a/src/clos/boot.lsp +++ b/src/clos/boot.lsp @@ -15,6 +15,38 @@ (defconstant +builtin-classes-pre-array+ (make-array (1+ #.(length +builtin-classes-list+)))) + +;;; +;;; The following macro is also used at bootstap for instantiating +;;; a class based only on the s-form description. +;;; +(eval-when (:compile-toplevel :execute) + (defmacro with-early-make-instance (slots (object class &rest key-value-pairs) + &rest body) + (when (symbolp slots) + (setf slots (symbol-value slots))) + `(let* ((%class ,class) + (,object (si::allocate-raw-instance nil %class + ,(length slots)))) + (declare (type standard-object ,object)) + ,@(flet ((initializerp (name list) + (not (eq (getf list name 'wrong) 'wrong)))) + (loop for (name . slotd) in slots + for initarg = (getf slotd :initarg) + for initform = (getf slotd :initform (si::unbound)) + for initvalue = (getf key-value-pairs initarg) + for index from 0 + do (cond ((and initarg (initializerp initarg key-value-pairs)) + (setf initform (getf key-value-pairs initarg))) + ((initializerp name key-value-pairs) + (setf initform (getf key-value-pairs name)))) + when (si:sl-boundp initform) + collect `(si::instance-set ,object ,index ,initform))) + (when %class + (si::instance-sig-set ,object)) + (with-early-accessors (,slots) + ,@body)))) + ;;; ---------------------------------------------------------------------- ;;; Building the classes T, CLASS, STANDARD-OBJECT and STANDARD-CLASS. ;;; @@ -87,42 +119,6 @@ (slot-table class) table (class-direct-slots class) (copy-list all-slots))))) -(defun reader-closure (index) - (declare (si::c-local)) - (lambda (object) (si::instance-ref object index))) - -(defun writer-closure (index) - (declare (si::c-local)) - (lambda (value object) (si::instance-set object index value))) - -(defun generate-accessors (class slotd-definitions) - (declare (si::c-local) - (optimize speed (safety 0))) - (loop for index from 0 - for slotd in slotd-definitions - do (loop with key-value-pairs = (rest slotd) - for key = (pop key-value-pairs) - for value = (pop key-value-pairs) - while key - do (case key - (:reader - (setf (fdefinition value) (reader-closure index)) - #+(or) - (install-method value nil (list class) '(self) - (reader-closure index) t)) - #+(or) - (:writer ;; not used above - (setf (fdefinition value) (writer-closure index))) - (:accessor - (setf (fdefinition value) (reader-closure index) - (fdefinition `(setf ,value)) (writer-closure index)) - #+(or) - (install-method value nil (list class) '(self) - (reader-closure index) t)) - #+(or) - (install-method value nil (list (find-class 't) class) '(value self) - (writer-closure index) t))))) - ;; 1) Create the classes ;; ;; Notice that, due to circularity in the definition, STANDARD-CLASS has @@ -137,8 +133,6 @@ (defconstant +the-std-class+ (find-class 'std-class nil)) (defconstant +the-funcallable-standard-class+ (find-class 'funcallable-standard-class nil)) - (loop for c in class-hierarchy - do (generate-accessors (find-class (first c)) (getf (rest c) :direct-slots))) ;; ;; 2) Class T had its metaclass wrong. Fix it. ;; @@ -150,5 +144,5 @@ ;; ;; This is needed for further optimization ;; - (setf (class-sealedp (find-class 'method-combination)) t) + (setf (slot-value (find-class 'method-combination) 'sealedp) t) )) diff --git a/src/clos/load.lsp.in b/src/clos/load.lsp.in index 2cbc30627..14389a1de 100644 --- a/src/clos/load.lsp.in +++ b/src/clos/load.lsp.in @@ -7,6 +7,7 @@ "src:clos;std-slot-value.lsp" "src:clos;boot.lsp" "src:clos;kernel.lsp" + "src:clos;std-accessors.lsp" "src:clos;method.lsp" "src:clos;combin.lsp" "src:clos;defclass.lsp" diff --git a/src/clos/standard.lsp b/src/clos/standard.lsp index 841a0bd6e..2392d2a6e 100644 --- a/src/clos/standard.lsp +++ b/src/clos/standard.lsp @@ -565,165 +565,6 @@ because it contains a reference to the undefined class~% ~A" (defmethod compute-slots :around ((class std-class)) (std-class-compute-slots class (call-next-method))) -;;; ---------------------------------------------------------------------- -;;; Optional accessors -;;; - -(defun unbound-slot-error (object index) - (declare (type standard-object object) - (type fixnum index) - (optimize (safety 0)) - (si::c-local)) - (let* ((class (class-of object)) - (slotd (find index (class-slots class) :key #'slot-definition-location))) - (values (slot-unbound class object (slot-definition-name slotd))))) - -(defun safe-instance-ref (object index) - (declare (type standard-object object) - (type fixnum index) - (optimize (safety 0))) - (let ((value (si:instance-ref object index))) - (if (si:sl-boundp value) - value - (unbound-slot-error object index)))) - -;;; The following does not get as fast as it should because we are not -;;; allowed to memoize the position of a slot. The problem is that the -;;; AMOP specifies that slot accessors are created from the direct -;;; slots, without knowing the slot position. This semantics is -;;; required for working standard-reader- and -;;; standard-writer-method. OTOH if we want to have memoized slot -;;; positions we have to work from the effective slots and we have to -;;; create methods for all slots, not only the direct ones in this -;;; class. Both semantics are incompatible, but we currently have no -;;; safe way to choose one or another -;;; -(defun std-class-optimized-accessors (slot-name) - (declare (si::c-local)) - (macrolet ((slot-table (class) - `(si::instance-ref ,class #.(position 'slot-table +standard-class-slots+ - :key #'first))) - (slot-definition-location (slotd) - `(si::instance-ref ,slotd #.(position 'location +slot-definition-slots+ - :key #'first)))) - (values #'(lambda (self) - (declare (optimize (safety 0) (speed 3) (debug 0)) - (standard-object self)) - (ensure-up-to-date-instance self) - (let* ((class (si:instance-class self)) - (table (slot-table class)) - (slotd (truly-the slot-definition (gethash slot-name table))) - (index (slot-definition-location slotd)) - (value (if (si::fixnump index) - (si:instance-ref self (truly-the fixnum index)) - (car (truly-the cons index))))) - (if (si:sl-boundp value) - value - (values (slot-unbound (class-of self) self slot-name))))) - #'(lambda (value self) - (declare (optimize (safety 0) (speed 3) (debug 0)) - (standard-object self)) - (ensure-up-to-date-instance self) - (let* ((class (si:instance-class self)) - (table (slot-table class)) - (slotd (truly-the slot-definition (gethash slot-name table))) - (index (slot-definition-location slotd))) - (if (si::fixnump index) - (si:instance-set self (truly-the fixnum index) value) - (rplaca (truly-the cons index) value))))))) - -(defun std-class-sealed-accessors (index) - (declare (si::c-local) - (fixnum index)) - (values #'(lambda (self) - (declare (optimize (safety 0) (speed 3) (debug 0)) - (standard-object self)) - (ensure-up-to-date-instance self) - (safe-instance-ref self index)) - #'(lambda (value self) - (declare (optimize (safety 0) (speed 3) (debug 0)) - (standard-object self)) - (ensure-up-to-date-instance self) - (si:instance-set self index value)))) - -(defun std-class-accessors (slot-name) - (declare (si::c-local)) - ;; The following are very slow. We do not optimize for the slot position. - (values #'(lambda (self) - (slot-value self slot-name)) - #'(lambda (value self) - (setf (slot-value self slot-name) value)))) - -(defun safe-add-method (name method) - ;; Adds a method to a function which might have been previously defined - ;; as non-generic, without breaking the function - (cond ((or *clos-booted* - (not (fboundp name)) - (si::instancep (fdefinition name))) - (add-method (ensure-generic-function name) method)) - (t - (let* ((alt-name '#:foo) - (gf (ensure-generic-function alt-name))) - (add-method gf method) - (setf (generic-function-name gf) name) - (setf (fdefinition name) gf) - (fmakunbound alt-name))))) - -(defun std-class-generate-accessors (standard-class &aux optimizable) - ;; - ;; The accessors are closures, which are generated every time the - ;; slots of the class change. The accessors are safe: they check that - ;; the slot is bound after retreiving the value, and they may take - ;; the liberty of using SI:INSTANCE-REF because they know the class of - ;; the instance. - ;; - (dolist (slotd (class-direct-slots standard-class)) - (multiple-value-bind (reader writer) - (let ((name (slot-definition-name slotd)) - (allocation (slot-definition-allocation slotd)) - (location (safe-slot-definition-location slotd))) - (cond ((and (eq allocation :instance) (typep location 'fixnum)) - (std-class-sealed-accessors (slot-definition-location slotd))) - ;; When a class is the specified STANDARD-CLASS, then the - ;; user may not write any method around SLOT-VALUE-USING-CLASS - ;; This allows us to write optimized versions of the accessors. - ((and (eq allocation :instance) - (eq standard-class +the-standard-class+)) - (std-class-optimized-accessors name)) - (t - (std-class-accessors name)))) - (let* ((reader-args (list :function reader - :generic-function nil - :qualifiers nil - :lambda-list '(object) - :specializers `(,standard-class) - :slot-definition slotd)) - (reader-class (if (boundp '*early-methods*) - 'standard-reader-method - (apply #'reader-method-class standard-class slotd - reader-args))) - (writer-args (list :function writer - :generic-function nil - :qualifiers nil - :lambda-list '(value object) - :specializers `(,(find-class t) ,standard-class) - :slot-definition slotd)) - (writer-class (if (boundp '*early-methods*) - 'standard-writer-method - (apply #'writer-method-class standard-class slotd - writer-args)))) - (dolist (fname (slot-definition-readers slotd)) - (safe-add-method fname - (make-method reader-class nil `(,standard-class) '(self) - (wrapped-method-function reader) - (list :slot-definition slotd)))) - (dolist (fname (slot-definition-writers slotd)) - (safe-add-method fname - (make-method writer-class nil - `(,(find-class t) ,standard-class) '(value self) - (wrapped-method-function writer) - (list :slot-definition slotd)))))))) - ;;; ====================================================================== ;;; STANDARD-OBJECT ;;; diff --git a/src/clos/std-accessors.lsp b/src/clos/std-accessors.lsp new file mode 100644 index 000000000..fca7b6ee7 --- /dev/null +++ b/src/clos/std-accessors.lsp @@ -0,0 +1,203 @@ +;;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: CLOS -*- +;;;; +;;;; Copyright (c) 1992, Giuseppe Attardi.o +;;;; Copyright (c) 2001, Juan Jose Garcia Ripoll. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 2 of the License, or (at your option) any later version. +;;;; +;;;; See file '../Copyright' for full details. + +(in-package "CLOS") + +;;; ---------------------------------------------------------------------- +;;; ACCESSOR / READER / WRITER METHOD CREATION +;;; +;;; The following code creates optimized and unoptimized versions of the +;;; slot accessors defined for a class. They are designed so that at least +;;; some varieties work at boot time. +;;; + +(defun unbound-slot-error (object index) + (declare (type standard-object object) + (type fixnum index) + (optimize (safety 0)) + (si::c-local)) + (let* ((class (class-of object)) + (slotd (find index (class-slots class) :key #'slot-definition-location))) + (values (slot-unbound class object (slot-definition-name slotd))))) + +(defun safe-instance-ref (object index) + (declare (type standard-object object) + (type fixnum index) + (optimize (safety 0))) + (let ((value (si:instance-ref object index))) + (if (si:sl-boundp value) + value + (unbound-slot-error object index)))) + +;;; The following does not get as fast as it should because we are not +;;; allowed to memoize the position of a slot. The problem is that the +;;; AMOP specifies that slot accessors are created from the direct +;;; slots, without knowing the slot position. This semantics is +;;; required for working standard-reader- and +;;; standard-writer-method. OTOH if we want to have memoized slot +;;; positions we have to work from the effective slots and we have to +;;; create methods for all slots, not only the direct ones in this +;;; class. Both semantics are incompatible, but we currently have no +;;; safe way to choose one or another +;;; +(defun std-class-optimized-accessors (slot-name) + (declare (si::c-local)) + (macrolet ((slot-table (class) + `(si::instance-ref ,class #.(position 'slot-table +standard-class-slots+ + :key #'first))) + (slot-definition-location (slotd) + `(si::instance-ref ,slotd #.(position 'location +slot-definition-slots+ + :key #'first)))) + (values #'(lambda (self) + (declare (optimize (safety 0) (speed 3) (debug 0)) + (standard-object self)) + (ensure-up-to-date-instance self) + (let* ((class (si:instance-class self)) + (table (slot-table class)) + (slotd (truly-the slot-definition (gethash slot-name table))) + (index (slot-definition-location slotd)) + (value (if (si::fixnump index) + (si:instance-ref self (truly-the fixnum index)) + (car (truly-the cons index))))) + (if (si:sl-boundp value) + value + (values (slot-unbound (class-of self) self slot-name))))) + #'(lambda (value self) + (declare (optimize (safety 0) (speed 3) (debug 0)) + (standard-object self)) + (ensure-up-to-date-instance self) + (let* ((class (si:instance-class self)) + (table (slot-table class)) + (slotd (truly-the slot-definition (gethash slot-name table))) + (index (slot-definition-location slotd))) + (if (si::fixnump index) + (si:instance-set self (truly-the fixnum index) value) + (rplaca (truly-the cons index) value))))))) + +(defun std-class-sealed-accessors (index) + (declare (si::c-local) + (fixnum index)) + (values #'(lambda (self) + (declare (optimize (safety 0) (speed 3) (debug 0)) + (standard-object self)) + (ensure-up-to-date-instance self) + (safe-instance-ref self index)) + #'(lambda (value self) + (declare (optimize (safety 0) (speed 3) (debug 0)) + (standard-object self)) + (ensure-up-to-date-instance self) + (si:instance-set self index value)))) + +(defun std-class-accessors (slot-name) + (declare (si::c-local)) + ;; The following are very slow. We do not optimize for the slot position. + (values #'(lambda (self) + (slot-value self slot-name)) + #'(lambda (value self) + (setf (slot-value self slot-name) value)))) + +(defun safe-add-method (name method) + ;; Adds a method to a function which might have been previously defined + ;; as non-generic, without breaking the function + (cond ((or *clos-booted* + (not (fboundp name)) + (si::instancep (fdefinition name))) + (add-method (ensure-generic-function name) method)) + (t + (let* ((alt-name '#:foo) + (gf (ensure-generic-function alt-name))) + (add-method gf method) + (setf (generic-function-name gf) name) + (setf (fdefinition name) gf) + (fmakunbound alt-name))))) + +(defun std-class-generate-accessors (standard-class &aux optimizable) + ;; + ;; The accessors are closures, which are generated every time the + ;; slots of the class change. The accessors are safe: they check that + ;; the slot is bound after retreiving the value, and they may take + ;; the liberty of using SI:INSTANCE-REF because they know the class of + ;; the instance. + ;; + (dolist (slotd (class-direct-slots standard-class)) + (multiple-value-bind (reader writer) + (let ((name (slot-definition-name slotd)) + (allocation (slot-definition-allocation slotd)) + (location (safe-slot-definition-location slotd))) + (cond ((and (eq allocation :instance) (typep location 'fixnum)) + (std-class-sealed-accessors (slot-definition-location slotd))) + ;; When a class is the specified STANDARD-CLASS, then the + ;; user may not write any method around SLOT-VALUE-USING-CLASS + ;; This allows us to write optimized versions of the accessors. + ((and (eq allocation :instance) + (eq standard-class +the-standard-class+)) + (std-class-optimized-accessors name)) + (t + (std-class-accessors name)))) + (let* ((reader-args (list :function reader + :generic-function nil + :qualifiers nil + :lambda-list '(object) + :specializers `(,standard-class) + :slot-definition slotd)) + (reader-class (if (boundp '*early-methods*) + 'standard-reader-method + (apply #'reader-method-class standard-class slotd + reader-args))) + (writer-args (list :function writer + :generic-function nil + :qualifiers nil + :lambda-list '(value object) + :specializers `(,(find-class t) ,standard-class) + :slot-definition slotd)) + (writer-class (if (boundp '*early-methods*) + 'standard-writer-method + (apply #'writer-method-class standard-class slotd + writer-args)))) + (dolist (fname (slot-definition-readers slotd)) + (safe-add-method fname + (make-method reader-class nil `(,standard-class) '(self) + (wrapped-method-function reader) + (list :slot-definition slotd)))) + (dolist (fname (slot-definition-writers slotd)) + (safe-add-method fname + (make-method writer-class nil + `(,(find-class t) ,standard-class) '(value self) + (wrapped-method-function writer) + (list :slot-definition slotd)))))))) + +(defun reader-closure (index) + (declare (si::c-local)) + (lambda (object) (si::instance-ref object index))) + +(defun writer-closure (index) + (declare (si::c-local)) + (lambda (value object) (si::instance-set object index value))) + +(labels ((generate-accessors (class) + (declare (optimize speed (safety 0))) + (loop for index from 0 + for slotd in (slot-value class 'slots) + do (loop for reader in (slot-definition-readers slotd) + do (setf (fdefinition reader) (reader-closure index)) + #+(or) + (install-method reader nil (list class) '(self) + (reader-closure index) t)) + do (loop for writer in (slot-definition-writers slotd) + do (setf (fdefinition writer) (writer-closure index)) + #+(or) + (install-method writer nil (list +the-t-class+ class) + '(value self) + (writer-closure index) t))) + (mapc #'generate-accessors (slot-value class 'direct-subclasses)))) + (generate-accessors +the-t-class+)) +