# Monday, 05 December 2011
IKVM.NET 7.0 Released

I've released IKVM.NET 7.0 to SourceForge. The binaries are identical to the ones in release candidate 0.

Release Notes

This document lists the improvements, known issues and incompatibilities.

What's New (relative to IKVM.NET 0.46):

  • Integrated OpenJDK 7 b147.
  • Implemented Java 7 JVM changes.
  • Java annotations on statically compiled code are now returned as java.lang.reflect.Proxy objects for compability with broken code that assumes this is always the case.
  • Added delegate conversion for java.lang.reflect.InvocationHandler to ikvm.runtime.Delegates.
  • Various remap file improvements.
  • Changed build and JNI code to use different names for the Windows x86 and x64 versions of the native dll.
  • Retain reflection field ordering for ikvmc compiled code (not required by spec, but to improve compatibility with broken code).
  • Various AWT fixes.
  • Interop between java.lang.AutoCloseable and System.IDisposable.
  • Various build system improvements.
  • Added ikvmc -warnaserror option.
  • Fixed java.io.FileOutputStream in append mode to use atomic append.
  • Various performance improvements.
  • Added -Xnoglobbing option to ikvm.exe.
  • Various minor fixes.
  • Implemented dual stack sockets (Windows Vista and up only).
  • Implemented platform MBean server.
  • Implemented SocketChannel.sendOutOfBandData().
  • Implemented DatagramChannel multicast methods.
  • Removed mapping of System.Security.VerificationException to java.lang.VerifyError.
  • IKVM.Reflection: Massive enhancements to support a very large portion of the managed PE capabilities (much more than System.Reflection.Emit).

Runtime

  • Code unloading (aka class GC) is not supported.
  • In Java static initializers can deadlock, on .NET some threads can see uninitialized state in cases where deadlock would occur on the JVM.
  • JNI
     
    • Only supported in the default AppDomain.
    • Only the JNICALL calling convention is supported! (On Windows, HotSpot appears to also support the cdecl calling convention).
    • Cannot call string contructors on already existing string instances
    • A few limitations in Invocation API support
       
      • The Invocation API is only supported when running on .NET.
      • JNI_CreateJavaVM: init options "-verbose[:class|:gc|:jni]", "vfprintf", "exit" and "abort" are not implemented. The JDK 1.1 version of JavaVMInitArgs isn't supported.
      • JNI_GetDefaultJavaVMInitArgs not implemented
      • JNI_GetCreatedJavaVMs only returns the JavaVM if the VM was started through JNI or a JNI call that retrieves the JavaVM has already occurred.
      • DestroyJVM is only partially implemented (it waits until there are no more non-daemon Java threads and then returns JNI_ERR).
      • DetachCurrentThread doesn't release monitors held by the thread.
    • Native libraries are never unloaded (because code unloading is not supported).
  • The JVM allows any reference type to be passed where an interface reference is expected (and to store any reference type in an interface reference type field), on IKVM this results in an IncompatibleClassChangeError.
  • monitorenter / monitorexit cannot be used on unitialized this reference.
  • Floating point is not fully spec compliant.
  • A method returning a boolean that returns an integer other than 0 or 1 behaves differently (this also applies to byte/char/short and for method parameters).
  • Synchronized blocks are not async exception safe.
  • Ghost arrays don't throw ArrayStoreException when you store an object that doesn't implement the ghost interface.
  • Class loading is more eager than on the reference VM.
  • Interface implementation methods are never really final (interface can be reimplemented by .NET subclasses).
  • JSR-133 finalization spec change is not fully implemented. The JSR-133 changes dictate that an object should not be finalized unless the Object constructor has run successfully, but this isn't implemented.
  • Strict class-file checking is not implemented.

