617bd85 broke the Android build by creating an unresolvable
order-of-operations bug in classpath-android.cpp's
MyClasspath::preBoot method.
The problem is that, while JNIEnv::FindClass is supposed to initialize
the class that it finds, this causes JniConstants::init to indirectly
invoke native methods which are not registered until JNI_OnLoad is
called (which happens after JniConstants::init is called). However,
if we call JNI_OnLoad first, that causes methods to be invoked which
rely on JniConstants::init having already been run.
I haven't checked to see how Dalvik handles this, but I don't see any
way around the problem besides disabling initialization by
JNIEnv::FindClass until the preBoot phase is complete. Moreover, it's
dangerous to allow Java code to be invoked so early anyway, since the
VM is not yet fully initialized.
For some reason, running Avian under the SVN version of Valgrind
caused mmap to fail, which caused tryAllocateExecutable to return a
null pointer, which led to a non-obvious crash later on. Adding an
expect to check the result immediately will at least make it obvious
what went wrong.
bb86500 was a step in the right direction, but there was a bug that
caused Type_pad fields to be inserted between every other field in for
a derived class when type-maps.cpp was generated, and this led to
miscompilation of e.g. Android's
java.lang.reflect.Constructor.getModifiers.
We should define EXPORT to be __declspec(dllexport) on Windows
regardless of architecture, not just non-x86_64 arches. This fixes
errors to to embedded JAVA_HOME files not being found in openjdk-src
builds, e.g. lib/currency.data.
Although the JNI reference documentation does not mention it,
FindClass should initialize the class before it returns it. That's
what HotSpot does, and that's what we have to do too.
In particular, OpenJDK's
Java_java_net_Inet6AddressImpl_lookupAllHostAddr relies on
Inet6Address's static initializer being run when it is resolved using
FindClass, or else it will crash.
* Unsafe.arrayIndexScale was always returning the native word size,
due to a thinko on my part
* Unsafe.getLongVolatile and putLongVolatile did not work for array
elements on 32-bit systems
Turns out Function can do the jobs of both CallbackReceiver and
FunctionReceiver, so I've removed the latter two.
Also, shift and reset should work with a combination of types, not
just a single type, so I've expanded their generic signatures.
There's more work to do to derive all the properties of a given class
from its code source (e.g. JAR file), but this at least ensures that
ClassLoader.getPackage will actually return something non-null when
appropriate.
classpath-common.h's getDeclaringClass was trying to look up
non-existing classes, which led to an abort, and I don't even know
what Class.getDeclaredClasses was trying to do, but it was ugly and
wrong.
The various Architecture::nextFrame implementations were not walking
the stack correctly when a StackOverflowError was thrown. The
throwStackOverflow thunk is called before the frame of the most
recently called method has been fully created, and because tails=true
builds use a different calling convention, we need to treat this
situation carefully when building a stack trace or unwinding.
Otherwise, we will skip past all the java frames to the next native
frame, which is what was happening.
Tail call and dead code optimizations can cause code after a throw to
be eliminated, which confuses findUnwindTarget because it doesn't know
what code is throwing the exception. So we need at least one
instruction to follow the call to the throw_ thunk. Previously, we
only added such an instruction when we knew the throw was the last
instruction in the bytecode, but it turns out there are other cases
where it is needed, including certain try/finally situations.
This is the simplest possible ConcurrentHashMap I could come up with
that works and is actually concurrent in the way one would expect.
It's pretty unconventional, being based on a persistent red-black
tree, and not particularly memory-efficient or cache-friendly. I
think this is a good place to start, though, and it should perform
reasonably well for most workloads. Patches for a more efficient
implementation are welcome!
I also implemented AtomicReferenceArray, since I was using it in my
first, naive attempt to implement ConcurrentHashMap.
I had to do a bit of refactoring, including moving some non-standard
stuff from java.util.Collections to avian.Data so I could make it
available to code outside the java.util package, which is why I had to
modify several unrelated files.
getDeclaredMethods was returning methods which were inherited from
interfaces but not (re)declared in the class itself, due to the VM's
internal use of VMClass.methodTable differing from its role in
reflection. For reflection, we must only include the declared
methods, not the inherited but un-redeclared ones.
Previously, we saved the original method table in
ClassAddendum.methodTable before creating a new one which contains
both declared and inherited methods. That wasted space, so this patch
replaces ClassAddendum.methodTable with
ClassAddendum.declaredMethodCount, which specifies how many of the
methods in VMClass.methodTable were declared in that class.
Alternatively, we could ensure that undeclared methods always have
their VMMethod.class_ field set to the declaring class instead of the
inheriting class. I tried this, but it led to subtle crashes in
interface method lookup. The rest of the VM relies not only on
VMClass.methodTable containing all inherited interface methods but
also that those methods point to the inheriting class, not the
declaring class. Changing those assumptions would be a much bigger
(and more dangerous in terms of regression potential) effort than I
care to take on right now. The solution I chose is a bit ugly, but
it's safe.
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 makes them available in all class libraries, not just the OpenJDK
library. Note that I've also removed the unecessary idle statements,
per ab4adef.