2009-12-14 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
index 9a566676810a9c7c1e5546b6a1796dc4dabed307..ed162cafeae522d7146a66b7a35e154bec9816f7 100644 (file)
@@ -3,9 +3,10 @@
 //
 // Authors:
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//      Marek Habersack <mhabersack@novell.com>
 //
 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
-// Copyright (c) 2004,2006 Novell, Inc (http://www.novell.com)
+// Copyright (c) 2004-2009 Novell, Inc (http://www.novell.com)
 //
 
 //
@@ -30,6 +31,7 @@
 //
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.CodeDom.Compiler;
 using System.Globalization;
 using System.IO;
@@ -42,10 +44,6 @@ using System.Web.UI;
 using System.Web.UI.HtmlControls;
 using System.Web.Util;
 
-#if NET_2_0
-using System.Collections.Generic;
-#endif
-
 namespace System.Web.Compilation
 {
        class BuilderLocation
@@ -56,7 +54,7 @@ namespace System.Web.Compilation
                public BuilderLocation (ControlBuilder builder, ILocation location)
                {
                        this.Builder = builder;
-                       this.Location = location;
+                       this.Location = new Location (location);
                }
        }
 
@@ -169,7 +167,7 @@ namespace System.Web.Compilation
                        if (tags.Count == 0)
                                return false;
 
-                       return 0 == String.Compare (tagid, (string) tags.Peek (), true, CultureInfo.InvariantCulture);
+                       return 0 == String.Compare (tagid, (string) tags.Peek (), true, Helpers.InvariantCulture);
                }
                
                public int Count {
@@ -181,13 +179,58 @@ namespace System.Web.Compilation
                }
        }
 
+       enum TextBlockType
+       {
+               Verbatim,
+               Expression,
+               Tag,
+               Comment
+       }
+       
+       sealed class TextBlock
+       {
+               public string Content;
+               public readonly TextBlockType Type;
+               public readonly int Length;
+               
+               public TextBlock (TextBlockType type, string content)
+               {
+                       Content = content;
+                       Type = type;
+                       Length = content.Length;
+               }
+
+               public override string ToString ()
+               {
+                       return this.GetType ().FullName + " [" + this.Type + "]";
+               }
+       }
+       
        class AspGenerator
        {
-#if NET_2_0
                const int READ_BUFFER_SIZE = 8192;
                
                internal static Regex DirectiveRegex = new Regex (@"<%\s*@(\s*(?<attrname>\w[\w:]*(?=\W))(\s*(?<equal>=)\s*""(?<attrval>[^""]*)""|\s*(?<equal>=)\s*'(?<attrval>[^']*)'|\s*(?<equal>=)\s*(?<attrval>[^\s%>]*)|(?<equal>)(?<attrval>\s*?)))*\s*?%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-#endif
+
+               static readonly Regex runatServer = new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/?>",
+                                                              RegexOptions.Compiled | RegexOptions.Singleline |
+                                                              RegexOptions.Multiline | RegexOptions.IgnoreCase |
+                                                              RegexOptions.CultureInvariant);
+               
+               static readonly Regex endOfTag = new Regex (@"</[\w:\.]+\s*?>",
+                                                           RegexOptions.Compiled | RegexOptions.Singleline |
+                                                           RegexOptions.Multiline | RegexOptions.IgnoreCase |
+                                                           RegexOptions.CultureInvariant);
+               
+               static readonly Regex expressionRegex = new Regex (@"<%.*?%>",
+                                                                  RegexOptions.Compiled | RegexOptions.Singleline |
+                                                                  RegexOptions.Multiline | RegexOptions.IgnoreCase |
+                                                                  RegexOptions.CultureInvariant);
+
+               static readonly Regex clientCommentRegex = new Regex (@"<!--(.|\s)*?-->",
+                                                                     RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase |
+                                                                     RegexOptions.CultureInvariant);
+               
                ParserStack pstack;
                BuilderLocationStack stack;
                TemplateParser tparser;
@@ -201,7 +244,6 @@ namespace System.Web.Compilation
                bool inForm;
                bool useOtherTags;
                TagType lastTag;
-#if NET_2_0
                AspComponentFoundry componentFoundry;
                Stream inputStream;
 
@@ -209,19 +251,12 @@ namespace System.Web.Compilation
                {
                        this.componentFoundry = componentFoundry;
                }
-#endif
                
                public AspGenerator (TemplateParser tparser)
                {
                        this.tparser = tparser;
                        text = new StringBuilder ();
                        stack = new BuilderLocationStack ();
-
-#if !NET_2_0
-                       rootBuilder = new RootBuilder (tparser);
-                       tparser.RootBuilder = rootBuilder;
-                       stack.Push (rootBuilder, null);
-#endif
                        pstack = new ParserStack ();
                }
 
@@ -237,7 +272,6 @@ namespace System.Web.Compilation
                        get { return pstack.Filename; }
                }
 
