Search code examples
.netdllx8632-bitikvm

IllegalArgumentException in IKVM-compiled DLL


Quick Summary:

My IKVM-compiled JAR-to-DLL libraries work with some methods and classes in a .NET project, but a particular one is throwing an exception that seems to indicate my IKVM.Runtime.JNI is attempting to store a 64bit address to a 32bit variable, when I want the whole system to be 32bit. This causes an IllegalArgumentException to be thrown.

The Problem

I have a JAVA API in the form of multiple JAR files that needs to be integrated into a .NET project. The JAR files search for the API's license and config files based on directories in the CLASSPATH. The JAR files also use JNI calls to included DLLs in the java.library.path.

The .NET project has an x86 target. The DLLs included for JNI have both win64 and win32 versions, so I use the ones in the win32 directory.

This is all happening in VS2010, using .NET Framework 4. The OS is 64bit Windows 7.

The Plan

Okay, so the plan is to use the IKVM compiler to compile the JAR files to DLLs. Then, include those DLLs in the .NET project, along with IKVM libraries, and be able to interact with the JAVA API in that manner.

Compilation

First, I compile the log4j jar:

~>ikvmc -target:library log4j-1.2.14.jar

IKVM.NET Compiler version 7.3.4830.0
Copyright (C) 2002-2013 Jeroen Frijters
http://www.ikvm.net/

note IKVMC0002: Output file is "log4j-1.2.14.dll"
warning IKVMC0100: Class "javax.jms.MessageListener" not found
...

There is about 20 or so warnings dealing with javax.jms.* and javax.mail.* mostly, but it seems to compile fine.

And then, I pull in the rest.

~>ikvmc -platform:x86 -target:library -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -r:log4j-1.2.14 { commons-io-1.4.jar } { commons-lang3-3.1.jar } { EngineAPI_PC-1.0.13.jar } { EngineAPI_PC-api-1.0.13.jar } { guice_no_aop-3.0.jar } { guice-assistedinject-3.0.jar } { inject-330.jar } { IVectorsMultiSpeaker4GResources-1.0.1.jar } { IVectorsResources-1.0.6.jar } { slf4j-api-1.7.2.jar } { slf4j-log4j12-1.7.2.jar }
IKVM.NET Compiler version 7.3.4830.0
Copyright (C) 2002-2013 Jeroen Frijters
http://www.ikvm.net/

