Eric Lippert on infoof()

May 22, 2009 at 06:42 PM | categories: Uncategorized | View Comments

I previously mentioned some hypothetical C# fieldof and methodof operators, which would obtain FieldInfo and MethodInfo objects, in the same way that typeof retrieves Type at runtime.

Eric Lippert explains the pros and cons of a combined infoof operator, which mainly centre around how hard it would be to pick the right method overload each time.

In the comments, Jonathan Pryor points out my favourite replacement for methodof:

Fastest by far in my testing -- faster than string-based Reflection, in fact -- is Mike's "Poor's man infoof" using the Delegate.Method property. This effectively bypasses most of the Reflection infrastructure (no Type.GetMember(), etc.), and is the closest we can get to IL member lookup resolution.

Read and Post Comments

Inline IL assembly

April 23, 2009 at 09:05 PM | categories: Uncategorized | View Comments

Writing about C# and IL made me wonder how hard it would be to give inline assembly capabilities to, say, C#. (F# already has inline IL; I'm not sure about other .NET languages.)

It turns out to be surprisingly easy to write a tool that injects IL into a .NET assembly, after the compiler has finished, using Mono Cecil. In fact, the example on the Cecil FAQ injects WriteLine calls at the start of each method.

I put together a proof of concept that lets you write code like this:

public static class Program
{
    private static int Calculate()
    {
        IL.Push(10, 1, 1);
        IL.Emit("add");
        IL.Emit("mul");
        IL.Emit("ret");
        return 0;
    }
 
    public static void Main()
    {
        Console.WriteLine("(1 + 1) * 10 = {0}", Calculate());
    }
}

The methods on the IL class are placeholders that get replaced by a separate post-processor:

  • IL.Push: Removed by the post-processor, leaving the arguments behind on the virtual machine stack
  • IL.Emit: Replaced with the appropriate opcode by the post-processor
  • IL.Pop: Removed by the post-processor, allowing a value on the VM stack to be consumed by regular C# code

The IL class needs to be defined as part of your application. Although the code inside this class isn't important -- it'll never be executed -- the way the methods are declared is important:

  • IL.Push needs separate generic overloads taking 1, 2, 3, etc. parameters. If we defined one method, taking params object[], then the compiler would construct an array. If we defined overloads taking object instead of generic types, then the compiler would box value types.
  • IL.Emit is straightforward, taking a single string parameter.
  • IL.Pop returns a generic value. It's up to you to specify this generic type according to what's on the stack.

One limitation with this demo is that the IL.Emit method just takes a string, so no MethodInfo, Label etc., and no calls, branching, etc. I haven't thought of a decent way of specifying this optional parameter in a call to IL.Emit: maybe another string, suitably encoded?

Here's the code for the post processor, conveniently provided as an NUnit test fixture. Its purpose is to load an assembly using Mono Cecil, loop through each method, recognise the IL emitted by the C# compiler for each of the three IL methods, replace those calls with chunks of real IL, and save a new assembly. A word of advice: peverify is essential for testing: it's easy to generate unverifiable IL this way.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
using NUnit.Framework;
 
[TestFixture]
public class InlineIL
{
    private static bool ReplaceInstructions(MethodDefinition method)
    {
        CilWorker worker = method.Body.CilWorker;
        foreach (Instruction instruction in method.Body.Instructions)
        {
            if (instruction.OpCode == OpCodes.Ldstr)
            {
                Instruction next = instruction.Next;
                if (next == null)
                    continue;

                if (next.OpCode != OpCodes.Call)
                    continue;

                MethodReference operand = (MethodReference) next.Operand;
                if (operand.DeclaringType.Name != "IL")
                    continue;

                switch (operand.Name)
                {
                    case "Emit":
                        string asm = ((string) instruction.Operand).Replace('.', '_');
                        FieldInfo field = typeof(OpCodes).GetField(
                            asm,
                            BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase);

                        if (field == null)
                            throw new InvalidOperationException("Unrecognised opcode " + asm + ".");

                        OpCode opCode = (OpCode) field.GetValue(null);
                        Instruction replacement = worker.Create(opCode);
                        worker.Replace(instruction, replacement);
                        worker.Remove(next);
                        return true;
                }
            }
            else if (instruction.OpCode == OpCodes.Call)
            {
                MethodReference operand = (MethodReference) instruction.Operand;
                if (operand.DeclaringType.Name != "IL")
                    continue;

                switch (operand.Name)
                {
                    case "Push":
                    case "Pop":
                        worker.Remove(instruction);
                        return true;
                }
            }
        }

        return false;
    }

    [Test]
    public void ExpandInlineAsm()
    {
        AssemblyDefinition assembly = AssemblyFactory.GetAssembly("Temp.exe");
        IEnumerable<MethodDefinition> methods =
            from module in assembly.Modules.Cast<ModuleDefinition>()
            from type in module.Types.Cast<TypeDefinition>()
            from method in type.Methods.Cast<MethodDefinition>()
            select method;

        foreach (var method in methods)
        {
            while (ReplaceInstructions(method))
            {
            }
        }

        AssemblyFactory.SaveAssembly(assembly, "Temp.new.exe");
    }
}
Read and Post Comments

More IL features missing from C#

