Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / IlGen / XmlILModule.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlILModule.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 using System.Collections;
10 using System.Diagnostics;
11 using System.Diagnostics.SymbolStore;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Security;
15 using System.Security.Permissions;
16 using System.Xml.Xsl.Runtime;
17 using System.Runtime.Versioning;
18
19 namespace System.Xml.Xsl.IlGen {
20     using DebuggingModes = DebuggableAttribute.DebuggingModes;
21
22     internal enum XmlILMethodAttributes {
23         None = 0,
24         NonUser = 1,    // Non-user method which should debugger should step through
25         Raw = 2,        // Raw method which should not add an implicit first argument of type XmlQueryRuntime
26     }
27
28     internal class XmlILModule {
29         public static readonly PermissionSet CreateModulePermissionSet;     // Permission set that contains permissions required for generating module
30         private static long AssemblyId;                                     // Unique identifier used to ensure that assembly names are unique within AppDomain
31         private static ModuleBuilder LREModule;                             // Module used to emit dynamic lightweight-reflection-emit (LRE) methods
32
33         private TypeBuilder typeBldr;
34         private Hashtable methods, urlToSymWriter;
35         private string modFile;
36         private bool persistAsm, useLRE, emitSymbols;
37
38         private static readonly Guid LanguageGuid = new Guid(0x462d4a3e, 0xb257, 0x4aee, 0x97, 0xcd, 0x59, 0x18, 0xc7, 0x53, 0x17, 0x58);
39         private static readonly Guid VendorGuid = new Guid(0x994b45c4, 0xe6e9, 0x11d2, 0x90, 0x3f, 0x00, 0xc0, 0x4f, 0xa3, 0x02, 0xa1);
40         private const string RuntimeName = "{" + XmlReservedNs.NsXslDebug + "}" + "runtime";
41
42         static XmlILModule() {
43             AssemblyName asmName;
44             AssemblyBuilder asmBldr;
45
46             CreateModulePermissionSet = new PermissionSet(PermissionState.None);
47             // CreateDelegate demands MemberAccess permission
48             CreateModulePermissionSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
49             // DynamicMethod constructor demands ControlEvidence permissions. 
50             // Emitting symbols in DefineDynamicModule (to allow to debug the stylesheet) requires UnmanagedCode permission. 
51             CreateModulePermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.ControlEvidence | SecurityPermissionFlag.UnmanagedCode));
52
53             AssemblyId = 0;
54
55             // 1. LRE assembly only needs to execute
56             // 2. No temp files need be created
57             // 3. Never allow assembly to Assert permissions
58             asmName = CreateAssemblyName();
59             asmBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
60
61             try {
62                 CreateModulePermissionSet.Assert();
63
64                 // Add custom attribute to assembly marking it as security transparent so that Assert will not be allowed
65                 // and link demands will be converted to full demands.
66                 asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Transparent, new object[] {}));
67
68                 // Store LREModule once.  If multiple threads are doing this, then some threads might get different
69                 // modules.  This is OK, since it's not mandatory to share, just preferable.
70                 LREModule = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery", false);
71             }
72             finally {
73                 CodeAccessPermission.RevertAssert();
74             }
75         }
76
77         public XmlILModule(TypeBuilder typeBldr) {
78             this.typeBldr = typeBldr;
79
80             this.emitSymbols = ((ModuleBuilder) this.typeBldr.Module).GetSymWriter() != null;
81             this.useLRE = false;
82             this.persistAsm = false;
83
84             // Index all methods added to this module by unique name
85             this.methods = new Hashtable();
86
87             if (this.emitSymbols) {
88                 // Create mapping from source document to symbol writer
89                 this.urlToSymWriter = new Hashtable();
90             }
91         }
92
93         public bool EmitSymbols {
94             get {
95                 return this.emitSymbols;
96             }
97         }
98
99         // SxS note: AssemblyBuilder.DefineDynamicModule() below may be using name which is not SxS safe. 
100         // This file is written only for internal tracing/debugging purposes. In retail builds persistAsm 
101         // will be always false and the file should never be written. As a result it's fine just to supress 
102         // the the SxS warning.
103         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
104         [ResourceExposure(ResourceScope.None)]
105         public XmlILModule(bool useLRE, bool emitSymbols) {
106             AssemblyName asmName;
107             AssemblyBuilder asmBldr;
108             ModuleBuilder modBldr;
109             Debug.Assert(!(useLRE && emitSymbols));
110
111             this.useLRE = useLRE;
112             this.emitSymbols = emitSymbols;
113             this.persistAsm = false;
114
115             // Index all methods added to this module by unique name
116             this.methods = new Hashtable();
117
118             if (!useLRE) {
119                 // 1. If assembly needs to support debugging, then it must be saved and re-loaded (rule of CLR)
120                 // 2. Get path of temp directory, where assembly will be saved
121                 // 3. Never allow assembly to Assert permissions
122                 asmName = CreateAssemblyName();
123
124             #if DEBUG
125                 if (XmlILTrace.IsEnabled) {
126                     this.modFile = "System.Xml.Xsl.CompiledQuery";
127                     this.persistAsm = true;
128                 }
129             #endif
130
131                 asmBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(
132                             asmName,
133                             this.persistAsm ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run);
134
135                 // Add custom attribute to assembly marking it as security transparent so that Assert will not be allowed
136                 // and link demands will be converted to full demands.
137                 asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Transparent, new object[] { }));
138
139                 if (emitSymbols) {
140                     // Create mapping from source document to symbol writer
141                     this.urlToSymWriter = new Hashtable();
142
143                     // Add DebuggableAttribute to assembly so that debugging is a better experience
144                     DebuggingModes debuggingModes = DebuggingModes.Default | DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggingModes.DisableOptimizations;
145                     asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Debuggable, new object[] {debuggingModes}));
146                 }
147
148                 // Create ModuleBuilder
149                 if (this.persistAsm)
150                     modBldr = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery", this.modFile + ".dll", emitSymbols);
151                 else
152                     modBldr = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery", emitSymbols);
153
154                 this.typeBldr = modBldr.DefineType("System.Xml.Xsl.CompiledQuery.Query", TypeAttributes.Public);
155             }
156         }
157
158         /// <summary>
159         /// Define a method in this module with the specified name and parameters.
160         /// </summary>
161         public MethodInfo DefineMethod(string name, Type returnType, Type[] paramTypes, string[] paramNames, XmlILMethodAttributes xmlAttrs) {
162             MethodInfo methResult;
163             int uniqueId = 1;
164             string nameOrig = name;
165             Type[] paramTypesNew;
166             bool isRaw = (xmlAttrs & XmlILMethodAttributes.Raw) != 0;
167
168             // Ensure that name is unique
169             while (this.methods[name] != null) {
170                 // Add unique id to end of name in order to make it unique within this module
171                 uniqueId++;
172                 name = nameOrig + " (" + uniqueId + ")";
173             }
174
175             if (!isRaw) {
176                 // XmlQueryRuntime is always 0th parameter
177                 paramTypesNew = new Type[paramTypes.Length + 1];
178                 paramTypesNew[0] = typeof(XmlQueryRuntime);
179                 Array.Copy(paramTypes, 0, paramTypesNew, 1, paramTypes.Length);
180                 paramTypes = paramTypesNew;
181             }
182
183             if (!this.useLRE) {
184                 MethodBuilder methBldr;
185
186                 methBldr = this.typeBldr.DefineMethod(
187                             name,
188                             MethodAttributes.Private | MethodAttributes.Static, 
189                             returnType,
190                             paramTypes);
191
192                 if (emitSymbols && (xmlAttrs & XmlILMethodAttributes.NonUser) != 0) {
193                     // Add DebuggerStepThroughAttribute and DebuggerNonUserCodeAttribute to non-user methods so that debugging is a better experience
194                     methBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.StepThrough, new object[] {}));
195                     methBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.NonUserCode, new object[] {}));
196                 }
197
198                 if (!isRaw)
199                     methBldr.DefineParameter(1, ParameterAttributes.None, RuntimeName);
200
201                 for (int i = 0; i < paramNames.Length; i++) {
202                     if (paramNames[i] != null && paramNames[i].Length != 0)
203                         methBldr.DefineParameter(i + (isRaw ? 1 : 2), ParameterAttributes.None, paramNames[i]);
204                 }
205
206                 methResult = methBldr;
207             }
208             else {
209                 DynamicMethod methDyn = new DynamicMethod(name, returnType, paramTypes, LREModule);
210                 methDyn.InitLocals = true;
211
212                 if (!isRaw)
213                     methDyn.DefineParameter(1, ParameterAttributes.None, RuntimeName);
214
215                 for (int i = 0; i < paramNames.Length; i++) {
216                     if (paramNames[i] != null && paramNames[i].Length != 0)
217                         methDyn.DefineParameter(i + (isRaw ? 1 : 2), ParameterAttributes.None, paramNames[i]);
218                 }
219
220                 methResult = methDyn;
221             }
222
223             // Index method by name
224             this.methods[name] = methResult;
225             return methResult;
226         }
227
228         /// <summary>
229         /// Get an XmlILGenerator that can be used to generate the body of the specified method.
230         /// </summary>
231         public static ILGenerator DefineMethodBody(MethodBase methInfo) {
232             DynamicMethod methDyn = methInfo as DynamicMethod;
233             if (methDyn != null)
234                 return methDyn.GetILGenerator();
235
236             MethodBuilder methBldr = methInfo as MethodBuilder;
237             if (methBldr != null)
238                 return methBldr.GetILGenerator();
239
240             return ((ConstructorBuilder) methInfo).GetILGenerator();
241         }
242
243         /// <summary>
244         /// Find a MethodInfo of the specified name and return it.  Return null if no such method exists.
245         /// </summary>
246         public MethodInfo FindMethod(string name) {
247             return (MethodInfo) this.methods[name];
248         }
249
250         /// <summary>
251         /// Define ginitialized data field with the specified name and value.
252         /// </summary>
253         public FieldInfo DefineInitializedData(string name, byte[] data) {
254             Debug.Assert(!this.useLRE, "Cannot create initialized data for an LRE module");
255             return this.typeBldr.DefineInitializedData(name, data, FieldAttributes.Private | FieldAttributes.Static);
256         }
257
258         /// <summary>
259         /// Define private static field with the specified name and value.
260         /// </summary>
261         public FieldInfo DefineField(string fieldName, Type type) {
262             Debug.Assert(!this.useLRE, "Cannot create field for an LRE module");
263             return this.typeBldr.DefineField(fieldName, type, FieldAttributes.Private | FieldAttributes.Static);
264         }
265
266         /// <summary>
267         /// Define static constructor for this type.
268         /// </summary>
269         public ConstructorInfo DefineTypeInitializer() {
270             Debug.Assert(!this.useLRE, "Cannot create type initializer for an LRE module");
271             return this.typeBldr.DefineTypeInitializer();
272         }
273
274         /// <summary>
275         /// Add the file name of a document containing source code for this module and return a symbol writer.
276         /// </summary>
277         public ISymbolDocumentWriter AddSourceDocument(string fileName) {
278             ISymbolDocumentWriter symDoc;
279             Debug.Assert(this.emitSymbols, "Cannot add source information to a module that doesn't allow symbols.");
280
281             symDoc = this.urlToSymWriter[fileName] as ISymbolDocumentWriter;
282             if (symDoc == null) {
283                 symDoc = ((ModuleBuilder) this.typeBldr.Module).DefineDocument(fileName, LanguageGuid, VendorGuid, Guid.Empty);
284                 this.urlToSymWriter.Add(fileName, symDoc);
285             }
286
287             return symDoc;
288         }
289
290         /// <summary>
291         /// Once all methods have been defined, CreateModule must be called in order to "bake" the methods within
292         /// this module.
293         /// </summary>
294         // SxS note: AssemblyBuilder.Save() below is using name which is not SxS safe. This file is written only for 
295         // internal tracing/debugging purposes. In retail builds persistAsm will be always false and the file should 
296         // never be written. As a result it's fine just to supress the the SxS warning.
297         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
298         [ResourceExposure(ResourceScope.None)]
299         public void BakeMethods() {
300             Type typBaked;
301             Hashtable methodsBaked;
302
303             if (!this.useLRE) {
304                 typBaked = this.typeBldr.CreateType();
305
306                 if (this.persistAsm) {
307                     // Persist the assembly to disk
308                     ((AssemblyBuilder) this.typeBldr.Module.Assembly).Save(this.modFile + ".dll");
309                 }
310
311                 // Replace all MethodInfos in this.methods
312                 methodsBaked = new Hashtable(this.methods.Count);
313                 foreach (string methName in this.methods.Keys) {
314                     methodsBaked[methName] = typBaked.GetMethod(methName, BindingFlags.NonPublic | BindingFlags.Static);
315                 }
316                 this.methods = methodsBaked;
317
318                 // Release TypeBuilder and symbol writer resources
319                 this.typeBldr = null;
320                 this.urlToSymWriter = null;
321             }
322         }
323
324         /// <summary>
325         /// Wrap a delegate around a MethodInfo of the specified name and type and return it.
326         /// </summary>
327         public Delegate CreateDelegate(string name, Type typDelegate) {
328             if (!this.useLRE)
329                 return Delegate.CreateDelegate(typDelegate, (MethodInfo) this.methods[name]);
330
331             return ((DynamicMethod) this.methods[name]).CreateDelegate(typDelegate);
332         }
333
334         /// <summary>
335         /// Define unique assembly name (within AppDomain).
336         /// </summary>
337         private static AssemblyName CreateAssemblyName() {
338             AssemblyName name;
339
340             System.Threading.Interlocked.Increment(ref AssemblyId);
341             name = new AssemblyName();
342             name.Name = "System.Xml.Xsl.CompiledQuery." + AssemblyId;
343
344             return name;
345         }
346     }
347 }