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