the mysterios IllegalAccessError

19 views
Skip to first unread message

Jochen Theodorou

unread,
Oct 7, 2010, 12:56:22 PM10/7/10
to jvm-la...@googlegroups.com
Hi all,

I have here an issue that is driving me crazy - and I have the hope
someone reading this here may have an idea what is wrong. So basically I
have this code in Groovy:

G4410JavaStringProducer jsp = new G4410JavaStringProducer()

which atm gets compiled to something like this:

> ALOAD 1
> LDC 3
> AALOAD
> INVOKESTATIC groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer ()Ljava/lang/Class;
> INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;)Ljava/lang/Object;
> INVOKESTATIC groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer ()Ljava/lang/Class;
> INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
> CHECKCAST groovy/bugs/G4410JavaStringProducer
> ASTORE 6

ugly and inefficient, but I am working on that... anyway.. the problem
is that this code makes problems. G4410JavaStringProducer is package
private class in groovy.bugs and this class is in the same package... I
veryfied that the callConstructor call works, that the castToType call
works as well... then my debugger jumps to
ClassLoader#checkPackageAccess for the class
groovy.bugs.G4410JavaStringProducer with no SecurityManager set.

And the next thing my debugger stops is is then suddenly the constructor
of IllegalAccessError with

java.lang.IllegalAccessError: tried to access class
groovy.bugs.G4410JavaStringProducer from class groovy.bugs.test

the parent stack frame for the constructor call is the stack frame of
the groovy program, so there is nothing in between.

So now... what is this error? afaik the program is allowed to access the
class. I was not able to reproduce the problem in pure Java. It is a
totally strange thing to me.

If I change the program to

def jsp = new G4410JavaStringProducer()

then the CHECKCAST and the castToType call will not in there and the
error vanishes as well... and the only thing I can say here atm is that
I am absolutely clueless as of what the problem is.


Does anyone at least have a hint what could be wrong here? I am
thankful for every idea.

bye Jochen

--
Jochen "blackdrag" Theodorou
The Groovy Project Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/

Jochen Theodorou

unread,
Oct 7, 2010, 1:06:59 PM10/7/10
to jvm-la...@googlegroups.com
Jochen Theodorou wrote:
[...]

> then the CHECKCAST and the castToType call will not in there and the
> error vanishes as well... and the only thing I can say here atm is that
> I am absolutely clueless as of what the problem is.

I can now exclude castToType from causing the problem. I modified the
bytecode generation to have no longer the castToType call and I get
still the error. If I remove the checkcast call as well, then the error
vanishes, as I already said

... still clueless

Jochen Theodorou

unread,
Oct 7, 2010, 1:44:54 PM10/7/10
to jvm-la...@googlegroups.com
Jochen Theodorou wrote:
[...]

> If I change the program to
>
> def jsp = new G4410JavaStringProducer()
>
> then the CHECKCAST and the castToType call will not in there and the
> error vanishes as well...

funny thing is... if I change the code to

G4410JavaStringProducer jsp = null

then the castToType call and CHECKCAST are still in, but I get no error.
This time the constructor call is missing.... wtf?

Alessio Stalla

unread,
Oct 7, 2010, 1:55:06 PM10/7/10
to jvm-la...@googlegroups.com
On Thu, Oct 7, 2010 at 7:44 PM, Jochen Theodorou <blac...@gmx.org> wrote:
> Jochen Theodorou wrote:
> [...]
>>
>> If I change the program to
>>
>> def jsp = new G4410JavaStringProducer()
>>
>> then the CHECKCAST and the castToType call will not in there and the error
>> vanishes as well...
>
> funny thing is... if I change the code to
>
> G4410JavaStringProducer jsp = null
>
> then the castToType call and CHECKCAST are still in, but I get no error.
> This time the constructor call is missing.... wtf?

How did you try to reproduce the problem in Java? What does javac
produce for your code? I remember having a vaguely similar problem,
but it involved reflection and private inner classes, and you don't
seem to be using any of those.

Cheers,
Alessio

Jochen Theodorou

