;;;-*- Mode: Lisp; Package: CCL -*-
;;;
;;;   Copyright (C) 2009 Clozure Associates
;;;   Copyright (C) 1994-2001 Digitool, Inc
;;;   This file is part of Clozure CL.  
;;;
;;;   Clozure CL is licensed under the terms of the Lisp Lesser GNU Public
;;;   License , known as the LLGPL and distributed with Clozure CL as the
;;;   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
;;;   which is distributed with Clozure CL as the file "LGPL".  Where these
;;;   conflict, the preamble takes precedence.  
;;;
;;;   Clozure CL is referenced in the preamble as the "LIBRARY."
;;;
;;;   The LLGPL is also available online at
;;;   http://opensource.franz.com/preamble.html

(in-package "CCL")

;;; No error checking, no interrupts, no protect_caller, no nuthin.
;;; No error, no cons.  No problem.
(defun %progvrestore (saved)
  (declare (optimize (speed 3) (safety 0)))
  (dolist (pair saved)
    (%set-sym-value (car pair) (cdr pair))))

;;; Check that something that's supposed to be a proper list of
;;; symbols is; error otherwise.
;;; This is called only by the compiler output of a PROGV form.
;;; It checks for the maximum length that the progvsave subprim
;;; can handle.

(defun check-symbol-list (l &optional (max-length
                                        (floor (- 4096 20) (* target::node-size 3))
                                       ))
  (let ((len (list-length l)))
    (if (and len
             (or (null max-length)
                 (< len max-length))
             (dolist (s l t) 
               (unless (and (symbolp s)
                            (not (constant-symbol-p s))
                            (not (logbitp $sym_vbit_global (the fixnum (%symbol-bits s))))
                            (ensure-binding-index s))
                 (return nil))))
      l
      (error "~s is not a proper list of bindable symbols~@[ of length < ~s~]." l max-length))))

;;; The type-checking done on the "plist" arg shouldn't be removed.
(defun set-symbol-plist (sym plist)
  (when plist
    (let* ((len (list-length plist)))
      (unless (and len (evenp len))
        (error "Bad plist: ~s" plist))))
  (let* ((vector (symptr->symvector (%symbol->symptr sym)))
         (cell (%svref vector target::symbol.plist-cell))
         (consp (consp cell)))
    (if plist
      (if consp
        (setf (cdr cell) plist)
        (cdr (setf (%svref vector target::symbol.plist-cell) (cons nil plist))))
      (progn
        (if consp
          (setf (%svref vector target::symbol.plist-cell) (%car cell)))
        nil))))


(eval-when (:compile-toplevel :execute)
  (declaim (inline %pl-search)))

(defun %pl-search (l key)
  (declare (list l) (optimize (speed 3)))
  (loop
    (if (eq (car l) key)
      (return)
      (if l
        (setq l (cdr (the list (cdr l))))
        (return))))
  l)


(defun symbol-plist (sym)
  "Return SYMBOL's property list."
  (let* ((cell (%svref (symptr->symvector (%symbol->symptr sym)) target::symbol.plist-cell)))
    (if (consp cell)
      (cdr cell))))


(defun get (sym key &optional default)
  "Look on the property list of SYMBOL for the specified INDICATOR. If this
  is found, return the associated value, else return DEFAULT."
  (let* ((cell (%svref (symptr->symvector (%symbol->symptr sym)) target::symbol.plist-cell))
         (tail (if (consp cell)
                 (%pl-search (cdr cell ) key))))
    (if tail (%cadr tail) default)))

(defun put (sym key value)
  (let* ((symptr (%symbol->symptr sym))
         (vector (symptr->symvector symptr))
         (cell  (%svref vector target::symbol.plist-cell))
         (plist (if (consp cell) (cdr cell)))
         (tail (%pl-search plist key)))
    (if tail 
      (%rplaca (%cdr tail) value)
      (progn
        (setq plist (cons key (cons value plist)))
        (if (consp cell)
          (setf (cdr cell) plist)
          (setf (%svref vector target::symbol.plist-cell) (cons nil plist)))))
    value))


(defun get-type-predicate (name)
  (let* ((symvec (symptr->symvector (%symbol->symptr name)))
         (pp (%svref symvec target::symbol.package-predicate-cell)))
    (if (consp pp)
      (%cdr pp))))

(defun set-type-predicate (name function)
  (let* ((bits (%symbol-bits name))
         (symvec (symptr->symvector (%symbol->symptr name)))
         (spp (%svref symvec target::symbol.package-predicate-cell)))
    (declare (fixnum bits))
    (if (logbitp $sym_vbit_typeppred bits)
      (%rplacd spp function)
      (progn
        (%symbol-bits name (the fixnum (bitset $sym_vbit_typeppred bits)))
        (setf (%svref symvec target::symbol.package-predicate-cell) (cons spp function))))
    function))

(defun symbol-value (sym)
  "Return SYMBOL's current bound value."
  (let* ((val (%sym-value sym)))
    (if (eq val (%unbound-marker))
      (%kernel-restart $xvunbnd sym)
      val)))

(defun set (sym value)
  "Set SYMBOL's value cell to NEW-VALUE."
  (let* ((bits (%symbol-bits sym)))
    (declare (fixnum bits))
    (if (logbitp $sym_vbit_const bits)
      (%err-disp $XCONST sym)
      (%set-sym-value sym value))))

