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

Compiling Source Code from a String

, 19 Aug 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
A method that uses a CodeDomProvider to compile program code from a string into an Assembly.

Introduction

For an application I'm working on, I need the ability to allow the user to enter some text and have that text be compiled into a class just as if he had typed the class himself and passed it through a compiler. The application will wrap the text in some template code to form a (hopefully valid) program. But then it needs to be compiled so that the application can use the resultant class. The compiled Assembly doesn't need to be stored to disk for future reference, it can get garbage collected once the application is done with it. This method implements that functionality.

Background

Among the various articles here on the Code Project that deal with this topic are:
Dynamic Creation Of Assemblies/Apps[^]
Compiling with CodeDom[^]

Although I had no need to use CodeDom to create the program code, these articles (and MSDN) provided a starting point for my eventual solution.

My Compile method

There really isn't much to the code:

  1. Check the input parameters
  2. Ask the system if the requested language exists
  3. Get a Provider for the requested language
  4. Get default parameters for the Provider's compiler
  5. Set additional parameters as required
  6. Compile the provided code
  7. Check for errors
  8. Return the compiled Assembly

Any Exceptions thrown by called methods will simply be passed along; there's no point catching them here.

public static System.Reflection.Assembly
Compile
(
    string          Code
,
    string          Language
,
    params string[] ReferencedAssemblies
)
{
    if ( System.String.IsNullOrEmpty ( Code ) )
    {
        throw ( new System.ArgumentException
            ( "You must supply some code" , "Code" ) ) ;
    }

    if ( System.String.IsNullOrEmpty ( Language ) )
    {
        throw ( new System.ArgumentException
            ( "You must supply the name of a known language" , "Language" ) ) ;
    }

    if ( !System.CodeDom.Compiler.CodeDomProvider.IsDefinedLanguage ( Language ) )
    {
        throw ( new System.ArgumentException
            ( "That language is not known on this system" , "Language" ) ) ;
    }

    using
    (
        System.CodeDom.Compiler.CodeDomProvider cdp
    =
        System.CodeDom.Compiler.CodeDomProvider.CreateProvider ( Language )
    )
    {
        System.CodeDom.Compiler.CompilerParameters cp =
            System.CodeDom.Compiler.CodeDomProvider.GetCompilerInfo
                ( Language ).CreateDefaultCompilerParameters() ;

        cp.GenerateInMemory = true ;

        cp.TreatWarningsAsErrors = true ;

        cp.WarningLevel = 4 ;

        cp.ReferencedAssemblies.Add ( "System.dll" ) ;

        if
        (
            ( ReferencedAssemblies != null )
        &&
            ( ReferencedAssemblies.Length > 0 )
        )
        {
            cp.ReferencedAssemblies.AddRange ( ReferencedAssemblies ) ;
        }

        System.CodeDom.Compiler.CompilerResults cr =
            cdp.CompileAssemblyFromSource
            (
                cp
            ,
                Code
            ) ;

        if ( cr.Errors.HasErrors )
        {
            System.Exception err = new System.Exception ( "Compilation failure" ) ;

            err.Data [ "Errors" ] = cr.Errors ;

            err.Data [ "Output" ] = cr.Output ;

            throw ( err ) ;
        }

        return ( cr.CompiledAssembly ) ;
    }
}

Using the Code

In the application I'm working on, the user has the option to enter text into a TextBox just as he would write a string literal when writing a program. For instance, in a (C#) program I may write:

string text = "Hello, world!" ;

Ordinarily, in a TextBox, the Hello, World! would not be entered with the quotes, but in a program it's required. Things get even more complex if the text requires escapes:

string text = "Hello, \"Bob\"!" ;
 
or
 
string text = @"Hello, ""Bob""!" ;

Because mistakes can happen when you transcribe data, for this application I want to allow the user (a developer) to be able to copy-and-paste a string literal, exactly as he wrote it in a program, to the TextBox and have it passed through the compiler, just as it will be when his own application gets compiled.

