From 7de555c7975383b4f3c53f373b6f59e444f7a7e0 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 23 Apr 2014 15:22:10 -0600 Subject: [PATCH] add support for using the OpenJDK 8 class library This ensures that all tests pass when Avian is built with an openjdk=$path option such that $path points to either OpenJDK 7 or 8. Note that I have not yet tried using the openjdk-src option with OpenJDK 8. I'll work on that next. --- openjdk.ld | 5 ++ src/avian/classpath-common.h | 4 +- src/avian/constants.h | 17 +++--- src/avian/machine.h | 31 ++++++++++- src/classpath-openjdk.cpp | 89 +++++++++++++++++++++++++++++- src/compile.cpp | 5 +- src/machine.cpp | 91 +++++++++++++++++++++++++++++-- src/tools/type-generator/main.cpp | 7 ++- src/types.def | 11 ++++ test/Regex.java | 8 ++- test/Strings.java | 31 ++++++++--- 11 files changed, 271 insertions(+), 28 deletions(-) diff --git a/openjdk.ld b/openjdk.ld index fdad025d57..ac767806e5 100644 --- a/openjdk.ld +++ b/openjdk.ld @@ -74,6 +74,7 @@ SUNWprivate_1.1 { JVM_CurrentTimeMillis; JVM_DefineClass; JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; JVM_DesiredAssertionStatus; JVM_DisableCompiler; JVM_DoPrivileged; @@ -127,12 +128,14 @@ SUNWprivate_1.1 { JVM_GetClassNameUTF; JVM_GetClassSignature; JVM_GetClassSigners; + JVM_GetClassTypeAnnotations; JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; JVM_GetFieldAnnotations; JVM_GetFieldIxModifiers; + JVM_GetFieldTypeAnnotations; JVM_GetHostName; JVM_GetInheritedAccessControlContext; JVM_GetInterfaceVersion; @@ -185,6 +188,7 @@ SUNWprivate_1.1 { JVM_IsSilentCompiler; JVM_IsSupportedJNIVersion; JVM_IsThreadAlive; + JVM_IsVMGeneratedMethodIx; JVM_LatestUserDefinedLoader; JVM_Listen; JVM_LoadClass0; @@ -220,6 +224,7 @@ SUNWprivate_1.1 { JVM_SetArrayElement; JVM_SetClassSigners; JVM_SetLength; + JVM_SetNativeThreadName; JVM_SetPrimitiveArrayElement; JVM_SetProtectionDomain; JVM_SetSockOpt; diff --git a/src/avian/classpath-common.h b/src/avian/classpath-common.h index b41734e006..8fe7c39e78 100644 --- a/src/avian/classpath-common.h +++ b/src/avian/classpath-common.h @@ -239,8 +239,8 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName, runOnLoadIfFound(t, lib); } } else if (throw_) { - throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s", - name); + throwNew(t, Machine::UnsatisfiedLinkErrorType, + "library not found in %s: %s", path, name); } return lib; diff --git a/src/avian/constants.h b/src/avian/constants.h index c6f88c2b34..cd1ec7ee1e 100644 --- a/src/avian/constants.h +++ b/src/avian/constants.h @@ -233,15 +233,18 @@ enum TypeCode { enum Constant { CONSTANT_Class = 7, - CONSTANT_Fieldref = 9, - CONSTANT_Methodref = 10, - CONSTANT_InterfaceMethodref = 11, - CONSTANT_String = 8, - CONSTANT_Integer = 3, - CONSTANT_Float = 4, - CONSTANT_Long = 5, CONSTANT_Double = 6, + CONSTANT_Fieldref = 9, + CONSTANT_Float = 4, + CONSTANT_Integer = 3, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_InvokeDynamic = 18, + CONSTANT_Long = 5, + CONSTANT_MethodHandle = 15, + CONSTANT_MethodType = 16, + CONSTANT_Methodref = 10, CONSTANT_NameAndType = 12, + CONSTANT_String = 8, CONSTANT_Utf8 = 1 }; diff --git a/src/avian/machine.h b/src/avian/machine.h index 5197185b71..62427b5144 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -2197,6 +2197,16 @@ stringOffset(Thread*, object) return 0; } +# ifndef HAVE_StringHash32 + +inline object +makeString(Thread* t, object data, int32_t hash, int32_t) +{ + return makeString(t, data, hash); +} + +# endif // not HAVE_StringHash32 + inline object makeString(Thread* t, object data, unsigned offset, unsigned length, unsigned) { @@ -2476,16 +2486,33 @@ fieldSize(Thread* t, object field) } inline void -scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount, +scanMethodSpec(Thread* t, const char* s, bool static_, + unsigned* parameterCount, unsigned* parameterFootprint, unsigned* returnCode) { unsigned count = 0; + unsigned footprint = 0; MethodSpecIterator it(t, s); - for (; it.hasNext(); it.next()) { + while (it.hasNext()) { ++ count; + switch (*it.next()) { + case 'J': + case 'D': + footprint += 2; + break; + + default: + ++ footprint; + break; + } + } + + if (not static_) { + ++ footprint; } *parameterCount = count; + *parameterFootprint = footprint; *returnCode = fieldCode(t, *it.returnSpec()); } diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index cb03ec8645..e38ff814cf 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -2136,6 +2136,58 @@ countConstructors(Thread* t, object c, bool publicOnly) return count; } +#ifdef HAVE_JexecutableHasRealParameterData +object +makeJmethod(Thread* t, + uint8_t override, + object securityCheckCache, + object clazz, + uint32_t slot, + object name, + object returnType, + object parameterTypes, + object exceptionTypes, + uint32_t modifiers, + object signature, + object genericInfo, + object annotations, + object parameterAnnotations, + object annotationDefault, + object methodAccessor, + object root, + object declaredAnnotations) +{ + return makeJmethod + (t, override, securityCheckCache, 0, 0, declaredAnnotations, clazz, slot, + name, returnType, parameterTypes, exceptionTypes, modifiers, signature, + genericInfo, annotations, parameterAnnotations, annotationDefault, + methodAccessor, root); +} + +object +makeJconstructor(Thread* t, + uint8_t override, + object securityCheckCache, + object clazz, + uint32_t slot, + object parameterTypes, + object exceptionTypes, + uint32_t modifiers, + object signature, + object genericInfo, + object annotations, + object parameterAnnotations, + object constructorAccessor, + object root, + object declaredAnnotations) +{ + return makeJconstructor + (t, override, securityCheckCache, 0, 0, declaredAnnotations, clazz, slot, + parameterTypes, exceptionTypes, modifiers, signature, genericInfo, + annotations, parameterAnnotations, constructorAccessor, root); +} +#endif // HAVE_JexecutableHasRealParameterData + object makeJmethod(Thread* t, object vmMethod, int index) { @@ -3081,7 +3133,13 @@ EXPORT(JVM_FindLibraryEntry)(void* library, const char* name) library = t->m->libraries; } - return static_cast(library)->resolve(name); + for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { + if (library == lib) { + return lib->resolve(name); + } + } + + return 0; } extern "C" AVIAN_EXPORT jboolean JNICALL @@ -3882,6 +3940,14 @@ EXPORT(JVM_DefineClassWithSource)(Thread* t, const char*, jobject loader, return EXPORT(JVM_DefineClass)(t, 0, loader, data, length, 0); } +extern "C" AVIAN_EXPORT jclass JNICALL +EXPORT(JVM_DefineClassWithSourceCond)(Thread* t, const char*, jobject loader, + const uint8_t* data, jsize length, + jobject, const char*, jboolean) +{ + return EXPORT(JVM_DefineClass)(t, 0, loader, data, length, 0); +} + extern "C" AVIAN_EXPORT jstring JNICALL EXPORT(JVM_GetClassName)(Thread* t, jclass c) { @@ -3972,6 +4038,24 @@ EXPORT(JVM_GetClassSigners)(Thread* t, jclass c) (t, classRuntimeDataSigners(t, runtimeData)) : 0; } +extern "C" AVIAN_EXPORT jbyteArray JNICALL +EXPORT(JVM_GetClassTypeAnnotations)(Thread*, jclass) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL +EXPORT(JVM_GetFieldTypeAnnotations)(Thread*, jobject) +{ + abort(); +} + +extern "C" AVIAN_EXPORT jbyteArray JNICALL +EXPORT(JVM_GetMethodTypeAnnotations)(Thread*, jobject) +{ + abort(); +} + extern "C" AVIAN_EXPORT void JNICALL EXPORT(JVM_SetClassSigners)(Thread* t, jclass c, jobjectArray signers) { @@ -4653,6 +4737,9 @@ EXPORT(JVM_GetMethodIxMaxStack)(Thread*, jclass, int) { abort(); } extern "C" AVIAN_EXPORT jboolean JNICALL EXPORT(JVM_IsConstructorIx)(Thread*, jclass, int) { abort(); } +extern "C" AVIAN_EXPORT jboolean JNICALL +EXPORT(JVM_IsVMGeneratedMethodIx)(Thread*, jclass, int) { abort(); } + extern "C" AVIAN_EXPORT const char* JNICALL EXPORT(JVM_GetMethodIxNameUTF)(Thread*, jclass, jint) { abort(); } diff --git a/src/compile.cpp b/src/compile.cpp index 0a62a57483..68c013ffdd 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -3296,11 +3296,12 @@ int methodReferenceReturnCode(Thread* t, object reference) { unsigned parameterCount; + unsigned parameterFootprint; unsigned returnCode; scanMethodSpec (t, reinterpret_cast - (&byteArrayBody(t, referenceSpec(t, reference), 0)), ¶meterCount, - &returnCode); + (&byteArrayBody(t, referenceSpec(t, reference), 0)), true, + ¶meterCount, ¶meterFootprint, &returnCode); return returnCode; } diff --git a/src/machine.cpp b/src/machine.cpp index 29f1a8a270..d350bfd4d4 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -945,7 +945,7 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) unsigned si = s.read2() - 1; parsePoolEntry(t, s, index, pool, si); - object value = makeReference(t, 0, singletonObject(t, pool, si), 0); + object value = makeReference(t, 0, 0, singletonObject(t, pool, si), 0); set(t, pool, SingletonBody + (i * BytesPerWord), value); if(DebugClassReader) { @@ -1004,7 +1004,7 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) object nameAndType = singletonObject(t, pool, nti); object value = makeReference - (t, class_, pairFirst(t, nameAndType), pairSecond(t, nameAndType)); + (t, 0, class_, pairFirst(t, nameAndType), pairSecond(t, nameAndType)); set(t, pool, SingletonBody + (i * BytesPerWord), value); if(DebugClassReader) { @@ -1013,6 +1013,68 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) } } return 1; + case CONSTANT_MethodHandle: + if (singletonObject(t, pool, i) == 0) { + unsigned kind = s.read1(); + unsigned ri = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ri); + + object value = singletonObject(t, pool, ri); + + if (DebugClassReader) { + fprintf(stderr, " consts[%d] = method handle %d %s.%s%s\n", i, kind, + &byteArrayBody(t, referenceClass(t, value), 0), + &byteArrayBody(t, referenceName(t, value), 0), + &byteArrayBody(t, referenceSpec(t, value), 0)); + } + + value = makeReference + (t, kind, referenceClass(t, value), referenceName(t, value), + referenceSpec(t, value)); + + set(t, pool, SingletonBody + (i * BytesPerWord), value); + } return 1; + + case CONSTANT_MethodType: + if (singletonObject(t, pool, i) == 0) { + unsigned ni = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, ni); + + set(t, pool, SingletonBody + (i * BytesPerWord), + singletonObject(t, pool, ni)); + } return 1; + + case CONSTANT_InvokeDynamic: + if (singletonObject(t, pool, i) == 0) { + unsigned bootstrap = s.read2(); + unsigned nti = s.read2() - 1; + + parsePoolEntry(t, s, index, pool, nti); + + object nameAndType = singletonObject(t, pool, nti); + + const char* specString = reinterpret_cast + (&byteArrayBody(t, pairSecond(t, nameAndType), 0)); + + unsigned parameterCount; + unsigned parameterFootprint; + unsigned returnCode; + scanMethodSpec + (t, specString, true, ¶meterCount, ¶meterFootprint, + &returnCode); + + object template_ = makeMethod + (t, 0, returnCode, parameterCount, parameterFootprint, 0, 0, 0, 0, + pairFirst(t, nameAndType), pairSecond(t, nameAndType), 0, 0, 0); + + object value = makeInvocation + (t, bootstrap, -1, 0, pool, template_, 0); + + set(t, pool, SingletonBody + (i * BytesPerWord), value); + } return 1; + default: abort(t); } } @@ -1078,6 +1140,21 @@ parsePool(Thread* t, Stream& s) s.skip(s.read2()); break; + case CONSTANT_MethodHandle: + singletonMarkObject(t, pool, i); + s.skip(3); + break; + + case CONSTANT_MethodType: + singletonMarkObject(t, pool, i); + s.skip(2); + break; + + case CONSTANT_InvokeDynamic: + singletonMarkObject(t, pool, i); + s.skip(4); + break; + default: abort(t); } } @@ -2066,15 +2143,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) (&byteArrayBody(t, singletonObject(t, pool, spec - 1), 0)); unsigned parameterCount; + unsigned parameterFootprint; unsigned returnCode; - scanMethodSpec(t, specString, ¶meterCount, &returnCode); + scanMethodSpec(t, specString, flags & ACC_STATIC, ¶meterCount, + ¶meterFootprint, &returnCode); object method = t->m->processor->makeMethod (t, 0, // vm flags returnCode, parameterCount, - parameterFootprint(t, specString, flags & ACC_STATIC), + parameterFootprint, flags, 0, // offset singletonObject(t, pool, name - 1), @@ -5158,6 +5237,10 @@ parseUtf8(Thread* t, object array) object getCaller(Thread* t, unsigned target, bool skipMethodInvoke) { + if (static_cast(target) == -1) { + target = 2; + } + class Visitor: public Processor::StackVisitor { public: Visitor(Thread* t, unsigned target, bool skipMethodInvoke): diff --git a/src/tools/type-generator/main.cpp b/src/tools/type-generator/main.cpp index 887b5117e9..ae4bb3349a 100644 --- a/src/tools/type-generator/main.cpp +++ b/src/tools/type-generator/main.cpp @@ -1061,6 +1061,7 @@ parseType(Finder* finder, Object::ObjectType type, Object* p, } } client; System::Region* region = finder->find(append(javaName, ".class")); + if (region == 0) return 0; Stream s(&client, region->start(), region->length()); parseJavaClass(t, &s, declarations); region->dispose(); @@ -1109,8 +1110,10 @@ parse(Finder* finder, Input* in) Object* o; while ((o = read(in, eos, 0)) != eos) { - declarations.append - (parseDeclaration(finder, o, declarations.first)); + Object* declaration = parseDeclaration(finder, o, declarations.first); + if (declaration) { + declarations.append(declaration); + } } return declarations.first; diff --git a/src/types.def b/src/types.def index 82b0e5a5aa..0b6b63f5ab 100644 --- a/src/types.def +++ b/src/types.def @@ -8,6 +8,8 @@ (type jaccessibleObject java/lang/reflect/AccessibleObject) +(type jexecutable java/lang/reflect/Executable) + (type jfield java/lang/reflect/Field) (type jmethod java/lang/reflect/Method) @@ -87,10 +89,19 @@ (array uint8_t body)) (type reference + (uint8_t kind) (object class) (object name) (object spec)) +(type invocation + (uint16_t bootstrap) + (int32_t index) + (object class) + (object pool) + (object template) + (object site)) + (type triple (object first) (object second) diff --git a/test/Regex.java b/test/Regex.java index f67413978c..1409c67e3e 100644 --- a/test/Regex.java +++ b/test/Regex.java @@ -85,7 +85,13 @@ public class Regex { expectNoMatch("[a-z&&[^d-f]]", "f"); expectSplit("^H", "Hello\nHobbes!", "", "ello\nHobbes!"); expectSplit("o.*?$", "Hello\r\nHobbes!", "Hello\r\nH"); - expectSplit("\\b", "a+ b + c\nd", "", "a", "+ ", "b", " + ", "c", "\n", "d"); + try { + expectSplit("\\b", "a+ b + c\nd", "", "a", "+ ", "b", " + ", "c", "\n", "d"); + } catch (RuntimeException e) { + // Java 8 changed the semantics of split, so if we're on 8, the + // above will fail and this will succeed: + expectSplit("\\b", "a+ b + c\nd", "a", "+ ", "b", " + ", "c", "\n", "d"); + } expectSplit("\\B", "Hi Cal!", "H", "i C", "a", "l!"); expectMatch("a{2,5}", "aaaa"); expectGroups("a??(a{2,5}?)", "aaaa", "aaaa"); diff --git a/test/Strings.java b/test/Strings.java index f37c99efaa..823b024c02 100644 --- a/test/Strings.java +++ b/test/Strings.java @@ -124,22 +124,39 @@ public class Strings { expect(months.split("\u00ae").length == 3); expect(months.replaceAll("\u00ae", ".").equals("Jan.Feb.Mar.")); + // Java 8 changed the semantics of String.split relative to + // previous versions, therefore we accept multiple possible + // results: expect(arraysEqual - ("xyz".split("", 0), new String[] { "", "x", "y", "z" })); + ("xyz".split("", 0), new String[] { "", "x", "y", "z" }) + || arraysEqual + ("xyz".split("", 0), new String[] { "x", "y", "z" })); expect(arraysEqual ("xyz".split("", 1), new String[] { "xyz" })); expect(arraysEqual - ("xyz".split("", 2), new String[] { "", "xyz" })); + ("xyz".split("", 2), new String[] { "", "xyz" }) + || arraysEqual + ("xyz".split("", 2), new String[] { "x", "yz" })); expect(arraysEqual - ("xyz".split("", 3), new String[] { "", "x", "yz" })); + ("xyz".split("", 3), new String[] { "", "x", "yz" }) + || arraysEqual + ("xyz".split("", 3), new String[] { "x", "y", "z" })); expect(arraysEqual - ("xyz".split("", 4), new String[] { "", "x", "y", "z" })); + ("xyz".split("", 4), new String[] { "", "x", "y", "z" }) + || arraysEqual + ("xyz".split("", 4), new String[] { "x", "y", "z", "" })); expect(arraysEqual - ("xyz".split("", 5), new String[] { "", "x", "y", "z", "" })); + ("xyz".split("", 5), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", 5), new String[] { "x", "y", "z", "" })); expect(arraysEqual - ("xyz".split("", 6), new String[] { "", "x", "y", "z", "" })); + ("xyz".split("", 6), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", 6), new String[] { "x", "y", "z", "" })); expect(arraysEqual - ("xyz".split("", -1), new String[] { "", "x", "y", "z", "" })); + ("xyz".split("", -1), new String[] { "", "x", "y", "z", "" }) + || arraysEqual + ("xyz".split("", -1), new String[] { "x", "y", "z", "" })); expect(arraysEqual("".split("xyz", 0), new String[] { "" })); expect(arraysEqual("".split("xyz", 1), new String[] { "" }));