-#if NET_2_0
                PageParserFilter PageParserFilter {
                        get {
                                if (tparser == null)
@@ -337,7 +371,7 @@ namespace System.Web.Compilation
                        if (matches == null || matches.Count == 0)
                                return null;
 
-                       string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower ();
+                       string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower (Helpers.InvariantCulture);
                        string directiveName;
                        GroupCollection groups;
                        CaptureCollection ccNames;
@@ -352,7 +386,7 @@ namespace System.Web.Compilation
                                if (String.IsNullOrEmpty (directiveName))
                                        continue;
                                
-                               if (String.Compare (directiveName.ToLower (), wantedDirectiveName, StringComparison.Ordinal) != 0)
+                               if (String.Compare (directiveName.ToLower (Helpers.InvariantCulture), wantedDirectiveName, StringComparison.Ordinal) != 0)
                                        continue;
 
                                var loc = new Location (null);
@@ -476,7 +510,6 @@ namespace System.Web.Compilation
                        stack.Push (rootBuilder, null);
                        tparser.RootBuilder = rootBuilder;
                }
-#endif
                
                BaseCompiler GetCompilerFromType ()
                {
@@ -489,10 +522,9 @@ namespace System.Web.Compilation
 
                        if (type == typeof (UserControlParser))
                                return new UserControlCompiler ((UserControlParser) tparser);
-#if NET_2_0
+
                        if (type == typeof(MasterPageParser))
                                return new MasterPageCompiler ((MasterPageParser) tparser);
-#endif
 
                        throw new Exception ("Got type: " + type);
                }
@@ -503,11 +535,10 @@ namespace System.Web.Compilation
                        parser.Error += new ParseErrorHandler (ParseError);
                        parser.TagParsed += new TagParsedHandler (TagParsed);
                        parser.TextParsed += new TextParsedHandler (TextParsed);
-#if NET_2_0
                        parser.ParsingComplete += new ParsingCompleteHandler (ParsingCompleted);
                        tparser.AspGenerator = this;
                        CreateRootBuilder (inputStream, filename);
-#endif
+
                        if (!pstack.Push (parser))
                                throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
 
@@ -520,13 +551,11 @@ namespace System.Web.Compilation
                        }
                }
                
-#if NET_2_0
                void InitParser (string filename)
                {
                        StreamReader reader = new StreamReader (filename, WebEncoding.FileEncoding);
                        InitParser (reader, filename);
                }
