| File: System\Xml\Xsl\IlGen\XmlILModule.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Versioning; using System.Security; using System.Xml.Xsl.Runtime; using DebuggingModes = System.Diagnostics.DebuggableAttribute.DebuggingModes; namespace System.Xml.Xsl.IlGen { internal enum XmlILMethodAttributes { None = 0, NonUser = 1, // Non-user method which should debugger should step through Raw = 2, // Raw method which should not add an implicit first argument of type XmlQueryRuntime } [RequiresDynamicCode("Creates DynamicMethods")] internal sealed class XmlILModule { private static long s_assemblyId; // Unique identifier used to ensure that assembly names are unique within AppDomain private static readonly ModuleBuilder s_LREModule = CreateLREModule(); // Module used to emit dynamic lightweight-reflection-emit (LRE) methods private TypeBuilder? _typeBldr; private Hashtable _methods; private readonly bool _useLRE, _emitSymbols; private const string RuntimeName = $"{{{XmlReservedNs.NsXslDebug}}}runtime"; private static ModuleBuilder CreateLREModule() { // 1. LRE assembly only needs to execute // 2. No temp files need be created // 3. Never allow assembly to Assert permissions AssemblyName asmName = CreateAssemblyName(); AssemblyBuilder asmBldr = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run); // Add custom attribute to assembly marking it as security transparent so that Assert will not be allowed // and link demands will be converted to full demands. asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Transparent, Array.Empty<object>())); // Store LREModule once. If multiple threads are doing this, then some threads might get different // modules. This is OK, since it's not mandatory to share, just preferable. return asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery"); } public XmlILModule(TypeBuilder typeBldr) { _typeBldr = typeBldr; _emitSymbols = false; _useLRE = false; // Index all methods added to this module by unique name _methods = new Hashtable(); } public bool EmitSymbols { get { return _emitSymbols; } } // SxS note: AssemblyBuilder.DefineDynamicModule() below may be using name which is not SxS safe. // This file is written only for internal tracing/debugging purposes. In retail builds persistAsm // will be always false and the file should never be written. As a result it's fine just to suppress // the SxS warning. public XmlILModule(bool useLRE, bool emitSymbols) { AssemblyName asmName; AssemblyBuilder asmBldr; ModuleBuilder modBldr; Debug.Assert(!(useLRE && emitSymbols)); _useLRE = useLRE; _emitSymbols = emitSymbols; // Index all methods added to this module by unique name _methods = new Hashtable(); if (!useLRE) { // 1. If assembly needs to support debugging, then it must be saved and re-loaded (rule of CLR) // 2. Get path of temp directory, where assembly will be saved // 3. Never allow assembly to Assert permissions asmName = CreateAssemblyName(); asmBldr = AssemblyBuilder.DefineDynamicAssembly( asmName, AssemblyBuilderAccess.Run); // Add custom attribute to assembly marking it as security transparent so that Assert will not be allowed // and link demands will be converted to full demands. asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Transparent, Array.Empty<object>())); if (emitSymbols) { // Add DebuggableAttribute to assembly so that debugging is a better experience DebuggingModes debuggingModes = DebuggingModes.Default | DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggingModes.DisableOptimizations; asmBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.Debuggable, new object[] { debuggingModes })); } // Create ModuleBuilder modBldr = asmBldr.DefineDynamicModule("System.Xml.Xsl.CompiledQuery"); _typeBldr = modBldr.DefineType("System.Xml.Xsl.CompiledQuery.Query", TypeAttributes.Public); } } /// <summary> /// Define a method in this module with the specified name and parameters. /// </summary> public MethodInfo DefineMethod(string name, Type returnType, Type[] paramTypes, string?[] paramNames, XmlILMethodAttributes xmlAttrs) { MethodInfo methResult; int uniqueId = 1; string nameOrig = name; Type[] paramTypesNew; bool isRaw = (xmlAttrs & XmlILMethodAttributes.Raw) != 0; // Ensure that name is unique while (_methods[name] != null) { // Add unique id to end of name in order to make it unique within this module uniqueId++; name = $"{nameOrig} ({uniqueId})"; } if (!isRaw) { // XmlQueryRuntime is always 0th parameter paramTypesNew = new Type[paramTypes.Length + 1]; paramTypesNew[0] = typeof(XmlQueryRuntime); Array.Copy(paramTypes, 0, paramTypesNew, 1, paramTypes.Length); paramTypes = paramTypesNew; } if (!_useLRE) { MethodBuilder methBldr = _typeBldr!.DefineMethod( name, MethodAttributes.Private | MethodAttributes.Static, returnType, paramTypes); if (_emitSymbols && (xmlAttrs & XmlILMethodAttributes.NonUser) != 0) { // Add DebuggerStepThroughAttribute and DebuggerNonUserCodeAttribute to non-user methods so that debugging is a better experience methBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.StepThrough, Array.Empty<object>())); methBldr.SetCustomAttribute(new CustomAttributeBuilder(XmlILConstructors.NonUserCode, Array.Empty<object>())); } if (!isRaw) methBldr.DefineParameter(1, ParameterAttributes.None, RuntimeName); for (int i = 0; i < paramNames.Length; i++) { if (!string.IsNullOrEmpty(paramNames[i])) methBldr.DefineParameter(i + (isRaw ? 1 : 2), ParameterAttributes.None, paramNames[i]); } methResult = methBldr; } else { DynamicMethod methDyn = new DynamicMethod(name, returnType, paramTypes, s_LREModule); methDyn.InitLocals = true; methResult = methDyn; } // Index method by name _methods[name] = methResult; return methResult; } /// <summary> /// Get an XmlILGenerator that can be used to generate the body of the specified method. /// </summary> public static ILGenerator DefineMethodBody(MethodBase methInfo) { DynamicMethod? methDyn = methInfo as DynamicMethod; if (methDyn != null) return methDyn.GetILGenerator(); MethodBuilder? methBldr = methInfo as MethodBuilder; if (methBldr != null) return methBldr.GetILGenerator(); return ((ConstructorBuilder)methInfo).GetILGenerator(); } /// <summary> /// Find a MethodInfo of the specified name and return it. Return null if no such method exists. /// </summary> public MethodInfo? FindMethod(string name) { return (MethodInfo?)_methods[name]; } /// <summary> /// Define ginitialized data field with the specified name and value. /// </summary> public FieldInfo DefineInitializedData(string name, byte[] data) { Debug.Assert(!_useLRE, "Cannot create initialized data for an LRE module"); return _typeBldr!.DefineInitializedData(name, data, FieldAttributes.Private | FieldAttributes.Static); } /// <summary> /// Define private static field with the specified name and value. /// </summary> public FieldInfo DefineField(string fieldName, Type type) { Debug.Assert(!_useLRE, "Cannot create field for an LRE module"); return _typeBldr!.DefineField(fieldName, type, FieldAttributes.Private | FieldAttributes.Static); } /// <summary> /// Define static constructor for this type. /// </summary> public ConstructorInfo DefineTypeInitializer() { Debug.Assert(!_useLRE, "Cannot create type initializer for an LRE module"); return _typeBldr!.DefineTypeInitializer(); } /// <summary> /// Once all methods have been defined, CreateModule must be called in order to "bake" the methods within /// this module. /// </summary> public void BakeMethods() { Type typBaked; Hashtable methodsBaked; if (!_useLRE) { typBaked = _typeBldr!.CreateType(); // Replace all MethodInfos in this.methods methodsBaked = new Hashtable(_methods.Count); foreach (string methName in _methods.Keys) { methodsBaked[methName] = typBaked.GetMethod(methName, BindingFlags.NonPublic | BindingFlags.Static); } _methods = methodsBaked; // Release TypeBuilder and symbol writer resources _typeBldr = null; } } /// <summary> /// Wrap a delegate around a MethodInfo of the specified name and type and return it. /// </summary> public Delegate CreateDelegate(string name, Type typDelegate) { if (!_useLRE) return ((MethodInfo)_methods[name]!).CreateDelegate(typDelegate); return ((DynamicMethod)_methods[name]!).CreateDelegate(typDelegate); } /// <summary> /// Define unique assembly name (within AppDomain). /// </summary> private static AssemblyName CreateAssemblyName() { AssemblyName name; System.Threading.Interlocked.Increment(ref s_assemblyId); name = new AssemblyName(); name.Name = $"System.Xml.Xsl.CompiledQuery.{s_assemblyId}"; return name; } } }