Hi all,
This is old history, but I remember seeing some really old email threads about hardcoding of Lisp function (as symbols) calls into Java methods. Attached please find a patch for trying this out. I refactored the original method body generation code slightly, then added java::runtime-class-add-symbol-method-body which involves low level bytecode generation, so I'd appreciate additional checks/reviews (I believe the repetitive load/store sequences won't have any effects with the JIT & optimisers, but may be fractionally easier to decompile...).
I also updated java:jnew-runtime-class's docstring, but I'm not sure if the wording "function designator" is accurate enough, since (funcall '(lambda () 1)) => 1
Would '(lambda () 1) be considered a FUNCTION DESIGNATOR? In any case, java::runtime-class-add-methods already works with it just the same.
This code has not been rigorously tested as it doesn't work in abcl-1.2.1 (tested/works for older versions) due to a regression, since fixed in r14570 (Restore Packages.findPackage() API...Fixes #324).
See appendix, where I include a start for some tests. And in case it isn't obvious, the hardcoding of symbol funcalls (test3) into a class method is the only new functionality here.
Yong
(defun test3 (this) "test3")
(defun test4 (this x y z) (+ x y z))
(defparameter *test-class* (java:jnew-runtime-class "runtime_class" :methods `(("test1" "java.lang.String" () ,(lambda (this) "test1")) ("test2" "java.lang.String" () (lambda (this) "test2")) ("test3" "java.lang.String" () test3) ("test4" "java.lang.Object" ;; (:int :int :int) ;; unsupported for now ("java.lang.Object" "java.lang.Object" "java.lang.Object") ,(function test4)) ("test5" "java.lang.String" ("java.lang.String") (lambda (this x) (declare (ignore x)) "test5")) ("undefined" :void () undefined))))
(let ((obj (java:jnew *test-class*))) (list (java:jcall "test1" obj) (java:jcall "test2" obj) (java:jcall "test3" obj) (java:jcall "test4" obj 1 2 3) (java:jcall "test5" obj "foo"))) ==> ("test1" "test2" "test3" 6 "test5")
;; This should thrown an error... (java:jcall "undefined" (java:jnew *test-class*))
;; As should this, but java.lang.NoSuchMethodException: No applicable method is not very obvious... (java:jcall "test5" (java:jnew *test-class*))
;; This shouldn't, but does, see stack trace below, (quite relatedly, at least for me using Slime, (java:jcall "undefined" (java:jnew *test-class*)) above actually seemed to throw 2 errors)
(ignore-errors (java:jcall "undefined" (java:jnew *test-class*))) ==> ;;error
Java exception 'org.armedbear.lisp.Go'. [Condition of type JAVA:JAVA-EXCEPTION]
Restarts: 0: [RETRY] Retry SLIME interactive evaluation request. 1: [*ABORT] Return to SLIME's top level. 2: [ABORT] Abort thread.
Backtrace: 0: (#<FUNCTION {106FAF1E}> #<JAVA-EXCEPTION {658A7B6C}> #<FUNCTION {106FAF1E}>) Locals: "??" = #<FUNCTION {106FAF1E}> "??"#1 = #<JAVA:JAVA-EXCEPTION org.armedbear.lisp.Go {658A7B6C}> "??"#2 = #<FUNCTION {106FAF1E}> 1: (APPLY #<FUNCTION {106FAF1E}> (#<JAVA-EXCEPTION {658A7B6C}> #<FUNCTION {106FAF1E}>)) Locals: "??" = APPLY "??"#1 = #<FUNCTION {106FAF1E}> "??"#2 = (#<JAVA:JAVA-EXCEPTION org.armedbear.lisp.Go {658A7B6C}> ..) 2: (SYSTEM::RUN-HOOK SYSTEM::*INVOKE-DEBUGGER-HOOK* #<JAVA-EXCEPTION {658A7B6C}> #<FUNCTION {106FAF1E}>) Locals: "??" = SYSTEM::RUN-HOOK "??"#1 = SYSTEM::*INVOKE-DEBUGGER-HOOK* "??"#2 = #<JAVA:JAVA-EXCEPTION org.armedbear.lisp.Go {658A7B6C}> "??"#3 = #<FUNCTION {106FAF1E}> 3: (INVOKE-DEBUGGER #<JAVA-EXCEPTION {658A7B6C}>) Locals: "??" = INVOKE-DEBUGGER "??"#1 = #<JAVA:JAVA-EXCEPTION org.armedbear.lisp.Go {658A7B6C}> 4: org.armedbear.lisp.Lisp.error(Lisp.java:382) Locals: "??" = "??" 5: org.armedbear.lisp.Java.jcall(Java.java:909) Locals: "??" = "??" 6: org.armedbear.lisp.Java$pf_jcall.execute(Java.java:756) Locals: "??" = :CLASS "??"#1 = "org.armedbear.lisp.Java$pf_jcall" "??"#2 = :METHOD "??"#3 = "execute" "??"#4 = :FILE "??"#5 = "Java.java" "??"#6 = :LINE "??"#7 = 756 7: org.armedbear.lisp.Primitive.execute(Primitive.java:123) Locals: "??" = :CLASS "??"#1 = "org.armedbear.lisp.Primitive" "??"#2 = :METHOD "??"#3 = "execute" "??"#4 = :FILE "??"#5 = "Primitive.java" "??"#6 = :LINE "??"#7 = 123 8: (JAVA:JCALL "undefined" #<runtime_class runtime_class@1451e300 {C0E1F6C}>) Locals: "??" = JAVA:JCALL "??"#1 = "undefined" "??"#2 = #<runtime_class runtime_class@1451e300 {C0E1F6C}> 9: (#<FUNCTION (LAMBDA ()) {7330F1A2}>) Locals: "??" = #<FUNCTION (LAMBDA ()) {7330F1A2}> 10: (JAVA:JRUN-EXCEPTION-PROTECTED #<FUNCTION (LAMBDA ()) {7330F1A2}>) 11: (SYSTEM::%EVAL (IGNORE-ERRORS (JAVA:JCALL "undefined" (JAVA:JNEW *TEST-CLASS*)))) 12: (EVAL (IGNORE-ERRORS (JAVA:JCALL "undefined" (JAVA:JNEW *TEST-CLASS*)))) 13: (#<FUNCTION {1247FDC3}>)
Theam Yong Chew senatorzergling@gmail.com writes:
I also updated java:jnew-runtime-class's docstring, but I'm not sure if the wording "function designator" is accurate enough, since (funcall '(lambda () 1)) => 1
Would '(lambda () 1) be considered a FUNCTION DESIGNATOR?
CLHS Glossary says:
function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself). The consequences are undefined if a symbol is used as a function designator but it does not have a global definition as a function, or it has a global definition as a macro or a special form. See also extended function designator.
therefore:
(defun function-designator-p (object) (or (symbolp object) (functionp object)))
(function-designator-p '(lambda () 1)) --> NIL
So, the answer is no.
On 12/16/13, Pascal J. Bourguignon pjb@informatimago.com wrote:
Theam Yong Chew senatorzergling@gmail.com writes:
I also updated java:jnew-runtime-class's docstring, but I'm not sure if the wording "function designator" is accurate enough, since (funcall '(lambda () 1)) => 1
Would '(lambda () 1) be considered a FUNCTION DESIGNATOR?
CLHS Glossary says:
function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself). The consequences are undefined if a symbol is used as a function designator but it does not have a global definition as a function, or it has a global definition as a macro or a special form. See also extended function designator.
therefore:
(defun function-designator-p (object) (or (symbolp object) (functionp object))) (function-designator-p '(lambda () 1)) --> NIL
So, the answer is no.
-- __Pascal Bourguignon__ http://www.informatimago.com/
Ok, I did see that. I was momentarily confused by ABCL's behaviour, it accepted this,
CL-USER(1): (funcall '(lambda (x) 1) 1) ==> 1
whereas in SBCL: (funcall '(lambda (x) 1) 1) leads to an error.
The value (LAMBDA (X) 1) is not of type (OR FUNCTION SYMBOL). [Condition of type TYPE-ERROR]
Restarts: 0: [ABORT] Exit debugger, returning to top level. ...
After re-reading CLHS (glossary, FUNCALL, APPLY etc), this seems expected. I'm also trying to remember, this seems to that conform to my muscle memory more consistently as well.
I was going to modify my patch to change "FUNCTION designator" -> "FUNCALLable Lisp object" instead, but perhaps ABCL's APPLY needs to be modified instead? Is this non-compliant behaviour?
Yong
On 12/17/13, Theam Yong Chew senatorzergling@gmail.com wrote:
I was going to modify my patch to change "FUNCTION designator" -> "FUNCALLable Lisp object" instead, but perhaps ABCL's APPLY needs to be modified instead? Is this non-compliant behaviour?
Yong
Oops, I forgot to remove my attachment, please ignore the attached patch in my previous email.
Yong
armedbear-devel@common-lisp.net