-#endif
 
                void CheckForDuplicateIds (ControlBuilder root, Stack scopes)
                {
@@ -534,27 +563,15 @@ namespace System.Web.Compilation
                                return;
                        
                        if (scopes == null)
-                               scopes = new Stack ();
-                       
-#if NET_2_0
+                               scopes = new Stack ();                  
+
                        Dictionary <string, bool> ids;
-#else
-                       Hashtable ids;
-#endif
                        
                        if (scopes.Count == 0 || root.IsNamingContainer) {
-#if NET_2_0
                                ids = new Dictionary <string, bool> (StringComparer.Ordinal);
-#else
-                               ids = new Hashtable ();
-#endif
                                scopes.Push (ids);
                        } else {
-#if NET_2_0
                                ids = scopes.Peek () as Dictionary <string, bool>;
-#else
-                               ids = scopes.Peek () as Hashtable;
-#endif
                        }
                        
                        if (ids == null)
@@ -584,11 +601,7 @@ namespace System.Web.Compilation
                
                public void Parse (string file)
                {
-#if ONLY_1_1
-                       Parse (file, true);
-#else
                        Parse (file, false);
-#endif
                }
                
                public void Parse (TextReader reader, string filename, bool doInitParser)
@@ -603,9 +616,7 @@ namespace System.Web.Compilation
                                if (text.Length > 0)
                                        FlushText ();
 
-#if NET_2_0
                                tparser.MD5Checksum = pstack.Parser.MD5Checksum;
-#endif
                                pstack.Pop ();
 
 #if DEBUG
@@ -625,9 +636,7 @@ namespace System.Web.Compilation
 
                public void Parse (Stream stream, string filename, bool doInitParser)
                {
-#if NET_2_0
                        inputStream = stream;
-#endif
                        Parse (new StreamReader (stream, WebEncoding.FileEncoding), filename, doInitParser);
                }
                
@@ -639,7 +648,6 @@ namespace System.Web.Compilation
 
                public void Parse ()
                {
-#if NET_2_0
                        string inputFile = tparser.InputFile;
                        TextReader inputReader = tparser.Reader;
 
@@ -669,9 +677,6 @@ namespace System.Web.Compilation
                                if (inputReader != null)
                                        inputReader.Close ();
                        }
-#else
-                       Parse (Path.GetFullPath (tparser.InputFile));
-#endif
                }
 
                internal static void AddTypeToCache (ArrayList dependencies, string inputFile, Type type)
@@ -703,7 +708,6 @@ namespace System.Web.Compilation
                        }
 
                        Parse ();
-
                        BaseCompiler compiler = GetCompilerFromType ();
                        
                        type = compiler.GetCompiledType ();
@@ -757,10 +761,6 @@ namespace System.Web.Compilation
                // The kludge supports only self-closing tags inside attributes.
                //
                // KLUDGE WARNING!!
-               static readonly Regex runatServer=new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/>",
-                                                            RegexOptions.Compiled | RegexOptions.Singleline |
-                                                            RegexOptions.Multiline | RegexOptions.IgnoreCase |
-                                                            RegexOptions.CultureInvariant);
                bool ProcessTagsInAttributes (ILocation location, string tagid, TagAttributes attributes, TagType type)
                {
                        if (attributes == null || attributes.Count == 0)
@@ -798,7 +798,7 @@ namespace System.Web.Compilation
 
                                TextParsed (location, String.Format (" {0}=\"{1}", key, index > 0 ? value.Substring (0, index) : String.Empty));;
                                FlushText ();
-                               ParseAttributeTag (group.Value);
+                               ParseAttributeTag (group.Value, location);
                                if (index + length < value.Length)
                                        TextParsed (location, value.Substring (index + length) + "\"");
                                else
@@ -815,9 +815,11 @@ namespace System.Web.Compilation
                        return retval;
                }
 
-               void ParseAttributeTag (string code)
+               void ParseAttributeTag (string code, ILocation location)
                {
-                       AspParser parser = new AspParser ("@@attribute_tag@@", new StringReader (code));
+                       AspParser outerParser = location as AspParser;
+                       int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+                       AspParser parser = new AspParser ("@@attribute_tag@@", new StringReader (code), location.BeginLine - 1, positionOffset, outerParser);
                        parser.Error += new ParseErrorHandler (ParseError);
                        parser.TagParsed += new TagParsedHandler (TagParsed);
                        parser.TextParsed += new TextParsedHandler (TextParsed);
@@ -826,7 +828,6 @@ namespace System.Web.Compilation
                                FlushText ();
                }
 
