1 //------------------------------------------------------------------------------
2 // <copyright file="TemplateParser.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 * Implements the ASP.NET template parser
10 * Copyright (c) 1998 Microsoft Corporation
13 // Turn this on to do regex profiling
14 //#define PROFILE_REGEX
16 namespace System.Web.UI {
17 using System.Runtime.Serialization.Formatters;
19 using System.Runtime.InteropServices;
20 using System.Runtime.Serialization;
23 using System.Collections;
24 using System.Collections.Generic;
25 using System.Collections.Specialized;
26 using System.Threading;
27 using System.Reflection;
28 using System.Globalization;
29 using System.CodeDom.Compiler;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Web.Caching;
33 using System.Web.Util;
34 using System.Web.Hosting;
35 using System.Web.Compilation;
36 using HttpException = System.Web.HttpException;
37 using System.Text.RegularExpressions;
38 using System.Security.Permissions;
39 using System.Web.Configuration;
40 using System.Web.Instrumentation;
45 /// <para>[To be supplied.]</para>
47 public abstract class TemplateParser : BaseParser, IAssemblyDependencyParser {
49 internal const string CodeFileBaseClassAttributeName = "codefilebaseclass";
51 // The <compilation> config section
52 private CompilationSection _compConfig;
53 internal CompilationSection CompConfig {
54 get { return _compConfig; }
57 // The <pages> config section
58 private PagesSection _pagesConfig;
59 internal PagesSection PagesConfig {
60 get { return _pagesConfig; }
63 // const masks into the BitVector32
64 private const int isServerTag = 0x00000001;
65 private const int inScriptTag = 0x00000002;
66 private const int ignoreScriptTag = 0x00000004;
67 private const int ignoreNextSpaceString = 0x00000008;
68 internal const int requiresCompilation = 0x00000010; // Has constructs that require compilation
69 private const int ignoreControlProperties = 0x00000020;
70 internal const int aspCompatMode = 0x00000040;
71 private const int hasCodeBehind = 0x00000080;
72 private const int inDesigner = 0x00000100;
73 private const int ignoreParseErrors = 0x00000200;
74 private const int mainDirectiveSpecified = 0x00000400;
75 private const int mainDirectiveHandled = 0x00000800;
76 private const int useExplicit = 0x00001000;
77 private const int hasDebugAttribute = 0x00002000;
78 private const int debug = 0x00004000;
79 private const int noLinePragmas = 0x00008000;
80 private const int strict = 0x00010000;
81 internal const int noAutoEventWireup = 0x00020000;
82 private const int attemptedImplicitResources = 0x00040000;
83 internal const int buffer = 0x00080000;
84 internal const int requiresSessionState = 0x00100000;
85 internal const int readOnlySessionState = 0x00200000;
86 internal const int validateRequest = 0x00400000;
87 internal const int asyncMode = 0x00800000;
88 private const int throwOnFirstParseError = 0x01000000;
89 private const int ignoreParserFilter = 0x02000000;
90 internal const int calledFromParseControlFlag = 0x04000000;
91 #pragma warning disable 0649
92 internal SimpleBitVector32 flags;
93 #pragma warning restore 0649
95 private MainTagNameToTypeMapper _typeMapper;
96 internal MainTagNameToTypeMapper TypeMapper { get { return _typeMapper; } }
98 internal ICollection UserControlRegisterEntries { get { return TypeMapper.UserControlRegisterEntries; } }
99 internal List<TagNamespaceRegisterEntry> TagRegisterEntries { get { return TypeMapper.TagRegisterEntries; } }
101 private Stack _builderStack; // Stack of BuilderStackEntry's
102 internal Stack BuilderStack {
104 EnsureRootBuilderCreated();
105 return _builderStack;
110 private StringSet _idList;
111 private Stack _idListStack;
112 private ScriptBlockData _currentScript;
113 private StringBuilder _literalBuilder;
115 // The line number in file currently being parsed
116 internal int _lineNumber;
118 // The line number at which the current script block started
119 private int _scriptStartLineNumber;
121 // String that contains the data to be parsed
122 private string _text;
124 get { return _text; }
125 internal set { _text = value; }
128 // The class from which to inherit if we are compiling a class
129 private Type _baseType;
130 internal Type BaseType {
131 get { return _baseType; }
132 set { _baseType = value; }
135 // The namespace and name of the class from which to inherit. Only used with code separation,
136 // since we don't have the live Type in that case (not yet compiled)
137 private string _baseTypeNamespace;
138 internal string BaseTypeNamespace { get { return _baseTypeNamespace; } }
139 private string _baseTypeName;
140 internal string BaseTypeName { get { return _baseTypeName; } }
142 internal bool IgnoreControlProperties {
143 get { return flags[ignoreControlProperties]; }
144 set { flags[ignoreControlProperties] = value; }
147 // Indicates whether the parser should throw on the first error.
148 internal bool ThrowOnFirstParseError {
149 get { return flags[throwOnFirstParseError]; }
150 set { flags[throwOnFirstParseError] = value; }
153 // The interfaces that we implement (ArrayList of Type objects)
154 private ArrayList _implementedInterfaces;
155 internal ArrayList ImplementedInterfaces { get { return _implementedInterfaces; } }
157 internal bool HasCodeBehind { get { return flags[hasCodeBehind]; } }
159 internal abstract Type DefaultBaseType { get; }
161 internal PageParserFilter _pageParserFilter;
163 private IImplicitResourceProvider _implicitResourceProvider;
165 // The FInDesigner property gets used by control builders so that
166 // they can behave differently if needed.
167 internal virtual bool FInDesigner {
168 get { return flags[inDesigner]; }
169 set { flags[inDesigner] = value; }
172 // When this is set, we ignore parse errors and keep on processing the page as
173 // well as possible. This is used for the Venus CBM scenario
174 internal virtual bool IgnoreParseErrors {
175 get { return flags[ignoreParseErrors]; }
176 set { flags[ignoreParseErrors] = value; }
179 // When true, it is not legal to have any constructs that require compilation
180 private CompilationMode _compilationMode;
181 internal CompilationMode CompilationMode {
183 // When precompiling for deployment, always compile everything (VSWhidbey 266509)
184 if (BuildManager.PrecompilingForDeployment)
185 return CompilationMode.Always;
187 return _compilationMode;
190 if (value == CompilationMode.Never && flags[requiresCompilation]) {
191 ProcessError(SR.GetString(SR.Compilmode_not_allowed));
194 _compilationMode = value;
198 private ParserErrorCollection _parserErrors;
199 private ParserErrorCollection ParserErrors {
201 if (_parserErrors == null) {
202 _parserErrors = new ParserErrorCollection();
205 return _parserErrors;
209 private bool HasParserErrors {
210 get { return _parserErrors != null && _parserErrors.Count > 0; }
213 // Method to report parser errors.
214 protected void ProcessError(string message) {
215 // Ignore the errors if in that mode.
216 if (IgnoreParseErrors) {
220 // Rethrow as innerexception if in that mode.
221 if (ThrowOnFirstParseError) {
222 throw new HttpException(message);
225 // otherwise add to the error collection with proper info.
226 ParserError parseError = new ParserError(message, CurrentVirtualPath, _lineNumber);
227 ParserErrors.Add(parseError);
229 // If there is a CBM callback, inform it of the error
230 BuildManager.ReportParseError(parseError);
233 // Method to report exception, this is called when external exceptions are caught in the parser.
234 protected void ProcessException(Exception ex) {
235 // Ignore the errors if in that mode.
236 if (IgnoreParseErrors) {
240 // Rethrow as innerexception if in that mode or it is a compile exception.
241 if (ThrowOnFirstParseError || ex is HttpCompileException) {
242 if (ex is HttpParseException)
244 throw new HttpParseException(ex.Message, ex);
247 // If it is already a parser exception remember the location corresponding to
248 // the original error.
249 ParserError parseError;
251 HttpParseException hpe = ex as HttpParseException;
253 parseError = new ParserError(hpe.Message, hpe.VirtualPath, hpe.Line);
256 parseError = new ParserError(ex.Message, CurrentVirtualPath, _lineNumber);
259 // Remember the original exception.
260 parseError.Exception = ex;
262 ParserErrors.Add(parseError);
264 // If there is a CBM callback, inform it of the error only if the HttpParseException comes
265 // from the current virtualpath. Since if the exception is thrown from parsing another file,
266 // it would have been reported already.
267 if (hpe == null || CurrentVirtualPath.Equals(hpe.VirtualPathObject)) {
268 BuildManager.ReportParseError(parseError);
272 // When true, there are construct that require compilation
273 internal virtual bool RequiresCompilation {
275 // By default, require compilation. The Page parser overrides this
276 // and can allow no-compile pages depending on the page contents
281 internal virtual bool IsCodeAllowed {
283 // If it's a no-compile page, code is not allowed
284 if (CompilationMode == CompilationMode.Never)
287 // Likewise, check if the PageParserFilter allows code
288 if (_pageParserFilter != null && !_pageParserFilter.AllowCode)
295 internal void EnsureCodeAllowed() {
297 // If it's a no-compile page, fail since there is code on it.
298 // Likewise if the PageParserFilter returns IsCodeAllowed == false
299 if (!IsCodeAllowed) {
300 ProcessError(SR.GetString(SR.Code_not_allowed));
303 // Remember the fact that this page MUST be compiled
304 flags[requiresCompilation] = true;
307 // This is called whenever we parse an attribute that requires compilation
308 internal void OnFoundAttributeRequiringCompilation(string attribName) {
310 // If compilation is not alowed, fail
311 if (!IsCodeAllowed) {
312 ProcessError(SR.GetString(SR.Attrib_not_allowed, attribName));
315 // Remember the fact that this page MUST be compiled
316 flags[requiresCompilation] = true;
319 // This is called whenever we parse a directive that requires compilation
320 internal void OnFoundDirectiveRequiringCompilation(string directiveName) {
322 // If compilation is not alowed, fail
323 if (!IsCodeAllowed) {
324 ProcessError(SR.GetString(SR.Directive_not_allowed, directiveName));
327 // Remember the fact that this page MUST be compiled
328 flags[requiresCompilation] = true;
331 // This is called whenever we parse an event attribute on a tag
332 internal void OnFoundEventHandler(string directiveName) {
334 // If compilation is not alowed, fail
335 if (!IsCodeAllowed) {
336 ProcessError(SR.GetString(SR.Event_not_allowed, directiveName));
339 // Remember the fact that this page MUST be compiled
340 flags[requiresCompilation] = true;
343 private IDesignerHost _designerHost;
344 private ITypeResolutionService _typeResolutionService;
345 internal IDesignerHost DesignerHost {
347 Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
348 return _designerHost;
351 Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
352 _designerHost = value;
354 _typeResolutionService = null;
355 if (_designerHost != null) {
356 _typeResolutionService = (ITypeResolutionService)_designerHost.GetService(typeof(ITypeResolutionService));
357 if (_typeResolutionService == null) {
358 throw new ArgumentException(SR.GetString(SR.TypeResService_Needed));
364 // true if we're parsing global.asax
365 internal virtual bool FApplicationFile { get { return false; } }
367 // The global delegate to use for the DataBind event on controls when
368 // the parser is run in design-mode.
369 private EventHandler _designTimeDataBindHandler;
370 internal EventHandler DesignTimeDataBindHandler {
371 get { return _designTimeDataBindHandler; }
372 set { _designTimeDataBindHandler = value; }
375 // Used to detect circular references
376 private StringSet _circularReferenceChecker;
378 // The set of assemblies that the build system is telling us we will be linked with
379 private ICollection _referencedAssemblies;
381 // The set of assemblies that this file is explicitly asking for
382 private AssemblySet _assemblyDependencies;
383 internal AssemblySet AssemblyDependencies {
384 get { return _assemblyDependencies; }
387 // The list of virtual paths to source files we are dependent on
388 private StringSet _sourceDependencies;
389 internal StringSet SourceDependencies {
390 get { return _sourceDependencies; }
393 // The collection of <object> tags with scope=session
394 internal HttpStaticObjectsCollection _sessionObjects;
395 internal HttpStaticObjectsCollection SessionObjects {
396 get { return _sessionObjects; }
399 // The collection of <object> tags with scope=application
400 internal HttpStaticObjectsCollection _applicationObjects;
401 internal HttpStaticObjectsCollection ApplicationObjects {
402 get { return _applicationObjects; }
405 // data that was obtained from parsing the input file
407 private RootBuilder _rootBuilder;
408 internal RootBuilder RootBuilder {
410 EnsureRootBuilderCreated();
415 // Main directive attributes coming from config
416 internal IDictionary _mainDirectiveConfigSettings;
418 // <namespace name, NamespaceEntry>
419 private Hashtable _namespaceEntries;
420 internal Hashtable NamespaceEntries { get { return _namespaceEntries; } }
422 private CompilerType _compilerType;
423 internal CompilerType CompilerType { get { return _compilerType; } }
425 // the server side scripts (list of ScriptBlockData's)
426 private ArrayList _scriptList;
427 internal ArrayList ScriptList { get { return _scriptList; } }
429 // the hash code which determines the set of controls on the page
430 private HashCodeCombiner _typeHashCode = new HashCodeCombiner();
431 internal int TypeHashCode { get { return _typeHashCode.CombinedHash32; } }
433 // The <object> tags local to the page. Entries are ObjectTagBuilder's.
434 private ArrayList _pageObjectList;
435 internal ArrayList PageObjectList { get { return _pageObjectList; } }
437 // Record extra parse data
438 private ParseRecorder _parseRecorders = ParseRecorder.Null;
439 internal ParseRecorder ParseRecorders { get { return _parseRecorders; } }
441 // Data parsed from the directives
443 internal CompilerParameters CompilParams { get { return _compilerType.CompilerParameters; } }
445 internal bool FExplicit { get { return flags[useExplicit]; } }
447 internal bool FLinePragmas { get { return !flags[noLinePragmas]; } }
449 private int _warningLevel=-1;
450 private string _compilerOptions;
452 internal bool FStrict { get { return flags[strict]; } }
454 // File that we must be compiled with, aka code besides (optional)
455 private VirtualPath _codeFileVirtualPath;
456 internal VirtualPath CodeFileVirtualPath { get { return _codeFileVirtualPath; } }
458 // Name that the user wants to give to the generated class
459 private string _generatedClassName;
460 internal string GeneratedClassName { get { return _generatedClassName; } }
462 // Name that the user wants to give to the generated namespace
463 private string _generatedNamespace = null;
464 internal string GeneratedNamespace {
466 // If no namespace was specified, use "ASP"
467 if (_generatedNamespace == null)
468 return BaseCodeDomTreeGenerator.defaultNamespace;
470 return _generatedNamespace;
474 private ControlBuilderInterceptor _controlBuilderInterceptor;
475 internal ControlBuilderInterceptor ControlBuilderInterceptor {
477 if (_controlBuilderInterceptor == null && CompConfig != null && CompConfig.ControlBuilderInterceptorTypeInternal != null) {
478 _controlBuilderInterceptor = (ControlBuilderInterceptor) Activator.CreateInstance(CompConfig.ControlBuilderInterceptorTypeInternal);
480 return _controlBuilderInterceptor;
485 /// Parse the input into a Control. This is used to parse in a control dynamically from some
488 internal static Control ParseControl(string content, VirtualPath virtualPath, bool ignoreFilter) {
489 if (content == null) {
493 ITemplate t = ParseTemplate(content, virtualPath, ignoreFilter);
495 // Create a parent control to hold the controls we parsed
496 Control c = new Control();
502 public static ITemplate ParseTemplate(string content, string virtualPath, bool ignoreFilter) {
503 return ParseTemplate(content, VirtualPath.Create(virtualPath), ignoreFilter);
506 private static ITemplate ParseTemplate(string content, VirtualPath virtualPath, bool ignoreFilter) {
507 TemplateParser parser = new UserControlParser();
508 return parser.ParseTemplateInternal(content, virtualPath, ignoreFilter);
511 private ITemplate ParseTemplateInternal(string content, VirtualPath virtualPath, bool ignoreFilter) {
513 // Use the passed in virtualPath, since we need to have one, and the content string
514 // itself doesn't have one.
515 CurrentVirtualPath = virtualPath;
516 CompilationMode = CompilationMode.Never;
519 // Ignore the PageParserFilter when processing ParserControl/ParseTemplate (VSWhidbey 361509)
520 // Allow the ignore action to be controlled by a parameter (DevDiv 38679)
521 flags[ignoreParserFilter] = ignoreFilter;
522 flags[calledFromParseControlFlag] = true;
526 Debug.Assert(RootBuilder != null);
531 * Do some initialization before the parsing
533 internal virtual void PrepareParse() {
534 if (_circularReferenceChecker == null)
535 _circularReferenceChecker = new CaseInsensitiveStringSet();
537 _baseType = DefaultBaseType;
539 // Initialize the main directive
540 _mainDirectiveConfigSettings = CreateEmptyAttributeBag();
542 // Get the config sections we care about
544 _compConfig = MTConfigUtil.GetCompilationConfig(CurrentVirtualPath);
545 _pagesConfig = MTConfigUtil.GetPagesConfig(CurrentVirtualPath);
548 // Get default settings from config
549 ProcessConfigSettings();
551 // Initialize the type mapper
552 // This must follow processing of config, so it can use the results
553 _typeMapper = new MainTagNameToTypeMapper(this as BaseTemplateParser);
555 // Register the <object> tag
556 _typeMapper.RegisterTag("object", typeof(System.Web.UI.ObjectTag));
558 _sourceDependencies = new CaseInsensitiveStringSet();
560 // Create and seed the stack of ID lists.
561 _idListStack = new Stack();
562 _idList = new CaseInsensitiveStringSet();
564 _scriptList = new ArrayList();
566 // Optionally collect additional parse data for render tracing
567 InitializeParseRecorders();
570 private void InitializeParseRecorders() {
574 _parseRecorders = ParseRecorder.CreateRecorders(this);
577 private void EnsureRootBuilderCreated() {
579 // Create it on demand
580 if (_rootBuilder != null)
583 if (BaseType == DefaultBaseType) {
584 // If the base type is the default, no need to look up the attribute
585 _rootBuilder = CreateDefaultFileLevelBuilder();
588 // Look for a custom attribute
589 Type fileLevelBuilderType = GetFileLevelControlBuilderType();
591 if (fileLevelBuilderType == null) {
592 // No custom type: use the default
593 _rootBuilder = CreateDefaultFileLevelBuilder();
596 // Create the custom file level builder
597 _rootBuilder = (RootBuilder) HttpRuntime.CreateNonPublicInstance(
598 fileLevelBuilderType);
602 _rootBuilder.Line = 1;
603 _rootBuilder.Init(this, null, null, null, null, null);
604 _rootBuilder.SetTypeMapper(TypeMapper);
606 _rootBuilder.VirtualPath = CurrentVirtualPath;
608 // Create and seed the stack of builders.
609 _builderStack = new Stack();
610 _builderStack.Push(new BuilderStackEntry(RootBuilder, null, null, 0, null, 0));
613 internal virtual Type DefaultFileLevelBuilderType {
615 return typeof(RootBuilder);
619 internal virtual RootBuilder CreateDefaultFileLevelBuilder() {
621 // By default, create a RootBuilder
622 return new RootBuilder();
625 private Type GetFileLevelControlBuilderType() {
626 // Check whether the control's class exposes a custom file level builder type
627 FileLevelControlBuilderAttribute cba = null;
628 object[] attrs = BaseType.GetCustomAttributes(
629 typeof(FileLevelControlBuilderAttribute), /*inherit*/ true);
630 if ((attrs != null) && (attrs.Length > 0)) {
631 Debug.Assert(attrs[0] is FileLevelControlBuilderAttribute);
632 cba = (FileLevelControlBuilderAttribute)attrs[0];
638 // Make sure the type has the correct base class
639 Util.CheckAssignableType(DefaultFileLevelBuilderType, cba.BuilderType);
641 return cba.BuilderType;
644 // Get default settings from config
645 internal virtual void ProcessConfigSettings() {
646 if (_compConfig != null) {
647 flags[useExplicit] = _compConfig.Explicit;
648 flags[strict] = _compConfig.Strict;
651 if (PagesConfig != null) {
652 _namespaceEntries = PagesConfig.Namespaces.NamespaceEntries;
654 // Clone it so we don't modify the config settings
655 if (_namespaceEntries != null)
656 _namespaceEntries = (Hashtable) _namespaceEntries.Clone();
658 if (!flags[ignoreParserFilter]) {
659 // Check if a filter is registered, and if so initialize it
660 _pageParserFilter = PageParserFilter.Create(PagesConfig, CurrentVirtualPath, this);
665 internal void Parse(ICollection referencedAssemblies, VirtualPath virtualPath) {
667 _referencedAssemblies = referencedAssemblies;
668 CurrentVirtualPath = virtualPath;
676 internal void Parse() {
678 // Always set the culture to Invariant when parsing (ASURT 99071)
679 Thread currentThread = Thread.CurrentThread;
680 CultureInfo prevCulture = currentThread.CurrentCulture;
681 System.Web.Util.Debug.Trace("Culture", "Before parsing, culture is " + prevCulture.DisplayName);
682 currentThread.CurrentCulture = CultureInfo.InvariantCulture;
686 // Do some initialization before the parsing
692 // Restore the previous culture
693 System.Web.Util.Debug.Trace("Culture", "After parsing, culture is " + currentThread.CurrentCulture.DisplayName);
694 currentThread.CurrentCulture = prevCulture;
695 System.Web.Util.Debug.Trace("Culture", "Restored culture to " + prevCulture.DisplayName);
698 catch { throw; } // Prevent Exception Filter Security Issue (ASURT 122835)
701 internal virtual void ParseInternal() {
702 // Parse either the file or string
704 ParseString(_text, CurrentVirtualPath, Encoding.UTF8);
707 AddSourceDependency(CurrentVirtualPath);
708 ParseFile(null /*physicalPath*/, CurrentVirtualPath.VirtualPathString);
712 internal TemplateParser() {
713 ThrowOnFirstParseError = true;
717 * Parse the contents of the input file
720 protected void ParseFile(string physicalPath, string virtualPath) {
721 ParseFile(physicalPath, VirtualPath.Create(virtualPath));
724 internal void ParseFile(string physicalPath, VirtualPath virtualPath) {
726 // Determine the file used for the circular references checker. Normally,
727 // we use the virtualPath, but we use the physical path if it specified,
728 // as is the case for <!-- #include file="foo.inc" -->
729 string fileToReferenceCheck = physicalPath != null ? physicalPath : virtualPath.VirtualPathString;
731 // Check for circular references of include files
732 if (_circularReferenceChecker.Contains(fileToReferenceCheck)) {
733 ProcessError(SR.GetString(SR.Circular_include));
738 // Add the current file to the circular references checker.
739 _circularReferenceChecker.Add(fileToReferenceCheck);
742 // Open a TextReader either from the physical or virtual path
744 if (physicalPath != null) {
745 using (reader = Util.ReaderFromFile(physicalPath, CurrentVirtualPath)) {
746 ParseReader(reader, virtualPath);
750 // Open a TextReader for the virtualPath we're parsing
751 using (Stream stream = virtualPath.OpenFile()) {
752 reader = Util.ReaderFromStream(stream, CurrentVirtualPath);
753 ParseReader(reader, virtualPath);
758 // Remove the current file from the circular references checker
759 _circularReferenceChecker.Remove(fileToReferenceCheck);
764 * Parse the contents of the TextReader
766 private void ParseReader(StreamReader reader, VirtualPath virtualPath) {
767 string s = reader.ReadToEnd();
769 // Save the text of the input file in case it's trivial
772 ParseString(s, virtualPath, reader.CurrentEncoding);
775 private void AddLiteral(string literal) {
777 if (_literalBuilder == null)
778 _literalBuilder = new StringBuilder();
780 _literalBuilder.Append(literal);
783 private string GetLiteral() {
784 if (_literalBuilder == null)
787 return _literalBuilder.ToString();
791 * Update the hash code of the Type we're creating by xor'ing it with
794 internal void UpdateTypeHashCode(string text) {
795 _typeHashCode.AddObject(text);
799 * Parse the contents of the string, and catch exceptions
801 internal void ParseString(string text, VirtualPath virtualPath, Encoding fileEncoding) {
803 System.Web.Util.Debug.Trace("Template", "Starting parse at " + DateTime.Now);
805 // Save the previous base dirs and line number
806 VirtualPath prevVirtualPath = CurrentVirtualPath;
807 int prevLineNumber = _lineNumber;
809 // Set the new current base dirs and line number
810 CurrentVirtualPath = virtualPath;
813 // Always ignore the spaces at the beginning of a string
814 flags[ignoreNextSpaceString] = true;
817 ParseStringInternal(text, fileEncoding);
819 // If there are parser errors caught in the parser
820 if (HasParserErrors) {
821 ParserError firstError = ParserErrors[0];
823 Exception originalException = firstError.Exception;
825 // Use the first error as the inner exception if not already caught one.
826 if (originalException == null) {
827 originalException = new HttpException(firstError.ErrorText);
830 // Make it a HttpParseException with proper info.
831 HttpParseException ex = new HttpParseException(firstError.ErrorText,
832 originalException, firstError.VirtualPath, Text, firstError.Line);
834 // Add the rest of the errors
835 for (int i = 1; i < ParserErrors.Count; i++) {
836 ex.ParserErrors.Add(ParserErrors[i]);
839 // throw the new exception
843 // Make sure that if any code calls ProcessError/ProcessException after this point,
844 // it throws the error right away, since we won't look at ParserErrors/_firstParseException
846 ThrowOnFirstParseError = true;
848 catch (Exception e) {
849 ErrorFormatter errorFormatter = null;
851 PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_PRE_PROCESSING);
852 PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
854 // Check if the exception has a formatter
855 errorFormatter = HttpException.GetErrorFormatter(e);
857 // If it doesn't, throw a parse exception
858 if (errorFormatter == null) {
860 throw new HttpParseException(e.Message, e,
861 CurrentVirtualPath, text, _lineNumber);
864 // Otherwise, just rethrow it
869 // Restore the previous base dirs and line number
870 CurrentVirtualPath = prevVirtualPath;
871 _lineNumber = prevLineNumber;
874 System.Web.Util.Debug.Trace("Template", "Ending parse at " + DateTime.Now);
878 private Match RunTagRegex(string text, int textPos) {
881 throw new HttpException("Bogus exception just to prevent method inlining");
883 return TagRegex.Match(text, textPos);
886 private Match RunDirectiveRegex(string text, int textPos) {
889 throw new HttpException("Bogus exception just to prevent method inlining");
891 return directiveRegex.Match(text, textPos);
894 private Match RunEndTagRegex(string text, int textPos) {
897 throw new HttpException("Bogus exception just to prevent method inlining");
899 return endtagRegex.Match(text, textPos);
902 private Match RunCodeBlockRegex(string text, int textPos) {
905 throw new HttpException("Bogus exception just to prevent method inlining");
907 return aspCodeRegex.Match(text, textPos);
910 private Match RunExprCodeBlockRegex(string text, int textPos) {
913 throw new HttpException("Bogus exception just to prevent method inlining");
915 return aspExprRegex.Match(text, textPos);
918 private Match RunCommentRegex(string text, int textPos) {
921 throw new HttpException("Bogus exception just to prevent method inlining");
923 return commentRegex.Match(text, textPos);
926 private Match RunIncludeRegex(string text, int textPos) {
929 throw new HttpException("Bogus exception just to prevent method inlining");
931 return includeRegex.Match(text, textPos);
934 private Match RunTextRegex(string text, int textPos) {
937 throw new HttpException("Bogus exception just to prevent method inlining");
939 return textRegex.Match(text, textPos);
941 #endif // PROFILE_REGEX
944 * Parse the contents of the string
946 private void ParseStringInternal(string text, Encoding fileEncoding) {
949 // Find the last '>' in the input string
950 int lastGTIndex = text.LastIndexOf('>');
952 Regex tagRegex = TagRegex;
957 // 1: scan for text up to the next tag.
960 if ((match = RunTextRegex(text, textPos)).Success)
962 if ((match = textRegex.Match(text, textPos)).Success)
965 // Append the text to the literal builder
966 AddLiteral(match.ToString());
968 _lineNumber += Util.LineCount(text, textPos,
969 match.Index + match.Length);
970 textPos = match.Index + match.Length;
973 // we might be done now
975 if (textPos == text.Length)
978 // 2: handle constructs that start with <
980 // This later gets set to true if we match a regex, but do not
982 bool fMatchedButNotProcessed = false;
984 // Check to see if it's a directive (i.e. <%@ %> block)
986 if (!flags[inScriptTag] &&
988 (match = RunDirectiveRegex(text, textPos)).Success)
990 (match = directiveRegex.Match(text, textPos)).Success)
995 // Get all the directives into a bag
996 ParsedAttributeCollection directive;
997 string duplicateAttribute;
998 string directiveName = ProcessAttributes(text, match, out directive, true, out duplicateAttribute);
1001 // If there is a parser filter, give it a chance to look at the directive
1002 PreprocessDirective(directiveName, directive);
1004 ProcessDirective(directiveName, directive);
1006 catch(Exception e) {
1007 ProcessException(e);
1010 // If we just found the main directive, and it uses a codeFile, check if we need to create
1011 // a modified version of the file (used for updatable deployment precompilation)
1012 if (directiveName.Length == 0 && _codeFileVirtualPath != null) {
1013 CreateModifiedMainDirectiveFileIfNeeded(text, match, directive, fileEncoding);
1016 // Always ignore the spaces after a directive
1017 flags[ignoreNextSpaceString] = true;
1020 // Check to see if it's a server side include
1021 // e.g. <!-- #include file="foo.inc" -->
1024 else if ((match = RunIncludeRegex(text, textPos)).Success)
1026 else if ((match = includeRegex.Match(text, textPos)).Success)
1030 ProcessServerInclude(match);
1032 catch(Exception ex) {
1033 ProcessException(ex);
1037 // Check to see if it's a comment <%-- --%> block
1040 else if ((match = RunCommentRegex(text, textPos)).Success)
1042 else if ((match = commentRegex.Match(text, textPos)).Success)
1048 // Check to see if it's an expression code block (i.e. <%= ... %> block)
1050 else if (!flags[inScriptTag] &&
1052 (match = RunExprCodeBlockRegex(text, textPos)).Success)
1054 (match = aspExprRegex.Match(text, textPos)).Success)
1057 ProcessCodeBlock(match, CodeBlockType.Expression, text);
1060 // Check to see if it's an encoded expression code block (i.e. <%: ... %> block)
1062 else if (!flags[inScriptTag] && (match = aspEncodedExprRegex.Match(text, textPos)).Success) {
1063 ProcessCodeBlock(match, CodeBlockType.EncodedExpression, text);
1066 // Check to see if it's a databinding expression block (i.e. <%# ... %> block)
1067 // This does not include <%# %> blocks used as values for
1068 // attributes of server tags.
1070 else if (!flags[inScriptTag] &&
1071 (match = databindExprRegex.Match(text, textPos)).Success) {
1072 ProcessCodeBlock(match, CodeBlockType.DataBinding, text);
1075 // Check to see if it's a code block (<% ... %>)
1077 else if (!flags[inScriptTag] &&
1079 (match = RunCodeBlockRegex(text, textPos)).Success)
1081 (match = aspCodeRegex.Match(text, textPos)).Success)
1084 string code = match.Groups["code"].Value.Trim();
1085 if (code.StartsWith("$", StringComparison.Ordinal)) {
1086 ProcessError(SR.GetString(SR.ExpressionBuilder_LiteralExpressionsNotAllowed, match.ToString(), code));
1089 ProcessCodeBlock(match, CodeBlockType.Code, text);
1093 // Check to see if it's a tag. Don't run the tag regex if there is no '>' after the
1094 // current position, since we know it cannot match, and it can take an exponential
1095 // amount of time to run (VSWhidbey 141878,358072)
1097 else if (!flags[inScriptTag] &&
1099 (match = RunTagRegex(text, textPos)).Success)
1101 lastGTIndex > textPos && (match = tagRegex.Match(text, textPos)).Success)
1105 if (!ProcessBeginTag(match, text))
1106 fMatchedButNotProcessed = true;
1108 catch (Exception ex) {
1109 ProcessException(ex);
1113 // Check to see if it's an end tag
1116 else if ((match = RunEndTagRegex(text, textPos)).Success)
1118 else if ((match = endtagRegex.Match(text, textPos)).Success)
1121 if (!ProcessEndTag(match))
1122 fMatchedButNotProcessed = true;
1125 // Did we process the block that started with a '<'?
1126 if (match == null || !match.Success || fMatchedButNotProcessed) {
1127 // If we could not match the '<' at all, check for some
1128 // specific syntax errors
1129 if (!fMatchedButNotProcessed && !flags[inScriptTag])
1130 DetectSpecialServerTagError(text, textPos);
1137 _lineNumber += Util.LineCount(text, textPos,
1138 match.Index + match.Length);
1139 textPos = match.Index + match.Length;
1142 // we might be done now
1143 if (textPos == text.Length)
1147 if (flags[inScriptTag] && !IgnoreParseErrors) {
1148 // Change the line number to where the script tag started to get
1149 // the correct error message (ASURT 13698).
1150 _lineNumber = _scriptStartLineNumber;
1152 ProcessError(SR.GetString(SR.Unexpected_eof_looking_for_tag, "script"));
1156 // Process the final literal (if any)
1160 // Used for updatable deployment precompilation
1161 void CreateModifiedMainDirectiveFileIfNeeded(string text, Match match,
1162 ParsedAttributeCollection mainDirective, Encoding fileEncoding) {
1164 TextWriter precompTargetWriter = BuildManager.GetUpdatableDeploymentTargetWriter(CurrentVirtualPath, fileEncoding);
1166 // If we're not precompiling for deployment, there is nothing to do here
1167 if (precompTargetWriter == null)
1170 using (precompTargetWriter) {
1172 // Write out everything up to the main directive
1173 precompTargetWriter.Write(text.Substring(0, match.Index));
1175 precompTargetWriter.Write("<%@ " + DefaultDirectiveName);
1177 // Go through all the attributes on the main directive
1178 foreach (DictionaryEntry entry in mainDirective) {
1180 string attribName = (string) entry.Key;
1181 string attribValue = (string) entry.Value;
1183 // Remove the codefile and CodeFileBaseClass attributes
1184 if (StringUtil.EqualsIgnoreCase(attribName, "codefile")) continue;
1185 if (StringUtil.EqualsIgnoreCase(attribName, CodeFileBaseClassAttributeName)) continue;
1187 // Write out a special token for the inherits attribute. It will later be replaced by
1188 // the full type later in the precompilation. We can't do it here because we don't know
1189 // the assembly name yet (VSWhidbey 467936)
1190 if (StringUtil.EqualsIgnoreCase(attribName, "inherits")) {
1191 attribValue = BuildManager.UpdatableInheritReplacementToken;
1194 precompTargetWriter.Write(" ");
1195 precompTargetWriter.Write(attribName);
1196 precompTargetWriter.Write("=\"");
1197 precompTargetWriter.Write(attribValue);
1198 precompTargetWriter.Write("\"");
1201 precompTargetWriter.Write(" %>");
1203 // Write out everything after the main directive
1204 precompTargetWriter.Write(text.Substring(match.Index+match.Length));
1209 * Do what needs to be done before returning after the parsing is complete
1211 internal virtual void HandlePostParse() {
1213 // If there was no main directive in the page, process settings that may have come from config
1214 if (!flags[mainDirectiveHandled]) {
1215 ProcessMainDirective(_mainDirectiveConfigSettings);
1216 flags[mainDirectiveHandled] = true;
1219 // We need to check the PageParserFilter here to handle the case where the base was specified
1220 // in web.config, and was *not* overridden with an 'inherits' attribute.
1221 if (_pageParserFilter != null) {
1222 if (!_pageParserFilter.AllowBaseType(BaseType)) {
1223 throw new HttpException(
1224 SR.GetString(SR.Base_type_not_allowed, BaseType.FullName));
1228 // If there is more than one builder on the stack, some tag was
1229 // not correctly closed, which is an error.
1230 if (BuilderStack.Count > 1) {
1231 BuilderStackEntry entry = (BuilderStackEntry) _builderStack.Peek();
1233 string message = SR.GetString(SR.Unexpected_eof_looking_for_tag, entry._tagName);
1234 ProcessException(new HttpParseException(message, null, entry.VirtualPath, entry._inputText, entry.Line));
1239 // If no language was specified in the page
1240 if (_compilerType == null) {
1243 // Get a default from config
1244 _compilerType = CompilationUtil.GetDefaultLanguageCompilerInfo(
1245 _compConfig, CurrentVirtualPath);
1248 // Get default from code
1249 _compilerType = CompilationUtil.GetCodeDefaultLanguageCompilerInfo();
1253 CompilerParameters compilParams = _compilerType.CompilerParameters;
1255 // Override certain settings if they were specified on the page
1256 if (flags[hasDebugAttribute])
1257 compilParams.IncludeDebugInformation = flags[debug];
1259 // Debugging requires medium trust level
1260 if (compilParams.IncludeDebugInformation)
1261 HttpRuntime.CheckAspNetHostingPermission(AspNetHostingPermissionLevel.Medium, SR.Debugging_not_supported_in_low_trust);
1263 // If warningLevel was specified in the page, use it
1264 if (_warningLevel >= 0) {
1265 compilParams.WarningLevel = _warningLevel;
1266 compilParams.TreatWarningsAsErrors = (_warningLevel>0);
1268 if (_compilerOptions != null)
1269 compilParams.CompilerOptions = _compilerOptions;
1271 // Tell the filter (if any) that the parsing is complete
1272 if (_pageParserFilter != null)
1273 _pageParserFilter.ParseComplete(RootBuilder);
1275 // Tell the ParseRecorders that parsing is complete
1276 ParseRecorders.ParseComplete(RootBuilder);
1280 * Process all the text in the literal StringBuilder, and reset it
1282 private void ProcessLiteral() {
1283 // Debug.Trace("Template", "Literal text: \"" + _literalBuilder.ToString() + "\"");
1285 // Get the current literal string
1286 string literal = GetLiteral();
1288 // Nothing to do if it's empty
1289 if (String.IsNullOrEmpty(literal)) {
1290 flags[ignoreNextSpaceString] = false;
1294 // In global.asax, we don't allow random rendering content
1295 if (FApplicationFile) {
1296 // Make sure the literal is just white spaces
1297 int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(literal);
1299 // Only move the line number if not ignore parser error, otherwise the linenumber
1300 // of other valid elements will be off.
1301 if (iFirstNonWhiteSpace >= 0 && !IgnoreParseErrors) {
1302 // Move the line number back to the first non-whitespace
1303 _lineNumber -= Util.LineCount(literal, iFirstNonWhiteSpace, literal.Length);
1305 ProcessError(SR.GetString(SR.Invalid_app_file_content));
1310 // Check if we should ignore the string (ASURT 8186)
1311 bool fIgnoreThisLiteral = false;
1312 if (flags[ignoreNextSpaceString]) {
1313 flags[ignoreNextSpaceString] = false;
1315 if (Util.IsWhiteSpaceString(literal))
1316 fIgnoreThisLiteral = true;
1319 if (!fIgnoreThisLiteral) {
1321 // Process the settings that may come from config when the first non-trivial literal is parsed. VS Whidbey 141882
1322 if (!flags[mainDirectiveHandled]) {
1323 ProcessMainDirective(_mainDirectiveConfigSettings);
1324 flags[mainDirectiveHandled] = true;
1327 // Add it to the top builder
1328 ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
1330 builder.AppendLiteralString(literal);
1332 catch (Exception e) {
1333 if (!IgnoreParseErrors) {
1334 // If there was an error during the parsing of the literal, move
1335 // the line number back to the beginning of the literal
1336 int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(literal);
1337 if (iFirstNonWhiteSpace < 0) iFirstNonWhiteSpace = 0;
1338 _lineNumber -= Util.LineCount(literal, iFirstNonWhiteSpace, literal.Length);
1340 ProcessException(e);
1344 // Update the hash code with a fixed string, to mark that there is
1345 // a literal, but allow it to change without affecting the hash.
1346 UpdateTypeHashCode("string");
1350 // Reset the StringBuilder for the next literal
1351 _literalBuilder = null;
1355 * Process a server side SCRIPT tag
1357 private void ProcessServerScript() {
1358 // Get the contents of the script tag
1359 string script = GetLiteral();
1361 // Nothing to do if it's empty.
1362 // Unless we're in GenerateCodeCompileUnit mode, in which case
1363 // we want the empty block (VSWhidbey 112777)
1364 if (String.IsNullOrEmpty(script)) {
1365 if (!IgnoreParseErrors)
1368 script = String.Empty;
1371 // Add this script to the script builder, unless we're
1372 // supposed to ignore it
1373 if (!flags[ignoreScriptTag]) {
1375 // First, give the PageParserFilter a chance to handle the code block
1376 if (!PageParserFilterProcessedCodeBlock(CodeConstructType.ScriptTag, script, _currentScript.Line)) {
1377 // Make sure it's legal to have code in this page
1378 EnsureCodeAllowed();
1380 _currentScript.Script = script;
1381 _scriptList.Add(_currentScript);
1382 _currentScript = null;
1385 // Reset the StringBuilder for the next literal
1386 _literalBuilder = null;
1389 internal virtual void CheckObjectTagScope(ref ObjectTagScope scope) {
1391 // Map the default scope to Page
1392 if (scope == ObjectTagScope.Default)
1393 scope = ObjectTagScope.Page;
1395 // Check for invalid scopes
1396 if (scope != ObjectTagScope.Page) {
1397 throw new HttpException(
1398 SR.GetString(SR.App_session_only_valid_in_global_asax));
1403 * Process an Object tag, depending on its scope
1405 private void ProcessObjectTag(ObjectTagBuilder objectBuilder) {
1407 ObjectTagScope scope = objectBuilder.Scope;
1408 CheckObjectTagScope(ref scope);
1410 // Page and AppInstance are treated identically
1411 if (scope == ObjectTagScope.Page ||
1412 scope == ObjectTagScope.AppInstance) {
1413 if (_pageObjectList == null)
1414 _pageObjectList = new ArrayList();
1416 _pageObjectList.Add(objectBuilder);
1418 else if (scope == ObjectTagScope.Session) {
1419 if (_sessionObjects == null)
1420 _sessionObjects = new HttpStaticObjectsCollection();
1422 _sessionObjects.Add(objectBuilder.ID,
1423 objectBuilder.ObjectType,
1424 objectBuilder.LateBound);
1426 else if (scope == ObjectTagScope.Application) {
1427 if (_applicationObjects == null)
1428 _applicationObjects = new HttpStaticObjectsCollection();
1430 _applicationObjects.Add(objectBuilder.ID,
1431 objectBuilder.ObjectType,
1432 objectBuilder.LateBound);
1435 Debug.Assert(false, "Unexpected scope!");
1440 * Add a child builder to a builder
1442 private void AppendSubBuilder(ControlBuilder builder, ControlBuilder subBuilder) {
1443 // Check if it's an object tag
1444 if (subBuilder is ObjectTagBuilder) {
1445 ProcessObjectTag((ObjectTagBuilder) subBuilder);
1449 builder.AppendSubBuilder(subBuilder);
1453 * Process an opening tag (possibly self-closed)
1455 // Used to generate unique id's
1456 private int _controlCount;
1457 private bool ProcessBeginTag(Match match, string inputText) {
1458 string tagName = match.Groups["tagname"].Value;
1460 // Get all the attributes into a bag
1461 ParsedAttributeCollection attribs;
1462 string duplicateAttribute;
1463 ProcessAttributes(inputText, match, out attribs, false /*fDirective*/, out duplicateAttribute);
1465 // Check if the tag is self closed
1466 bool fSelfClosed = match.Groups["empty"].Success;
1468 // Is it a server side script tag?
1469 if (StringUtil.EqualsIgnoreCase(tagName, "script") && flags[isServerTag]) {
1470 ProcessScriptTag(match, inputText, attribs, fSelfClosed);
1474 // Process the settings that may come from config when the first non-trivial tag is parsed. VS Whidbey 141882
1475 if (!flags[mainDirectiveHandled]) {
1476 ProcessMainDirective(_mainDirectiveConfigSettings);
1477 flags[mainDirectiveHandled] = true;
1480 ControlBuilder parentBuilder = null;
1481 ControlBuilder subBuilder = null;
1482 Type childType = null;
1484 // This could be a property of an object that is filterable
1486 string filter = Util.ParsePropertyDeviceFilter(tagName, out realTagName);
1488 // Check if the parent builder wants to create a subcontrol for this tag.
1489 if (BuilderStack.Count > 1) {
1491 parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
1493 // If the parent builder is a StringPropertyBuilder, we want to treat everything
1494 // in it as a literal, so we always return false here (VSWhidbey 285429)
1495 if (parentBuilder is StringPropertyBuilder)
1498 subBuilder = parentBuilder.CreateChildBuilder(filter, realTagName, attribs,
1499 this, parentBuilder, _id, _lineNumber, CurrentVirtualPath, ref childType, false);
1502 // If not, use the root builder if runat=server is there.
1503 if (subBuilder == null && flags[isServerTag]) {
1504 subBuilder = RootBuilder.CreateChildBuilder(filter, realTagName, attribs,
1505 this, parentBuilder, _id, _lineNumber, CurrentVirtualPath, ref childType, false);
1508 // In case we find that the top stack item has the same name as the
1509 // current tag, we increase a count on the stack item. This way, we
1510 // know that we need to ignore the corresponding closing tag (ASURT 50795)
1511 if (subBuilder == null && _builderStack.Count > 1 && !fSelfClosed) {
1512 BuilderStackEntry stackEntry = (BuilderStackEntry) _builderStack.Peek();
1513 if (StringUtil.EqualsIgnoreCase(tagName, stackEntry._tagName))
1514 stackEntry._repeatCount++;
1517 // We could not get the type of a server control from that tag
1518 if (subBuilder == null) {
1519 // If it wasn't marked as runat=server, ignore
1520 if (!flags[isServerTag] || IgnoreParseErrors)
1523 // If it was marked as runat=server, fail
1524 ProcessError(SR.GetString(SR.Unknown_server_tag, tagName));
1528 // We have a server control
1530 // If we have a control type filter, make sure the child control is allowed
1531 if (_pageParserFilter != null) {
1532 Debug.Assert(childType != null);
1534 if (!_pageParserFilter.AllowControlInternal(childType, subBuilder)) {
1535 ProcessError(SR.GetString(SR.Control_type_not_allowed, childType.FullName));
1540 // Make sure it doesn't have duplicated attributes
1541 if (duplicateAttribute != null) {
1542 ProcessError(SR.GetString(SR.Duplicate_attr_in_tag, duplicateAttribute));
1545 // Get the id from the builder. Note that it may be null even if _id was not initially null,
1546 // if the builder is not for a Control (VSWhidbey 406302)
1547 _id = subBuilder.ID;
1549 // If it has an id, enforce validity and uniqueness
1551 if (!System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(_id)) {
1552 ProcessError(SR.GetString(SR.Invalid_identifier, _id));
1556 if (_idList.Contains(_id)) {
1557 ProcessError(SR.GetString(SR.Id_already_used, _id));
1563 else if (flags[isServerTag]) {
1564 // Make sure that cached controls always have a fixed id to prevent
1565 // unpredictable behavior (ASURT 83402)
1566 PartialCachingAttribute cacheAttrib = (PartialCachingAttribute)
1567 TypeDescriptor.GetAttributes(childType)[typeof(PartialCachingAttribute)];
1569 // If we are parsing a theme file, the controls do not have an ID,
1570 // and we should not be adding one. (Dev10
1571 if (!(subBuilder.Parser is PageThemeParser) && cacheAttrib != null) {
1572 _id = "_ctrl_" + _controlCount.ToString(NumberFormatInfo.InvariantInfo);
1573 subBuilder.ID = _id;
1575 // Controls can't be filtered, so use the default filter
1576 subBuilder.PreprocessAttribute(String.Empty, "id", _id, false /*mainDirectiveMode*/);
1581 // Take care of the previous literal string
1584 if (childType != null) {
1585 // Update the hash code with the name of the control's type
1586 UpdateTypeHashCode(childType.FullName);
1589 // If the server control has a body, and if it didn't self-close
1590 // (i.e. wasn't terminated with "/>"), put it on the stack of controls.
1591 if (!fSelfClosed && subBuilder.HasBody()) {
1593 // If it's a template, push a new ID list (ASURT 72773)
1594 if (subBuilder is TemplateBuilder && ((TemplateBuilder)subBuilder).AllowMultipleInstances) {
1595 _idListStack.Push(_idList);
1596 _idList = new CaseInsensitiveStringSet();
1599 _builderStack.Push(new BuilderStackEntry(subBuilder, tagName,
1600 CurrentVirtualPathString, _lineNumber,
1601 inputText, match.Index + match.Length));
1603 // Optionally record begin tag position data
1604 ParseRecorders.RecordBeginTag(subBuilder, match);
1607 // Append the sub builder to the current builder
1608 parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
1609 AppendSubBuilder(parentBuilder, subBuilder);
1611 // Tell the builder that we're done parsing its control
1612 subBuilder.CloseControl();
1614 // Optionally record empty tag position data
1615 ParseRecorders.RecordEmptyTag(subBuilder, match);
1622 * Process a <script runat=server> tag
1624 private void ProcessScriptTag(Match match, string text, IDictionary attribs, bool fSelfClosed) {
1627 // Always ignore the spaces after a script tag
1628 flags[ignoreNextSpaceString] = true;
1630 // Check if there is a 'src' attribute
1631 VirtualPath virtualPath = Util.GetAndRemoveVirtualPathAttribute(attribs, "src");
1632 if (virtualPath != null) {
1634 // Make sure it's legal to have code in this page
1635 EnsureCodeAllowed();
1637 // Get a full path to the script file
1638 virtualPath = ResolveVirtualPath(virtualPath);
1640 // Make sure access to the file is allowed (VSWhidbey 195545)
1641 HttpRuntime.CheckVirtualFilePermission(virtualPath.VirtualPathString);
1643 AddSourceDependency(virtualPath);
1645 ProcessLanguageAttribute((string)attribs["language"]);
1646 _currentScript = new ScriptBlockData(1, 1, virtualPath.VirtualPathString);
1648 _currentScript.Script = Util.StringFromVirtualPath(virtualPath);
1650 // Add this script to the script builder
1651 _scriptList.Add(_currentScript);
1652 _currentScript = null;
1654 // If the script tag is not self closed (even though it has a
1655 // src attribute), continue processing it, but eventually
1656 // ignore the content (ASURT 8883)
1658 flags[inScriptTag] = true;
1659 _scriptStartLineNumber = _lineNumber;
1660 flags[ignoreScriptTag] = true;
1666 ProcessLanguageAttribute((string)attribs["language"]);
1669 // Look for the last newline before the script block code string
1670 int startOfCode = match.Index + match.Length;
1671 int newlineIndex = text.LastIndexOfAny(s_newlineChars, startOfCode-1);
1673 // Use it to calculate the column where the code starts,
1674 // which improves the debugging experience (VSWhidbey 87172)
1675 int column = startOfCode-newlineIndex;
1676 Debug.Assert(column > 0);
1678 _currentScript = new ScriptBlockData(_lineNumber, column, CurrentVirtualPathString);
1680 // No 'src' attribute. Make sure tag is not self closed.
1682 ProcessError(SR.GetString(SR.Script_tag_without_src_must_have_content));
1685 flags[inScriptTag] = true;
1686 _scriptStartLineNumber = _lineNumber;
1690 * Called when a '</' sequence is seen. This means we can start closing
1693 private bool ProcessEndTag(Match match) {
1694 string tagName = match.Groups["tagname"].Value;
1696 // If we are in the middle of a server side SCRIPT tag
1697 if (flags[inScriptTag]) {
1698 // Ignore anything that's not a </script>
1699 if (!StringUtil.EqualsIgnoreCase(tagName, "script"))
1702 ProcessServerScript();
1704 flags[inScriptTag] = false;
1705 flags[ignoreScriptTag] = false;
1710 // See if anyone on the stack cares about termination.
1711 return MaybeTerminateControl(tagName, match);
1714 internal bool IsExpressionBuilderValue(string val) {
1715 return ControlBuilder.expressionBuilderRegex.Match(val, 0).Success;
1719 internal abstract string DefaultDirectiveName { get; }
1721 // If there is a parser filter, give it a chance to look at the directive
1722 internal void PreprocessDirective(string directiveName, IDictionary directive) {
1724 // No parser filter: done
1725 if (_pageParserFilter == null)
1728 if (directiveName.Length == 0)
1729 directiveName = DefaultDirectiveName;
1731 _pageParserFilter.PreprocessDirective(directiveName, directive);
1735 * Process a <%@ %> block
1737 internal virtual void ProcessDirective(string directiveName, IDictionary directive) {
1739 // Check for the main directive, which is "page" for an aspx,
1740 // and "application" for global.asax
1741 if (directiveName.Length == 0) {
1747 // Make sure the main directive was not already specified
1748 if (flags[mainDirectiveSpecified]) {
1749 ProcessError(SR.GetString(SR.Only_one_directive_allowed, DefaultDirectiveName));
1754 // If there are some config settings that were not overriden, add them to the list
1755 if (_mainDirectiveConfigSettings != null) {
1756 // Go through all the config attributes
1757 foreach (DictionaryEntry entry in _mainDirectiveConfigSettings) {
1759 // If it was overridden, ignore the config setting
1760 if (directive.Contains(entry.Key))
1763 // Add it to the list
1764 directive[entry.Key] = entry.Value;
1768 ProcessMainDirective(directive);
1770 flags[mainDirectiveSpecified] = true;
1771 flags[mainDirectiveHandled] = true;
1773 else if (StringUtil.EqualsIgnoreCase(directiveName, "assembly")) {
1774 // Assembly directive
1776 // Even though this only makes sense for compiled pages, Sharepoint needs us to
1777 // ignore instead of throw when the page in non-compiled.
1779 // Remove the attributes as we get them from the dictionary
1780 string assemblyName = Util.GetAndRemoveNonEmptyAttribute(directive, "name");
1781 VirtualPath src = Util.GetAndRemoveVirtualPathAttribute(directive, "src");
1783 // If there are some attributes left, fail
1784 Util.CheckUnknownDirectiveAttributes(directiveName, directive);
1786 if (assemblyName != null && src != null) {
1787 ProcessError(SR.GetString(SR.Attributes_mutually_exclusive, "Name", "Src"));
1790 if (assemblyName != null) {
1791 AddAssemblyDependency(assemblyName);
1793 // Is it a source file that needs to be compiled on the fly
1794 else if (src != null) {
1795 ImportSourceFile(src);
1798 ProcessError(SR.GetString(SR.Missing_attr, "name"));
1801 else if (StringUtil.EqualsIgnoreCase(directiveName, "import")) {
1805 ProcessImportDirective(directiveName, directive);
1807 else if (StringUtil.EqualsIgnoreCase(directiveName, "implements")) {
1808 // 'implements' directive
1810 // We must compile the page if it asks to implement an interface
1811 OnFoundDirectiveRequiringCompilation(directiveName);
1813 // Remove the attributes as we get them from the dictionary
1814 string interfaceName = Util.GetAndRemoveRequiredAttribute(directive, "interface");
1816 // If there are some attributes left, fail
1817 Util.CheckUnknownDirectiveAttributes(directiveName, directive);
1819 Type interfaceType = GetType(interfaceName);
1821 // Make sure that it's an interface
1822 if (!interfaceType.IsInterface) {
1823 ProcessError(SR.GetString(SR.Invalid_type_to_implement, interfaceName));
1828 // Add the interface type to the list
1829 if (_implementedInterfaces == null) {
1830 _implementedInterfaces = new ArrayList();
1832 _implementedInterfaces.Add(interfaceType);
1834 else if (!FInDesigner) {
1835 ProcessError(SR.GetString(SR.Unknown_directive, directiveName));
1839 internal virtual void ProcessMainDirective(IDictionary mainDirective) {
1841 // Used to store some temporary data resulting from the parsing of the directive
1842 IDictionary parseData = new HybridDictionary();
1844 // Keep track of unknown attributes
1845 ParsedAttributeCollection unknownAttributes = null;
1847 // Go through all the attributes on the directive
1848 foreach (DictionaryEntry entry in mainDirective) {
1850 string attribName = (string)entry.Key;
1852 // Parse out the device name, if any
1853 string deviceName = Util.ParsePropertyDeviceFilter(attribName, out attribName);
1856 // Try to process the attribute, and if not, keep track of it in the 'unknown' list
1857 if (!ProcessMainDirectiveAttribute(deviceName, attribName, (string)entry.Value, parseData)) {
1858 if (unknownAttributes == null) {
1859 unknownAttributes = CreateEmptyAttributeBag();
1862 unknownAttributes.AddFilteredAttribute(deviceName, attribName, (string)entry.Value);
1865 catch (Exception e) {
1866 ProcessException(e);
1870 // Allow some postprocessing to happen, in case attributes have dependencies
1871 // on each other (e.g. mutually exclusive attributes).
1872 PostProcessMainDirectiveAttributes(parseData);
1874 // We should always set the control type of the root builder regardless of unknown attributes
1875 RootBuilder.SetControlType(BaseType);
1877 // If we didn't have any unknown attributes, we're done
1878 if (unknownAttributes == null)
1881 RootBuilder.ProcessImplicitResources(unknownAttributes);
1883 // Process all the unknown attributes
1884 foreach (FilteredAttributeDictionary filteredAttributes in unknownAttributes.GetFilteredAttributeDictionaries()) {
1885 string filter = filteredAttributes.Filter;
1887 foreach (DictionaryEntry attribute in filteredAttributes) {
1888 string attribName = (string)attribute.Key;
1889 ProcessUnknownMainDirectiveAttribute(filter, attribName, (string) attribute.Value);
1894 internal virtual bool ProcessMainDirectiveAttribute(string deviceName, string name,
1895 string value, IDictionary parseData) {
1898 // Ignore description and codebehind attributes
1904 flags[debug] = Util.GetBooleanAttribute(name, value);
1905 if (flags[debug] && !HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
1906 throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "debug"));
1909 flags[hasDebugAttribute] = true;
1913 flags[noLinePragmas] = !Util.GetBooleanAttribute(name, value);
1916 case "warninglevel":
1917 _warningLevel = Util.GetNonNegativeIntegerAttribute(name, value);
1920 case "compileroptions":
1921 // This only makes sense for compiled pages
1922 OnFoundAttributeRequiringCompilation(name);
1924 string compilerOptions = value.Trim();
1926 CompilationUtil.CheckCompilerOptionsAllowed(compilerOptions, false /*config*/, null, 0);
1928 _compilerOptions = compilerOptions;
1931 // These two really only make sense in VB
1933 flags[useExplicit] = Util.GetBooleanAttribute(name, value);
1936 flags[strict] = Util.GetBooleanAttribute(name, value);
1940 ValidateBuiltInAttribute(deviceName, name, value);
1941 string language = Util.GetNonEmptyAttribute(name, value);
1942 ProcessLanguageAttribute(language);
1945 // A "src" attribute is equivalent to an imported source file
1947 // This only makes sense for compiled pages
1948 OnFoundAttributeRequiringCompilation(name);
1950 // Remember the src assembly for post processing
1951 parseData[name] = Util.GetNonEmptyAttribute(name, value);
1955 // Remember the base class for post processing
1956 parseData[name] = Util.GetNonEmptyAttribute(name, value);
1961 // Even though this only makes sense for compiled pages, Sharepoint needs us to
1962 // ignore instead of throw when the page in non-compiled.
1964 _generatedClassName = Util.GetNonEmptyFullClassNameAttribute(name, value,
1965 ref _generatedNamespace);
1969 // This only makes sense for compiled pages
1970 OnFoundAttributeRequiringCompilation(name);
1973 ProcessCodeFile(VirtualPath.Create(Util.GetNonEmptyAttribute(name, value)));
1975 catch (Exception ex) {
1976 ProcessException(ex);
1981 // We didn't handle the attribute
1985 // The attribute was handled
1987 // Make sure no device filter or resource expression was specified
1988 ValidateBuiltInAttribute(deviceName, name, value);
1993 // Throw an exception if there is a device filter or resource expression
1994 internal void ValidateBuiltInAttribute(string deviceName, string name, string value) {
1995 Debug.Assert(deviceName != null);
1997 if (IsExpressionBuilderValue(value)) {
1998 ProcessError(SR.GetString(SR.Illegal_Resource_Builder, name));
2001 if (deviceName.Length > 0) {
2002 ProcessError(SR.GetString(SR.Illegal_Device, name));
2006 internal virtual void ProcessUnknownMainDirectiveAttribute(string filter, string attribName, string value) {
2007 // By default, it is not legal to have unknown attributes. But derived parser
2008 // classes can change this behavior
2009 ProcessError(SR.GetString(SR.Attr_not_supported_in_directive,
2010 attribName, DefaultDirectiveName));
2013 internal virtual void PostProcessMainDirectiveAttributes(IDictionary parseData) {
2015 // Post process the src and inherits attributes
2017 string src = (string) parseData["src"];
2018 Assembly assembly = null;
2021 assembly = ImportSourceFile(VirtualPath.Create(src));
2023 catch (Exception ex) {
2024 ProcessException(ex);
2028 // Was a code file base type specified in the directive
2029 string codeFileBaseTypeName = (string)parseData[CodeFileBaseClassAttributeName];
2031 // If so, there must also be a CodeFile attribute
2032 if (codeFileBaseTypeName != null && _codeFileVirtualPath == null) {
2033 throw new HttpException(SR.GetString(SR.CodeFileBaseClass_Without_Codefile));
2036 // Was a base type specified in the directive
2037 string baseTypeName = (string) parseData["inherits"];
2038 if (baseTypeName != null) {
2040 ProcessInheritsAttribute(baseTypeName, codeFileBaseTypeName, src, assembly);
2042 catch (Exception ex) {
2043 ProcessException(ex);
2047 if (_codeFileVirtualPath != null) {
2048 throw new HttpException(SR.GetString(SR.Codefile_without_inherits));
2053 private void ProcessInheritsAttribute(string baseTypeName, string codeFileBaseTypeName,
2054 string src, Assembly assembly) {
2056 // If a code file is used, then the inherits attribute points to the class in that file,
2057 // which is not yet compiled. In that case, we cannot get the Type, so we just keep
2058 // track of the class name (and namespace)
2059 if (_codeFileVirtualPath != null) {
2060 _baseTypeName = Util.GetNonEmptyFullClassNameAttribute("inherits", baseTypeName,
2061 ref _baseTypeNamespace);
2063 // Now set baseTypeName to codeFileBaseTypeName, so that if it was set, it will
2064 // be used as the BaseType during parsing (DevDiv 43024)
2065 baseTypeName = codeFileBaseTypeName;
2067 if (baseTypeName == null)
2071 Type baseType = null;
2072 if (assembly != null)
2073 baseType = assembly.GetType(baseTypeName, false /* throwOnError */, true /* caseInsensitive */);
2076 baseType = GetType(baseTypeName);
2079 // We couldn't load the inherits type. If the classname attribute has a
2080 // namespace, check if the inherits type exists in there (VSWhidbey 297056)
2082 // If the classname attribute doesn't have a namespace, give up
2083 if (_generatedNamespace == null)
2086 // If the inherits attribute already had a namespace, don't try this
2087 if (baseTypeName.IndexOf('.') >= 0)
2091 // Try loading the inherit using the classname's namespace
2092 string baseTypeNameWithNS = _generatedNamespace + "." + baseTypeName;
2093 baseType = GetType(baseTypeNameWithNS);
2097 // If that failed too, rethrow the original exception
2098 if (baseType == null)
2103 // Make sure we successfully got the Type of the base class
2104 if (baseType == null) {
2105 Debug.Assert(assembly != null, "assembly != null");
2106 ProcessError(SR.GetString(SR.Non_existent_base_type, baseTypeName, src));
2111 // Make sure the base type extends the DefaultBaseType (Page or UserControl)
2112 if (!DefaultBaseType.IsAssignableFrom(baseType)) {
2113 ProcessError(SR.GetString(SR.Invalid_type_to_inherit_from, baseTypeName,
2114 _baseType.FullName));
2119 // If we have a control type filter, make sure the base type is allowed
2120 if (_pageParserFilter != null) {
2122 if (!_pageParserFilter.AllowBaseType(baseType)) {
2123 throw new HttpException(
2124 SR.GetString(SR.Base_type_not_allowed, baseType.FullName));
2128 _baseType = baseType;
2130 // Now that we have the base type, we can create the RootBuilder
2131 Debug.Assert(_rootBuilder == null);
2132 EnsureRootBuilderCreated();
2134 // Make sure we link with the assembly of the base type (ASURT 101778)
2135 AddTypeDependency(_baseType);
2137 // Remember the fact that the page uses codebehind
2138 flags[hasCodeBehind] = true;
2141 private void ProcessImportDirective(string directiveName, IDictionary directive) {
2143 // Even though this only makes sense for compiled pages, Sharepoint needs us to
2144 // ignore instead of throw when the page in non-compiled.
2146 // Remove the attributes as we get them from the dictionary
2147 string ns = Util.GetAndRemoveNonEmptyNoSpaceAttribute(directive, "namespace");
2150 ProcessError(SR.GetString(SR.Missing_attr, "namespace"));
2154 // If there are some attributes left, fail
2155 Util.CheckUnknownDirectiveAttributes(directiveName, directive);
2159 * Process a language attribute, as can appear in the Page directive and in
2160 * <script runat=server> tags.
2162 private void ProcessLanguageAttribute(string language) {
2163 if (language == null)
2166 // We don't have CompilationConfig at design-time and the language attribute isn't used either.
2170 CompilerType compilerType = CompilationUtil.GetCompilerInfoFromLanguage(
2171 CurrentVirtualPath, language);
2173 // Make sure we don't get conflicting languages
2174 if (_compilerType != null &&
2175 _compilerType.CodeDomProviderType != compilerType.CodeDomProviderType) {
2176 ProcessError(SR.GetString(SR.Mixed_lang_not_supported, language));
2181 _compilerType = compilerType;
2185 * Process a compileFile attribute (aka code besides)
2187 private void ProcessCodeFile(VirtualPath codeFileVirtualPath) {
2189 Debug.Assert(_codeFileVirtualPath == null);
2191 _codeFileVirtualPath = ResolveVirtualPath(codeFileVirtualPath);
2193 // Get the language for the code beside page
2194 CompilerType compilerType = CompilationUtil.GetCompilerInfoFromVirtualPath(
2195 _codeFileVirtualPath);
2197 // Make sure we don't get conflicting languages
2198 if (_compilerType != null &&
2199 _compilerType.CodeDomProviderType != compilerType.CodeDomProviderType) {
2200 ProcessError(SR.GetString(SR.Inconsistent_CodeFile_Language));
2205 // Check if it's trying to go cross app, or points to a special directory.
2206 // It's important to do this before checkin existence, to avoid revealing information
2207 // about other apps (VSWhidbey 442957)
2208 BuildManager.ValidateCodeFileVirtualPath(_codeFileVirtualPath);
2210 // Make sure the file exists
2211 Util.CheckVirtualFileExists(_codeFileVirtualPath);
2213 _compilerType = compilerType;
2215 // Add the code file to the list of files we depend on
2216 AddSourceDependency(_codeFileVirtualPath);
2220 * Compile a source file into an assembly, and import it
2222 private Assembly ImportSourceFile(VirtualPath virtualPath) {
2224 // If it's a no-compile page, ignore the imported source file
2225 if (CompilationMode == CompilationMode.Never)
2228 // Get a full path to the source file
2229 virtualPath = ResolveVirtualPath(virtualPath);
2231 // If we have a page parser filter, make sure the reference is allowed
2232 if (_pageParserFilter != null && !_pageParserFilter.AllowVirtualReference(CompConfig, virtualPath)) {
2233 ProcessError(SR.GetString(SR.Reference_not_allowed, virtualPath));
2236 // Add the source file to the list of files we depend on
2237 AddSourceDependency(virtualPath);
2239 // Compile it into an assembly
2241 BuildResultCompiledAssembly result = BuildManager.GetVPathBuildResult(
2242 virtualPath) as BuildResultCompiledAssembly;
2243 if (result == null) {
2244 ProcessError(SR.GetString(SR.Not_a_src_file, virtualPath));
2247 Assembly a = result.ResultAssembly;
2249 // Add a dependency to the assembly and its dependencies
2250 AddAssemblyDependency(a, true /*addDependentAssemblies*/);
2256 * If we could not match the '<' at all, check for some specific syntax
2259 private void DetectSpecialServerTagError(string text, int textPos) {
2261 if (IgnoreParseErrors) return;
2263 // If it started with <%, it's probably not closed (ASURT 13661)
2264 if (text.Length > textPos+1 && text[textPos+1] == '%') {
2265 ProcessError(SR.GetString(SR.Malformed_server_block));
2270 // Search for the end of the tag ('>')
2271 Match match = gtRegex.Match(text, textPos);
2277 // Get the complete potential tag
2278 string tag = text.Substring(textPos, match.Index-textPos+2);
2280 // Check if it's a case of nested <% %> block in a server tag (ASURT 8714)
2282 // If the tag does not contain runat=server, do nothing
2283 match = runatServerRegex.Match(tag);
2287 // If it has runat=server, but there is a '<' before it, don't fail, since
2288 // this '<' is probably the true tag start, and it will be processed later (ASURT 39531)
2289 // But ignore "<%" (ASURT 77554)
2290 Match matchLessThan = ltRegex.Match(tag, 1);
2291 if (matchLessThan.Success && matchLessThan.Index < match.Index)
2294 System.Web.Util.Debug.Trace("Template", "Found malformed server tag: " + tag);
2296 // Remove all <% %> constructs from within it.
2297 string tag2 = serverTagsRegex.Replace(tag, String.Empty);
2299 // If there were some <% %> constructs in the tag
2300 if ((object)tag2 != (object)tag) {
2301 // If it can be parsed as a tag after we removed the <% %> constructs, fail
2302 if (TagRegex.Match(tag2).Success) {
2303 ProcessError(SR.GetString(SR.Server_tags_cant_contain_percent_constructs));
2309 // Give a more generic error (fixed 18969, 30312)
2310 ProcessError(SR.GetString(SR.Malformed_server_tag));
2314 * Add an entry to our list of NamespaceEntry's
2316 internal void AddImportEntry(string ns) {
2318 // We're about to modify the list of namespaces, so if we already
2319 // have one (coming from config), clone it so we don't modify theirs.
2320 if (_namespaceEntries != null)
2321 _namespaceEntries = (Hashtable) _namespaceEntries.Clone();
2323 _namespaceEntries = new Hashtable();
2325 NamespaceEntry namespaceEntry = new NamespaceEntry();
2326 namespaceEntry.Namespace = ns;
2328 namespaceEntry.Line = _lineNumber;
2329 namespaceEntry.VirtualPath = CurrentVirtualPathString;
2331 _namespaceEntries[ns] = namespaceEntry;
2334 internal Assembly LoadAssembly(string assemblyName, bool throwOnFail) {
2336 if (_typeResolutionService != null) {
2337 AssemblyName asmName = new AssemblyName(assemblyName);
2338 return _typeResolutionService.GetAssembly(asmName, throwOnFail);
2341 return _compConfig.LoadAssembly(assemblyName, throwOnFail);
2344 internal Type GetType(string typeName, bool ignoreCase) {
2345 return GetType(typeName, ignoreCase, /* throwOnError */ true);
2349 * Look for a type by name in the assemblies that this page links with
2351 internal Type GetType(string typeName, bool ignoreCase, bool throwOnError) {
2353 // If it contains an assembly name, parse it out and load the assembly (ASURT 53589)
2355 int commaIndex = Util.CommaIndexInTypeName(typeName);
2356 if (commaIndex > 0) {
2357 string assemblyName = typeName.Substring(commaIndex + 1).Trim();
2358 typeName = typeName.Substring(0, commaIndex).Trim();
2361 a = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
2364 throw new HttpException(
2365 SR.GetString(SR.Assembly_not_compiled, assemblyName));
2369 // If we got an assembly, load the type from it
2371 return a.GetType(typeName, throwOnError, ignoreCase);
2373 // Otherwise, look for the type in the referenced assemblies (given by the build system)
2375 t = Util.GetTypeFromAssemblies(_referencedAssemblies, typeName, ignoreCase);
2379 // Or in the assemblies that this page depends on
2380 t = Util.GetTypeFromAssemblies(AssemblyDependencies, typeName, ignoreCase);
2385 throw new HttpException(
2386 SR.GetString(SR.Invalid_type, typeName));
2393 * Look for a type by name in the assemblies that this page links with
2395 internal Type GetType(string typeName) {
2396 return GetType(typeName, false /*ignoreCase*/);
2400 * Process a server side include. e.g. <!-- #include file="foo.inc" -->
2402 private void ProcessServerInclude(Match match) {
2403 if (flags[inScriptTag]) {
2404 throw new HttpException(
2405 SR.GetString(SR.Include_not_allowed_in_server_script_tag));
2410 string pathType = match.Groups["pathtype"].Value;
2411 string filename = match.Groups["filename"].Value;
2412 //System.Web.Util.Debug.Trace("Template", "#Include " + pathType + "=" + filename);
2414 if (filename.Length == 0) {
2415 ProcessError(SR.GetString(SR.Empty_file_name));
2420 VirtualPath newVirtualPath = CurrentVirtualPath;
2421 string newPhysicalPath = null;
2423 if (StringUtil.EqualsIgnoreCase(pathType, "file")) {
2425 if (UrlPath.IsAbsolutePhysicalPath(filename)) {
2426 // If it's an absolute physical path, use it as is
2427 newPhysicalPath = filename;
2431 // If it's relative, try to treat it as virtual
2433 bool treatAsVirtual = true;
2436 newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
2439 // If this fails, it probably means it tried to escape the root of the app.
2440 // In that case, fall back to treating it as physical (VSWhidbey 339705)
2441 treatAsVirtual = false;
2444 if (treatAsVirtual) {
2445 HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
2446 AddSourceDependency(newVirtualPath);
2449 // Treat it as relative to the physical path of the current page
2450 string currentPhysicalDir = Path.GetDirectoryName(
2451 CurrentVirtualPath.MapPath());
2452 newPhysicalPath = Path.GetFullPath(Path.Combine(currentPhysicalDir, filename.Replace('/', '\\')));
2456 else if (StringUtil.EqualsIgnoreCase(pathType, "virtual")) {
2457 newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
2458 HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
2459 AddSourceDependency(newVirtualPath);
2462 ProcessError(SR.GetString(SR.Only_file_virtual_supported_on_server_include));
2467 if (newPhysicalPath != null) {
2468 // Make sure that access to the file is permitted (ASURT 73792,85467)
2469 HttpRuntime.CheckFilePermission(newPhysicalPath);
2472 // If there is a filter, check whether it allows this include file
2473 if (_pageParserFilter != null && !_pageParserFilter.AllowServerSideInclude(newVirtualPath.VirtualPathString)) {
2474 ProcessError(SR.GetString(SR.Include_not_allowed, newVirtualPath));
2477 // Parse the included file recursively
2478 ParseFile(newPhysicalPath, newVirtualPath);
2480 // Always ignore the spaces after an include directive
2481 flags[ignoreNextSpaceString] = true;
2484 private static char[] s_newlineChars = new char[] { '\r', '\n' };
2487 * Handle <%= ... %>, <%# ... %> and <% ... %> blocks
2489 private void ProcessCodeBlock(Match match, CodeBlockType blockType, string text) {
2491 // Take care of the previous literal string
2494 // Get the piece of code
2495 Group codeGroup = match.Groups["code"];
2496 string code = codeGroup.Value;
2498 bool encode = match.Groups["encode"].Success;
2500 // Replace "%\>" with "%>" (ASURT 7175)
2501 code = code.Replace(@"%\>", "%>");
2503 int lineNumber = _lineNumber;
2506 if (blockType != CodeBlockType.Code) {
2508 // It a <%= %>, <%# %> or <%: %> block. We need to do special handling of newline chars
2510 // If there are newlines in the beginning of the code string we need to get rid of them,
2511 // and adjust the line pragma accordingly. This is needed because some compilers (like VB)
2512 // don't support multiline expression (ASURT 13662)
2513 int newlineIndex = -1;
2514 for (int i = 0; i < code.Length && Char.IsWhiteSpace(code[i]); i++) {
2515 if (code[i] == '\r' || (code[i] == '\n' && (i == 0 || code[i - 1] != '\r'))) {
2519 else if (code[i] == '\n') {
2524 if (newlineIndex >= 0) {
2525 // If we found some newlines in the beginning, get rid of them. Note
2526 // that we preserve the spaces, to get correct column information
2527 code = code.Substring(newlineIndex + 1);
2529 // The code starts at column 1 (since we keep the spaces)
2533 // Same deal for the end of the string: look for the first newline
2534 // after the last non-blank chararacter
2536 for (int i = code.Length - 1; i >= 0 && Char.IsWhiteSpace(code[i]); i--) {
2537 if (code[i] == '\r' || code[i] == '\n')
2541 // And if we found one, remove it and everything after
2542 if (newlineIndex >= 0)
2543 code = code.Substring(0, newlineIndex);
2545 // Disallow empty expressions (ASURT 40124)
2546 // Do not treat as error in CBM. This is necessary so we still generate
2547 // code blocks for empty expressions. (VSWhidbey 406212)
2548 if (!IgnoreParseErrors && Util.IsWhiteSpaceString(code)) {
2549 ProcessError(SR.GetString(SR.Empty_expression));
2557 // It a <% %> block. Newline chars are not a problem.
2559 // Look for the last newline before the code string
2560 int newlineIndex = text.LastIndexOfAny(s_newlineChars, codeGroup.Index-1);
2562 // Use it to calculate the column where the code starts,
2563 // which improves the debugging experience (VSWhidbey 87172)
2564 column = codeGroup.Index-newlineIndex;
2565 Debug.Assert(column > 0);
2568 ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
2569 ControlBuilder subBuilder;
2571 // First, give the PageParserFilter a chance to handle the code block
2572 if (!PageParserFilterProcessedCodeBlock(CodeConstructTypeFromCodeBlockType(blockType), code, lineNumber)) {
2574 // Make sure it's legal to have code in this page
2575 EnsureCodeAllowed();
2577 // Add the code block to the top builder
2578 subBuilder = new CodeBlockBuilder(blockType, code, lineNumber, column, CurrentVirtualPath, encode);
2580 AppendSubBuilder(builder, subBuilder);
2582 // Optionally record code block position data
2583 ParseRecorders.RecordCodeBlock(subBuilder, match);
2586 // Always ignore the spaces after a <% ... %> block
2587 if (blockType == CodeBlockType.Code)
2588 flags[ignoreNextSpaceString] = true;
2591 // Map a CodeBlockType to the equivalent CodeConstructType
2592 private static CodeConstructType CodeConstructTypeFromCodeBlockType(CodeBlockType blockType) {
2593 switch (blockType) {
2594 case CodeBlockType.Code:
2595 return CodeConstructType.CodeSnippet;
2596 case CodeBlockType.Expression:
2597 return CodeConstructType.ExpressionSnippet;
2598 case CodeBlockType.EncodedExpression:
2599 return CodeConstructType.EncodedExpressionSnippet;
2600 case CodeBlockType.DataBinding:
2601 return CodeConstructType.DataBindingSnippet;
2603 Debug.Assert(false);
2604 return CodeConstructType.CodeSnippet;
2608 private bool PageParserFilterProcessedCodeBlock(CodeConstructType codeConstructType,
2609 string code, int lineNumber) {
2611 // This requires a PageParserFilter, and CompilationMode must allow it
2612 if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2615 // Temporarily adjust the line number to match what's passed in. This makes it correctly
2616 // point to the begining of <script> blocks rather than the end
2617 int restoreLineNumber = _lineNumber;
2618 _lineNumber = lineNumber;
2620 // Ask the PageParserFilter whether it wants to process it
2621 return _pageParserFilter.ProcessCodeConstruct(codeConstructType, code);
2624 _lineNumber = restoreLineNumber;
2628 internal bool PageParserFilterProcessedDataBindingAttribute(string controlId, string attributeName,
2631 // This requires a PageParserFilter, and CompilationMode must allow it
2632 if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2635 // Ask the PageParserFilter whether it wants to process it
2636 return _pageParserFilter.ProcessDataBindingAttribute(controlId, attributeName, code);
2639 internal bool PageParserFilterProcessedEventHookupAttribute(string controlId, string eventName,
2640 string handlerName) {
2642 // This requires a PageParserFilter, and CompilationMode must allow it
2643 if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2646 // Ask the PageParserFilter whether it wants to process it
2647 return _pageParserFilter.ProcessEventHookup(controlId, eventName, handlerName);
2650 // Add a ControlBuilder in the tree at the current parser position
2651 internal void AddControl(Type type, IDictionary attributes) {
2652 ControlBuilder parentBuilder = ((BuilderStackEntry)BuilderStack.Peek())._builder;
2654 ControlBuilder subBuilder = ControlBuilder.CreateBuilderFromType(this, parentBuilder,
2655 type, null, null, attributes, _lineNumber,
2656 CurrentVirtualPath.VirtualPathString);
2658 AppendSubBuilder(parentBuilder, subBuilder);
2662 * Adds attributes and their values to the attribs
2663 * Sets the _id and isServerTag data members as appropriate.
2664 * If fDirective is true, we are being called for a <%@ %> block, in
2665 * which case the name of the directive is returned (e.g. "page")
2667 private string ProcessAttributes(string text, Match match, out ParsedAttributeCollection attribs,
2668 bool fDirective, out string duplicateAttribute) {
2669 string directiveName = String.Empty;
2670 attribs = CreateEmptyAttributeBag();;
2671 CaptureCollection attrnames = match.Groups["attrname"].Captures;
2672 CaptureCollection attrvalues = match.Groups["attrval"].Captures;
2673 CaptureCollection equalsign = null;
2675 equalsign = match.Groups["equal"].Captures;
2677 flags[isServerTag] = false;
2680 duplicateAttribute = null;
2682 for (int i = 0; i < attrnames.Count; i++) {
2683 string attribName = attrnames[i].ToString();
2685 // If processing a directive, set the attribute name to lower case for easier processing later
2687 attribName = attribName.ToLower(CultureInfo.InvariantCulture);
2689 Capture attrValue = attrvalues[i];
2690 string attribValue = attrValue.ToString();
2692 // Any of the attributes could be filtered
2693 string realAttributeName = String.Empty;
2694 string filter = Util.ParsePropertyDeviceFilter(attribName, out realAttributeName);
2696 // Always HTML decode all attributes (ASURT 54544)
2697 attribValue = HttpUtility.HtmlDecode(attribValue);
2699 // If we're parsing a directive, check if there is an equal sign.
2700 bool fHasEqual = false;
2702 fHasEqual = (equalsign[i].ToString().Length > 0);
2704 // If this is a server ID, remember it
2707 if (StringUtil.EqualsIgnoreCase(realAttributeName, "id")) {
2710 else if (StringUtil.EqualsIgnoreCase(realAttributeName, "runat")) {
2711 // Make sure no device filter or resource expression was specified (VSWhidbey 85325)
2712 ValidateBuiltInAttribute(filter, realAttributeName, attribValue);
2714 // Only runat=server is valid
2715 if (!StringUtil.EqualsIgnoreCase(attribValue, "server")) {
2716 ProcessError(SR.GetString(SR.Runat_can_only_be_server));
2719 // Set a flag if we see runat=server
2720 flags[isServerTag] = true;
2721 attribName = null; // Don't put it in attribute bag
2723 else if (FInDesigner && StringUtil.EqualsIgnoreCase(realAttributeName, "ignoreParentFrozen")) {
2724 // VSWhidbey 537398: "ignoreParentFrozen" is a special expando used in Venus. Ideally
2725 // Venus would hide the expando altogether but that's not practical in Whidbey.
2729 if (attribName != null) {
2730 // A <%@ %> block can have two formats:
2731 // <%@ directive foo=1 bar=hello %>
2732 // <%@ foo=1 bar=hello %>
2733 // Check if we have the first format
2734 if (fDirective && !fHasEqual && i==0) {
2735 directiveName = attribName;
2736 if (string.Compare(directiveName, DefaultDirectiveName,
2737 StringComparison.OrdinalIgnoreCase) == 0) {
2738 directiveName = String.Empty;
2744 // Don't allow filters in directive other than the main one
2745 if (fDirective && directiveName.Length > 0 && filter.Length > 0) {
2746 ProcessError(SR.GetString(SR.Device_unsupported_in_directive, directiveName));
2751 attribs.AddFilteredAttribute(filter, realAttributeName, attribValue);
2752 //Since the attribute column values are only used for generating line pragmas at design time for intellisense to work,
2753 //we populate that only at design time so that runtime memory usage is not affected.
2754 if (BuildManagerHost.InClientBuildManager) {
2755 //Linenumber = Linenumber of tag beginning + new lines between tag beginning and attribute value beginning.
2756 //Column = number of characters from the last new line (before the attrValue in the entire Text) till attrValue.
2757 int lineNumber = _lineNumber + Util.LineCount(text, match.Index, attrValue.Index);
2758 int column = attrValue.Index - text.LastIndexOfAny(s_newlineChars, attrValue.Index - 1);
2759 attribs.AddAttributeValuePositionInformation(realAttributeName, lineNumber, column);
2762 catch (ArgumentException) {
2763 // Duplicate attribute. We can't throw until we find out if
2764 // it's a server side tag (ASURT 51273)
2765 duplicateAttribute = attribName;
2767 catch (Exception ex) {
2768 ProcessException(ex);
2773 if (duplicateAttribute != null && fDirective) {
2774 ProcessError(SR.GetString(SR.Duplicate_attr_in_directive, duplicateAttribute));
2777 return directiveName;
2780 private static ParsedAttributeCollection CreateEmptyAttributeBag() {
2781 // use a ParsedAttributeCollection to preserve the order and store filtered information
2782 return new ParsedAttributeCollection();
2785 private bool MaybeTerminateControl(string tagName, Match match) {
2787 BuilderStackEntry stackEntry = (BuilderStackEntry) BuilderStack.Peek();
2788 ControlBuilder builder = stackEntry._builder;
2790 // If the tag doesn't match, return false
2791 if (stackEntry._tagName == null || !StringUtil.EqualsIgnoreCase(stackEntry._tagName, tagName)) {
2795 // If the repeat count is non-zero, just decrease it
2796 if (stackEntry._repeatCount > 0) {
2797 stackEntry._repeatCount--;
2801 // Take care of the previous literal string
2804 // If the builder wants the raw text of the tag, give it to it
2805 if (builder.NeedsTagInnerText()) {
2807 builder.SetTagInnerText(stackEntry._inputText.Substring(
2808 stackEntry._textPos,
2809 match.Index - stackEntry._textPos));
2811 catch (Exception e) {
2812 if (!IgnoreParseErrors) {
2813 // Reset the line number to the beginning of the tag if there is an error
2814 _lineNumber = builder.Line;
2815 ProcessException(e);
2822 // If it's ending a template, pop the idList (ASURT 72773)
2823 if (builder is TemplateBuilder && ((TemplateBuilder)builder).AllowMultipleInstances)
2824 _idList = (StringSet) _idListStack.Pop();
2826 // Pop the top entry from the stack
2827 _builderStack.Pop();
2829 // Give the builder to its parent
2830 AppendSubBuilder(((BuilderStackEntry) _builderStack.Peek())._builder, builder);
2832 // Tell the builder that we're done parsing its control
2833 builder.CloseControl();
2835 // Optionally record end tag position data
2836 ParseRecorders.RecordEndTag(builder, match);
2842 * Map a type name to a Type.
2844 internal Type MapStringToType(string typeName, IDictionary attribs) {
2845 return RootBuilder.GetChildControlType(typeName, attribs);
2849 * Add a file as a dependency of the file we're parsing
2851 internal void AddSourceDependency(VirtualPath fileName) {
2853 // Tell the filter that a dependency was added
2854 if (_pageParserFilter != null) {
2855 _pageParserFilter.OnDependencyAdded();
2856 _pageParserFilter.OnDirectDependencyAdded();
2859 AddSourceDependency2(fileName);
2863 * Add a file as a dependency of the file we're parsing
2865 private void AddSourceDependency2(VirtualPath fileName) {
2866 if (_sourceDependencies == null)
2867 _sourceDependencies = new CaseInsensitiveStringSet();
2869 _sourceDependencies.Add(fileName.VirtualPathString);
2873 * Add a BuildResult's source dependencies to our own source dependencies
2875 internal void AddBuildResultDependency(BuildResult result) {
2877 // Add one direct dependency
2878 if (_pageParserFilter != null)
2879 _pageParserFilter.OnDirectDependencyAdded();
2881 if (result.VirtualPathDependencies == null)
2884 foreach (string virtualPath in result.VirtualPathDependencies) {
2886 // Add one dependency for each file (to include direct and indirect)
2887 if (_pageParserFilter != null)
2888 _pageParserFilter.OnDependencyAdded();
2890 AddSourceDependency2(VirtualPath.Create(virtualPath));
2895 * Add a type that we must 'link' with in order to build
2897 internal void AddTypeDependency(Type type) {
2898 // We must link with all the types in the inheritance hierarchy (ASURT 83509)
2899 AddBaseTypeDependencies(type);
2901 // Add an import for the namespace of the type (if any)
2902 // Per ASURT 83942, only do this for namespaces we generate (e.g. ASP & _ASP)
2903 if (type.Namespace != null && BaseCodeDomTreeGenerator.IsAspNetNamespace(type.Namespace))
2904 AddImportEntry(type.Namespace);
2908 * Add as dependencies all the assembly in the inheritance chain of a Type,
2909 * including interfaces.
2911 private void AddBaseTypeDependencies(Type type) {
2912 Assembly a = type.Module.Assembly;
2914 // If the type is in a standard assembly, don't bother
2915 if (a == typeof(string).Assembly || a == typeof(Page).Assembly || a == typeof(Uri).Assembly)
2918 AddAssemblyDependency(a);
2920 // Recurse on the base Type
2921 if (type.BaseType != null)
2922 AddBaseTypeDependencies(type.BaseType);
2924 // Recurse on all the implemented interfaces
2925 Type[] interfaceTypes = type.GetInterfaces();
2926 foreach (Type interfaceType in interfaceTypes)
2927 AddBaseTypeDependencies(interfaceType);
2931 * Add an assembly that we must 'link' with in order to build
2933 internal Assembly AddAssemblyDependency(string assemblyName, bool addDependentAssemblies) {
2935 Assembly assembly = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
2937 if (assembly != null)
2938 AddAssemblyDependency(assembly, addDependentAssemblies);
2942 internal Assembly AddAssemblyDependency(string assemblyName) {
2943 return AddAssemblyDependency(assemblyName, false /*addDependentAssemblies*/);
2947 * Add an assembly that we must 'link' with in order to build
2949 internal void AddAssemblyDependency(Assembly assembly, bool addDependentAssemblies) {
2950 if (_assemblyDependencies == null)
2951 _assemblyDependencies = new AssemblySet();
2953 if (_typeResolutionService != null)
2954 _typeResolutionService.ReferenceAssembly(assembly.GetName());
2956 _assemblyDependencies.Add(assembly);
2958 // If addDependentAssemblies is true, add its dependent assemblies as well
2959 if (addDependentAssemblies) {
2960 AssemblySet assemblyDependencies = Util.GetReferencedAssemblies(assembly);
2961 AddAssemblyDependencies(assemblyDependencies);
2964 internal void AddAssemblyDependency(Assembly assembly) {
2965 AddAssemblyDependency(assembly, false /*addDependentAssemblies*/);
2969 * Add a set of assemblies that we must 'link' with in order to build
2971 private void AddAssemblyDependencies(AssemblySet assemblyDependencies) {
2972 if (assemblyDependencies == null)
2975 foreach (Assembly a in assemblyDependencies)
2976 AddAssemblyDependency(a);
2981 ICollection IAssemblyDependencyParser.AssemblyDependencies {
2983 return AssemblyDependencies;
2987 internal IImplicitResourceProvider GetImplicitResourceProvider() {
2993 // If we already attempted to get them, return whatever we got
2994 if (flags[attemptedImplicitResources])
2995 return _implicitResourceProvider;
2997 flags[attemptedImplicitResources] = true;
2999 IResourceProvider resourceProvider = ResourceExpressionBuilder.GetLocalResourceProvider(_rootBuilder.VirtualPath);
3000 if (resourceProvider == null)
3003 // If the resource provider is also an IImplicitResourceProvider, use that
3004 _implicitResourceProvider = resourceProvider as IImplicitResourceProvider;
3006 // Otherwise, use the default IImplicitResourceProvider implementation
3007 if (_implicitResourceProvider == null)
3008 _implicitResourceProvider = new DefaultImplicitResourceProvider(resourceProvider);
3010 return _implicitResourceProvider;
3015 * Base class for classes that contain source file & line information for error reporting
3017 internal abstract class SourceLineInfo {
3019 // Source file where the information appears
3020 private string _virtualPath;
3021 internal string VirtualPath {
3022 get { return _virtualPath;}
3023 set { _virtualPath = value;}
3026 // Line number in the source file where the information appears
3029 get { return _line;}
3030 set { _line = value;}
3036 * Objects that are placed on the BuilderStack
3038 internal class BuilderStackEntry: SourceLineInfo {
3039 internal BuilderStackEntry (ControlBuilder builder,
3040 string tagName, string virtualPath, int line,
3041 string inputText, int textPos) {
3045 VirtualPath = virtualPath;
3047 _inputText = inputText;
3051 internal ControlBuilder _builder;
3052 internal string _tagName;
3054 // the input string that contains the tag
3055 internal string _inputText;
3057 // Offset in the input string of the beginning of the tag's contents
3058 internal int _textPos;
3060 // Used to deal with non server tags nested in server tag with the same name
3061 internal int _repeatCount;
3066 * Entry representing an import directive.
3067 * e.g. <%@ import namespace="System.Web.UI" %>
3069 internal class NamespaceEntry: SourceLineInfo {
3070 private string _namespace;
3072 internal NamespaceEntry() {
3075 internal string Namespace {
3076 get { return _namespace;}
3077 set { _namespace = value;}
3081 internal class ScriptBlockData: SourceLineInfo {
3082 protected string _script;
3084 internal ScriptBlockData(int line, int column, string virtualPath) {
3087 VirtualPath = virtualPath;
3090 // Line number in the source file where the information appears
3091 private int _column;
3092 internal int Column {
3093 get { return _column;}
3094 set { _column = value;}
3097 internal string Script {
3098 get { return _script;}
3099 set { _script = value;}