1 //------------------------------------------------------------------------------
2 // <copyright file="XslCompiledTransform.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <spec>http://webdata/xml/specs/XslCompiledTransform.xml</spec>
7 //------------------------------------------------------------------------------
9 using System.CodeDom.Compiler;
10 using System.Diagnostics;
11 using System.Diagnostics.CodeAnalysis;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Security;
16 using System.Security.Permissions;
17 using System.Xml.XPath;
18 using System.Xml.Xsl.Qil;
19 using System.Xml.Xsl.Runtime;
20 using System.Xml.Xsl.Xslt;
21 using System.Runtime.Versioning;
22 using System.Xml.XmlConfiguration;
24 namespace System.Xml.Xsl {
27 //----------------------------------------------------------------------------------------------------
28 // Clarification on null values in this API:
29 // stylesheet, stylesheetUri - cannot be null
30 // settings - if null, XsltSettings.Default will be used
31 // stylesheetResolver - if null, XmlNullResolver will be used for includes/imports.
32 // However, if the principal stylesheet is given by its URI, that
33 // URI will be resolved using XmlUrlResolver (for compatibility
34 // with XslTransform and XmlReader).
35 // typeBuilder - cannot be null
36 // scriptAssemblyPath - can be null only if scripts are disabled
37 // compiledStylesheet - cannot be null
38 // executeMethod, queryData - cannot be null
39 // earlyBoundTypes - null means no script types
40 // documentResolver - if null, XmlNullResolver will be used
41 // input, inputUri - cannot be null
42 // arguments - null means no arguments
43 // results, resultsFile - cannot be null
44 //----------------------------------------------------------------------------------------------------
46 public sealed class XslCompiledTransform {
47 // Reader settings used when creating XmlReader from inputUri
48 private static readonly XmlReaderSettings ReaderSettings = null;
50 // Permission set that contains Reflection [MemberAccess] permissions
51 private static readonly PermissionSet MemberAccessPermissionSet;
53 // Version for GeneratedCodeAttribute
54 private const string Version = ThisAssembly.Version;
56 static XslCompiledTransform() {
57 MemberAccessPermissionSet = new PermissionSet(PermissionState.None);
58 MemberAccessPermissionSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
59 ReaderSettings = new XmlReaderSettings();
62 // Options of compilation
63 private bool enableDebug = false;
65 // Results of compilation
66 private CompilerResults compilerResults = null;
67 private XmlWriterSettings outputSettings = null;
68 private QilExpression qil = null;
70 // Executable command for the compiled stylesheet
71 private XmlILCommand command = null;
73 public XslCompiledTransform() {}
75 public XslCompiledTransform(bool enableDebug) {
76 this.enableDebug = enableDebug;
80 /// This function is called on every recompilation to discard all previous results
82 private void Reset() {
83 this.compilerResults = null;
84 this.outputSettings = null;
89 internal CompilerErrorCollection Errors {
90 get { return this.compilerResults != null ? this.compilerResults.Errors : null; }
94 /// Writer settings specified in the stylesheet
96 public XmlWriterSettings OutputSettings {
98 return this.outputSettings;
102 public TempFileCollection TemporaryFiles {
103 [PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
104 get { return this.compilerResults != null ? this.compilerResults.TempFiles : null; }
107 //------------------------------------------------
109 //------------------------------------------------
111 // SxS: This method does not take any resource name and does not expose any resources to the caller.
112 // It's OK to suppress the SxS warning.
113 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
114 [ResourceExposure(ResourceScope.None)]
115 public void Load(XmlReader stylesheet) {
117 LoadInternal(stylesheet, XsltSettings.Default, XsltConfigSection.CreateDefaultResolver());
120 // SxS: This method does not take any resource name and does not expose any resources to the caller.
121 // It's OK to suppress the SxS warning.
122 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
123 [ResourceExposure(ResourceScope.None)]
124 public void Load(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) {
126 LoadInternal(stylesheet, settings, stylesheetResolver);
129 // SxS: This method does not take any resource name and does not expose any resources to the caller.
130 // It's OK to suppress the SxS warning.
131 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
132 [ResourceExposure(ResourceScope.None)]
133 public void Load(IXPathNavigable stylesheet) {
135 LoadInternal(stylesheet, XsltSettings.Default, XsltConfigSection.CreateDefaultResolver());
138 // SxS: This method does not take any resource name and does not expose any resources to the caller.
139 // It's OK to suppress the SxS warning.
140 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
141 [ResourceExposure(ResourceScope.None)]
142 public void Load(IXPathNavigable stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) {
144 LoadInternal(stylesheet, settings, stylesheetResolver);
147 [ResourceConsumption(ResourceScope.Machine)]
148 [ResourceExposure(ResourceScope.Machine)]
149 public void Load(string stylesheetUri) {
151 if (stylesheetUri == null) {
152 throw new ArgumentNullException("stylesheetUri");
154 LoadInternal(stylesheetUri, XsltSettings.Default, XsltConfigSection.CreateDefaultResolver());
157 [ResourceConsumption(ResourceScope.Machine)]
158 [ResourceExposure(ResourceScope.Machine)]
159 public void Load(string stylesheetUri, XsltSettings settings, XmlResolver stylesheetResolver) {
161 if (stylesheetUri == null) {
162 throw new ArgumentNullException("stylesheetUri");
164 LoadInternal(stylesheetUri, settings, stylesheetResolver);
167 [ResourceConsumption(ResourceScope.Machine)]
168 [ResourceExposure(ResourceScope.Machine)]
169 private CompilerResults LoadInternal(object stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) {
170 if (stylesheet == null) {
171 throw new ArgumentNullException("stylesheet");
173 if (settings == null) {
174 settings = XsltSettings.Default;
176 CompileXsltToQil(stylesheet, settings, stylesheetResolver);
177 CompilerError error = GetFirstError();
179 throw new XslLoadException(error);
181 if (!settings.CheckOnly) {
182 CompileQilToMsil(settings);
184 return this.compilerResults;
187 [ResourceConsumption(ResourceScope.Machine)]
188 [ResourceExposure(ResourceScope.Machine)]
189 private void CompileXsltToQil(object stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) {
190 this.compilerResults = new Compiler(settings, this.enableDebug, null).Compile(stylesheet, stylesheetResolver, out this.qil);
194 /// Returns the first compiler error except warnings
196 private CompilerError GetFirstError() {
197 foreach (CompilerError error in compilerResults.Errors) {
198 if (!error.IsWarning) {
205 private void CompileQilToMsil(XsltSettings settings) {
206 this.command = new XmlILGenerator().Generate(this.qil, /*typeBuilder:*/null);
207 this.outputSettings = this.command.StaticData.DefaultWriterSettings;
211 //------------------------------------------------
212 // Compile stylesheet to a TypeBuilder
213 //------------------------------------------------
215 private static volatile ConstructorInfo GeneratedCodeCtor;
217 [ResourceConsumption(ResourceScope.Machine)]
218 [ResourceExposure(ResourceScope.Machine)]
219 public static CompilerErrorCollection CompileToType(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver, bool debug, TypeBuilder typeBuilder, string scriptAssemblyPath) {
220 if (stylesheet == null)
221 throw new ArgumentNullException("stylesheet");
223 if (typeBuilder == null)
224 throw new ArgumentNullException("typeBuilder");
226 if (settings == null)
227 settings = XsltSettings.Default;
229 if (settings.EnableScript && scriptAssemblyPath == null)
230 throw new ArgumentNullException("scriptAssemblyPath");
232 if (scriptAssemblyPath != null)
233 scriptAssemblyPath = Path.GetFullPath(scriptAssemblyPath);
236 CompilerErrorCollection errors = new Compiler(settings, debug, scriptAssemblyPath).Compile(stylesheet, stylesheetResolver, out qil).Errors;
238 if (!errors.HasErrors) {
239 // Mark the type with GeneratedCodeAttribute to identify its origin
240 if (GeneratedCodeCtor == null)
241 GeneratedCodeCtor = typeof(GeneratedCodeAttribute).GetConstructor(new Type[] { typeof(string), typeof(string) });
243 typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(GeneratedCodeCtor,
244 new object[] { typeof(XslCompiledTransform).FullName, Version }));
246 new XmlILGenerator().Generate(qil, typeBuilder);
252 //------------------------------------------------
253 // Load compiled stylesheet from a Type
254 //------------------------------------------------
256 public void Load(Type compiledStylesheet) {
258 if (compiledStylesheet == null)
259 throw new ArgumentNullException("compiledStylesheet");
261 object[] customAttrs = compiledStylesheet.GetCustomAttributes(typeof(GeneratedCodeAttribute), /*inherit:*/false);
262 GeneratedCodeAttribute generatedCodeAttr = customAttrs.Length > 0 ? (GeneratedCodeAttribute)customAttrs[0] : null;
264 // If GeneratedCodeAttribute is not there, it is not a compiled stylesheet class
265 if (generatedCodeAttr != null && generatedCodeAttr.Tool == typeof(XslCompiledTransform).FullName) {
267 if(new Version(Version).CompareTo(new Version(generatedCodeAttr.Version)) < 0) {
268 throw new ArgumentException(Res.GetString(Res.Xslt_IncompatibleCompiledStylesheetVersion, generatedCodeAttr.Version, Version), "compiledStylesheet");
271 FieldInfo fldData = compiledStylesheet.GetField(XmlQueryStaticData.DataFieldName, BindingFlags.Static | BindingFlags.NonPublic);
272 FieldInfo fldTypes = compiledStylesheet.GetField(XmlQueryStaticData.TypesFieldName, BindingFlags.Static | BindingFlags.NonPublic);
274 // If private fields are not there, it is not a compiled stylesheet class
275 if (fldData != null && fldTypes != null) {
276 if (System.Xml.XmlConfiguration.XsltConfigSection.EnableMemberAccessForXslCompiledTransform)
278 // Need MemberAccess reflection permission to access a private data field and create a delegate
279 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert();
282 // Retrieve query static data from the type
283 byte[] queryData = fldData.GetValue(/*this:*/null) as byte[];
285 if (queryData != null) {
286 MethodInfo executeMethod = compiledStylesheet.GetMethod("Execute", BindingFlags.Static | BindingFlags.NonPublic);
287 Type[] earlyBoundTypes = (Type[])fldTypes.GetValue(/*this:*/null);
289 // Load the stylesheet
290 Load(executeMethod, queryData, earlyBoundTypes);
296 // Throw an exception if the command was not loaded
297 if (this.command == null)
298 throw new ArgumentException(Res.GetString(Res.Xslt_NotCompiledStylesheet, compiledStylesheet.FullName), "compiledStylesheet");
301 public void Load(MethodInfo executeMethod, byte[] queryData, Type[] earlyBoundTypes) {
304 if (executeMethod == null)
305 throw new ArgumentNullException("executeMethod");
307 if (queryData == null)
308 throw new ArgumentNullException("queryData");
310 // earlyBoundTypes may be null
312 if (!System.Xml.XmlConfiguration.XsltConfigSection.EnableMemberAccessForXslCompiledTransform)
314 // make sure we have permission to create the delegate if the type is not visible.
315 // If the declaring type is null we cannot check for visibility.
316 // NOTE: a DynamicMethod will always have a DeclaringType == null. DynamicMethods will do demand on their own if skipVisibility is true.
317 if (executeMethod.DeclaringType != null && !executeMethod.DeclaringType.IsVisible)
319 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Demand();
323 DynamicMethod dm = executeMethod as DynamicMethod;
324 Delegate delExec = (dm != null) ? dm.CreateDelegate(typeof(ExecuteDelegate)) : Delegate.CreateDelegate(typeof(ExecuteDelegate), executeMethod);
325 this.command = new XmlILCommand((ExecuteDelegate)delExec, new XmlQueryStaticData(queryData, earlyBoundTypes));
326 this.outputSettings = this.command.StaticData.DefaultWriterSettings;
329 //------------------------------------------------
330 // Transform methods which take an IXPathNavigable
331 //------------------------------------------------
333 public void Transform(IXPathNavigable input, XmlWriter results) {
334 CheckArguments(input, results);
335 Transform(input, (XsltArgumentList)null, results, XsltConfigSection.CreateDefaultResolver());
338 public void Transform(IXPathNavigable input, XsltArgumentList arguments, XmlWriter results) {
339 CheckArguments(input, results);
340 Transform(input, arguments, results, XsltConfigSection.CreateDefaultResolver());
343 public void Transform(IXPathNavigable input, XsltArgumentList arguments, TextWriter results) {
344 CheckArguments(input, results);
345 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
346 Transform(input, arguments, writer, XsltConfigSection.CreateDefaultResolver());
351 public void Transform(IXPathNavigable input, XsltArgumentList arguments, Stream results) {
352 CheckArguments(input, results);
353 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
354 Transform(input, arguments, writer, XsltConfigSection.CreateDefaultResolver());
359 //------------------------------------------------
360 // Transform methods which take an XmlReader
361 //------------------------------------------------
363 public void Transform(XmlReader input, XmlWriter results) {
364 CheckArguments(input, results);
365 Transform(input, (XsltArgumentList)null, results, XsltConfigSection.CreateDefaultResolver());
368 public void Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results) {
369 CheckArguments(input, results);
370 Transform(input, arguments, results, XsltConfigSection.CreateDefaultResolver());
373 public void Transform(XmlReader input, XsltArgumentList arguments, TextWriter results) {
374 CheckArguments(input, results);
375 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
376 Transform(input, arguments, writer, XsltConfigSection.CreateDefaultResolver());
381 public void Transform(XmlReader input, XsltArgumentList arguments, Stream results) {
382 CheckArguments(input, results);
383 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
384 Transform(input, arguments, writer, XsltConfigSection.CreateDefaultResolver());
389 //------------------------------------------------
390 // Transform methods which take a uri
391 // SxS Note: Annotations should propagate to the caller to have him either check that
392 // the passed URIs are SxS safe or decide that they don't have to be SxS safe and
393 // suppress the message.
394 //------------------------------------------------
396 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")]
397 [ResourceConsumption(ResourceScope.Machine)]
398 [ResourceExposure(ResourceScope.Machine)]
399 public void Transform(string inputUri, XmlWriter results) {
400 CheckArguments(inputUri, results);
401 using (XmlReader reader = XmlReader.Create(inputUri, ReaderSettings)) {
402 Transform(reader, (XsltArgumentList)null, results, XsltConfigSection.CreateDefaultResolver());
406 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")]
407 [ResourceConsumption(ResourceScope.Machine)]
408 [ResourceExposure(ResourceScope.Machine)]
409 public void Transform(string inputUri, XsltArgumentList arguments, XmlWriter results) {
410 CheckArguments(inputUri, results);
411 using (XmlReader reader = XmlReader.Create(inputUri, ReaderSettings)) {
412 Transform(reader, arguments, results, XsltConfigSection.CreateDefaultResolver());
416 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")]
417 [ResourceConsumption(ResourceScope.Machine)]
418 [ResourceExposure(ResourceScope.Machine)]
419 public void Transform(string inputUri, XsltArgumentList arguments, TextWriter results) {
420 CheckArguments(inputUri, results);
421 using (XmlReader reader = XmlReader.Create(inputUri, ReaderSettings))
422 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
423 Transform(reader, arguments, writer, XsltConfigSection.CreateDefaultResolver());
428 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")]
429 [ResourceConsumption(ResourceScope.Machine)]
430 [ResourceExposure(ResourceScope.Machine)]
431 public void Transform(string inputUri, XsltArgumentList arguments, Stream results) {
432 CheckArguments(inputUri, results);
433 using (XmlReader reader = XmlReader.Create(inputUri, ReaderSettings))
434 using (XmlWriter writer = XmlWriter.Create(results, OutputSettings)) {
435 Transform(reader, arguments, writer, XsltConfigSection.CreateDefaultResolver());
440 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings")]
441 [ResourceConsumption(ResourceScope.Machine)]
442 [ResourceExposure(ResourceScope.Machine)]
443 public void Transform(string inputUri, string resultsFile) {
444 if (inputUri == null)
445 throw new ArgumentNullException("inputUri");
447 if (resultsFile == null)
448 throw new ArgumentNullException("resultsFile");
450 // SQLBUDT 276415: Prevent wiping out the content of the input file if the output file is the same
451 using (XmlReader reader = XmlReader.Create(inputUri, ReaderSettings))
452 using (XmlWriter writer = XmlWriter.Create(resultsFile, OutputSettings)) {
453 Transform(reader, (XsltArgumentList)null, writer, XsltConfigSection.CreateDefaultResolver());
458 //------------------------------------------------
459 // Main Transform overloads
460 //------------------------------------------------
462 // SxS: This method does not take any resource name and does not expose any resources to the caller.
463 // It's OK to suppress the SxS warning.
464 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
465 [ResourceExposure(ResourceScope.None)]
466 public void Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver) {
467 CheckArguments(input, results);
469 this.command.Execute((object)input, documentResolver, arguments, results);
472 // SxS: This method does not take any resource name and does not expose any resources to the caller.
473 // It's OK to suppress the SxS warning.
474 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
475 [ResourceExposure(ResourceScope.None)]
476 public void Transform(IXPathNavigable input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver) {
477 CheckArguments(input, results);
479 this.command.Execute((object)input.CreateNavigator(), documentResolver, arguments, results);
482 //------------------------------------------------
484 //------------------------------------------------
486 private static void CheckArguments(object input, object results) {
488 throw new ArgumentNullException("input");
491 throw new ArgumentNullException("results");
494 private static void CheckArguments(string inputUri, object results) {
495 if (inputUri == null)
496 throw new ArgumentNullException("inputUri");
499 throw new ArgumentNullException("results");
502 private void CheckCommand() {
503 if (this.command == null) {
504 throw new InvalidOperationException(Res.GetString(Res.Xslt_NoStylesheetLoaded));
508 //------------------------------------------------
509 // Test suites entry points
510 //------------------------------------------------
512 [ResourceConsumption(ResourceScope.Machine)]
513 [ResourceExposure(ResourceScope.Machine)]
514 private QilExpression TestCompile(object stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) {
516 CompileXsltToQil(stylesheet, settings, stylesheetResolver);
520 private void TestGenerate(XsltSettings settings) {
521 Debug.Assert(qil != null, "You must compile to Qil first");
522 CompileQilToMsil(settings);
525 [ResourceConsumption(ResourceScope.Machine)]
526 [ResourceExposure(ResourceScope.Machine)]
527 private void Transform(string inputUri, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver) {
528 command.Execute(inputUri, documentResolver, arguments, results);
531 internal static void PrintQil(object qil, XmlWriter xw, bool printComments, bool printTypes, bool printLineInfo) {
532 QilExpression qilExpr = (QilExpression)qil;
533 QilXmlWriter.Options options = QilXmlWriter.Options.None;
534 QilValidationVisitor.Validate(qilExpr);
535 if (printComments) options |= QilXmlWriter.Options.Annotations;
536 if (printTypes) options |= QilXmlWriter.Options.TypeInfo;
537 if (printLineInfo) options |= QilXmlWriter.Options.LineInfo;
538 QilXmlWriter qw = new QilXmlWriter(xw, options);