-#if NET_2_0
                void ParsingCompleted ()
                {
                        PageParserFilter pfilter = PageParserFilter;
@@ -835,7 +836,6 @@ namespace System.Web.Compilation
 
                        pfilter.ParseComplete (RootBuilder);
                }
-#endif
 
                void CheckIfIncludeFileIsSecure (string filePath)
                {
@@ -864,6 +864,17 @@ namespace System.Web.Compilation
                        if (exception != null || !StrUtils.StartsWith (newdir, HttpRuntime.AppDomainAppPath))
                                throw new ParseException (Location, "Files above the application's root directory cannot be included.");
                }
+
+               string ChopOffTagStart (ILocation location, string content, string tagid)
+               {
+                       string tagstart = '<' + tagid;
+                       if (content.StartsWith (tagstart)) {
+                               TextParsed (location, tagstart);
+                               content = content.Substring (tagstart.Length);
+                       }
+
+                       return content;
+               }
                
                void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
                {
@@ -876,7 +887,7 @@ namespace System.Web.Compilation
                        if (text.Length != 0)
                                FlushText (lastTag == TagType.CodeRender);
 
-                       if (0 == String.Compare (tagid, "script", true, CultureInfo.InvariantCulture)) {
+                       if (0 == String.Compare (tagid, "script", true, Helpers.InvariantCulture)) {
                                bool in_script = (inScript || ignore_text);
                                if (in_script) {
                                        if (ProcessScript (tagtype, attributes))
@@ -909,7 +920,7 @@ namespace System.Web.Compilation
                                {
                                        string plainText = location.PlainText;
                                        if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.Tag))
-                                               TextParsed (location, plainText);
+                                               TextParsed (location, ChopOffTagStart (location, plainText, tagid));
                                }
                                break;
                        case TagType.Close:
@@ -924,7 +935,7 @@ namespace System.Web.Compilation
                                if (!ProcessTag (location, tagid, attributes, tagtype, out tagIgnored) && !tagIgnored) {
                                        string plainText = location.PlainText;
                                        if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.SelfClosing))
-                                               TextParsed (location, plainText);
+                                               TextParsed (location, ChopOffTagStart (location, plainText, tagid));
                                } else if (stack.Count != count) {
                                        CloseControl (tagid);
                                }
@@ -950,7 +961,6 @@ namespace System.Web.Compilation
 
                                if (isvirtual) {
                                        bool parsed = false;
-#if NET_2_0
                                        VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
 
                                        if (vpp.FileExists (file)) {
@@ -960,7 +970,6 @@ namespace System.Web.Compilation
                                                        parsed = true;
                                                }
                                        }
-#endif
                                        
                                        if (!parsed)
                                                Parse (tparser.MapPath (file), true);