unread,
Oct 7, 2010, 2:10:35 PM10/7/10
to jvm-la...@googlegroups.com
Alessio Stalla wrote:
> On Thu, Oct 7, 2010 at 7:44 PM, Jochen Theodorou <blac...@gmx.org> wrote:
[...]

> How did you try to reproduce the problem in Java? What does javac
> produce for your code? I remember having a vaguely similar problem,
> but it involved reflection and private inner classes, and you don't
> seem to be using any of those.

I did not try to insert the constructor call as of yet. I mostly tried
variants of the groovy code in Java, one time forced the CHECKCAST call,
since I thought this is the culprit. but no success.

javac produces:

NEW G4410JavaStringProducer
DUP
INVOKESPECIAL G4410JavaStringProducer.<init> ()V
ASTORE 2

John Wilson

unread,
Oct 7, 2010, 2:25:08 PM10/7/10
to jvm-la...@googlegroups.com
On 7 October 2010 19:10, Jochen Theodorou <blac...@gmx.org> wrote:
> Alessio Stalla wrote:
>>
>> On Thu, Oct 7, 2010 at 7:44 PM, Jochen Theodorou <blac...@gmx.org>
>> wrote:
>
> [...]
>>
>> How did you try to reproduce the problem in Java? What does javac
>> produce for your code? I remember having a vaguely similar problem,
>> but it involved reflection and private inner classes, and you don't
>> seem to be using any of those.
>
> I did not try to insert the constructor call as of yet. I mostly tried
> variants of the groovy code in Java, one time forced the CHECKCAST call,
> since I thought this is the culprit. but no success.
>
> javac produces:
>
>    NEW G4410JavaStringProducer
>    DUP
>    INVOKESPECIAL G4410JavaStringProducer.<init> ()V
>    ASTORE 2


You might try this Java

Object o = new G4410JavaStringProducer();
G4410JavaStringProducer jsp = (G4410JavaStringProducer)o;

John Wilson

Jochen Theodorou

unread,
Oct 7, 2010, 2:29:49 PM10/7/10
to jvm-la...@googlegroups.com
Jochen Theodorou wrote:
> Alessio Stalla wrote:
>> On Thu, Oct 7, 2010 at 7:44 PM, Jochen Theodorou <blac...@gmx.org>
>> wrote:
> [...]
>> How did you try to reproduce the problem in Java? What does javac
>> produce for your code? I remember having a vaguely similar problem,
>> but it involved reflection and private inner classes, and you don't
>> seem to be using any of those.
>
> I did not try to insert the constructor call as of yet. I mostly tried
> variants of the groovy code in Java, one time forced the CHECKCAST call,
> since I thought this is the culprit. but no success.
>
> javac produces:
>
> NEW G4410JavaStringProducer
> DUP
> INVOKESPECIAL G4410JavaStringProducer.<init> ()V
> ASTORE 2

I tried it now with this Java code:

> CallSite[] csa = (new CallSiteArray(X.class,new String[1])).array;
> Object tmp = csa[0].callConstructor(G4410JavaStringProducer.class);
> tmp = ScriptBytecodeAdapter.castToType(tmp, G4410JavaStringProducer.class);
> G4410JavaStringProducer jsp = (G4410JavaStringProducer) tmp;

which is roughly what Groovy produces transfered to Java and this works
without problem. The generated bytecode for this is:

> ALOAD 2
> ICONST_0
> AALOAD
> LDC Lgroovy/bugs/G4410JavaStringProducer;.class
> INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;)Ljava/lang/Object;
> ASTORE 3
> ALOAD 3
> LDC Lgroovy/bugs/G4410JavaStringProducer;.class


> INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;

> ASTORE 3
> ALOAD 3
> CHECKCAST groovy/bugs/G4410JavaStringProducer
> ASTORE 4

for a direct compare here again the code from groovy:

> ALOAD 1
> LDC 3
> AALOAD
> INVOKESTATIC groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer ()Ljava/lang/Class;
> INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;)Ljava/lang/Object;

> ASTORE 5
> ALOAD 5


> INVOKESTATIC groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer ()Ljava/lang/Class;
> INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
> CHECKCAST groovy/bugs/G4410JavaStringProducer
> ASTORE 6

