• Shortcuts : 'n' next unread feed - 'p' previous unread feed • Styles : 1 2

» Publishers, Monetize your RSS feeds with FeedShow:  More infos  (Show/Hide Ads)


Date: Friday, 29 Sep 2006 05:33

Hi all,

I've decided - with lots of thought and deliberation - to take a "tinkering" break (sabbatical?) over the next little while: grok some cool new software areas and hang out with the family. I'll hopefully be taking a look at stuff like machine learning, natural language processing and more programming language goodness. You can follow the ramblings at my new blog location: http://callvirt.net/blog, posts should be more regular... ;)

thanks!

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 04 Nov 2005 15:19

Joe Duffy and I recently did an MSDN TV stint on Method Dispatch Internals. Joe concentrated on the static end of the spectrum, while I talked about late-bound binding and dynamic calling convention.

I also appeared with fellow Australian Microsofties at the recent Whidbey Ship party. Lots of fun! 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 04 Oct 2005 18:26
Joe Duffy and I were really impressed with the amount of people who showed up for the PDC session “Write a Dynamic Language compiler in an hour” at the PDC last month. It confirmed my belief that customers care for details about compiler technologies and the managed libraries that enable them. We promised source download from commnet and our blogs, so here it is: 

http://www.bluebytesoftware.com/code/05/10/GfnCompiler.zip
 

Thanks Dominic, for your help on the original presentation, much appreciated! 

What follows are resources I found useful to get bootstrapped in to the world of compiler construction. If you have any other resources you'd like to see appear on this list, drop me an email.


Tools, Languages, Source and more...:


GPPG (The Gardens Point Parser Generator): Yacc/Bison like parser generator that emits C#. This was just released recently, and looks pretty solid.  If you look on the link, the QUT folk talk about this being built for a "Ruby .NET" project in the context of parsing the full Ruby grammar. There doesn't seem to be anything mentioned officially about the Ruby .NET project but given their track record on delivery, I'm happy to get my hopes up. Can't wait to get my hands on it!

PEAPI and PERWAPI: Managed API for reading and writing managed executables. It's a lower level Reflection.Emit like interface, gives you more control over the metadata bits and bytes. I believe the Mono C# compiler uses PERWAPI as it's backend. Fast too.

IronPython: Python compiler for .NET, incubated in my team, the CLR, fronted by Jim Hugunin. A full dynamic language compiler with source, released under a liberal Shared Source license. We're getting close to 1.0 release on this one. I think this is a great starting point for anyone looking to write or port a dynamic language - Python has some interesting problems and solutions around interop (language, BCL, etc.), performance (late-bound binding and invocation), code loading, IDE integration and more. It has a very active community.

F#: A functional language with ML like flavor from Don Syme at MSR Cambridge. Very stable, fantastic VS.NET integration (both 2003 and 2.0 Beta 2) and the F# to BCL interop stuff is awesome. Lately, Don has been expanding the breadth of F# language features and investing in developer compiler interactivity (IDE/REPL etc.) to make it play great in the .NET ecosystem. I'd love to see an F# book though - mapping OCaml to F# can be tedious if you've never approached either before.

Rotor (SSCLI): Subset source to the Common Language Runtime, C# and JScript.NET compilers, shipped with an academic/hobbiest friendly license. Runs on multiple platforms and architectures (Windows, FreeBSD, MacOS - x86, PPC). It's great having the source to the CLR, C# and JScript.NET handy. I recommend buying the Rotor book (see below) if you want to ramp quickly on the source.

ECMA Specification: Submission of the CLI (Common Language Infrastructure) to the ECMA standards organization. More commonly known as "the docs to the product". Has all the execution semantics, rules and metadata bits of the CLR - exellent resource for compiler writers.

List of .NET Languages: A pretty complete list of languages that run on top of the CLR/.NET platform. Has a lot of links to the respective project websites. Some of the languages come with source too.

DOTNET-LANGUAGE-DEVS: This is the place where commercial, academic and hobbiest compiler writers hang out. The list is light on traffic, but you can usually expect a rapid response to questions.

DOTNET-CLR: A mailing list for CLR geeks. Some of the language people hang out on this list. Great place to ask questions about compiler<-->VM problems you might face when targeting the CLR.

Lambda the Ultimate: A great programming languages weblog.


Technologies:

Reflection.Emit MSDN documentation: Documentation for the Reflection.Emit namespace. Useful if you intend on using Reflection.Emit as the backend for your compiler.

Hello World, Reflection.Emit style: Hello, World!

DynamicMethod (Lightweight Code Generation): MSDN documentation for LCG. LCG is a Whidbey (2.0) technology for lightweight, GC reclaimable code generation. It uses familiar Reflection.Emit API's. Recommended in most scenarios where code generation is required and assemblies and type generation is not needed.

Hello World, Lightweight Code Generation (LCG): Hello, World, this time using LCG as the code generation technology.

Reflection Performance article: An article I wrote a few months back on how to improve the performance of common Reflection scenarios. Has some stuff on LCG and binding.

Under the hood of Dynamic Languages: Part 1 of a blog post I did about how dynamic languages map to the runtime. A lot of what I talk about here can be found in source form in the IronPython compiler. Part 2 is coming soon. ;)

Whidbey Delegates: Describes the relaxed signature matching changes we've done for Whidbey in the delegate space. What isn't mentioned is the open instance/closed static delegates that we've added - the Whidbey delegate docs describe these changes but I don't believe the latest docs are on the web yet. These small but powerful additions open up a whole new range of calling convention opportunities, especially in the dynamic languages space.

