13ba31d98c53803b5ce30b60031e6052f10bc0a2
[mono.git] / mcs / class / referencesource / System.Web / UI / TemplateParser.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="TemplateParser.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  * Implements the ASP.NET template parser
9  *
10  * Copyright (c) 1998 Microsoft Corporation
11  */
12
13 // Turn this on to do regex profiling
14 //#define PROFILE_REGEX
15
16 namespace System.Web.UI {
17 using System.Runtime.Serialization.Formatters;
18 using System.Text;
19 using System.Runtime.InteropServices;
20 using System.Runtime.Serialization;
21 using System;
22 using System.IO;
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;
41
42
43 /// <internalonly/>
44 /// <devdoc>
45 ///    <para>[To be supplied.]</para>
46 /// </devdoc>
47 public abstract class TemplateParser : BaseParser, IAssemblyDependencyParser {
48
49     internal const string CodeFileBaseClassAttributeName = "codefilebaseclass";
50
51     // The <compilation> config section
52     private CompilationSection _compConfig;
53     internal CompilationSection CompConfig {
54         get { return _compConfig; }
55     }
56
57     // The <pages> config section
58     private PagesSection _pagesConfig;
59     internal PagesSection PagesConfig {
60         get { return _pagesConfig; }
61     }
62
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
94
95     private MainTagNameToTypeMapper _typeMapper;
96     internal MainTagNameToTypeMapper TypeMapper { get { return _typeMapper; } }
97
98     internal ICollection UserControlRegisterEntries { get { return TypeMapper.UserControlRegisterEntries; } }
99     internal List<TagNamespaceRegisterEntry> TagRegisterEntries { get { return TypeMapper.TagRegisterEntries; } }
100
101     private Stack _builderStack; // Stack of BuilderStackEntry's
102     internal Stack BuilderStack {
103         get {
104             EnsureRootBuilderCreated();
105             return _builderStack;
106         }
107     }
108
109     private string _id;
110     private StringSet _idList;
111     private Stack _idListStack;
112     private ScriptBlockData _currentScript;
113     private StringBuilder _literalBuilder;
114
115     // The line number in file currently being parsed
116     internal int _lineNumber;
117
118     // The line number at which the current script block started
119     private int _scriptStartLineNumber;
120
121     // String that contains the data to be parsed
122     private string _text;
123     public string Text {
124         get { return _text; }
125         internal set { _text = value; }
126     }
127
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; }
133     }
134
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; } }
141
142     internal bool IgnoreControlProperties {
143         get { return flags[ignoreControlProperties]; }
144         set { flags[ignoreControlProperties] = value; }
145     }
146
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; }
151     }
152
153     // The interfaces that we implement (ArrayList of Type objects)
154     private ArrayList _implementedInterfaces;
155     internal ArrayList ImplementedInterfaces { get { return _implementedInterfaces; } }
156
157     internal bool HasCodeBehind { get { return flags[hasCodeBehind]; } }
158
159     internal abstract Type DefaultBaseType { get; }
160
161     internal PageParserFilter _pageParserFilter;
162
163     private IImplicitResourceProvider _implicitResourceProvider;
164
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; }
170     }
171
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; }
177     }
178
179     // When true, it is not legal to have any constructs that require compilation
180     private CompilationMode _compilationMode;
181     internal CompilationMode CompilationMode {
182         get {
183             // When precompiling for deployment, always compile everything (VSWhidbey 266509)
184             if (BuildManager.PrecompilingForDeployment)
185                 return CompilationMode.Always;
186
187             return _compilationMode;
188         }
189         set {
190             if (value == CompilationMode.Never && flags[requiresCompilation]) {
191                 ProcessError(SR.GetString(SR.Compilmode_not_allowed));
192             }
193
194             _compilationMode = value;
195         }
196     }
197
198     private ParserErrorCollection _parserErrors;
199     private ParserErrorCollection ParserErrors {
200         get {
201             if (_parserErrors == null) {
202                 _parserErrors = new ParserErrorCollection();
203             }
204
205             return _parserErrors;
206         }
207     }
208
209     private bool HasParserErrors {
210         get { return _parserErrors != null && _parserErrors.Count > 0; }
211     }
212
213     // Method to report parser errors.
214     protected void ProcessError(string message) {
215         // Ignore the errors if in that mode.
216         if (IgnoreParseErrors) {
217             return;
218         }
219
220         // Rethrow as innerexception if in that mode.
221         if (ThrowOnFirstParseError) {
222             throw new HttpException(message);
223         }
224
225         // otherwise add to the error collection with proper info.
226         ParserError parseError = new ParserError(message, CurrentVirtualPath, _lineNumber);
227         ParserErrors.Add(parseError);
228
229         // If there is a CBM callback, inform it of the error
230         BuildManager.ReportParseError(parseError);
231     }
232
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) {
237             return;
238         }
239
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)
243                 throw ex;
244             throw new HttpParseException(ex.Message, ex);
245         }
246
247         // If it is already a parser exception remember the location corresponding to 
248         // the original error.
249         ParserError parseError;
250
251         HttpParseException hpe = ex as HttpParseException;
252         if (hpe != null) {
253             parseError = new ParserError(hpe.Message, hpe.VirtualPath, hpe.Line);
254         }
255         else {
256             parseError = new ParserError(ex.Message, CurrentVirtualPath, _lineNumber);
257         }
258
259         // Remember the original exception.
260         parseError.Exception = ex;
261
262         ParserErrors.Add(parseError);
263
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);
269         }
270     }
271
272     // When true, there are construct that require compilation
273     internal virtual bool RequiresCompilation {
274         get {
275             // By default, require compilation.  The Page parser overrides this
276             // and can allow no-compile pages depending on the page contents
277             return true;
278         }
279     }
280
281     internal virtual bool IsCodeAllowed {
282         get {
283             // If it's a no-compile page, code is not allowed
284             if (CompilationMode == CompilationMode.Never)
285                 return false;
286
287             // Likewise, check if the PageParserFilter allows code
288             if (_pageParserFilter != null && !_pageParserFilter.AllowCode)
289                 return false;
290
291             return true;
292         }
293     }
294
295     internal void EnsureCodeAllowed() {
296
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));
301         }
302
303         // Remember the fact that this page MUST be compiled
304         flags[requiresCompilation] = true;
305     }
306
307     // This is called whenever we parse an attribute that requires compilation
308     internal void OnFoundAttributeRequiringCompilation(string attribName) {
309
310         // If compilation is not alowed, fail
311         if (!IsCodeAllowed) {
312             ProcessError(SR.GetString(SR.Attrib_not_allowed, attribName));
313         }
314
315         // Remember the fact that this page MUST be compiled
316         flags[requiresCompilation] = true;
317     }
318
319     // This is called whenever we parse a directive that requires compilation
320     internal void OnFoundDirectiveRequiringCompilation(string directiveName) {
321
322         // If compilation is not alowed, fail
323         if (!IsCodeAllowed) {
324             ProcessError(SR.GetString(SR.Directive_not_allowed, directiveName));
325         }
326
327         // Remember the fact that this page MUST be compiled
328         flags[requiresCompilation] = true;
329     }
330
331     // This is called whenever we parse an event attribute on a tag
332     internal void OnFoundEventHandler(string directiveName) {
333
334         // If compilation is not alowed, fail
335         if (!IsCodeAllowed) {
336             ProcessError(SR.GetString(SR.Event_not_allowed, directiveName));
337         }
338
339         // Remember the fact that this page MUST be compiled
340         flags[requiresCompilation] = true;
341     }
342
343     private IDesignerHost _designerHost;
344     private ITypeResolutionService _typeResolutionService;
345     internal IDesignerHost DesignerHost {
346         get {
347             Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
348             return _designerHost;
349         }
350         set {
351             Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
352             _designerHost = value;
353
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));
359                 }
360             }
361         }
362     }
363
364     // true if we're parsing global.asax
365     internal virtual bool FApplicationFile { get { return false; } }
366
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; }
373     }
374
375     // Used to detect circular references
376     private StringSet _circularReferenceChecker;
377
378     // The set of assemblies that the build system is telling us we will be linked with
379     private ICollection _referencedAssemblies;
380
381     // The set of assemblies that this file is explicitly asking for
382     private AssemblySet _assemblyDependencies;
383     internal AssemblySet AssemblyDependencies {
384         get { return _assemblyDependencies; }
385     }
386
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; }
391     }
392
393     // The collection of <object> tags with scope=session
394     internal HttpStaticObjectsCollection _sessionObjects;
395     internal HttpStaticObjectsCollection SessionObjects {
396         get { return _sessionObjects; }
397     }
398
399     // The collection of <object> tags with scope=application
400     internal HttpStaticObjectsCollection _applicationObjects;
401     internal HttpStaticObjectsCollection ApplicationObjects {
402         get { return _applicationObjects; }
403     }
404
405     // data that was obtained from parsing the input file
406
407     private RootBuilder _rootBuilder;
408     internal RootBuilder RootBuilder {
409         get {
410             EnsureRootBuilderCreated();
411             return _rootBuilder;
412         }
413     }
414
415     // Main directive attributes coming from config
416     internal IDictionary _mainDirectiveConfigSettings;
417
418     // <namespace name, NamespaceEntry>
419     private Hashtable _namespaceEntries;
420     internal Hashtable NamespaceEntries { get { return _namespaceEntries; } }
421
422     private CompilerType _compilerType;
423     internal CompilerType CompilerType { get { return _compilerType; } }
424
425     // the server side scripts (list of ScriptBlockData's)
426     private ArrayList _scriptList;
427     internal ArrayList ScriptList { get { return _scriptList; } }
428
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; } }
432
433     // The <object> tags local to the page.  Entries are ObjectTagBuilder's.
434     private ArrayList _pageObjectList;
435     internal ArrayList PageObjectList { get { return _pageObjectList; } }
436
437     // Record extra parse data
438     private ParseRecorder _parseRecorders = ParseRecorder.Null;
439     internal ParseRecorder ParseRecorders { get { return _parseRecorders; } }
440
441     // Data parsed from the directives
442
443     internal CompilerParameters CompilParams { get { return _compilerType.CompilerParameters; } }
444
445     internal bool FExplicit { get { return flags[useExplicit]; } }
446
447     internal bool FLinePragmas { get { return !flags[noLinePragmas]; } }
448
449     private int _warningLevel=-1;
450     private string _compilerOptions;
451
452     internal bool FStrict { get { return flags[strict]; } }
453
454     // File that we must be compiled with, aka code besides (optional)
455     private VirtualPath _codeFileVirtualPath;
456     internal VirtualPath CodeFileVirtualPath { get { return _codeFileVirtualPath; } }
457
458     // Name that the user wants to give to the generated class
459     private string _generatedClassName;
460     internal string GeneratedClassName { get { return _generatedClassName; } }
461
462     // Name that the user wants to give to the generated namespace
463     private string _generatedNamespace = null;
464     internal string GeneratedNamespace {
465         get {
466             // If no namespace was specified, use "ASP"
467             if (_generatedNamespace == null)
468                 return BaseCodeDomTreeGenerator.defaultNamespace;
469
470             return _generatedNamespace;
471         }
472     }
473
474     private ControlBuilderInterceptor _controlBuilderInterceptor;
475     internal ControlBuilderInterceptor ControlBuilderInterceptor {
476         get {
477             if (_controlBuilderInterceptor == null && CompConfig != null && CompConfig.ControlBuilderInterceptorTypeInternal != null) {
478                 _controlBuilderInterceptor = (ControlBuilderInterceptor) Activator.CreateInstance(CompConfig.ControlBuilderInterceptorTypeInternal);
479             }
480             return _controlBuilderInterceptor;
481         }
482     }
483
484     /// <devdoc>
485     /// Parse the input into a Control. This is used to parse in a control dynamically from some
486     /// textual content.
487     /// </devdoc>
488     internal static Control ParseControl(string content, VirtualPath virtualPath, bool ignoreFilter) {
489         if (content == null) {
490             return null;
491         }
492
493         ITemplate t = ParseTemplate(content, virtualPath, ignoreFilter);
494
495         // Create a parent control to hold the controls we parsed
496         Control c = new Control();
497         t.InstantiateIn(c);
498
499         return c;
500     }
501
502     public static ITemplate ParseTemplate(string content, string virtualPath, bool ignoreFilter) {
503         return ParseTemplate(content, VirtualPath.Create(virtualPath), ignoreFilter);
504     }
505
506     private static ITemplate ParseTemplate(string content, VirtualPath virtualPath, bool ignoreFilter) {
507         TemplateParser parser = new UserControlParser();
508         return parser.ParseTemplateInternal(content, virtualPath, ignoreFilter);
509     }
510
511     private ITemplate ParseTemplateInternal(string content, VirtualPath virtualPath, bool ignoreFilter) {
512
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;
517         _text = content;
518
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;
523
524         Parse();
525
526         Debug.Assert(RootBuilder != null);
527         return RootBuilder;
528     }
529
530     /*
531      * Do some initialization before the parsing
532      */
533     internal virtual void PrepareParse() {
534         if (_circularReferenceChecker == null)
535             _circularReferenceChecker = new CaseInsensitiveStringSet();
536
537         _baseType = DefaultBaseType;
538
539         // Initialize the main directive
540         _mainDirectiveConfigSettings = CreateEmptyAttributeBag();
541
542         // Get the config sections we care about
543         if (!FInDesigner) {
544             _compConfig = MTConfigUtil.GetCompilationConfig(CurrentVirtualPath);
545             _pagesConfig = MTConfigUtil.GetPagesConfig(CurrentVirtualPath);
546         }
547
548         // Get default settings from config
549         ProcessConfigSettings();
550
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);
554
555         // Register the <object> tag
556         _typeMapper.RegisterTag("object", typeof(System.Web.UI.ObjectTag));
557
558         _sourceDependencies = new CaseInsensitiveStringSet();
559
560         // Create and seed the stack of ID lists.
561         _idListStack = new Stack();
562         _idList = new CaseInsensitiveStringSet();
563
564         _scriptList = new ArrayList();
565
566         // Optionally collect additional parse data for render tracing
567         InitializeParseRecorders();
568     }
569
570     private void InitializeParseRecorders() {
571         if (FInDesigner)
572             return;
573
574         _parseRecorders = ParseRecorder.CreateRecorders(this);
575     }
576
577     private void EnsureRootBuilderCreated() {
578
579         // Create it on demand
580         if (_rootBuilder != null)
581             return;
582
583         if (BaseType == DefaultBaseType) {
584             // If the base type is the default, no need to look up the attribute
585             _rootBuilder = CreateDefaultFileLevelBuilder();
586         }
587         else {
588             // Look for a custom attribute
589             Type fileLevelBuilderType = GetFileLevelControlBuilderType();
590
591             if (fileLevelBuilderType == null) {
592                 // No custom type: use the default
593                 _rootBuilder = CreateDefaultFileLevelBuilder();
594             }
595             else {
596                 // Create the custom file level builder
597                 _rootBuilder = (RootBuilder) HttpRuntime.CreateNonPublicInstance(
598                     fileLevelBuilderType);
599             }
600         }
601
602         _rootBuilder.Line = 1;
603         _rootBuilder.Init(this, null, null, null, null, null);
604         _rootBuilder.SetTypeMapper(TypeMapper);
605
606         _rootBuilder.VirtualPath = CurrentVirtualPath;
607
608         // Create and seed the stack of builders.
609         _builderStack = new Stack();
610         _builderStack.Push(new BuilderStackEntry(RootBuilder, null, null, 0, null, 0));
611     }
612
613     internal virtual Type DefaultFileLevelBuilderType {
614         get {
615             return typeof(RootBuilder);
616         }
617     }
618
619     internal virtual RootBuilder CreateDefaultFileLevelBuilder() {
620
621         // By default, create a RootBuilder
622         return new RootBuilder();
623     }
624
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];
633         }
634
635         if (cba == null)
636             return null;
637
638         // Make sure the type has the correct base class
639         Util.CheckAssignableType(DefaultFileLevelBuilderType, cba.BuilderType);
640
641         return cba.BuilderType;
642     }
643
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;
649         }
650
651         if (PagesConfig != null) {
652             _namespaceEntries = PagesConfig.Namespaces.NamespaceEntries;
653
654             // Clone it so we don't modify the config settings
655             if (_namespaceEntries != null)
656                 _namespaceEntries = (Hashtable) _namespaceEntries.Clone();
657
658             if (!flags[ignoreParserFilter]) {
659                 // Check if a filter is registered, and if so initialize it
660                 _pageParserFilter = PageParserFilter.Create(PagesConfig, CurrentVirtualPath, this);
661             }
662         }
663     }
664
665     internal void Parse(ICollection referencedAssemblies, VirtualPath virtualPath) {
666
667         _referencedAssemblies = referencedAssemblies;
668         CurrentVirtualPath = virtualPath;
669
670         Parse();
671     }
672
673     /*
674      * Parse the input
675      */
676     internal void Parse() {
677
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;
683
684         try {
685             try {
686                 // Do some initialization before the parsing
687                 PrepareParse();
688                 ParseInternal();
689                 HandlePostParse();
690             }
691             finally {
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);
696             }
697         }
698         catch { throw; }    // Prevent Exception Filter Security Issue (ASURT 122835)
699     }
700
701     internal virtual void ParseInternal() {
702         // Parse either the file or string
703         if (_text != null) {
704             ParseString(_text, CurrentVirtualPath, Encoding.UTF8);
705         }
706         else {
707             AddSourceDependency(CurrentVirtualPath);
708             ParseFile(null /*physicalPath*/, CurrentVirtualPath.VirtualPathString);
709         }
710     }
711
712     internal TemplateParser() {
713         ThrowOnFirstParseError = true;
714     }
715
716     /*
717      * Parse the contents of the input file
718      */
719
720     protected void ParseFile(string physicalPath, string virtualPath) {
721         ParseFile(physicalPath, VirtualPath.Create(virtualPath));
722     }
723
724     internal void ParseFile(string physicalPath, VirtualPath virtualPath) {
725
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;
730
731         // Check for circular references of include files
732         if (_circularReferenceChecker.Contains(fileToReferenceCheck)) {
733             ProcessError(SR.GetString(SR.Circular_include));
734
735             return;
736         }
737
738         // Add the current file to the circular references checker.
739         _circularReferenceChecker.Add(fileToReferenceCheck);
740
741         try {
742             // Open a TextReader either from the physical or virtual path
743             StreamReader reader;
744             if (physicalPath != null) {
745                 using (reader = Util.ReaderFromFile(physicalPath, CurrentVirtualPath)) {
746                     ParseReader(reader, virtualPath);
747                 }
748             }
749             else {
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);
754                 }
755             }
756         }
757         finally {
758             // Remove the current file from the circular references checker
759             _circularReferenceChecker.Remove(fileToReferenceCheck);
760         }
761     }
762
763     /*
764      * Parse the contents of the TextReader
765      */
766     private void ParseReader(StreamReader reader, VirtualPath virtualPath) {
767         string s = reader.ReadToEnd();
768
769         // Save the text of the input file in case it's trivial
770         _text = s;
771
772         ParseString(s, virtualPath, reader.CurrentEncoding);
773     }
774
775     private void AddLiteral(string literal) {
776
777         if (_literalBuilder == null)
778             _literalBuilder = new StringBuilder();
779
780         _literalBuilder.Append(literal);
781     }
782
783     private string GetLiteral() {
784         if (_literalBuilder == null)
785             return null;
786
787         return _literalBuilder.ToString();
788     }
789
790     /*
791      * Update the hash code of the Type we're creating by xor'ing it with
792      * a string.
793      */
794     internal void UpdateTypeHashCode(string text) {
795         _typeHashCode.AddObject(text);
796     }
797
798     /*
799      * Parse the contents of the string, and catch exceptions
800      */
801     internal void ParseString(string text, VirtualPath virtualPath, Encoding fileEncoding) {
802
803         System.Web.Util.Debug.Trace("Template", "Starting parse at " + DateTime.Now);
804
805         // Save the previous base dirs and line number
806         VirtualPath prevVirtualPath = CurrentVirtualPath;
807         int prevLineNumber = _lineNumber;
808
809         // Set the new current base dirs and line number
810         CurrentVirtualPath = virtualPath;
811         _lineNumber = 1;
812
813         // Always ignore the spaces at the beginning of a string
814         flags[ignoreNextSpaceString] = true;
815
816         try {
817             ParseStringInternal(text, fileEncoding);
818
819             // If there are parser errors caught in the parser
820             if (HasParserErrors) {
821                 ParserError firstError = ParserErrors[0];
822
823                 Exception originalException = firstError.Exception;
824
825                 // Use the first error as the inner exception if not already caught one.
826                 if (originalException == null) {
827                     originalException = new HttpException(firstError.ErrorText);
828                 }
829
830                 // Make it a HttpParseException with proper info.
831                 HttpParseException ex = new HttpParseException(firstError.ErrorText,
832                     originalException, firstError.VirtualPath, Text, firstError.Line);
833
834                 // Add the rest of the errors
835                 for (int i = 1; i < ParserErrors.Count; i++) {
836                     ex.ParserErrors.Add(ParserErrors[i]);
837                 }
838
839                 // throw the new exception
840                 throw ex;
841             }
842
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
845             // anymore
846             ThrowOnFirstParseError = true;
847         }
848         catch (Exception e) {
849             ErrorFormatter errorFormatter = null;
850
851             PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_PRE_PROCESSING);
852             PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
853
854             // Check if the exception has a formatter
855             errorFormatter = HttpException.GetErrorFormatter(e);
856
857             // If it doesn't, throw a parse exception
858             if (errorFormatter == null) {
859
860                 throw new HttpParseException(e.Message, e,
861                     CurrentVirtualPath, text, _lineNumber);
862             }
863             else {
864                 // Otherwise, just rethrow it
865                 throw;
866             }
867         }
868         finally {
869             // Restore the previous base dirs and line number
870             CurrentVirtualPath = prevVirtualPath;
871             _lineNumber = prevLineNumber;
872         }
873
874         System.Web.Util.Debug.Trace("Template", "Ending parse at " + DateTime.Now);
875     }
876
877 #if PROFILE_REGEX
878     private Match RunTagRegex(string text, int textPos) {
879         int i=1;
880         if (i==0)
881             throw new HttpException("Bogus exception just to prevent method inlining");
882
883         return TagRegex.Match(text, textPos);
884     }
885
886     private Match RunDirectiveRegex(string text, int textPos) {
887         int i=1;
888         if (i==0)
889             throw new HttpException("Bogus exception just to prevent method inlining");
890
891         return directiveRegex.Match(text, textPos);
892     }
893
894     private Match RunEndTagRegex(string text, int textPos) {
895         int i=1;
896         if (i==0)
897             throw new HttpException("Bogus exception just to prevent method inlining");
898
899         return endtagRegex.Match(text, textPos);
900     }
901
902     private Match RunCodeBlockRegex(string text, int textPos) {
903         int i=1;
904         if (i==0)
905             throw new HttpException("Bogus exception just to prevent method inlining");
906
907         return aspCodeRegex.Match(text, textPos);
908     }
909
910     private Match RunExprCodeBlockRegex(string text, int textPos) {
911         int i=1;
912         if (i==0)
913             throw new HttpException("Bogus exception just to prevent method inlining");
914
915         return aspExprRegex.Match(text, textPos);
916     }
917
918     private Match RunCommentRegex(string text, int textPos) {
919         int i=1;
920         if (i==0)
921             throw new HttpException("Bogus exception just to prevent method inlining");
922
923         return commentRegex.Match(text, textPos);
924     }
925
926     private Match RunIncludeRegex(string text, int textPos) {
927         int i=1;
928         if (i==0)
929             throw new HttpException("Bogus exception just to prevent method inlining");
930
931         return includeRegex.Match(text, textPos);
932     }
933
934     private Match RunTextRegex(string text, int textPos) {
935         int i=1;
936         if (i==0)
937             throw new HttpException("Bogus exception just to prevent method inlining");
938
939         return textRegex.Match(text, textPos);
940     }
941 #endif // PROFILE_REGEX
942
943     /*
944      * Parse the contents of the string
945      */
946     private void ParseStringInternal(string text, Encoding fileEncoding) {
947         int textPos = 0;
948
949         // Find the last '>' in the input string
950         int lastGTIndex = text.LastIndexOf('>');
951
952         Regex tagRegex = TagRegex;
953
954         for (;;) {
955             Match match;
956
957             // 1: scan for text up to the next tag.
958
959 #if PROFILE_REGEX
960             if ((match = RunTextRegex(text, textPos)).Success)
961 #else
962             if ((match = textRegex.Match(text, textPos)).Success)
963 #endif
964             {
965                 // Append the text to the literal builder
966                 AddLiteral(match.ToString());
967
968                 _lineNumber += Util.LineCount(text, textPos,
969                                          match.Index + match.Length);
970                 textPos = match.Index + match.Length;
971             }
972
973             // we might be done now
974
975             if (textPos == text.Length)
976                 break;
977
978             // 2: handle constructs that start with <
979
980             // This later gets set to true if we match a regex, but do not
981             // process the match
982             bool fMatchedButNotProcessed = false;
983
984             // Check to see if it's a directive (i.e. <%@ %> block)
985
986             if (!flags[inScriptTag] &&
987 #if PROFILE_REGEX
988                 (match = RunDirectiveRegex(text, textPos)).Success)
989 #else
990                 (match = directiveRegex.Match(text, textPos)).Success)
991 #endif
992             {
993                 ProcessLiteral();
994
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);
999
1000                 try {
1001                     // If there is a parser filter, give it a chance to look at the directive
1002                     PreprocessDirective(directiveName, directive);
1003
1004                     ProcessDirective(directiveName, directive);
1005                 }
1006                 catch(Exception e) {
1007                     ProcessException(e);
1008                 }
1009
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);
1014                 }
1015
1016                 // Always ignore the spaces after a directive
1017                 flags[ignoreNextSpaceString] = true;
1018             }
1019
1020             // Check to see if it's a server side include
1021             // e.g. <!-- #include file="foo.inc" -->
1022
1023 #if PROFILE_REGEX
1024             else if ((match = RunIncludeRegex(text, textPos)).Success)
1025 #else
1026             else if ((match = includeRegex.Match(text, textPos)).Success)
1027 #endif
1028             {
1029                 try {
1030                     ProcessServerInclude(match);
1031                 }
1032                 catch(Exception ex) {
1033                     ProcessException(ex);
1034                 }
1035             }
1036
1037             // Check to see if it's a comment <%-- --%> block
1038
1039 #if PROFILE_REGEX
1040             else if ((match = RunCommentRegex(text, textPos)).Success)
1041 #else
1042             else if ((match = commentRegex.Match(text, textPos)).Success)
1043 #endif
1044             {
1045                 // Just skip it
1046             }
1047
1048             // Check to see if it's an expression code block (i.e. <%= ... %> block)
1049
1050             else if (!flags[inScriptTag] &&
1051 #if PROFILE_REGEX
1052                      (match = RunExprCodeBlockRegex(text, textPos)).Success)
1053 #else
1054                      (match = aspExprRegex.Match(text, textPos)).Success)
1055 #endif
1056             {
1057                 ProcessCodeBlock(match, CodeBlockType.Expression, text);
1058             }
1059
1060             // Check to see if it's an encoded expression code block (i.e. <%: ... %> block)
1061
1062             else if (!flags[inScriptTag] && (match = aspEncodedExprRegex.Match(text, textPos)).Success) {
1063                 ProcessCodeBlock(match, CodeBlockType.EncodedExpression, text);
1064             }
1065
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.
1069
1070             else if (!flags[inScriptTag] &&
1071                      (match = databindExprRegex.Match(text, textPos)).Success) {
1072                 ProcessCodeBlock(match, CodeBlockType.DataBinding, text);
1073             }
1074
1075             // Check to see if it's a code block (<% ... %>)
1076
1077             else if (!flags[inScriptTag] &&
1078 #if PROFILE_REGEX
1079                      (match = RunCodeBlockRegex(text, textPos)).Success)
1080 #else
1081                      (match = aspCodeRegex.Match(text, textPos)).Success)
1082 #endif
1083             {
1084                 string code = match.Groups["code"].Value.Trim();
1085                 if (code.StartsWith("$", StringComparison.Ordinal)) {
1086                     ProcessError(SR.GetString(SR.ExpressionBuilder_LiteralExpressionsNotAllowed, match.ToString(), code));
1087                 }
1088                 else {
1089                     ProcessCodeBlock(match, CodeBlockType.Code, text);
1090                 }
1091             }
1092
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)
1096
1097             else if (!flags[inScriptTag] &&
1098 #if PROFILE_REGEX
1099                      (match = RunTagRegex(text, textPos)).Success)
1100 #else
1101                      lastGTIndex > textPos && (match = tagRegex.Match(text, textPos)).Success)
1102 #endif
1103             {
1104                 try {
1105                     if (!ProcessBeginTag(match, text))
1106                         fMatchedButNotProcessed = true;
1107                 }
1108                 catch (Exception ex) {
1109                     ProcessException(ex);
1110                 }
1111             }
1112
1113             // Check to see if it's an end tag
1114
1115 #if PROFILE_REGEX
1116             else if ((match = RunEndTagRegex(text, textPos)).Success)
1117 #else
1118             else if ((match = endtagRegex.Match(text, textPos)).Success)
1119 #endif
1120             {
1121                 if (!ProcessEndTag(match))
1122                     fMatchedButNotProcessed = true;
1123             }
1124
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);
1131
1132                 // Skip the '<'
1133                 textPos++;
1134                 AddLiteral("<");
1135             }
1136             else {
1137                 _lineNumber += Util.LineCount(text, textPos,
1138                                          match.Index + match.Length);
1139                 textPos = match.Index + match.Length;
1140             }
1141
1142             // we might be done now
1143             if (textPos == text.Length)
1144                 break;
1145         }
1146
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;
1151
1152             ProcessError(SR.GetString(SR.Unexpected_eof_looking_for_tag, "script"));
1153             return;
1154         }
1155
1156         // Process the final literal (if any)
1157         ProcessLiteral();
1158     }
1159
1160     // Used for updatable deployment precompilation
1161     void CreateModifiedMainDirectiveFileIfNeeded(string text, Match match,
1162             ParsedAttributeCollection mainDirective, Encoding fileEncoding) {
1163
1164         TextWriter precompTargetWriter = BuildManager.GetUpdatableDeploymentTargetWriter(CurrentVirtualPath, fileEncoding);
1165
1166         // If we're not precompiling for deployment, there is nothing to do here
1167         if (precompTargetWriter == null)
1168             return;
1169
1170         using (precompTargetWriter) {
1171
1172             // Write out everything up to the main directive
1173             precompTargetWriter.Write(text.Substring(0, match.Index));
1174
1175             precompTargetWriter.Write("<%@ " + DefaultDirectiveName);
1176
1177             // Go through all the attributes on the main directive
1178             foreach (DictionaryEntry entry in mainDirective) {
1179
1180                 string attribName = (string) entry.Key;
1181                 string attribValue = (string) entry.Value;
1182
1183                 // Remove the codefile and CodeFileBaseClass attributes
1184                 if (StringUtil.EqualsIgnoreCase(attribName, "codefile")) continue;
1185                 if (StringUtil.EqualsIgnoreCase(attribName, CodeFileBaseClassAttributeName)) continue;
1186
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;
1192                 }
1193
1194                 precompTargetWriter.Write(" ");
1195                 precompTargetWriter.Write(attribName);
1196                 precompTargetWriter.Write("=\"");
1197                 precompTargetWriter.Write(attribValue);
1198                 precompTargetWriter.Write("\"");
1199             }
1200
1201             precompTargetWriter.Write(" %>");
1202
1203             // Write out everything after the main directive
1204             precompTargetWriter.Write(text.Substring(match.Index+match.Length));
1205         }
1206     }
1207
1208     /*
1209      * Do what needs to be done before returning after the parsing is complete
1210      */
1211     internal virtual void HandlePostParse() {
1212
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;
1217         }
1218
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));
1225             }
1226         }
1227
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();
1232
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));
1235
1236             return;
1237         }
1238
1239         // If no language was specified in the page
1240         if (_compilerType == null) {
1241
1242             if (!FInDesigner) {
1243                 // Get a default from config
1244                 _compilerType = CompilationUtil.GetDefaultLanguageCompilerInfo(
1245                     _compConfig, CurrentVirtualPath);
1246             }
1247             else {
1248                 // Get default from code
1249                 _compilerType = CompilationUtil.GetCodeDefaultLanguageCompilerInfo();
1250             }
1251         }
1252
1253         CompilerParameters compilParams = _compilerType.CompilerParameters;
1254
1255         // Override certain settings if they were specified on the page
1256         if (flags[hasDebugAttribute])
1257             compilParams.IncludeDebugInformation = flags[debug];
1258
1259         // Debugging requires medium trust level
1260         if (compilParams.IncludeDebugInformation)
1261             HttpRuntime.CheckAspNetHostingPermission(AspNetHostingPermissionLevel.Medium, SR.Debugging_not_supported_in_low_trust);
1262
1263         // If warningLevel was specified in the page, use it
1264         if (_warningLevel >= 0) {
1265             compilParams.WarningLevel = _warningLevel;
1266             compilParams.TreatWarningsAsErrors = (_warningLevel>0);
1267         }
1268         if (_compilerOptions != null)
1269             compilParams.CompilerOptions = _compilerOptions;
1270
1271         // Tell the filter (if any) that the parsing is complete
1272         if (_pageParserFilter != null)
1273             _pageParserFilter.ParseComplete(RootBuilder);
1274
1275         // Tell the ParseRecorders that parsing is complete
1276         ParseRecorders.ParseComplete(RootBuilder);
1277     }
1278
1279     /*
1280      * Process all the text in the literal StringBuilder, and reset it
1281      */
1282     private void ProcessLiteral() {
1283         // Debug.Trace("Template", "Literal text: \"" + _literalBuilder.ToString() + "\"");
1284
1285         // Get the current literal string
1286         string literal = GetLiteral();
1287
1288         // Nothing to do if it's empty
1289         if (String.IsNullOrEmpty(literal)) {
1290             flags[ignoreNextSpaceString] = false;
1291             return;
1292         }
1293
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);
1298
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);
1304
1305                 ProcessError(SR.GetString(SR.Invalid_app_file_content));
1306             }
1307         }
1308         else {
1309
1310             // Check if we should ignore the string (ASURT 8186)
1311             bool fIgnoreThisLiteral = false;
1312             if (flags[ignoreNextSpaceString]) {
1313                 flags[ignoreNextSpaceString] = false;
1314
1315                 if (Util.IsWhiteSpaceString(literal))
1316                     fIgnoreThisLiteral = true;
1317             }
1318
1319             if (!fIgnoreThisLiteral) {
1320
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;
1325                 }
1326
1327                 // Add it to the top builder
1328                 ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
1329                 try {
1330                     builder.AppendLiteralString(literal);
1331                 }
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);
1339
1340                         ProcessException(e);
1341                     }
1342                 }
1343
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");
1347             }
1348         }
1349
1350         // Reset the StringBuilder for the next literal
1351         _literalBuilder = null;
1352     }
1353
1354     /*
1355      * Process a server side SCRIPT tag
1356      */
1357     private void ProcessServerScript() {
1358         // Get the contents of the script tag
1359         string script = GetLiteral();
1360
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)
1366                 return;
1367
1368             script = String.Empty;
1369         }
1370
1371         // Add this script to the script builder, unless we're
1372         // supposed to ignore it
1373         if (!flags[ignoreScriptTag]) {
1374
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();
1379
1380                 _currentScript.Script = script;
1381                 _scriptList.Add(_currentScript);
1382                 _currentScript = null;
1383             }
1384         }
1385         // Reset the StringBuilder for the next literal
1386         _literalBuilder = null;
1387     }
1388
1389     internal virtual void CheckObjectTagScope(ref ObjectTagScope scope) {
1390
1391         // Map the default scope to Page
1392         if (scope == ObjectTagScope.Default)
1393             scope = ObjectTagScope.Page;
1394
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));
1399         }
1400     }
1401
1402     /*
1403      * Process an Object tag, depending on its scope
1404      */
1405     private void ProcessObjectTag(ObjectTagBuilder objectBuilder) {
1406
1407         ObjectTagScope scope = objectBuilder.Scope;
1408         CheckObjectTagScope(ref scope);
1409
1410         // Page and AppInstance are treated identically
1411         if (scope == ObjectTagScope.Page ||
1412             scope == ObjectTagScope.AppInstance) {
1413             if (_pageObjectList == null)
1414                 _pageObjectList = new ArrayList();
1415
1416             _pageObjectList.Add(objectBuilder);
1417         }
1418         else if (scope == ObjectTagScope.Session) {
1419             if (_sessionObjects == null)
1420                 _sessionObjects = new HttpStaticObjectsCollection();
1421
1422             _sessionObjects.Add(objectBuilder.ID,
1423                 objectBuilder.ObjectType,
1424                 objectBuilder.LateBound);
1425         }
1426         else if (scope == ObjectTagScope.Application) {
1427             if (_applicationObjects == null)
1428                 _applicationObjects = new HttpStaticObjectsCollection();
1429
1430             _applicationObjects.Add(objectBuilder.ID,
1431                 objectBuilder.ObjectType,
1432                 objectBuilder.LateBound);
1433         }
1434         else {
1435             Debug.Assert(false, "Unexpected scope!");
1436         }
1437     }
1438
1439     /*
1440      * Add a child builder to a builder
1441      */
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);
1446             return;
1447         }
1448
1449         builder.AppendSubBuilder(subBuilder);
1450     }
1451
1452     /*
1453      * Process an opening tag (possibly self-closed)
1454      */
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;
1459
1460         // Get all the attributes into a bag
1461         ParsedAttributeCollection attribs;
1462         string duplicateAttribute;
1463         ProcessAttributes(inputText, match, out attribs, false /*fDirective*/, out duplicateAttribute);
1464
1465         // Check if the tag is self closed
1466         bool fSelfClosed = match.Groups["empty"].Success;
1467
1468         // Is it a server side script tag?
1469         if (StringUtil.EqualsIgnoreCase(tagName, "script") && flags[isServerTag]) {
1470             ProcessScriptTag(match, inputText, attribs, fSelfClosed);
1471             return true;
1472         }
1473
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;
1478         }
1479
1480         ControlBuilder parentBuilder = null;
1481         ControlBuilder subBuilder = null;
1482         Type childType = null;
1483
1484         // This could be a property of an object that is filterable
1485         string realTagName;
1486         string filter = Util.ParsePropertyDeviceFilter(tagName, out realTagName);
1487
1488         // Check if the parent builder wants to create a subcontrol for this tag.
1489         if (BuilderStack.Count > 1) {
1490
1491             parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
1492
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)
1496                 return false;
1497
1498             subBuilder = parentBuilder.CreateChildBuilder(filter, realTagName, attribs, 
1499                 this, parentBuilder, _id, _lineNumber, CurrentVirtualPath, ref childType, false);
1500         }
1501
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);
1506         }
1507
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++;
1515         }
1516
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)
1521                 return false;
1522
1523             // If it was marked as runat=server, fail
1524             ProcessError(SR.GetString(SR.Unknown_server_tag, tagName));
1525             return true;
1526         }
1527
1528         // We have a server control
1529
1530         // If we have a control type filter, make sure the child control is allowed
1531         if (_pageParserFilter != null) {
1532             Debug.Assert(childType != null);
1533
1534             if (!_pageParserFilter.AllowControlInternal(childType, subBuilder)) {
1535                 ProcessError(SR.GetString(SR.Control_type_not_allowed, childType.FullName));
1536                 return true;
1537             }
1538         }
1539
1540         // Make sure it doesn't have duplicated attributes
1541         if (duplicateAttribute != null) {
1542             ProcessError(SR.GetString(SR.Duplicate_attr_in_tag, duplicateAttribute));
1543         }
1544
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;
1548
1549         // If it has an id, enforce validity and uniqueness
1550         if (_id != null) {
1551             if (!System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(_id)) {
1552                 ProcessError(SR.GetString(SR.Invalid_identifier, _id));
1553                 return true;
1554             }
1555
1556             if (_idList.Contains(_id)) {
1557                 ProcessError(SR.GetString(SR.Id_already_used, _id));
1558                 return true;
1559             }
1560
1561             _idList.Add(_id);
1562         }
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)];
1568
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;
1574                 _controlCount++;
1575                 // Controls can't be filtered, so use the default filter
1576                 subBuilder.PreprocessAttribute(String.Empty, "id", _id, false /*mainDirectiveMode*/);
1577             }
1578         }
1579
1580
1581         // Take care of the previous literal string
1582         ProcessLiteral();
1583
1584         if (childType != null) {
1585             // Update the hash code with the name of the control's type
1586             UpdateTypeHashCode(childType.FullName);
1587         }
1588
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()) {
1592
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();
1597             }
1598
1599             _builderStack.Push(new BuilderStackEntry(subBuilder, tagName,
1600                                CurrentVirtualPathString, _lineNumber,
1601                                inputText, match.Index + match.Length));
1602
1603             // Optionally record begin tag position data
1604             ParseRecorders.RecordBeginTag(subBuilder, match);
1605         }
1606         else {
1607             // Append the sub builder to the current builder
1608             parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
1609             AppendSubBuilder(parentBuilder, subBuilder);
1610
1611             // Tell the builder that we're done parsing its control
1612             subBuilder.CloseControl();
1613
1614             // Optionally record empty tag position data
1615             ParseRecorders.RecordEmptyTag(subBuilder, match);
1616         }
1617
1618         return true;
1619     }
1620
1621     /*
1622      * Process a <script runat=server> tag
1623      */
1624     private void ProcessScriptTag(Match match, string text, IDictionary attribs, bool fSelfClosed) {
1625         ProcessLiteral();
1626
1627         // Always ignore the spaces after a script tag
1628         flags[ignoreNextSpaceString] = true;
1629
1630         // Check if there is a 'src' attribute
1631         VirtualPath virtualPath = Util.GetAndRemoveVirtualPathAttribute(attribs, "src");
1632         if (virtualPath != null) {
1633
1634             // Make sure it's legal to have code in this page
1635             EnsureCodeAllowed();
1636
1637             // Get a full path to the script file
1638             virtualPath = ResolveVirtualPath(virtualPath);
1639
1640             // Make sure access to the file is allowed (VSWhidbey 195545)
1641             HttpRuntime.CheckVirtualFilePermission(virtualPath.VirtualPathString);
1642
1643             AddSourceDependency(virtualPath);
1644
1645             ProcessLanguageAttribute((string)attribs["language"]);
1646             _currentScript = new ScriptBlockData(1, 1, virtualPath.VirtualPathString);
1647
1648             _currentScript.Script = Util.StringFromVirtualPath(virtualPath);
1649
1650             // Add this script to the script builder
1651             _scriptList.Add(_currentScript);
1652             _currentScript = null;
1653
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)
1657             if (!fSelfClosed) {
1658                 flags[inScriptTag] = true;
1659                 _scriptStartLineNumber = _lineNumber;
1660                 flags[ignoreScriptTag] = true;
1661             }
1662
1663             return;
1664         }
1665
1666         ProcessLanguageAttribute((string)attribs["language"]);
1667
1668
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);
1672
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);
1677
1678         _currentScript = new ScriptBlockData(_lineNumber, column, CurrentVirtualPathString);
1679
1680         // No 'src' attribute.  Make sure tag is not self closed.
1681         if (fSelfClosed) {
1682             ProcessError(SR.GetString(SR.Script_tag_without_src_must_have_content));
1683         }
1684
1685         flags[inScriptTag] = true;
1686         _scriptStartLineNumber = _lineNumber;
1687     }
1688
1689     /*
1690      * Called when a '</' sequence is seen. This means we can start closing
1691      * tags.
1692      */
1693     private bool ProcessEndTag(Match match) {
1694         string tagName = match.Groups["tagname"].Value;
1695
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"))
1700                 return false;
1701
1702             ProcessServerScript();
1703
1704             flags[inScriptTag] = false;
1705             flags[ignoreScriptTag] = false;
1706
1707             return true;
1708         }
1709
1710         // See if anyone on the stack cares about termination.
1711         return MaybeTerminateControl(tagName, match);
1712     }
1713
1714     internal bool IsExpressionBuilderValue(string val) {
1715         return ControlBuilder.expressionBuilderRegex.Match(val, 0).Success;
1716     }
1717
1718
1719     internal abstract string DefaultDirectiveName { get; }
1720
1721     // If there is a parser filter, give it a chance to look at the directive
1722     internal void PreprocessDirective(string directiveName, IDictionary directive) {
1723
1724         // No parser filter: done
1725         if (_pageParserFilter == null)
1726             return;
1727
1728         if (directiveName.Length == 0)
1729             directiveName = DefaultDirectiveName;
1730
1731         _pageParserFilter.PreprocessDirective(directiveName, directive);
1732     }
1733
1734     /*
1735      * Process a <%@ %> block
1736      */
1737     internal virtual void ProcessDirective(string directiveName, IDictionary directive) {
1738
1739         // Check for the main directive, which is "page" for an aspx,
1740         // and "application" for global.asax
1741         if (directiveName.Length == 0) {
1742
1743             if (FInDesigner) {
1744                 return;
1745             }
1746
1747             // Make sure the main directive was not already specified
1748             if (flags[mainDirectiveSpecified]) {
1749                 ProcessError(SR.GetString(SR.Only_one_directive_allowed, DefaultDirectiveName));
1750
1751                 return;
1752             }
1753
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) {
1758
1759                     // If it was overridden, ignore the config setting
1760                     if (directive.Contains(entry.Key))
1761                         continue;
1762
1763                     // Add it to the list
1764                     directive[entry.Key] = entry.Value;
1765                 }
1766             }
1767
1768             ProcessMainDirective(directive);
1769
1770             flags[mainDirectiveSpecified] = true;
1771             flags[mainDirectiveHandled] = true;
1772         }
1773         else if (StringUtil.EqualsIgnoreCase(directiveName, "assembly")) {
1774             // Assembly directive
1775
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.
1778
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");
1782
1783             // If there are some attributes left, fail
1784             Util.CheckUnknownDirectiveAttributes(directiveName, directive);
1785
1786             if (assemblyName != null && src != null) {
1787                 ProcessError(SR.GetString(SR.Attributes_mutually_exclusive, "Name", "Src"));
1788             }
1789
1790             if (assemblyName != null) {
1791                 AddAssemblyDependency(assemblyName);
1792             }
1793             // Is it a source file that needs to be compiled on the fly
1794             else if (src != null) {
1795                 ImportSourceFile(src);
1796             }
1797             else {
1798                 ProcessError(SR.GetString(SR.Missing_attr, "name"));
1799             }
1800         }
1801         else if (StringUtil.EqualsIgnoreCase(directiveName, "import")) {
1802
1803             // Import directive
1804
1805             ProcessImportDirective(directiveName, directive);
1806         }
1807         else if (StringUtil.EqualsIgnoreCase(directiveName, "implements")) {
1808             // 'implements' directive
1809
1810             // We must compile the page if it asks to implement an interface
1811             OnFoundDirectiveRequiringCompilation(directiveName);
1812
1813             // Remove the attributes as we get them from the dictionary
1814             string interfaceName = Util.GetAndRemoveRequiredAttribute(directive, "interface");
1815
1816             // If there are some attributes left, fail
1817             Util.CheckUnknownDirectiveAttributes(directiveName, directive);
1818
1819             Type interfaceType = GetType(interfaceName);
1820
1821             // Make sure that it's an interface
1822             if (!interfaceType.IsInterface) {
1823                 ProcessError(SR.GetString(SR.Invalid_type_to_implement, interfaceName));
1824
1825                 return;
1826             }
1827
1828             // Add the interface type to the list
1829             if (_implementedInterfaces == null) {
1830                 _implementedInterfaces = new ArrayList();
1831             }
1832             _implementedInterfaces.Add(interfaceType);
1833         }
1834         else if (!FInDesigner) {
1835             ProcessError(SR.GetString(SR.Unknown_directive, directiveName));
1836         }
1837     }
1838
1839     internal virtual void ProcessMainDirective(IDictionary mainDirective) {
1840
1841         // Used to store some temporary data resulting from the parsing of the directive
1842         IDictionary parseData = new HybridDictionary();
1843
1844         // Keep track of unknown attributes
1845         ParsedAttributeCollection unknownAttributes = null;
1846
1847         // Go through all the attributes on the directive
1848         foreach (DictionaryEntry entry in mainDirective) {
1849
1850             string attribName = (string)entry.Key;
1851
1852             // Parse out the device name, if any
1853             string deviceName = Util.ParsePropertyDeviceFilter(attribName, out attribName);
1854
1855             try {
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();
1860                     }
1861
1862                     unknownAttributes.AddFilteredAttribute(deviceName, attribName, (string)entry.Value);
1863                 }
1864             }
1865             catch (Exception e) {
1866                 ProcessException(e);
1867             }
1868         }
1869
1870         // Allow some postprocessing to happen, in case attributes have dependencies
1871         // on each other (e.g. mutually exclusive attributes).
1872         PostProcessMainDirectiveAttributes(parseData);
1873
1874         // We should always set the control type of the root builder regardless of unknown attributes
1875         RootBuilder.SetControlType(BaseType);
1876
1877         // If we didn't have any unknown attributes, we're done
1878         if (unknownAttributes == null)
1879             return;
1880
1881         RootBuilder.ProcessImplicitResources(unknownAttributes);
1882
1883         // Process all the unknown attributes
1884         foreach (FilteredAttributeDictionary filteredAttributes in unknownAttributes.GetFilteredAttributeDictionaries()) {
1885             string filter = filteredAttributes.Filter;
1886
1887             foreach (DictionaryEntry attribute in filteredAttributes) {
1888                 string attribName = (string)attribute.Key;
1889                 ProcessUnknownMainDirectiveAttribute(filter, attribName, (string) attribute.Value);
1890             }
1891         }
1892     }
1893
1894     internal virtual bool ProcessMainDirectiveAttribute(string deviceName, string name,
1895         string value, IDictionary parseData) {
1896
1897         switch (name) {
1898         // Ignore description and codebehind attributes
1899         case "description":
1900         case "codebehind":
1901             break;
1902
1903         case "debug":
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"));
1907             }
1908
1909             flags[hasDebugAttribute] = true;
1910             break;
1911
1912         case "linepragmas":
1913             flags[noLinePragmas] = !Util.GetBooleanAttribute(name, value);
1914             break;
1915
1916         case "warninglevel":
1917             _warningLevel = Util.GetNonNegativeIntegerAttribute(name, value);
1918             break;
1919
1920         case "compileroptions":
1921             // This only makes sense for compiled pages
1922             OnFoundAttributeRequiringCompilation(name);
1923
1924             string compilerOptions = value.Trim();
1925
1926             CompilationUtil.CheckCompilerOptionsAllowed(compilerOptions, false /*config*/, null, 0);
1927
1928             _compilerOptions = compilerOptions;
1929             break;
1930
1931         // These two really only make sense in VB
1932         case "explicit":
1933             flags[useExplicit] = Util.GetBooleanAttribute(name, value);
1934             break;
1935         case "strict":
1936             flags[strict] = Util.GetBooleanAttribute(name, value);
1937             break;
1938
1939         case "language":
1940             ValidateBuiltInAttribute(deviceName, name, value);
1941             string language = Util.GetNonEmptyAttribute(name, value);
1942             ProcessLanguageAttribute(language);
1943             break;
1944
1945         // A "src" attribute is equivalent to an imported source file
1946         case "src":
1947             // This only makes sense for compiled pages
1948             OnFoundAttributeRequiringCompilation(name);
1949
1950             // Remember the src assembly for post processing
1951             parseData[name] = Util.GetNonEmptyAttribute(name, value);
1952             break;
1953
1954         case "inherits":
1955             // Remember the base class for post processing
1956             parseData[name] = Util.GetNonEmptyAttribute(name, value);
1957             break;
1958
1959         case "classname":
1960
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.
1963
1964             _generatedClassName = Util.GetNonEmptyFullClassNameAttribute(name, value,
1965                 ref _generatedNamespace);
1966             break;
1967
1968         case "codefile":
1969             // This only makes sense for compiled pages
1970             OnFoundAttributeRequiringCompilation(name);
1971
1972             try {
1973                 ProcessCodeFile(VirtualPath.Create(Util.GetNonEmptyAttribute(name, value)));
1974             }
1975             catch (Exception ex) {
1976                 ProcessException(ex);
1977             }
1978             break;
1979
1980         default:
1981             // We didn't handle the attribute
1982             return false;
1983         }
1984
1985         // The attribute was handled
1986
1987         // Make sure no device filter or resource expression was specified
1988         ValidateBuiltInAttribute(deviceName, name, value);
1989
1990         return true;
1991     }
1992
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);
1996
1997         if (IsExpressionBuilderValue(value)) {
1998             ProcessError(SR.GetString(SR.Illegal_Resource_Builder, name));
1999         }
2000
2001         if (deviceName.Length > 0) {
2002             ProcessError(SR.GetString(SR.Illegal_Device, name));
2003         }
2004     }
2005
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));
2011     }
2012
2013     internal virtual void PostProcessMainDirectiveAttributes(IDictionary parseData) {
2014
2015         // Post process the src and inherits attributes
2016
2017         string src = (string) parseData["src"];
2018         Assembly assembly = null;
2019         if (src != null) {
2020             try {
2021                 assembly = ImportSourceFile(VirtualPath.Create(src));
2022             }
2023             catch (Exception ex) {
2024                 ProcessException(ex);
2025             }
2026         }
2027
2028         // Was a code file base type specified in the directive
2029         string codeFileBaseTypeName = (string)parseData[CodeFileBaseClassAttributeName];
2030
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));
2034         }
2035
2036         // Was a base type specified in the directive
2037         string baseTypeName = (string) parseData["inherits"];
2038         if (baseTypeName != null) {
2039             try {
2040                 ProcessInheritsAttribute(baseTypeName, codeFileBaseTypeName, src, assembly);
2041             }
2042             catch (Exception ex) {
2043                 ProcessException(ex);
2044             }
2045         }
2046         else {
2047             if (_codeFileVirtualPath != null) {
2048                 throw new HttpException(SR.GetString(SR.Codefile_without_inherits));
2049             }
2050         }
2051     }
2052
2053     private void ProcessInheritsAttribute(string baseTypeName, string codeFileBaseTypeName,
2054         string src, Assembly assembly) {
2055
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);
2062
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;
2066
2067             if (baseTypeName == null)
2068                 return;
2069         }
2070
2071         Type baseType = null;
2072         if (assembly != null)
2073             baseType = assembly.GetType(baseTypeName, false /* throwOnError */, true /* caseInsensitive */);
2074         else {
2075             try {
2076                 baseType = GetType(baseTypeName);
2077             }
2078             catch {
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)
2081
2082                 // If the classname attribute doesn't have a namespace, give up
2083                 if (_generatedNamespace == null)
2084                     throw;
2085
2086                 // If the inherits attribute already had a namespace, don't try this
2087                 if (baseTypeName.IndexOf('.') >= 0)
2088                     throw;
2089
2090                 try {
2091                     // Try loading the inherit using the classname's namespace
2092                     string baseTypeNameWithNS = _generatedNamespace + "." + baseTypeName;
2093                     baseType = GetType(baseTypeNameWithNS);
2094                 }
2095                 catch {}
2096
2097                 // If that failed too, rethrow the original exception
2098                 if (baseType == null)
2099                     throw;
2100             }
2101         }
2102
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));
2107
2108             return;
2109         }
2110
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));
2115
2116             return;
2117         }
2118
2119         // If we have a control type filter, make sure the base type is allowed
2120         if (_pageParserFilter != null) {
2121
2122             if (!_pageParserFilter.AllowBaseType(baseType)) {
2123                 throw new HttpException(
2124                     SR.GetString(SR.Base_type_not_allowed, baseType.FullName));
2125             }
2126         }
2127
2128         _baseType = baseType;
2129
2130         // Now that we have the base type, we can create the RootBuilder
2131         Debug.Assert(_rootBuilder == null);
2132         EnsureRootBuilderCreated();
2133
2134         // Make sure we link with the assembly of the base type (ASURT 101778)
2135         AddTypeDependency(_baseType);
2136
2137         // Remember the fact that the page uses codebehind
2138         flags[hasCodeBehind] = true;
2139     }
2140
2141     private void ProcessImportDirective(string directiveName, IDictionary directive) {
2142
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.
2145
2146         // Remove the attributes as we get them from the dictionary
2147         string ns = Util.GetAndRemoveNonEmptyNoSpaceAttribute(directive, "namespace");
2148
2149         if (ns == null)
2150             ProcessError(SR.GetString(SR.Missing_attr, "namespace"));
2151         else
2152             AddImportEntry(ns);
2153
2154         // If there are some attributes left, fail
2155         Util.CheckUnknownDirectiveAttributes(directiveName, directive);
2156     }
2157
2158     /*
2159      * Process a language attribute, as can appear in the Page directive and in
2160      * <script runat=server> tags.
2161      */
2162     private void ProcessLanguageAttribute(string language) {
2163         if (language == null)
2164             return;
2165
2166         // We don't have CompilationConfig at design-time and the language attribute isn't used either.
2167         if (FInDesigner)
2168             return;
2169
2170         CompilerType compilerType = CompilationUtil.GetCompilerInfoFromLanguage(
2171             CurrentVirtualPath, language);
2172
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));
2177
2178             return;
2179         }
2180
2181         _compilerType = compilerType;
2182     }
2183
2184     /*
2185      * Process a compileFile attribute (aka code besides)
2186      */
2187     private void ProcessCodeFile(VirtualPath codeFileVirtualPath) {
2188
2189         Debug.Assert(_codeFileVirtualPath == null);
2190
2191         _codeFileVirtualPath = ResolveVirtualPath(codeFileVirtualPath);
2192
2193         // Get the language for the code beside page
2194         CompilerType compilerType = CompilationUtil.GetCompilerInfoFromVirtualPath(
2195             _codeFileVirtualPath);
2196
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));
2201
2202             return;
2203         }
2204
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);
2209
2210         // Make sure the file exists
2211         Util.CheckVirtualFileExists(_codeFileVirtualPath);
2212
2213         _compilerType = compilerType;
2214
2215         // Add the code file to the list of files we depend on
2216         AddSourceDependency(_codeFileVirtualPath);
2217     }
2218
2219     /*
2220      * Compile a source file into an assembly, and import it
2221      */
2222     private Assembly ImportSourceFile(VirtualPath virtualPath) {
2223
2224         // If it's a no-compile page, ignore the imported source file
2225         if (CompilationMode == CompilationMode.Never)
2226             return null;
2227
2228         // Get a full path to the source file
2229         virtualPath = ResolveVirtualPath(virtualPath);
2230
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));
2234         }
2235
2236         // Add the source file to the list of files we depend on
2237         AddSourceDependency(virtualPath);
2238
2239         // Compile it into an assembly
2240
2241         BuildResultCompiledAssembly result = BuildManager.GetVPathBuildResult(
2242             virtualPath) as BuildResultCompiledAssembly;
2243         if (result == null) {
2244             ProcessError(SR.GetString(SR.Not_a_src_file, virtualPath));
2245         }
2246
2247         Assembly a = result.ResultAssembly;
2248
2249         // Add a dependency to the assembly and its dependencies
2250         AddAssemblyDependency(a, true /*addDependentAssemblies*/);
2251
2252         return a;
2253     }
2254
2255     /*
2256      * If we could not match the '<' at all, check for some specific syntax
2257      * errors.
2258      */
2259     private void DetectSpecialServerTagError(string text, int textPos) {
2260
2261         if (IgnoreParseErrors) return;
2262
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));
2266
2267             return;
2268         }
2269
2270         // Search for the end of the tag ('>')
2271         Match match = gtRegex.Match(text, textPos);
2272
2273         // No match, return
2274         if (!match.Success)
2275             return;
2276
2277         // Get the complete potential tag
2278         string tag = text.Substring(textPos, match.Index-textPos+2);
2279
2280         // Check if it's a case of nested <% %> block in a server tag (ASURT 8714)
2281
2282         // If the tag does not contain runat=server, do nothing
2283         match = runatServerRegex.Match(tag);
2284         if (!match.Success)
2285             return;
2286
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)
2292             return;
2293
2294         System.Web.Util.Debug.Trace("Template", "Found malformed server tag: " + tag);
2295
2296         // Remove all <% %> constructs from within it.
2297         string tag2 = serverTagsRegex.Replace(tag, String.Empty);
2298
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));
2304
2305                 return;
2306             }
2307         }
2308
2309         // Give a more generic error (fixed 18969, 30312)
2310         ProcessError(SR.GetString(SR.Malformed_server_tag));
2311     }
2312
2313     /*
2314      * Add an entry to our list of NamespaceEntry's
2315      */
2316     internal void AddImportEntry(string ns) {
2317
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();
2322         else
2323             _namespaceEntries = new Hashtable();
2324
2325         NamespaceEntry namespaceEntry = new NamespaceEntry();
2326         namespaceEntry.Namespace = ns;
2327
2328         namespaceEntry.Line = _lineNumber;
2329         namespaceEntry.VirtualPath = CurrentVirtualPathString;
2330
2331         _namespaceEntries[ns] = namespaceEntry;
2332     }
2333
2334     internal Assembly LoadAssembly(string assemblyName, bool throwOnFail) {
2335
2336         if (_typeResolutionService != null) {
2337             AssemblyName asmName = new AssemblyName(assemblyName);
2338             return _typeResolutionService.GetAssembly(asmName, throwOnFail);
2339         }
2340
2341         return _compConfig.LoadAssembly(assemblyName, throwOnFail);
2342     }
2343
2344     internal Type GetType(string typeName, bool ignoreCase) {
2345         return GetType(typeName, ignoreCase, /* throwOnError */ true);
2346     }
2347
2348     /*
2349      * Look for a type by name in the assemblies that this page links with
2350      */
2351     internal Type GetType(string typeName, bool ignoreCase, bool throwOnError) {
2352
2353         // If it contains an assembly name, parse it out and load the assembly (ASURT 53589)
2354         Assembly a = null;
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();
2359
2360             try {
2361                 a = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
2362             }
2363             catch {
2364                 throw new HttpException(
2365                     SR.GetString(SR.Assembly_not_compiled, assemblyName));
2366             }
2367         }
2368
2369         // If we got an assembly, load the type from it
2370         if (a != null)
2371             return a.GetType(typeName, throwOnError, ignoreCase);
2372
2373         // Otherwise, look for the type in the referenced assemblies (given by the build system)
2374         Type t;
2375         t = Util.GetTypeFromAssemblies(_referencedAssemblies, typeName, ignoreCase);
2376         if (t != null)
2377             return t;
2378
2379         // Or in the assemblies that this page depends on
2380         t = Util.GetTypeFromAssemblies(AssemblyDependencies, typeName, ignoreCase);
2381         if (t != null)
2382             return t;
2383
2384         if (throwOnError) {
2385             throw new HttpException(
2386                 SR.GetString(SR.Invalid_type, typeName));
2387         }
2388
2389         return null;
2390     }
2391
2392     /*
2393      * Look for a type by name in the assemblies that this page links with
2394      */
2395     internal Type GetType(string typeName) {
2396         return GetType(typeName, false /*ignoreCase*/);
2397     }
2398
2399     /*
2400      * Process a server side include.  e.g. <!-- #include file="foo.inc" -->
2401      */
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));
2406         }
2407
2408         ProcessLiteral();
2409
2410         string pathType = match.Groups["pathtype"].Value;
2411         string filename = match.Groups["filename"].Value;
2412         //System.Web.Util.Debug.Trace("Template", "#Include " + pathType + "=" + filename);
2413
2414         if (filename.Length == 0) {
2415             ProcessError(SR.GetString(SR.Empty_file_name));
2416
2417             return;
2418         }
2419
2420         VirtualPath newVirtualPath = CurrentVirtualPath;
2421         string newPhysicalPath = null;
2422
2423         if (StringUtil.EqualsIgnoreCase(pathType, "file")) {
2424
2425             if (UrlPath.IsAbsolutePhysicalPath(filename)) {
2426                 // If it's an absolute physical path, use it as is
2427                 newPhysicalPath = filename;
2428             }
2429             else {
2430
2431                 // If it's relative, try to treat it as virtual
2432
2433                 bool treatAsVirtual = true;
2434
2435                 try {
2436                     newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
2437                 }
2438                 catch {
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;
2442                 }
2443
2444                 if (treatAsVirtual) {
2445                     HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
2446                     AddSourceDependency(newVirtualPath);
2447                 }
2448                 else {
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('/', '\\')));
2453                 }
2454             }
2455         }
2456         else if (StringUtil.EqualsIgnoreCase(pathType, "virtual")) {
2457             newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
2458             HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
2459             AddSourceDependency(newVirtualPath);
2460         }
2461         else {
2462             ProcessError(SR.GetString(SR.Only_file_virtual_supported_on_server_include));
2463
2464             return;
2465         }
2466
2467         if (newPhysicalPath != null) {
2468             // Make sure that access to the file is permitted (ASURT 73792,85467)
2469             HttpRuntime.CheckFilePermission(newPhysicalPath);
2470         }
2471
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));
2475         }
2476
2477         // Parse the included file recursively
2478         ParseFile(newPhysicalPath, newVirtualPath);
2479
2480         // Always ignore the spaces after an include directive
2481         flags[ignoreNextSpaceString] = true;
2482     }
2483
2484     private static char[] s_newlineChars = new char[] { '\r', '\n' };
2485
2486     /*
2487      *  Handle <%= ... %>, <%# ... %> and <% ... %> blocks
2488      */
2489     private void ProcessCodeBlock(Match match, CodeBlockType blockType, string text) {
2490
2491         // Take care of the previous literal string
2492         ProcessLiteral();
2493
2494         // Get the piece of code
2495         Group codeGroup = match.Groups["code"];
2496         string code = codeGroup.Value;
2497
2498         bool encode = match.Groups["encode"].Success;
2499
2500         // Replace "%\>" with "%>" (ASURT 7175)
2501         code = code.Replace(@"%\>", "%>");
2502
2503         int lineNumber = _lineNumber;
2504         int column = -1;
2505
2506         if (blockType != CodeBlockType.Code) {
2507
2508             // It a <%= %>, <%# %> or <%: %> block.  We need to do special handling of newline chars
2509
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'))) {
2516                     lineNumber++;
2517                     newlineIndex = i;
2518                 }
2519                 else if (code[i] == '\n') {
2520                     newlineIndex = i;
2521                 }
2522             }
2523
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);
2528
2529                 // The code starts at column 1 (since we keep the spaces)
2530                 column = 1;
2531             }
2532
2533             // Same deal for the end of the string: look for the first newline
2534             // after the last non-blank chararacter
2535             newlineIndex = -1;
2536             for (int i = code.Length - 1; i >= 0 && Char.IsWhiteSpace(code[i]); i--) {
2537                 if (code[i] == '\r' || code[i] == '\n')
2538                     newlineIndex = i;
2539             }
2540
2541             // And if we found one, remove it and everything after
2542             if (newlineIndex >= 0)
2543                 code = code.Substring(0, newlineIndex);
2544
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));
2550
2551                 return;
2552             }
2553         }
2554
2555         if (column < 0) {
2556
2557             // It a <% %> block.  Newline chars are not a problem.
2558
2559             // Look for the last newline before the code string
2560             int newlineIndex = text.LastIndexOfAny(s_newlineChars, codeGroup.Index-1);
2561
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);
2566         }
2567
2568         ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
2569         ControlBuilder subBuilder;
2570
2571         // First, give the PageParserFilter a chance to handle the code block
2572         if (!PageParserFilterProcessedCodeBlock(CodeConstructTypeFromCodeBlockType(blockType), code, lineNumber)) {
2573
2574             // Make sure it's legal to have code in this page
2575             EnsureCodeAllowed();
2576
2577             // Add the code block to the top builder
2578             subBuilder = new CodeBlockBuilder(blockType, code, lineNumber, column, CurrentVirtualPath, encode);
2579
2580             AppendSubBuilder(builder, subBuilder);
2581
2582             // Optionally record code block position data
2583             ParseRecorders.RecordCodeBlock(subBuilder, match);
2584         }
2585
2586         // Always ignore the spaces after a <% ... %> block
2587         if (blockType == CodeBlockType.Code)
2588             flags[ignoreNextSpaceString] = true;
2589     }
2590
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;
2602             default:
2603                 Debug.Assert(false);
2604                 return CodeConstructType.CodeSnippet;
2605         }
2606     }
2607
2608     private bool PageParserFilterProcessedCodeBlock(CodeConstructType codeConstructType,
2609         string code, int lineNumber) {
2610
2611         // This requires a PageParserFilter, and CompilationMode must allow it
2612         if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2613             return false;
2614
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;
2619         try {
2620             // Ask the PageParserFilter whether it wants to process it
2621             return _pageParserFilter.ProcessCodeConstruct(codeConstructType, code);
2622         }
2623         finally {
2624             _lineNumber = restoreLineNumber;
2625         }
2626     }
2627
2628     internal bool PageParserFilterProcessedDataBindingAttribute(string controlId, string attributeName,
2629         string code) {
2630
2631         // This requires a PageParserFilter, and CompilationMode must allow it
2632         if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2633             return false;
2634
2635         // Ask the PageParserFilter whether it wants to process it
2636         return _pageParserFilter.ProcessDataBindingAttribute(controlId, attributeName, code);
2637     }
2638
2639     internal bool PageParserFilterProcessedEventHookupAttribute(string controlId, string eventName,
2640         string handlerName) {
2641
2642         // This requires a PageParserFilter, and CompilationMode must allow it
2643         if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
2644             return false;
2645
2646         // Ask the PageParserFilter whether it wants to process it
2647         return _pageParserFilter.ProcessEventHookup(controlId, eventName, handlerName); 
2648     }
2649
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;
2653
2654         ControlBuilder subBuilder = ControlBuilder.CreateBuilderFromType(this, parentBuilder,
2655             type, null, null, attributes, _lineNumber,
2656             CurrentVirtualPath.VirtualPathString);
2657
2658         AppendSubBuilder(parentBuilder, subBuilder);
2659     }
2660
2661     /*
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")
2666      */
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;
2674         if (fDirective)
2675             equalsign = match.Groups["equal"].Captures;
2676
2677         flags[isServerTag] = false;
2678         _id = null;
2679
2680         duplicateAttribute = null;
2681
2682         for (int i = 0; i < attrnames.Count; i++) {
2683             string attribName = attrnames[i].ToString();
2684
2685             // If processing a directive, set the attribute name to lower case for easier processing later
2686             if (fDirective)
2687                 attribName = attribName.ToLower(CultureInfo.InvariantCulture);
2688
2689             Capture attrValue = attrvalues[i];
2690             string attribValue = attrValue.ToString();
2691
2692             // Any of the attributes could be filtered
2693             string realAttributeName = String.Empty;
2694             string filter = Util.ParsePropertyDeviceFilter(attribName, out realAttributeName);
2695
2696             // Always HTML decode all attributes (ASURT 54544)
2697             attribValue = HttpUtility.HtmlDecode(attribValue);
2698
2699             // If we're parsing a directive, check if there is an equal sign.
2700             bool fHasEqual = false;
2701             if (fDirective)
2702                 fHasEqual = (equalsign[i].ToString().Length > 0);
2703
2704             // If this is a server ID, remember it
2705             // 
2706
2707             if (StringUtil.EqualsIgnoreCase(realAttributeName, "id")) {
2708                 _id = attribValue;
2709             }
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);
2713
2714                 // Only runat=server is valid
2715                 if (!StringUtil.EqualsIgnoreCase(attribValue, "server")) {
2716                     ProcessError(SR.GetString(SR.Runat_can_only_be_server));
2717                 }
2718
2719                 // Set a flag if we see runat=server
2720                 flags[isServerTag] = true;
2721                 attribName = null;       // Don't put it in attribute bag
2722             }
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.
2726                 attribName = null;
2727             }
2728
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;
2739                     }
2740                     continue;
2741                 }
2742
2743                 try {
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));
2747
2748                         continue;
2749                     }
2750
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);
2760                     }
2761                 }
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;
2766                 }
2767                 catch (Exception ex) {
2768                     ProcessException(ex);
2769                 }
2770             }
2771         }
2772
2773         if (duplicateAttribute != null && fDirective) {
2774             ProcessError(SR.GetString(SR.Duplicate_attr_in_directive, duplicateAttribute));
2775         }
2776
2777         return directiveName;
2778     }
2779
2780     private static ParsedAttributeCollection CreateEmptyAttributeBag() {
2781         // use a ParsedAttributeCollection to preserve the order and store filtered information
2782         return new ParsedAttributeCollection();
2783     }
2784
2785     private bool MaybeTerminateControl(string tagName, Match match) {
2786
2787         BuilderStackEntry stackEntry = (BuilderStackEntry) BuilderStack.Peek();
2788         ControlBuilder builder = stackEntry._builder;
2789
2790         // If the tag doesn't match, return false
2791         if (stackEntry._tagName == null || !StringUtil.EqualsIgnoreCase(stackEntry._tagName, tagName)) {
2792             return false;
2793         }
2794
2795         // If the repeat count is non-zero, just decrease it
2796         if (stackEntry._repeatCount > 0) {
2797             stackEntry._repeatCount--;
2798             return false;
2799         }
2800
2801         // Take care of the previous literal string
2802         ProcessLiteral();
2803
2804         // If the builder wants the raw text of the tag, give it to it
2805         if (builder.NeedsTagInnerText()) {
2806             try {
2807                 builder.SetTagInnerText(stackEntry._inputText.Substring(
2808                       stackEntry._textPos,
2809                       match.Index - stackEntry._textPos));
2810             }
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);
2816
2817                     return true;
2818                 }
2819             }
2820         }
2821
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();
2825
2826         // Pop the top entry from the stack
2827         _builderStack.Pop();
2828
2829         // Give the builder to its parent
2830         AppendSubBuilder(((BuilderStackEntry) _builderStack.Peek())._builder, builder);
2831
2832         // Tell the builder that we're done parsing its control
2833         builder.CloseControl();
2834
2835         // Optionally record end tag position data
2836         ParseRecorders.RecordEndTag(builder, match);
2837
2838         return true;
2839     }
2840
2841     /*
2842      * Map a type name to a Type.
2843      */
2844     internal Type MapStringToType(string typeName, IDictionary attribs) {
2845         return RootBuilder.GetChildControlType(typeName, attribs);
2846     }
2847
2848     /*
2849      * Add a file as a dependency of the file we're parsing
2850      */
2851     internal void AddSourceDependency(VirtualPath fileName) {
2852
2853         // Tell the filter that a dependency was added
2854         if (_pageParserFilter != null) {
2855             _pageParserFilter.OnDependencyAdded();
2856             _pageParserFilter.OnDirectDependencyAdded();
2857         }
2858
2859         AddSourceDependency2(fileName);
2860     }
2861
2862     /*
2863      * Add a file as a dependency of the file we're parsing
2864      */
2865     private void AddSourceDependency2(VirtualPath fileName) {
2866         if (_sourceDependencies == null)
2867             _sourceDependencies = new CaseInsensitiveStringSet();
2868
2869         _sourceDependencies.Add(fileName.VirtualPathString);
2870     }
2871
2872     /*
2873      * Add a BuildResult's source dependencies to our own source dependencies
2874      */
2875     internal void AddBuildResultDependency(BuildResult result) {
2876
2877         // Add one direct dependency
2878         if (_pageParserFilter != null)
2879             _pageParserFilter.OnDirectDependencyAdded();
2880
2881         if (result.VirtualPathDependencies == null)
2882             return;
2883
2884         foreach (string virtualPath in result.VirtualPathDependencies) {
2885
2886             // Add one dependency for each file (to include direct and indirect)
2887             if (_pageParserFilter != null)
2888                 _pageParserFilter.OnDependencyAdded();
2889
2890             AddSourceDependency2(VirtualPath.Create(virtualPath));
2891         }
2892     }
2893
2894     /*
2895      * Add a type that we must 'link' with in order to build
2896      */
2897     internal void AddTypeDependency(Type type) {
2898         // We must link with all the types in the inheritance hierarchy (ASURT 83509)
2899         AddBaseTypeDependencies(type);
2900
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);
2905     }
2906
2907     /*
2908      * Add as dependencies all the assembly in the inheritance chain of a Type,
2909      * including interfaces.
2910      */
2911     private void AddBaseTypeDependencies(Type type) {
2912         Assembly a = type.Module.Assembly;
2913
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)
2916             return;
2917
2918         AddAssemblyDependency(a);
2919
2920         // Recurse on the base Type
2921         if (type.BaseType != null)
2922             AddBaseTypeDependencies(type.BaseType);
2923
2924         // Recurse on all the implemented interfaces
2925         Type[] interfaceTypes = type.GetInterfaces();
2926         foreach (Type interfaceType in interfaceTypes)
2927             AddBaseTypeDependencies(interfaceType);
2928     }
2929
2930     /*
2931      * Add an assembly that we must 'link' with in order to build
2932      */
2933     internal Assembly AddAssemblyDependency(string assemblyName, bool addDependentAssemblies) {
2934
2935         Assembly assembly = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
2936
2937         if (assembly != null)
2938             AddAssemblyDependency(assembly, addDependentAssemblies);
2939
2940         return assembly;
2941     }
2942     internal Assembly AddAssemblyDependency(string assemblyName) {
2943         return AddAssemblyDependency(assemblyName, false /*addDependentAssemblies*/);
2944     }
2945
2946     /*
2947      * Add an assembly that we must 'link' with in order to build
2948      */
2949     internal void AddAssemblyDependency(Assembly assembly, bool addDependentAssemblies) {
2950         if (_assemblyDependencies == null)
2951             _assemblyDependencies = new AssemblySet();
2952
2953         if (_typeResolutionService != null)
2954             _typeResolutionService.ReferenceAssembly(assembly.GetName());
2955
2956         _assemblyDependencies.Add(assembly);
2957
2958         // If addDependentAssemblies is true, add its dependent assemblies as well
2959         if (addDependentAssemblies) {
2960             AssemblySet assemblyDependencies = Util.GetReferencedAssemblies(assembly);
2961             AddAssemblyDependencies(assemblyDependencies);
2962         }
2963     }
2964     internal void AddAssemblyDependency(Assembly assembly) {
2965         AddAssemblyDependency(assembly, false /*addDependentAssemblies*/);
2966     }
2967
2968     /*
2969      * Add a set of assemblies that we must 'link' with in order to build
2970      */
2971     private void AddAssemblyDependencies(AssemblySet assemblyDependencies) {
2972         if (assemblyDependencies == null)
2973             return;
2974
2975         foreach (Assembly a in assemblyDependencies)
2976             AddAssemblyDependency(a);
2977     }
2978
2979
2980     /// <internalonly/>
2981     ICollection IAssemblyDependencyParser.AssemblyDependencies {
2982         get {
2983             return AssemblyDependencies;
2984         }
2985     }
2986
2987     internal IImplicitResourceProvider GetImplicitResourceProvider() {
2988
2989         // 
2990         if (FInDesigner)
2991             return null;
2992
2993         // If we already attempted to get them, return whatever we got
2994         if (flags[attemptedImplicitResources])
2995             return _implicitResourceProvider;
2996
2997         flags[attemptedImplicitResources] = true;
2998
2999         IResourceProvider resourceProvider = ResourceExpressionBuilder.GetLocalResourceProvider(_rootBuilder.VirtualPath);
3000         if (resourceProvider == null)
3001             return null;
3002
3003         // If the resource provider is also an IImplicitResourceProvider, use that
3004         _implicitResourceProvider = resourceProvider as IImplicitResourceProvider;
3005
3006         // Otherwise, use the default IImplicitResourceProvider implementation
3007         if (_implicitResourceProvider == null)
3008             _implicitResourceProvider = new DefaultImplicitResourceProvider(resourceProvider);
3009
3010         return _implicitResourceProvider;
3011     }
3012 }
3013
3014 /*
3015  * Base class for classes that contain source file & line information for error reporting
3016  */
3017 internal abstract class SourceLineInfo {
3018
3019     // Source file where the information appears
3020     private string _virtualPath;
3021     internal string VirtualPath {
3022         get { return _virtualPath;}
3023         set { _virtualPath = value;}
3024     }
3025
3026     // Line number in the source file where the information appears
3027     private int _line;
3028     internal int Line {
3029         get { return _line;}
3030         set { _line = value;}
3031     }
3032 }
3033
3034
3035 /*
3036  * Objects that are placed on the BuilderStack
3037  */
3038 internal class BuilderStackEntry: SourceLineInfo {
3039     internal BuilderStackEntry (ControlBuilder builder,
3040                        string tagName, string virtualPath, int line,
3041                        string inputText, int textPos) {
3042
3043         _builder = builder;
3044         _tagName = tagName;
3045         VirtualPath = virtualPath;
3046         Line = line;
3047         _inputText = inputText;
3048         _textPos = textPos;
3049     }
3050
3051     internal ControlBuilder _builder;
3052     internal string _tagName;
3053
3054     // the input string that contains the tag
3055     internal string _inputText;
3056
3057     // Offset in the input string of the beginning of the tag's contents
3058     internal int _textPos;
3059
3060     // Used to deal with non server tags nested in server tag with the same name
3061     internal int _repeatCount;
3062 }
3063
3064
3065 /*
3066  * Entry representing an import directive.
3067  * e.g. <%@ import namespace="System.Web.UI" %>
3068  */
3069 internal class NamespaceEntry: SourceLineInfo {
3070     private string _namespace;
3071
3072     internal NamespaceEntry() {
3073     }
3074
3075     internal string Namespace {
3076         get { return _namespace;}
3077         set { _namespace = value;}
3078     }
3079 }
3080
3081 internal class ScriptBlockData: SourceLineInfo {
3082     protected string _script;
3083
3084     internal ScriptBlockData(int line, int column, string virtualPath) {
3085         Line = line;
3086         Column = column;
3087         VirtualPath = virtualPath;
3088     }
3089
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;}
3095     }
3096
3097     internal string Script {
3098         get { return _script;}
3099         set { _script = value;}
3100     }
3101 }
3102 }