Removed 1.x code and NET_2_0 ifdefs
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateParser.cs
1 //
2 // System.Web.UI.TemplateParser
3 //
4 // Authors:
5 //      Duncan Mak (duncan@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Marek Habersack (mhabersack@novell.com)
8 //
9 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.CodeDom.Compiler;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.ComponentModel;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Security.Permissions;
40 using System.Text;
41 using System.Threading;
42 using System.Web.Compilation;
43 using System.Web.Configuration;
44 using System.Web.Hosting;
45 using System.Web.Util;
46
47 namespace System.Web.UI {
48         internal class ServerSideScript
49         {
50                 public readonly string Script;
51                 public readonly ILocation Location;
52                 
53                 public ServerSideScript (string script, ILocation location)
54                 {
55                         Script = script;
56                         Location = location;
57                 }
58         }
59         
60         // CAS
61         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
62         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
63         public abstract class TemplateParser : BaseParser
64         {
65                 [Flags]
66                 internal enum OutputCacheParsedParams
67                 {
68                         Location               = 0x0001,
69                         CacheProfile           = 0x0002,
70                         NoStore                = 0x0004,
71                         SqlDependency          = 0x0008,
72                         VaryByCustom           = 0x0010,
73                         VaryByHeader           = 0x0020,
74                         VaryByControl          = 0x0040,
75                         VaryByContentEncodings = 0x0080
76                 }
77                 
78                 string inputFile;
79                 string text;
80                 Hashtable mainAttributes;
81                 ArrayList dependencies;
82                 ArrayList assemblies;
83                 Hashtable anames;
84                 string[] binDirAssemblies;
85                 Dictionary <string, bool> namespacesCache;
86                 Dictionary <string, bool> imports;
87                 List <string> interfaces;
88                 List <ServerSideScript> scripts;
89                 Type baseType;
90                 bool baseTypeIsGlobal = true;
91                 string className;
92                 RootBuilder rootBuilder;
93                 bool debug;
94                 string compilerOptions;
95                 string language;
96                 bool implicitLanguage;
97                 bool strictOn ;
98                 bool explicitOn;
99                 bool linePragmasOn = true;
100                 bool output_cache;
101                 int oc_duration;
102                 string oc_header, oc_custom, oc_param, oc_controls;
103                 string oc_content_encodings, oc_cacheprofile, oc_sqldependency;
104                 bool oc_nostore;
105                 OutputCacheParsedParams oc_parsed_params = 0;
106                 bool oc_shared;
107                 OutputCacheLocation oc_location;
108
109                 // Kludge needed to support pre-parsing of the main directive (see
110                 // AspNetGenerator.GetRootBuilderType)
111                 internal int allowedMainDirectives = 0;
112                 
113                 byte[] md5checksum;
114                 string src;
115                 bool srcIsLegacy;
116                 string partialClassName;
117                 string codeFileBaseClass;
118                 string metaResourceKey;
119                 Type codeFileBaseClassType;
120                 Type pageParserFilterType;
121                 PageParserFilter pageParserFilter;
122                 
123                 List <UnknownAttributeDescriptor> unknownMainAttributes;
124                 Stack <string> includeDirs;
125                 List <string> registeredTagNames;
126                 ILocation directiveLocation;
127                 
128                 int appAssemblyIndex = -1;
129
130                 internal TemplateParser ()
131                 {
132                         imports = new Dictionary <string, bool> (StringComparer.Ordinal);
133                         LoadConfigDefaults ();
134                         assemblies = new ArrayList ();
135                         CompilationSection compConfig = CompilationConfig;
136                         foreach (AssemblyInfo info in compConfig.Assemblies) {
137                                 if (info.Assembly != "*")
138                                         AddAssemblyByName (info.Assembly);
139                         }
140
141                         language = compConfig.DefaultLanguage;
142                         implicitLanguage = true;
143                 }
144
145                 internal virtual void LoadConfigDefaults ()
146                 {
147                         AddNamespaces (imports);
148                         debug = CompilationConfig.Debug;
149                 }
150                 
151                 internal void AddApplicationAssembly ()
152                 {
153                         if (Context.ApplicationInstance == null)
154                                 return; // this may happen if we have Global.asax and have
155                                         // controls registered from Web.Config
156                         string location = Context.ApplicationInstance.AssemblyLocation;
157                         if (location != typeof (TemplateParser).Assembly.Location) {
158                                 appAssemblyIndex = assemblies.Add (location);
159                         }
160                 }
161
162                 protected abstract Type CompileIntoType ();
163
164                 internal void AddControl (Type type, IDictionary attributes)
165                 {
166                         AspGenerator generator = AspGenerator;
167                         if (generator == null)
168                                 return;
169                         generator.AddControl (type, attributes);
170                 }
171                 
172                 void AddNamespaces (Dictionary <string, bool> imports)
173                 {
174                         if (BuildManager.HaveResources)
175                                 imports.Add ("System.Resources", true);
176                         
177                         PagesSection pages = PagesConfig;
178                         if (pages == null)
179                                 return;
180
181                         NamespaceCollection namespaces = pages.Namespaces;
182                         if (namespaces == null || namespaces.Count == 0)
183                                 return;
184                         
185                         foreach (NamespaceInfo nsi in namespaces) {
186                                 string ns = nsi.Namespace;
187                                 if (imports.ContainsKey (ns))
188                                         continue;
189                                 
190                                 imports.Add (ns, true);
191                         }
192                 }
193                 
194                 internal void RegisterCustomControl (string tagPrefix, string tagName, string src)
195                 {
196                         string realpath = null;
197                         bool fileExists = false;
198                         VirtualFile vf = null;
199                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
200                         VirtualPath vp = new VirtualPath (src, BaseVirtualDir);
201                         string vpAbsolute = vp.Absolute;
202                         
203                         if (vpp.FileExists (vpAbsolute)) {
204                                 fileExists = true;
205                                 vf = vpp.GetFile (vpAbsolute);
206                                 if (vf != null)
207                                         realpath = MapPath (vf.VirtualPath);
208                         }
209
210                         if (!fileExists)
211                                 ThrowParseFileNotFound (src);
212
213                         if (String.Compare (realpath, inputFile, false, Helpers.InvariantCulture) == 0)
214                                 return;
215                         
216                         string vpath = vf.VirtualPath;
217                         
218                         try {
219                                 RegisterTagName (tagPrefix + ":" + tagName);
220                                 RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, vpath);
221                                 AddDependency (vpath);
222                         } catch (ParseException pe) {
223                                 if (this is UserControlParser)
224                                         throw new ParseException (Location, pe.Message, pe);
225                                 throw;
226                         }
227                 }
228
229                 internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
230                 {
231                         AddImport (ns);
232                         Assembly ass = null;
233                         
234                         if (assembly != null && assembly.Length > 0)
235                                 ass = AddAssemblyByName (assembly);
236                         
237                         RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
238                 }
239
240                 internal virtual void HandleOptions (object obj)
241                 {
242                 }
243
244                 internal static string GetOneKey (Hashtable tbl)
245                 {
246                         foreach (object key in tbl.Keys)
247                                 return key.ToString ();
248
249                         return null;
250                 }
251                 
252                 internal virtual void AddDirective (string directive, Hashtable atts)
253                 {
254                         var pageParserFilter = PageParserFilter;
255                         if (String.Compare (directive, DefaultDirectiveName, true, Helpers.InvariantCulture) == 0) {
256                                 bool allowMainDirective = allowedMainDirectives > 0;
257                                 
258                                 if (mainAttributes != null && !allowMainDirective)
259                                         ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
260
261                                 allowedMainDirectives--;
262                                 if (mainAttributes != null)
263                                         return;
264                                 
265                                 if (pageParserFilter != null)
266                                         pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
267                                 
268                                 mainAttributes = atts;
269                                 ProcessMainAttributes (mainAttributes);
270                                 return;
271                         } else if (pageParserFilter != null)
272                                 pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
273                                 
274                         int cmp = String.Compare ("Assembly", directive, true, Helpers.InvariantCulture);
275                         if (cmp == 0) {
276                                 string name = GetString (atts, "Name", null);
277                                 string src = GetString (atts, "Src", null);
278
279                                 if (atts.Count > 0)
280                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
281
282                                 if (name == null && src == null)
283                                         ThrowParseException ("You gotta specify Src or Name");
284                                         
285                                 if (name != null && src != null)
286                                         ThrowParseException ("Src and Name cannot be used together");
287
288                                 if (name != null) {
289                                         AddAssemblyByName (name);
290                                 } else {
291                                         GetAssemblyFromSource (src);
292                                 }
293
294                                 return;
295                         }
296
297                         cmp = String.Compare ("Import", directive, true, Helpers.InvariantCulture);
298                         if (cmp == 0) {
299                                 string namesp = GetString (atts, "Namespace", null);
300                                 if (atts.Count > 0)
301                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
302                                 
303                                 AddImport (namesp);
304                                 return;
305                         }
306
307                         cmp = String.Compare ("Implements", directive, true, Helpers.InvariantCulture);
308                         if (cmp == 0) {
309                                 string ifacename = GetString (atts, "Interface", "");
310
311                                 if (atts.Count > 0)
312                                         ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
313                                 
314                                 Type iface = LoadType (ifacename);
315                                 if (iface == null)
316                                         ThrowParseException ("Cannot find type " + ifacename);
317
318                                 if (!iface.IsInterface)
319                                         ThrowParseException (iface + " is not an interface");
320
321                                 AddInterface (iface.FullName);
322                                 return;
323                         }
324
325                         cmp = String.Compare ("OutputCache", directive, true, Helpers.InvariantCulture);
326                         if (cmp == 0) {
327                                 HttpResponse response = HttpContext.Current.Response;
328                                 if (response != null)
329                                         response.Cache.SetValidUntilExpires (true);
330                                 
331                                 output_cache = true;
332                                 
333                                 if (atts ["Duration"] == null)
334                                         ThrowParseException ("The directive is missing a 'duration' attribute.");
335                                 if (atts ["VaryByParam"] == null && atts ["VaryByControl"] == null)
336                                         ThrowParseException ("This directive is missing 'VaryByParam' " +
337                                                         "or 'VaryByControl' attribute, which should be set to \"none\", \"*\", " +
338                                                         "or a list of name/value pairs.");
339
340                                 foreach (DictionaryEntry entry in atts) {
341                                         string key = (string) entry.Key;
342                                         if (key == null)
343                                                 continue;
344                                         
345                                         switch (key.ToLower (Helpers.InvariantCulture)) {
346                                                 case "duration":
347                                                         oc_duration = Int32.Parse ((string) entry.Value);
348                                                         if (oc_duration < 1)
349                                                                 ThrowParseException ("The 'duration' attribute must be set " +
350                                                                                      "to a positive integer value");
351                                                         break;
352
353                                                 case "sqldependency":
354                                                         oc_sqldependency = (string) entry.Value;
355                                                         break;
356                                                         
357                                                 case "nostore":
358                                                         try {
359                                                                 oc_nostore = Boolean.Parse ((string) entry.Value);
360                                                                 oc_parsed_params |= OutputCacheParsedParams.NoStore;
361                                                         } catch {
362                                                                 ThrowParseException ("The 'NoStore' attribute is case sensitive" +
363                                                                                      " and must be set to 'true' or 'false'.");
364                                                         }
365                                                         break;
366
367                                                 case "cacheprofile":
368                                                         oc_cacheprofile = (string) entry.Value;
369                                                         oc_parsed_params |= OutputCacheParsedParams.CacheProfile;
370                                                         break;
371                                                         
372                                                 case "varybycontentencodings":
373                                                         oc_content_encodings = (string) entry.Value;
374                                                         oc_parsed_params |= OutputCacheParsedParams.VaryByContentEncodings;
375                                                         break;
376
377                                                 case "varybyparam":
378                                                         oc_param = (string) entry.Value;
379                                                         if (String.Compare (oc_param, "none", true, Helpers.InvariantCulture) == 0)
380                                                                 oc_param = null;
381                                                         break;
382                                                 case "varybyheader":
383                                                         oc_header = (string) entry.Value;
384                                                         oc_parsed_params |= OutputCacheParsedParams.VaryByHeader;
385                                                         break;
386                                                 case "varybycustom":
387                                                         oc_custom = (string) entry.Value;
388                                                         oc_parsed_params |= OutputCacheParsedParams.VaryByCustom;
389                                                         break;
390                                                 case "location":
391                                                         if (!(this is PageParser))
392                                                                 goto default;
393                                                 
394                                                         try {
395                                                                 oc_location = (OutputCacheLocation) Enum.Parse (
396                                                                         typeof (OutputCacheLocation), (string) entry.Value, true);
397                                                                 oc_parsed_params |= OutputCacheParsedParams.Location;
398                                                         } catch {
399                                                                 ThrowParseException ("The 'location' attribute is case sensitive and " +
400                                                                                      "must be one of the following values: Any, Client, " +
401                                                                                      "Downstream, Server, None, ServerAndClient.");
402                                                         }
403                                                         break;
404                                                 case "varybycontrol":
405                                                         oc_controls = (string) entry.Value;
406                                                         oc_parsed_params |= OutputCacheParsedParams.VaryByControl;
407                                                         break;
408                                                 case "shared":
409                                                         if (this is PageParser)
410                                                                 goto default;
411
412                                                         try {
413                                                                 oc_shared = Boolean.Parse ((string) entry.Value);
414                                                         } catch {
415                                                                 ThrowParseException ("The 'shared' attribute is case sensitive" +
416                                                                                      " and must be set to 'true' or 'false'.");
417                                                         }
418                                                         break;
419                                                 default:
420                                                         ThrowParseException ("The '" + key + "' attribute is not " +
421                                                                              "supported by the 'Outputcache' directive.");
422                                                         break;
423                                         }
424                                         
425                                 }
426                                 
427                                 return;
428                         }
429
430                         ThrowParseException ("Unknown directive: " + directive);
431                 }
432
433                 internal Type LoadType (string typeName)
434                 {
435                         Type type = HttpApplication.LoadType (typeName);
436                         if (type == null)
437                                 return null;
438                         Assembly asm = type.Assembly;
439                         string location = asm.Location;
440                         
441                         string dirname = Path.GetDirectoryName (location);
442                         bool doAddAssembly = true;
443                         if (dirname == HttpApplication.BinDirectory)
444                                 doAddAssembly = false;
445
446                         if (doAddAssembly)
447                                 AddAssembly (asm, true);
448
449                         return type;
450                 }
451
452                 internal virtual void AddInterface (string iface)
453                 {
454                         if (interfaces == null)
455                                 interfaces = new List <string> ();
456
457                         if (!interfaces.Contains (iface))
458                                 interfaces.Add (iface);
459                 }
460                 
461                 internal virtual void AddImport (string namesp)
462                 {
463                         if (namesp == null || namesp.Length == 0)
464                                 return;
465                         
466                         if (imports == null)
467                                 imports = new Dictionary <string, bool> (StringComparer.Ordinal);
468                         
469                         if (imports.ContainsKey (namesp))
470                                 return;
471                         
472                         imports.Add (namesp, true);
473                         AddAssemblyForNamespace (namesp);
474                 }
475
476                 void AddAssemblyForNamespace (string namesp)
477                 {
478                         if (binDirAssemblies == null)
479                                 binDirAssemblies = HttpApplication.BinDirectoryAssemblies;
480                         if (binDirAssemblies.Length == 0)
481                                 return;
482
483                         if (namespacesCache == null)
484                                 namespacesCache = new Dictionary <string, bool> ();
485                         else if (namespacesCache.ContainsKey (namesp))
486                                 return;
487                         
488                         foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ())
489                                 if (FindNamespaceInAssembly (asm, namesp))
490                                         return;
491                         
492                         IList tla = BuildManager.TopLevelAssemblies;
493                         if (tla != null && tla.Count > 0) {
494                                 foreach (Assembly asm in tla) {
495                                         if (FindNamespaceInAssembly (asm, namesp))
496                                                 return;
497                                 }
498                         }
499
500                         Assembly a;
501                         foreach (string s in binDirAssemblies) {
502                                 a = Assembly.LoadFrom (s);
503                                 if (FindNamespaceInAssembly (a, namesp))
504                                         return;
505                         }
506                 }
507
508                 bool FindNamespaceInAssembly (Assembly asm, string namesp)
509                 {
510                         Type[] asmTypes;
511
512                         try {
513                                 asmTypes = asm.GetTypes ();
514                         } catch (ReflectionTypeLoadException) {
515                                 // ignore
516                                 return false;
517                         }
518                         
519                         foreach (Type type in asmTypes) {
520                                 if (String.Compare (type.Namespace, namesp, StringComparison.Ordinal) == 0) {
521                                         namespacesCache.Add (namesp, true);
522                                         AddAssembly (asm, true);
523                                         return true;
524                                 }
525                         }
526
527                         return false;
528                 }
529                 
530                 internal virtual void AddSourceDependency (string filename)
531                 {
532                         if (dependencies != null && dependencies.Contains (filename))
533                                 ThrowParseException ("Circular file references are not allowed. File: " + filename);
534
535                         AddDependency (filename);
536                 }
537
538                 internal virtual void AddDependency (string filename)
539                 {
540                         if (filename == null || filename == String.Empty)
541                                 return;
542
543                         if (dependencies == null)
544                                 dependencies = new ArrayList ();
545
546                         if (!dependencies.Contains (filename))
547                                 dependencies.Add (filename);
548                 }
549                 
550                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
551                 {
552                         if (assembly == null || assembly.Location == String.Empty)
553                                 return;
554
555                         if (anames == null)
556                                 anames = new Hashtable ();
557
558                         string name = assembly.GetName ().Name;
559                         string loc = assembly.Location;
560                         if (fullPath) {
561                                 if (!assemblies.Contains (loc)) {
562                                         assemblies.Add (loc);
563                                 }
564
565                                 anames [name] = loc;
566                                 anames [loc] = assembly;
567                         } else {
568                                 if (!assemblies.Contains (name)) {
569                                         assemblies.Add (name);
570                                 }
571
572                                 anames [name] = assembly;
573                         }
574                 }
575
576                 internal virtual Assembly AddAssemblyByFileName (string filename)
577                 {
578                         Assembly assembly = null;
579                         Exception error = null;
580
581                         try {
582                                 assembly = Assembly.LoadFrom (filename);
583                         } catch (Exception e) { error = e; }
584
585                         if (assembly == null)
586                                 ThrowParseException ("Assembly " + filename + " not found", error);
587
588                         AddAssembly (assembly, true);
589                         return assembly;
590                 }
591
592                 internal virtual Assembly AddAssemblyByName (string name)
593                 {
594                         if (anames == null)
595                                 anames = new Hashtable ();
596
597                         if (anames.Contains (name)) {
598                                 object o = anames [name];
599                                 if (o is string)
600                                         o = anames [o];
601
602                                 return (Assembly) o;
603                         }
604
605                         Assembly assembly = null;
606                         Exception error = null;
607                         try {
608                                 assembly = Assembly.Load (name);
609                         } catch (Exception e) { error = e; }
610
611                         if (assembly == null) {
612                                 try {
613                                         assembly = Assembly.LoadWithPartialName (name);
614                                 } catch (Exception e) { error = e; }
615                         }
616                         
617                         if (assembly == null)
618                                 ThrowParseException ("Assembly " + name + " not found", error);
619
620                         AddAssembly (assembly, true);
621                         return assembly;
622                 }
623                 
624                 internal virtual void ProcessMainAttributes (Hashtable atts)
625                 {
626                         directiveLocation = new System.Web.Compilation.Location (Location);
627                         CompilationSection compConfig;
628
629                         compConfig = CompilationConfig;
630                         
631                         atts.Remove ("Description"); // ignored
632                         atts.Remove ("CodeBehind");  // ignored
633                         atts.Remove ("AspCompat"); // ignored
634                         
635                         debug = GetBool (atts, "Debug", compConfig.Debug);
636                         compilerOptions = GetString (atts, "CompilerOptions", "");
637                         language = GetString (atts, "Language", "");
638                         if (language.Length != 0)
639                                 implicitLanguage = false;
640                         else
641                                 language = compConfig.DefaultLanguage;
642                         
643                         strictOn = GetBool (atts, "Strict", compConfig.Strict);
644                         explicitOn = GetBool (atts, "Explicit", compConfig.Explicit);
645                         if (atts.ContainsKey ("LinePragmas"))
646                                 linePragmasOn = GetBool (atts, "LinePragmas", true);
647
648                         string inherits = GetString (atts, "Inherits", null);
649                         string srcRealPath = null;
650                         
651                         // In ASP 2, the source file is actually integrated with
652                         // the generated file via the use of partial classes. This
653                         // means that the code file has to be confirmed, but not
654                         // used at this point.
655                         src = GetString (atts, "CodeFile", null);
656                         codeFileBaseClass = GetString (atts, "CodeFileBaseClass", null);
657
658                         if (src == null && codeFileBaseClass != null)
659                                 ThrowParseException ("The 'CodeFileBaseClass' attribute cannot be used without a 'CodeFile' attribute");
660
661                         string legacySrc = GetString (atts, "Src", null);
662                         if (legacySrc != null) {
663                                 legacySrc = UrlUtils.Combine (BaseVirtualDir, legacySrc);
664                                 GetAssemblyFromSource (legacySrc);
665
666                                 if (src == null) {
667                                         src = legacySrc;
668                                         legacySrc = MapPath (legacySrc, false);
669                                         srcRealPath = legacySrc;
670                                         if (!File.Exists (srcRealPath))
671                                                 ThrowParseException ("File " + src + " not found");
672                                         
673                                         srcIsLegacy = true;
674                                 } else 
675                                         legacySrc = MapPath (legacySrc, false);                         
676
677                                 AddDependency (legacySrc);
678                         }
679                         
680                         if (!srcIsLegacy && src != null && inherits != null) {
681                                 // Make sure the source exists
682                                 src = UrlUtils.Combine (BaseVirtualDir, src);
683                                 srcRealPath = MapPath (src, false);
684
685                                 if (!HostingEnvironment.VirtualPathProvider.FileExists (src))
686                                         ThrowParseException ("File " + src + " not found");
687
688                                 // We are going to create a partial class that shares
689                                 // the same name as the inherits tag, so reset the
690                                 // name. The base type is changed because it is the
691                                 // code file's responsibilty to extend the classes
692                                 // needed.
693                                 partialClassName = inherits;
694
695                                 // Add the code file as an option to the
696                                 // compiler. This lets both files be compiled at once.
697                                 compilerOptions += " \"" + srcRealPath + "\"";
698
699                                 if (codeFileBaseClass != null) {
700                                         try {
701                                                 codeFileBaseClassType = LoadType (codeFileBaseClass);
702                                         } catch (Exception) {
703                                         }
704
705                                         if (codeFileBaseClassType == null)
706                                                 ThrowParseException ("Could not load type '{0}'", codeFileBaseClass);
707                                 }
708                         } else if (inherits != null) {
709                                 // We just set the inherits directly because this is a
710                                 // Single-Page model.
711                                 SetBaseType (inherits);
712                         }
713
714                         if (src != null) {
715                                 if (VirtualPathUtility.IsAbsolute (src))
716                                         src = VirtualPathUtility.ToAppRelative (src);
717                                 AddDependency (src);
718                         }
719                         
720                         className = GetString (atts, "ClassName", null);
721                         if (className != null) {
722                                 string [] identifiers = className.Split ('.');
723                                 for (int i = 0; i < identifiers.Length; i++)
724                                         if (!CodeGenerator.IsValidLanguageIndependentIdentifier (identifiers [i]))
725                                                 ThrowParseException (String.Format ("'{0}' is not a valid "
726                                                         + "value for attribute 'classname'.", className));
727                         }
728
729                         if (this is TemplateControlParser)
730                                 metaResourceKey = GetString (atts, "meta:resourcekey", null);
731                         
732                         if (inherits != null && (this is PageParser || this is UserControlParser) && atts.Count > 0) {
733                                 if (unknownMainAttributes == null)
734                                         unknownMainAttributes = new List <UnknownAttributeDescriptor> ();
735                                 string key, val;
736                                 
737                                 foreach (DictionaryEntry de in atts) {
738                                         key = de.Key as string;
739                                         val = de.Value as string;
740                                         
741                                         if (String.IsNullOrEmpty (key) || String.IsNullOrEmpty (val))
742                                                 continue;
743                                         CheckUnknownAttribute (key, val, inherits);
744                                 }
745                                 return;
746                         }
747
748                         if (atts.Count > 0)
749                                 ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
750                 }
751
752                 void RegisterTagName (string tagName)
753                 {
754                         if (registeredTagNames == null)
755                                 registeredTagNames = new List <string> ();
756
757                         if (registeredTagNames.Contains (tagName))
758                                 return;
759
760                         registeredTagNames.Add (tagName);
761                 }
762                 
763                 void CheckUnknownAttribute (string name, string val, string inherits)
764                 {
765                         MemberInfo mi = null;
766                         bool missing = false;
767                         string memberName = name.Trim ().ToLower (Helpers.InvariantCulture);
768                         Type parent = codeFileBaseClassType;
769
770                         if (parent == null)
771                                 parent = baseType;
772                         
773                         try {
774                                 MemberInfo[] infos = parent.GetMember (memberName,
775                                                                        MemberTypes.Field | MemberTypes.Property,
776                                                                        BindingFlags.Public | BindingFlags.Instance |
777                                                                        BindingFlags.IgnoreCase | BindingFlags.Static);
778                                 if (infos.Length != 0) {
779                                         // prefer public properties to public methods (it's what MS.NET does)
780                                         foreach (MemberInfo tmp in infos) {
781                                                 if (tmp is PropertyInfo) {
782                                                         mi = tmp;
783                                                         break;
784                                                 }
785                                         }
786                                         if (mi == null)
787                                                 mi = infos [0];
788                                 } else
789                                         missing = true;
790                         } catch (Exception) {
791                                 missing = true;
792                         }
793                         if (missing)
794                                 ThrowParseException (
795                                         "Error parsing attribute '{0}': Type '{1}' does not have a public property named '{0}'",
796                                         memberName, inherits);
797                         
798                         Type memberType = null;
799                         if (mi is PropertyInfo) {
800                                 PropertyInfo pi = mi as PropertyInfo;
801                                 
802                                 if (!pi.CanWrite)
803                                         ThrowParseException (
804                                                 "Error parsing attribute '{0}': The '{0}' property is read-only and cannot be set.",
805                                                 memberName);
806                                 memberType = pi.PropertyType;
807                         } else if (mi is FieldInfo) {
808                                 memberType = ((FieldInfo)mi).FieldType;
809                         } else
810                                 ThrowParseException ("Could not determine member the kind of '{0}' in base type '{1}",
811                                                      memberName, inherits);
812                         TypeConverter converter = TypeDescriptor.GetConverter (memberType);
813                         bool convertible = true;
814                         object value = null;
815                         
816                         if (converter == null || !converter.CanConvertFrom (typeof (string)))
817                                 convertible = false;
818
819                         if (convertible) {
820                                 try {
821                                         value = converter.ConvertFromInvariantString (val);
822                                 } catch (Exception) {
823                                         convertible = false;
824                                 }
825                         }
826
827                         if (!convertible)
828                                 ThrowParseException ("Error parsing attribute '{0}': Cannot create an object of type '{1}' from its string representation '{2}' for the '{3}' property.",
829                                                      memberName, memberType, val, mi.Name);
830                         
831                         UnknownAttributeDescriptor desc = new UnknownAttributeDescriptor (mi, value);
832                         unknownMainAttributes.Add (desc);
833                 }
834                 
835                 internal void SetBaseType (string type)
836                 {
837                         Type parent;                    
838                         if (type == null || type == DefaultBaseTypeName)
839                                 parent = DefaultBaseType;
840                         else
841                                 parent = null;
842
843                         if (parent == null) {
844                                 parent = LoadType (type);
845
846                                 if (parent == null)
847                                         ThrowParseException ("Cannot find type " + type);
848
849                                 if (!DefaultBaseType.IsAssignableFrom (parent))
850                                         ThrowParseException ("The parent type '" + type + "' does not derive from " + DefaultBaseType);
851                         }
852
853                         var pageParserFilter = PageParserFilter;
854                         if (pageParserFilter != null && !pageParserFilter.AllowBaseType (parent))
855                                 throw new HttpException ("Base type '" + parent + "' is not allowed.");
856                         
857                         baseType = parent;
858                 }
859
860                 internal void SetLanguage (string language)
861                 {
862                         this.language = language;
863                         implicitLanguage = false;
864                 }
865
866                 internal void PushIncludeDir (string dir)
867                 {
868                         if (includeDirs == null)
869                                 includeDirs = new Stack <string> (1);
870
871                         includeDirs.Push (dir);
872                 }
873
874                 internal string PopIncludeDir ()
875                 {
876                         if (includeDirs == null || includeDirs.Count == 0)
877                                 return null;
878
879                         return includeDirs.Pop () as string;
880                 }
881                 
882                 Assembly GetAssemblyFromSource (string vpath)
883                 {                       
884                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
885                         string realPath = MapPath (vpath, false);
886                         if (!File.Exists (realPath))
887                                 ThrowParseException ("File " + vpath + " not found");
888
889                         AddSourceDependency (vpath);
890                         
891                         CompilerResults result;
892                         string tmp;
893                         CompilerParameters parameters;
894                         CodeDomProvider provider = BaseCompiler.CreateProvider (HttpContext.Current, language, out parameters, out tmp);
895                         if (provider == null)
896                                 throw new HttpException ("Cannot find provider for language '" + language + "'.");
897                         
898                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
899                         abuilder.CompilerOptions = parameters;
900                         abuilder.AddAssemblyReference (BuildManager.GetReferencedAssemblies () as List <Assembly>);
901                         abuilder.AddCodeFile (realPath);
902                         result = abuilder.BuildAssembly (new VirtualPath (vpath));
903
904                         if (result.NativeCompilerReturnValue != 0) {
905                                 using (StreamReader reader = new StreamReader (realPath)) {
906                                         throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
907                                 }
908                         }
909
910                         AddAssembly (result.CompiledAssembly, true);
911                         return result.CompiledAssembly;
912                 }               
913
914                 internal abstract string DefaultBaseTypeName { get; }
915                 internal abstract string DefaultDirectiveName { get; }
916
917                 internal bool LinePragmasOn {
918                         get { return linePragmasOn; }
919                 }
920                 
921                 internal byte[] MD5Checksum {
922                         get { return md5checksum; }
923                         set { md5checksum = value; }
924                 }
925
926                 internal PageParserFilter PageParserFilter {
927                         get {
928                                 if (pageParserFilter != null)
929                                         return pageParserFilter;
930
931                                 Type t = PageParserFilterType;
932                                 if (t == null)
933                                         return null;
934                                 
935                                 pageParserFilter = Activator.CreateInstance (t) as PageParserFilter;
936                                 pageParserFilter.Initialize (this);
937
938                                 return pageParserFilter;
939                         }
940                 }
941                 
942                 internal Type PageParserFilterType {
943                         get {
944                                 if (pageParserFilterType == null) {
945                                         string typeName = PagesConfig.PageParserFilterType;
946                                         if (String.IsNullOrEmpty (typeName))
947                                                 return null;
948                                         
949                                         pageParserFilterType = HttpApplication.LoadType (typeName, true);
950                                 }
951                                 
952                                 return pageParserFilterType;
953                         }
954                 }
955                 
956                 internal Type DefaultBaseType {
957                         get {
958                                 Type type = Type.GetType (DefaultBaseTypeName, true);
959
960                                 return type;
961                         }
962                 }
963                 
964                 internal ILocation DirectiveLocation {
965                         get { return directiveLocation; }
966                 }
967                 
968                 internal string ParserDir {
969                         get {
970                                 if (includeDirs == null || includeDirs.Count == 0)
971                                         return BaseDir;
972
973                                 return includeDirs.Peek () as string;
974                         }
975                 }
976                 
977                 internal string InputFile
978                 {
979                         get { return inputFile; }
980                         set { inputFile = value; }
981                 }
982
983                 internal bool IsPartial {
984                         get { return (!srcIsLegacy && src != null); }
985                 }
986
987                 internal string CodeBehindSource {
988                         get {
989                                 if (srcIsLegacy)
990                                         return null;
991                                 
992                                 return src;
993                         }
994                 }
995                         
996                 internal string PartialClassName {
997                         get { return partialClassName; }
998                 }
999
1000                 internal string CodeFileBaseClass {
1001                         get { return codeFileBaseClass; }
1002                 }
1003
1004                 internal string MetaResourceKey {
1005                         get { return metaResourceKey; }
1006                 }
1007                 
1008                 internal Type CodeFileBaseClassType
1009                 {
1010                         get { return codeFileBaseClassType; }
1011                 }
1012                 
1013                 internal List <UnknownAttributeDescriptor> UnknownMainAttributes
1014                 {
1015                         get { return unknownMainAttributes; }
1016                 }
1017
1018                 internal string Text {
1019                         get { return text; }
1020                         set { text = value; }
1021                 }
1022
1023                 internal Type BaseType {
1024                         get {
1025                                 if (baseType == null)
1026                                         SetBaseType (DefaultBaseTypeName);
1027                                 
1028                                 return baseType;
1029                         }
1030                 }
1031                 
1032                 internal bool BaseTypeIsGlobal {
1033                         get { return baseTypeIsGlobal; }
1034                         set { baseTypeIsGlobal = value; }
1035                 }
1036
1037                 static long autoClassCounter = 0;
1038
1039                 internal string EncodeIdentifier (string value)
1040                 {
1041                         if (value == null || value.Length == 0 || CodeGenerator.IsValidLanguageIndependentIdentifier (value))
1042                                 return value;
1043
1044                         StringBuilder ret = new StringBuilder ();
1045
1046                         char ch = value [0];
1047                         switch (Char.GetUnicodeCategory (ch)) {
1048                                 case UnicodeCategory.LetterNumber:
1049                                 case UnicodeCategory.LowercaseLetter:
1050                                 case UnicodeCategory.TitlecaseLetter:
1051                                 case UnicodeCategory.UppercaseLetter:
1052                                 case UnicodeCategory.OtherLetter:
1053                                 case UnicodeCategory.ModifierLetter:
1054                                 case UnicodeCategory.ConnectorPunctuation:
1055                                         ret.Append (ch);
1056                                         break;
1057
1058                                 case UnicodeCategory.DecimalDigitNumber:
1059                                         ret.Append ('_');
1060                                         ret.Append (ch);
1061                                         break;
1062                                         
1063                                 default:
1064                                         ret.Append ('_');
1065                                         break;
1066                         }
1067
1068                         for (int i = 1; i < value.Length; i++) {
1069                                 ch = value [i];
1070                                 switch (Char.GetUnicodeCategory (ch)) {
1071                                         case UnicodeCategory.LetterNumber:
1072                                         case UnicodeCategory.LowercaseLetter:
1073                                         case UnicodeCategory.TitlecaseLetter:
1074                                         case UnicodeCategory.UppercaseLetter:
1075                                         case UnicodeCategory.OtherLetter:
1076                                         case UnicodeCategory.ModifierLetter:
1077                                         case UnicodeCategory.ConnectorPunctuation:
1078                                         case UnicodeCategory.DecimalDigitNumber:
1079                                         case UnicodeCategory.NonSpacingMark:
1080                                         case UnicodeCategory.SpacingCombiningMark:
1081                                         case UnicodeCategory.Format:
1082                                                 ret.Append (ch);
1083                                                 break;
1084                                                 
1085                                         default:
1086                                                 ret.Append ('_');
1087                                                 break;
1088                                 }
1089                         }
1090
1091                         return ret.ToString ();
1092                 }
1093                 
1094                 internal string ClassName {
1095                         get {
1096                                 if (className != null)
1097                                         return className;
1098
1099                                 string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
1100                                 string inFile;
1101                                 
1102                                 if (String.IsNullOrEmpty (inputFile)) {
1103                                         inFile = null;
1104                                         using (StreamReader sr = Reader as StreamReader) {
1105                                                 if (sr != null) {
1106                                                         FileStream fr = sr.BaseStream as FileStream;
1107                                                         if (fr != null)
1108                                                                 inFile = fr.Name;
1109                                                 }
1110                                         }
1111                                 } else
1112                                         inFile = inputFile;
1113
1114                                 if (String.IsNullOrEmpty (inFile)) {
1115                                         // generate a unique class name
1116                                         long suffix;
1117                                         suffix = Interlocked.Increment (ref autoClassCounter);
1118                                         className = String.Format ("autoclass_nosource_{0:x}", suffix);
1119                                         return className;
1120                                 }
1121                                 
1122                                 if (StrUtils.StartsWith (inFile, physPath))
1123                                         className = inputFile.Substring (physPath.Length).ToLower (Helpers.InvariantCulture);
1124                                 else
1125                                         className = Path.GetFileName (inputFile);
1126                                 className = EncodeIdentifier (className);
1127                                 return className;
1128                         }
1129                 }
1130
1131                 internal List <ServerSideScript> Scripts {
1132                         get {
1133                                 if (scripts == null)
1134                                         scripts = new List <ServerSideScript> ();
1135
1136                                 return scripts;
1137                         }
1138                 }
1139
1140                 internal Dictionary <string, bool> Imports {
1141                         get { return imports; }
1142                 }
1143
1144                 internal List <string> Interfaces {
1145                         get { return interfaces; }
1146                 }
1147                 
1148                 internal ArrayList Assemblies {
1149                         get {
1150                                 if (appAssemblyIndex != -1) {
1151                                         object o = assemblies [appAssemblyIndex];
1152                                         assemblies.RemoveAt (appAssemblyIndex);
1153                                         assemblies.Add (o);
1154                                         appAssemblyIndex = -1;
1155                                 }
1156
1157                                 return assemblies;
1158                         }
1159                 }
1160
1161                 internal RootBuilder RootBuilder {
1162                         get {
1163                                 if (rootBuilder != null)
1164                                         return rootBuilder;
1165                                 AspGenerator generator = AspGenerator;
1166                                 if (generator != null)
1167                                         rootBuilder = generator.RootBuilder;
1168
1169                                 return rootBuilder;
1170                         }
1171                         set { rootBuilder = value; }
1172                 }
1173
1174                 internal ArrayList Dependencies {
1175                         get { return dependencies; }
1176                         set { dependencies = value; }
1177                 }
1178
1179                 internal string CompilerOptions {
1180                         get { return compilerOptions; }
1181                 }
1182
1183                 internal string Language {
1184                         get { return language; }
1185                 }
1186
1187                 internal bool ImplicitLanguage {
1188                         get { return implicitLanguage; }
1189                 }
1190                 
1191                 internal bool StrictOn {
1192                         get { return strictOn; }
1193                 }
1194
1195                 internal bool ExplicitOn {
1196                         get { return explicitOn; }
1197                 }
1198                 
1199                 internal bool Debug {
1200                         get { return debug; }
1201                 }
1202
1203                 internal bool OutputCache {
1204                         get { return output_cache; }
1205                 }
1206
1207                 internal int OutputCacheDuration {
1208                         get { return oc_duration; }
1209                 }
1210
1211                 internal OutputCacheParsedParams OutputCacheParsedParameters {
1212                         get { return oc_parsed_params; }
1213                 }
1214
1215                 internal string OutputCacheSqlDependency {
1216                         get { return oc_sqldependency; }
1217                 }
1218                 
1219                 internal string OutputCacheCacheProfile {
1220                         get { return oc_cacheprofile; }
1221                 }
1222                 
1223                 internal string OutputCacheVaryByContentEncodings {
1224                         get { return oc_content_encodings; }
1225                 }
1226
1227                 internal bool OutputCacheNoStore {
1228                         get { return oc_nostore; }
1229                 }
1230                 
1231                 internal virtual TextReader Reader {
1232                         get { return null; }
1233                         set { /* no-op */ }
1234                 }
1235                 
1236                 internal string OutputCacheVaryByHeader {
1237                         get { return oc_header; }
1238                 }
1239
1240                 internal string OutputCacheVaryByCustom {
1241                         get { return oc_custom; }
1242                 }
1243
1244                 internal string OutputCacheVaryByControls {
1245                         get { return oc_controls; }
1246                 }
1247                 
1248                 internal bool OutputCacheShared {
1249                         get { return oc_shared; }
1250                 }
1251                 
1252                 internal OutputCacheLocation OutputCacheLocation {
1253                         get { return oc_location; }
1254                 }
1255
1256                 internal string OutputCacheVaryByParam {
1257                         get { return oc_param; }
1258                 }
1259
1260                 internal List <string> RegisteredTagNames {
1261                         get { return registeredTagNames; }
1262                 }
1263                 
1264                 internal PagesSection PagesConfig {
1265                         get { return GetConfigSection <PagesSection> ("system.web/pages") as PagesSection; }
1266                 }
1267
1268                 internal AspGenerator AspGenerator {
1269                         get;
1270                         set;
1271                 }
1272         }
1273 }