From 2a43e68c16e4d991442ce4334b87b599a1539cf4 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 12 Jul 2014 16:03:11 -0600 Subject: [PATCH] fix all the bugs So there I was, planning to just fix one little bug: Thread.holdsLock and Thread.yield were missing for the Android class library. Easy enough, right? So, I added a test, got it passing, and figured I'd go ahead and run ci.sh with all three class libraries. Big mistake. Here's the stuff I found: * minor inconsistency in README.md about OpenSSL version * untested, broken Class.getEnclosingMethod (reported by Josh) * JNI test failed for tails=true Android build * Runtime.nativeExit missing for Android build * obsolete assertion in CallEvent broke tails=true Android build * obsolete superclass field offset padding broke bootimage=true Android build * runtime annotation parsing broke bootimage=true Android build (because we couldn't modify Addendum.annotationTable for classes in the heap image) * ci.sh tried building with both android=... and openjdk=..., which the makefile rightfully balked at Sorry this is all in a single commit; I didn't expect so many unrelated issues, and I'm too lazy to break them apart. --- README.md | 2 +- classpath/avian/ClassAddendum.java | 6 +- classpath/avian/Pair.java | 5 ++ classpath/java/lang/Class.java | 6 ++ classpath/java/lang/reflect/Method.java | 4 + makefile | 8 +- src/avian/classpath-common.h | 21 ----- src/avian/machine.h | 2 + src/builtin.cpp | 54 +++++++++++ src/classpath-android.cpp | 115 +++++++++++------------- src/classpath-avian.cpp | 9 ++ src/codegen/compiler/event.cpp | 2 - src/machine.cpp | 30 ++++++- src/tools/bootimage-generator/main.cpp | 48 ++++------ src/tools/type-generator/main.cpp | 9 -- src/types.def | 2 +- test/Reflection.java | 5 ++ test/Threads.java | 45 +++++++--- test/ci.sh | 6 +- 19 files changed, 228 insertions(+), 151 deletions(-) create mode 100644 classpath/avian/Pair.java diff --git a/README.md b/README.md index baf68ce626..78515e4eb0 100644 --- a/README.md +++ b/README.md @@ -432,7 +432,7 @@ Also note that we use the upstream OpenSSL repository and apply the Android patches to it. This is because it is not clear how to build the Android fork of OpenSSL directly without checking out and building the entire platform. As of this writing, the patches apply cleanly -against OpenSSL 1.0.1e, so that's the tag we check out, but this may +against OpenSSL 1.0.1h, so that's the tag we check out, but this may change in the future when the Android fork rebases against a new OpenSSL version. diff --git a/classpath/avian/ClassAddendum.java b/classpath/avian/ClassAddendum.java index 30ef27249a..d9605eb7b9 100644 --- a/classpath/avian/ClassAddendum.java +++ b/classpath/avian/ClassAddendum.java @@ -22,9 +22,7 @@ public class ClassAddendum extends Addendum { */ public int declaredMethodCount; - // Either a byte[] or a Pair, apparently... - // TODO: make it monomorphic - public Object enclosingClass; + public byte[] enclosingClass; - public Object enclosingMethod; + public Pair enclosingMethod; } diff --git a/classpath/avian/Pair.java b/classpath/avian/Pair.java new file mode 100644 index 0000000000..dff3313542 --- /dev/null +++ b/classpath/avian/Pair.java @@ -0,0 +1,5 @@ +package avian; + +abstract class Pair { + // VM-visible fields in types.def +} diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 53879ccf7e..ef38e6ec66 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -392,6 +392,12 @@ public final class Class implements Type, AnnotatedElement { } } + public native Class getEnclosingClass(); + + public native Method getEnclosingMethod(); + + public native Constructor getEnclosingConstructor(); + public T[] getEnumConstants() { if (Enum.class.isAssignableFrom(this)) { try { diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 85e4dce6a3..6a8e8e189d 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -25,6 +25,10 @@ public class Method extends AccessibleObject implements Member { this.vmMethod = vmMethod; } + public boolean equals(Object o) { + return o instanceof Method && ((Method) o).vmMethod == vmMethod; + } + public boolean isAccessible() { return accessible; } diff --git a/makefile b/makefile index 734c03a2c8..1fb2258439 100755 --- a/makefile +++ b/makefile @@ -1,7 +1,7 @@ MAKEFLAGS = -s name = avian -version = 1.0.1 +version = 1.0.2 build-arch := $(shell uname -m \ | sed 's/^i.86$$/i386/' \ @@ -1355,21 +1355,22 @@ ifneq ($(classpath),avian) # them to synthesize a class: classpath-sources := \ $(classpath-src)/avian/Addendum.java \ - $(classpath-src)/avian/Code.java \ $(classpath-src)/avian/AnnotationInvocationHandler.java \ $(classpath-src)/avian/Assembler.java \ $(classpath-src)/avian/Callback.java \ $(classpath-src)/avian/Cell.java \ $(classpath-src)/avian/ClassAddendum.java \ - $(classpath-src)/avian/InnerClassReference.java \ $(classpath-src)/avian/Classes.java \ + $(classpath-src)/avian/Code.java \ $(classpath-src)/avian/ConstantPool.java \ $(classpath-src)/avian/Continuations.java \ $(classpath-src)/avian/FieldAddendum.java \ $(classpath-src)/avian/Function.java \ $(classpath-src)/avian/IncompatibleContinuationException.java \ + $(classpath-src)/avian/InnerClassReference.java \ $(classpath-src)/avian/Machine.java \ $(classpath-src)/avian/MethodAddendum.java \ + $(classpath-src)/avian/Pair.java \ $(classpath-src)/avian/Singleton.java \ $(classpath-src)/avian/Stream.java \ $(classpath-src)/avian/SystemClassLoader.java \ @@ -1919,6 +1920,7 @@ $(bootimage-generator): $(bootimage-generator-objects) $(vm-objects) armv6=$(armv6) \ platform=$(bootimage-platform) \ target-format=$(target-format) \ + android=$(android) \ openjdk=$(openjdk) \ openjdk-src=$(openjdk-src) \ bootimage-generator= \ diff --git a/src/avian/classpath-common.h b/src/avian/classpath-common.h index 9f625cbd3a..83d62d6eae 100644 --- a/src/avian/classpath-common.h +++ b/src/avian/classpath-common.h @@ -760,27 +760,6 @@ object getDeclaredClasses(Thread* t, GcClass* c, bool publicOnly) return makeObjectArray(t, type(t, GcJclass::Type), 0); } -GcJclass* getDeclaringClass(Thread* t, GcClass* c) -{ - GcClassAddendum* addendum = c->addendum(); - if (addendum) { - GcArray* table = cast(t, addendum->innerClassTable()); - if (table) { - for (unsigned i = 0; i < table->length(); ++i) { - GcInnerClassReference* reference - = cast(t, table->body()[i]); - if (reference->outer() - and strcmp(reference->inner()->body().begin(), - c->name()->body().begin()) == 0) { - return getJClass(t, resolveClass(t, c->loader(), reference->outer())); - } - } - } - } - - return 0; -} - unsigned classModifiers(Thread* t, GcClass* c) { GcClassAddendum* addendum = c->addendum(); diff --git a/src/avian/machine.h b/src/avian/machine.h index 4245dbb952..21af952d4f 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -2332,6 +2332,8 @@ inline void scanMethodSpec(Thread* t, GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec); +GcJclass* getDeclaringClass(Thread* t, GcClass* c); + inline bool emptyMethod(Thread* t UNUSED, GcMethod* method) { return ((method->flags() & ACC_NATIVE) == 0) diff --git a/src/builtin.cpp b/src/builtin.cpp index b6fd26b75e..b4aaff4fed 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1215,3 +1215,57 @@ extern "C" AVIAN_EXPORT int64_t JNICALL { return reinterpret_cast(primitiveClass(t, arguments[0])); } + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getDeclaringClass(Thread* t, + object, + uintptr_t* arguments) +{ + return reinterpret_cast(getDeclaringClass( + t, cast(t, reinterpret_cast(arguments[0]))->vmClass())); +} + +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Class_getEnclosingMethod(Thread* t, + object, + uintptr_t* arguments) +{ + GcClass* c + = cast(t, reinterpret_cast(arguments[0]))->vmClass(); + PROTECT(t, c); + + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + PROTECT(t, addendum); + + GcByteArray* enclosingClass + = cast(t, addendum->enclosingClass()); + + if (enclosingClass) { + GcClass* enclosing = resolveClass(t, c->loader(), enclosingClass); + + GcPair* enclosingMethod = cast(t, addendum->enclosingMethod()); + + if (enclosingMethod) { + return reinterpret_cast(t->m->classpath->makeJMethod( + t, + cast( + t, + findMethodInClass( + t, + enclosing, + cast(t, enclosingMethod->first()), + cast(t, enclosingMethod->second()))))); + } + } + } + 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); +} diff --git a/src/classpath-android.cpp b/src/classpath-android.cpp index cdd1c4d589..6650a829c9 100644 --- a/src/classpath-android.cpp +++ b/src/classpath-android.cpp @@ -583,13 +583,27 @@ class MyClasspath : public Classpath { return fieldAtOffset(b, field->offset()); } - virtual bool canTailCall(Thread*, + virtual bool canTailCall(Thread* t UNUSED, GcMethod*, - GcByteArray*, - GcByteArray*, + GcByteArray* calleeClassName, + GcByteArray* calleeMethodName, GcByteArray*) { - return true; + // we can't tail call System.load[Library] or + // Runtime.load[Library] due to their use of + // ClassLoader.getCaller, which gets confused if we elide stack + // frames. + + return ( + (strcmp("loadLibrary", + reinterpret_cast(calleeMethodName->body().begin())) + and strcmp("load", + reinterpret_cast(calleeMethodName->body().begin()))) + or (strcmp("java/lang/System", + reinterpret_cast(calleeClassName->body().begin())) + and strcmp( + "java/lang/Runtime", + reinterpret_cast(calleeClassName->body().begin())))); } virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) @@ -1150,63 +1164,6 @@ extern "C" AVIAN_EXPORT int64_t JNICALL 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, cast(t, reinterpret_cast(arguments[0]))->vmClass())); -} - -extern "C" AVIAN_EXPORT int64_t JNICALL - Avian_java_lang_Class_getEnclosingMethod(Thread* t, - object, - uintptr_t* arguments) -{ - GcClass* c - = cast(t, reinterpret_cast(arguments[0]))->vmClass(); - PROTECT(t, c); - - GcClassAddendum* addendum = c->addendum(); - if (addendum) { - object enclosingClass = addendum->enclosingClass(); - if (enclosingClass) { - PROTECT(t, enclosingClass); - - // enclosingClass = getJClass - // (t, resolveClass(t, classLoader(t, c), enclosingClass)); - - object enclosingMethod = addendum->enclosingMethod(); - if (enclosingMethod) { - PROTECT(t, enclosingMethod); - - abort(t); - // TODO: the following violates type safety; enclosingClass at this - // point is a GcJclass (having come from "getJClass()") - but the method - // expects a GcClass. - // Figure it out. - - // return reinterpret_cast - // (t->m->classpath->makeJMethod - // (t, findMethodInClass - // (t, cast(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, @@ -1417,6 +1374,14 @@ extern "C" AVIAN_EXPORT void JNICALL collect(t, Heap::MajorCollection); } +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_Runtime_nativeExit(Thread* t, object, uintptr_t* arguments) +{ + shutDown(t); + + t->m->system->exit(arguments[0]); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_java_lang_Runtime_nativeLoad(Thread* t, object, uintptr_t* arguments) { @@ -1537,6 +1502,34 @@ extern "C" AVIAN_EXPORT void JNICALL release(t, t->javaThread->sleepLock()); } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_VMThread_holdsLock(Thread* t, object, uintptr_t* arguments) +{ + object vmThread = reinterpret_cast(arguments[0]); + PROTECT(t, vmThread); + + GcField* field = resolveField( + t, objectClass(t, vmThread), "thread", "Ljava/lang/Thread;"); + + if (cast(t, fieldAtOffset(vmThread, field->offset())) + != t->javaThread) { + throwNew(t, + GcIllegalStateException::Type, + "VMThread.holdsLock may only be called on current thread"); + } + + GcMonitor* m + = objectMonitor(t, reinterpret_cast(arguments[1]), false); + + return m and m->owner() == t; +} + +extern "C" AVIAN_EXPORT void JNICALL + Avian_java_lang_VMThread_yield(Thread* t, object, uintptr_t*) +{ + t->m->system->yield(); +} + extern "C" AVIAN_EXPORT int64_t JNICALL Avian_dalvik_system_VMStack_getThreadStackTrace(Thread* t, object, diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 86626215d2..85821a7025 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -730,6 +730,15 @@ extern "C" AVIAN_EXPORT int64_t JNICALL return count; } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_java_lang_Thread_holdsLock(Thread* t, object, uintptr_t* arguments) +{ + GcMonitor* m + = objectMonitor(t, reinterpret_cast(arguments[0]), false); + + return m and m->owner() == t; +} + extern "C" AVIAN_EXPORT void JNICALL Avian_java_lang_Thread_yield(Thread* t, object, uintptr_t*) { diff --git a/src/codegen/compiler/event.cpp b/src/codegen/compiler/event.cpp index 5bd721815a..f59f5d124e 100644 --- a/src/codegen/compiler/event.cpp +++ b/src/codegen/compiler/event.cpp @@ -475,8 +475,6 @@ class CallEvent : public Event { int frameOffset; if (TailCalls and (flags & Compiler::TailJump)) { - assertT(c, arguments.count == 0); - int base = frameBase(c); returnAddressIndex = base + c->arch->returnAddressOffset(); if (UseFramePointer) { diff --git a/src/machine.cpp b/src/machine.cpp index a9af32e917..f8e312ebc2 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -2820,13 +2820,14 @@ void parseAttributeTable(Thread* t, addendum->setEnclosingClass( t, - reinterpret_cast( - cast(t, singletonObject(t, pool, enclosingClass - 1)) - ->name())); + cast(t, singletonObject(t, pool, enclosingClass - 1)) + ->name()); addendum->setEnclosingMethod( t, - enclosingMethod ? singletonObject(t, pool, enclosingMethod - 1) : 0); + enclosingMethod + ? cast(t, singletonObject(t, pool, enclosingMethod - 1)) + : 0); } else { s.skip(length); } @@ -5949,6 +5950,27 @@ bool threadIsInterrupted(Thread* t, GcThread* thread, bool clear) return v; } +GcJclass* getDeclaringClass(Thread* t, GcClass* c) +{ + GcClassAddendum* addendum = c->addendum(); + if (addendum) { + GcArray* table = cast(t, addendum->innerClassTable()); + if (table) { + for (unsigned i = 0; i < table->length(); ++i) { + GcInnerClassReference* reference + = cast(t, table->body()[i]); + if (reference->outer() + and strcmp(reference->inner()->body().begin(), + c->name()->body().begin()) == 0) { + return getJClass(t, resolveClass(t, c->loader(), reference->outer())); + } + } + } + } + + return 0; +} + void noop() { } diff --git a/src/tools/bootimage-generator/main.cpp b/src/tools/bootimage-generator/main.cpp index bdb4102c00..b85cc1317c 100644 --- a/src/tools/bootimage-generator/main.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -51,7 +51,6 @@ const bool DebugNativeTarget = false; enum Type { Type_none, - Type_pad, Type_object, Type_object_nogc, Type_int8_t, @@ -293,18 +292,18 @@ TypeMap* typeMap(Thread* t, GcHashMap* typeMaps, object p) unsigned targetFieldOffset(Thread* t, GcHashMap* typeMaps, GcField* field) { - // if (strcmp(reinterpret_cast - // (&byteArrayBody(t, className(t, field->class_()), 0)), - // "java/lang/Throwable") == 0) trap(); + unsigned offset + = ((field->flags() & ACC_STATIC) + ? typeMap(t, + typeMaps, + reinterpret_cast(field->class_()->staticTable())) + : classTypeMap( + t, typeMaps, reinterpret_cast(field->class_()))) + ->targetFixedOffsets()[field->offset()]; - return ((field->flags() & ACC_STATIC) - ? typeMap( - t, - typeMaps, - reinterpret_cast(field->class_()->staticTable())) - : classTypeMap( - t, typeMaps, reinterpret_cast(field->class_()))) - ->targetFixedOffsets()[field->offset()]; + assertT(t, not((field->offset() == 0) xor (offset == 0))); + + return offset; } GcTriple* makeCodeImage(Thread* t, @@ -1284,10 +1283,11 @@ HeapWalker* makeHeapImage(Thread* t, if ((currentObject and objectClass(t, currentObject) == type(t, GcClass::Type) and (currentOffset * BytesPerWord) == ClassStaticTable) - or instanceOf(t, type(t, GcSystemClassLoader::Type), p)) { - // Static tables and system classloaders must be allocated - // as fixed objects in the heap image so that they can be - // marked as dirty and visited during GC. Otherwise, + or instanceOf(t, type(t, GcSystemClassLoader::Type), p) + or instanceOf(t, type(t, GcAddendum::Type), p)) { + // Static tables, system classloaders, and addendums must be + // allocated as fixed objects in the heap image so that they + // can be marked as dirty and visited during GC. Otherwise, // attempts to update references in these objects to point // to runtime-allocated memory would fail because we don't // scan non-fixed objects in the heap image during GC. @@ -1492,9 +1492,7 @@ void writeBootImage2(Thread* t, unsigned fieldCount = 1; while (source[typeCount] != Type_none) { ++typeCount; - if (source[typeCount] != Type_pad) { - ++fieldCount; - } + ++fieldCount; } THREAD_RUNTIME_ARRAY(t, Field, fields, fieldCount); @@ -1515,12 +1513,6 @@ void writeBootImage2(Thread* t, unsigned fieldOffset = 1; for (unsigned j = 0; j < typeCount; ++j) { switch (source[j]) { - case Type_pad: - type = Type_pad; - buildSize = 0; - targetSize = 0; - break; - case Type_object: type = Type_object; buildSize = BytesPerWord; @@ -1580,11 +1572,7 @@ void writeBootImage2(Thread* t, sawArray = true; } - if (type == Type_pad) { - buildOffset = pad(buildOffset, BytesPerWord); - - targetOffset = pad(targetOffset, TargetBytesPerWord); - } else if (not sawArray) { + if (not sawArray) { buildOffset = pad(buildOffset, buildSize); targetOffset = pad(targetOffset, targetSize); diff --git a/src/tools/type-generator/main.cpp b/src/tools/type-generator/main.cpp index 04e329721c..d983ebfded 100644 --- a/src/tools/type-generator/main.cpp +++ b/src/tools/type-generator/main.cpp @@ -1534,17 +1534,11 @@ void writeNameInitializations(Output* out, Module& module) void writeMap(Output* out, Module& module, Class* cl) { std::ostringstream ss; - uintptr_t ownerId = 0; for (std::vector::iterator it = cl->fields.begin(); it != cl->fields.end(); it++) { Field& f = **it; - if (ownerId && ownerId != f.ownerId) { - ss << "Type_pad, "; - } - ownerId = f.ownerId; - ss << "Type_"; ss << enumName(module, f); if (f.nogc) { @@ -1556,9 +1550,6 @@ void writeMap(Output* out, Module& module, Class* cl) if (cl->arrayField) { Field& f = *cl->arrayField; - if (ownerId && ownerId != f.ownerId) { - ss << "Type_pad, "; - } ss << "Type_array, "; ss << "Type_"; ss << enumName(module, f); diff --git a/src/types.def b/src/types.def index 5adffbb963..6623ac44ea 100644 --- a/src/types.def +++ b/src/types.def @@ -130,7 +130,7 @@ (type weakHashMap (extends hashMap)) -(type pair +(type pair avian/Pair (object first) (object second)) diff --git a/test/Reflection.java b/test/Reflection.java index 96305010e1..592c266c99 100644 --- a/test/Reflection.java +++ b/test/Reflection.java @@ -255,6 +255,11 @@ public class Reflection { expect(Baz.class.getField("foo").getAnnotation(Ann.class) == null); expect(Baz.class.getField("foo").getAnnotations().length == 0); + + expect(new Runnable() { public void run() { } }.getClass() + .getEnclosingMethod().equals + (Reflection.class.getMethod + ("main", new Class[] { String[].class }))); } protected static class Baz { diff --git a/test/Threads.java b/test/Threads.java index 0cef7c75b7..5fa4b780c7 100644 --- a/test/Threads.java +++ b/test/Threads.java @@ -1,4 +1,10 @@ -public class Threads implements Runnable { +public class Threads implements Runnable { + private static boolean success = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + public static void main(String[] args) throws Exception { { Threads test = new Threads(); Thread thread = new Thread(test); @@ -40,15 +46,24 @@ public class Threads implements Runnable { thread.join(); } - System.out.println("finished"); + System.out.println("finished; success? " + success); + + if (! success) { + System.exit(-1); + } } public void run() { - synchronized (this) { - int i = 0; - try { + int i = 0; + try { + expect(! Thread.holdsLock(this)); + synchronized (this) { + expect(Thread.holdsLock(this)); + System.out.println("I'm running in a separate thread!"); + Thread.yield(); // just to prove Thread.yield exists and is callable + final int arrayCount = 16; final int arraySize = 4; System.out.println("Allocating and discarding " + arrayCount + @@ -57,14 +72,18 @@ public class Threads implements Runnable { byte[] array = new byte[arraySize * 1024 * 1024]; } - long nap = 5; - System.out.println("sleeping for " + nap + " seconds"); - Thread.sleep(nap * 1000); - } catch (Throwable e) { - System.err.println("caught something in second thread after " + i + - " iterations"); - e.printStackTrace(); - } finally { + long nap = 500; + System.out.println("sleeping for " + nap + " milliseconds"); + Thread.sleep(nap); + notifyAll(); + } + success = true; + } catch (Throwable e) { + System.err.println("caught something in second thread after " + i + + " iterations"); + e.printStackTrace(); + } finally { + synchronized (this) { notifyAll(); } } diff --git a/test/ci.sh b/test/ci.sh index f64c13939b..5399d78e15 100755 --- a/test/ci.sh +++ b/test/ci.sh @@ -25,8 +25,10 @@ run make ${flags} process=interpret ${test_target} if [ -z "${openjdk}" ]; then run make ${flags} bootimage=true ${test_target} run make ${flags} mode=debug bootimage=true ${test_target} - # might as well do an openjdk test while we're here: - run make openjdk=$JAVA_HOME ${flags} ${test_target} + if [ -z "${android}" ]; then + # might as well do an openjdk test while we're here: + run make openjdk=$JAVA_HOME ${flags} ${test_target} + fi fi run make ${flags} tails=true continuations=true ${test_target} run make ${flags} codegen-targets=all