@@ -989,7 +998,7 @@ namespace System.Web.Compilation
 
                        for (int idx = otags.Count - 1; idx >= 0; idx--) {
                                string otagid = (string) otags [idx];
-                               if (0 == String.Compare (tagid, otagid, true, CultureInfo.InvariantCulture)) {
+                               if (0 == String.Compare (tagid, otagid, true, Helpers.InvariantCulture)) {
                                        do {
                                                otags.RemoveAt (idx);
                                        } while (otags.Count - 1 >= idx);
@@ -1006,118 +1015,122 @@ namespace System.Web.Compilation
 
                        return Path.GetFullPath (Path.Combine (basedir, filename));
                }
+
+               delegate bool CheckBlockEnd (string text);
                
-               void TextParsed (ILocation location, string text)
+               bool CheckTagEndNeeded (string text)
                {
-                       if (ignore_text)
-                               return;
-                       
-                       // Another gross hack - get rid of comments in the parsed text
-                       int textLen = text.Length;
-                       int textMaxIndex = textLen - 1;
-                       int commentStart = text.IndexOf ("<!--");
-                       int commentLastStart = 0;
-#if NET_2_0
-                       List <int> commentRanges = null;
-#else
-                       ArrayList commentRanges = null;
-#endif
+                       return !text.EndsWith ("/>");
+               }
+               
+               List <TextBlock> FindRegexBlocks (Regex rxStart, Regex rxEnd, CheckBlockEnd checkEnd, IList blocks, TextBlockType typeForMatches, bool discardBlocks)
+               {
+                       var ret = new List <TextBlock> ();
+                       foreach (TextBlock block in blocks) {
+                               if (block.Type != TextBlockType.Verbatim) {
+                                       ret.Add (block);
+                                       continue;
+                               }
 
-                       while (commentStart != -1) {
-                               int commentEnd = text.IndexOf ("-->", commentStart);
+                               int lastIndex = 0, index;
+                               MatchCollection matches = rxStart.Matches (block.Content);
+                               bool foundMatches = matches.Count > 0;
+                               foreach (Match match in matches) {
+                                       foundMatches = true;
+                                       index = match.Index;
+                                       if (lastIndex < index)
+                                               ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex, index - lastIndex)));
 
-                               if (commentEnd == -1) {
-                                       if (commentStart == 0)
-                                               return;
-                                       commentEnd = textMaxIndex;
-                               }
+                                       string value = match.Value;
+                                       if (rxEnd != null && checkEnd (value)) {
+                                               int startFrom = index + value.Length;
+                                               Match m = rxEnd.Match (block.Content, startFrom);
+                                               if (m.Success)
+                                                       value += block.Content.Substring (startFrom, m.Index - startFrom) + m.Value;
+                                       }
 
-                               if (commentRanges == null) {
-#if NET_2_0
-                                       commentRanges = new List <int> ();
-#else
-                                       commentRanges = new ArrayList ();
-#endif
+                                       if (!discardBlocks)
+                                               ret.Add (new TextBlock (typeForMatches, value));
+                                       lastIndex = index + value.Length;
                                }
 
-                               if (commentStart > commentLastStart) {
-                                       commentRanges.Add (commentLastStart);
-                                       commentRanges.Add (commentStart);
-                               }
+                               if (lastIndex > 0 && lastIndex < block.Content.Length)
+                                       ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex)));
 
-                               if (commentEnd == textMaxIndex)
-                                       break;
-                               
-                               commentLastStart = commentEnd + 3;
-                               if (commentLastStart > textMaxIndex)
-                                       break;
-                               
-                               commentStart = text.IndexOf ("<!--", commentLastStart);
-                               if (commentStart == -1) {
-                                       int tailLength = textMaxIndex - commentLastStart;
-                                       if (tailLength > 0) {
-                                               commentRanges.Add (commentLastStart);
-                                               commentRanges.Add (tailLength);
-                                       }
-                                       break;
-                               }
+                               if (!foundMatches)
+                                       ret.Add (block);
                        }
 
-                       if (commentRanges != null) {
-                               if (commentRanges.Count == 0)
-                                       return;
-                               
-                               StringBuilder sb = new StringBuilder ();
-                               for (int i = 0; i < commentRanges.Count; i += 2) {
-#if NET_2_0
-                                       sb.Append (text.Substring (commentRanges [i], commentRanges [i + 1]));
-#else
-                                       sb.Append (text.Substring ((int)commentRanges [i], (int)commentRanges [i + 1]));
-#endif
-                               }
+                       return ret;
+               }
+               
+               IList SplitTextIntoBlocks (string text)
+               {
+                       var ret = new List <TextBlock> ();
 
-                               string noComments = sb.ToString ().Trim ();
-                               if (noComments.Length == 0)
-                                       return;
+                       ret.Add (new TextBlock (TextBlockType.Verbatim, text));
+                       ret = FindRegexBlocks (clientCommentRegex, null, null, ret, TextBlockType.Comment, false);
+                       ret = FindRegexBlocks (runatServer, endOfTag, CheckTagEndNeeded, ret, TextBlockType.Tag, false);
+                       ret = FindRegexBlocks (expressionRegex, null, null, ret, TextBlockType.Expression, false);
 
-                               text = noComments;
-                       }
+                       return ret;
+               }
 
