[armedbear-devel] new jcall / jar:file: path functions

Alessio Stalla alessiostalla at gmail.com
Thu Jan 7 16:37:46 UTC 2010


On Thu, Jan 7, 2010 at 4:32 PM, Alessio Stalla <alessiostalla at gmail.com> wrote:
> On Thu, Jan 7, 2010 at 6:39 AM, Alan Ruttenberg
> <alanruttenberg at gmail.com> wrote:
>> I'm trying to use the new jcall code in place of jss calls. However I
>> am getting an error I don't understand.
>>
>> In the following:
>>
>> (defun directory-in-jar (pathname)
>>  (let* ((jarfile (subseq (namestring (pathname-device pathname)) 9))
>>         (rest-pathname (namestring (make-pathname :directory
>> (pathname-directory pathname)
>>                                                   :name (pathname-name pathname)
>>                                                   :type (pathname-type pathname)))))
>>    (if (or (position #\* (namestring rest-pathname))
>>            (wild-pathname-p rest-pathname))
>>        (let ((jar (jnew  "java.util.zip.ZipFile" jarfile)))
>>          (let ((els (jcall "entries" jar)))
>>            (loop while (#"hasMoreElements" els)
>>               for name = (jcall "getName" (#"nextElement" els))
>>               when (pathname-match-p (concatenate 'string "/" name) rest-pathname)
>>               collect (make-pathname :device (pathname-device pathname)
>>                                      :name (pathname-name name)
>>                                      :type (pathname-type name)
>>                                      :directory `(:absolute ,@(cdr (pathname-directory name)))))))
>>        (let ((truename (probe-file-in-jar pathname)))
>>          (if truename
>>              (list truename)
>>              nil)))))
>>
>> If I replace the call (#"hasMoreElements" els) with (jcall
>> "hasMoreElements" els) I get the error:
>>
>> java.lang.IllegalAccessException: Class org.armedbear.lisp.Java can
>> not access a member of class java.util.zip.ZipFile$3 with modifiers
>> "public"
>>        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
>>        at java.lang.reflect.Method.invoke(Method.java:583)
>>        at org.armedbear.lisp.Java.jcall(Java.java:660)
>> ...
>>
>> If I replace the call  (#"nextElement" els) with  (jcall "nextElement"
>> els) I get the error
>>
>> java.lang.IllegalAccessException: Class org.armedbear.lisp.Java can
>> not access a member of class java.util.zip.ZipFile$3 with modifiers
>> "public volatile"
>>        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
>>        at java.lang.reflect.Method.invoke(Method.java:583)
>>        at org.armedbear.lisp.Java.jcall(Java.java:660)
>>
>> This from http://svn.mumble.net:8080/svn/lsw/trunk/patches/jarfile.lisp
>> where I'm starting to add more path functions that work with the
>> jar:file: pathname scheme. Feel free to steal and incorporate into
>> abcl.
>
> This code fails too with the same error:
>
> (let ((jar (jnew  "java.util.zip.ZipFile" "/home/alessio/abcl/pippo.jar")))
>   (let ((els (jcall "entries" jar)))
>     (let ((method (jmethod (jclass-of els) "hasMoreElements")))
>       (jcall method els))))
>
> as does its translation to Java:
>
> ZipFile jar = new ZipFile("/home/alessio/abcl/pippo.jar");
> Object els = jar.entries();
> Method method = els.getClass().getMethod("hasMoreElements");
> method.invoke(els);
>
> so it's some quirk of Java reflection that I'm not aware of. In fact,
> calling method.setAccessible(true); before invoking it removes the
> error. Maybe the inner classes ZipFile$x are not public, and that is
> sufficient to cause an IllegalAccessException? In any case, ABCL's
> jcall currently makes no attempts at ensuring the method is accessible
> before calling it.

hmm, it's weird. In the Java code I posted, using
Method method = java.util.Enumeration.class.getMethod("hasMoreElements");
works, even without setAccessible(true)! Although with dynamic
dispatch the method that gets called is really the same...

And it's even weirder: iterating through els.getClass().getMethods() gives

...
public java.util.zip.ZipEntry java.util.zip.ZipFile$2.nextElement()
throws java.util.NoSuchElementException
declared in class java.util.zip.ZipFile$2
public java.lang.Object java.util.zip.ZipFile$2.nextElement()
declared in class java.util.zip.ZipFile$2
...

clearly one of the two is inherited from Enumeration, but it gets
reported as being defined in the inner class, resulting in an
apparently duplicated method!

There are several bugs filed for the JVM about this or similar issues,
dating as far back as Java 1.2 (!!!); examples are
<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4819108>,
<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957>. To my
understanding, implementing the proper checks efficiently in order to
solve the problem would involve changing the class file format, so
this bug has been forever postponed.

At this point, we can either

1. ignore the problem,
2. always call setAccessible(true) before calling a method,
3. try to find the overridden method as up as possible in the class
hierarchy before calling it, hoping to get at some point to a public
class/interface

2. and 3. would make all method calls through jcall less efficient;
however, we could enable one of those techniques only when some
additional parameter is passed to jcall (e.g. the try-harder that was
proposed some time ago for jfield).

Bye,
Ale




More information about the armedbear-devel mailing list