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