-                       // And again... the first one wins - if we have expressions and server-side
-                       // controls together in one block of plain text, tough luck...
-                       if (text.IndexOf ("<%") != -1 && !inScript) {
-                               if (this.text.Length > 0)
-                                       FlushText (true);
-                               CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
-                               r.AddChildren (this);
+               void TextParsed (ILocation location, string text)
+               {
+                       if (ignore_text)
+                               return;
+
+                       if (inScript) {
+                               this.text.Append (text);
+                               FlushText (true);
                                return;
                        }
 
-                       int startIndex = 0, index = 0;
-                       Match match;
+                       IList blocks = SplitTextIntoBlocks (text);
+                       foreach (TextBlock block in blocks) {
+                               switch (block.Type) {
+                                       case TextBlockType.Verbatim:
+                                               this.text.Append (block.Content);
+                                               break;
 
-                       while (index > -1 && startIndex < textLen) {
-                               match = runatServer.Match (text, index);
-                                       
-                               if (match.Success) {
-                                       string value = match.Value;
-                                       index = match.Index;
-                                       if (index > startIndex)
-                                               this.text.Append (text.Substring (startIndex, index - startIndex));
-                                       ParseAttributeTag (value);
-                                       index += value.Length;
-                                       startIndex = index;
-                               } else
-                                       break;
+                                       case TextBlockType.Expression:
+                                               if (this.text.Length > 0)
+                                                       FlushText (true);
+                                               CodeRenderParser r = new CodeRenderParser (block.Content, stack.Builder, location);
+                                               r.AddChildren (this);
+                                               break;
 
-                               if (index < textLen)
-                                       index = text.IndexOf ('<', index);
-                               else
-                                       break;
+                                       case TextBlockType.Tag:
+                                               ParseAttributeTag (block.Content, location);
+                                               break;
+
+                                       case TextBlockType.Comment: {
+                                               this.text.Append ("<!--");
+                                               FlushText (true);
+                                               string blockToParse = block.Content.Substring (4, block.Length - 7);
+                                               bool condEndif;
+                                               if (blockToParse.EndsWith ("<![endif]")) {
+                                                       blockToParse = blockToParse.Substring (0, blockToParse.Length - 9);
+                                                       condEndif = true;
+                                               } else
+                                                       condEndif = false;
+
+                                               AspParser outerParser = location as AspParser;
+                                               int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+                                               AspParser parser = new AspParser ("@@comment_code@@", new StringReader (blockToParse), location.BeginLine - 1, positionOffset, outerParser);
+                                               parser.Error += new ParseErrorHandler (ParseError);
+                                               parser.TagParsed += new TagParsedHandler (TagParsed);
+                                               parser.TextParsed += new TextParsedHandler (TextParsed);
+                                               parser.Parse ();
+                                               if (condEndif)
+                                                       this.text.Append ("<![endif]");
+                                               this.text.Append ("-->");
+                                               FlushText (true);
+                                               break;
+                                       }
+                               }
                        }
-                       
-                       this.text.Append (text.Substring (startIndex));
-                       //PrintLocation (location);
                }
 
                void FlushText ()
@@ -1134,11 +1147,10 @@ namespace System.Web.Compilation
                                return;
                        
                        if (inScript) {
-#if NET_2_0
                                PageParserFilter pfilter = PageParserFilter;
                                if (pfilter != null && !pfilter.ProcessCodeConstruct (CodeConstructType.ScriptTag, t))
                                        return;
-#endif
+
                                tparser.Scripts.Add (new ServerSideScript (t, new System.Web.Compilation.Location (tparser.Location)));
                                return;
                        }
@@ -1153,7 +1165,6 @@ namespace System.Web.Compilation
                        }
                }
 
