;;; abcl-script.lisp ;;; ;;; Copyright (C) 2008 Alessio Stalla ;;; ;;; This program is free software; you can redistribute it and/or ;;; modify it under the terms of the GNU General Public License ;;; as published by the Free Software Foundation; either version 2 ;;; 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 General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;;; ;;; As a special exception, the copyright holders of this library give you ;;; permission to link this library with independent modules to produce an ;;; executable, regardless of the license terms of these independent ;;; modules, and to copy and distribute the resulting executable under ;;; terms of your choice, provided that you also meet, for each linked ;;; independent module, the terms and conditions of the license of that ;;; module. An independent module is a module which is not derived from ;;; or based on this library. If you modify this library, you may extend ;;; this exception to your version of the library, but you are not ;;; obligated to do so. If you do not wish to do so, delete this ;;; exception statement from your version. (in-package :abcl-script) (defconstant +global-scope+ (jfield "javax.script.ScriptContext" "GLOBAL_SCOPE")) (defconstant +engine-scope+ (jfield "javax.script.ScriptContext" "ENGINE_SCOPE")) (defconstant +put-binding+ (jmethod "javax.script.Bindings" "put" "java.lang.String" "java.lang.Object")) (defconstant +get-bindings+ (jmethod "javax.script.ScriptContext" "getBindings" "int")) (defun generate-bindings (bindings) (let ((*package* (find-package :abcl-script-user))) (mapcar (lambda (binding) (list (read-from-string (car binding)) (cdr binding))) bindings))) (defun generate-special-declarations (bindings) (let ((*package* (find-package :abcl-script-user))) `(declare (special ,@(mapcar (lambda (binding) (read-from-string (car binding))) bindings))))) (defun generate-java-bindings (bindings-list actual-bindings java-bindings) (loop :for binding :in actual-bindings :for jbinding :in bindings-list :collect `(jcall +put-binding+ ,java-bindings ,(car jbinding) ,(car binding)))) (defmacro eval-in-script-context ((global-bindings engine-bindings stdin stdout script-context) body) "Sets up a context in which to evaluate a piece of code coming from Java through the JSR-223 methods." (let ((actual-global-bindings (gensym)) (actual-engine-bindings (gensym))) `(let ((*package* (find-package :abcl-script-user)) (*standard-input* ,stdin) (*standard-output* ,stdout) (,actual-global-bindings (generate-bindings ,global-bindings)) (,actual-engine-bindings (generate-bindings ,engine-bindings))) (eval `(let (,@,actual-global-bindings) (let (,@,actual-engine-bindings) ,(generate-special-declarations ,global-bindings) ,(generate-special-declarations ,engine-bindings) (prog1 (progn ,@,body) (finish-output *standard-output*) ,@(generate-java-bindings ,global-bindings ,actual-global-bindings (jcall +get-bindings+ ,script-context +global-scope+)) ,@(generate-java-bindings ,engine-bindings ,actual-engine-bindings (jcall +get-bindings+ ,script-context +engine-scope+))))))))) (defun eval-function (global-bindings engine-bindings stdin stdout function-and-args script-context) (eval-in-script-context (global-bindings engine-bindings stdin stdout script-context) `((funcall ,@(mapcar (lambda (arg) `(quote ,arg)) function-and-args))))) (defun eval-script (global-bindings engine-bindings stdin stdout code-string script-context) (eval-in-script-context (global-bindings engine-bindings stdin stdout script-context) `((with-input-from-string (str ,code-string) (sys::load-returning-last-result str))))) (defun eval-compiled-script (global-bindings engine-bindings stdin stdout function script-context) (eval-in-script-context (global-bindings engine-bindings stdin stdout script-context) `((funcall ,function)))) (defun compile-script (code-string) (let* ((tmp-file (jstatic (jmethod "java.io.File" "createTempFile" "java.lang.String" "java.lang.String") nil "abcl-src-file-" ".lisp")) (tmp-file-path (jcall (jmethod "java.io.File" "getAbsolutePath") tmp-file))) (jcall (jmethod "java.io.File" "deleteOnExit") tmp-file) ;to be really-really-really sure... (unwind-protect (progn (with-open-file (stream tmp-file-path :direction :output) (princ "(in-package :abcl-script-user)" stream) (princ code-string stream) (finish-output stream)) (let ((compiled-file (compile-file tmp-file-path))) (jcall (jmethod "java.io.File" "deleteOnExit") (jnew (jconstructor "java.io.File" "java.lang.String") (namestring compiled-file))) (lambda () (let ((*package* (find-package :abcl-script-user))) (sys::load-returning-last-result compiled-file))))) (delete-file tmp-file-path)))) ;;Java interface implementation - TODO (defvar *interface-implementation-map* (make-hash-table :test #'equal)) (defun find-java-interface-implementation (interface) (gethash interface *interface-implementation-map*)) (defun register-java-interface-implementation (interface implementation &optional lisp-this) (setf (gethash interface *interface-implementation-map*) (jmake-proxy interface implementation lisp-this))) (defun remove-java-interface-implementation (interface) (remhash interface *interface-implementation-map*)) ;Let's load it so asdf package is already defined when loading config.lisp (require 'asdf)