The obfuscator (or compiler, potentially?) converts INVOKEVIRTUAL
instructions to INVOKESPECIAL in the following circumstances:
* The owner of the method is the same as the class containing the
INVOKE instruction.
* The owner's ACC_FINAL flag is set.
When those two conditions are met, and when ACC_SUPER is set on the
owner (which is always true in the RuneScape client, and the flag is
ignored in modern JVMs), then INVOKESPECIAL and INVOKEVIRTUAL are
equivalent.
This has not caused problems until now. However, the future static
scrambling transformer will break the first condition in some cases, as
it moves methods between classes.
This transformer reverses the obfuscation, such that INVOKEVIRTUAL is
used again. INVOKEVIRTUAL instructions may be moved between classes
without complication.
In Java 1.4 and earlier, the compiler creates a synthetic method for
invoking Class.forName() and a static field for caching the Class<?>
instance.
This will not interact well with the static scrambling transformer. The
obfuscator strips some synthetic flags. Furthermore, the field and
method need to be moved together with the code that uses them for
decompilers like Fernflower to recognise them and convert them back to
class literals.
If Fernflower misses one, the decompiled Class.forName() helper does not
compile cleanly (as the bytecode is missing a CHECKCAST).
Rather than complicating the future static scrambling transformer, it's
easier to convert these to use LDC and upgrade the .class file version
to Java 5.
Calling super.write(byte[], int, int) in FilterOutputStream didn't work,
as it called super.write(byte). This is inefficient and also caused us
to fiddle with skipBytes twice, causing us to skip the incorrect number
of bytes.