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.
This commit is contained in:
Joel Dice 2014-07-12 16:03:11 -06:00
parent 80f19abf3a
commit 2a43e68c16
19 changed files with 228 additions and 151 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -0,0 +1,5 @@
package avian;
abstract class Pair {
// VM-visible fields in types.def
}

View File

@ -392,6 +392,12 @@ public final class Class <T> implements Type, AnnotatedElement {
}
}
public native Class getEnclosingClass();
public native Method getEnclosingMethod();
public native Constructor getEnclosingConstructor();
public T[] getEnumConstants() {
if (Enum.class.isAssignableFrom(this)) {
try {

View File

@ -25,6 +25,10 @@ public class Method<T> 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;
}

View File

@ -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= \

View File

@ -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<GcArray>(t, addendum->innerClassTable());
if (table) {
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference
= cast<GcInnerClassReference>(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();

View File

@ -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)

View File

@ -1215,3 +1215,57 @@ extern "C" AVIAN_EXPORT int64_t JNICALL
{
return reinterpret_cast<int64_t>(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<intptr_t>(getDeclaringClass(
t, cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))->vmClass()));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_Class_getEnclosingMethod(Thread* t,
object,
uintptr_t* arguments)
{
GcClass* c
= cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))->vmClass();
PROTECT(t, c);
GcClassAddendum* addendum = c->addendum();
if (addendum) {
PROTECT(t, addendum);
GcByteArray* enclosingClass
= cast<GcByteArray>(t, addendum->enclosingClass());
if (enclosingClass) {
GcClass* enclosing = resolveClass(t, c->loader(), enclosingClass);
GcPair* enclosingMethod = cast<GcPair>(t, addendum->enclosingMethod());
if (enclosingMethod) {
return reinterpret_cast<uintptr_t>(t->m->classpath->makeJMethod(
t,
cast<GcMethod>(
t,
findMethodInClass(
t,
enclosing,
cast<GcByteArray>(t, enclosingMethod->first()),
cast<GcByteArray>(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);
}

View File

@ -583,13 +583,27 @@ class MyClasspath : public Classpath {
return fieldAtOffset<int32_t>(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<char*>(calleeMethodName->body().begin()))
and strcmp("load",
reinterpret_cast<char*>(calleeMethodName->body().begin())))
or (strcmp("java/lang/System",
reinterpret_cast<char*>(calleeClassName->body().begin()))
and strcmp(
"java/lang/Runtime",
reinterpret_cast<char*>(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<intptr_t>(getDeclaringClass(
t, cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))->vmClass()));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_Class_getEnclosingMethod(Thread* t,
object,
uintptr_t* arguments)
{
GcClass* c
= cast<GcJclass>(t, reinterpret_cast<object>(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<uintptr_t>
// (t->m->classpath->makeJMethod
// (t, findMethodInClass
// (t, cast<GcClass>(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<object>(arguments[0]);
PROTECT(t, vmThread);
GcField* field = resolveField(
t, objectClass(t, vmThread), "thread", "Ljava/lang/Thread;");
if (cast<GcThread>(t, fieldAtOffset<object>(vmThread, field->offset()))
!= t->javaThread) {
throwNew(t,
GcIllegalStateException::Type,
"VMThread.holdsLock may only be called on current thread");
}
GcMonitor* m
= objectMonitor(t, reinterpret_cast<object>(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,

View File

@ -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<object>(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*)
{

View File

@ -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) {

View File

@ -2820,13 +2820,14 @@ void parseAttributeTable(Thread* t,
addendum->setEnclosingClass(
t,
reinterpret_cast<object>(
cast<GcReference>(t, singletonObject(t, pool, enclosingClass - 1))
->name()));
cast<GcReference>(t, singletonObject(t, pool, enclosingClass - 1))
->name());
addendum->setEnclosingMethod(
t,
enclosingMethod ? singletonObject(t, pool, enclosingMethod - 1) : 0);
enclosingMethod
? cast<GcPair>(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<GcArray>(t, addendum->innerClassTable());
if (table) {
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference
= cast<GcInnerClassReference>(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()
{
}

View File

@ -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<const char*>
// (&byteArrayBody(t, className(t, field->class_()), 0)),
// "java/lang/Throwable") == 0) trap();
unsigned offset
= ((field->flags() & ACC_STATIC)
? typeMap(t,
typeMaps,
reinterpret_cast<object>(field->class_()->staticTable()))
: classTypeMap(
t, typeMaps, reinterpret_cast<object>(field->class_())))
->targetFixedOffsets()[field->offset()];
return ((field->flags() & ACC_STATIC)
? typeMap(
t,
typeMaps,
reinterpret_cast<object>(field->class_()->staticTable()))
: classTypeMap(
t, typeMaps, reinterpret_cast<object>(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);

View File

@ -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<Field*>::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);

View File

@ -130,7 +130,7 @@
(type weakHashMap
(extends hashMap))
(type pair
(type pair avian/Pair
(object first)
(object second))

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -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