# Friday, January 25, 2013
« New Development Snapshot | Main | New Development Snapshot »
Fixing a Long Standing Limitation

I finally fixed the limitation that classes dynamically loaded by an assembly class loader can't access the internals of the assembly.

Here's a simple demonstration of the problem. Suppose you have these two classes:

class Other {
  static {
    Test.m();
  }
}

class Test {
  public static void main(String[] args) throws Exception {
    Class.forName("Other");
  }
  static void m() {
    System.out.println("Test.m");
  }
}

Now if you compile Test.class (without Other) into an assembly with a ClassPathAssemblyClassLoader and run this with IKVM.NET 7.2 you'll see something like this:


C:\j>ikvmc Test.class -classloader:ikvm.runtime.ClassPathAssemblyClassLoader
IKVM.NET Compiler version 7.2.4630.6
Copyright (C) 2002-2013 Jeroen Frijters
http://www.ikvm.net/

note IKVMC0001: Found main method in class "Test"
note IKVMC0002: Output file is "Test.exe"

C:\j>Test
Exception in thread "main" java.lang.ExceptionInInitializerError
        at cli.System.Runtime.CompilerServices.RuntimeHelpers._RunClassConstructor(Unknown Source)
        at IKVM.Internal.TypeWrapper.RunClassInit(Unknown Source)
        at IKVM.NativeCode.java.lang.Class.forName0(Unknown Source)
        at java.lang.Class.forName(Class.java:287)
        at Test.main(Test.java:9)
Caused by: cli.System.MethodAccessException: Test.m()
        at Other.<clinit>(Test.java)

The call from Other to Test fails with a MethodAccessException, because the dynamic assembly does not have access to the internal members of the Test.exe assembly.

Long ago I considered fixing this by adding an InternalsVisibleToAttribute to all statically compiled assemblies that allows the runtime generated dynamic assembly access to its internals, but I dismissed this because it would mean that untrusted code could abuse this same assembly identity to access the internals of any ikvmc compiled assemblies.

Forging a StrongNameKeyPair

Yesterday I realized that I could use InternalsVisibleToAttribute without opening a security hole. When the IKVM runtime is running as fully trusted code, it can forge a StrongNameKeyPair to attach to the dynamic assemblies it generates and thereby gain access to the statically compiled assemblies, without giving untrusted code the same access.

The only downside to this approach is that it relies on an implementation detail of the CLR (and Mono). I try to stay away from depending on implementation details, but in this case the gain far outways any risk.

Using It From C#

It also works for your C# assemblies. By adding a custom attribute, you can now enable dynamically loaded Java classes access to your assembly internals.

Here's a C# example:

using System;
using System.Runtime.CompilerServices;
using java.lang;
using java.lang.reflect;

[assembly: InternalsVisibleTo("DemoApp-ikvm-runtime-injected, PublicKey=00240000048000009400000006020000002400005253413100040000010001009D674F3D63B8D7A4C428BD7388341B025C71AA61C6224CD53A12C21330A3159D300051FE2EED154FE30D70673A079E4529D0FD78113DCA771DA8B0C1EF2F77B73651D55645B0A4294F0AF9BF7078432E13D0F46F951D712C2FCF02EB15552C0FE7817FC0AED58E0984F86661BF64D882F29B619899DD264041E7D4992548EB9E")]

interface IFoo {
  Program Instance { get; }
}

class Program : InvocationHandler {
  static void Main() {
    Class c = typeof(Program);
    var cl = c.getClassLoader();
    var interfaces = new Class[] { typeof(IFoo) };
    var handler = new Program();
    var proxy = (IFoo)Proxy.newProxyInstance(cl, interfaces, handler);
    Console.WriteLine(proxy.Instance);
  }
  public object invoke(object obj, Method m, object[] objarr) {
    return this;
  }
}

Here we create a Proxy for the non-public interface IFoo. This is allowed because of the InternalsVisibleToAttribute that gives access to an assembly named DemoApp-ikvm-runtime-injected with the specified public key. The name of the assembly must be the name of the current assembly (in this example "DemoApp") with "-ikvm-runtime-injected" appended. The public key is always the same. The corresponding private key does not exist.

Development Snapshot

Please note that this is a development snapshot, not a release or release candidate. So consider it untested.

Changes:

  • Use InternalsVisibleToAttribute to allow the runtime to dynamically inject classes into statically compiled assemblies.
  • Stop generating proxy helpers for non-public interfaces.
  • Added support for RadialGradientPaint in Graphics.setPaint().

Binaries available here: ikvmbin-7.3.4772.zip

Friday, January 25, 2013 7:51:20 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]