Static Compiler (ikvmc)

  • Some subtle differences with ikvmc compiled code for public members inherited from non-public base classes (so called "access stubs"). Because the access stub lives in a derived class, when accessing a member in a base class, the derived cctor will be run whereas java (and ikvm) only runs the base cctor.
  • Try blocks around base class ctor invocation result in unverifiable code (no known compilers produce this type of code).
  • Try/catch blocks before base class ctor invocation result in unverifiable code (this actually happens with the Eclipse compiler when you pass a class literal to the base class ctor and compile with -target 1.4).
  • Only code compiled together during a single compilation fully obeys the JLS binary compatibility rules.

Class Library

Most class library code is based on OpenJDK 7 build 147. Below is a list of divergences and IKVM.NET specific implementation notes.

com.sun.security.auth.module        Not supported.
java.applet Not implemented.
java.awt Partial System.Windows.Forms based back-end. Not supported.
java.io.Console Not implemented.
java.lang.instrument Not implemented.
java.lang.management Limited implementation.
java.net SCTP and SDP not implemented.
java.net.ProxySelector Getting the default system proxy for a URL is not implemented.
java.nio.file Most optional features (e.g. ACLs) are not implemented.
java.text.Bidi Not supported.
java.util.zip Partially based on GNU Classpath implementation.
javax.crypto ECC is not implemented.
javax.imageio.plugins.jpeg Partial implementation. JPEGs can be read and written and there is limited metadata support.
javax.management Limited implementation.
javax.print There is a Win32 specific printing implementation. Not supported.
javax.script ECMAScript implementation is not included.
javax.smartcardio Not implemented.
javax.sound Not implemented.
javax.swing Not supported.
javax.tools Not supported.
org.ietfs.jgss Not implemented.
sun.jdbc.odbc Implementation based on .NET ODBC managed provider.
sun.net.www.content.audio Audio content handlers not implemented.
sun.net.www.content.image Not supported.

The entire public API is available, so "Not implemented." for javax.smartcardio, for example, means that the API is there but there is no back-end to provide the actual smartcard communication support. "Not supported." means that the code is there and probably works at least somewhat, but that I'm less likely to fix bugs reported in these areas, but patches are welcome, of course.

Specific API notes:

  • java.lang.Thread.stop(Throwable t) doesn't support throwing arbitrary exceptions on other threads (only java.lang.ThreadDeath).
  • java.lang.Thread.holdsLock(Object o) causes a spurious notify on the object (this is allowed by the J2SE 5.0 spec).
  • java.lang.String.intern() strings are never garbage collected.
  • Weak/soft references and reference queues are inefficient and do not fully implement the required semantics.
  • java.lang.ref.SoftReference: Soft references are not guaranteed to be cleared before an OutOfMemoryError is thrown.
  • Threads started outside of Java aren't "visible" (e.g. in ThreadGroup.enumerate()) until they first call Thread.currentThread().
  • java.lang.Thread.getState() returns WAITING or TIMED_WAITING instead of BLOCKING when we're inside Object.wait() and blocking to re-acquire the monitor.
  • java.nio.channel.FileChannel.lock() shared locks are only supported on Windows NT derived operating systems.
  • java.lang.SecurityManager: Deprecated methods not implemented: classDepth(String), inClass(String), classLoaderDepth(), currentLoadedClass(), currentClassLoader(), inClassLoader()

Supported Platforms

This release has been tested on the following CLI implementations / platforms:

CLI Implementation       Architecture      Operating System
.NET 2.0 SP2 x86 Windows
.NET 2.0 SP2 x64 Windows
.NET 4.0 x86 Windows
.NET 4.0 x64 Windows


Partial Trust

There is experimental support for running in partial trust.

Monday, 05 December 2011 07:56:27 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Friday, 02 December 2011
Function Pointer Types

In today's snapshot I also added support for function pointer types. The CLI supports both managed and unmanaged function pointer types, however both are unverifiable.

Here's a small C++/CLI example of using a managed function pointer type (in the call method signature):

void func() {
  System::Console::WriteLine("Hello from Func");
}

void call(void (*p)()) {
  p();
}

void main() {
  call(func);
}