-#if NET_2_0
                bool BuilderHasOtherThan (Type type, ControlBuilder cb)
                {
                        ArrayList al = cb.OtherTags;
@@ -1201,7 +1212,6 @@ namespace System.Web.Compilation
                        
                        return true;
                }
-#endif
 
                public void AddControl (Type type, IDictionary attributes)
                {
@@ -1217,15 +1227,15 @@ namespace System.Web.Compilation
                {
                        ignored = false;
                        if (isApplication) {
-                               if (String.Compare (tagid, "object", true, CultureInfo.InvariantCulture) != 0)
+                               if (String.Compare (tagid, "object", true, Helpers.InvariantCulture) != 0)
                                        throw new ParseException (location, "Invalid tag for application file.");
                        }
 
                        ControlBuilder parent = stack.Builder;
                        ControlBuilder builder = null;
                        if (parent != null && parent.ControlType == typeof (HtmlTable) &&
-                           (String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0 ||
-                            String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0)) {
+                           (String.Compare (tagid, "thead", true, Helpers.InvariantCulture) == 0 ||
+                            String.Compare (tagid, "tbody", true, Helpers.InvariantCulture) == 0)) {
                                ignored = true;
                                return true;
                        }
@@ -1270,14 +1280,13 @@ namespace System.Web.Compilation
                        string plainText = location.PlainText;
                        if (!runatServer && plainText.IndexOf ("<%$") == -1&& plainText.IndexOf ("<%") > -1)
                                return false;
-#if NET_2_0
+
                        PageParserFilter pfilter = PageParserFilter;
                        if (pfilter != null && !pfilter.AllowControl (builder.ControlType, builder))
                                throw new ParseException (Location, "Control type '" + builder.ControlType + "' not allowed.");
                        
                        if (!OtherControlsAllowed (builder))
                                throw new ParseException (Location, "Only Content controls are allowed directly in a content page that contains Content controls.");
-#endif
                        
                        builder.Location = location;
                        builder.ID = htable ["id"] as string;
@@ -1331,7 +1340,7 @@ namespace System.Web.Compilation
                                        CheckLanguage (language);
                                        string src = (string) attributes ["src"];
                                        if (src != null) {
-                                               if (src == "")
+                                               if (src.Length == 0)
                                                        throw new ParseException (Parser,
                                                                "src cannot be an empty string");
 
@@ -1355,7 +1364,14 @@ namespace System.Web.Compilation
                                                Parser.VerbatimID = "script";
                                                javascript = true;
                                        }
-                                       TextParsed (location, location.PlainText);
+                                       string content = location.PlainText;
+                                       /* HACK, HACK, HACK */
+                                       if (content.StartsWith ("<script")) {
+                                               TextParsed (location, "<script");
+                                               content = content.Substring (7);
+                                       }
+
+                                       TextParsed (location, content);
                                        return true;
                                }
                        }
@@ -1380,8 +1396,8 @@ namespace System.Web.Compilation
                {
                        ControlBuilder current = stack.Builder;
                        string btag = current.OriginalTagName;
-                       if (String.Compare (btag, "tbody", true, CultureInfo.InvariantCulture) != 0 &&
-                           String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0) {
+                       if (String.Compare (btag, "tbody", true, Helpers.InvariantCulture) != 0 &&
+                           String.Compare (tagid, "tbody", true, Helpers.InvariantCulture) == 0) {
                                if (!current.ChildrenAsProperties) {
                                        try {
                                                TextParsed (location, location.PlainText);
@@ -1391,10 +1407,10 @@ namespace System.Web.Compilation
                                return true;
                        }
 
-                       if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0)
+                       if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, Helpers.InvariantCulture) == 0)
                                return true;
                        
-                       if (0 != String.Compare (tagid, btag, true, CultureInfo.InvariantCulture))
+                       if (0 != String.Compare (tagid, btag, true, Helpers.InvariantCulture))
                                return false;
 
                        // if (current is TemplateBuilder)
@@ -1419,7 +1435,6 @@ namespace System.Web.Compilation
                        return true;
                }
 