April 20, 2009 at 09:30 PM | categories: Uncategorized | View Comments Wesner Moise wrote about IL features missing in C#: extended precision for floating-point calculations, and tail call optimizations. His post reminded me of a few more IL features that could be useful in C# but that aren't exposed or aren't documented.

ldtoken: This is the opcode behind typeof. ldtoken also operates on fields and methods, opening up the possibility of hypothetical fieldof and methodof keywords: these might return FieldInfo and MethodInfo objects at runtime respectively, allowing you to write reflection code that's checked at compile time.

There is in fact a trick you can use to get hold of a MethodInfo object without having to call Type.GetMethod or the like:

MethodInfo methodInfo = new Action<string>(Console.WriteLine).Method;

However, because this trick instantiates a new delegate to use its Method property, it's only useful when:

  • You're dealing with static methods
  • You have an instance of the right type

Note that this trick doesn't work on property accessors, since there's no delegate syntax for these like there is for methods.

Typed references: You may have noticed the System.TypedReference class documented in MSDN, and wondered what it was useful for. C# supports typed references -- effectively safe, verifiable pointers -- via three undocumented keywords.

int i = 42;
TypedReference reference = __makeref(i);
Debug.Assert(__reftype(reference) == typeof(int));
Debug.Assert(__refvalue(reference, int) == 42);

I haven't seen any official statement from Microsoft saying that this technique is going to stop working any time soon, but they don't seem to be in a hurry to fix bugs relating to these keywords.

Read and Post Comments

Making sense of XSD type names, in C#

April 17, 2009 at 11:00 PM | categories: Uncategorized | View Comments

In response to a conversation with @phil_nash earlier this week:

I have some data from an XML document, along with a string indicating the type of that data, as one of the XSD type names. The .NET XML serializer knows all about these XSD types, since it'll emit xsi:type attributes on elements that correspond to .NET properties of type object.

However, there doesn't seem to be an easy way of parsing these XSD type names from your own code. A little digging led me to the XmlSerializationReader.ReadTypedPrimitive method, which is (naturally) declared as private.

At this point I have three options:

  1. Call ReadTypedPrimitive directly, using Reflection. Evil, since I'd rather not write code that accesses framework methods declared as private.
  2. Write my own equivalent of the ReadTypedPrimitive method. This appears to involve a giant switch statement.
  3. Come up with some way of persuading the framework to call ReadTypedPrimitive on my behalf.

The XmlSerializationReader class is in fact used as a base class for the code generated for you when you call the XmlSerializer constructor. I realised that, in order to arrange for the framework to make this method call, I just need a small serializable class that results in the right auto-generated code.

In summary, I went with option 3: construct an XML document in memory, containing the string I originally wanted converted, and the XSD type name supplied to me. Running that through the XmlSerializer causes ReadTypedPrimitive -- with its large switch statement -- to get called, and I end up with a .NET object whose type corresponds to the XSD type of my choice.

using System;
using System.Diagnostics;
using System.Xml;
using System.Xml.Serialization;

public class XmlValueWrapper
{
    private object value;

    public object Value
    {
        get { return value; }
        set { this.value = value; }
    }
}

public static class XsdConvert
{
    private static XmlSerializer serializer = new XmlSerializer(typeof(XmlValueWrapper));

    public static object ConvertFrom(string value, string xsdType)
    {
        XmlDocument doc = new XmlDocument();
        XmlElement rootElement = (XmlElement) doc.AppendChild(doc.CreateElement("XmlValueWrapper"));
        rootElement.SetAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema");

        XmlElement valueElement = (XmlElement) rootElement.AppendChild(doc.CreateElement("Value"));
        valueElement.SetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance", xsdType);
        valueElement.AppendChild(doc.CreateTextNode(value));

        using (XmlNodeReader reader = new XmlNodeReader(doc))
        {
            XmlValueWrapper wrapper = (XmlValueWrapper) serializer.Deserialize(reader);
            return wrapper.Value;
        }
    }
}

public static class Program
{
    public static void Main()
    {
        Debug.Assert(Equals(42, XsdConvert.ConvertFrom("42", "xs:int")));
        Debug.Assert(Equals(42.0, XsdConvert.ConvertFrom("42", "xs:double")));
        Debug.Assert(Equals(42m, XsdConvert.ConvertFrom("42", "xs:decimal")));
        Debug.Assert(Equals("42", XsdConvert.ConvertFrom("42", "xs:string")));
        Debug.Assert(Equals(true, XsdConvert.ConvertFrom("true", "xs:boolean")));
        Debug.Assert(Equals(new DateTime(2009, 4, 17), XsdConvert.ConvertFrom("2009-04-17", "xs:date")));
    }
}
Read and Post Comments

Social networking sites via Excel?

April 17, 2009 at 04:45 PM | categories: Uncategorized | View Comments Spreadtweet makes Twitter look like Excel:

So, you work at a big corporate, huh?
And you're not allowed to use Twitter...
Wouldn't it be awesome if there were a Twitter tool
that looked just like Excel?

We need more of this kind of thing – Faceworkbook, anyone? Maybe I’ve been programming Excel too long, if I’m getting excited about this.

Read and Post Comments

« Previous Page -- Next Page »