(defun constant-symbol-p (sym)
  (and (symbolp sym)
       (%ilogbitp $sym_vbit_const (%symbol-bits sym))))

;;; This leaves the SPECIAL bit alone, clears the others.
(defun makunbound (sym)
  "Make SYMBOL unbound, removing any value it may currently have."
  (if (and *warn-if-redefine-kernel*
           (constant-symbol-p sym))
    (cerror "Make ~S be unbound anyway."
            "~S is a constant; making it unbound might be a bad idea." sym))
  (%symbol-bits sym (the fixnum (logand (logior #xff00 (ash 1 $sym_bit_special))
                                        (the fixnum (%symbol-bits sym)))))
  (%set-sym-value sym (%unbound-marker))
  sym)

(defun non-nil-symbolp (x)
  "Returns symbol if true"
  (if (symbolp x) x))

(defun symbol-package (sym)
  "Return the package SYMBOL was interned in, or NIL if none."
  (let* ((pp (%svref (symptr->symvector (%symbol->symptr sym)) target::symbol.package-predicate-cell)))
    (if (consp pp) (car pp) pp)))

(defun boundp (sym)
  "Return non-NIL if SYMBOL is bound to a value."
  (not (eq (%sym-value sym) (%unbound-marker))))

(defun make-symbol (name)
  "Make and return a new symbol with the NAME as its print name."
  (symvector->symptr
   (%gvector target::subtag-symbol
             (ensure-simple-string name) ; pname
             (%unbound-marker)          ; value cell
             %unbound-function%         ; function cell
             nil                        ; package&predicate
             0                          ; flags
             nil                        ; plist
             0)))                       ; binding-index

(defun %symbol-bits (sym &optional new)
  (let* ((p (%symbol->symptr sym))
         (bits (%svref (symptr->symvector p) target::symbol.flags-cell)))
    (if new
      (setf (%svref (symptr->symvector p) target::symbol.flags-cell) new))
    bits))

(defun %sym-value (name)
  (%symptr-value (%symbol->symptr name)))

(defun %set-sym-value (name val)
  (%set-symptr-value (%symbol->symptr name) val))
    
(defun %sym-global-value (name)
  (%svref (symptr->symvector (%symbol->symptr name)) target::symbol.vcell-cell))

(defun %set-sym-global-value (name val)
  (setf (%svref (symptr->symvector (%symbol->symptr name)) target::symbol.vcell-cell) val))

(defun symbol-name (sym)
  "Return SYMBOL's name as a string."
  #+(or ppc32-target x8632-target x8664-target arm-target)
  (%svref (symptr->symvector (%symbol->symptr sym)) target::symbol.pname-cell)
  #+ppc64-target
  (if sym                               ;NIL's pname is implicit
    (%svref (%symbol->symptr sym) ppc64::symbol.pname-cell)
    "NIL")
  )




(defun %global-macro-function (symbol)
  (let* ((fbinding (fboundp symbol)))
    (if (and #-arm-target (typep fbinding 'simple-vector)
             #+arm-target (= (typecode fbinding) arm::subtag-pseudofunction)
             (= (the fixnum (uvsize fbinding)) #-arm-target 2 #+arm-target 3))
      (let* ((fun (%svref fbinding #-arm-target 1 #+arm-target 2)))
        (if (functionp fun) fun)))))

(defun %symbol-binding-address (sym)
  (%symptr-binding-address (%symbol->symptr sym)))

(defun symbol-binding-index (sym)
  (%svref (symptr->symvector (%symbol->symptr sym)) target::symbol.binding-index-cell))

(defvar *interrupt-level* -1)

;;; Special binding indices, and the inverse mapping between indices
;;; and symbols
(let* ((binding-index-lock (make-lock))
       (binding-index-reverse-map (make-hash-table :test #'eq :weak :value))
       (next-binding-index 0))
  (defun %set-binding-index (val) (setq next-binding-index val))
  (defun next-binding-index () (1+ next-binding-index))
  (defun ensure-binding-index (sym)
    (with-lock-grabbed (binding-index-lock)
      (let* ((symvec (symptr->symvector (%symbol->symptr sym)))
             (idx (%svref symvec target::symbol.binding-index-cell))
             (bits (%symbol-bits sym)))
        (declare (fixnum idx bits))
        (if (or (logbitp $sym_vbit_global bits)
                (logbitp $sym_vbit_const bits))
          (unless (zerop idx)
            (remhash idx binding-index-reverse-map)
            (setf (%svref symvec target::symbol.binding-index-cell) 0))
          (if (zerop idx)
            (let* ((new-idx (incf next-binding-index)))
              (setf (%svref symvec target::symbol.binding-index-cell) new-idx)
              (setf (gethash new-idx binding-index-reverse-map) sym))))
        sym)))
  (defun binding-index-symbol (idx)
    (with-lock-grabbed (binding-index-lock)
      (gethash idx binding-index-reverse-map)))
  (defun cold-load-binding-index (sym)
    ;; Index may have been assigned via xloader.  Update
    ;; reverse map
    (with-lock-grabbed (binding-index-lock)
      (let* ((idx (%svref (symptr->symvector (%symbol->symptr sym))
                          target::symbol.binding-index-cell)))
        (declare (fixnum idx))
        (unless (zerop idx)
          (setf (gethash idx binding-index-reverse-map) sym))))))

       