Compile this with cl /clr:pure test.cpp and the resulting test.exe will be a managed PE file with these three methods (and a lot of other junk).

Here's an IKVM.Reflection example to create an equivalent foo.exe (without the extra junk):

using System;
using IKVM.Reflection;
using IKVM.Reflection.Emit;
using Type = IKVM.Reflection.Type;

class Program {
  public static void Main() {
    var u = new Universe();
    var ab = u.DefineDynamicAssembly(new AssemblyName("Foo"), AssemblyBuilderAccess.Save);
    var modb = ab.DefineDynamicModule("Foo", "Foo.exe");
   
    var mb2 = modb.DefineGlobalMethod("func", MethodAttributes.Static, null, null);
    var ilgen = mb2.GetILGenerator();
    ilgen.EmitWriteLine("Hello from Func");
    ilgen.Emit(OpCodes.Ret);
   
    Type func = u.MakeFunctionPointer(u.MakeStandAloneMethodSig(CallingConventions.Standard, null, default(CustomModifiers), null, null, null));
   
    var mb3 = modb.DefineGlobalMethod("call", MethodAttributes.Static, null, new Type[] { func });
    ilgen = mb3.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.__EmitCalli(OpCodes.Calli, func.__MethodSignature);
    ilgen.Emit(OpCodes.Ret);
   
    var mb1 = modb.DefineGlobalMethod("main", MethodAttributes.Static, null, null);
    ilgen = mb1.GetILGenerator();
    ilgen.Emit(OpCodes.Ldftn, mb2);
    ilgen.Emit(OpCodes.Call, mb3);
    ilgen.Emit(OpCodes.Ret);
   
    modb.CreateGlobalFunctions();
    ab.SetEntryPoint(mb1);
    ab.Save("Foo.exe");
  }
}

(I should point out that this won't work with today's snapshot, because while writing this I discovered that I had incorrectly added a static Create() method to __StandAloneMethodSig, but that should instead be an instance method on Universe.)

Friday, 02 December 2011 09:43:53 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
Custom Modifiers

As I wrote earlier today, the theme of today's snapshot is custom modifiers, but I decided to expand a bit on that, since they are a relatively unknown feature of .NET.

In the early days of IKVM.NET I ran into the problem that some Java types must be erased in .NET signatures. An example are arrays of what I call ghost types. Fortunately these are not very common, but here's an example method:

void foo(CharSequence[] array) { }

When this is compiled with ikvmc, you end up with something like this:

[NameSig("foo", "([Ljava.lang.CharSequence;)V")]
void foo(object[] array) { }

If the class happened to already have a foo method with that signature, the method name would be mangled with a numerical suffix (e.g. foo_0).

Now, instead of adding a custom attribute and using name mangling, .NET has a much better solution for this. Custom modifiers, but unfortunately you couldn't use them via .NET's System.Reflection.Emit API back in the .NET 1.x days.

When .NET 2.0 came around they added (some) support for custom modifiers, but it both had some issues and by then I already had a working solution, so there was not much need for changing the implementation. The one exception was in constructor signatures, since there you cannot mangle the method name, you have to rely on the signature types to make the signature unique, so there I did add custom modifiers when necessary to make sure the signatures are unique.

In today's snapshot I've changed fields and constructors to always use custom modifiers (when necessary) and removed the usage of the NameSigAttribute. Methods still need to be done.

Here's an example with two constructors that end up with the same signatures:

public class Test {
  public Test(Object obj) { }
  public Test(cli.System.Object obj) { }
}

When you compile this with ikvmc, you get something like this:

public class Test {
  public Test(object obj) { }
  public Test(object modopt(object) obj) { }
}

The modopt(object) construct here stands for an optional custom modifier of type object. When this class is encountered by IKVM (reflection or ikvmc) it can recover the original Java types based on the signature and custom modifier. When you use this class from C#, the C# compiler will prefer the constructor without the custom modifier, so you can still use the class. The cli.System.Object overload is not useable from C#, but when there is no signature conflict, C# will also allow you to use the version with the custom modifier. So it allows for nice cross language interop (especially given how unlikely all these scenarios are).