This method:

  1. Accepts the Text from the TextBox
  2. Wraps it in a very simple C# program (notice how)
  3. Uses the CSharp (C#) compiler to compile the code into an Assembly
  4. Retrieves the Type defined by the program
  5. Gets the field that contains the compiled string literal
  6. Retrieves the string literal and returns it
private static string
WrapText
(
    string Text
)
{
    string code = System.String.Format
    (
        @"
        namespace TextWrapper
        {{
            public static class TextWrapper
            {{
                public const string Text = {0} ;
            }}
        }}
        "
    ,
        Text
    ) ;

    System.Reflection.Assembly assm = PIEBALD.Lib.LibSys.Compile
    (
        code
    ,
        "CSharp"
    ) ;

    System.Type type = assm.GetType ( "TextWrapper.TextWrapper" ) ;

    System.Reflection.FieldInfo field = type.GetField
    (
        "Text"
    ,
        System.Reflection.BindingFlags.Public
        |
        System.Reflection.BindingFlags.Static
    ) ;

    return ( (System.String) field.GetValue ( null ) ) ;
}

Note: I intend to support VB.net as well.

CodeDom is in the System.Runtime.Remoting dll, so add a reference.

Conclusion

Overkill? I don't think so. For the samples above, sure, but consider more complex string literals, especially Regular Expressions, which often contain a lot of escapes and may be long enough to warrant being broken onto several lines. For example here's one that's not too big:

private static readonly System.Text.RegularExpressions.Regex HrefReg =
new System.Text.RegularExpressions.Regex
(
    "href\\s*=\\s*(('(?'uri'[^'#]*)?(#(?'id'[^']*))?')" +
    "|(\"(?'uri'[^\"#]*)?(#(?'id'[^\"]*))?\"))"
) ;

To copy-and-paste this the usual way, I'd have to copy the two sections separately, and then remove the escapes. If I edit the string in the TextBox and then want to copy-and-paste it back, I have to reverse the steps, ensuring that I get all the escapes back where they belong.

Using a verbatim string literal would make it easier:

private static readonly System.Text.RegularExpressions.Regex HrefReg =
new System.Text.RegularExpressions.Regex
(
    @"
    href\s*=\s*(('(?'uri'[^'#]*)?(#(?'id'[^']*))?')
    |(""(?'uri'[^""#]*)?(#(?'id'[^""]*))?""))
    "
) ;

Using the technique described in this article, either of these string literals may be copied-and-pasted directly to the TextBox (or wherever) and parsed by the actual compiler that will be used to compile the strings later. If the text is edited away from the source code, it may be copied-and-pasted back without change.

Points of Interest

I wrestled for a long time with trying to load the compiled Assembly into a different AppDomain. I had some success and a lot of headache. Eventually I came across the following comment in the MSDN documentation of AppDomain.RelativeSearchPath[^]:

// Because Load returns an Assembly object, the assemblies must be
// loaded into the current domain as well. This will fail unless the
// current domain also has these directories in its search path.

So that's what was causing the headache. Plus, if loading an Assembly into a different AppDomain doesn't keep the Assembly from being loaded into the current one, then I don't see the point of trying.

History

2009-08-15 First submitted

License

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

Share

About the Author

PIEBALDconsult
Software Developer (Senior)
United States United States
BSCS 1992 Wentworth Institute of Technology
 
Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.
 
OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB, acknowledged contrarian
 
---------------
 
"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]
 
"Typing is no substitute for thinking." -- R.W. Hamming
 
"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup
 
ZagNut’s Law: Arrogance is inversely proportional to ability.
 
"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon
 
"linq'ish" sounds like "inept" in German -- Andreas Gieriet
 
"Things would be different if I ran the zoo." -- Dr. Seuss
 
"Wrong is evil, and it must be defeated." – Jeff Ello
 
"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw
 
“It’s always easier to do it the hard way.” -- Blackhart

“If Unix wasn’t so bad that you can’t give it away, Bill Gates would never have succeeded in selling Windows.” -- Blackhart

"Omit needless local variables." -- Strunk... had he taught programming
 

 
"We learn more from our mistakes than we do from getting it right the first time."
 
My first rule of debugging: "If you get a different error message, you're making progress."
 
My golden rule of database management: "Do not unto others' databases as you would not have done unto yours."
 
My general rule of software development: "Design should be top-down, but implementation should be bottom-up."

Comments and Discussions

 
GeneralMy vote of 1 PinmemberIzzet Kerem Kusmezer13-Apr-10 11:30 
GeneralRe: My vote of 1 PinmemberIzzet Kerem Kusmezer13-Apr-10 11:31 
GeneralRe: My vote of 1 PinmvpPIEBALDconsult13-Apr-10 12:03 
GeneralRe: My vote of 1 PinmemberIzzet Kerem Kusmezer14-Apr-10 4:15 
GeneralRe: My vote of 1 PinmvpPIEBALDconsult14-Apr-10 5:24 
GeneralRe: My vote of 1 PinmemberIzzet Kerem Kusmezer14-Apr-10 5:27 
GeneralRe: My vote of 1 PinmvpPIEBALDconsult13-Apr-10 12:04 
GeneralInteresting idea. PinmemberOakman24-Aug-09 4:45 
GeneralRe: Interesting idea. PinmemberPIEBALDconsult24-Aug-09 19:03 
GeneralRe: Interesting idea. PinmemberOakman25-Aug-09 2:02 
GeneralRe: Interesting idea. PinmemberPIEBALDconsult26-Aug-09 5:17 
GeneralMy vote of 1 Pinmemberkeefb24-Aug-09 4:08 
GeneralRe: My vote of 1 PinmemberPIEBALDconsult24-Aug-09 19:01 
GeneralMy vote of 1 PinmemberDmitri Nesteruk20-Aug-09 6:10 
GeneralRe: My vote of 1 PinmemberPIEBALDconsult20-Aug-09 8:56 
GeneralIf you are interesed in the subject... PinmemberOleg Shilo19-Aug-09 18:14 
GeneralRe: If you are interesed in the subject... PinmemberPIEBALDconsult19-Aug-09 19:07 
GeneralFormatting PinmvpN a v a n e e t h19-Aug-09 5:11 
GeneralRe: Formatting PinmemberPIEBALDconsult19-Aug-09 5:53 
GeneralRe: Formatting Pinmemberjohannesnestler19-Aug-09 6:11 
GeneralRe: Formatting PinmemberPIEBALDconsult19-Aug-09 6:27 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150327.1 | Last Updated 19 Aug 2009
Article Copyright 2009 by PIEBALDconsult
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid