From 7056315c182e12607a3390710e9f80087b5562ef Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 6 Dec 2013 15:45:46 -0700 Subject: [PATCH] fix various Android test suite regressions and add more reflection tests Most of these regressions were simply due to testing a lot more stuff, esp. annotations and reflection, revealing holes in the Android compatibility code. There are still some holes, but at least the suite is passing (except for a fragile test in Serialize.java which I will open an issue for). Sorry this is such a big commit; there was more to address than I initially expected. --- classpath/avian/Classes.java | 47 ++ .../java/lang/annotation/Annotation.java | 1 + classpath/java/lang/reflect/Field.java | 47 +- classpath/java/lang/reflect/Proxy.java | 2 +- src/avian/classpath-common.h | 72 +++ src/classpath-android.cpp | 513 +++++++++++++++++- src/classpath-openjdk.cpp | 72 +-- src/machine.cpp | 67 ++- test/Reflection.java | 76 ++- 9 files changed, 769 insertions(+), 128 deletions(-) diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 8b14fd233f..552d954988 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -441,6 +441,39 @@ public class Classes { return array; } + public static int countFields(VMClass vmClass, boolean publicOnly) { + int count = 0; + if (vmClass.fieldTable != null) { + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if ((! publicOnly) + || ((vmClass.fieldTable[i].flags & Modifier.PUBLIC)) + != 0) + { + ++ count; + } + } + } + return count; + } + + public static Field[] getFields(VMClass vmClass, boolean publicOnly) { + Field[] array = new Field[countFields(vmClass, publicOnly)]; + if (vmClass.fieldTable != null) { + Classes.link(vmClass); + + int ai = 0; + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (((vmClass.fieldTable[i].flags & Modifier.PUBLIC) != 0) + || (! publicOnly)) + { + array[ai++] = makeField(SystemClassLoader.getClass(vmClass), i); + } + } + } + + return array; + } + public static Annotation getAnnotation(ClassLoader loader, Object[] a) { if (a[0] == null) { a[0] = Proxy.newProxyInstance @@ -469,7 +502,21 @@ public class Classes { } } + private static int index(VMMethod m) { + VMMethod[] table = m.class_.methodTable; + for (int i = 0; i < table.length; ++i) { + if (m == table[i]) return i; + } + throw new AssertionError(); + } + + public static Method makeMethod(VMMethod m) { + return makeMethod(SystemClassLoader.getClass(m.class_), index(m)); + } + public static native Method makeMethod(Class c, int slot); + + public static native Field makeField(Class c, int slot); private static native void acquireClassLock(); diff --git a/classpath/java/lang/annotation/Annotation.java b/classpath/java/lang/annotation/Annotation.java index b31aa87070..b10350890d 100644 --- a/classpath/java/lang/annotation/Annotation.java +++ b/classpath/java/lang/annotation/Annotation.java @@ -11,4 +11,5 @@ package java.lang.annotation; public interface Annotation { + Class annotationType(); } diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 5bb0bc6081..05512f108b 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -161,6 +161,40 @@ public class Field extends AccessibleObject { return ((Double) get(instance)).doubleValue(); } + private boolean matchType(Object value) { + switch (vmField.code) { + case ByteField: + return value instanceof Byte; + + case BooleanField: + return value instanceof Boolean; + + case CharField: + return value instanceof Character; + + case ShortField: + return value instanceof Short; + + case IntField: + return value instanceof Integer; + + case LongField: + return value instanceof Long; + + case FloatField: + return value instanceof Float; + + case DoubleField: + return value instanceof Double; + + case ObjectField: + return value == null || getType().isInstance(value); + + default: + throw new Error(); + } + } + public void set(Object instance, Object value) throws IllegalAccessException { @@ -173,6 +207,10 @@ public class Field extends AccessibleObject { throw new IllegalArgumentException(); } + if (! matchType(value)) { + throw new IllegalArgumentException(); + } + Classes.initialize(vmField.class_); switch (vmField.code) { @@ -212,14 +250,7 @@ public class Field extends AccessibleObject { break; case ObjectField: - if (value == null || getType().isInstance(value)) { - setObject(target, vmField.offset, value); - } else { - throw new IllegalArgumentException - ("needed " + getType() + ", got " - + value.getClass().getName() + - " when setting " + Class.getName(vmField.class_) + "." + getName()); - } + setObject(target, vmField.offset, value); break; default: diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java index 4160c6bdf3..8f5059f51e 100644 --- a/classpath/java/lang/reflect/Proxy.java +++ b/classpath/java/lang/reflect/Proxy.java @@ -384,7 +384,7 @@ public class Proxy { ConstantPool.addUtf8(pool, Classes.toString(m.spec)), makeInvokeCode(pool, name, m.spec, m.parameterCount, m.parameterFootprint, methodTable.size()))); - refs.add(new Method(m)); + refs.add(Classes.makeMethod(m)); } } } diff --git a/src/avian/classpath-common.h b/src/avian/classpath-common.h index b2c5fb7d45..c1bb129518 100644 --- a/src/avian/classpath-common.h +++ b/src/avian/classpath-common.h @@ -674,6 +674,78 @@ getFinder(Thread* t, const char* name, unsigned nameLength) return 0; } +object +getDeclaredClasses(Thread* t, object c, bool publicOnly) +{ + object addendum = classAddendum(t, c); + if (addendum) { + object table = classAddendumInnerClassTable(t, addendum); + if (table) { + PROTECT(t, table); + + unsigned count = 0; + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + object reference = arrayBody(t, table, i); + object outer = innerClassReferenceOuter(t, reference); + if (outer and byteArrayEqual(t, outer, className(t, c)) + and ((not publicOnly) + or (innerClassReferenceFlags(t, reference) & ACC_PUBLIC))) + { + ++ count; + } + } + + object result = makeObjectArray(t, type(t, Machine::JclassType), count); + PROTECT(t, result); + + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + object reference = arrayBody(t, table, i); + object outer = innerClassReferenceOuter(t, reference); + if (outer and byteArrayEqual(t, outer, className(t, c)) + and ((not publicOnly) + or (innerClassReferenceFlags(t, reference) & ACC_PUBLIC))) + { + object inner = getJClass + (t, resolveClass + (t, classLoader(t, c), + innerClassReferenceInner(t, arrayBody(t, table, i)))); + + -- count; + set(t, result, ArrayBody + (count * BytesPerWord), inner); + } + } + + return result; + } + } + + return makeObjectArray(t, type(t, Machine::JclassType), 0); +} + +object +getDeclaringClass(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 (strcmp + (&byteArrayBody(t, innerClassReferenceInner(t, reference), 0), + &byteArrayBody(t, className(t, c), 0)) == 0) + { + return getJClass + (t, resolveClass + (t, classLoader(t, c), innerClassReferenceOuter(t, reference))); + } + } + } + } + + return 0; +} + } // namespace vm #endif//CLASSPATH_COMMON_H diff --git a/src/classpath-android.cpp b/src/classpath-android.cpp index 392f1052f5..59d7bf3456 100644 --- a/src/classpath-android.cpp +++ b/src/classpath-android.cpp @@ -142,11 +142,12 @@ makeField(Thread* t, object c, unsigned index) PROTECT(t, field); - object type = resolveClassBySpec - (t, classLoader(t, fieldClass(t, field)), - reinterpret_cast - (&byteArrayBody(t, fieldSpec(t, field), 0)), - byteArrayLength(t, fieldSpec(t, field)) - 1); + object type = getJClass + (t, resolveClassBySpec + (t, classLoader(t, fieldClass(t, field)), + reinterpret_cast + (&byteArrayBody(t, fieldSpec(t, field), 0)), + byteArrayLength(t, fieldSpec(t, field)) - 1)); PROTECT(t, type); object name = t->m->classpath->makeString @@ -604,6 +605,177 @@ closeMemoryMappedFile(Thread* t, object method, uintptr_t* arguments) file); } +bool +matchType(Thread* t, object field, object o) +{ + switch (fieldCode(t, field)) { + case ByteField: + return objectClass(t, o) == type(t, Machine::ByteType); + + case BooleanField: + return objectClass(t, o) == type(t, Machine::BooleanType); + + case CharField: + return objectClass(t, o) == type(t, Machine::CharType); + + case ShortField: + return objectClass(t, o) == type(t, Machine::ShortType); + + case IntField: + return objectClass(t, o) == type(t, Machine::IntType); + + case LongField: + return objectClass(t, o) == type(t, Machine::LongType); + + case FloatField: + return objectClass(t, o) == type(t, Machine::FloatType); + + case DoubleField: + return objectClass(t, o) == type(t, Machine::DoubleType); + + case ObjectField: + if (o == 0) { + return true; + } else { + PROTECT(t, o); + + object spec; + if (byteArrayBody(t, fieldSpec(t, field), 0) == '[') { + spec = fieldSpec(t, field);; + } else { + spec = makeByteArray(t, byteArrayLength(t, fieldSpec(t, field)) - 2); + + memcpy(&byteArrayBody(t, spec, 0), + &byteArrayBody(t, fieldSpec(t, field), 1), + byteArrayLength(t, fieldSpec(t, field)) - 3); + + byteArrayBody + (t, spec, byteArrayLength(t, fieldSpec(t, field)) - 3) = 0; + } + + return instanceOf + (t, resolveClass(t, classLoader(t, fieldClass(t, field)), spec), o); + } + + default: abort(t); + } +} + +object +getField(Thread* t, object field, object instance) +{ + PROTECT(t, field); + PROTECT(t, instance); + + initClass(t, fieldClass(t, field)); + + object target; + if (fieldFlags(t, field) & ACC_STATIC) { + target = classStaticTable(t, fieldClass(t, field)); + } else if (instanceOf(t, fieldClass(t, field), instance)){ + target = instance; + } else { + throwNew(t, Machine::IllegalArgumentExceptionType); + } + + unsigned offset = fieldOffset(t, field); + switch (fieldCode(t, field)) { + case ByteField: + return makeByte(t, fieldAtOffset(target, offset)); + + case BooleanField: + return makeBoolean(t, fieldAtOffset(target, offset)); + + case CharField: + return makeChar(t, fieldAtOffset(target, offset)); + + case ShortField: + return makeShort(t, fieldAtOffset(target, offset)); + + case IntField: + return makeInt(t, fieldAtOffset(target, offset)); + + case LongField: + return makeLong(t, fieldAtOffset(target, offset)); + + case FloatField: + return makeFloat(t, fieldAtOffset(target, offset)); + + case DoubleField: + return makeDouble(t, fieldAtOffset(target, offset)); + + case ObjectField: + return fieldAtOffset(target, offset); + + default: abort(t); + } +} + +void +setField(Thread* t, object field, object instance, object value) +{ + PROTECT(t, field); + PROTECT(t, instance); + PROTECT(t, value); + + if (not matchType(t, field, value)) { + throwNew(t, Machine::IllegalArgumentExceptionType); + } + + object target; + if ((fieldFlags(t, field) & ACC_STATIC) != 0) { + target = classStaticTable(t, fieldClass(t, field)); + } else if (instanceOf(t, fieldClass(t, field), instance)){ + target = instance; + } else { + throwNew(t, Machine::IllegalArgumentExceptionType); + } + PROTECT(t, target); + + initClass(t, fieldClass(t, field)); + + unsigned offset = fieldOffset(t, field); + switch (fieldCode(t, field)) { + case ByteField: + fieldAtOffset(target, offset) = byteValue(t, value); + break; + + case BooleanField: + fieldAtOffset(target, offset) = booleanValue(t, value); + break; + + case CharField: + fieldAtOffset(target, offset) = charValue(t, value); + break; + + case ShortField: + fieldAtOffset(target, offset) = shortValue(t, value); + break; + + case IntField: + fieldAtOffset(target, offset) = intValue(t, value); + break; + + case LongField: + fieldAtOffset(target, offset) = longValue(t, value); + break; + + case FloatField: + fieldAtOffset(target, offset) = floatValue(t, value); + break; + + case DoubleField: + fieldAtOffset(target, offset) = doubleValue(t, value); + break; + + case ObjectField: + set(t, target, offset, value); + break; + + default: abort(t); + } +} + } // namespace local } // namespace @@ -788,6 +960,14 @@ Avian_java_lang_String_length return stringLength(t, reinterpret_cast(arguments[0])); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_String_intern +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (intern(t, reinterpret_cast(arguments[0]))); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_String_charAt (Thread* t, object, uintptr_t* arguments) @@ -849,6 +1029,63 @@ Avian_java_lang_Class_getInterfaces (makeObjectArray(t, type(t, Machine::JclassType), 0)); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_getDeclaredClasses +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (getDeclaredClasses + (t, jclassVmClass(t, reinterpret_cast(arguments[0])), + arguments[1])); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_getDeclaringClass +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (getDeclaringClass + (t, jclassVmClass(t, reinterpret_cast(arguments[0])))); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_getEnclosingMethod +(Thread* t, object, uintptr_t* arguments) +{ + object c = jclassVmClass(t, reinterpret_cast(arguments[0])); + PROTECT(t, c); + + object addendum = classAddendum(t, c); + if (addendum) { + object enclosingClass = classAddendumEnclosingClass(t, addendum); + if (enclosingClass) { + PROTECT(t, enclosingClass); + + enclosingClass = getJClass + (t, resolveClass(t, classLoader(t, c), enclosingClass)); + + object enclosingMethod = classAddendumEnclosingMethod(t, addendum); + if (enclosingMethod) { + PROTECT(t, enclosingMethod); + + return reinterpret_cast + (t->m->classpath->makeJMethod + (t, findMethodInClass + (t, enclosingClass, pairFirst(t, enclosingMethod), + pairSecond(t, enclosingMethod)))); + } + } + } + return 0; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_getEnclosingConstructor +(Thread* t, object method, uintptr_t* arguments) +{ + return Avian_java_lang_Class_getEnclosingMethod(t, method, arguments); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_Class_newInstanceImpl (Thread* t, object, uintptr_t* arguments) @@ -1201,6 +1438,50 @@ Avian_dalvik_system_VMStack_getCallingClassLoader return reinterpret_cast(v.loader); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_dalvik_system_VMStack_getClasses +(Thread* t, object, uintptr_t*) +{ + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t): + t(t), array(0), counter(0) + { } + + virtual bool visit(Processor::StackWalker* walker) { + if (counter < 2) { + return true; + } else { + if (array == 0) { + array = makeObjectArray + (t, type(t, Machine::JclassType), walker->count()); + } + + object c = getJClass(t, methodClass(t, walker->method())); + + assert(t, counter - 2 < objectArrayLength(t, array)); + + set(t, array, ArrayBody + ((counter - 2) * BytesPerWord), c); + + return true; + } + + ++ counter; + } + + Thread* t; + object array; + unsigned counter; + } v(t); + + PROTECT(t, v.array); + + t->m->processor->walkStack(t, &v); + + return reinterpret_cast + (v.array ? v.array : makeObjectArray(t, type(t, Machine::JclassType), 0)); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_Math_min (Thread*, object, uintptr_t* arguments) @@ -1391,6 +1672,23 @@ Avian_java_lang_Class_isPrimitive & PrimitiveFlag) != 0; } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_isAnonymousClass +(Thread* t, object, uintptr_t* arguments) +{ + object name = className + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))); + + for (unsigned i = 0; i < byteArrayLength(t, name) - 1; ++i) { + int c = byteArrayBody(t, name, i); + if (c != '$' and (c < '0' or c > '9')) { + return false; + } + } + + return true; +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_Class_getClassLoader (Thread* t, object, uintptr_t* arguments) @@ -1446,6 +1744,23 @@ Avian_java_lang_Class_getDeclaredMethods (t->m->processor->invoke(t, get, 0, jclassVmClass(t, c), publicOnly)); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_Class_getDeclaredFields +(Thread* t, object, uintptr_t* arguments) +{ + object c = reinterpret_cast(arguments[0]); + PROTECT(t, c); + + bool publicOnly = arguments[1]; + + object get = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "getFields", + "(Lavian/VMClass;Z)[Ljava/lang/reflect/Field;"); + + return reinterpret_cast + (t->m->processor->invoke(t, get, 0, jclassVmClass(t, c), publicOnly)); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_reflect_Method_invokeNative (Thread* t, object, uintptr_t* arguments) @@ -1581,6 +1896,31 @@ Avian_java_lang_reflect_Method_getDeclaredAnnotations 0)); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_getDefaultValue +(Thread* t, object, uintptr_t* arguments) +{ + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[1]))), + arguments[2]); + + object addendum = methodAddendum(t, method); + if (addendum) { + object get = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", + "getAnnotationDefaultValue", + "(Ljava/lang/ClassLoader;Lavian/MethodAddendum;)" + "Ljava/lang/Object;"); + + return reinterpret_cast + (t->m->processor->invoke + (t, get, 0, classLoader(t, methodClass(t, method)), addendum)); + } + + return 0; +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_reflect_Constructor_constructNative (Thread* t, object, uintptr_t* arguments) @@ -1589,6 +1929,9 @@ Avian_java_lang_reflect_Constructor_constructNative PROTECT(t, args); object c = jclassVmClass(t, reinterpret_cast(arguments[2])); + PROTECT(t, c); + + initClass(t, c); object method = arrayBody(t, classMethodTable(t, c), arguments[4]); PROTECT(t, method); @@ -1609,16 +1952,84 @@ Avian_java_lang_reflect_Field_getField (t, classFieldTable (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), arguments[4]); + + PROTECT(t, field); - if (fieldFlags(t, field) & ACC_STATIC) { - return reinterpret_cast - (fieldAtOffset - (classStaticTable(t, fieldClass(t, field)), fieldOffset(t, field))); - } else { - return reinterpret_cast - (fieldAtOffset - (reinterpret_cast(arguments[1]), fieldOffset(t, field))); - } + object instance = reinterpret_cast(arguments[1]); + PROTECT(t, instance); + + return reinterpret_cast(local::getField(t, field, instance)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getIField +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), + arguments[4]); + + PROTECT(t, field); + + object instance = reinterpret_cast(arguments[1]); + PROTECT(t, instance); + + return intValue(t, local::getField(t, field, instance)); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getJField +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), + arguments[4]); + + PROTECT(t, field); + + object instance = reinterpret_cast(arguments[1]); + PROTECT(t, instance); + + return longValue(t, local::getField(t, field, instance)); +} + +extern "C" AVIAN_EXPORT void JNICALL +Avian_java_lang_reflect_Field_setField +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), + arguments[4]); + + PROTECT(t, field); + + object instance = reinterpret_cast(arguments[1]); + PROTECT(t, instance); + + object value = reinterpret_cast(arguments[6]); + PROTECT(t, value); + + local::setField(t, field, instance, value); +} + +extern "C" AVIAN_EXPORT void JNICALL +Avian_java_lang_reflect_Field_setIField +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), + arguments[4]); + + object instance = reinterpret_cast(arguments[1]); + PROTECT(t, instance); + + object value = makeInt(t, arguments[7]); + + local::setField(t, field, instance, value); } extern "C" AVIAN_EXPORT int64_t JNICALL @@ -1632,6 +2043,71 @@ Avian_java_lang_reflect_Field_getFieldModifiers arguments[2])); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getAnnotation +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1]); + + object addendum = fieldAddendum(t, field); + if (addendum) { + object table = addendumAnnotationTable(t, addendum); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + if (objectArrayBody(t, objectArrayBody(t, table, i), 1) + == reinterpret_cast(arguments[2])) + { + PROTECT(t, field); + PROTECT(t, table); + + object get = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "getAnnotation", + "(Ljava/lang/ClassLoader;[Ljava/lang/Object;)" + "Ljava/lang/annotation/Annotation;"); + + return reinterpret_cast + (t->m->processor->invoke + (t, get, 0, classLoader(t, fieldClass(t, field)), + objectArrayBody(t, table, i))); + } + } + } + } + + return false; +} + +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getSignatureAnnotation +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[1]))), + arguments[2]); + + object addendum = fieldAddendum(t, field); + if (addendum) { + object signature = addendumSignature(t, addendum); + if (signature) { + object array = makeObjectArray(t, 1); + PROTECT(t, array); + + object string = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + + set(t, array, ArrayBody, string); + + return reinterpret_cast(array); + } + } + + return reinterpret_cast(makeObjectArray(t, 0)); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_Throwable_nativeFillInStackTrace (Thread* t, object, uintptr_t*) @@ -1656,6 +2132,15 @@ Avian_avian_Classes_makeMethod (t, reinterpret_cast(arguments[0]), arguments[1])); } +extern "C" AVIAN_EXPORT int64_t JNICALL +Avian_avian_Classes_makeField +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (local::makeField + (t, reinterpret_cast(arguments[0]), arguments[1])); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_reflect_Array_createObjectArray (Thread* t, object, uintptr_t* arguments) diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 89ab018d31..f27bf88605 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -4297,49 +4297,10 @@ EXPORT(JVM_GetClassModifiers)(Thread* t, jclass c) uint64_t jvmGetDeclaredClasses(Thread* t, uintptr_t* arguments) { - jclass c = reinterpret_cast(arguments[0]); - - object addendum = classAddendum(t, jclassVmClass(t, *c)); - if (addendum) { - object table = classAddendumInnerClassTable(t, addendum); - if (table) { - PROTECT(t, table); - - unsigned count = 0; - for (unsigned i = 0; i < arrayLength(t, table); ++i) { - object outer = innerClassReferenceOuter(t, arrayBody(t, table, i)); - if (outer and byteArrayEqual - (t, outer, className(t, jclassVmClass(t, *c)))) - { - ++ count; - } - } - - object result = makeObjectArray(t, type(t, Machine::JclassType), count); - PROTECT(t, result); - - for (unsigned i = 0; i < arrayLength(t, table); ++i) { - object outer = innerClassReferenceOuter(t, arrayBody(t, table, i)); - if (outer and byteArrayEqual - (t, outer, className(t, jclassVmClass(t, *c)))) - { - object inner = getJClass - (t, resolveClass - (t, classLoader(t, jclassVmClass(t, *c)), - innerClassReferenceInner(t, arrayBody(t, table, i)))); - - -- count; - set(t, result, ArrayBody + (count * BytesPerWord), inner); - } - } - - return reinterpret_cast(makeLocalReference(t, result)); - } - } - return reinterpret_cast (makeLocalReference - (t, makeObjectArray(t, type(t, Machine::JclassType), 0))); + (t, getDeclaredClasses + (t, jclassVmClass(t, *reinterpret_cast(arguments[0])), false))); } extern "C" AVIAN_EXPORT jobjectArray JNICALL @@ -4353,31 +4314,10 @@ EXPORT(JVM_GetDeclaredClasses)(Thread* t, jclass c) uint64_t jvmGetDeclaringClass(Thread* t, uintptr_t* arguments) { - jclass c = reinterpret_cast(arguments[0]); - - object class_ = jclassVmClass(t, *c); - object addendum = classAddendum(t, class_); - 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 (strcmp - (&byteArrayBody(t, innerClassReferenceInner(t, reference), 0), - &byteArrayBody(t, className(t, class_), 0)) == 0) - { - return reinterpret_cast - (makeLocalReference - (t, getJClass - (t, resolveClass - (t, classLoader(t, class_), innerClassReferenceOuter - (t, reference))))); - } - } - } - } - - return 0; + return reinterpret_cast + (makeLocalReference + (t, getDeclaringClass + (t, jclassVmClass(t, *reinterpret_cast(arguments[0]))))); } extern "C" AVIAN_EXPORT jclass JNICALL diff --git a/src/machine.cpp b/src/machine.cpp index a338e9ac6b..f21d66e78d 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1947,9 +1947,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) object superVirtualTable = 0; PROTECT(t, superVirtualTable); - if (classFlags(t, class_) & ACC_INTERFACE) { - addInterfaceMethods(t, class_, virtualMap, &virtualCount, false); - } else { + if ((classFlags(t, class_) & ACC_INTERFACE) == 0) { if (classSuper(t, class_)) { superVirtualTable = classVirtualTable(t, classSuper(t, class_)); } @@ -2144,13 +2142,10 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) set(t, class_, ClassMethodTable, methodTable); } - object abstractVirtuals; - if (classFlags(t, class_) & ACC_INTERFACE) { - abstractVirtuals = 0; - } else { - abstractVirtuals = addInterfaceMethods - (t, class_, virtualMap, &virtualCount, true); - } + + object abstractVirtuals = addInterfaceMethods + (t, class_, virtualMap, &virtualCount, true); + PROTECT(t, abstractVirtuals); bool populateInterfaceVtables = false; @@ -2212,43 +2207,45 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) set(t, vtable, ArrayBody + (i * BytesPerWord), pairFirst(t, p)); ++ i; } + } - if (abstractVirtuals) { - PROTECT(t, vtable); + if (abstractVirtuals) { + PROTECT(t, vtable); - object addendum = getClassAddendum(t, class_, pool); - set(t, addendum, ClassAddendumMethodTable, - classMethodTable(t, class_)); + object addendum = getClassAddendum(t, class_, pool); + set(t, addendum, ClassAddendumMethodTable, + classMethodTable(t, class_)); - unsigned oldLength = classMethodTable(t, class_) ? - arrayLength(t, classMethodTable(t, class_)) : 0; + unsigned oldLength = classMethodTable(t, class_) ? + arrayLength(t, classMethodTable(t, class_)) : 0; - object newMethodTable = makeArray - (t, oldLength + listSize(t, abstractVirtuals)); + object newMethodTable = makeArray + (t, oldLength + listSize(t, abstractVirtuals)); - if (oldLength) { - memcpy(&arrayBody(t, newMethodTable, 0), - &arrayBody(t, classMethodTable(t, class_), 0), - oldLength * sizeof(object)); - } + if (oldLength) { + memcpy(&arrayBody(t, newMethodTable, 0), + &arrayBody(t, classMethodTable(t, class_), 0), + oldLength * sizeof(object)); + } - mark(t, newMethodTable, ArrayBody, oldLength); + mark(t, newMethodTable, ArrayBody, oldLength); - unsigned mti = oldLength; - for (object p = listFront(t, abstractVirtuals); - p; p = pairSecond(t, p)) - { - set(t, newMethodTable, - ArrayBody + ((mti++) * BytesPerWord), pairFirst(t, p)); + unsigned mti = oldLength; + for (object p = listFront(t, abstractVirtuals); + p; p = pairSecond(t, p)) + { + set(t, newMethodTable, + ArrayBody + ((mti++) * BytesPerWord), pairFirst(t, p)); + if ((classFlags(t, class_) & ACC_INTERFACE) == 0) { set(t, vtable, ArrayBody + ((i++) * BytesPerWord), pairFirst(t, p)); } - - assert(t, arrayLength(t, newMethodTable) == mti); - - set(t, class_, ClassMethodTable, newMethodTable); } + + assert(t, arrayLength(t, newMethodTable) == mti); + + set(t, class_, ClassMethodTable, newMethodTable); } assert(t, arrayLength(t, vtable) == i); diff --git a/test/Reflection.java b/test/Reflection.java index cd75f68200..3afe5316b5 100644 --- a/test/Reflection.java +++ b/test/Reflection.java @@ -59,6 +59,10 @@ public class Reflection { expect(egads.getAnnotation(Deprecated.class) == null); } + private Integer[] array; + + private Integer integer; + public static Hello>.World> pinky; private static void genericType() throws Exception { @@ -131,10 +135,58 @@ public class Reflection { expect(7.0 == (Double) Reflection.class.getMethod ("doubleMethod").invoke(null)); - Class[][] array = new Class[][] { { Class.class } }; - expect("[Ljava.lang.Class;".equals(array[0].getClass().getName())); - expect(Class[].class == array[0].getClass()); - expect(array.getClass().getComponentType() == array[0].getClass()); + { Class[][] array = new Class[][] { { Class.class } }; + expect("[Ljava.lang.Class;".equals(array[0].getClass().getName())); + expect(Class[].class == array[0].getClass()); + expect(array.getClass().getComponentType() == array[0].getClass()); + } + + { Reflection r = new Reflection(); + expect(r.egads == 0); + + Reflection.class.getDeclaredField("egads").set(r, 42); + expect(((int) Reflection.class.getDeclaredField("egads").get(r)) == 42); + + Reflection.class.getDeclaredField("egads").setInt(r, 43); + expect(Reflection.class.getDeclaredField("egads").getInt(r) == 43); + + Integer[] array = new Integer[0]; + Reflection.class.getDeclaredField("array").set(r, array); + expect(Reflection.class.getDeclaredField("array").get(r) == array); + + try { + Reflection.class.getDeclaredField("array").set(r, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + Integer integer = 45; + Reflection.class.getDeclaredField("integer").set(r, integer); + expect(Reflection.class.getDeclaredField("integer").get(r) == integer); + + try { + Reflection.class.getDeclaredField("integer").set(r, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + try { + Reflection.class.getDeclaredField("integer").set + (new Object(), integer); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + + try { + Reflection.class.getDeclaredField("integer").get(new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } + } try { Foo.class.getMethod("foo").invoke(null); @@ -157,6 +209,22 @@ public class Reflection { // cool } + try { + Foo.class.getField("foo").set(null, 42); + expect(false); + } catch (NoClassDefFoundError e) { + // cool + } + + try { + Foo.class.getField("foo").set(null, new Object()); + expect(false); + } catch (IllegalArgumentException e) { + // cool + } catch (NoClassDefFoundError e) { + // cool + } + { Method m = Reflection.class.getMethod("throwOOME"); try { m.invoke(null);