warning IKVMC0126: Found assembly "log4j-1.2.14" using legacy search rule, pleas
e append '.dll' to the reference
note IKVMC0002: Output file is "commons-io-1.4.dll"
note IKVMC0002: Output file is "commons-lang3-3.1.dll"
note IKVMC0002: Output file is "EngineAPI_PC-1.0.13.dll"
note IKVMC0002: Output file is "EngineAPI_PC-api-1.0.13.dll"
note IKVMC0002: Output file is "guice_no_aop-3.0.dll"
note IKVMC0002: Output file is "guice-assistedinject-3.0.dll"
note IKVMC0002: Output file is "inject-330.dll"
note IKVMC0002: Output file is "IVectorsMultiSpeaker4GResources-1.0.1.dll"
note IKVMC0002: Output file is "IVectorsResources-1.0.6.dll"
note IKVMC0002: Output file is "slf4j-api-1.7.2.dll"
note IKVMC0002: Output file is "slf4j-log4j12-1.7.2.dll"
warning IKVMC0112: Emitted java.lang.IllegalAccessError in "es.agnitio.core3.Voi
ceSampleImpl.getIdentifiableData()Ljava.util.List;"
    ("Try to access class es.agnitio.core3.a from class es.agnitio.core3.VoiceSa
mpleImpl")
    (in EngineAPI_PC-1.0.13.dll)
warning IKVMC0112: Emitted java.lang.IllegalAccessError in "es.agnitio.core3.Voi
ceSampleImpl.getIdentifiableData()Ljava.util.List;"
    ("Try to access method es.agnitio.core3.a.<init>(Les.agnitio.core3.VoiceSamp
leImpl;Les.agnitio.core3.IdentifiableData;)V from class es.agnitio.core3.VoiceSa
mpleImpl")
    (in EngineAPI_PC-1.0.13.dll)

As you can see, I see -platform:x86 to ensure 32 bit DLLs. Using -classloader:... is necessary to be able to edit the CLASSPATH later on include the directories with the config and license files. I reference the log4j DLL and list the remaining JARs. After this, I have DLLs for each file.

Setting Up Project

In the .NET project, I reference:

  • All DLLs in the IKVM/bin directory except for ikvm-native-*.dll
  • The JVM.DLL in IKVM/bin-x86
  • The compiled DLLs from the the previous step.

I add ikvm-native-win32-x86.dll to the project as a link to be included with the project build. I do not add any of the IKVM EXEs to the project. I should also note that at no point does the project debug output indicate that it uses JVM.DLL or ikvm-native-win32-x86.dll, despite being able to make JNI calls.

My project has an App.config, where ikvm:java.library.path and ikvm:java.class.path are set:

<appSettings>
    <add key="ikvm:java.library.path" value="C:\path\to\DLLs\for\JNI;" />
    <add key="ikvm:java.class.path" value="C:\path\to\config;C:\path\to\license;" />
</appSettings>

The App.config also has assembly bindings for all the IKVM libraries.

Compiling and Running the Project

I include the java.io and es.agnitio.* namespaces. I am able to use methods and classes from the JAVA project and compile successfully. The library also successfully finds the config, license, and JNI DLL files.

When I run the application, the first call to a JAVA function stalls for awhile while JAVA libraries are loaded. At this point, I get this output:

[17:42:38.76691 ] loadLibrary: C:\path\to\DLLs\for\JNI\predj.dll, class loader: ikvm.runtime.ClassPathAssemblyClassLoader@3EDD7A7
[17:42:38.80491 ] Library loaded: C:\path\to\DLLs\for\JNI\predj.dll, handle = 0xF800000

for all of the JNI libraries, and I see native methods being linked successfully. After this, several of the Agnitio JAVA API classes and methods work as expected, and I am able to perform JAVA file IO. However, a particular method gives this exception:

*** exception in native code ***
java.lang.IllegalArgumentException: Can not set long field es.agnitio.data.Nativ
eMemoryJNI.ptrAddress to es.agnitio.core.ArrayForNativeCode
System.Collections.ListDictionaryInternal
Can not set long field es.agnitio.data.NativeMemoryJNI.ptrAddress to es.agnitio.
core.ArrayForNativeCode

   at __<Setter>(IReflectionException , Object , Int64 , Object )
   at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.FieldA
ccessor`1.lazySet(Object obj, T value)
   at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.FieldA
ccessor`1.lazySet(Object obj, T value, FieldAccessor`1 acc)
   at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.LongFi
eld.setLong(Object obj, Int64 value)
   at IKVM.Runtime.JNIEnv.SetLongField(JNIEnv* pEnv, IntPtr obj, IntPtr fieldID,
 Int64 val)
   at es.agnitio.ivectors.IVectorsNative.updateStreaming4GSessionS(ArrayForNativ
eCode afnc1, ArrayForNativeCode afnc2, ArrayForNativeCode afnc3)
   at es.agnitio.ivectors.h.b(FeaturesNoJNA fnjna)
   at es.agnitio.core3.internal.d.a(FrontEndResult fer, Int32 i1, Int32 i2)
   at es.agnitio.core3.internal.d.a(Int32 i1, Int32 i2, List l)
   at es.agnitio.core3.internal.b.extractVoiceSamples(Int32 i, List l)
   at es.agnitio.core3.internal.b.extractVoiceSamples(Int32 i)
   at es.agnitio.core3.internal.b.extractVoiceSample(List l)
   at es.agnitio.modeling.ModelFactoryAbstract.a(List , List , List )
   at es.agnitio.modeling.ModelFactoryAbstract.trainModelFromAudio(AudioStandard
 as, List l)
   at es.agnitio.modeling.ModelFactoryAbstract.trainModelFromAudio(AudioStandard
 as)
   at BS3Test.Program.Main(String[] args) in C:\path\to\source\Program.cs:line 38

My Thoughts

It looks like the Agnitio library is storing the address of some object in a 32 bit variable, but the IKVM library methods are converting it to a 64 bit address, hence the IllegalArgumentException. A lot of documentation online says ikvm-native-*.dll determines whether JNI calls are made as 32 bit or 64 bit, but it doesn't seem like my JVM.dll or ikvm-native-win32-x86.dll are being involved in this process at all. Any ideas on how to fix this issue (or a better way of integrating this JAVA library)?

Edit: My initial impression was incorrect. ikvm-native-win32-x86.dll and JVM.dll are successfully included in the project in their 32bit version. I'm at a bit of a loss as to what the error message indicates, now.


Solution

  • This is probably a bug in the native code. It looks like it tries to set the field NativeMemoryJNI.ptrAddress but it passes an object of type ArrayForNativeCode instead of NativeMemoryJNI (or a subclass).