1 //------------------------------------------------------------------------------
2 // <copyright file="XmlILModule.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
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;
19 namespace System.Xml.Xsl.IlGen {
20 using DebuggingModes = DebuggableAttribute.DebuggingModes;
22 internal enum XmlILMethodAttributes {
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
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
33 private TypeBuilder typeBldr;
34 private Hashtable methods, urlToSymWriter;
35 private string modFile;
36 private bool persistAsm, useLRE, emitSymbols;
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";
42 static XmlILModule() {
44 AssemblyBuilder asmBldr;
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));
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);
62 CreateModulePermissionSet.Assert();
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[] {}));
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);
73 CodeAccessPermission.RevertAssert();
77 public XmlILModule(TypeBuilder typeBldr) {
78 this.typeBldr = typeBldr;
80 this.emitSymbols = ((ModuleBuilder) this.typeBldr.Module).GetSymWriter() != null;
82 this.persistAsm = false;
84 // Index all methods added to this module by unique name
85 this.methods = new Hashtable();
87 if (this.emitSymbols) {
88 // Create mapping from source document to symbol writer
89 this.urlToSymWriter = new Hashtable();
93 public bool EmitSymbols {
95 return this.emitSymbols;
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));
111 this.useLRE = useLRE;
112 this.emitSymbols = emitSymbols;
113 this.persistAsm = false;
115 // Index all methods added to this module by unique name
116 this.methods = new Hashtable();
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();
125 if (XmlILTrace.IsEnabled) {
126 this.modFile = "System.Xml.Xsl.CompiledQuery";
127 this.persistAsm = true;
131 asmBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(
133 this.persistAsm ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run);
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[] { }));
140 // Create mapping from source document to symbol writer
141 this.urlToSymWriter = new Hashtable();
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}));
148 // Create ModuleBuilder
150 modBldr = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery", this.modFile + ".dll", emitSymbols);
152 modBldr = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery", emitSymbols);
154 this.typeBldr = modBldr.DefineType("System.Xml.Xsl.CompiledQuery.Query", TypeAttributes.Public);
159 /// Define a method in this module with the specified name and parameters.
161 public MethodInfo DefineMethod(string name, Type returnType, Type[] paramTypes, string[] paramNames, XmlILMethodAttributes xmlAttrs) {
162 MethodInfo methResult;
164 string nameOrig = name;
165 Type[] paramTypesNew;
166 bool isRaw = (xmlAttrs & XmlILMethodAttributes.Raw) != 0;
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
172 name = nameOrig + " (" + uniqueId + ")";
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;
184 MethodBuilder methBldr;
186 methBldr = this.typeBldr.DefineMethod(
188 MethodAttributes.Private | MethodAttributes.Static,
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[] {}));
199 methBldr.DefineParameter(1, ParameterAttributes.None, RuntimeName);
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]);
206 methResult = methBldr;
209 DynamicMethod methDyn = new DynamicMethod(name, returnType, paramTypes, LREModule);
210 methDyn.InitLocals = true;
213 methDyn.DefineParameter(1, ParameterAttributes.None, RuntimeName);
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]);
220 methResult = methDyn;
223 // Index method by name
224 this.methods[name] = methResult;
229 /// Get an XmlILGenerator that can be used to generate the body of the specified method.
231 public static ILGenerator DefineMethodBody(MethodBase methInfo) {
232 DynamicMethod methDyn = methInfo as DynamicMethod;
234 return methDyn.GetILGenerator();
236 MethodBuilder methBldr = methInfo as MethodBuilder;
237 if (methBldr != null)
238 return methBldr.GetILGenerator();
240 return ((ConstructorBuilder) methInfo).GetILGenerator();
244 /// Find a MethodInfo of the specified name and return it. Return null if no such method exists.
246 public MethodInfo FindMethod(string name) {
247 return (MethodInfo) this.methods[name];
251 /// Define ginitialized data field with the specified name and value.
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);
259 /// Define private static field with the specified name and value.
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);
267 /// Define static constructor for this type.
269 public ConstructorInfo DefineTypeInitializer() {
270 Debug.Assert(!this.useLRE, "Cannot create type initializer for an LRE module");
271 return this.typeBldr.DefineTypeInitializer();
275 /// Add the file name of a document containing source code for this module and return a symbol writer.
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.");
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);
291 /// Once all methods have been defined, CreateModule must be called in order to "bake" the methods within
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() {
301 Hashtable methodsBaked;
304 typBaked = this.typeBldr.CreateType();
306 if (this.persistAsm) {
307 // Persist the assembly to disk
308 ((AssemblyBuilder) this.typeBldr.Module.Assembly).Save(this.modFile + ".dll");
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);
316 this.methods = methodsBaked;
318 // Release TypeBuilder and symbol writer resources
319 this.typeBldr = null;
320 this.urlToSymWriter = null;
325 /// Wrap a delegate around a MethodInfo of the specified name and type and return it.
327 public Delegate CreateDelegate(string name, Type typDelegate) {
329 return Delegate.CreateDelegate(typDelegate, (MethodInfo) this.methods[name]);
331 return ((DynamicMethod) this.methods[name]).CreateDelegate(typDelegate);
335 /// Define unique assembly name (within AppDomain).
337 private static AssemblyName CreateAssemblyName() {
340 System.Threading.Interlocked.Increment(ref AssemblyId);
341 name = new AssemblyName();
342 name.Name = "System.Xml.Xsl.CompiledQuery." + AssemblyId;