In order to facilitate making the VM compatible with multiple class
libraries, it's useful to separate the VM-specific representation of
these classes from the library implementations. This commit
introduces VMClass, VMField, and VMMethod for that purpose.
Note the following excerpt from PNGFileFormat.java in SWT:
/*
* InflaterInputStream does not consume all bytes in the stream
* when it is closed. This may leave unread IDAT chunks. The fix
* is to read all available bytes before closing it.
*/
while (stream.available() > 0) stream.read();
stream.close();
This code relies on the documented behavior of
InflaterInputStream.available, which must return "0 after EOF has been
reached, otherwise always return 1". This is unlike
InputStream.available, which is documented to return "the number of
bytes that can be read (or skipped over) from this input stream
without blocking by the next caller of a method for this input
stream", and says nothing about how many bytes are left until the end
of stream.
This commit modifies InflaterInputStream.available to behave according
to Sun's documentation.
A long time ago, I refactored the class initialization code in the VM,
but did not notice until today that it had caused the
process=interpret build to break on certain recursive initializations.
In particular, we were not always detecting when a thread recursively
tried to initialize a class it was already in the process of
initializing, leading to the mistaken assumption that another thread
was initializing it and that we should wait until it was done, in
which case we would wait forever.
This commit ensures that we always detect recursive initialization and
short-circuit it.
In PersistentSet.remove, we were modifying the child node in place
instead of making a copy to update, which would corrupt older
revisions. This commit ensures that we always create a copy if
necessary.
The shiftLeftC function in powerpc.cpp was miscompiling such shifts,
leading to crashes due to illegal instructions and other weirdness due
to instructions that meant something completely different. This
commit fixes that and adds a test to Longs.java to make sure it stays
fixed.
Previously, we risked segfaults by passing negative numbers to memcpy.
This commit also makes arraycopy throw an IndexOutOfBounds exception
instead of an ArrayStoreException if the specified offsets and lengths
would take us outside the bounds of one or both of the arrays, per the
Sun documentation.
If we catch the target thread in a virtual thunk when getting its
stack trace, we must assume its Thread::stack field is garbage and use
the register values instead. Previously, we treated these thunks as
any other native code, leading to crashes when we tried to use the
garbage pointer.
32MB was just slightly too large for PowerPC immediate call instructions
to span, and 16MB matches the JIT executable memory area we use in
compile.cpp.
compileDirectInvoke does some magic to optimize tail calls to native
methods which involves storing the return address (which we'll never
actually return to, since it's a tail call) in a thread-local field so
the thunk function can figure out which native method to look up at
runtime. Since this address will change when the boot image is
loaded, the boot image creation code needs to know about it.
callContinuation failed to call the correct continuation when feeding
it an exception due to a regression introduced with the
Thread.getStackTrace changes.
The new Thread::defaultHeap declaration has increased the offset of all
the fields following it.
This commit also makes vmInvoke_returnAddress global so it can be refered
to from compile.cpp.
It's not safe to use malloc from a signal handler, so we can't
allocate new memory when handling segfaults or Thread.getStackTrace
signals. Instead, we allocate a fixed-size backup heap for each
thread ahead of time and use it if there's no space left in the normal
heap pool. In the rare case that the backup heap isn't large enough,
we fall back to using a preallocated exception without a stack trace
as a last resort.
This function was broken in two different ways:
1. It only checked MyProcessor::thunks, not MyProcessor::bootThunks.
It needs to check both.
2. When checking MyProcessor::thunks, it used fields from
MyProcessor::bootThunks instead of from the same thunk collection.
This fixes both problems.
Implementing Thread.getStackTrace is tricky. A thread may interrupt
another thread at any time to grab a stack trace, including while the
latter is executing Java code, JNI code, helper thunks, VM code, or
while transitioning between any of these.
To create a stack trace we use several context fields associated with
the target thread, including snapshots of the instruction pointer,
stack pointer, and frame pointer. These fields must be current,
accurate, and consistent with each other in order to get a reliable
trace. Otherwise, we risk crashing the VM by trying to walk garbage
stack frames or by misinterpreting the size and/or content of
legitimate frames.
This commit addresses sensitive transition points such as entering the
helper thunks which bridge the transitions from Java to native code
(where we must save the stack and frame registers for use from native
code) and stack unwinding (where we must atomically update the thread
context fields to indicate which frame we are unwinding to). When
grabbing a trace for another thread, we determine what kind of code we
caught the thread executing in and use that information to choose the
thread context values with which to begin the trace. See
MyProcessor::getStackTrace::Visitor::visit for details.
In order to atomically update the thread context fields, we do the
following:
1. Create a temporary "transition" object to serve as a staging area
and populate it with the new field values.
2. Update a transition pointer in the thread object to point to the
object created above. As long as this pointer is non-null,
interrupting threads will use the context values in the staging
object instead of those in the thread object.
3. Update the fields in the thread object.
4. Clear the transition pointer in the thread object.
We use a memory barrier between each of these steps to ensure they are
made visible to other threads in program order. See
MyThread::doTransition for details.
gethostbyname may return any combination of IPv4 and IPv6 addresses,
and it's not safe to assume the first address is IPv4, which is all
our code is currently prepared to handle. In contrast, getaddrinfo
allows us to specify whether we want IPv4, IPv6, or both.
We should eventually make this switch on Windows as well, but the
status of getaddrinfo in Windows 2000 is not clear, and MinGW's
ws2tcpip.h only declares it for XP and above.
This commit also adds InetAddress.getByName for explicit DNS lookups.
In java-nio.cpp, we can't use GetPrimitiveArrayCritical when reading
from or writing to blocking sockets since it may block the rest of the
VM indefinitely.
In SelectableChannel.java, we can't use a null test on
SelectableChannel.key to determine whether the channel is open since
it might never be registered with a Selector. According to the Sun
documentation, a SelectableChannel is open as soon as it's created, so
that's what we now implement.