mirror of
https://gitlab.com/embeddable-common-lisp/ecl.git
synced 2026-01-15 22:01:36 -08:00
449 lines
16 KiB
Common Lisp
449 lines
16 KiB
Common Lisp
;;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: CLOS -*-
|
||
;;;;
|
||
;;;; Copyright (c) 1992, Giuseppe Attardi.
|
||
;;;;
|
||
;;;; 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.
|
||
|
||
(defpackage "CLOS"
|
||
(:use "CL" "EXT")
|
||
(:import-from "SI" "UNBOUND" "GET-SYSPROP" "PUT-SYSPROP" "REM-SYSPROP"
|
||
"SIMPLE-PROGRAM-ERROR"))
|
||
|
||
(in-package "CLOS")
|
||
|
||
(defparameter *clos-booted* nil)
|
||
|
||
(defconstant *default-method-cache-size* 64 "Size of hash tables for methods")
|
||
|
||
;;;----------------------------------------------------------------------
|
||
;;; BOOTSTRAP FUNCTIONS TO ACCESS SLOTS
|
||
;;;
|
||
;;; ECL has some restictions regarding the basic classes CLASS,
|
||
;;; STANDARD-CLASS and STANDARD-GENERIC-FUNCTION. These are that, certain
|
||
;;; slots must have pre-defined positions which cannot change. That means
|
||
;;; that a user can extend these classes, but they must be the first ones
|
||
;;; in the class hierarchy, and the position of their slots must not change.
|
||
|
||
(eval-when (compile eval)
|
||
(defun create-accessors (slotds type)
|
||
(let ((i 0)
|
||
(output '())
|
||
(names '())
|
||
name)
|
||
(dolist (s slotds)
|
||
(when (setf name (getf (cdr s) :accessor))
|
||
(push name names)
|
||
(setf output
|
||
(append output
|
||
`((defun ,name (obj)
|
||
(si:instance-ref obj ,i))
|
||
(defsetf ,name (obj) (x)
|
||
`(si:instance-set ,obj ,,i ,x))
|
||
#+nil
|
||
(define-compiler-macro ,name (obj)
|
||
`(si:instance-ref ,obj ,,i))
|
||
))))
|
||
(incf i))
|
||
`(progn
|
||
#+nil
|
||
(eval-when (:compile-toplevel :execute)
|
||
(proclaim '(notinline ,@names)))
|
||
,@output)))
|
||
(defun remove-accessors (slotds)
|
||
(loop for i in slotds
|
||
for j = (copy-list i)
|
||
do (remf (cdr j) :accessor)
|
||
collect j))
|
||
)
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; Class CLASS
|
||
|
||
(eval-when (compile eval)
|
||
(defparameter +class-slots+
|
||
'((name :initarg :name :initform nil :accessor class-id)
|
||
(direct-superclasses :initarg :direct-superclasses
|
||
:accessor class-direct-superclasses)
|
||
(direct-subclasses :initform nil :accessor class-direct-subclasses)
|
||
(slots :accessor class-slots)
|
||
(precedence-list :accessor class-precedence-list)
|
||
(direct-slots :initarg :direct-slots :accessor class-direct-slots)
|
||
(direct-default-initargs :initarg :direct-default-initargs
|
||
:initform nil :accessor class-direct-default-initargs)
|
||
(default-initargs :accessor class-default-initargs)
|
||
(finalized :initform nil :accessor class-finalized-p)
|
||
(documentation :initarg :documentation :initform nil)
|
||
(size :accessor class-size)
|
||
(sealedp :initarg :sealedp :initform nil :accessor class-sealedp)
|
||
(prototype)
|
||
(dependents :initform nil :accessor class-dependents)
|
||
(valid-initargs :accessor class-valid-initargs))))
|
||
|
||
;#.(create-accessors +class-slots+ 'class)
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; STANDARD-CLASS
|
||
|
||
(eval-when (compile eval)
|
||
(defparameter +standard-class-slots+
|
||
(append +class-slots+
|
||
'((slot-table :accessor slot-table)
|
||
(optimize-slot-access)
|
||
(forward)))))
|
||
|
||
#.(create-accessors +standard-class-slots+ 'standard-class)
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; STANDARD-GENERIC-FUNCTION
|
||
|
||
(eval-when (compile eval)
|
||
(defparameter +standard-generic-function-slots+
|
||
'((name :initarg :name :initform nil
|
||
:accessor generic-function-name)
|
||
(spec-list :initform nil :accessor generic-function-spec-list)
|
||
(method-combination
|
||
:initarg :method-combination :initform '(standard)
|
||
:accessor generic-function-method-combination)
|
||
(lambda-list :initarg :lambda-list
|
||
:accessor generic-function-lambda-list)
|
||
(argument-precedence-order
|
||
:initarg :argument-precedence-order
|
||
:initform nil
|
||
:accessor generic-function-argument-precedence-order)
|
||
(method-class
|
||
:initarg :method-class
|
||
:initform (find-class 'standard-method)
|
||
:accessor generic-function-method-class)
|
||
(documentation :initarg :documentation :initform nil)
|
||
(methods :initform nil :accessor generic-function-methods)
|
||
(a-p-o-function :initform nil :accessor generic-function-a-p-o-function)
|
||
(dependents :initform nil :accessor generic-function-dependents))))
|
||
|
||
#.(create-accessors +standard-generic-function-slots+
|
||
'standard-generic-function)
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; STANDARD-METHOD
|
||
|
||
(eval-when (compile eval)
|
||
(defparameter +standard-method-slots+
|
||
'((generic-function :initarg :generic-function :initform nil
|
||
:accessor method-generic-function)
|
||
(lambda-list :initarg :lambda-list
|
||
:accessor method-lambda-list)
|
||
(specializers :initarg :specializers :accessor method-specializers)
|
||
(qualifiers :initform nil :initarg :qualifiers :accessor method-qualifiers)
|
||
(function :initarg :function :accessor method-function)
|
||
(documentation :initform nil :initarg documentation)
|
||
(plist :initform nil :initarg :plist :accessor method-plist)
|
||
(keywords :initform nil :accessor method-keywords))))
|
||
|
||
#.(create-accessors +standard-method-slots+ 'standard-method)
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;;
|
||
;;; FIND-CLASS naming classes.
|
||
;;;
|
||
;;;
|
||
;;; (FIND-CLASS <name>) returns the class named <name>. setf can be used
|
||
;;; with find-class to set the class named <name>. These are "extrinsic"
|
||
;;; names. Neither find-class nor setf of find-class do anything with the
|
||
;;; name slot of the class, they only lookup and change the association from
|
||
;;; name to class.
|
||
;;;
|
||
;;; This is only used during boot. The real one is in built-in.
|
||
(eval-when (compile)
|
||
(defun setf-find-class (new-value class &optional errorp env)
|
||
(warn "Ignoring class definition for ~S" class)))
|
||
|
||
(defun setf-find-class (new-value name &optional errorp env)
|
||
(declare (ignore errorp env))
|
||
(let ((old-class (find-class name nil)))
|
||
(cond
|
||
((and old-class
|
||
(or (typep old-class 'built-in-class)
|
||
(member name '(class built-in-class) :test #'eq)))
|
||
(error "The class associated to the CL specifier ~S cannot be changed."
|
||
name))
|
||
((classp new-value)
|
||
(setf (gethash name si:*class-name-hash-table*) new-value))
|
||
((null new-value) (remhash name si:*class-name-hash-table*))
|
||
(t (error "~A is not a class." new-value))))
|
||
new-value)
|
||
|
||
(defsetf find-class (&rest x) (v) `(setf-find-class ,v ,@x))
|
||
|
||
(defun classp (obj)
|
||
(and (si:instancep obj)
|
||
(let ((topmost (find-class 'CLASS nil)))
|
||
;; All instances can be classes until the class CLASS has
|
||
;; been installed. Otherwise, we check the parents.
|
||
(or (null topmost)
|
||
(si::subclassp (si::instance-class obj) topmost)))
|
||
t))
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; Methods
|
||
|
||
(defun install-method (name qualifiers specializers lambda-list doc plist fun
|
||
&optional method-class &rest options)
|
||
(declare (ignore doc)
|
||
(notinline ensure-generic-function))
|
||
; (record-definition 'method `(method ,name ,@qualifiers ,specializers))
|
||
(let* ((gf (ensure-generic-function name))
|
||
(specializers (mapcar #'(lambda (x)
|
||
(cond ((null x) x)
|
||
((consp x) x)
|
||
((si::instancep x) x)
|
||
(t (find-class x))))
|
||
specializers))
|
||
(method (make-method (or method-class
|
||
(generic-function-method-class gf))
|
||
qualifiers specializers lambda-list
|
||
fun plist options)))
|
||
(add-method gf method)
|
||
method))
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; early versions
|
||
|
||
;;; early version used during bootstrap
|
||
(defun ensure-generic-function (name &key (lambda-list (si::unbound) l-l-p))
|
||
(if (and (fboundp name) (si::instancep (fdefinition name)))
|
||
(fdefinition name)
|
||
;; create a fake standard-generic-function object:
|
||
(let ((gfun (si:allocate-raw-instance nil (find-class 't)
|
||
#.(length +standard-generic-function-slots+))))
|
||
(declare (type standard-object gfun))
|
||
;; create a new gfun
|
||
(si::instance-sig-set gfun)
|
||
(setf (generic-function-name gfun) name
|
||
(generic-function-lambda-list gfun) lambda-list
|
||
(generic-function-method-combination gfun) '(standard)
|
||
(generic-function-methods gfun) nil
|
||
(generic-function-spec-list gfun) nil
|
||
(generic-function-method-class gfun) 'standard-method
|
||
(generic-function-dependents gfun) nil)
|
||
(when l-l-p
|
||
(setf (generic-function-argument-precedence-order gfun)
|
||
(rest (si::process-lambda-list lambda-list t))))
|
||
(set-funcallable-instance-function gfun t)
|
||
(setf (fdefinition name) gfun)
|
||
gfun)))
|
||
|
||
(defun set-generic-function-dispatch (gfun)
|
||
(flet ((gf-type (gfun)
|
||
(loop with common-class = nil
|
||
for method in (generic-function-methods gfun)
|
||
for class = (si::instance-class method)
|
||
for specializers = (method-specializers method)
|
||
do (cond ((null common-class)
|
||
(setf common-class class))
|
||
((not (eq common-class class))
|
||
(return t)))
|
||
do (loop for spec in specializers
|
||
unless (or (eq spec t)
|
||
(null spec)
|
||
(eq spec +the-t-class+)
|
||
(and (si::instancep spec)
|
||
(eq (si::instance-class spec)
|
||
+the-standard-class+)))
|
||
do (return-from gf-type t))
|
||
finally (cond ((null class)
|
||
(return t))
|
||
((eq class (find-class 'standard-reader-method nil))
|
||
(return 'standard-reader-method))
|
||
((eq class (find-class 'standard-writer-method nil))
|
||
(return 'standard-writer-method))
|
||
(t
|
||
(return t))))))
|
||
(set-funcallable-instance-function gfun (gf-type gfun))))
|
||
|
||
|
||
|
||
;;; ----------------------------------------------------------------------
|
||
;;; COMPUTE-APPLICABLE-METHODS
|
||
;;;
|
||
;;; FIXME! This should be split int an internal function, like
|
||
;;; raw-compute-... and a higher level interface, because the current
|
||
;;; version does not check _any_ of the arguments but it is
|
||
;;; nevertheless exported by the ANSI specification!
|
||
;;;
|
||
(defun std-compute-applicable-methods (gf args)
|
||
(declare (optimize (safety 0) (speed 3)))
|
||
(sort-applicable-methods gf (applicable-method-list gf args) args))
|
||
|
||
(setf (fdefinition 'compute-applicable-methods) #'std-compute-applicable-methods)
|
||
|
||
(defun applicable-method-list (gf args)
|
||
(declare (optimize (safety 0) (speed 3))
|
||
(si::c-local))
|
||
(flet ((applicable-method-p (method args)
|
||
(loop for spec in (method-specializers method)
|
||
for arg in args
|
||
always (cond ((null spec) t)
|
||
((listp spec)
|
||
;; EQL specializer
|
||
(eql arg (second spec)))
|
||
((si::of-class-p arg spec))))))
|
||
(loop for method in (generic-function-methods gf)
|
||
when (applicable-method-p method args)
|
||
collect method)))
|
||
|
||
(defun std-compute-applicable-methods-using-classes (gf args)
|
||
(declare (optimize (safety 0) (speed 3)))
|
||
(sort-applicable-methods gf (applicable-method-list gf args) args))
|
||
|
||
(defun applicable-method-list-with-classes (gf classes)
|
||
(declare (optimize (safety 0) (speed 3))
|
||
(si::c-local))
|
||
(flet ((applicable-method-p (method classes)
|
||
(loop for spec in (method-specializers method)
|
||
for class in classes
|
||
always (cond ((null spec))
|
||
((listp spec)
|
||
;; EQL specializer
|
||
(si::of-class-p (second spec) class))
|
||
((si::subclassp class spec))))))
|
||
(loop for method in (generic-function-methods gf)
|
||
when (applicable-method-p method classes)
|
||
collect method)))
|
||
|
||
(defun sort-applicable-methods (gf applicable-list args)
|
||
(declare (optimize (safety 0) (speed 3)))
|
||
(let ((f (generic-function-a-p-o-function gf))
|
||
(args-specializers (mapcar #'class-of args)))
|
||
;; reorder args to match the precedence order
|
||
(when f
|
||
(setf args-specializers
|
||
(funcall f (subseq args-specializers 0
|
||
(length (generic-function-argument-precedence-order gf))))))
|
||
;; then order the list
|
||
(do* ((scan applicable-list)
|
||
(most-specific (first scan) (first scan))
|
||
(ordered-list))
|
||
((null (cdr scan))
|
||
(when most-specific
|
||
;; at least one method
|
||
(nreverse
|
||
(push most-specific ordered-list))))
|
||
(dolist (meth (cdr scan))
|
||
(when (eq (compare-methods most-specific
|
||
meth args-specializers f) 2)
|
||
(setq most-specific meth)))
|
||
(setq scan (delete most-specific scan))
|
||
(push most-specific ordered-list))))
|
||
|
||
(defun compare-methods (method-1 method-2 args-specializers f)
|
||
(declare (si::c-local))
|
||
(let* ((specializers-list-1 (method-specializers method-1))
|
||
(specializers-list-2 (method-specializers method-2)))
|
||
(compare-specializers-lists (if f (funcall f specializers-list-1) specializers-list-1)
|
||
(if f (funcall f specializers-list-2) specializers-list-2)
|
||
args-specializers)))
|
||
|
||
(defun compare-specializers-lists (spec-list-1 spec-list-2 args-specializers)
|
||
(declare (si::c-local))
|
||
(when (or spec-list-1 spec-list-2)
|
||
(ecase (compare-specializers (first spec-list-1)
|
||
(first spec-list-2)
|
||
(first args-specializers))
|
||
(1 '1)
|
||
(2 '2)
|
||
(=
|
||
(compare-specializers-lists (cdr spec-list-1)
|
||
(cdr spec-list-2)
|
||
(cdr args-specializers)))
|
||
((nil)
|
||
(error "The type specifiers ~S and ~S can not be disambiguated~
|
||
with respect to the argument specializer: ~S"
|
||
(or (car spec-list-1) t)
|
||
(or (car spec-list-2) t)
|
||
(car args-specializers)))))
|
||
)
|
||
|
||
(defun fast-subtypep (spec1 spec2)
|
||
(declare (si::c-local))
|
||
;; Specialized version of subtypep which uses the fact that spec1
|
||
;; and spec2 are either classes or of the form (EQL x)
|
||
(if (atom spec1)
|
||
(if (atom spec2)
|
||
(si::subclassp spec1 spec2)
|
||
;; There is only one class with a single element, which
|
||
;; is NULL = (MEMBER NIL).
|
||
(and (null (second spec2))
|
||
(eq (class-name spec1) 'null)))
|
||
(if (atom spec2)
|
||
(si::of-class-p (second spec1) spec2)
|
||
(eql (second spec1) (second spec2)))))
|
||
|
||
(defun compare-specializers (spec-1 spec-2 arg-class)
|
||
(declare (si::c-local))
|
||
(let* ((cpl (class-precedence-list arg-class)))
|
||
(cond ((equal spec-1 spec-2) '=)
|
||
((null spec-1) '2)
|
||
((null spec-2) '1)
|
||
((fast-subtypep spec-1 spec-2) '1)
|
||
((fast-subtypep spec-2 spec-1) '2)
|
||
((and (listp spec-1) (eq (car spec-1) 'eql)) '1) ; is this engough?
|
||
((and (listp spec-2) (eq (car spec-2) 'eql)) '2) ; Beppe
|
||
((member spec-1 (member spec-2 cpl)) '2)
|
||
((member spec-2 (member spec-1 cpl)) '1)
|
||
;; This will force an error in the caller
|
||
(t nil))))
|
||
|
||
(defun compute-g-f-spec-list (gf)
|
||
(flet ((nupdate-spec-how-list (spec-how-list specializers gf)
|
||
;; FIXME! This check should have happened before, shouldn't it???
|
||
(let ((l (length specializers)))
|
||
(if spec-how-list
|
||
(unless (= (length spec-how-list) l)
|
||
(error "The generic function ~A~%has ~D required arguments, but the new specialization provides ~D."
|
||
gf (length spec-how-list) l))
|
||
(setf spec-how-list (make-list l))))
|
||
;; update the spec-how of the gfun
|
||
;; computing the or of the previous value and the new one
|
||
(do* ((l specializers (cdr l))
|
||
(l2 spec-how-list (cdr l2))
|
||
(spec-how)
|
||
(spec-how-old))
|
||
((null l))
|
||
(setq spec-how (first l) spec-how-old (first l2))
|
||
(setf (first l2)
|
||
(if (consp spec-how) ; an eql list
|
||
(if (consp spec-how-old)
|
||
(list* (second spec-how) spec-how-old)
|
||
(cdr spec-how))
|
||
(if (consp spec-how-old)
|
||
spec-how-old
|
||
(or spec-how spec-how-old)))))
|
||
spec-how-list))
|
||
(let* ((spec-how-list nil)
|
||
(function nil)
|
||
(a-p-o (generic-function-argument-precedence-order gf)))
|
||
(dolist (method (generic-function-methods gf))
|
||
(setf spec-how-list
|
||
(nupdate-spec-how-list spec-how-list (method-specializers method) gf)))
|
||
(setf (generic-function-spec-list gf)
|
||
(loop for type in spec-how-list
|
||
for i from 0
|
||
when type collect (cons type i)))
|
||
(let* ((g-f-l-l (generic-function-lambda-list gf)))
|
||
(when (consp g-f-l-l)
|
||
(let ((required-arguments (rest (si::process-lambda-list g-f-l-l t))))
|
||
(unless (equal a-p-o required-arguments)
|
||
(setf function
|
||
(coerce `(lambda (%list)
|
||
(destructuring-bind ,required-arguments %list
|
||
(list ,@a-p-o)))
|
||
'function))))))
|
||
(setf (generic-function-a-p-o-function gf) function)
|
||
(si:clear-gfun-hash gf))))
|
||
|
||
(defun print-object (object stream)
|
||
(print-unreadable-object (object stream)))
|