apart from some obvious differences in the register numbers, one time
storing the result, the other time not the biggest difference is the
call to
groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer()
instead of the LDC, which is because groovy does here atm no generate
java5 bytecode.

oh, and the Java version has class name X, in Groovy it is test ;)

Jochen Theodorou

unread,
Oct 7, 2010, 2:30:45 PM10/7/10
to jvm-la...@googlegroups.com
John Wilson wrote:
[...]

> You might try this Java
>
> Object o = new G4410JavaStringProducer();
> G4410JavaStringProducer jsp = (G4410JavaStringProducer)o;

I tried it already before my first post, no problem with that.

John Wilson

unread,
Oct 7, 2010, 2:37:00 PM10/7/10
to jvm-la...@googlegroups.com
Have you tried it on the IBM JVM or JRockit?

John Wilson

Jochen Theodorou

unread,
Oct 7, 2010, 5:18:08 PM10/7/10
to jvm-la...@googlegroups.com
John Wilson wrote:
> Have you tried it on the IBM JVM or JRockit?

just tried the ibm jdk6_60 and it shows the same problem, with a bit
less informative error message.

Rémi Forax

unread,
Oct 7, 2010, 5:31:29 PM10/7/10
to jvm-la...@googlegroups.com
Le 07/10/2010 20:29, Jochen Theodorou a �crit :

INVOKESTATIC
groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer
()Ljava/lang/Class;
can trigger the loading of class groovy/bugs/test.

So the error can perhaps come from the static init of groovy/bugs/test

R�mi

Jochen Theodorou

unread,
Oct 7, 2010, 6:11:11 PM10/7/10
to jvm-la...@googlegroups.com
R�mi Forax wrote:
[...]

> INVOKESTATIC
> groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer
> ()Ljava/lang/Class;
> can trigger the loading of class groovy/bugs/test.
>
> So the error can perhaps come from the static init of groovy/bugs/test

maybe it got a bit messie due to my many experiments, but the class
groovy.bugs.test is the class executing the failing code. So this cannot
be. And even if it were there, then it would show up in the stack trace,
which it does not. Also the here called method is loading the class
groovy.bugs.G4410JavaStringProducer, to whom the access fails, since it
is package private... Only that the access should normally be ok, and
that the error is not risen in that method or directly after it.

Alessio Stalla

unread,
Oct 8, 2010, 4:24:22 AM10/8/10
to jvm-la...@googlegroups.com
On Fri, Oct 8, 2010 at 12:11 AM, Jochen Theodorou <blac...@gmx.org> wrote:
> Rémi Forax wrote:
> [...]
>>
>> INVOKESTATIC
>> groovy/bugs/test.$get$$class$groovy$bugs$G4410JavaStringProducer
>> ()Ljava/lang/Class;
>> can trigger the loading of class groovy/bugs/test.
>>
>> So the error can perhaps come from the static init of groovy/bugs/test
>
> maybe it got a bit messie due to my many experiments, but the class
> groovy.bugs.test is the class executing the failing code. So this cannot be.
> And even if it were there, then it would show up in the stack trace, which
> it does not. Also the here called method is loading the class
> groovy.bugs.G4410JavaStringProducer, to whom the access fails, since it is
> package private... Only that the access should normally be ok, and that the
> error is not risen in that method or directly after it.

Just to rule this out: are you sure the two classes (test and
G4410JavaStringProducer) are loaded by the same classloader?

Jochen Theodorou

unread,
Oct 8, 2010, 7:16:24 AM10/8/10
to jvm-la...@googlegroups.com
Alessio Stalla wrote:
[...]

> Just to rule this out: are you sure the two classes (test and
> G4410JavaStringProducer) are loaded by the same classloader?

If I execute that from eclipse, then it is not the same loader. In case
of the junit test I think it is. But in case of eclipse the loader for
test is a child of the loader for the G4410JavaStringProducer class

bye blackdrag

Alessio Stalla

