PageRenderTime 35ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/ctor.impure.lisp

http://github.com/akovalenko/sbcl-win32-threads
Lisp | 305 lines | 240 code | 45 blank | 20 comment | 0 complexity | a3db6697a85ecc270f5a9f7da5a4b475 MD5 | raw file
  1. ;;;; gray-box testing of the constructor optimization machinery
  2. ;;;; This software is part of the SBCL system. See the README file for
  3. ;;;; more information.
  4. ;;;;
  5. ;;;; While most of SBCL is derived from the CMU CL system, the test
  6. ;;;; files (like this one) were written from scratch after the fork
  7. ;;;; from CMU CL.
  8. ;;;;
  9. ;;;; This software is in the public domain and is provided with
  10. ;;;; absolutely no warranty. See the COPYING and CREDITS files for
  11. ;;;; more information.
  12. (load "test-util.lisp")
  13. (load "compiler-test-util.lisp")
  14. (defpackage "CTOR-TEST"
  15. (:use "CL" "TEST-UTIL" "COMPILER-TEST-UTIL"))
  16. (in-package "CTOR-TEST")
  17. (defclass no-slots () ())
  18. (defun make-no-slots ()
  19. (make-instance 'no-slots))
  20. (compile 'make-no-slots)
  21. (defmethod update-instance-for-redefined-class
  22. ((object no-slots) added discarded plist &rest initargs)
  23. (declare (ignore initargs))
  24. (error "Called U-I-F-R-C on ~A" object))
  25. (assert (typep (make-no-slots) 'no-slots))
  26. (make-instances-obsolete 'no-slots)
  27. (assert (typep (make-no-slots) 'no-slots))
  28. (assert (typep (funcall #'(sb-pcl::ctor no-slots nil)) 'no-slots))
  29. (defclass one-slot ()
  30. ((a :initarg :a)))
  31. (defun make-one-slot-a (a)
  32. (make-instance 'one-slot :a a))
  33. (compile 'make-one-slot-a)
  34. (defun make-one-slot-noa ()
  35. (make-instance 'one-slot))
  36. (compile 'make-one-slot-noa)
  37. (defmethod update-instance-for-redefined-class
  38. ((object one-slot) added discarded plist &rest initargs)
  39. (declare (ignore initargs))
  40. (error "Called U-I-F-R-C on ~A" object))
  41. (assert (= (slot-value (make-one-slot-a 3) 'a) 3))
  42. (assert (not (slot-boundp (make-one-slot-noa) 'a)))
  43. (make-instances-obsolete 'one-slot)
  44. (assert (= (slot-value (make-one-slot-a 3) 'a) 3))
  45. (assert (= (slot-value (funcall #'(sb-pcl::ctor one-slot nil :a sb-pcl::\.p0.) 4) 'a) 4))
  46. (assert (not (slot-boundp (make-one-slot-noa) 'a)))
  47. (assert (not (slot-boundp (funcall #'(sb-pcl::ctor one-slot nil)) 'a)))
  48. (defclass one-slot-superclass ()
  49. ((b :initarg :b)))
  50. (defclass one-slot-subclass (one-slot-superclass)
  51. ())
  52. (defun make-one-slot-subclass (b)
  53. (make-instance 'one-slot-subclass :b b))
  54. (compile 'make-one-slot-subclass)
  55. (defmethod update-instance-for-redefined-class
  56. ((object one-slot-superclass) added discarded plist &rest initargs)
  57. (declare (ignore initargs))
  58. (error "Called U-I-F-R-C on ~A" object))
  59. (assert (= (slot-value (make-one-slot-subclass 2) 'b) 2))
  60. (make-instances-obsolete 'one-slot-subclass)
  61. (assert (= (slot-value (make-one-slot-subclass 2) 'b) 2))
  62. (assert (= (slot-value (funcall #'(sb-pcl::ctor one-slot-subclass nil :b sb-pcl::\.p0.) 3) 'b) 3))
  63. (make-instances-obsolete 'one-slot-superclass)
  64. (assert (= (slot-value (make-one-slot-subclass 2) 'b) 2))
  65. (assert (= (slot-value (funcall #'(sb-pcl::ctor one-slot-subclass nil :b sb-pcl::\.p0.) 4) 'b) 4))
  66. ;;; Tests for CTOR optimization of non-constant class args and constant class object args
  67. (defun find-ctor-caches (fun)
  68. (remove-if-not (lambda (value)
  69. (and (consp value) (eq 'sb-pcl::ctor-cache (car value))))
  70. (find-value-cell-values fun)))
  71. (let* ((cmacro (compiler-macro-function 'make-instance))
  72. (opt 0)
  73. (wrapper (lambda (form env)
  74. (let ((res (funcall cmacro form env)))
  75. (unless (eq form res)
  76. (incf opt))
  77. res))))
  78. (sb-ext:without-package-locks
  79. (unwind-protect
  80. (progn
  81. (setf (compiler-macro-function 'make-instance) wrapper)
  82. (with-test (:name (make-instance :non-constant-class))
  83. (assert (= 0 opt))
  84. (let ((f (compile nil `(lambda (class)
  85. (make-instance class :b t)))))
  86. (assert (= 1 (length (find-ctor-caches f))))
  87. (assert (= 1 opt))
  88. (assert (typep (funcall f 'one-slot-subclass) 'one-slot-subclass))))
  89. (with-test (:name (make-instance :constant-class-object))
  90. (let ((f (compile nil `(lambda ()
  91. (make-instance ,(find-class 'one-slot-subclass) :b t)))))
  92. (assert (not (find-ctor-caches f)))
  93. (assert (= 2 opt))
  94. (assert (typep (funcall f) 'one-slot-subclass))))
  95. (with-test (:name (make-instance :constant-non-std-class-object))
  96. (let ((f (compile nil `(lambda ()
  97. (make-instance ,(find-class 'structure-object))))))
  98. (assert (not (find-ctor-caches f)))
  99. (assert (= 3 opt))
  100. (assert (typep (funcall f) 'structure-object))))
  101. (with-test (:name (make-instance :constant-non-std-class-name))
  102. (let ((f (compile nil `(lambda ()
  103. (make-instance 'structure-object)))))
  104. (assert (not (find-ctor-caches f)))
  105. (assert (= 4 opt))
  106. (assert (typep (funcall f) 'structure-object)))))
  107. (setf (compiler-macro-function 'make-instance) cmacro))))
  108. (with-test (:name (make-instance :ctor-inline-cache-resize))
  109. (let* ((f (compile nil `(lambda (name) (make-instance name))))
  110. (classes (loop repeat (* 2 sb-pcl::+ctor-table-max-size+)
  111. collect (class-name (eval `(defclass ,(gentemp) () ())))))
  112. (count 0)
  113. (caches (find-ctor-caches f))
  114. (cache (pop caches)))
  115. (assert cache)
  116. (assert (not caches))
  117. (assert (not (cdr cache)))
  118. (dolist (class classes)
  119. (assert (typep (funcall f (if (oddp count) class (find-class class))) class))
  120. (incf count)
  121. (cond ((<= count sb-pcl::+ctor-list-max-size+)
  122. (unless (consp (cdr cache))
  123. (error "oops, wanted list cache, got: ~S" cache))
  124. (unless (= count (length (cdr cache)))
  125. (error "oops, wanted ~S elts in cache, got: ~S" count cache)))
  126. (t
  127. (assert (simple-vector-p (cdr cache))))))
  128. (dolist (class classes)
  129. (assert (typep (funcall f (if (oddp count) class (find-class class))) class))
  130. (incf count))))
  131. ;;; Make sure we get default initargs right with on the FAST-MAKE-INSTANCE path CTORs
  132. (defclass some-class ()
  133. ((aroundp :initform nil :reader aroundp))
  134. (:default-initargs :x :success1))
  135. (defmethod shared-initialize :around ((some-class some-class) slots &key (x :fail?))
  136. (unless (eq x :success1)
  137. (error "Default initarg lossage"))
  138. (setf (slot-value some-class 'aroundp) t)
  139. (when (next-method-p)
  140. (call-next-method)))
  141. (with-test (:name (make-instance :ctor-default-initargs-1))
  142. (assert (aroundp (eval `(make-instance 'some-class))))
  143. (let ((fun (compile nil `(lambda () (make-instance 'some-class)))))
  144. (assert (aroundp (funcall fun)))
  145. ;; make sure we tested what we think we tested...
  146. (let ((ctors (find-named-callees fun :type 'sb-pcl::ctor)))
  147. (assert ctors)
  148. (assert (not (cdr ctors)))
  149. (assert (find-named-callees (car ctors) :name 'sb-pcl::fast-make-instance)))))
  150. ;;; Make sure we get default initargs right with on the FAST-MAKE-INSTANCE path CTORs
  151. ;;; in more interesting cases as well...
  152. (defparameter *some-counter* 0)
  153. (let* ((x 'success2))
  154. (defclass some-class2 ()
  155. ((aroundp :initform nil :reader aroundp))
  156. (:default-initargs :x (progn (incf *some-counter*) x))))
  157. (defmethod shared-initialize :around ((some-class some-class2) slots &key (x :fail2?))
  158. (unless (eq x 'success2)
  159. (error "Default initarg lossage"))
  160. (setf (slot-value some-class 'aroundp) t)
  161. (when (next-method-p)
  162. (call-next-method)))
  163. (with-test (:name (make-instance :ctor-default-initargs-2))
  164. (assert (= 0 *some-counter*))
  165. (assert (aroundp (eval `(make-instance 'some-class2))))
  166. (assert (= 1 *some-counter*))
  167. (let ((fun (compile nil `(lambda () (make-instance 'some-class2)))))
  168. (assert (= 1 *some-counter*))
  169. (assert (aroundp (funcall fun)))
  170. (assert (= 2 *some-counter*))
  171. ;; make sure we tested what we think we tested...
  172. (let ((ctors (find-named-callees fun :type 'sb-pcl::ctor)))
  173. (assert ctors)
  174. (assert (not (cdr ctors)))
  175. (assert (find-named-callees (car ctors) :name 'sb-pcl::fast-make-instance)))))
  176. ;;; No compiler notes, please
  177. (locally (declare (optimize safety))
  178. (defclass type-check-thing ()
  179. ((slot :type (integer 0) :initarg :slot))))
  180. (with-test (:name (make-instance :no-compile-note-at-runtime))
  181. (let ((fun (compile nil `(lambda (x)
  182. (declare (optimize safety))
  183. (make-instance 'type-check-thing :slot x)))))
  184. (handler-bind ((sb-ext:compiler-note #'error))
  185. (funcall fun 41)
  186. (funcall fun 13))))
  187. ;;; NO-APPLICABLE-METHOD called
  188. (defmethod no-applicable-method ((gf (eql #'make-instance)) &rest args)
  189. (cons :no-applicable-method args))
  190. (with-test (:name :constant-invalid-class-arg)
  191. (assert (equal
  192. '(:no-applicable-method "FOO" :quux 14)
  193. (funcall (compile nil `(lambda (x) (make-instance "FOO" :quux x))) 14)))
  194. (assert (equal
  195. '(:no-applicable-method 'abc zot 1 bar 2)
  196. (funcall (compile nil `(lambda (x y) (make-instance ''abc 'zot x 'bar y)))
  197. 1 2))))
  198. (with-test (:name :variable-invalid-class-arg)
  199. (assert (equal
  200. '(:no-applicable-method "FOO" :quux 14)
  201. (funcall (compile nil `(lambda (c x) (make-instance c :quux x))) "FOO" 14)))
  202. (assert (equal
  203. '(:no-applicable-method 'abc zot 1 bar 2)
  204. (funcall (compile nil `(lambda (c x y) (make-instance c 'zot x 'bar y)))
  205. ''abc 1 2))))
  206. (defclass sneaky-class (standard-class)
  207. ())
  208. (defmethod sb-mop:validate-superclass ((class sneaky-class) (super standard-class))
  209. t)
  210. (defclass sneaky ()
  211. ((dirty :initform nil :accessor dirty-slots)
  212. (a :initarg :a :reader sneaky-a)
  213. (b :initform "b" :reader sneaky-b)
  214. (c :accessor sneaky-c))
  215. (:metaclass sneaky-class))
  216. (defvar *supervising* nil)
  217. (defmethod (setf sb-mop:slot-value-using-class)
  218. :before (value (class sneaky-class) (instance sneaky) slotd)
  219. (unless *supervising*
  220. (let ((name (sb-mop:slot-definition-name slotd))
  221. (*supervising* t))
  222. (when (slot-boundp instance 'dirty)
  223. (pushnew name (dirty-slots instance))))))
  224. (with-test (:name (make-instance :setf-slot-value-using-class-hits-other-slots))
  225. (let ((fun (compile nil `(lambda (a c)
  226. (let ((i (make-instance 'sneaky :a a)))
  227. (setf (sneaky-c i) c)
  228. i)))))
  229. (loop repeat 3
  230. do (let ((i (funcall fun "a" "c")))
  231. (assert (equal '(c b a) (dirty-slots i)))
  232. (assert (equal "a" (sneaky-a i)))
  233. (assert (equal "b" (sneaky-b i)))
  234. (assert (equal "c" (sneaky-c i)))))))
  235. (defclass bug-728650-base ()
  236. ((value
  237. :initarg :value
  238. :initform nil)))
  239. (defmethod initialize-instance :after ((instance bug-728650-base) &key)
  240. (with-slots (value) instance
  241. (unless value
  242. (error "Impossible! Value slot not initialized in ~S" instance))))
  243. (defclass bug-728650-child-1 (bug-728650-base)
  244. ())
  245. (defmethod initialize-instance :around ((instance bug-728650-child-1) &rest initargs &key)
  246. (apply #'call-next-method instance :value 'provided-by-child-1 initargs))
  247. (defclass bug-728650-child-2 (bug-728650-base)
  248. ())
  249. (defmethod initialize-instance :around ((instance bug-728650-child-2) &rest initargs &key)
  250. (let ((foo (make-instance 'bug-728650-child-1)))
  251. (apply #'call-next-method instance :value foo initargs)))
  252. (with-test (:name :bug-728650)
  253. (let ((child1 (slot-value (make-instance 'bug-728650-child-2) 'value)))
  254. (assert (typep child1 'bug-728650-child-1))
  255. (assert (eq 'provided-by-child-1 (slot-value child1 'value)))))
  256. ;;;; success