undo
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateParser.cs
index 3506ff9ee86871b3fcaedcdadf548ece64680e6a..c3bb9afb900a712e4104c4ad1ef67fb1fa45f393 100644 (file)
@@ -4,9 +4,10 @@
 // Authors:
 //     Duncan Mak (duncan@ximian.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//      Marek Habersack (mhabersack@novell.com)
 //
 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 
 using System.CodeDom.Compiler;
 using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
 using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Security.Permissions;
+using System.Text;
+using System.Threading;
 using System.Web.Compilation;
 using System.Web.Configuration;
+using System.Web.Hosting;
 using System.Web.Util;
 
 namespace System.Web.UI {
-
+       internal class ServerSideScript
+       {
+               public readonly string Script;
+               public readonly ILocation Location;
+               
+               public ServerSideScript (string script, ILocation location)
+               {
+                       Script = script;
+                       Location = location;
+               }
+       }
+       
        // CAS
        [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public abstract class TemplateParser : BaseParser
        {
+               [Flags]
+               internal enum OutputCacheParsedParams
+               {
+                       Location               = 0x0001,
+                       CacheProfile           = 0x0002,
+                       NoStore                = 0x0004,
+                       SqlDependency          = 0x0008,
+                       VaryByCustom           = 0x0010,
+                       VaryByHeader           = 0x0020,
+                       VaryByControl          = 0x0040,
+                       VaryByContentEncodings = 0x0080
+               }
+               
                string inputFile;
                string text;
-               string privateBinPath;
                Hashtable mainAttributes;
                ArrayList dependencies;
                ArrayList assemblies;
                Hashtable anames;
-               ArrayList imports;
-               ArrayList interfaces;
-               ArrayList scripts;
+               string[] binDirAssemblies;
+               Dictionary <string, bool> namespacesCache;
+               Dictionary <string, bool> imports;
+               List <string> interfaces;
+               List <ServerSideScript> scripts;
                Type baseType;
-               bool baseTypeIsGlobal;
+               bool baseTypeIsGlobal = true;
                string className;
                RootBuilder rootBuilder;
                bool debug;
                string compilerOptions;
                string language;
-               bool strictOn = false;
-               bool explicitOn = false;
-               bool linePragmasOn = false;
+               bool implicitLanguage;
+               bool strictOn ;
+               bool explicitOn;
+               bool linePragmasOn = true;
                bool output_cache;
                int oc_duration;
                string oc_header, oc_custom, oc_param, oc_controls;
+               string oc_content_encodings, oc_cacheprofile, oc_sqldependency;
+               bool oc_nostore;
+               OutputCacheParsedParams oc_parsed_params = 0;
                bool oc_shared;
                OutputCacheLocation oc_location;
-               CultureInfo invariantCulture = CultureInfo.InvariantCulture;
-#if NET_2_0
+
+               // Kludge needed to support pre-parsing of the main directive (see
+               // AspNetGenerator.GetRootBuilderType)
+               internal int allowedMainDirectives = 0;
+               
+               byte[] md5checksum;
                string src;
+               bool srcIsLegacy;
                string partialClassName;
-#endif
-               Assembly srcAssembly;
+               string codeFileBaseClass;
+               string metaResourceKey;
+               Type codeFileBaseClassType;
+               Type pageParserFilterType;
+               PageParserFilter pageParserFilter;
+               
+               List <UnknownAttributeDescriptor> unknownMainAttributes;
+               Stack <string> includeDirs;
+               List <string> registeredTagNames;
+               ILocation directiveLocation;
+               
                int appAssemblyIndex = -1;
 
                internal TemplateParser ()
                {
-                       imports = new ArrayList ();
-#if NET_2_0
-                       AddNamespaces (imports);
-#else
-                       imports.Add ("System");
-                       imports.Add ("System.Collections");
-                       imports.Add ("System.Collections.Specialized");
-                       imports.Add ("System.Configuration");
-                       imports.Add ("System.Text");
-                       imports.Add ("System.Text.RegularExpressions");
-                       imports.Add ("System.Web");
-                       imports.Add ("System.Web.Caching");
-                       imports.Add ("System.Web.Security");
-                       imports.Add ("System.Web.SessionState");
-                       imports.Add ("System.Web.UI");
-                       imports.Add ("System.Web.UI.WebControls");
-                       imports.Add ("System.Web.UI.HtmlControls");
-#endif
-
+                       imports = new Dictionary <string, bool> (StringComparer.Ordinal);
+                       LoadConfigDefaults ();
                        assemblies = new ArrayList ();
-#if NET_2_0
-                       bool addAssembliesInBin = false;
-                       foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
-                               if (info.Assembly == "*")
-                                       addAssembliesInBin = true;
-                               else
+                       CompilationSection compConfig = CompilationConfig;
+                       foreach (AssemblyInfo info in compConfig.Assemblies) {
+                               if (info.Assembly != "*")
                                        AddAssemblyByName (info.Assembly);
                        }
-                       if (addAssembliesInBin)
-                               AddAssembliesInBin ();
 
-                       foreach (NamespaceInfo info in PagesConfig.Namespaces) {
-                               imports.Add (info.Namespace);
-                       }
-#else
-                       foreach (string a in CompilationConfig.Assemblies)
-                               AddAssemblyByName (a);
-                       if (CompilationConfig.AssembliesInBin)
-                               AddAssembliesInBin ();
-#endif
+                       language = compConfig.DefaultLanguage;
+                       implicitLanguage = true;
+               }
 
-                       language = CompilationConfig.DefaultLanguage;
+               internal virtual void LoadConfigDefaults ()
+               {
+                       AddNamespaces (imports);
+                       debug = CompilationConfig.Debug;
                }
                
                internal void AddApplicationAssembly ()
@@ -137,57 +161,79 @@ namespace System.Web.UI {
 
                protected abstract Type CompileIntoType ();
 
-#if NET_2_0
-               void AddNamespaces (ArrayList imports)
+               internal void AddControl (Type type, IDictionary attributes)
+               {
+                       AspGenerator generator = AspGenerator;
+                       if (generator == null)
+                               return;
+                       generator.AddControl (type, attributes);
+               }
+               
+               void AddNamespaces (Dictionary <string, bool> imports)
                {
                        if (BuildManager.HaveResources)
-                               imports.Add ("System.Resources");
+                               imports.Add ("System.Resources", true);
                        
-                       PagesSection pages = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+                       PagesSection pages = PagesConfig;
                        if (pages == null)
                                return;
 
                        NamespaceCollection namespaces = pages.Namespaces;
                        if (namespaces == null || namespaces.Count == 0)
                                return;
-
-                       foreach (NamespaceInfo nsi in namespaces)
-                               imports.Add (nsi.Namespace);
+                       
+                       foreach (NamespaceInfo nsi in namespaces) {
+                               string ns = nsi.Namespace;
+                               if (imports.ContainsKey (ns))
+                                       continue;
+                               
+                               imports.Add (ns, true);
+                       }
                }
-#endif
                
                internal void RegisterCustomControl (string tagPrefix, string tagName, string src)
                 {
-                        string realpath = MapPath (src);
-                        if (String.Compare (realpath, inputFile, false, invariantCulture) == 0)
+                        string realpath = null;
+                       bool fileExists = false;
+                       VirtualFile vf = null;
+                       VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
+                       VirtualPath vp = new VirtualPath (src, BaseVirtualDir);
+                       string vpAbsolute = vp.Absolute;
+                       
+                       if (vpp.FileExists (vpAbsolute)) {
+                               fileExists = true;
+                               vf = vpp.GetFile (vpAbsolute);
+                               if (vf != null)
+                                       realpath = MapPath (vf.VirtualPath);
+                       }
+
+                       if (!fileExists)
+                               ThrowParseFileNotFound (src);
+
+                       if (String.Compare (realpath, inputFile, false, Helpers.InvariantCulture) == 0)
                                 return;
+                       
+                       string vpath = vf.VirtualPath;
                         
-                        if (!File.Exists (realpath))
-                                throw new ParseException (Location, "Could not find file \"" + realpath + "\".");
-                        string vpath = UrlUtils.Combine (BaseVirtualDir, src);
-                        Type type = null;
-                        AddDependency (realpath);
                         try {
-                                ArrayList other_deps = new ArrayList ();
-                                type = UserControlParser.GetCompiledType (vpath, realpath, other_deps, Context);
-                                foreach (string s in other_deps) {
-                                        AddDependency (s);
-                                }
+                               RegisterTagName (tagPrefix + ":" + tagName);
+                               RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, vpath);
+                               AddDependency (vpath);
                         } catch (ParseException pe) {
                                 if (this is UserControlParser)
                                         throw new ParseException (Location, pe.Message, pe);
                                 throw;
                         }
-
-                        AddAssembly (type.Assembly, true);
-                        RootBuilder.Foundry.RegisterFoundry (tagPrefix, tagName, type);
                 }
 
                 internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
                 {
                         AddImport (ns);
-                        Assembly ass = AddAssemblyByName (assembly);
-                        AddDependency (ass.Location);
+                        Assembly ass = null;
+                       
+                       if (assembly != null && assembly.Length > 0)
+                               ass = AddAssemblyByName (assembly);
+                       
                         RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
                 }
 
@@ -205,16 +251,27 @@ namespace System.Web.UI {
                
                internal virtual void AddDirective (string directive, Hashtable atts)
                {
-                       if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
-                               if (mainAttributes != null)
+                       var pageParserFilter = PageParserFilter;
+                       if (String.Compare (directive, DefaultDirectiveName, true, Helpers.InvariantCulture) == 0) {
+                               bool allowMainDirective = allowedMainDirectives > 0;
+                               
+                               if (mainAttributes != null && !allowMainDirective)
                                        ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
 
+                               allowedMainDirectives--;
+                               if (mainAttributes != null)
+                                       return;
+                               
+                               if (pageParserFilter != null)
+                                       pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
+                               
                                mainAttributes = atts;
                                ProcessMainAttributes (mainAttributes);
                                return;
-                       }
-
-                       int cmp = String.Compare ("Assembly", directive, true);
+                       } else if (pageParserFilter != null)
+                               pageParserFilter.PreprocessDirective (directive.ToLower (Helpers.InvariantCulture), atts);
+                               
+                       int cmp = String.Compare ("Assembly", directive, true, Helpers.InvariantCulture);
                        if (cmp == 0) {
                                string name = GetString (atts, "Name", null);
                                string src = GetString (atts, "Src", null);
@@ -237,18 +294,17 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       cmp = String.Compare ("Import", directive, true);
+                       cmp = String.Compare ("Import", directive, true, Helpers.InvariantCulture);
                        if (cmp == 0) {
                                string namesp = GetString (atts, "Namespace", null);
                                if (atts.Count > 0)
                                        ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
                                
-                               if (namesp != null && namesp != "")
-                                       AddImport (namesp);
+                               AddImport (namesp);
                                return;
                        }
 
-                       cmp = String.Compare ("Implements", directive, true);
+                       cmp = String.Compare ("Implements", directive, true, Helpers.InvariantCulture);
                        if (cmp == 0) {
                                string ifacename = GetString (atts, "Interface", "");
 
@@ -266,7 +322,7 @@ namespace System.Web.UI {
                                return;
                        }
 
-                       cmp = String.Compare ("OutputCache", directive, true);
+                       cmp = String.Compare ("OutputCache", directive, true, Helpers.InvariantCulture);
                        if (cmp == 0) {
                                HttpResponse response = HttpContext.Current.Response;
                                if (response != null)
@@ -276,65 +332,94 @@ namespace System.Web.UI {
                                
                                if (atts ["Duration"] == null)
                                        ThrowParseException ("The directive is missing a 'duration' attribute.");
-                               if (atts ["VaryByParam"] == null)
-                                       ThrowParseException ("This directive is missing 'VaryByParam' " +
-                                                       "attribute, which should be set to \"none\", \"*\", " +
+                               if (atts ["VaryByParam"] == null && atts ["VaryByControl"] == null)
+                                       ThrowParseException ("This directive is missing 'VaryByParam' " +
+                                                       "or 'VaryByControl' attribute, which should be set to \"none\", \"*\", " +
                                                        "or a list of name/value pairs.");
 
                                foreach (DictionaryEntry entry in atts) {
                                        string key = (string) entry.Key;
-                                       switch (key.ToLower ()) {
-                                       case "duration":
-                                               oc_duration = Int32.Parse ((string) entry.Value);
-                                               if (oc_duration < 1)
-                                                       ThrowParseException ("The 'duration' attribute must be set " +
-                                                                       "to a positive integer value");
-                                               break;
-                                       case "varybyparam":
-                                               oc_param = (string) entry.Value;
-                                               if (String.Compare (oc_param, "none") == 0)
-                                                       oc_param = null;
-                                               break;
-                                       case "varybyheader":
-                                               oc_header = (string) entry.Value;
-                                               break;
-                                       case "varybycustom":
-                                               oc_custom = (string) entry.Value;
-                                               break;
-                                       case "location":
-                                               if (!(this is PageParser))
-                                                       goto default;
-
-                                               try {
-                                                       oc_location = (OutputCacheLocation) Enum.Parse (
-                                                               typeof (OutputCacheLocation), (string) entry.Value, true);
-                                               } catch {
-                                                       ThrowParseException ("The 'location' attribute is case sensitive and " +
-                                                                       "must be one of the following values: Any, Client, " +
-                                                                       "Downstream, Server, None, ServerAndClient.");
-                                               }
-                                               break;
-                                       case "varybycontrol":
-                                               if (this is PageParser)
-                                                       goto default;
-
-                                                oc_controls = (string) entry.Value;
-                                               break;
-                                       case "shared":
-                                               if (this is PageParser)
-                                                       goto default;
-
-                                               try {
-                                                       oc_shared = Boolean.Parse ((string) entry.Value);
-                                               } catch {
-                                                       ThrowParseException ("The 'shared' attribute is case sensitive" +
-                                                                       " and must be set to 'true' or 'false'.");
-                                               }
-                                               break;
-                                       default:
-                                               ThrowParseException ("The '" + key + "' attribute is not " +
-                                                               "supported by the 'Outputcache' directive.");
-                                               break;
+                                       if (key == null)
+                                               continue;
+                                       
+                                       switch (key.ToLower (Helpers.InvariantCulture)) {
+                                               case "duration":
+                                                       oc_duration = Int32.Parse ((string) entry.Value);
+                                                       if (oc_duration < 1)
+                                                               ThrowParseException ("The 'duration' attribute must be set " +
+                                                                                    "to a positive integer value");
+                                                       break;
+
+                                               case "sqldependency":
+                                                       oc_sqldependency = (string) entry.Value;
+                                                       break;
+                                                       
+                                               case "nostore":
+                                                       try {
+                                                               oc_nostore = Boolean.Parse ((string) entry.Value);
+                                                               oc_parsed_params |= OutputCacheParsedParams.NoStore;
+                                                       } catch {
+                                                               ThrowParseException ("The 'NoStore' attribute is case sensitive" +
+                                                                                    " and must be set to 'true' or 'false'.");
+                                                       }
+                                                       break;
+
+                                               case "cacheprofile":
+                                                       oc_cacheprofile = (string) entry.Value;
+                                                       oc_parsed_params |= OutputCacheParsedParams.CacheProfile;
+                                                       break;
+                                                       
+                                               case "varybycontentencodings":
+                                                       oc_content_encodings = (string) entry.Value;
+                                                       oc_parsed_params |= OutputCacheParsedParams.VaryByContentEncodings;
+                                                       break;
+
+                                               case "varybyparam":
+                                                       oc_param = (string) entry.Value;
+                                                       if (String.Compare (oc_param, "none", true, Helpers.InvariantCulture) == 0)
+                                                               oc_param = null;
+                                                       break;
+                                               case "varybyheader":
+                                                       oc_header = (string) entry.Value;
+                                                       oc_parsed_params |= OutputCacheParsedParams.VaryByHeader;
+                                                       break;
+                                               case "varybycustom":
+                                                       oc_custom = (string) entry.Value;
+                                                       oc_parsed_params |= OutputCacheParsedParams.VaryByCustom;
+                                                       break;
+                                               case "location":
+                                                       if (!(this is PageParser))
+                                                               goto default;
+                                               
+                                                       try {
+                                                               oc_location = (OutputCacheLocation) Enum.Parse (
+                                                                       typeof (OutputCacheLocation), (string) entry.Value, true);
+                                                               oc_parsed_params |= OutputCacheParsedParams.Location;
+                                                       } catch {
+                                                               ThrowParseException ("The 'location' attribute is case sensitive and " +
+                                                                                    "must be one of the following values: Any, Client, " +
+                                                                                    "Downstream, Server, None, ServerAndClient.");
+                                                       }
+                                                       break;
+                                               case "varybycontrol":
+                                                       oc_controls = (string) entry.Value;
+                                                       oc_parsed_params |= OutputCacheParsedParams.VaryByControl;
+                                                       break;
+                                               case "shared":
+                                                       if (this is PageParser)
+                                                               goto default;
+
+                                                       try {
+                                                               oc_shared = Boolean.Parse ((string) entry.Value);
+                                                       } catch {
+                                                               ThrowParseException ("The 'shared' attribute is case sensitive" +
+                                                                                    " and must be set to 'true' or 'false'.");
+                                                       }
+                                                       break;
+                                               default:
+                                                       ThrowParseException ("The '" + key + "' attribute is not " +
+                                                                            "supported by the 'Outputcache' directive.");
+                                                       break;
                                        }
                                        
                                }
@@ -347,60 +432,27 @@ namespace System.Web.UI {
 
                internal Type LoadType (string typeName)
                {
-                       // First try loaded assemblies, then try assemblies in Bin directory.
-                       Type type = null;
-                       bool seenBin = false;
-                       Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
-                       foreach (Assembly ass in assemblies) {
-                               type = ass.GetType (typeName);
-                               if (type == null)
-                                       continue;
-
-                               if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
-                                       AddAssembly (ass, true);
-                               } else {
-                                       seenBin = true;
-                               }
-
-                               AddDependency (ass.Location);
-                               return type;
-                       }
-
-                       if (seenBin)
-                               return null;
-
-                       // Load from bin
-                       if (!Directory.Exists (PrivateBinPath))
+                       Type type = HttpApplication.LoadType (typeName);
+                       if (type == null)
                                return null;
+                       Assembly asm = type.Assembly;
+                       string location = asm.Location;
+                       
+                       string dirname = Path.GetDirectoryName (location);
+                       bool doAddAssembly = true;
+                       if (dirname == HttpApplication.BinDirectory)
+                               doAddAssembly = false;
 
-                       string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
-                       foreach (string s in binDlls) {
-                               Assembly binA = Assembly.LoadFrom (s);
-                               type = binA.GetType (typeName);
-                               if (type == null)
-                                       continue;
-
-                               AddDependency (binA.Location);
-                               return type;
-                       }
-
-                       return null;
-               }
+                       if (doAddAssembly)
+                               AddAssembly (asm, true);
 
-               void AddAssembliesInBin ()
-               {
-                       if (!Directory.Exists (PrivateBinPath))
-                               return;
-
-                       string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
-                       foreach (string s in binDlls)
-                               assemblies.Add (s);
+                       return type;
                }
 
                internal virtual void AddInterface (string iface)
                {
                        if (interfaces == null)
-                               interfaces = new ArrayList ();
+                               interfaces = new List <string> ();
 
                        if (!interfaces.Contains (iface))
                                interfaces.Add (iface);
@@ -408,25 +460,84 @@ namespace System.Web.UI {
                
                internal virtual void AddImport (string namesp)
                {
+                       if (namesp == null || namesp.Length == 0)
+                               return;
+                       
                        if (imports == null)
-                               imports = new ArrayList ();
+                               imports = new Dictionary <string, bool> (StringComparer.Ordinal);
+                       
+                       if (imports.ContainsKey (namesp))
+                               return;
+                       
+                       imports.Add (namesp, true);
+                       AddAssemblyForNamespace (namesp);
+               }
+
+               void AddAssemblyForNamespace (string namesp)
+               {
+                       if (binDirAssemblies == null)
+                               binDirAssemblies = HttpApplication.BinDirectoryAssemblies;
+                       if (binDirAssemblies.Length == 0)
+                               return;
+
+                       if (namespacesCache == null)
+                               namespacesCache = new Dictionary <string, bool> ();
+                       else if (namespacesCache.ContainsKey (namesp))
+                               return;
+                       
+                       foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ())
+                               if (FindNamespaceInAssembly (asm, namesp))
+                                       return;
+                       
+                       IList tla = BuildManager.TopLevelAssemblies;
+                       if (tla != null && tla.Count > 0) {
+                               foreach (Assembly asm in tla) {
+                                       if (FindNamespaceInAssembly (asm, namesp))
+                                               return;
+                               }
+                       }
 
-                       if (!imports.Contains (namesp))
-                               imports.Add (namesp);
+                       Assembly a;
+                       foreach (string s in binDirAssemblies) {
+                               a = Assembly.LoadFrom (s);
+                               if (FindNamespaceInAssembly (a, namesp))
+                                       return;
+                       }
                }
 
+               bool FindNamespaceInAssembly (Assembly asm, string namesp)
+               {
+                       Type[] asmTypes;
+
+                       try {
+                               asmTypes = asm.GetTypes ();
+                       } catch (ReflectionTypeLoadException) {
+                               // ignore
+                               return false;
+                       }
+                       
+                       foreach (Type type in asmTypes) {
+                               if (String.Compare (type.Namespace, namesp, StringComparison.Ordinal) == 0) {
+                                       namespacesCache.Add (namesp, true);
+                                       AddAssembly (asm, true);
+                                       return true;
+                               }
+                       }
+
+                       return false;
+               }
+               
                internal virtual void AddSourceDependency (string filename)
                {
-                       if (dependencies != null && dependencies.Contains (filename)) {
+                       if (dependencies != null && dependencies.Contains (filename))
                                ThrowParseException ("Circular file references are not allowed. File: " + filename);
-                       }
 
                        AddDependency (filename);
                }
 
                internal virtual void AddDependency (string filename)
                {
-                       if (filename == "")
+                       if (filename == null || filename == String.Empty)
                                return;
 
                        if (dependencies == null)
@@ -438,7 +549,7 @@ namespace System.Web.UI {
                
                internal virtual void AddAssembly (Assembly assembly, bool fullPath)
                {
-                       if (assembly.Location == "")
+                       if (assembly == null || assembly.Location == String.Empty)
                                return;
 
                        if (anames == null)
@@ -493,11 +604,9 @@ namespace System.Web.UI {
 
                        Assembly assembly = null;
                        Exception error = null;
-                       if (name.IndexOf (',') != -1) {
-                               try {
-                                       assembly = Assembly.Load (name);
-                               } catch (Exception e) { error = e; }
-                       }
+                       try {
+                               assembly = Assembly.Load (name);
+                       } catch (Exception e) { error = e; }
 
                        if (assembly == null) {
                                try {
@@ -511,40 +620,69 @@ namespace System.Web.UI {
                        AddAssembly (assembly, true);
                        return assembly;
                }
-
+               
                internal virtual void ProcessMainAttributes (Hashtable atts)
                {
+                       directiveLocation = new System.Web.Compilation.Location (Location);
+                       CompilationSection compConfig;
+
+                       compConfig = CompilationConfig;
+                       
                        atts.Remove ("Description"); // ignored
-#if NET_1_1
                        atts.Remove ("CodeBehind");  // ignored
-#endif
                        atts.Remove ("AspCompat"); // ignored
-#if NET_2_0
-                       // these two are ignored for the moment
-                       atts.Remove ("Async");
-                       atts.Remove ("AsyncTimeOut");
-#endif
                        
-                       debug = GetBool (atts, "Debug", true);
+                       debug = GetBool (atts, "Debug", compConfig.Debug);
                        compilerOptions = GetString (atts, "CompilerOptions", "");
-                       language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
-                       strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
-                       explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
-                       linePragmasOn = GetBool (atts, "LinePragmas", false);
+                       language = GetString (atts, "Language", "");
+                       if (language.Length != 0)
+                               implicitLanguage = false;
+                       else
+                               language = compConfig.DefaultLanguage;
                        
+                       strictOn = GetBool (atts, "Strict", compConfig.Strict);
+                       explicitOn = GetBool (atts, "Explicit", compConfig.Explicit);
+                       if (atts.ContainsKey ("LinePragmas"))
+                               linePragmasOn = GetBool (atts, "LinePragmas", true);
+
                        string inherits = GetString (atts, "Inherits", null);
-#if NET_2_0
+                       string srcRealPath = null;
+                       
                        // In ASP 2, the source file is actually integrated with
                        // the generated file via the use of partial classes. This
                        // means that the code file has to be confirmed, but not
                        // used at this point.
                        src = GetString (atts, "CodeFile", null);
+                       codeFileBaseClass = GetString (atts, "CodeFileBaseClass", null);
+
+                       if (src == null && codeFileBaseClass != null)
+                               ThrowParseException ("The 'CodeFileBaseClass' attribute cannot be used without a 'CodeFile' attribute");
+
+                       string legacySrc = GetString (atts, "Src", null);
+                       if (legacySrc != null) {
+                               legacySrc = UrlUtils.Combine (BaseVirtualDir, legacySrc);
+                               GetAssemblyFromSource (legacySrc);
+
+                               if (src == null) {
+                                       src = legacySrc;
+                                       legacySrc = MapPath (legacySrc, false);
+                                       srcRealPath = legacySrc;
+                                       if (!File.Exists (srcRealPath))
+                                               ThrowParseException ("File " + src + " not found");
+                                       
+                                       srcIsLegacy = true;
+                               } else 
+                                       legacySrc = MapPath (legacySrc, false);                         
 
-                       if (src != null && inherits != null) {
+                               AddDependency (legacySrc);
+                       }
+                       
+                       if (!srcIsLegacy && src != null && inherits != null) {
                                // Make sure the source exists
                                src = UrlUtils.Combine (BaseVirtualDir, src);
-                               string realPath = MapPath (src, false);
-                               if (!File.Exists (realPath))
+                               srcRealPath = MapPath (src, false);
+
+                               if (!HostingEnvironment.VirtualPathProvider.FileExists (src))
                                        ThrowParseException ("File " + src + " not found");
 
                                // We are going to create a partial class that shares
@@ -556,112 +694,401 @@ namespace System.Web.UI {
 
                                // Add the code file as an option to the
                                // compiler. This lets both files be compiled at once.
-                               compilerOptions += " \"" + realPath + "\"";
+                               compilerOptions += " \"" + srcRealPath + "\"";
+
+                               if (codeFileBaseClass != null) {
+                                       try {
+                                               codeFileBaseClassType = LoadType (codeFileBaseClass);
+                                       } catch (Exception) {
+                                       }
+
+                                       if (codeFileBaseClassType == null)
+                                               ThrowParseException ("Could not load type '{0}'", codeFileBaseClass);
+                               }
                        } else if (inherits != null) {
                                // We just set the inherits directly because this is a
                                // Single-Page model.
                                SetBaseType (inherits);
                        }
-#else
-                       string src = GetString (atts, "Src", null);
-
-                       if (src != null)
-                               srcAssembly = GetAssemblyFromSource (src);
 
-                       if (inherits != null)
-                               SetBaseType (inherits);
-#endif
+                       if (src != null) {
+                               if (VirtualPathUtility.IsAbsolute (src))
+                                       src = VirtualPathUtility.ToAppRelative (src);
+                               AddDependency (src);
+                       }
+                       
                        className = GetString (atts, "ClassName", null);
-                       if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
-                               ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
+                       if (className != null) {
+                               string [] identifiers = className.Split ('.');
+                               for (int i = 0; i < identifiers.Length; i++)
+                                       if (!CodeGenerator.IsValidLanguageIndependentIdentifier (identifiers [i]))
+                                               ThrowParseException (String.Format ("'{0}' is not a valid "
+                                                       + "value for attribute 'classname'.", className));
+                       }
 
+                       if (this is TemplateControlParser)
+                               metaResourceKey = GetString (atts, "meta:resourcekey", null);
+                       
+                       if (inherits != null && (this is PageParser || this is UserControlParser) && atts.Count > 0) {
+                               if (unknownMainAttributes == null)
+                                       unknownMainAttributes = new List <UnknownAttributeDescriptor> ();
+                               string key, val;
+                               
+                               foreach (DictionaryEntry de in atts) {
+                                       key = de.Key as string;
+                                       val = de.Value as string;
+                                       
+                                       if (String.IsNullOrEmpty (key) || String.IsNullOrEmpty (val))
+                                               continue;
+                                       CheckUnknownAttribute (key, val, inherits);
+                               }
+                               return;
+                       }
 
                        if (atts.Count > 0)
                                ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
                }
 
-               internal void SetBaseType (string type)
+               void RegisterTagName (string tagName)
                {
-                       if (type == DefaultBaseTypeName)
+                       if (registeredTagNames == null)
+                               registeredTagNames = new List <string> ();
+
+                       if (registeredTagNames.Contains (tagName))
                                return;
 
-                       Type parent = null;
-                       if (srcAssembly != null)
-                               parent = srcAssembly.GetType (type);
+                       registeredTagNames.Add (tagName);
+               }
+               
+               void CheckUnknownAttribute (string name, string val, string inherits)
+               {
+                       MemberInfo mi = null;
+                       bool missing = false;
+                       string memberName = name.Trim ().ToLower (Helpers.InvariantCulture);
+                       Type parent = codeFileBaseClassType;
 
                        if (parent == null)
+                               parent = baseType;
+                       
+                       try {
+                               MemberInfo[] infos = parent.GetMember (memberName,
+                                                                      MemberTypes.Field | MemberTypes.Property,
+                                                                      BindingFlags.Public | BindingFlags.Instance |
+                                                                      BindingFlags.IgnoreCase | BindingFlags.Static);
+                               if (infos.Length != 0) {
+                                       // prefer public properties to public methods (it's what MS.NET does)
+                                       foreach (MemberInfo tmp in infos) {
+                                               if (tmp is PropertyInfo) {
+                                                       mi = tmp;
+                                                       break;
+                                               }
+                                       }
+                                       if (mi == null)
+                                               mi = infos [0];
+                               } else
+                                       missing = true;
+                       } catch (Exception) {
+                               missing = true;
+                       }
+                       if (missing)
+                               ThrowParseException (
+                                       "Error parsing attribute '{0}': Type '{1}' does not have a public property named '{0}'",
+                                       memberName, inherits);
+                       
+                       Type memberType = null;
+                       if (mi is PropertyInfo) {
+                               PropertyInfo pi = mi as PropertyInfo;
+                               
+                               if (!pi.CanWrite)
+                                       ThrowParseException (
+                                               "Error parsing attribute '{0}': The '{0}' property is read-only and cannot be set.",
+                                               memberName);
+                               memberType = pi.PropertyType;
+                       } else if (mi is FieldInfo) {
+                               memberType = ((FieldInfo)mi).FieldType;
+                       } else
+                               ThrowParseException ("Could not determine member the kind of '{0}' in base type '{1}",
+                                                    memberName, inherits);
+                       TypeConverter converter = TypeDescriptor.GetConverter (memberType);
+                       bool convertible = true;
+                       object value = null;
+                       
+                       if (converter == null || !converter.CanConvertFrom (typeof (string)))
+                               convertible = false;
+
+                       if (convertible) {
+                               try {
+                                       value = converter.ConvertFromInvariantString (val);
+                               } catch (Exception) {
+                                       convertible = false;
+                               }
+                       }
+
+                       if (!convertible)
+                               ThrowParseException ("Error parsing attribute '{0}': Cannot create an object of type '{1}' from its string representation '{2}' for the '{3}' property.",
+                                                    memberName, memberType, val, mi.Name);
+                       
+                       UnknownAttributeDescriptor desc = new UnknownAttributeDescriptor (mi, value);
+                       unknownMainAttributes.Add (desc);
+               }
+               
+               internal void SetBaseType (string type)
+               {
+                       Type parent;                    
+                       if (type == null || type == DefaultBaseTypeName)
+                               parent = DefaultBaseType;
+                       else
+                               parent = null;
+
+                       if (parent == null) {
                                parent = LoadType (type);
 
-                       if (parent == null)
-                               ThrowParseException ("Cannot find type " + type);
+                               if (parent == null)
+                                       ThrowParseException ("Cannot find type " + type);
 
-                       if (!DefaultBaseType.IsAssignableFrom (parent))
-                               ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
+                               if (!DefaultBaseType.IsAssignableFrom (parent))
+                                       ThrowParseException ("The parent type '" + type + "' does not derive from " + DefaultBaseType);
+                       }
 
+                       var pageParserFilter = PageParserFilter;
+                       if (pageParserFilter != null && !pageParserFilter.AllowBaseType (parent))
+                               throw new HttpException ("Base type '" + parent + "' is not allowed.");
+                       
                        baseType = parent;
-                       if (parent.FullName.IndexOf ('.') == -1)
-                               baseTypeIsGlobal = true;
                }
 
-               Assembly GetAssemblyFromSource (string vpath)
+               internal void SetLanguage (string language)
+               {
+                       this.language = language;
+                       implicitLanguage = false;
+               }
+
+               internal void PushIncludeDir (string dir)
+               {
+                       if (includeDirs == null)
+                               includeDirs = new Stack <string> (1);
+
+                       includeDirs.Push (dir);
+               }
+
+               internal string PopIncludeDir ()
                {
+                       if (includeDirs == null || includeDirs.Count == 0)
+                               return null;
+
+                       return includeDirs.Pop () as string;
+               }
+               
+               Assembly GetAssemblyFromSource (string vpath)
+               {                       
                        vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
                        string realPath = MapPath (vpath, false);
                        if (!File.Exists (realPath))
                                ThrowParseException ("File " + vpath + " not found");
 
-                       AddSourceDependency (realPath);
+                       AddSourceDependency (vpath);
+                       
+                       CompilerResults result;
+                       string tmp;
+                       CompilerParameters parameters;
+                       CodeDomProvider provider = BaseCompiler.CreateProvider (HttpContext.Current, language, out parameters, out tmp);
+                       if (provider == null)
+                               throw new HttpException ("Cannot find provider for language '" + language + "'.");
+                       
+                       AssemblyBuilder abuilder = new AssemblyBuilder (provider);
+                       abuilder.CompilerOptions = parameters;
+                       abuilder.AddAssemblyReference (BuildManager.GetReferencedAssemblies () as List <Assembly>);
+                       abuilder.AddCodeFile (realPath);
+                       result = abuilder.BuildAssembly (new VirtualPath (vpath));
 
-                       CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
                        if (result.NativeCompilerReturnValue != 0) {
-                               StreamReader reader = new StreamReader (realPath);
-                               throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
+                               using (StreamReader reader = new StreamReader (realPath)) {
+                                       throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
+                               }
                        }
 
                        AddAssembly (result.CompiledAssembly, true);
                        return result.CompiledAssembly;
-               }
-               
-               internal abstract Type DefaultBaseType { get; }
+               }               
+
                internal abstract string DefaultBaseTypeName { get; }
                internal abstract string DefaultDirectiveName { get; }
 
+               internal bool LinePragmasOn {
+                       get { return linePragmasOn; }
+               }
+               
+               internal byte[] MD5Checksum {
+                       get { return md5checksum; }
+                       set { md5checksum = value; }
+               }
+
+               internal PageParserFilter PageParserFilter {
+                       get {
+                               if (pageParserFilter != null)
+                                       return pageParserFilter;
+
+                               Type t = PageParserFilterType;
+                               if (t == null)
+                                       return null;
+                               
+                               pageParserFilter = Activator.CreateInstance (t) as PageParserFilter;
+                               pageParserFilter.Initialize (this);
+
+                               return pageParserFilter;
+                       }
+               }
+               
+               internal Type PageParserFilterType {
+                       get {
+                               if (pageParserFilterType == null) {
+                                       string typeName = PagesConfig.PageParserFilterType;
+                                       if (String.IsNullOrEmpty (typeName))
+                                               return null;
+                                       
+                                       pageParserFilterType = HttpApplication.LoadType (typeName, true);
+                               }
+                               
+                               return pageParserFilterType;
+                       }
+               }
+               
+               internal Type DefaultBaseType {
+                       get {
+                               Type type = Type.GetType (DefaultBaseTypeName, true);
+
+                               return type;
+                       }
+               }
+               
+               internal ILocation DirectiveLocation {
+                       get { return directiveLocation; }
+               }
+               
+               internal string ParserDir {
+                       get {
+                               if (includeDirs == null || includeDirs.Count == 0)
+                                       return BaseDir;
+
+                               return includeDirs.Peek () as string;
+                       }
+               }
+               
                internal string InputFile
                {
                        get { return inputFile; }
                        set { inputFile = value; }
                }
 
-#if NET_2_0
-               internal bool IsPartial
-               {
-                       get { return src != null; }
+               internal bool IsPartial {
+                       get { return (!srcIsLegacy && src != null); }
                }
 
-               internal string PartialClassName
-               {
+               internal string CodeBehindSource {
+                       get {
+                               if (srcIsLegacy)
+                                       return null;
+                               
+                               return src;
+                       }
+               }
+                       
+               internal string PartialClassName {
                        get { return partialClassName; }
                }
-#endif
 
-               internal string Text
+               internal string CodeFileBaseClass {
+                       get { return codeFileBaseClass; }
+               }
+
+               internal string MetaResourceKey {
+                       get { return metaResourceKey; }
+               }
+               
+               internal Type CodeFileBaseClassType
                {
+                       get { return codeFileBaseClassType; }
+               }
+               
+               internal List <UnknownAttributeDescriptor> UnknownMainAttributes
+               {
+                       get { return unknownMainAttributes; }
+               }
+
+               internal string Text {
                        get { return text; }
                        set { text = value; }
                }
 
-               internal Type BaseType
-               {
+               internal Type BaseType {
                        get {
                                if (baseType == null)
-                                       baseType = DefaultBaseType;
-
+                                       SetBaseType (DefaultBaseTypeName);
+                               
                                return baseType;
                        }
                }
                
                internal bool BaseTypeIsGlobal {
                        get { return baseTypeIsGlobal; }
+                       set { baseTypeIsGlobal = value; }
+               }
+
+               static long autoClassCounter = 0;
+
+               internal string EncodeIdentifier (string value)
+               {
+                       if (value == null || value.Length == 0 || CodeGenerator.IsValidLanguageIndependentIdentifier (value))
+                               return value;
+
+                       StringBuilder ret = new StringBuilder ();
+
+                       char ch = value [0];
+                       switch (Char.GetUnicodeCategory (ch)) {
+                               case UnicodeCategory.LetterNumber:
+                               case UnicodeCategory.LowercaseLetter:
+                               case UnicodeCategory.TitlecaseLetter:
+                               case UnicodeCategory.UppercaseLetter:
+                               case UnicodeCategory.OtherLetter:
+                               case UnicodeCategory.ModifierLetter:
+                               case UnicodeCategory.ConnectorPunctuation:
+                                       ret.Append (ch);
+                                       break;
+
+                               case UnicodeCategory.DecimalDigitNumber:
+                                       ret.Append ('_');
+                                       ret.Append (ch);
+                                       break;
+                                       
+                               default:
+                                       ret.Append ('_');
+                                       break;
+                       }
+
+                       for (int i = 1; i < value.Length; i++) {
+                               ch = value [i];
+                               switch (Char.GetUnicodeCategory (ch)) {
+                                       case UnicodeCategory.LetterNumber:
+                                       case UnicodeCategory.LowercaseLetter:
+                                       case UnicodeCategory.TitlecaseLetter:
+                                       case UnicodeCategory.UppercaseLetter:
+                                       case UnicodeCategory.OtherLetter:
+                                       case UnicodeCategory.ModifierLetter:
+                                       case UnicodeCategory.ConnectorPunctuation:
+                                       case UnicodeCategory.DecimalDigitNumber:
+                                       case UnicodeCategory.NonSpacingMark:
+                                       case UnicodeCategory.SpacingCombiningMark:
+                                       case UnicodeCategory.Format:
+                                               ret.Append (ch);
+                                               break;
+                                               
+                                       default:
+                                               ret.Append ('_');
+                                               break;
+                               }
+                       }
+
+                       return ret.ToString ();
                }
                
                internal string ClassName {
@@ -669,52 +1096,55 @@ namespace System.Web.UI {
                                if (className != null)
                                        return className;
 
-#if NET_2_0
                                string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
+                               string inFile;
                                
-                               if (StrUtils.StartsWith (inputFile, physPath)) {
-                                       className = inputFile.Substring (physPath.Length).ToLower (CultureInfo.InvariantCulture);
-                                       className = className.Replace ('.', '_');
-                                       className = className.Replace ('/', '_').Replace ('\\', '_');
+                               if (String.IsNullOrEmpty (inputFile)) {
+                                       inFile = null;
+                                       using (StreamReader sr = Reader as StreamReader) {
+                                               if (sr != null) {
+                                                       FileStream fr = sr.BaseStream as FileStream;
+                                                       if (fr != null)
+                                                               inFile = fr.Name;
+                                               }
+                                       }
                                } else
-#endif
-                               className = Path.GetFileName (inputFile).Replace ('.', '_');
-                               className = className.Replace ('-', '_'); 
-                               className = className.Replace (' ', '_');
+                                       inFile = inputFile;
 
-                               if (Char.IsDigit(className[0])) {
-                                       className = "_" + className;
+                               if (String.IsNullOrEmpty (inFile)) {
+                                       // generate a unique class name
+                                       long suffix;
+                                       suffix = Interlocked.Increment (ref autoClassCounter);
+                                       className = String.Format ("autoclass_nosource_{0:x}", suffix);
+                                       return className;
                                }
-
+                               
+                               if (StrUtils.StartsWith (inFile, physPath))
+                                       className = inputFile.Substring (physPath.Length).ToLower (Helpers.InvariantCulture);
+                               else
+                                       className = Path.GetFileName (inputFile);
+                               className = EncodeIdentifier (className);
                                return className;
                        }
                }
 
-               internal string PrivateBinPath {
-                       get {
-                               if (privateBinPath != null)
-                                       return privateBinPath;
-
-                               AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
-                               privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
-
-                               return privateBinPath;
-                       }
-               }
-
-               internal ArrayList Scripts {
+               internal List <ServerSideScript> Scripts {
                        get {
                                if (scripts == null)
-                                       scripts = new ArrayList ();
+                                       scripts = new List <ServerSideScript> ();
 
                                return scripts;
                        }
                }
 
-               internal ArrayList Imports {
+               internal Dictionary <string, bool> Imports {
                        get { return imports; }
                }
 
+               internal List <string> Interfaces {
+                       get { return interfaces; }
+               }
+               
                internal ArrayList Assemblies {
                        get {
                                if (appAssemblyIndex != -1) {
@@ -728,12 +1158,16 @@ namespace System.Web.UI {
                        }
                }
 
-               internal ArrayList Interfaces {
-                       get { return interfaces; }
-               }
-
                internal RootBuilder RootBuilder {
-                       get { return rootBuilder; }
+                       get {
+                               if (rootBuilder != null)
+                                       return rootBuilder;
+                               AspGenerator generator = AspGenerator;
+                               if (generator != null)
+                                       rootBuilder = generator.RootBuilder;
+
+                               return rootBuilder;
+                       }
                        set { rootBuilder = value; }
                }
 
@@ -750,6 +1184,10 @@ namespace System.Web.UI {
                        get { return language; }
                }
 
+               internal bool ImplicitLanguage {
+                       get { return implicitLanguage; }
+               }
+               
                internal bool StrictOn {
                        get { return strictOn; }
                }
@@ -770,6 +1208,31 @@ namespace System.Web.UI {
                        get { return oc_duration; }
                }
 
+               internal OutputCacheParsedParams OutputCacheParsedParameters {
+                       get { return oc_parsed_params; }
+               }
+
+               internal string OutputCacheSqlDependency {
+                       get { return oc_sqldependency; }
+               }
+               
+               internal string OutputCacheCacheProfile {
+                       get { return oc_cacheprofile; }
+               }
+               
+               internal string OutputCacheVaryByContentEncodings {
+                       get { return oc_content_encodings; }
+               }
+
+               internal bool OutputCacheNoStore {
+                       get { return oc_nostore; }
+               }
+               
+               internal virtual TextReader Reader {
+                       get { return null; }
+                       set { /* no-op */ }
+               }
+               
                internal string OutputCacheVaryByHeader {
                        get { return oc_header; }
                }
@@ -794,17 +1257,17 @@ namespace System.Web.UI {
                        get { return oc_param; }
                }
 
-#if NET_2_0
+               internal List <string> RegisteredTagNames {
+                       get { return registeredTagNames; }
+               }
+               
                internal PagesSection PagesConfig {
-                       get {
-                               return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
-                       }
+                       get { return GetConfigSection <PagesSection> ("system.web/pages") as PagesSection; }
                }
-#else
-               internal PagesConfiguration PagesConfig {
-                       get { return PagesConfiguration.GetInstance (Context); }
+
+               internal AspGenerator AspGenerator {
+                       get;
+                       set;
                }
-#endif
        }
-}
-
+}
\ No newline at end of file