# Wednesday, 23 March 2011
« IKVM.NET 0.46 Release Candidate 1 | Main | IKVM.NET 0.46 Released »
IKVM.Reflection Update

Missing Members

To support mcs I previously added some limited support for missing members. This has now been extended substantially to allow full fidelity reading/writing of assemblies. Normally IKVM.Reflection behaves like System.Reflection in how it tries to resolve member and assembly references, but now it is possible to customize this to enable reading a module without resolving any dependencies.

Roundtripping

To test this version of IKVM.Reflection, I've roundtripped 1758 (non-C++/CLI) modules from the CLR GAC plus the Mono 2.10 directories on my system. Roundtripping here means reading in the module and writing it back out. The original module and resulting module are both disassembled with ildasm and the resulting IL files compared (after going through two dozen regex replacements to filter out some trivial differences).

The code I used for the roundtripping is an (d)evolution of the linker prototype code. It is available here. Note that it is very far from production quality, but does make for a good case study in how to use the advanced IKVM.Reflection functionality.

Unmanaged Exports

A cool CLR feature is the ability to export static managed methods as unmanaged DLL exports. The only languages that support this, AFAIK, are C++/CLI and ILASM, but now you easily do it with IKVM.Reflection as well. Below is a Hello World example. Note that while this example exports a global method, you can export any static method. Note also that the string passed in must be ASCII, may be null and that the ordinal must be a 16 bit value that is unique (in the scope of the module).

using IKVM.Reflection;
using IKVM.Reflection.Emit;

class Demo
{
  static void Main()
  {
    var universe = new Universe();
    var name = new AssemblyName("ExportDemo");
    var ab = universe.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save);
    var modb = ab.DefineDynamicModule("ExportDemo", "ExportDemo.dll");
    var mb = modb.DefineGlobalMethod("HelloWorld", MethodAttributes.Static, null, null);
    var ilgen = mb.GetILGenerator();
    ilgen.EmitWriteLine("Hello World");
    ilgen.Emit(OpCodes.Ret);
    mb.__AddUnmanagedExport("HelloWorld", 1);
    modb.CreateGlobalFunctions();
    ab.Save("ExportDemo.dll", PortableExecutableKinds.Required32Bit, ImageFileMachine.I386);
  }
}

The resulting ExportDemo.dll can now be called from native code in the usual ways, for example:

    typedef void (__stdcall * PROC)();
    HMODULE hmod = LoadLibrary(L"ExportDemo.dll");
    PROC proc = (PROC)GetProcAddress(hmod, "HelloWorld");
    proc();

Creating unmanaged exports is supported for I386 and AMD64. Itanium (IA64) support has not been implemented.

What's Missing

It should now be possible to write an ILDASM clone using IKVM.Reflection, however to be able to write ILASM there are still a couple of things missing:

  • Function pointers (used by C++/CLI).
  • API to create missing type.
  • Preserving interleaved modopt/modreq ordering.
  • Various C++/CLI quirks (e.g. custom modifiers on local variable signatures).
  • Ability to set file alignment.

Changes:

  • Added support for using missing members in emitted assembly.
  • If you build IKVM.Reflection with the STABLE_SORT symbol defined, all metadata sorting will be done in a stable way, thus retaining the order in which the items are defined.
  • When target is .NET 1.x adjust the type name writing algorithm to match the quirks of the Microsoft .NET 1.x metadata writer.
  • Fixed Type.StructLayoutAttribute to be (bug) compatible with .NET.
  • Don't map unspecified pinvoke calling convention to winapi.
  • Don't needlessly read the contents of a resource module.
  • Automatically emit .NET 1.x compatible declarative security when appropriate.
  • Added support for having multiple mscorlib versions in a universe.
  • Added Assembly.__AssemblyFlags property.
  • Added CustomAttributeBuilder.__FromBlob().
  • Added CustomAttributeBuilder.__MakeTypedArgument() to make it possible to box an enum or create an array of an enum type in a location typed as object.
  • Added ILGenerator.__MaxStackSize property.
  • Added EventInfo.__GetMethods().
  • Added FieldBuilder.__SetReadOnlyDataAndRVA().
  • Added FieldInfo.__FieldRVA property.
  • Added FieldInfo.__GetFieldOnTypeDefinition().
  • Added ManifestResourceInfo.__ResourceAttributes property.
  • Added MethodBase.__GetMethodOnTypeDefinition().
  • Added MethodBuilder.__AddUnmanagedExport().
  • Added MethodBuilder.__SetCallingConvention().
  • Added Module.__ImageBase property.
  • Added Module.__GetCustomAttributesFor().
  • Added Module.__GetExportedTypes().
  • Added Module.__GetReferencedModules().
  • Added Module.__GetReferencedTypes().
  • Added Module.__GetSectionInfo().
  • Added Module.__ModuleHash property (for resource modules).
  • Added Module.__ReadDataFromRVA().
  • Added Module.__ResolveReferencedAssemblies().
  • Added Module.__StackReserve property.
  • Added ModuleBuilder.__AddModule().
  • Added ModuleBuilder.__AddModuleReference().
  • Added ModuleBuilder.__AddAssemblyReference(AssemblyName, Assembly) overload.
  • Added ModuleBuilder.__AddUnmanagedExportStub().
  • Added ModuleBuilder.__AddVTableFixups().
  • Added ModuleBuilder.__SetCustomAttributeFor().
  • Added ModuleBuilder.__SetModuleVersionId().
  • Added ModuleBuilder.__SetStackReserve().
  • Added PropertyInfo.__CallingConvention property.
  • Added Type.__GetLayout().
  • Added Type.__CreateMissingMethod(), Type.__CreateMissingField() and MissingGenericMethodBuilder.
  • Added Type.__GetArraySizes() and Type.__GetArrayLowerBounds().
  • Added Type.__MakeArrayType() overload that takes sizes and lower bounds.
  • Added TypeBuilder.__SetLayout().

Binary available here: IKVM.Reflection-0.47.4098.zip

Wednesday, 23 March 2011 08:04:24 (W. Europe Standard Time, UTC+01:00)  #    Comments [6]