-#if NET_2_0
                CodeConstructType MapTagTypeToConstructType (TagType tagtype)
                {
                        switch (tagtype) {
@@ -1437,10 +1452,8 @@ namespace System.Web.Compilation
                        }
                }
                
-#endif
                bool ProcessCode (TagType tagtype, string code, ILocation location)
                {
-#if NET_2_0
                        PageParserFilter pfilter = PageParserFilter;
                        // LAMESPEC:
                        //
@@ -1453,7 +1466,7 @@ namespace System.Web.Compilation
                        //
                        if (pfilter != null && (!pfilter.AllowCode || pfilter.ProcessCodeConstruct (MapTagTypeToConstructType (tagtype), code)))
                                return true;
-#endif
+
                        ControlBuilder b = null;
                        if (tagtype == TagType.CodeRender)
                                b = new CodeRenderBuilder (code, false, location);
@@ -1477,16 +1490,11 @@ namespace System.Web.Compilation
                        if (lang == null || lang == "")
                                return;
 
-                       if (String.Compare (lang, tparser.Language, true, CultureInfo.InvariantCulture) == 0)
+                       if (String.Compare (lang, tparser.Language, true, Helpers.InvariantCulture) == 0)
                                return;
 
-#if NET_2_0
                        CompilationSection section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
                        if (section.Compilers[tparser.Language] != section.Compilers[lang])
-#else
-                       CompilationConfiguration cfg = CompilationConfiguration.GetInstance (HttpContext.Current); 
-                       if (!cfg.Compilers.CompareLanguages (tparser.Language, lang))
-#endif
                                throw new ParseException (Location,
                                                String.Format ("Trying to mix language '{0}' and '{1}'.", 
                                                                tparser.Language, lang));
@@ -1498,11 +1506,13 @@ namespace System.Web.Compilation
                        string str;
                        ControlBuilder builder;
                        AspGenerator generator;
+                       ILocation location;
                        
-                       public CodeRenderParser (string str, ControlBuilder builder)
+                       public CodeRenderParser (string str, ControlBuilder builder, ILocation location)
                        {
                                this.str = str;
                                this.builder = builder;
+                               this.location = location;
                        }
 
                        public void AddChildren (AspGenerator generator)
@@ -1518,7 +1528,7 @@ namespace System.Web.Compilation
                        void DoParseExpressions (string str)
                        {
                                int startIndex = 0, index = 0;
-                               Regex codeDirective = new Regex ("(<%(?!@)(?<code>.*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
+                               Regex codeDirective = new Regex ("(<%(?!@)(?<code>(.|\\s)*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
                                                                 RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
                                Match match;
                                int strLen = str.Length;
@@ -1549,7 +1559,9 @@ namespace System.Web.Compilation
                        
                        void DoParse (string str)
                        {
-                               AspParser parser = new AspParser ("@@nested_tag@@", new StringReader (str));
+                               AspParser outerParser = location as AspParser;
+                               int positionOffset = outerParser != null ? outerParser.BeginPosition : 0;
+                               AspParser parser = new AspParser ("@@code_render@@", new StringReader (str), location.BeginLine - 1, positionOffset, outerParser);
                                parser.Error += new ParseErrorHandler (ParseError);
                                parser.TagParsed += new TagParsedHandler (TagParsed);
                                parser.TextParsed += new TextParsedHandler (TextParsed);