;;;
;;; frame-return-test.lisp
;;;
;;; Test for SBCL debugger frame-return machinery
;;;

(in-package :cl-user)

(defun foo1 (fun)
  (funcall fun)
  (format t "foo1~%"))

(defun find-enclosing-uwp (frame)
  ;; Walk the UWP chain looking for the first entry with an address
  ;; higher than the pointer for FRAME or a null pointer.

  ;; FIXME: Make this work on non-x86 systems.

  (let* ((frame-pointer (sb-di::frame-pointer frame))
	 (current-uwp (sb-sys:int-sap
			(ash (logand (logior (1+ most-positive-fixnum)
					     most-positive-fixnum)
				     sb-vm::*current-unwind-protect-block*)
			     2)))
	 (enclosing-uwp (loop for uwp-block = current-uwp
			          then (sb-sys:sap-ref-sap
					uwp-block sb-vm:unwind-block-current-uwp-slot)
			      when (or (zerop (sb-sys:sap-int uwp-block))
				       (sb-sys:sap> uwp-block frame-pointer))
			      return uwp-block)))
    (format t "find-enclosing-uwp: fp ~X current-uwp ~X enclosing-uwp ~X~%"
	    (sb-sys:sap-int frame-pointer)
	    (sb-sys:sap-int current-uwp)
	    (sb-sys:sap-int enclosing-uwp))
    enclosing-uwp))

;; FIXME: May want to turn this into an ir1-translator.
(defmacro frame-return (frame values)
  `(multiple-value-call #'sb-c::%frame-return (sb-di::frame-pointer ,frame)
    (find-enclosing-uwp ,frame) ,values))

(defun bar ()
  (format t "bar~%")
  (loop for frame = (sb-di:top-frame) #+nil (sb-kernel:%caller-frame-and-pc)
	    then (sb-di:frame-down frame)
	while frame
	when (eq (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame)) #'foo1)
	do (progn
	     (format t "bar frame ~A fun ~A~%" frame
		     (sb-di:debug-fun-fun (sb-di:frame-debug-fun frame)))
	     (frame-return frame (values 1 'two "three"))))
  (format t "bar returning normally~%"))

(defun foo2 ()
  (unwind-protect
       (bar)
    (format t "foo2 UWP~%"))
  (format t "foo2 post-UWP~%"))

(defun test-return ()
  (unwind-protect
       (prog1
	 (foo1 #'foo2)
	 (format t "test-return: foo1 returned~%"))
    (format t "test-return UWP~%")))

;;; EOF
