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