As I said, I haven't done methods yet, but I did experiment a little to see if C# interop would work. I was especially concerned about overriding/implementing methods with a custom modifier in the signature, but the .NET C# compiler supports this pretty well. The Mono C# compiler does not yet have support for this. Only when you have two or more abstract methods with the same signature, you can run into a problem. For base classes with "duplicate" abstract methods, the C# compiler will not allow you to subclass that class, but you can implement interfaces with "duplicate" methods and in that case the C# compiler will emit bridge methods for all signatures that call into your (necessarily) single implemenation method.

Crazy Stuff

With the new IKVM.Reflection custom modifier support, you can now generate signatures that cannot be generated with System.Reflection.Emit. Here's an example of interleaved required and optional custom modifiers:

var u = new Universe();
var ab = u.DefineDynamicAssembly(new AssemblyName("Foo"), AssemblyBuilderAccess.Save);
var modb = ab.DefineDynamicModule("Foo", "Foo.dll");
var tb = modb.DefineType("FooType", TypeAttributes.Public | TypeAttributes.Abstract);
tb.DefineDefaultConstructor(MethodAttributes.Public);
CustomModifiersBuilder mods = new CustomModifiersBuilder();
mods.AddOptional(u.Import(typeof(object)));
mods.AddRequired(u.Import(typeof(object)));
mods.AddOptional(u.Import(typeof(object)));
var mb = tb.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.NewSlot | MethodAttributes.Virtual);
mb.__SetSignature(null, new CustomModifiers(), new Type[] { u.Import(typeof(int)) }, new CustomModifiers[] { mods.Create() });
tb.CreateType();
ab.Save("Foo.dll");

The resulting FooType in Foo.dll contains a method M that cannot be called (or overridden) by System.Reflection.Emit generated code. It also can't be used from C#,  but that is by design, the C# compiler will not allow you to use any members that have required custom modifiers that it doesn't understand (I think that the only required custom modifier that it understands is IsVolatile on fields).

Of course, IKVM itself is not very good about dealing with custom modifiers yet. It only understands its own custom modifiers, but can get confused when accessing other .NET code with custom modifiers.

Friday, 02 December 2011 08:57:09 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
New Development Snapshot

The theme of this snapshot is custom modifiers. I've been working on ikvmc to make more use of custom modifiers for fields and constructors that have "special" types in their signatures and on the IKVM.Reflection side I completly rewrote custom modifier handling (unrelated to ikvmc) to allow roundtripping C++/CLI files (minus the unmanaged bits, obviously) and support interleaved modreq and modopt sequences.

