match Java's schizophrenic concept of inner class access modifiers

An inner class has two sets of modifier flags: one is declared in the
usual place in the class file and the other is part of the
InnerClasses attribute.  Not only is that redundant, but they can
contradict, and the VM can't just pick one and roll with it.  Instead,
Class.getModifiers must return the InnerClasses version, whereas
reflection must check the top-level version.  So even if
Class.getModifiers says the class is protected, it might still be
public for the purpose of reflection depending on what the
InnerClasses attribute says.  Crazy?  Yes.
This commit is contained in:
Joel Dice 2014-03-06 15:01:44 -07:00
parent 6ed98b85f8
commit 25d69f38ee
7 changed files with 69 additions and 13 deletions

View File

@ -30,6 +30,7 @@ import java.lang.annotation.Annotation;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
@ -439,6 +440,19 @@ public final class Class <T> implements Type, AnnotatedElement {
}
public int getModifiers() {
ClassAddendum addendum = vmClass.addendum;
if (addendum != null) {
InnerClassReference[] table = addendum.innerClassTable;
if (table != null) {
for (int i = 0; i < table.length; ++i) {
InnerClassReference reference = table[i];
if (Arrays.equals(vmClass.name, reference.inner)) {
return reference.flags;
}
}
}
}
return vmClass.flags;
}

View File

@ -746,6 +746,28 @@ getDeclaringClass(Thread* t, object c)
return 0;
}
unsigned
classModifiers(Thread* t, object c)
{
object addendum = classAddendum(t, c);
if (addendum) {
object table = classAddendumInnerClassTable(t, addendum);
if (table) {
for (unsigned i = 0; i < arrayLength(t, table); ++i) {
object reference = arrayBody(t, table, i);
if (0 == strcmp
(&byteArrayBody(t, className(t, c), 0),
&byteArrayBody(t, innerClassReferenceInner(t, reference), 0)))
{
return innerClassReferenceFlags(t, reference);
}
}
}
}
return classFlags(t, c);
}
} // namespace vm
#endif//CLASSPATH_COMMON_H

View File

@ -1634,7 +1634,7 @@ extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_Class_getModifiers
(Thread* t, object, uintptr_t* arguments)
{
return classFlags
return classModifiers
(t, jclassVmClass(t, reinterpret_cast<object>(arguments[0])));
}

View File

@ -4123,12 +4123,19 @@ EXPORT(JVM_GetComponentType)(Thread* t, jclass c)
return reinterpret_cast<jclass>(run(t, jvmGetComponentType, arguments));
}
uint64_t
jvmGetClassModifiers(Thread* t, uintptr_t* arguments)
{
return classModifiers
(t, jclassVmClass(t, *reinterpret_cast<jobject>(arguments[0])));
}
extern "C" AVIAN_EXPORT jint JNICALL
EXPORT(JVM_GetClassModifiers)(Thread* t, jclass c)
{
ENTER(t, Thread::ActiveState);
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(c) };
return classFlags(t, jclassVmClass(t, *c));
return run(t, jvmGetClassModifiers, arguments);
}
uint64_t
@ -4349,7 +4356,9 @@ EXPORT(JVM_GetClassDeclaredConstructors)(Thread* t, jclass c,
extern "C" AVIAN_EXPORT jint JNICALL
EXPORT(JVM_GetClassAccessFlags)(Thread* t, jclass c)
{
return EXPORT(JVM_GetClassModifiers)(t, c);
ENTER(t, Thread::ActiveState);
return classFlags(t, jclassVmClass(t, *c));
}
uint64_t

View File

@ -2314,13 +2314,6 @@ parseAttributeTable(Thread* t, Stream& s, object class_, object pool)
flags);
set(t, table, ArrayBody + (i * BytesPerWord), reference);
if (0 == strcmp
(&byteArrayBody(t, className(t, class_), 0),
&byteArrayBody(t, innerClassReferenceInner(t, reference), 0)))
{
classFlags(t, class_) = flags;
}
}
object addendum = getClassAddendum(t, class_, pool);

View File

@ -49,8 +49,9 @@ public class Reflection {
private static void innerClasses() throws Exception {
Class c = Reflection.class;
Class[] inner = c.getDeclaredClasses();
expect(1 == inner.length);
expect(Hello.class == inner[0]);
expect(2 == inner.length);
expect(Hello.class == inner[0]
|| Hello.class == inner[1]);
}
private int egads;
@ -236,6 +237,14 @@ public class Reflection {
expect((Foo.class.getMethod("toString").getModifiers()
& Modifier.PUBLIC) != 0);
expect(avian.TestReflection.get(Baz.class.getField("foo"), new Baz())
.equals(42));
expect((Baz.class.getModifiers() & Modifier.PUBLIC) == 0);
}
protected static class Baz {
public int foo = 42;
}
}

View File

@ -0,0 +1,9 @@
package avian;
import java.lang.reflect.Field;
public class TestReflection {
public static Object get(Field field, Object target) throws Exception {
return field.get(target);
}
}