Late-bound invocation: Some notes I wrote up on late-bound invocation using Reflection (if you're going dynamic, then I hope this helps you out). Part 2 here.


Compiler books:

Compiling for the .NET Common Language Runtime, John Gough, 0130622966

The bible for compiler writers targeting the CLR.
Inside Microosft .NET IL Assembler, Serge Lidin, 0735615470

All you need to know about IL and the ILASM/ILDASM tools that ship with the SDK. If your compiler is going to target textual IL and call on the ILASM compiler to cook you up an exe, this book will be invaluable.
Compilers, Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman, 0201100886

The bible. Also featured in the movie Hackers, as a l33t resource for those hacker people.
Modern Compiler Implementation in ML, Andrew W. Appel, 0521582741

I haven't finished reading this book yet, but so far so good. It'd be better if it was "Modern Compiler Implementation in F#", but I'm biased... :) (also somes in C and Java flavors)
Programming Language Pragmatics, Michael L. Scott, 1558604421

I love the chapter on concurrent programming languages towards the end of the book. Lots of really great archaic info on languages like Ada, Fortran and Prolog. Great stuff.
Advanced Compiler Design and Implementation, Steven Muchnick, 1558603204


.NET Platform books (useful to understand what you're targeting):

Essential .NET, Volume 1: The Common Language Runtime, Don Box, 0201734117

Suitably high level to get a good understanding of the CLR. I recommend this one to all CLR newbies.
Shared Source CLI Essentials, David Stutz, Ted Neward, Geoff Shilling, 059600351X

Total ego booster when coupled with the Rotor source. Impress your friends and co-workers with your deep understanding of the CLR. Graduates of this book are prone to be hired by the CLR team. Recommended to compiler writers who want a sharp understanding of what they are targeting.
The Common Language Infrastructure Annotated Standard, Jim Miller, 0321154932

If you find the ECMA spec lacks a little oomph, try this book from CLR Architect (and part-time opera singer) Jim Miller. Annotates the ECMA spec with design decision explainations and other runtime facts. Also known to produce CLR new hires.
Applied Microsoft .NET Framework Programming, Jeffrey Richter, 0735614229

Jeff is the man. A broader platform book that goes a little deeper than Essential .NET. Useful to newbie compiler writers for two reasons: you get to learn the platform API's to write your compiler, and it gives a good insight in to how the backend works. 
Professional .NET Framework 2.0, Joe Duffy, 0764571354

Where's Joe's face?

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 01 Jul 2005 16:24

There seems to be a fair amount of recent press and blog action surrounding the dynamic or “scripting” language movement, especially when the context includes virtual machines. While I wont bother commenting on why this is the case, I figured I would cook up a few rough notes (and I do stress that these are just notes) that describe how these languages effectively and efficiently target the CLR - hopefully this will inspire you to start toying with writing or porting a language on your own. I paraphrase what John Gough (Author of Component Pascal  http://www.plas.fit.qut.edu.au/gpcp/Default.aspx) stated recently (http://channel9.msdn.com/ShowPost.aspx?PostID=49330): “Compilers are fun, they’re full of all kinds of dirty little tricks; you can be really creative because you’re dealing with the nitty gritty down at the bare metal”. I couldn’t agree more.

Disclaimer:

I hope this post will end up being the first of a series of posts on technical notes surrounding dynamic languages on the CLR. Once again though, I do stress that these are just loose ideas – there are many many ways to do what I’m showing here, the intent is just to get your brains thinking about the interesting challenges and idea’s surrounding the dynamic language space.

Secondly – none of this is optimized. The loose idea’s I illustrate are based on hypotheticals and the mapping to the CLR is sometimes far from good (you’ll see boxing, and common perf unfriendly patterns all over the place). Just keep in mind that a good, solid dynamic language compiler has the chance to optimize code for even better performance on the runtime. These optimizations, while interesting, are best left to another post.

 

The high level first, please… (skip if necessary)

What encapsulates a dynamic language? A few people have been successful in encapsulating full and detailed descriptions (http://www.tcl.tk/doc/scripting.html), but I like to think interesting dynamic languages are languages with one or more of the following things: typeless or “loosely typed” language syntax; REPL loop (Read Eval Print Loop); and most importantly, a language runtime or late-binder for expression evaluation at runtime. Of course, dynamic languages are a whole lot more than this, I’ve just constrained this post to ideas around those particular aspects.

Where can I get a CLR based dynamic language so I can follow along

IronPython (Python for the CLR) ships with full source: http://workspaces.gotdotnet.com/ironpython/.

JScript.NET (Javascript for CLR) ships with the framework redist. jsc.exe is the compiler exe, just invoke that. More on JScript here: http://www.gotdotnet.com/team/jscript/.

VB.NET – yep, VB.NET could be called a “dynamic language” when you explicitly declare:
Option Strict Off  At the top of your vb source file.

Phalanger (PHP for CLR): http://www.php-compiler.net/

There are more I’m sure. You might also want to go digging around in this list: http://www.dotnetpowered.com/languages.aspx.

Gimme quick definitions: Loosely typed languages

Your typical C# program (C# being a kind of static language) will cobble together a whole slew of statements, one of which will be a declaration of a variable:

string s;
Foo foo = new Foo();
...

“s” and “foo” are variables with the type “string” and “Foo”. If I then try and assign an integer to the “foo” variable, the C# compiler will give me back an error. These compiler errors are because of strong typing rules associated with the language – once you type a variable, it’s hard if not impossible to change that type midway through a code block. Loose typing generally means that there are loose or no typing restrictions (you can do whatever you like really). You can do things like this (IronPython):

a = "hello world"
print (a)
a = 1
print (a)

Prints:

hello world
1

Strong (or static) typing – declare type and keep it that way. Got it. Dynamic (or loose) typing, type safety not enforced, can reassign variables to whatever instance you like. Got it.

If you think further about how this maps to the CLR, hopefully you’ll stumble on some interesting questions. How does loose typing pan out in a runtime that has a common type system? What about that verifiability thing the CLR supports, does the verifier allow loosely typed languages to run? What is the actual type of “a”? I’ll answer some of those questions later in the post.

Gimme quick definitions: REPL (Read Eval Print Loop)

REPL’s are usually quite simple command line programs that read in a line of text and feed that to a compiler or interpreter for statement or expression evaluation. IronPython supports a REPL loop:

C:\IronPython-0.7.6\bin>IronPythonConsole.exe
IronPython 0.7.6 on .NET 2.0.50215.44
Copyright (c) Microsoft Corporation. All rights reserved.
>>> a = "IronPython has a REPL"
>>> print (a)
IronPython has a REPL
>>>

The bolded stuff is the important part. The “>>> “ is basically a prompt for the next piece of Python code to execute. Each string passed to IronPython’s REPL actually does something (even if it doesn’t print to the screen), mostly because every line in the Python language is a statement (a language design choice I guess). This language semantic is fairly common in dynamic languages.

Not all dynamic languages have REPL’s, and that’s probably a good thing. I like the warmth of a functional IDE myself.

Gimme quick definitions: Language runtime or Late-binder

Easiest way to explain this one is with one simple but powerful statement:

o.m()

“o” being some sort of instance, and “m” being a method.

In the static language world, the compiler when it comes to figure this one out will know the type of “o” (because it was explicitly defined), and can therefore travel to that type and lookup the method “m”. If it can’t find “m”, it will throw a compiler error in your face. If it can find “m”, the compiler will happily emit IL code that simply does a call to “m”. Quite simple: compile time resolution of “m”, and static invocation of “m” via a call instruction with the metadata token all lined up ready to go. Assuming “o” was typed as Foo, the IL would look like this example:

ldloc.0
callvirt   instance void Foo::m()

In the dynamic language world, this statement gets evaluated at run-time, and usually for good reason. Here are a couple of good ones: firstly, dynamic languages are about instance based resolution and invocation at the last possible moment – do away with as many compile time rules as possible; secondly, at compile time the compiler doesn’t know what the type of “o” is, so it’ll need to use the instance to figure out if it even has an “m()” or not; thirdly, there may be rules around language extensibility (yes, some languages allow you to do crazy things like override the “.” operator! Lua (www.lua.org) is one of my favourite languages because it offers this kind of extensibility), so those rules might need to be run before resolution and invocation.

So what can a dynamic language compiler do with “o” and “m”? Not much as it turns out, but it needs to emit some sort of code right? The compiler will generally emit code that calls in to a runtime helper to do the resolution and another runtime helper to do the invocation (or maybe both is done in the same helper method). Let’s have a look at what VB.NET with Option Strict Off does:

Option Strict Off
...
Dim o
o.m()
...

The IL code looks like this:

ldloc.0
ldnull
ldstr  "m"
ldc.i4.0
newarr [mscorlib]System.Object
ldnull
ldnull
ldnull
ldc.i4.0
call   object Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateCall(object,
                            class [mscorlib]System.Type,
                            string,
                            object[],
                            string[],
                            class [mscorlib]System.Type[],
                            bool[],
                            bool)


The three interesting parts are bolded: ldloc.0, ldstr “m” and the call to the method “NewLateBinding::LateCall”. LateCall is taking (along with other things) an instance (the ldloc) and a string name representing the method (ldstr “m”). If you think a little ahead, what can you do with an instance and a string name? Reflection baby, Reflection! That’s exactly what the VB.NET latebinder ends up doing – Reflecting on the “o” instance to find “m”, then a .Invoke() to actually invoke the method it finds.

Okay, there are your quick definitions. Let’s look at some technical details on how some of this stuff works.

 

Latebinder for callsites

Let’s look at a hypothetical example of a callsite and the late binder (the language below is imaginary so ignore that part):

Foo
{
 void m(o)
 {}
}

// part we're generating code for
o = Foo.ctor()
o.m("test")

When our hypothetical dynamic language compiler runs across these two statements, it must generate code that forwards this request to a latebound language runtime. We’ve seen this kind of thing in the example above with VB.NET, but I can’t share the latebinder code for VB.NET so we’re contriving our own:

.locals init (object V_0)
newobj     instance void Foo::.ctor()
stloc.0
ldloc.0
ldstr      "m"
ldstr      "test"
call       object [LateBinder]LateBinder::Call(object, string, object)

Code for our hypothetical late-binder:

public class LateBinder
{
   public static object Call(object o, string name, object arg)
   {
      return o.GetType().GetMethod(name).Invoke(o, new object[] { arg });
   }
      //...

The IL sets up the “o.m()” callsite as a early-bound call to our LateBinder::Call method, which calls the “m” method using Reflection late-bound.

There are a few obvious situations we’ve missed with this small hypothetical example. Calls on static methods is one (simple enough, don’t bother passing in the “this” pointer). Calls to methods with more than one argument (again, simple enough, generate code that packs the arguments in to a temporary array, and create a Call overload that takes: object[] args as an argument) another. A couple of other issues/situations to think about: calls to methods that return void when LateBinder::Call return object, calls to methods with params, calling convention issues – does the dynamic language support OO style virtual calls, performance of the calls. I’ll tackle some of these issues at a later date.

 

Loose typing on a static virtual machine

It’s been said that the CLR isn’t too good a place for dynamic languages, mostly inferences gathered because of the inherit static nature of the CIL instructions, type and signature matching, and the CLS (Common Language Specification) and CTS (Common Type System). This is first evident when a compiler developer goes to make his or her first decision – what are my languages types, and how do they map to the CLR’s CTS? There are many common types that are provided by the runtime, and I’ve previously gone in to them here (http://blogs.msdn.com/joelpob/archive/2004/07/19/187709.aspx). The question I want to draw is this one, if I have the following:

Dim o
o.m()

Or back to the IronPython example:

a = "hello world"
a = 1


What type is “o” and “a”? When a compiler goes to emit code for this, it must specify in metadata the type of the variable. You can see these type declarations in ILDASM output, locals are declared with type in the .locals section, parameters are declared with type in the method signature, and members (fields, events, delegates etc) are also strongly typed.

Back to “o” and “a”. Given that these would probably be locals to a method, we need to give them a type as part of declaration, yet still be able to reassign that variable with any other instance of any given type. Enter good ‘ol polymorphism. Everything derives from System.Object, so therefore I can assign any instance of any type (including valuetypes if you box them) to an instance typed as System.Object. If you crack open the result from VB.NET or IronPython (Jscript .NET is another good example) you can verify this for yourself.

*** Note ****
Of course there are alternatives to using System.Object. If the language you’re mapping doesn’t require BCL or cross-language interoprerability, you can pretty much do whatever you like in your self contained world.

What about methods generated by a dynamic language? Yep – the signatures almost always have parameters and returns typed as object. Crack open a IronPython method:

IronPython code:

def saysomething(a):
 print (a)

a = "hello world"
saysomething(a)


ILDASM output:

.method public static object saysomething$f0 (object a) cil managed
{
 ldarg.0
 call void [IronPython]IronPython.Objects.Ops::Print(object)
 ldnull
 ret
}

You’ll notice a couple of things here. Firstly, saysomething is a static method, returns object and takes an object. Secondly, the call to the IronPython “print” method takes an object as a parameter.

Problems with everything being object

With this in mind, consider for a second your typical Base Class Library – we try our best to be accomodating, but given the strongly typed nature of the BCL, you can imagine that your dynamic language where everything is typed as object, might have issues trying to call on it’s Base Class Library counterparts that are all strongly typed, and visa versa.

Consider this BCL callsite case:

a = "hello world"
Console.WriteLine(a)

Where Console.WriteLine has some of the following overloads:

WriteLine(string, object, object, object)
WriteLine(string, object object)
WriteLine(char[])
// ...
WriteLine(string)
WriteLine(object)
// ...

WriteLine has 19 overloads in all, 12 of which take 1 argument. Given that “a” is typed as object, which overload do you pick? Well thankfully “WriteLine” has an overload that takes object, so the compiler can safely bind to that one without any issues, but what about the cases where there exists no overloads that take object, such as:

Console.SetWindowSize(int, int)

w = 100
h = 100
Console.SetWindowSize(w, h)

“w” and “h” are both typed as object (in this case both “w” and “h” are most likely objects holding on to boxed ints, depending on how the language treats numbers under the hood), and runtime rules don’t allow calls to be made to a method taking two ints when passing in two objects – it’s not verifiable (although a program like this will run in full trust, use ildasm/asm and try it). This derives a question – how do you resolve (find), bind to and call a BCL method from a dynamic language? This can usually be done in the late-binder using Reflection, with a whole bunch of magic code to fill in the gaps in Reflections coercion/casting semantics.

What about BCL callback mechanisms, delegates and events? Given that methods take and return objects as parameters, how do you hook up one of these methods to an event when a delegate may be statically typed as void MyDelegate(string s)? I solve this and the rest of these “loose typing” problems later on in the post (yes I know, another forward reference, but be patient!).

IL instructions are typed, what happens now?

I’ll talk about a simple case here – you’ll have to use your imagination to extrapolate all the other problems associated with IL typing. Consider the following C# expression

2 + 4

The IL sequence that the C# compiler will emit is as follows:

ldc.i4.2
ldc.i4.4
add

“ldc.i4” pushes a supplied value of type int32 onto the evaluation stack as an int32. In this case, the .2 and .4 part is an alias to ldc.i4 number. The “add” instruction adds two values that are popped from the stack together and pushes the result on to the stack. There are some rules over what types “add” can pop from the stack – namely, the type of the element must be a “number” (number being things like int32, int64, natural int, float etc – the actual list can be found on msdn). Add can’t add together a Foo and Bar. Simple right?

What about our simple dynamic language case, where everything is typed as object? The “add” instruction can’t add together objects, it’s not supported (and not correct either), so what can a dynamic language do? It can generate code to a latebound “Add” method in the language runtime that will do the necessary type casting to perform the add operation.

The Add method might look like this in the language runtime:

public static object Add(object o1, object o2)
{
 if (o1.GetType() == typeof(double) && o2.GetType() == typeof(double))
   return ((double)o1) + ((double)o2);
 else if (o1.GetType() == typeof(int) && o2.GetType() == typeof(int))
   return ((int)o1) + ((int)o2);
 //...

Add takes two objects, so if we utilize the “ldc.i4” instruction to load our ints on to the stack, we’d better box them before calling the Add method. Anyway, the code for the 2 + 4 expression might look like this:

ldc.i4  2.
box     [mscorlib]System.Int32
ldc.i4  4.
box     [mscorlib]System.Int32
call    object [LateBinder]LateBinder::Add(object, object)

It seems nasty doesn’t it – firstly, we’re boxing all these ints, and performing a late-bound call to do a simple addition, there goes the perf right? In reality, the Add method (if it’s not virtual) will hopefully be inlined so there’s no method call penalty, and boxing – well, there are tricks you can do to speed that up (have a cache of boxed ints ready to go etc). A lot of these perf tricks are done in the IronPython compiler, so there’s some code to go digging in if one feels like it.

In fact, there are many optimizations one can do at the dynamic language compiler level to completely remove boxing all together (simple data flow analysis). Some day I’d like to talk about those.

 

Language specific types and BCL interop

If you’re familiar with Python, you’d have come across a fairly common Python type called a “list”. Lists are defined like so:

myPythonList = ['hello','world']

This is what I call a language specific type, meaning that the language involved has full first class (syntactic and runtime) support for that type and operations on that type. There are many decisions around what that type could look like “under the hood”, a good choice would be to see if it maps safely to an existing BCL type (Dictionary, List<T> etc) so that there is a better chance of that working interoperably with existing BCL libraries (plus, the engineering has already been done for you, so less bugs as a result right?:)). Another possible choice is to cook the type up yourself and place that type in your language runtime. This is the route the IronPython compiler chose, mostly because of common Python libraries that exist for the Python list are not supported by the BCL’s List<T>. The implementation looks like this:

namespace IronPython.Objects {

public class List : IMutableSequence, IList, IComparable {
    private int size;
    private object[] data;

    public static List Make() {
        return new List();
    }
   
    public void reverse() {
        Array.Reverse(data, 0, size);
    }
    // ...

IronPython has a namespace in its language runtime called “IronPython.Objects”, which contains all the IronPython defined language types (like List) that it has first class support for. You can find these source files in the IronPython source distribution.

The interesting point to make here about Objects.List is it’s BCL interop capabilities. You’ll notice that List implements IList (and IComparable), meaning anywhere a BCL member takes an IList, this Python List will happily play in that sandpit. One can further ponder the other interesting problems/opportunities available for taking this Python List and making it malleable to other types – like string[]. That way, a Python programmer can pass a BCL member (or other language that produces/consumes) which takes an object[] and Python List. You can achieve this through type marshalling at runtime – probably placing this marshalling code in your languages late-binder.

Type marshalling is just hard. Look at the COM interop namespaces in the BCL sometime – plenty of gotcha’s hanging around in there.

 

Static to dynamic bindings (event hookup example)

This long section is some notes on how to get purely static constructs in the type system, binding effectively to dynamic constructs at runtime. I leave it as an exercise to the reader to extrapolate other instances where this may cause headaches for dynamic languages.

Lets look at an interesting static to dynamic binding problem that dynamic languages face when targeting the CLR. Events, which are basic syntactic sugar for MultiCastDelegates are everywhere in libraries like Winforms – consider the Button.Click event, it has a strongly typed delegate EventHandler:

public delegate void EventHandler(object sender, EventArgs e);

Now consider the case where you want to subscribe to that event using a “object” based method who’s signature looks like this:

.method public static object clickmethod$f0 (object s, object e) cil managed

The EventHandler delegate signature clearly doesn’t match the method signature – the parameter types don’t match, and the return type has a different type and semantic. That’s not going to stop us of course, so lets dig in to how we can solve this problem.

First of all, lets look at the way we “subscribe” to events. Take the Button.Click event – subscribing to this in IL looks like the following:

ldftn    instance void MainClass::clickmethod$f0(object, class [mscorlib]System.EventArgs)
newobj   instance void [mscorlib]System.EventHandler::.ctor(object, native int)
callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::add_Click(class [mscorlib]System.EventHandler)

“ldftn” is the IL opcode that loads a function pointer to a method on to the stack. In this case, it loads the function pointer of “clickmethod$f0”. “newobj” then instantiates the EventHandler delegate, passing in the function pointer as an argument (and null or the object instance depending if the method is static or not). Then we call the “add_Click” method, which adds the EventHandler delegate we just created to the Click event. The key thing I’m trying to point out here is – we need to obtain a delegate to the method we wish to invoke.

Enter late-bound delegates.

The Delegate class has a whole bunch of “CreateDelegate” method overloads, purely for the sole purpose of creating delegates late-bound – excellent for cases like this where the expression of an event hookup looks something like:

Button.Click += clickmethod

and resolution of “button.Click” and “clickmethod” must be done at runtime. Okay, so the language runtime resolves “button.Click” to a Reflection based EventInfo, then it resolves clickmethod, which is a method to a Reflection based MethodInfo, and it needs to now cook up a delegate that the “button.Click” event likes. If the “clickmethod” method signature matches the “.Click” event signature perfectly, then a quick call like this:

Delegate d = Delegate.CreateDelegate(eventInfo.EventHandlerType, clickmethodMethodInfo);

is all that’s needed. That method call hands you back a delegate, then you can bind that to the event like so:

eventInfo.AddEventHandler(null, d);

Of course, this will work fine for the case where the EventHandlerType (delegate signature) matches that of the clickmethod method signature. But by looking up at the signatures, you realize they don’t match. What’s the story?

Co and contravariant (or relaxed) delegate support in Whidbey

Binding “void EventHandler(object sender, EventArgs e)” to “object clickmethod$f0(object s, object e)” does not exactly match. In Everett, if you tried binding these two guys together, we would throw an exception in your face. In Whidbey, we now allow contravariant parameter bindings, and covariant return parameter bindings. That means, you can be less type specific on the parameters, and more type specific on the return parameter.

Well, we hit almost all of them: clearly “object sender” matches “object s” exactly, and “object s” is less specific than “EventArgs e”, so we’re fine on the parameters, but we fail on the return type. “void” (a strange type in itself) is not more specific than object (in fact, it’s like apples and oranges here – this basically specifies a whole different calling convention). How would a dynamic language create a delegate to methods that always return object (even though in their implementation, they may not actually return anything)?

Enter LCG for mismatch delegate hookup

I love the new feature Lightweight Code Generation. I talk about it here (http://blogs.msdn.com/joelpob/archive/2004/03/31/105282.aspx) and here (http://blogs.msdn.com/joelpob/archive/2004/04/01/105862.aspx). We can essentially generate a method that has the exact signature of the “Click” delegate, do whatever argument shuffling/coercion we need before calling the “clickmethod$f0” method, shuffling return arguments if needed as well, then create a delegate over the LCG method which we can now bind to the event. Cool huh?

The code to create this LCG method based on the signature of the event (which would probably live in the LateBinder) could look something like this:

Type delegateReturnType = eventInfo.EventHandlerType.GetMethod("Invoke").ReturnParameter.ParameterType;
ParameterInfo[] delegateParameters = eventInfo.EventHandlerType.GetMethod("Invoke").GetParameters();

Type[] dmHookupParameters = new Type[delegateParameters.Length];
for (int i = 0;i<delegateParameters.Length;i++)
  dmHookupParameters[i] = delegateParameters[i].ParameterType;

DynamicMethod eventHookupMethod = new DynamicMethod("eventHookup", delegateReturnType, dmHookupParameters, typeof(object));
ILGenerator eventIL = eventHookupMethod.GetILGenerator();
//...

Now we need to generate IL that can push the arguments on to the stack, ready for the call. Now, if your dynamic language has special rules around calling convention (type coercion rules etc), why not just make the LCG method call your LateBinder.Call() method so the heavy lifting is done all in one place? That’s exactly what this IL does:

// emit the call to "LateBinder.Call(object name, object[] args)"
eventIL.Emit(OpCodes.Ldstr, "clickmethod$f0");

LocalBuilder local = eventIL.DeclareLocal(typeof(object[]));
eventIL.Emit(OpCodes.Ldc_I4, delegateParameters.Length);
eventIL.Emit(OpCodes.Newarr, typeof(object));
eventIL.Emit(OpCodes.Stloc, local);

for (int i = 0; i < delegateParameters.Length; i++)
{
  eventIL.Emit(OpCodes.Ldloc, local);
  eventIL.Emit(OpCodes.Ldc_I4, i);
  eventIL.Emit(OpCodes.Ldarg, i);
  eventIL.Emit(OpCodes.Stelem_Ref);
}
eventIL.Emit(OpCodes.Ldloc, local);
eventIL.Emit(OpCodes.Call, typeof(LateBinder).GetMethod("Call"));

(Yes I did deviate from my previous LateBinder example. Just imagine that this LateBinder.Call method does a string based lookup for a MethodInfo, then does an invoke… *grin*)

Now the interesting part. The IL sequence to basically pop off that result from LateBinder.Call if the delegate return type is typed as void, or cast the “object” return from the LateBinder.Call method to the return type of the delegate.

if (delegateReturnType == typeof(void))
{
  eventIL.Emit(OpCodes.Pop);
  eventIL.Emit(OpCodes.Ret);
}
else
{
  eventIL.Emit(OpCodes.Castclass, delegateReturnType);
  eventIL.Emit(OpCodes.Ret);
}

Last but not least, create the delegate to the LCG method:

Delegate d = eventHookupMethod.CreateDelegate(eventInfo.EventHandlerType);

Hook it up to the event:

eventInfo.AddEventHandler(null, d);

And we’re done. We’ve created a delegate with a signature that matches the event latebound, hooked it up to an LCG method that does whatever shuffling required and then invokes our LateBinder.Call method to actually call the method we want to call. Very cool huh?

 

Last but not least…

That’s me done for this post. I’ve got a whole bunch more I want to talk about in this space, but I’ll keep that for another day. I’m going to be at the PDC (and Jim Hugunin (http://blogs.msdn.com/hugunin/) should hopefully be there), so if you’re going to be there and you’re interested in learning/discussing more, drop me an e-mail.

Thanks

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 17 Nov 2004 20:02

It’s been a while since I’ve posted - we’ve been busy getting Beta 2 ready, and that means fixing bugs, bugs, and more bugs. I have a bunch of nearly complete posts, mostly around Reflection and type system identity, which I’ll be kicking out soon I hope. Lately, I’ve received a few questions around code sharing on generic methods. I figured this would be as good a forum as any to drill down on some of the details. This post is a bit all over the place, so please post any questions you have below!

 

First up - the high level bits for generics and code sharing:

 

-         We do a mix and match of code sharing and specialization, depending what type arguments we’re dealing with.

-         Generic method instantiation code sharing is done for reference type arguments.

-         Generic method specialization is always done for primitives and valuetypes, including enums.

 

What is code sharing again?

 

In the case of generics, code sharing is when two or more “compatible” method instantiations point to the same x86 code. An example of this is Foo.M<MyClass1> and Foo.M<MyClass2> sharing the same generated x86, where MyClass1 and MyClass2 are ref types.

 

Brief history – we also code share for the arrays of reference types in v1.0 and v1.1 (we just use the code for object array).

 

Very quick recap of EE data structures

 

There’s excellent explanation’s on the CLR’s execution engine data structures in the SSCLI Essentials book. A quick recap though:

 

All objects on the heap have a fixed size pointer that points to a MethodTable, which describes the objects type identity (you get essentially get a managed code representation through RuntimeTypeHandle). MethodTable’s contain pointers to EE structures, and more importantly, a list of the type’s methods and their representative code pointers. These pointers can either be pointing at x86 code, or the JIT stub (which invokes the JIT if a method has not been JIT’ed yet). MethodTable’s are used extensively for type identity.

 

MethodDesc’s are small structures used to describe methods. Each method has a representative MethodDesc structure, although it’s not typically used by the runtime unless you’re trying to do bind to methods late-bound (reflection). There are different types of MethodDesc’s in the runtime, but for the purpose of this post, we just assume they’re all basically the same.

 

Calls on a instance method conceptually look something like this: start with the this pointer, hit the MethodTable, index to the code pointer of the method, then call the code pointer, passing the “this” address as an argument as per x86 calling convention. Calls on static methods do essentially the same thing, just without the “this” pointer being passed as an argument. Of course, the JIT can generate code for direct calls to these various pointers – it does the heavy lifting of MethodTable lookups for code pointers at JIT time.

 

Generic Methods in IL

 

When we introduce generics in to the picture, we have this potential unknown – when we JIT compile, what do we do with locals/arguments of T? How do we generate x86 for something like that?

 

Let’s consider the following code snippet:

 

class Foo

{

      [MethodImpl(MethodImplOptions.NoInlining)]

      public void M1<T>()

      {

            Console.WriteLine(typeof(T));

      }

}

 

Foo f1 = new Foo().M1<string>();

Foo f2 = new Foo().M1<object>();

 

 

How do we represent the code in M1<T> in IL when we don’t know what T will be? We actually specify the type parameter as “!!arity”, where arity is the index in to the type parameters on the generic method (0 being the first, 1 being the second etc).

 

The Foo.M1<T>() IL code looks like the following:

 

// IL Code for Foo.M1<T>

.method public hidebysig instance void  M1<([mscorlib]System.Object) T>() cil managed noinlining

{

  // Code size       17 (0x11)

  .maxstack  8

  IL_0000:  ldtoken    !!0

  IL_0005:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

  IL_000a:  call       void [mscorlib]System.Console::WriteLine(object)

  IL_000f:  nop

  IL_0010:  ret

} // end of method Foo::M1

 

We make no assumptions about the type that the method will be passed. You can imagine, in the abstract machine world, that calling this method, we would simply replace the !!0 with string or object, depending what was passed in as the generic argument. This imaginary scenario is close to what actually happens when we JIT compile this piece of code.

 

Enter: generic methods and code sharing

 

Looking at the code, I have two instances of the same type Foo, calling essentially different methods (ie: M1<object> is not the same as M1<string>, which is also exposed and represented via reflection). Code sharing steps in, these two method instantiations end up pointing to the same x86 code.

 

It happens like this: When we hit this piece of code: Foo f1 = new Foo().M1<string>(), kick the JIT to go compile the method for the first time. We take the IL above, and in a basic sense, replace !!0 with a pointer that calls in to the runtime to get the type information about the type we passed as generic argument in slot 0. Where do we get this pointer from? Well, the magic comes from the calling convention: we supply a “hidden” argument in the call, which is a pointer in to a runtime data structure that supplies us with that information.

 

Bring in the “hidden” argument data structure

 

A pointer to what? Well, in this specific instance where we only need to know about the type argument supplied to the generic method, we create and pass in a MethodDesc pointer for that particular declaration of the method. For the Foo().M1<string> case we pass in a MethodDesc that describes the method as M1<string>, for Foo().M1<object>, we pass in the MethodDesc for M1<object>. The MethodDesc essentially contains all the information needed to describe what !!0 will be. The JIT compiles x86 code to pull the type information out of the MethodDesc pointer.

 

So, for the case above, the JIT hits the “ldtoken !!0”, and compiles x86 code to retrieve the type token from the hidden argument pointer which was passed in. This code works for both the string and object case, because we’re hitting a pointer indirection, not a specific piece of code for string or object.

 

Cases where we need this “hidden” argument

 

We generally need this hidden argument for cases where we can’t derive the type information from any means available at x86 execution time in the current stack frame. Below lists a few examples, along with the data structure the hidden argument represents

 

  1. Foo<T> static M()                   == TypeHandle
  2. Foo<T> static M<T>               == MethodDesc
  3. Foo M<T>                               == MethodDesc
  4. Foo static M<T>                      == MethodDesc

Number 4 is an interesting one – we actually need both the TypeHandle and the MethodDesc, but the JIT knows that we can generate code to get at the TypeHandle _from_ the MethodDesc (there’s an indirection there, which I think is described in SSCLI Essentials).

 

One case that’s not pointed out is Foo<T> M(). Because M is an instance method, and we already pass in the “this” pointer as per the calling convention, we can derive the type argument of type argument T, directly from the “this” pointer.

 

For those that ask the question – why pass in the MethodDesc/TypeHandle, why can’t we just pass in the type handle as a “hidden argument”. Good question, but for the case where we have more than one generic parameter, it makes sense to minimize the amount of arguments passed in at calling convention time. M<U,W,Z> for example, is fully described by the MethodDesc, so its easier and more efficient to pass in just the MD pointer and generate code to index in to the MethodDesc appropriately.

 

Okay, so why the lack of code sharing on primitives and value types?

 

While technically, we could code share on primitives and value types, it’s clearly not very efficient to do so. Just to make it more understandable:

 

Consider:

Foo { M<T>() {} }

 

new Foo().M<int>();

new Foo().M<double>();

 

The JIT will actually go off and generate two separate code sections for both of those instantiations. Why? Well, primitives and valuetypes live on the stack and have no reference to a MethodTable (unless they’re boxed). Generally, different valuetypes/primitives have different data sizes, int and double being a good example of that. The JIT clearly can’t generate x86 code that deals with unknown data sizes, so it specializes them.

 

The runtime could have made these primitive/valuetype guys play well with code sharing by boxing every time we call, but isn’t preventing unnecessary boxing one of the best reasons around for using generics?

 

Partial specialization

 

I’m not exactly sure if we the CLR team calls it partial specialization, but that’s what I’ll call it for this blog post <g>. For cases where we have a valuetype and a ref type, we specialize for the valuetype case, and link the sharable code for the ref type.

 

Foo<int>.M<string> and Foo<int>.M<object> are essentially the same code. We share M<T> in the context of Foo<int>.

 

More resources

 

Some of this is fairly dense; the blog posts intention is for Google to pick some of this up, so I can remember how it works. If you want to know more detail about this, check out the following:

 

http://research.microsoft.com/projects/clrgen/

http://research.microsoft.com/projects/clrgen/generics.pdf

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/csharp_generics.asp

 

The book I mention: Shared Source CLI Essentials, 2003, ISBN: 059600351X

 

That’s all for now – hopefully I’ll have more time in the coming weeks to jot down some more random brain dump stuff from inside the guts of the CLR.

 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 22 Sep 2004 22:36

Firstly, the big metadata diagram. Thanks to Chris King for this absolute gem. Now, on to the various ways developers can read and write metadata bits...

 

Unmanaged Metadata Reader API’s

I mentioned various times before that there exists Unmanaged Metadata reader/writer APIs that you can use to traverse and write out metadata structures with. It’s a great workaround for those subtle Reflection (or Reflection.Emit) bugs, or gaping holes in the Reflection API. I get a few questions here and there regarding these unmanaged API’s (mostly bootstrapping related questions), so I’ve decided to drop in documentation and a bit of source code I cooked up today:

 

Some documentation for the Unmanaged Metadata API’s can be found here, (also on google, MSDN and various newsgroups).

 

A quick bootstrap code snippet is below, which gets all method names for all type definitions in a given assembly:

 

:/> MDRExample.exe System.Web.dll

// MDRExample.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "cor.h"

#include <stdio.h>

#include <objbase.h>

 

int _tmain(int argc, _TCHAR* argv[])

{

    IMetaDataDispenser* _IMetaDataDispenser;

    IMetaDataImport* _IMetaDataImport;

    IMetaDataAssemblyImport* _IMetaDataAssemblyImport;

 

    CoInitialize( 0 );

    HRESULT hr;

 

    // go create all the interfaces

    hr = CoCreateInstance(CLSID_CorMetaDataDispenser, 0,

                                    CLSCTX_INPROC_SERVER,

                                    IID_IMetaDataDispenser,

                                    (LPVOID*)&_IMetaDataDispenser );

    if ( FAILED(hr) )

        throw "failed on IMetaDataDispenser";

 

    wchar_t _FileName[MAX_PATH];

    mbstowcs( _FileName, argv[1], lstrlen(argv[1])+1 );

 

    hr = _IMetaDataDispenser->OpenScope(_FileName, ofRead,

                                    IID_IMetaDataImport,

                                    (LPUNKNOWN *)&_IMetaDataImport );

    if ( FAILED(hr) )

        throw "failed on IMetaDataImport";

 

    hr = _IMetaDataDispenser->OpenScope(_FileName, ofRead,

                                    IID_IMetaDataAssemblyImport,

                                    (LPUNKNOWN *)&_IMetaDataAssemblyImport);

    if ( FAILED(hr) )

        throw "failed on IMetaDataAssemblyImport";

 

    HCORENUM    _hCorEnum = 0;

    mdTypeDef   _typeDefs[2048];

    ULONG       _countTypeDefs = sizeof(_typeDefs);

   

 

    // go get all the type defs defined in the assembly

    hr = _IMetaDataImport->EnumTypeDefs(&_hCorEnum,

                                    _typeDefs,

                                    _countTypeDefs,

                                    &_countTypeDefs);

 

    for (int i=0;i<_countTypeDefs;i++)

    {

        mdMethodDef _methodDefs[2048];

        ULONG _countMethodDefs = sizeof(_methodDefs);

 

        // go get all the methods defined on the typedef

        _hCorEnum = 0;

        hr = _IMetaDataImport->EnumMethods(&_hCorEnum,

                                    _typeDefs[i],

                                    _methodDefs,

                                    _countMethodDefs,

                                    &_countMethodDefs);

      

        // now print out the methods name

        wchar_t _methodName[1024];

        ULONG _countMethodName = sizeof(_methodName);

 

        for (int j=0;j<_countMethodDefs;j++)

        {

            wchar_t _methodName[1024];

            hr = _IMetaDataImport->GetMethodProps(_methodDefs[j], 0, _methodName, _countMethodName, &_countMethodName, 0, 0, 0, 0, 0);

            wprintf(_methodName);

            printf("\n");

        }

    }

      return 0;

}

 

AbsIL (Abstract IL)

AbsIL is a toolkit from the smart people (yeah that’s you Don) at Microsoft Research, that provides an abstracted view of metadata (generally in the form of an abstract syntax tree). Of course, having this transformation from metadata relationships to an abstract tree view gives you all sorts of opportunity to do your little bit of analysis/manipulation. Some interesting uses may include things like assembly re-writing, contract and performance analysis, discovery for plug-in like architectures, exception handling related issues and more.

 

Consumers of this toolkit beware – you’ll need to brush up on OCaml, as the AbsIL libs have been written in F# (therefore exposing this functionally). Writing an AbsIL program in C# can be very frustrating to begin with, but sometimes power comes with pain. Here’s something I hacked up in C# that’s similar to the Unmanaged Metadata reader example above – it prints the name of each type and method to the screen from an assembly provided as a command line argument:

 

using System;

 

class AbsILExample

{

    static object writeName(object t)

    {

        if (t is Il.type_def)

            Console.WriteLine(((Il.type_def) t).tdName);

        else if (t is Il.method_def)

            Console.WriteLine(((Il.method_def) t).mdName);

        return t;

    }

 

    static object writeMethodName(object t) { List.iter(new System.Func(writeName), Il.dest_mdefs(((Il.type_def)t).tdMethodDefs)); return t; }

 

      static void Main(string[] args)

      {

        Il.modul module = Ilread.read_binary(args[0], FS.option.MkNone());

        FS.list l = Il.dest_tdefs(module.modulTypeDefs);

       

        Console.WriteLine("type names:");

        List.iter(new System.Func(writeName), l);

 

        Console.WriteLine("\nmethod names:");

        List.iter(new System.Func(writeMethodName), l);

    }

}

 

Find AbsIL at the Microsoft Research website.

 

PEAPI (or Perrrwapi)

My old research lab has a managed metadata reader/writer called PEAPI. From the website it looks like they’ve kicked some butt on perf (“The new backend can create a program executable file in almost exactly the same length of time as it takes to write the equivalent CIL text file”). Crazy fast. I believe the Mono C# compiler uses this component as its code generator, although I can’t verify that.

 

I don’t have much experience with this API, I haven’t had a chance to write some stuff with the latest bits, although I do remember years back when the component author spat out the first alpha - fun stuff. Ahh the good old days…

 

Find it here.

 

IL Reader for .NET

A fellow Microsoftee, Lutz Roeder wrote a great .NET library for IL reading called IL Reader. While it doesn’t exist for 2.0 (not exactly sure what his plans are for a 2.0 rev), it supports reading a types methods in 1.0 and 1.1 assemblies. The great thing about this library is that it returns its instructions as Reflection.Emit opcodes, giving you the ability to read in, manipulate, and write out via Reflection.Emit with relative ease.

 

Find IL Reader here.

 

Whidbey’s MethodBody API

I mention this API here and here. Similar to Lutz’s IL Reader, but doesn’t have the complete opcode abstraction yet. You’ll have to take a byte stream, go find the ECMA spec or something, and decode the bytes yourself (which is fairly trivial, yet fun to do).

 

 

Feel free to leave comments below if you know of other interesting Metadata reading/writing toolkits - I've only done a quick braindump here. In anycase, hopefully there's some value here in this post for those who want to read and write any metadata bit they like!

 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 22 Sep 2004 22:11

2 months since my last post? – Yeah I know, I’ve been very busy lately with Whidbey CLR commitments. I'll have two posts today – a quick postmortem on TechEd AU/NZ, and a post on various metadata related API’s developers are able to consume. They're kind of related... trust me.

 

AU/NZ TechEd 2004:

I took a trip down under to Australia and New Zealand for TechED 2004. The event was a great success, and I was very impressed by the colourful array of people I got to chat with – Mitch Denny, Greg Low, Frank Arrigo, my old research pals Richard and Doug, Bernard Oh, Nick Wienholt, Steve Riley, Peter Stanski and many more. So much fun hearing what these people work on, and then the heated debates that follow as to why the runtime doesn’t support this and that. I especially enjoyed the geek dinner in Canberra, which in traditional aussie style, ended up being a booze up at a local pub.

 

It seems that my “CLR Under the hood” talk was the most popular from the two talks I presented. I had a lot of questions afterwards about my metadata slides, which I suspect stems from the various research and third party products that are coming out based on metadata analysis and manipulation. I did promise in the talk to post up the big Visio diagram that I had in one slide that lays out metadata table dependencies, which is why this post has a follow up post on metadata - told you they were related. :)

 

I didn't manage to get any photos from the event, so if an attendee catches this post somehow, and has some photos he/she would like to share, please send them on through, I'd love to see them, and share them with some other CLR folk.

 

On to the next post...

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 19 Jul 2004 16:49

Over the past couple of months, I've written up a bunch of notes surrounding the CLR type system. You'll notice that it's littered with “This is how Reflection deals with these types“, as it's ment to be the start of a document that illustrates Reflection>Type system interactions. As always, it's just a brain dump, and you can expect some errors, along with gaping holes in reasoning and information. Feel free to leave a comment if there is a particular part of these notes you'd like me to dig deeper on.

The underlying type system

Following is a description of the sets of types that are treated specially either by the virtual machine or the internal system. It’s important to have a good understanding of the characteristics of these sets as they are the key blocks reflection builds on.

Please refer to the ECMA spec (partition I) for a more formal description of those types. In the next sections we want to outline features that are more interesting from a reflection point of view, so definitions may be slightly different (and certainly less formal) than those presented in the ECMA specification. The idea is to provide a general set of rules that can help people using reflection and/or reflection emit to do type analysis.

ReferenceType

Referred to common instances that are naturally allocated on the gc heap (i.e. Object, String). The object pointer points to type information for the instance (a MethodTable). The instance is fully described by the MethodTable.
We use Reference Type here to identify instances that are unambiguosly described by their MethodTable. That is, the instance pointer, points to the exact type information.

In that respect obtaining the TypeHandle from the instance is “conceptually” as simple as dereferencing the instance pointer and provides the full type information for that instance (Type.GetTypehandle(Object)).

ValueType

ValueTypes are types that derive from System.ValueType. They cannot be derived from, so they are implicitly sealed types.ValueTypes have stack semantic; that is, allocation of a ValueType does not occur on the gc heap but rather on the stack. ValueTypes can be moved on the gc heap via boxing (‘box’ IL opcode). A copy of the instance is made onto the gc heap as a result of boxing.

When on the stack, ValueTypes are not self describing, they do not carry any type information. Usually an object* points to the MethodTable of that object, the type of that instance. That allows to retrieve type information by simply having a reference to an instance. That  is not true for ValueTypes when allocated on the stack. A reference to a value type is a pointer to the beginning of the data for that value type.

In the unboxed form, they are tracked by flow analysis (either the type location of the local or an argument). Type safety is enforced by flow analysis (it’s referring to a location - local .locals - or argument - which have type information via the Method signature). In their boxed form, they carry their MethodTable and thus are self describing.

Note:
A ValueType derivative is allowed to declare methods of a static or instance nature. Virtual methods are not allowed to be specified on ValueType’s because for dispatch, the runtime needs the “this” pointer. Clearly, because the runtime identifies the ValueType through flow analysis, there is no “this” pointer on the stack at the point of invocation.

Primitive (Scalar types)

Primitives are built-in types that are treated specially by the system. They have special encoding in metadata and they have explicit opcodes that can operate on them (i.e. add opcode for numeric values).  There are a set of implicit conversion rules (int to long) applied to them. Reflection implicitly provides conversion of primitives during invocation whenever there is no loss of information. In any other respect primitives have exactly the same semantic as ValueType since they are ValueTypes.

Note:
C# “int” and System.Int32 are essentially the same thing, the C# compiler uses the syntactic sugar to minimize syntax overhead. In general, these primitive types are derived from ValueType and perform a set of common operations (CompareTo, Equals, GetHashCode, ToString and various conversion methods).

Enum

Enums are ValueType as well. However they’re special because Enum can only wrap a limited set of primitive types. They have somewhat peculiar assignability rules with respect to their underlying types and in that respect it can be important to distinguish them from other ValueTypes or primitives.

For instance the beahvior of the castclass (or isinst) and unbox instructions over a boxed enum instance is somewhat inconsistent. Consider the following:

Color c = Color.Black;
Object o = c;
int i;
an IL sequence using classcast will fail to assign to int local i
ldloc o
castclass int32 // this will throw InvalidCastException
stloc i
however an unbox instruction will work just fine
ldloc o
unbox int
ldind.i4
stloc i

In other words there are valid and verifiable IL sequences that allow a boxed enum instance to be assigned to its underlying type or to a different compatible enum type, whereas other sequences will throw. Considering reflection always deals with boxed value, there is an asymmetry in operations that check type assignability (i.e. binding) versus operation that check instance assignability (i.e. invocation).

Array

They are special reference types. Object layout for array is different to normal reference types – an instance of an array may point to a shared MethodTable. Instances of Object[], String[] they all point to the same MethodTable (Object[]). This is true for all arrays of reference types as they share the same gc layout. For value types the array instance carries the specific MethodTable (i.e int[], EnumColor[], MyStruct[]).

Arrays are also special in their assignability rules (array covariance). A String[] can be assigned to an Object[] even though Object[] is not in the hierarchy of String[].

ByRef

ByRef types are managed pointers. ByRef don’t box and there are very few IL instruction that can be performed on ByRef types (i.e. ldind.ref, stind.ref). Because of that restriction there is never an instance of a ByRef type as far as reflection is concerned. However ByRef types are real, concrete types in the type system. Inspecting a method that takes a ByRef arg will reveal a unique type that is in no relationship with the type it represents (i.e. int& and int are in no relationship).

Reflection simulates ByRef and it’s the user responsibility to fetch the updated value out of the argument array used in reflection invocation. Verifiable IL ensures that ByRef types are only used in argument position. Usage of ByRef in any other location (return value, fields) results in unverifiable code.

Note:
Interesting features of ByRef

ChangeMyString(ref string s)

How do I discover via Reflection? When Reflection takes it as an argument (new Object[] myref) to late bound invoke a method that takes a reference, Reflection actually passes the object array, the method (invoked latebound) modifies the array reference instead of the actual ByRef variable that we wanted to modify.

TypedRef

They are a special form of ByRef. They are a struct composed of two fields: a ByRef value to a location and a type compatible to the ByRef location. It can specify a contract on a local variable via the locals signature, or can be used for method parameters. TypedRef also share the limitations of ByRef in verifiable code.

TypedRef could be used by dynamic and latebound languages as a way to tag a location with information that is different from the runtime type of an object. That type information could be used by a language runtime to direct binding. However given the verification limitation we are not aware of any language that uses TypedRef. TypedRef are used in vararg functions and  are returned as a result of enumerating over the vararg argument list. The type the TypedRef points to is the statically declared type (as defined by the compiler in the metadata vararg signature).

There exists C# keywords and methods that hang of TypedReference that specifically deal with creating and manipulating TypedReferences:

__makeref(variable):                // Construction of TypedReference

__reftype(typedReference)           // Returns the Type that the TypedReference struct holds

__refvalue(typedReference)          // Changes the value of the &ptr the TypedReference holds

TypedReference.MakeTypedReference()

TypedReference.SetTypedReference()

TypedReference.GetTargetType()

 

Pointer

They represent an unmanaged pointer. Used in unsafe code, the usage of pointers requires skip verification.
Though pointers can box, their type identity is lost when boxing. This is particularly difficult for Reflection that always deals with boxed entities. There is no way to make reflection code that uses pointers type safe. Of course code using pointers is never verifiable by definition, but the system will generally check type safety and do properly binding in the presence of pointers. Reflection is less accurate in that respect .

COMObject

COMObject are types that are somehow exposed to the system via COM interop.
COMObject may be imported via tlbimp in which case they have some static type information that can be used to reason over them. However in the presence of a COMObject instance assignability is somewhat of a fuzzy concept.

Because of the semantic of COM, assignability is the result of a IUnknown::QueryInterface() call on that instance and thus may, in principle, give varying results for different instances. When no static type information is available every COM instance will show as an instance of __COMObject and few api will give results that are different from instance to instance (assignability, guid). In that sense type identity is not enough to guarantee assignability. The presence of __COMObject requires logic outside of the type system.

Interface

Interfaces are very much like Reference Types. However they are special in that they do not specify a concrete type but rather a contract a type must adhere to. Walking an interface hierarchy will not lead to Object even though Interface types are assignable to Object.  Deriving from an interface does not require exposing publicly any of the interface’s methods, however the interface methods must be implemented at least privately by a concrete type.  An InterfaceMapping struct is exposed via Reflection to identify the implementation of an interface for a given type.
An interface has a MethodTable (TypeHandle) but it never has an instantiation. Instances never point to an interface MethodTable.

TransparentProxy

A TransparentProxy is a type that acts as a proxy for another type. The TransparentProxy type is never revealed to the user. It’s an internal type. Every trasparent proxy points to the same MethodTable, the unique representation of the TrasparentProxy type.  So without extra operations transparent proxy do not carry any type identity. In that respect dealing with atransparent proxy introduces a perf penalty to type identity in the system. When an instance is asked for its type a check about transparent proxy needs to preceed any other check as, if the object is indeed a transparent proxy, a more complicated dereference needs to happen in order to fetch the “real” type of that object.

There are explicit checks in the runtime to make sure you never get back a TransparentProxy. Inspecting mscorlib metadata will show a __TransparentProxy type, however there is no representation of that type as System.Type. TransparentProxy does not exist in the type system.

Generic

Perhaps another time…

TypeParameter

Perhaps another time…

Delegate

Normal type analysis over delegate types will work as expected. They are true types in the type system.
However they are special in the way they wrap a function pointer. They were build with the idea of being the verifiable function pointer in the system. There are very special verification rules around deelgate construction. The rules for verification around delegates are extremely tight and bound to a well known IL sequence.

There are also very special rules in how you define your delegate type inheritance. System.Delegate cannot be derived from directly. Every delegate type must derive from System.MulticastDelegate. The presence of both types is more an artifact of historical evolution of the system than a real need (the clr used to make a distinction between System.Delegate – single cast delegate – and System.MulticastDelegate. That distinction, however, is long gone).

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 05 Jul 2004 20:50

Looks like I'll be taking the long flight down to Auckland, NZ and Canberra Australia to speak at TechEd 2004 under the developer tools banner. This week, I'll start the speaking preperations - I suspect it will take me around three weeks to perfect the two talks I'll be delivering (both CLR centric). I find all of this pretty exciting - I get to travel home for a week AND get to talk about the nuts and bolts of what we're working on.

Feel free to get in contact if you're going to be in town and want to shoot the breeze about Rotor, CLR, languages or the big house.

 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 23 Jun 2004 03:21

Figured I'd take the opportunity to capitalize on the increased link traffic Brad sent my way, by giving a quick and dirty overview of “What’s new in Reflection”. Illustrated are features I believe are worth their weight in gold - it's a subset of the overall change list. I wrote some of this a little while back to be included in the Whidbey docs, not sure if they’ve made it there yet. As always, things can and normally do change, and taking dependencies on Beta bits is generally a “bad thing(tm)”. Nevertheless, here goes:

 

Lightweight code gen (LCG)

 

LCG is probably one of the most interesting features in the late-bound space. I’ve spoken briefly about it here and here. Provides enhanced runtime code generation facilities for emitting static methods at runtime. Less overhead (no need to generate assemblies, modules and types at runtime). GC collection of generated methods which means better resource utilization in long running applications. LCG also has the ability to skip JIT-time visibility checks.

 

ReflectionOnly context

 

A new feature in Whidbey that allows the user to load an assembly in the “ReflectionOnly” loader context, where no code will be executed. This allows for some loader restrictions to be relaxed, such as the applying of policy. It also allows for reflection over architecture specific assemblies (Reflection over 64bit generated assemblies from a 32bit environment). A example subset of the API:

 

Assembly asm = Assembly.ReflectionOnlyLoad("assembly.dll");

// ...

Type t = Type.ReflectionOnlyGetType("MyType", false, false);

 

Reflection over MethodBody

 

This new Whidbey feature allows a user to reflect over a given methods IL stream, its locals, and its exceptions. This is a preliminary API for the power users of reflection. It basically gives the user the ability to parse an IL byte stream. A user is able to reflect on underlying method metadata, and make rational decisions about methods based on their functionality, code paths, exception handling and usage. It also allows a user to realize more than just the method contract and signature. An example subset of the API:

 

MethodInfo mi = typeof(Foo).GetMethod("Bar");

MethodBody mb = mi.GetMethodBody();

 

Console.WriteLine("MaxStackSize: " + mb.MaxStackSize);

Console.WriteLine("Local Signature metadata token: " + mb.LocalSignatureMetadataToken);

Console.WriteLine("Byte array length: " + mb.GetILAsByteArray().Length);

Console.WriteLine("Byte array: " + System.Text.Encoding.ASCII.GetString(mb.GetILAsByteArray()));

// ...

 

Token Handle Resolution API’s

 

My favorite. We’ve got some work to do here, but more on that later. New API’s added to move from Info to metadata token and RuntimeHandle. New API’s added to move from metadata token to RuntimeHandle. User is able to burn in type identity using the Module and metadata token. This has some fairly complex performance and working set benefits that I hope to blog about some other day. There's also the general sense of a better late-bound experience with runtime identity. Also has some interesting “if you were to build a compiler” implications. Example subset of the API:

 

int fooToken = typeof(Foo).MetadataToken;

RuntimeTypeHandle typeHandle = typeof(Foo).Module.ModuleHandle.ResolveTypeHandle(fooToken);

// ...

 

Reflection and Reflection.Emit support for Generic types

 

I think this has been well blogged about. We support full reflection and code generation on and of generics types. Various API’s have been added to the XXXInfo range of API’s, along with added Reflection.Emit API’s. I don’t believe the latest community drop supports full Reflection.Emit generics support, but expect that for Beta 2.

 

Improved Reflection support for Custom Attributes

 

We can now return a class that has information about a custom attribute, but yet does not execute its constructor! This is part of the whole “ReflectionOnly” story, and generally gives some important semantic benefits.

 

Improved Performance

 

Touchy subject. I’ll move on before I get in trouble… ;)

 

 

Writing a tool (using Reflection of course) to do a diff over Everett vs Whidbey API’s is generally how I work out what we’ve added in other namespaces. It’s pretty simple to do, so I’ll leave it as an exercise to the reader.

 

Enjoy.

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 22 Jun 2004 03:19

Jan Kotas, a developer on the CLR team, and an original member of the Rotor team has recently started a servicing project for the V1 source. This fixes a few known issues, and for the interest of google indexing, fixes one issue in particular that many will eventually run into: Windows XP SP2 Rotor build bug. It also includes fixes to support the building and running of Rotor V1 on the following OS's:

- Windows Server 2003
- Mac OSX 10.3
- FreeBSD 5.2
- Windows XP SP2

Follow this link for more details. While you're at it, jump on the list too!

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 20 Jun 2004 22:53

A month and a half since my last post - I've been slacking off. I do have a good excuse though, visited a couple of countries (more on that), and as a result have been digging myself out of e-mail hell. I'm cooking up the final episode in the series of Late-bound invocation notes, should have that posted out very soon.

Recently, myself and Peter traveled to Singapore for the Asia-Pacific Rotor Workshop, the first RW in the region. It was great fun, had an excellent array of speakers: John Gough, Nigel Perry, Nam Tran, Dominic Cooney, Andrew Gray, Witty, and more. All things languages and virtual machines were talked about, full of all sorts of interesting Rotor and CLI/CLR stuff. Kudos' to the Microsoft Singapore guys for a fun four days, and excellent hospitality. I took a quick photo of some of the speaker lineup, while they listened to a talk - I believe it was Andrew Gray speaking at the time, about his MMTk Rotor port:

 
Will be back on air with more soon...
Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 07 May 2004 00:19

I figured I’d go exploring through the Rotor debugger today – we had a bug come in for Type.InvokeMember() where we throw a System.IndexOutOfRangeException unexpectedly. If you’re trying to invoke a method on a class which has an overload that takes “params” args, chances are you’ll run in to it. To firm this up, take a look at the C# file – you’ll notice a params string[] args, and a string arg1, string arg2. What happens if we try to call the method with two arguments?

 

using System;

using System.Reflection;

 

public class MainClass

{

    public static void Main(string[] args)

    {

            typeof(MainClass).InvokeMember("MyMethod", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.InvokeMethod, null, null, new object[] { "test", "me" });

      }

 

 

      public static void MyMethod(params string[] args)

      {

            foreach (string s in args)

                  Console.WriteLine(s);

      }

 

      public static void MyMethod(string arg1, string arg2)

      {

            Console.WriteLine(arg1);

            Console.WriteLine(arg2);

      }

}

 

As it turns out, C# will just call MyMethod(string arg1, string arg2). However, Type.InvokeMember behaves differently. We get an exception:

 

C:\repro>clix invokememberbug.exe

 

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.

   at System.DefaultBinder.FindMostSpecific(ParameterInfo[] p1, Int32[] paramOrder1, ParameterInfo[] p2, Int32[] paramOrder2, Type[] types, Object[] args)

   at System.DefaultBinder.FindMostSpecificMethod(MethodBase m1, Int32[] paramOrder1, MethodBase m2, Int32[] paramOrder2, Type[] types, Object[] args)

   at System.DefaultBinder.BindToMethod(BindingFlags bindingAttr, MethodBase[] match, Object[]& args, ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, Object& state)

   at System.RuntimeType.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters)

   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)

   at MainClass.Main(String[] args) in C:\repro\invokememberbug.cs:line 8

 

IndexOutOfRangeException? That can’t be right. Either it hit the correct method (it’s up for debate which method it should hit), or throw an AmbigousMatchException, like usual. Our preference is for AmbigousMatchException.

 

Well, the object of the blog post is to prove it’s a bug, and not bad client code: lets cook this up in cordbg. We end up throwing the exception here:

 

C:\repro>cordbg clix invokememberbug.exe

Microsoft (R) Shared Source CLI Test Debugger Shell Version 1.0.0003.0

Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

 

(cordbg) run clix invokememberbug.exe

(cordbg) g

 

DefaultBinder.cs

671:            private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1,

672:                                             ParameterInfo[] p2, int[] paramOrder2,

673:                                             Type[] types, Object[] args)

674:            {

...

679:                 if (args != null && args[i] == Type.Missing)

680:                     continue;

681:                            Type c1 = p1[paramOrder1[i]].ParameterType;

682:*                           Type c2 = p2[paramOrder2[i]].ParameterType;

 

 

Lets check the length and the index:

 

(cordbg) print p1

p1=(0x00b77e70) array with dims=[2]

  p1[0] = (0x00b77b20) <System.Reflection.ParameterInfo>

  p1[1] = (0x00b77b88) <System.Reflection.ParameterInfo>

(cordbg) print p2

p2=(0x00b77e88) array with dims=[1]

  p2[0] = (0x00b77c70) <System.Reflection.ParameterInfo>

(cordbg) print i

i=0x00000001

(cordbg) print paramOrder1[1]

paramOrder1[1]=0x00000001

(cordbg) print paramOrder2[1]

paramOrder2[1]=0x00000001

(cordbg) print p2[1]

Array index out of range.

Variable unavailable, or not valid

 

As it turns out, indexing into p2[1] is takes you out of range of the array – there’s our bug. Okay, so we have a bug in the default binder – this is great, my client code should work, it’s the binder that blew up – we’ve found our bug. But why?

 

Turns out that part is a little more tricky. What does FindMostSpecific do? Intuition tells you that it’s probably got a couple of methods parameters, from methods that look similar and it needs to find the best fit (verification of this means looking at the defaultbinder.cs source code, but we’ll keep going…). What is it passing in? Let us look up the stack:

 

(cordbg) up

821:             int res = FindMostSpecific(m1.GetParameters(), paramOrder1, m2.GetParameters(), paramOrder2, types, args);

(cordbg) funceval System.Object::ToString m1

Function evaluation complete.

$result=(0x00b8101c) "Void MyMethod(System.String, System.String)"

(cordbg) up

821:             int res = FindMostSpecific(m1.GetParameters(), paramOrder1, m2.GetParameters(), paramOrder2, types, args);

(cordbg) funceval System.Object::ToString m2

Function evaluation complete.

$result=(0x00b81084) "Void MyMethod(System.String[])"

 

We looked up the stack (into FindMostSpecificMethod), found that we were invoking FindMostSpecific with the parameters of two methods, m1 and m2. Surprise surprise. Turns out the code wrapped around FindMostSpecificMethod passes in two methods that could be matches, and asks FindMostSpecificMethod to figure that out. It then calls a helper method FindMostSpecific.

 

It looks like FindMostSpecific relies on the fact that the two methods parameters it has to sort and identify as the most specific, are the same length as the InvokeMember object args list. In this case, MyMethod(params string[] args) is clearly only length 1, and the InvokeMember object array is length 2. Quite simply -  the binder found a couple of matches, asked the helper methods to go figure it out, but the assumption was that the helper methods would always receive a parameter list the same length as the args list. Clearly a bug.

 

Of course, you could dig a little deeper to find out where in the binder the logic exists to collect the methods that look like they could be a target invocation match – but I’ll leave that exercise up to the reader. I hope you caught the general theme – think you’ve found a bug? Okay, go away and take a peek. ;)

 

Have fun

 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 05 May 2004 22:47

Just thought I'd post a small screenshot of Rotor Whidbey running on a Longhorn build - we speculated if it'd even build, but as it turns out, coupled with the latest Whidbey C++ compiler it built and ran fine. As a side tidbit, we've got daily tarballs being generated from the latest CLR source checkins - however sometimes we experience a bit of lag, trying to update the CLR source checkins to be cross platform friendly.

The screenshot is Longhorn with a 5/5/04 build of Rotor Whidbey running “Hello World“.

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 23 Apr 2004 18:47

There are various other scenario’s in the invocation space that have not been dissected. I mentioned a few of them in the comments section of one of my other posts. To be a little more illustrative, this posting will iterate over some of these scenario’s, and provide notes for each. It’s worthwhile to remember, this is simply a braindump, and until we’ve firmed up the story for Whidbey, I wouldn’t recommend you take this as “written in stone”.

 

Some late bound invocation scenario’s may include:

 

           Known method signature (parameters), unknown method name and method type

           Known signature and known method name, unknown receiver

           Known name

 

I’ll be talking about the first two for these particular notes.

 

1. Known method signature (parameters), unknown method name and method type

 

This scenario is presented by an extensibility mechanism where the user specifies a particular custom attribute on a method with the given signature.  At runtime, the methods with the custom attribute are retrieved and thus the callback is available. In this scenario, the arguments are typically provided by the code invoking the callback (the source) so there is usually no coercion/conversion that needs to happen before hand. There is generally nothing unknown at the point of invocation.

 

Invocation through reflection is very heavy for such scenario’s, and a delegate is the right solution. Reflection invocation is heavy because there is a single entry point, so it’s forced to do heavy lifting, like hit metadata to understand the method, signature walking, argument checking etc. If the signature is known, a delegate is definitely the right choice.

 

The delegate story has an interesting spin to it. When you create a classic delegate over an instance, it is tied one-to-one to the instance of that type.  If the delegate is over a static function you don’t need to worry about the instance, so the type of the method does not come in to the picture – it’s a simple question of scoping. If the delegate is over an instance, then you have the issue of the instance possibly changing.

 

Note:

It’s worthwhile explaining the last sentence a little further. Consider the following: Class A { void M() } and a delegate void del(). When I do a Delegate.CreateDelegate() over the instance of A.M, and my instance of A changes to another instance of A, I need to recreate the delegate.

 

So consider the scenario where the instance is not always stable, we have introduced the recreation of the delegate into the picture. So now it’s a tradeoff between recreating the delegate and invoking, or using a MethodInfo.Invoke. We can either create the delegate multiple times for each instance, or we can cook up a MethodInfo, and utilize the MethodInfo’s independent binding to the instance (via the API – specifying the receiver object).

 

We have to consider the performance aspects here: creation of the delegate is pretty much the same as the binding of the MethodInfo, and Delegate.Invoke is much faster than MethodInfo.Invoke. The ratio we need to consider to produce a working result, is the ration of creation to invocation. If the ratio is one to one, the MethodInfo.Invoke is probably a better solution. If the ratio of invocation over creation is high, than delegate makes sense. If the ratio is different than these options, the user of the API should go away and think about what make sense (ie: benchmark and find out). There is no real concrete story here, as always, we recommend that the user measure their scenario.

 

2. Known signature and known method name, unknown receiver

 

This scenario is very similar to the previous model, except that the method is identified by name and not via a custom attribute. Besides the startup logic (actually binding to the method), the considerations for the previous case are very similar.

 

An alternative to these cases in a fully trusted application, is the use of the IL instruction “calli”. The obvious drawback of calli is the unverifiable nature of the instruction. Because of this, erroneous usage of calli may lead to type safety and security holes.  However, if used correctly, calli offers a great benefit in both speed and working set performance.

 

One obvious advantage of calli over delegates is that it binds to a signature and can take different types of instances. The usage of delegates requires that the delegate must be recreated when the instance changes. Calli uses a function pointer (IntPtr value) that can be retrieved at startup (Give me the MethodInfo, then the MethodHandle and then call GetFunctionPointer), and is extremely light.

 

Note:

Obviously, you can’t generate a calli call in C#. The way out of this if you’re using C#, you can generate an assembly (Reflection.Emit, ILASM etc) that has a method that takes an IntPtr, and performs a calli. One other thing to note, C++ uses calli extensively for virtuals and invocation.

 

Trusted components (particularly system components) that have tight performance requirements should pay great consideration to this late bound invocation solution.

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 21 Apr 2004 16:50

I'm posting up a “getting started“ style document that previous members of the Rotor team cooked up, it illustrates some notes for debugging under the GDB environment (FreeBSD and MacOS). Enjoy.

Launching GDB

Debugging a new instance of an application

Run "gdb app_name", then at the "(gdb)" prompt, enter "run [arguments]" to spawn the new instance and pass along optional command-line arguments to it. For example:

gdb clix

(gdb) run ~/rotor/tests/bvt/short/hello.exe

This will launch a new clix process, passing along the path to a managed exe to run.

Attaching gdb to a running process

Run "gdb app_name", where app_name is the name of the running process. At the "(gdb)" prompt, type "attach pid", where "pid" is the process ID of the process to attach to. Specifying the app name on GDB's command line allows GDB to load symbols for the process.

Setting Breakpoints

GDB cannot set breakpoints in .so files that are not currently loaded, though it does keep the .so's symbols loaded even if the .so unloads. So in order to set a breakpoint in a .so, you must wait until the .so is actually loaded. For example, to set breakpoints in libsscoree.so within a clix process, you must:

  • Set a breakpoint in clix, after it loads libsscoree.so. ie. "break main.cpp:235" then "run ~/rotor/tests/bvt/short/hello.exe"
  • Once the breakpoint in clix hits, libsscoree.so has been loaded, so breakpoints in libsscoree.so can be set. ie. "break _CorExeMain2"

Breakpoints may be set in the PAL once main() has been called.

Restarting the Debugging Session

Since GDB cannot set breakpoints in not-loaded .so files, and restarting the process unloads the .so files, GDB doesn't support breakpoints set in .so files when restarting.

So before restarting a debugging session, use "info break" to show the list of currently-set breakpoints, then use "dis n [ n...]" to disable all of the breakpoints set outside of the application itself. Multiple breakpoints can be disabled by one "dis" command - the list to disable is space-separated.

Once those breakpoints are disabled, the session can be restarted by typing "run [arguments]". If no arguments are specified, then gdb uses the arguments from the previous "run" command, which saves you from having to retype long arguments on each restart. GDB will prompt before restarting, to kill the current session.

Basic Commands

General Info

  • GDB uses Emacs-style line editing. Tab completes command names, Ctrl+a moves to start of line, Ctrl+e moves to end, etc.
  • "help" shows some basic help
  • "quit" exits

Breakpoints

  • "break sourcefile:linenum" - set a breakpoint at sourcefile:linenum. The command can be abbreviated to just "br". Note that the sourcefile:linenum tends to not work correctly for assembly files (*.s) and for included .cpp files (rotor_x86/*.cpp).
  • "break symbolname" - set a breakpoint on the symbol name. C++ class members are specified as classname::membername.
  • "break *address" - set a breakpoint at the specified address. The address defaults to decimal - use "0x" for hex. ie. "break *0x1234"
  • "break" - sets a breakpoint on the current line. If this is used when the current stack frame is not the bottom frame, then it sets a breakpoint on the line following the call to the child function, so it can be used to break when the child returns
  • "xbreak" - set a breakpoint on the return from the current function
  • "info break" - list all breakpoints
  • "delete n" - delete a breakpoint by number. More than one can be specified at once - use a space as the list separator
  • "disable n" - disable breakpoints by number. More than one can be specified at once. The abbreviation is "dis"
  • "enable n" - enable breakpoints by number. The abbreviation is "en"
  • "ignore n m" - alters breakpoint number 'n', changing it to have an ignore-count of 'm'.
  • "condition n (expression)" - alters breakpoint number 'n', so it breaks only when the 'expression' evaluates to true. The expression must be within parentheses.
  • "break if (expression)" combines "break" and "condition" into one statement

Callstacks and Local Variables

  • "backtrace" - dump stack backtrace. Each stack frame is numbered, with 0 being the bottommost frame. Can be abbrevated "bt"
  • "frame n" - switch to stack frame 'n'. Can be abbreviated "fr n"
  • "info locals" - print the values of all local variables within the frame
  • "info frame" - print everything GDB knows about the stack frame - its arguments, values of saved registers, etc.

Examining Data

  • "x /nfl expression" - examine (ie. print) the contents of the expression. 'n' is the number of instances to dump, and the default is 1. The 'f' is a format specifier:
    x - hex
    d - decimal
    u - unsigned decimal
    t - binary
    f - float
    a - address (also looks up symbolic info)
    i - instruction
    c - char
    s - string
    and 'l' is the size:

    b - byte
    h - halfword
    w - word
    g - 8 bytes
    and 'l' may be blank. ie. "x /8xw $esp" dumps 8 hex 32-bit integers from the stack. Other examples:

    "x /ni address" dumps 'n' instructions starting from address
    "x /nhc address" dumps a PAL Unicode string (see Useful Macros for another way to dump them.)
  • "printf" - works the same as C runtime printf, but omit the parentheses. ie. 'printf "%s", argv[0]'
  • "ptype typename" - show the structure fields etc. for the typename
  • "whatis expression" - show the type of the expression. ie. "whatis argc" in the stack frame for main() prints "type = int"

Source-level Debugging

  • "step", "next" - single-step, and step over. Abbreviations are "s" and "n"
  • "finish" - execute the bottommost function on the stack, until it returns. It can be abbreviated "fin".
  • "continue" - continue running (ie. after a breakpoint). The abbreviation is "c".

Note that "step" and "next" may occasionally fail to step as expected, and will break with a message like:

0x28074f60 in _init () from /home/barrybo/rotor/build/librotor_pal.so

When this happens, enter "fin" once and you should return back into the code you're debugging. This happens only on function calls within the PAL when calling another function within the PAL.

Assembly-level Debugging

GDB has assembly-level debugging, but the source-level debugging features tend to interfere. For example, GDB does not automatically print out the CPU registers when single-stepping at the instruction level. Some hints:

  • CPU register names are available symbolically, by using "$registername". ie. "$eax" or "$esp"
  • Before doing assembly-level debugging, enter "display /i $eip", which instructs GDB to disassemble the instruction at $eip each time it prompts. Use "undisplay" to remove this.
  • The "disassemble" command disassembles assembly instructions. "disassemble symbol" or "disassemble address" will disassemble the entire function containing the symbol/address. There are two workarounds: use "disassemble startaddress endaddress" to disassemble a range, or use "x /30i address" to disassemble 30 instructions (or whatever number you want). Note that disassembly is in AT&T style, not Intel-style like the *.s source files use. Use "set disassembly-flavor intel" to switch, and "set disassembly-flavor att" to switch back.
  • "stepi" and "nexti" single-step and trace instructions. "nexti" does not work while debugging jitted code: use "tbreak *address" to set a temporary breakpoint, then 'c' to continue until the breakpoint is hit.

Threads

  • "info threads" - shows the list of all threads
  • "thread n" - switch to thread #n
  • "thread apply n command" - run "command" on thread #n. Use "thread apply all command" to run the command on every thread. ie. "thread apply all bt" shows all callstacks.

Others

  • "info sharedlibrary" - shows all loaded shared libraries
  • "list" - show the source file starting at the current line
  • "call function" - invoke a function within the debuggee process. ie. "call GetLastError()" will show the thread's lasterror value. GDB is not robust and may crash both the debugger and debuggee if somthing goes wrong in a "call", so use it only in scenarios where loss of the scenario is acceptable.
  • "p *(MethodDesc *)address", where the address is the 4 bytes stored directly in front of a jitted method, will print out the method name and other info
  • "setenv COMPlus_JitHalt=class::name" tells the JIT to insert a breakpoint at the start of the given method
  • "setenv COMPLus_JitTrace=1" generates a method trace

Useful Macros

Create or modify ~/.gdbinit to define some handy macros to make debugging easier:

define printw
call PAL_get_stderr()
call (void) PAL_fprintf($, "%S", $arg0)
echo \n
end
 
define du
printw $arg0
end
 
define pw
printw $arg0
end
 
define sos
call (void)SOS("$arg0")
echo \n
end

The printw, du, and pw macros are all aliases for a command which prints a PAL Unicode string out as text. PAL Unicode characters are 16-bit, which are encoded differently than Unix Unicode characters, which are 32-bit. The macros print the string to the debuggee's stderr.

The sos macro can be used to load and call a .so file which contains postmortem debugging tools for managed code, called SOS. For more information on this, see clr/src/vm/ceemain.cpp's SOS() function, and also the contents of clr/src/tools/sos.

 

Author: "joelpob"
Comments Send by mail Print  Save  Delicious 
» You can also retrieve older items : Read
» © All content and copyrights belong to their respective authors.«
» © FeedShow - Online RSS Feeds Reader