Changes:

  • Updated version to 7.1.4352.0.
  • Fixed dynamic mode with class GC enabled to automatically add required InternalsVisibleToAttribute to the dynamic assemblies.
  • Greatly improved access stub generation. Fixed many related bugs.
  • Fixed ikvmc exception when decoding NameSigAttribute with missing type.
  • Added ikvm.runtime.Util.getClassFromTypeHandle() overload for classes that represent arrays of remapped .NET types and .NET primitives.
  • Instead of mangling field names and using NameSigAttribute, we now use modopt to modify the signature and record the real type.
  • Removed legacy remap feature that allowed final fields as properties to be defined (long time ago this was used for the System.in/out/err fields).
  • Fixed Graphics2D.clip(null) to not throw an exception for bitmap and print graphics.
  • Fixed java.nio.file.FileStore for non-accessible drive to throw exception when trying to create the FileStore, not when accessing the name() or type() properties.
  • Stop mangling property names for access stubs (instead we rely on custom modifiers to make them unique).
  • Stop mangling property accessor method names, but use a custom modifier instead.
  • Generate access stubs for final fields (when -strictfinalfieldsemantics isn't specified).
  • Fixed ikvmc to not treat informational messages as error when -warnaserror is specified. Fix for #3443377.
  • Changed ikvmc to not enforce pre-1.5 class name rules (since HotSpot, by default doesn't enforce any naming rules for classes loaded by trusted class loaders). Fix for #3443373.
  • Relax dynamic mode class name validation for trusted class loaders.
  • Implemented NetToolkit.isModalExclusionTypeSupported() (by always returning false). Fix for #3441959.
  • Added extension methods for (almost) all instance methods in Object, String and Throwable to ikvm.extensions.ExtensionMethods.
  • Added ObsoleteAttribute to instancehelper_ methods in remapped types (with a message saying that the extension methods should be used).
  • Added parameter name to Throwable.addSuppressed() in map.xml.
  • Added -Xverify option to ikvm.exe.
  • Use custom modifiers (instead of NameSigAttribute) for constructors with mangled types.
  • When a member with unloadable types in its signature is exposed via reflection, the unloadable types should be resolved to actual types.
  • Mark getSpace0 with SecuritySafeCritical to avoid getting an exception with .NET 4.0 build.
  • IKVM.Reflection: Rewrote custom modifier handling to retain ordering.
  • IKVM.Reflection: Added ConstructorInfo.__ReturnParameter to expose custom modifiers.
  • IKVM.Reflection: Added __GetCustomModifiers() to various *Info types.
  • IKVM.Reflection: Added CustomModifiers type to encapsulate a custom modifier sequence.
  • IKVM.Reflection: Added CustomModifiersBuilder to create a CustomModifiers sequence.
  • IKVM.Reflection: Marked a number of IKVM.Reflection specific methods Obsolete, because they are replaced with method that take CustomModifiers value(s).
  • IKVM.Reflection: Added support for function pointer types.
  • IKVM.Reflection: Added support for custom modifiers to StandAloneMethodSig.
  • IKVM.Reflection: Added custom modifier support in local variable signatures.
  • IKVM.Reflection: Fixed SignatureHelper to support custom modifiers in method signatures.
  • IKVM.Reflection: Added new overload of __ResolveOptionalParameterTypes() that supports resolving generic type parameters and custom modifiers.

Binaries available here: ikvmbin-7.1.4352.0.zip

Friday, 02 December 2011 07:49:54 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, 15 November 2011
New Development Snapshot

Hot on the heels of the 7.0 release candidate, the first development snapshot of 7.1. The most visible change is a new feature. The ikvm.lang.DllExport annotation to export static methods as unmanaged entry points.

Here's a crazy example:

import ikvm.lang.DllExport;
import cli.System.Runtime.InteropServices.DllImportAttribute;

public class test {
  @DllExport(name = "Foo", ordinal = 42)
  static void Dummy() {
    System.out.println("Hello World");
  }

  @DllImportAttribute.Annotation("test.exe")
  static native void Foo();

  public static void main(String[] args) {
    Foo();
  }

Compile as follows:

ikvmstub mscorlib
javac -cp \ikvm\lib\ikvm-api.jar;mscorlib.jar test.java
ikvmc test.class -r:mscorlib -platform:x86

The DllExport annotation only works when you explicitly specify -platform:x86 or -platform:x64. In the future ARM will also be supported, but I'll need to have an ARM Windows 8 system first. Since it emits an unmanaged entry point, the resulting assembly is a platform specific mixed assembly.

Changes:

  • Updated version to 7.1.4336.0.
  • Added ikvmc warnings for referencing non-primary assembly in shared class loader group and duplicate references.
  • Removed ikvmc -platform:Itanium option.
  • Added ikvm.lang.DllExport annotation to export static methods as unmanaged exports.
  • Implemented a different pinning scheme for GetPrimitiveArrayCritical() that tolerates broken code that passes back the wrong pointer to Release. Should also perform better in most cases. GetStringCritical() now also pins instead of copies. Note that we intentionally ignore the mode parameter in ReleasePrimitiveArrayCritical() as HotSpot does the same.
  • Added include stdarg.h to jni.h make the header file compile by itself.
  • Added JNI_TRUE and JNI_FALSE to jni.h.
  • IKVM.Reflection: Fixed various assembly name parsing bugs in managed Fusion CompareAssemblyIdentity.
  • IKVM.Reflection: Re-implemented (most of) AssemblyName without dependency on System.Reflection.AssemblyName.

Binaries available here: ikvmbin-7.1.4336.zip

Tuesday, 15 November 2011 07:25:45 (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
# Monday, 14 November 2011
IKVM.NET 7.0 Release Candidate 0

The first release candidate is available. No changes (except the version number and strong naming) relative to the last development snapshot.

What's New (relative to IKVM.NET 0.46):

  • Integrated OpenJDK 7 b147.
  • Implemented Java 7 JVM changes.
  • Java annotations on statically compiled code are now returned as java.lang.reflect.Proxy objects for compability with broken code that assumes this is always the case.
  • Added delegate conversion for java.lang.reflect.InvocationHandler to ikvm.runtime.Delegates.
  • Various remap file improvements.
  • Changed build and JNI code to use different names for the Windows x86 and x64 versions of the native dll.
  • Retain reflection field ordering for ikvmc compiled code (not required by spec, but to improve compatibility with broken code).
  • Various AWT fixes.
  • Interop between java.lang.AutoCloseable and System.IDisposable.
  • Various build system improvements.
  • Added ikvmc -warnaserror option.
  • Fixed java.io.FileOutputStream in append mode to use atomic append.
  • Various performance improvements.
  • Added -Xnoglobbing option to ikvm.exe.
  • Various minor fixes.
  • Implemented dual stack sockets (Windows Vista and up only).
  • Implemented platform MBean server.
  • Implemented SocketChannel.sendOutOfBandData().
  • Implemented DatagramChannel multicast methods.
  • Removed mapping of System.Security.VerificationException to java.lang.VerifyError.
  • IKVM.Reflection: Massive enhancements to support a very large portion of the managed PE capabilities (much more than System.Reflection.Emit).

When the final release is done, it will include the full release notes.

Binaries available here: ikvmbin-7.0.4335.0.zip

Sources: ikvmsrc-7.0.4335.0.zip, openjdk7-b147-stripped.zip

Monday, 14 November 2011 14:01:32 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
Managed PE File Types

Just a quick blog about the different types of managed PE files.

Here's a table:

Description C# compiler switch PE type machine corflags
MSIL /platform:anycpu (default) PE32 x86 ILONLY
MSIL 32 bit preferred /platform:anycpu32bitpreferred PE32 x86 ILONLY | 32BITREQUIRED | 32BITPREFERRED
x86 managed /platform:x86 PE32 x86 ILONLY | 32BITREQUIRED
x86 mixed n/a PE32 x86 32BITREQUIRED
x64 managed /platform:x64 PE32+ x64 ILONLY
x64 mixed n/a PE32+ x64  
ARM managed /platform:arm PE32 ARM ILONLY
ARM mixed n/a PE32 ARM  

The MSIL 32 bit preferred and ARM types are new in .NET 4.5. I've left out Itanium, because it's not that interesting.

For legacy reasons pure MSIL files specify x86 as the machine type and when you really want to target just x86 you need to set the (confusingly named) COMIMAGE_FLAGS_32BITREQUIRED flag.

When the COMIMAGE_FLAGS_32BITREQUIRED flag was introduced (back in .NET 1.0) they probably intended it to just mean that the image required a 32 bit architecture (not necessarily x86), but then the C# compiler mapped the /platform:x86 switch to this flag and they were locked into it meaning just x86.

Starting with Visual Studio 2010, the default platform target project setting for executables changed from Any CPU to x86. This was probably because x64 systems were becoming more popular and more people started running into the poor x64 JIT performance and its many bugs.

With the introduction of ARM support in Windows 8 there was now a need for a new flag to avoid the x64 JIT, but still be cross platform, so the COMIMAGE_FLAGS_32BITPREFERRED flag was invented. For downward compatibility, this flag is not set by itself, but instead modifies the meaning of the COMIMAGE_FLAGS_32BITREQUIRED flag, to allow .NET 4.0 systems to continue to run the executable in 32 bit mode (they simply ignore the new flag) while on ARM systems the system can see that the executable is not x86 specific because the new flag is set.

Finally, when your PE file contains unmanaged exports the ILONLY flag must not be set or the loader will refuse to load the file.

Monday, 14 November 2011 09:18:51 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Monday, 07 November 2011
New Development Snapshot

I've implemented the JDK 7 method overriding behavior. So this should be the final snapshot before the 7.0 release candidate.

It's really a testament to the .NET method overriding model that I was able to easily implement all the weird JDK 7 behavior without resorting to any hacks. It's a simple matter of using MethodAttributes.NewSlot and TypeBuilder.DefineMethodOverride() in the right places.

Changes:

  • Updated LICENSE and THIRD_PARTY_README to OpenJDK 7. Thanks to Martin for pointing out in the comments this was still missing.
  • Implemented JDK 7 method overriding behavior (minus the part 5 bug).

Binaries available here: ikvmbin-7.0.4328.zip

Monday, 07 November 2011 07:46:12 (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
Java Method Overriding Is FUBAR Part 6 of ∞

Parts 1 through 5 described behavior that is new in JDK 7, but there are also pre-existing bugs.

When you declare a static method as final (which makes no sense and there is no good reason this was ever allowed) you disallow a virtual method in a base class from being overridden:

public class A
{
  public void foo() {
    System.out.println("A.foo");
  }
}

public class B extends A
{
  public static final void foo() {
    System.out.println("B.foo");
  }
}

public class C extends B
{
  public void foo() {
    System.out.println("C.foo");
  }
}

public class test
{
  public static void main(String[] args) {
    new C();
  }
}

After you compile this (the easiest trick is probably to rename the B.foo method to f__ and after you compile use a hex editor to patch the resulting class file) and run it, you get:

Exception in thread "main" java.lang.VerifyError: class C overrides final method foo.()V
        at ...

Like I said, this is not new. I also tried this on JDK 1.1 and JDK 1.5 and on 1.1 behaves as expected and 1.5 has the modern behavior.

Monday, 07 November 2011 07:12:25 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Friday, 04 November 2011
Java Method Overriding Is FUBAR Part 5 of ∞

One of the bizarre things is that the method overriding behavior changed between Java 6 and Java 7 (and HotSpot preserves the Java 6 behavior for classes that have major class file version < 51), but the spec doesn't mention this at all, where it generally is pretty good about mentioning different behavior based on class file version.

Here's an example that uses a class hierarchy that mixes version 50 and 51 classes to confuse HotSpot JDK 7:

package p1;

public class A
{
  { foo(); }
  public void foo() {
    System.out.println("A.foo");
  }
  public static void main(String[] args) {
    new p4.D();
  }
}

package p2;

public class B extends p1.A
{
  { foo(); }
  void foo() {
    System.out.println("B.foo");
  }
}

package p3;

public class C extends p2.B
{
  { foo(); }
  void foo() {
    System.out.println("C.foo");
  }
}

package p4;

public class D extends p3.C
{
  { foo(); }
  void foo() {
    System.out.println("D.foo");
  }
}

Here's a table view of the structure:

p1   p2   p3   p4
A      
  B    
    C  
      D

After you compile this with javac 7 (again using the trick to first compile without the public modifier on A.foo) then use a hex editor to modify p3/C.class to change the class file version from 51 (33 hex) to 50 (32 hex) at offset 7 in the file.

When you run it you get:

D.foo
D.foo
Exception in thread "main" java.lang.AbstractMethodError: p3.C.foo()V
        at p3.C.(C.java:5)
        at p4.D.(D.java:3)
        at p1.A.main(A.java:10)

(This is a variation of the bug I reported here.)

Friday, 04 November 2011 08:54:44 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]