;;;; -*- mode:lisp;coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE:               oclo.lisp
;;;;LANGUAGE:           Common-Lisp
;;;;SYSTEM:             Common-Lisp
;;;;USER-INTERFACE:     NONE
;;;;DESCRIPTION
;;;;
;;;;    This file defines additions to the oclo package not provided by
;;;;    the implementation or implementation specific code.
;;;;
;;;;AUTHORS
;;;;    <PJB> Pascal J. Bourguignon <pjb@informatimago.com>
;;;;MODIFICATIONS
;;;;    2010-12-17 <PJB> Created.
;;;;BUGS
;;;;LEGAL
;;;;    AGPL3
;;;;
;;;;    Copyright Pascal J. Bourguignon 2010 - 2016
;;;;
;;;;    This program is free software: you can redistribute it and/or modify
;;;;    it under the terms of the GNU Affero General Public License as published by
;;;;    the Free Software Foundation, either version 3 of the License, or
;;;;    (at your option) any later version.
;;;;
;;;;    This program is distributed in the hope that it will be useful,
;;;;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;;    GNU Affero General Public License for more details.
;;;;
;;;;    You should have received a copy of the GNU Affero General Public License
;;;;    along with this program.  If not, see <http://www.gnu.org/licenses/>
;;;;**************************************************************************
(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf *readtable* (copy-readtable nil)))
(in-package "COM.INFORMATIMAGO.OBJECTIVE-C.LOWER")

(defmacro stret (expression &environment env)
  (let ((result (gensym "structure-result-")))
    `(slet ((,result ,(macroexpand expression env))) ,result)))


#-(and)
(defun needs-stret (o msg args env &optional sclassname)
  (multiple-value-bind (msg args vargs) (ccl::parse-message (cons msg args))
    (let ((message-info (ccl::get-objc-message-info msg)))
      (unless message-info
        (error "Unknown message: ~S" msg))
      ;; If a vararg exists, make sure that the message can accept it
      (when (and vargs (not (getf (ccl::objc-message-info-flags message-info)
                                  :accepts-varargs)))
        (error "Message ~S cannot accept a variable number of arguments" msg))
      (unless (= (length args) (ccl::objc-message-info-req-args message-info))
        (error "Message ~S requires ~a ~d args, but ~d were provided."
               msg
               (if vargs "at least" "exactly")
               (ccl::objc-message-info-req-args message-info)
               (length args)))
      (multiple-value-bind (args svarforms sinitforms) (ccl::sletify-message-args args)
        (let* ((ambiguous   (getf (ccl::objc-message-info-flags message-info) :ambiguous))
               (methods     (ccl::objc-message-info-methods message-info))
               (method-info (if ambiguous
                                (let ((class (if sclassname
                                                 (ccl::find-objc-class sclassname)
                                                 (ccl::get-objc-class-from-declaration (ccl::declared-type o env)))))
                                  (when class
                                    (dolist (m methods)
                                      (unless (getf (ccl::objc-method-info-flags m) :protocol)
                                        (let ((mclass (or (ccl::get-objc-method-info-class m)
                                                          (error "Can't find ObjC class named ~s"
                                                                 (ccl::objc-method-info-class-name m)))))
                                          (when (subtypep class mclass)
                                            (return m)))))))
                                (car methods))))
          (if method-info
              (ccl::result-type-requires-structure-return
               (ccl::objc-method-info-result-type method-info))
              (error "Cannot find method result type for message -~A sent to ~S.  Try declaring the class of the recipient."
                     (ccl::objc-message-info-message-name message-info) o)))))))


#-(and)
(defmacro send (&whole w o msg &rest args &environment env)
  (if (needs-stret o msg args env)
      `(stret ,w)
      (ccl::make-optimized-send o msg args env)))

#-(and)
(defmacro send/stret (&whole w s o msg &rest args &environment env)
  (if (needs-stret o msg args env)
      (if s
          (ccl::make-optimized-send o msg args env s)
          `(stret (send ,@(cddr w))))
      (ccl::make-optimized-send o msg args env)))


;;;; THE END ;;;;
ViewGit