unread,
Oct 8, 2010, 8:26:48 AM10/8/10
to jvm-la...@googlegroups.com
On Fri, Oct 8, 2010 at 1:16 PM, Jochen Theodorou <blac...@gmx.org> wrote:
> Alessio Stalla wrote:
> [...]
>>
>> Just to rule this out: are you sure the two classes (test and
>> G4410JavaStringProducer) are loaded by the same classloader?
>
> If I execute that from eclipse, then it is not the same loader. In case of
> the junit test I think it is. But in case of eclipse the loader for test is
> a child of the loader for the G4410JavaStringProducer class

Hmm. I doubt them being in a parent-child relationship changes
anything wrt determining whether the two classes are in the same
package. The JVM specification says [1]:

"A class or interface C is accessible to a class or interface D if and
only if either of the following conditions are true:

* C is public.

* C and D are members of the same runtime package (§5.3)."

And "runtime package" is so defined [2]:

"The runtime package of a class or interface is determined by the
package name and defining class loader of the class or interface".

I interpret that to mean that two classes at runtime are considered to
be in the same package if and only if they have the same package name
and they've been loaded by the very same classloader.

So, that might be the problem. Maybe with JUnit you get two classloaders too.

Cheers,
Alessio

[1] http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#75929
[2] http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#72007

Jochen Theodorou

unread,
Oct 8, 2010, 8:58:56 AM10/8/10
to jvm-la...@googlegroups.com
Alessio Stalla wrote:
[...]

> I interpret that to mean that two classes at runtime are considered to
> be in the same package if and only if they have the same package name
> and they've been loaded by the very same classloader.
>
> So, that might be the problem. Maybe with JUnit you get two classloaders too.

ok, but shouldn't the error then happen whenever I have a CHECKCAST,
because the class loading for this does not work? But I already produced
a variant containing he CHECKCAST and not giving the IllegalAccessError.

bye Jochen

Alessio Stalla

unread,
Oct 8, 2010, 9:14:41 AM10/8/10
to jvm-la...@googlegroups.com
On Fri, Oct 8, 2010 at 2:58 PM, Jochen Theodorou <blac...@gmx.org> wrote:
> Alessio Stalla wrote:
> [...]
>>
>> I interpret that to mean that two classes at runtime are considered to
>> be in the same package if and only if they have the same package name
>> and they've been loaded by the very same classloader.
>>
>> So, that might be the problem. Maybe with JUnit you get two classloaders
>> too.
>
> ok, but shouldn't the error then happen whenever I have a CHECKCAST, because
> the class loading for this does not work? But I already produced a variant
> containing he CHECKCAST and not giving the IllegalAccessError.

I'm not an expert about that. Generally linking is done very lazily by
the JVM, so it might not occur if you CHECKCAST a null pointer. When
instead you have a live object, a CHECKCAST might count as an access
to the referenced class and trigger the illegal access error. I'm
guessing, mind you; let's see if someone who knows the JVM better has
something different to say.

Cheers,
Alessio

Jochen Theodorou

unread,
Oct 8, 2010, 9:47:54 AM10/8/10
to jvm-la...@googlegroups.com
Alessio Stalla wrote:
[...]

> I'm not an expert about that. Generally linking is done very lazily by
> the JVM, so it might not occur if you CHECKCAST a null pointer. When
> instead you have a live object, a CHECKCAST might count as an access
> to the referenced class and trigger the illegal access error. I'm
> guessing, mind you; let's see if someone who knows the JVM better has
> something different to say.

but why is the access illegal? The class might be package private, but
the calling class is in the same package and thus allowed to access that
class. If not the Java version of it would not even compile.

Alessio Stalla

unread,
Oct 8, 2010, 9:56:08 AM10/8/10
to jvm-la...@googlegroups.com
On Fri, Oct 8, 2010 at 3:47 PM, Jochen Theodorou <blac...@gmx.org> wrote:
> Alessio Stalla wrote:
> [...]
>>
>> I'm not an expert about that. Generally linking is done very lazily by
>> the JVM, so it might not occur if you CHECKCAST a null pointer. When
>> instead you have a live object, a CHECKCAST might count as an access
>> to the referenced class and trigger the illegal access error. I'm
>> guessing, mind you; let's see if someone who knows the JVM better has
>> something different to say.
>
> but why is the access illegal? The class might be package private, but the
> calling class is in the same package and thus allowed to access that class.
> If not the Java version of it would not even compile.

