Click here to Skip to main content
Click here to Skip to main content

C# Scripts using DynamicMethod

By , 10 May 2011
 

Introduction

I had the problem to host active content in my document files, which included small scripts for animations and object specific interactions, like JavaScripts in HTML.

C# offers the great possibility to compile your own assemblies at runtime. However, there is no possibility to unload such compilations, to unload dynamic generated assemblies at runtime. The only way to do this is to create such assemblies in your own Domain and to unload such Domains later on but the communication between Domains is slow like inter process communication. Additional, to load the C# compiler environment and the compilation itself is not very fast at runtime, not nice for documents with hundreds of small internal scripts.

There are already quite a few articles about dynamic code generation using .NET and how to ship around these problems, but nothing was good enough for my case. The idea was to write my own C# script compiler based on C# syntax and conventions and to use Dynamic Methods to generate IL for best performance.

I found out that this works well without any assembly generation. With such solution, it is possible to use all existing classes and value structures but it is not possible to define your own new classes. The reason for this is that a .NET class always needs an assembly and the related assembly information.
However, the script itself works like a unique class with member functions and variables.

Using the Demo

For demonstration purposes, I wrote a small and very limited test program, only three C# files:
Program.cs contains a very simple user interface and EditCtrl.cs a simple code editor control.
The file Script.cs contains the class Script and this class is easy to use in other C# projects.

The demo looks like this and can be used to check and debug functionality and speed, the directory Demos contains some demo scripts for this.

Using the Code

To use the code in other C# projects, it is only necessary to import the class Script from Script.cs. After this is done, it’s possible to use the Script class like this:

var script = new Script();
script.Code = "using System.Windows.Forms; MessageBox.Show(\"Hello World!\");";
script.Run(null);

The second line in Script.cs contains the expression #define TraceOpCode. If this is defined (currently only in DEBUG), the Debug Output window will show the current MSIL output.
For this simple example, it is only:

ldstr Hello World!
call System.Windows.Forms.DialogResult Show(System.String)
pop
ret

How It Works

The namespace System.Reflection.Emit contains the class DynamicMethod. This class exists since .NET FrameWork version 2.0.
It is possible to use the DynamicMethod class to generate and execute methods at run time, without having to generate a dynamic assembly and a dynamic type to contain the method. Dynamic methods are the most efficient way to generate and execute small amounts of code. A good reference of how to use and an example code can be found here.

The Script class encapsulates a simple array of Dynamic methods: DynamicMethod[] methods. Every script function and the script body as creator is compiled to one of the dynamic methods in this array.
For this, the Script class contains the private helper class Script.Compiler to translate the script code to MSIL instructions using the ILGenerator from DynamicMethod.
After this own compilation, the .NET Framework just-in-time (JIT) compiler can translate the MSIL instructions to native machine code.
In difference to script interpreters, we get fast machine code for each supported CPU architecture.

Restrictions

The current compiler version has no implementation for switch and while. However, the same functionality is possible with if and for statements. There is o support for native unsafe pointers. Alternatively, the compiler implemented in C# is easy to extend for such and other requirements.

Feedback

I would appreciate any feedback you can give me on the code, concept, or the article itself.
Also, I'm curious about your ideas for enhancements and if you implement this concept what was the result.

History

  • 10th May, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

D. Christian Ohle
Germany Germany
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionmy most genuine admirationmemberserpiccio26 Apr '13 - 9:19 
You manually parsed to IL code c# code, that' s crazy cool. And you also shared your project with other programmers willing to learn. I admire you Thumbs Up | :thumbsup:
Questionunderscore is missingmemberVejen30 May '12 - 5:03 
First let me say: Thank you for sharing this with us. The code is really cool.
However, I have a little problem. On my computer when I try to write code the symbol _(underscore) is not appearing. Is there easy way to fix this or this is just my computer?. It does appear when I comment the line with //, but as soon as I leave it as code the symbol disappears?!?
Thanks
Vivi
AnswerRe: underscore is missingmemberD. Christian Ohle30 May '12 - 20:34 
Hello Vivi, I suspect that your DPI settings are different and that the underscores are overwritten from the next line. The Edit Control is quite simple and I spend no time to check what happens in different reselutions.
You could try to change the fontsize in EditCtrl.cpp
Font = new Font("Courier New", 13, FontStyle.Regular, GraphicsUnit.Pixel);
or the line: chardy = (int)charsize.Height + 1;
Would be nice to hear that this was the problem and how to solve.
Thanks
Christian
GeneralRe: underscore is missingmemberVejen1 Jun '12 - 4:40 
Hi Christian,
Thank you again for your prompt reply.
After some careful observation, I found out that underscore actually appears on the redactor, but only on the last line. As soon as I enter a line underneath- the underscore symbol disappears. I have tried different fonts, but this didn’t solve the problem because than the highlighting of the text shifted. The way I have fixed this was to change the Height by replacing “chardy = (int)charsize.Height + 1;” with “chardy = (int)charsize.Height + 2;”.
 
