2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / AssemblyGen.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Microsoft Public License. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Microsoft Public License, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 using System.Collections.Generic;
19 #if CODEPLEX_40
20 using System.Dynamic.Utils;
21 #else
22 using Microsoft.Scripting.Utils;
23 #endif
24 using System.IO;
25 using System.Reflection;
26 using System.Reflection.Emit;
27 using System.Security;
28 using System.Text;
29 using System.Threading;
30
31 #if CODEPLEX_40
32 namespace System.Linq.Expressions.Compiler {
33 #else
34 namespace Microsoft.Linq.Expressions.Compiler {
35 #endif
36     internal sealed class AssemblyGen {
37         private static AssemblyGen _assembly;
38
39         // Testing options. Only ever set in MICROSOFT_SCRIPTING_CORE build
40         // configurations, see SetSaveAssemblies
41 #if MICROSOFT_SCRIPTING_CORE
42         private static string _saveAssembliesPath;
43         private static bool _saveAssemblies;
44 #endif
45
46         private readonly AssemblyBuilder _myAssembly;
47         private readonly ModuleBuilder _myModule;
48
49 #if MICROSOFT_SCRIPTING_CORE && !SILVERLIGHT
50         private readonly string _outFileName;       // can be null iff !SaveAndReloadAssemblies
51         private readonly string _outDir;            // null means the current directory
52 #endif
53         private int _index;
54
55         private static AssemblyGen Assembly {
56             get {
57                 if (_assembly == null) {
58                     Interlocked.CompareExchange(ref _assembly, new AssemblyGen(), null);
59                 }
60                 return _assembly;
61             }
62         }
63
64         private AssemblyGen() {
65             var name = new AssemblyName("Snippets");
66
67 #if SILVERLIGHT  // AssemblyBuilderAccess.RunAndSave, Environment.CurrentDirectory
68             _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
69             _myModule = _myAssembly.DefineDynamicModule(name.Name, false);
70 #else
71
72             // mark the assembly transparent so that it works in partial trust:
73             var attributes = new[] { 
74                 new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0])
75             };
76
77 #if MICROSOFT_SCRIPTING_CORE
78             if (_saveAssemblies) {
79                 string outDir = _saveAssembliesPath ?? Directory.GetCurrentDirectory();
80                 try {
81                     outDir = Path.GetFullPath(outDir);
82                 } catch (Exception) {
83                     throw Error.InvalidOutputDir();
84                 }
85                 try {
86                     Path.Combine(outDir, name.Name + ".dll");
87                 } catch (ArgumentException) {
88                     throw Error.InvalidAsmNameOrExtension();
89                 }
90
91                 _outFileName = name.Name + ".dll";
92                 _outDir = outDir;
93                 _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave, outDir,
94                     null, null, null, null, false, attributes);
95
96                 _myModule = _myAssembly.DefineDynamicModule(name.Name, _outFileName, false);
97             } else
98 #endif
99             {
100                 _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, attributes);
101                 _myModule = _myAssembly.DefineDynamicModule(name.Name, false);
102             }
103
104             _myAssembly.DefineVersionInfoResource();
105 #endif
106         }
107
108         private TypeBuilder DefineType(string name, Type parent, TypeAttributes attr) {
109             ContractUtils.RequiresNotNull(name, "name");
110             ContractUtils.RequiresNotNull(parent, "parent");
111
112             StringBuilder sb = new StringBuilder(name);
113
114             int index = Interlocked.Increment(ref _index);
115             sb.Append("$");
116             sb.Append(index);
117
118             // There is a bug in Reflection.Emit that leads to 
119             // Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup.
120             // if there is any of the characters []*&+,\ in the type name and a method defined on the type is called.
121             sb.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_');
122
123             name = sb.ToString();
124
125             return _myModule.DefineType(name, attr, parent);
126         }
127
128         internal static TypeBuilder DefineDelegateType(string name) {
129             return Assembly.DefineType(
130                 name,
131                 typeof(MulticastDelegate),
132                 TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass
133             );
134         }
135
136 #if MICROSOFT_SCRIPTING_CORE
137         //Return the location of the saved assembly file.
138         //The file location is used by PE verification in Microsoft.Scripting.
139         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
140         internal string SaveAssembly() {
141 #if !SILVERLIGHT // AssemblyBuilder.Save
142             _myAssembly.Save(_outFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
143             return Path.Combine(_outDir, _outFileName);
144 #else
145             return null;
146 #endif
147         }
148
149         // NOTE: this method is called through reflection from Microsoft.Scripting
150         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
151         internal static void SetSaveAssemblies(bool enable, string directory) {
152             _saveAssemblies = enable;
153             _saveAssembliesPath = directory;
154         }
155
156         // NOTE: this method is called through reflection from Microsoft.Scripting
157         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
158         internal static string[] SaveAssembliesToDisk() {
159             if (!_saveAssemblies) {
160                 return new string[0];
161             }
162
163             var assemlyLocations = new List<string>();
164
165             // first save all assemblies to disk:
166             if (_assembly != null) {
167                 string assemblyLocation = _assembly.SaveAssembly();
168                 if (assemblyLocation != null) {
169                     assemlyLocations.Add(assemblyLocation);
170                 }
171                 _assembly = null;
172             }
173
174             return assemlyLocations.ToArray();
175         }
176 #endif
177     }
178
179     internal static class SymbolGuids {
180         internal static readonly Guid DocumentType_Text =
181             new Guid(0x5a869d0b, 0x6611, 0x11d3, 0xbd, 0x2a, 0, 0, 0xf8, 8, 0x49, 0xbd);
182     }
183 }
184