As I said at runtime "same package" means "same package name" AND
"same classloader" or at least this is my interpretation of the jvm
spec, so if you have two different classloaders loading your two
classes like you said, they're not considered to be in the same
package. In the Java version evidently there's only one classloader
involved.

Alessio

Alessio Stalla

unread,
Oct 8, 2010, 10:17:10 AM10/8/10
to jvm-la...@googlegroups.com

In fact, I think I managed to reproduce it. I have this structure:

<default package>/
Main2.java
pkg/
A.java
B.java

Main.java has the following main method:

------------------------
public static void main(String[] args) {
try {
ClassLoader a = new URLClassLoader(new URL[] { new
URL("file:///home/alessio/prova1/") });
System.out.println("a = " + a);
a.loadClass("pkg.A");
ClassLoader b = new URLClassLoader(new URL[] { new
URL("file:///home/alessio/prova2/") }, a);
System.out.println("b = " + b);
b.loadClass("pkg.B").getMethod("foo").invoke(null);
} catch(Exception e) {
e.printStackTrace();
}
}
--------------------------

A.java is

------------------
package pkg;

class A {
static { System.out.println("A loaded by " + A.class.getClassLoader()); }
public String aaa() { return "foo"; }
}
----------------

B.java is

------------------
package pkg;

public class B {
static { System.out.println("B loaded by " + B.class.getClassLoader()); }
public static void foo() {
Object o = new A();
System.out.println(((A) o).aaa());
}
}
------------------

I compile them, then move A.class to ~/prova1/pkg/ and B.class in
~/prova2/pkg/ and then I issue the command:

alessio@astalla-laptop:~$ java -cp workspace/prova/bin Main2
Main2 loaded by sun.misc.Launcher$AppClassLoader@13f5d07
a = java.net.URLClassLoader@1eed786
b = java.net.URLClassLoader@12dacd1
B loaded by java.net.URLClassLoader@12dacd1
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at Main2.main(Main2.java:13)
Caused by: java.lang.IllegalAccessError: tried to access class pkg.A
from class pkg.B
at pkg.B.foo(B.java:10)
... 5 more

Granted, this is not caused by a CHECKCAST, but the principle is the same.

Bye,
Alessio

Jochen Theodorou

unread,
Oct 8, 2010, 10:55:36 AM10/8/10
to jvm-la...@googlegroups.com
Alessio Stalla wrote:
[...]

> In fact, I think I managed to reproduce it. I have this structure:
>
> <default package>/
> Main2.java
> pkg/
> A.java
> B.java
>
> Main.java has the following main method:
>
> ------------------------
> public static void main(String[] args) {
> try {
> ClassLoader a = new URLClassLoader(new URL[] { new
> URL("file:///home/alessio/prova1/") });
> System.out.println("a = " + a);
> a.loadClass("pkg.A");
> ClassLoader b = new URLClassLoader(new URL[] { new
> URL("file:///home/alessio/prova2/") }, a);
> System.out.println("b = " + b);
> b.loadClass("pkg.B").getMethod("foo").invoke(null);
> } catch(Exception e) {
> e.printStackTrace();
> }
> }

in the meantime I did something similar... and got the same result... so
it seems the issue is related to that. Looks like not having package
private in Groovy by default was an even better decision than I always
thought of.

thank you very much... it makes much more sense to me now.

Especially as I could verify that indeed two class loaders are involved
in the junit test case. So it seems that is the background to this
error. Thanks a lot.

Alessio Stalla

unread,
Oct 8, 2010, 11:10:06 AM10/8/10
to jvm-la...@googlegroups.com

No problem. It has been a nice issue to think about :)

In general, I have found that some of the abstractions introduced by
javac leak or fall apart in certain corner cases, that are a lot more
common when dynamic languages are involved.

Peace,
Alessio

Reply all
Reply to author
Forward
0 new messages