Thanks Christian. Very well spotted.
 
Vivi
QuestionImpressive. 2 questions. Hope you still work on this...memberThomas Zeutschler20 Mar '12 - 9:30 
Hello Christian,
 
great idea, great code. Do you still work on this ?
 
I would like to check if this can be a basis for a rule-engine (currently i'm using a self developed interpreter, works fine but performance is sometimes an issue) and have two general questions that you may can answer before I decide to dive into the deep sea of IL:
 
1. Would it be possible to implement a method to call methods from outside like this "string result = script.MyMethod(myObject);" or at least like that "object result = script.Call("MyMethod", myObject);"?
 
2. Did I understood it right, that I can call and use reference of my own internal namespaces and classes?
 
You code is not so much documented Wink | ;-) Is there a version with more hints on what you are doing that you may can share. It is (at least for me) very hard to understand or maintain such genius low level code. I spend some hours reading it - hard stuff Smile | :)
 
Many thanks Thomas (german, like you...)
AnswerRe: Impressive. 2 questions. Hope you still work on this...memberD. Christian Ohle22 Mar '12 - 5:25 
Hello Thomas
 
Unfortunately (or maybe fortunately) the solution with IL is not more state of art. In the mean time we have linq expressions and it is much more efficent to use this technique to generate delegates from something like c or other syntax.
 
I have done this but I can not publish because I have a contract.
I can only assure that based on expression it needs only a fraction of code and it's more safe, faster and there seems to be a optimization behind.
 
Your questions: it's possible of course but you should check out the linq expressions approach.
Regards
Christian
QuestionParametersmemberrbacenetti9 Jan '12 - 8:05 
Would it be possible to pass parameter to the script and get back results ?
GeneralMy vote of 5memberMember 832045514 Oct '11 - 19:34 
Thanks a lot for sharing!
GeneralMy vote of 2memberVanBele23 May '11 - 5:03 
The article leaves the implementation details out. Which in a way should be the point of an article, not just providing a cool tool.
GeneralVery very interesting!memberpl.218 May '11 - 1:33 
please, keep on!
GeneralMy vote of 3memberMember 473373916 May '11 - 19:55 
.
General@ All, ThanksmemberD. Christian Ohle12 May '11 - 4:16 
Many Thanks for response critics and suggestions.
 
In the meantime I found a lot of possible significant enhancements, bugfixes and it would be possible to implement the complete C#4 specification, Extensions, Pointers, Inline delegates, Linq etc. All what is possible on these way, except declaration of new classes of course.
The challenge would be to find the best compromise between compiler code size, compilation time and resulting code quality.
On this occasion I would try to write the code with high-quality comments and documentation what could help others to help for improvements, the current version is more like a draft.
However, in the moment I'm happy for criticism and suggestions and maybe that I'm on a total wrong way, that there is a much better way to do such things....
 
That's way... Thanks at all
GeneralCool!memberahmet_uzun10 May '11 - 23:45 
I've been working on scripting with CodeDOM for some time and I think this article and the supplied code is a cool way of executing small scripts. Debugging feature is even more interesting.
 
Mr. Ohle. Thank you very much for sharing this...
GeneralNice article & codememberwvd_vegt10 May '11 - 22:28 
Hi,
 
Nice article and code (something else then the good old codedom compiler stuff).
 
Any chance for a overview of supported syntax? It's hard to extract from the script.cs.
 
wvd_vegt
GeneralMy vote of 3memberJV999910 May '11 - 22:20 
Only 3 because you gave us almost no information or details on what you did.
 
This could be a great article if you expand it with the info John Brett already mentioned...
GeneralSome details about the implementation, pleasememberJohn Brett10 May '11 - 21:23 
I think this has the basis of being a fascinating article.
However, there's very little detail about the actual implementation, beyond the basic concept of using DynamicObject.
If there were more detail about how it all worked, I'd rate this article.
GeneralInterestingmembersam.hill10 May '11 - 12:38 
This looks quite interesting.
How does execution speed compare to running the same code/logic as a compiled assembly?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 10 May 2011
Article Copyright 2011 by D. Christian Ohle
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid