X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Web%2FSystem.Web.Compilation%2FTemplateControlCompiler.cs;h=201801b31e7fbf4062c17febac0481f344da35c5;hb=96b54e28f09a43a3563572024f151ccb7da384bd;hp=75eedd54a1b313be9b2b96b618ca7244d0bfadfb;hpb=fae6aca6c46a552ac7e822e678df013b8c00bf79;p=mono.git diff --git a/mcs/class/System.Web/System.Web.Compilation/TemplateControlCompiler.cs b/mcs/class/System.Web/System.Web.Compilation/TemplateControlCompiler.cs index 75eedd54a1b..201801b31e7 100644 --- a/mcs/class/System.Web/System.Web.Compilation/TemplateControlCompiler.cs +++ b/mcs/class/System.Web/System.Web.Compilation/TemplateControlCompiler.cs @@ -3,9 +3,10 @@ // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Marek Habersack (mhabersack@novell.com) // // (C) 2003 Ximian, Inc (http://www.ximian.com) -// +// (C) 2004-2008 Novell, Inc (http://novell.com) // // Permission is hereby granted, free of charge, to any person obtaining @@ -31,22 +32,21 @@ using System; using System.CodeDom; using System.Collections; using System.ComponentModel; +using System.Configuration; +using System.Collections.Specialized; +using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Reflection; +using System.Resources; using System.Text; using System.Web; +using System.Web.Configuration; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.Util; using System.ComponentModel.Design.Serialization; -#if NET_2_0 -using System.Configuration; -using System.Collections.Specialized; -using System.Collections.Generic; using System.Text.RegularExpressions; -using System.Web.Configuration; -#endif namespace System.Web.Compilation { @@ -54,19 +54,31 @@ namespace System.Web.Compilation { static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase; - + static Type monoTypeType = Type.GetType ("System.MonoType"); + TemplateControlParser parser; int dataBoundAtts; - ILocation currentLocation; + internal ILocation currentLocation; static TypeConverter colorConverter; internal static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl"); -#if NET_2_0 - static Regex bindRegex = new Regex (@"Bind\s*\(""(.*?)""\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase); - static Regex bindRegexInValue = new Regex (@"Bind\s*\(""(.*?)""\)", RegexOptions.Compiled | RegexOptions.IgnoreCase); -#endif + List masterPageContentPlaceHolders; + static Regex startsWithBindRegex = new Regex (@"^Bind\s*\(", RegexOptions.Compiled | RegexOptions.IgnoreCase); + // When modifying those, make sure to look at the SanitizeBindCall to make sure it + // picks up correct groups. + static Regex bindRegex = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase); + static Regex bindRegexInValue = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + static Regex evalRegexInValue = new Regex (@"(.*)Eval\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)(.*)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + List MasterPageContentPlaceHolders { + get { + if (masterPageContentPlaceHolders == null) + masterPageContentPlaceHolders = new List (); + return masterPageContentPlaceHolders; + } + } public TemplateControlCompiler (TemplateControlParser parser) : base (parser) @@ -81,30 +93,27 @@ namespace System.Web.Compilation } void CreateField (ControlBuilder builder, bool check) - { + { if (builder == null || builder.ID == null || builder.ControlType == null) return; -#if NET_2_0 + if (partialNameOverride [builder.ID] != null) return; -#endif MemberAttributes ma = MemberAttributes.Family; - currentLocation = builder.location; + currentLocation = builder.Location; if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType, ref ma)) return; // The field or property already exists in a base class and is accesible. CodeMemberField field; field = new CodeMemberField (builder.ControlType.FullName, builder.ID); field.Attributes = ma; -#if NET_2_0 field.Type.Options |= CodeTypeReferenceOptions.GlobalReference; if (partialClass != null) - partialClass.Members.Add (field); + partialClass.Members.Add (AddLinePragma (field, builder)); else -#endif - mainClass.Members.Add (field); + mainClass.Members.Add (AddLinePragma (field, builder)); } bool CheckBaseFieldOrProperty (string id, Type type, ref MemberAttributes ma) @@ -127,43 +136,38 @@ namespace System.Web.Compilation return false; if (!other.IsAssignableFrom (type)) { -#if NET_2_0 ma |= MemberAttributes.New; return false; -#else - string msg = String.Format ("The base class includes the field '{0}', but its " + - "type '{1}' is not compatible with {2}", - id, other, type); - throw new ParseException (currentLocation, msg); -#endif } return true; } - + void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr) { - if (!builder.haveParserVariable) { + if (!builder.HaveParserVariable) { CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement(); p.Name = "__parser"; p.Type = new CodeTypeReference (typeof (IParserAccessor)); p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar); - builder.methodStatements.Add (p); - builder.haveParserVariable = true; + builder.MethodStatements.Add (p); + builder.HaveParserVariable = true; } CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser"); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject"); invoke.Parameters.Add (expr); - builder.methodStatements.Add (invoke); + builder.MethodStatements.Add (AddLinePragma (invoke, builder)); } - + void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties) { - string tailname = ((builder is RootBuilder) ? "Tree" : ("_" + builder.ID)); + currentLocation = builder.Location; + bool inBuildControlTree = builder is RootBuilder; + string tailname = (inBuildControlTree ? "Tree" : ("_" + builder.ID)); CodeMemberMethod method = new CodeMemberMethod (); - builder.method = method; - builder.methodStatements = method.Statements; + builder.Method = method; + builder.MethodStatements = method.Statements; method.Name = "__BuildControl" + tailname; method.Attributes = MemberAttributes.Private | MemberAttributes.Final; @@ -172,16 +176,14 @@ namespace System.Web.Compilation /* in the case this is the __BuildControlTree * method, allow subclasses to insert control * specific code. */ - if (builder is RootBuilder) { -#if NET_2_0 + if (inBuildControlTree) { SetCustomAttributes (method); -#endif - AddStatementsToInitMethod (method); + AddStatementsToInitMethodTop (builder, method); } if (builder.HasAspCode) { CodeMemberMethod renderMethod = new CodeMemberMethod (); - builder.renderMethod = renderMethod; + builder.RenderMethod = renderMethod; renderMethod.Name = "__Render" + tailname; renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final; CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression (); @@ -200,11 +202,12 @@ namespace System.Web.Compilation if (builder is RootBuilder) typeString = parser.ClassName; else { - if (builder.ControlType != null && builder.isProperty && + if (builder.ControlType != null && builder.IsProperty && !typeof (ITemplate).IsAssignableFrom (builder.ControlType)) typeString = builder.ControlType.FullName; else typeString = "System.Web.UI.Control"; + ProcessTemplateChildren (builder); } method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl")); @@ -231,8 +234,8 @@ namespace System.Web.Compilation CodeAssignStatement assign = new CodeAssignStatement (); assign.Left = ctrlVar; assign.Right = newExpr; - method.Statements.Add (assign); - + method.Statements.Add (AddLinePragma (assign, builder)); + // this.$builderID = _ctrl; // CodeFieldReferenceExpression builderID = new CodeFieldReferenceExpression (); @@ -241,7 +244,8 @@ namespace System.Web.Compilation assign = new CodeAssignStatement (); assign.Left = builderID; assign.Right = ctrlVar; - method.Statements.Add (assign); + method.Statements.Add (AddLinePragma (assign, builder)); + if (typeof (UserControl).IsAssignableFrom (type)) { CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (); mref.TargetObject = builderID; @@ -251,7 +255,6 @@ namespace System.Web.Compilation method.Statements.Add (initAsControl); } -#if NET_2_0 if (builder.ParentTemplateBuilder is System.Web.UI.WebControls.ContentBuilderInternal) { PropertyInfo pi; @@ -280,11 +283,10 @@ namespace System.Web.Compilation // CreateAssignStatementsFromAttributes // below. // - if (builder.attribs != null) { - string skinid = builder.attribs ["skinid"] as string; - if (skinid != null) - CreateAssignStatementFromAttribute (builder, "skinid"); - } + string skinid = builder.GetAttribute ("skinid"); + if (!String.IsNullOrEmpty (skinid)) + CreateAssignStatementFromAttribute (builder, "skinid"); + if (typeof (WebControl).IsAssignableFrom (type)) { CodeMethodInvokeExpression applyStyleSheetSkin = new CodeMethodInvokeExpression (ctrlVar, "ApplyStyleSheetSkin"); if (typeof (Page).IsAssignableFrom (parser.BaseType)) @@ -293,27 +295,38 @@ namespace System.Web.Compilation applyStyleSheetSkin.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page")); method.Statements.Add (applyStyleSheetSkin); } -#endif -#if NET_2_0 - if (typeof (ContentPlaceHolder).IsAssignableFrom (type)) { - CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders"); - CodeMethodInvokeExpression addPlaceholder = new CodeMethodInvokeExpression (prop, "Add"); - addPlaceholder.Parameters.Add (ctrlVar); - method.Statements.Add (addPlaceholder); + // Process template children before anything else + ProcessTemplateChildren (builder); + // process ID here. It should be set before any other attributes are + // assigned, since the control code may rely on ID being set. We + // skip ID in CreateAssignStatementsFromAttributes + string ctl_id = builder.GetAttribute ("id"); + if (ctl_id != null && ctl_id.Length != 0) + CreateAssignStatementFromAttribute (builder, "id"); + + if (typeof (ContentPlaceHolder).IsAssignableFrom (type)) { + List placeHolderIds = MasterPageContentPlaceHolders; + string cphID = builder.ID; + + if (!placeHolderIds.Contains (cphID)) + placeHolderIds.Add (cphID); CodeConditionStatement condStatement; // Add the __Template_* field - CodeMemberField fld = new CodeMemberField (typeof (ITemplate), "__Template_" + builder.ID); + string templateField = "__Template_" + cphID; + CodeMemberField fld = new CodeMemberField (typeof (ITemplate), templateField); fld.Attributes = MemberAttributes.Private; mainClass.Members.Add (fld); CodeFieldReferenceExpression templateID = new CodeFieldReferenceExpression (); templateID.TargetObject = thisRef; - templateID.FieldName = "__Template_" + builder.ID; + templateID.FieldName = templateField; + CreateContentPlaceHolderTemplateProperty (templateField, "Template_" + cphID); + // if ((this.ContentTemplates != null)) { // this.__Template_$builder.ID = ((System.Web.UI.ITemplate)(this.ContentTemplates["$builder.ID"])); // } @@ -324,7 +337,7 @@ namespace System.Web.Compilation CodeIndexerExpression indexer = new CodeIndexerExpression (); indexer.TargetObject = new CodePropertyReferenceExpression (thisRef, "ContentTemplates"); - indexer.Indices.Add (new CodePrimitiveExpression (builder.ID)); + indexer.Indices.Add (new CodePrimitiveExpression (cphID)); assign = new CodeAssignStatement (); assign.Left = templateID; @@ -359,15 +372,31 @@ namespace System.Web.Compilation method.Statements.Add (condStatement); // this is the bit that causes the following stuff to end up in the else { } - builder.methodStatements = condStatement.FalseStatements; + builder.MethodStatements = condStatement.FalseStatements; } -#endif } + + if (inBuildControlTree) + AddStatementsToInitMethodBottom (builder, method); mainClass.Members.Add (method); } -#if NET_2_0 + void ProcessTemplateChildren (ControlBuilder builder) + { + ArrayList templates = builder.TemplateChildren; + if (templates != null && templates.Count > 0) { + foreach (TemplateBuilder tb in templates) { + CreateControlTree (tb, true, false); + if (tb.BindingDirection == BindingDirection.TwoWay) { + string extractMethod = CreateExtractValuesMethod (tb); + AddBindableTemplateInvocation (builder, tb.TagName, tb.Method.Name, extractMethod); + } else + AddTemplateInvocation (builder, tb.TagName, tb.Method.Name); + } + } + } + void SetCustomAttribute (CodeMemberMethod method, UnknownAttributeDescriptor uad) { CodeAssignStatement assign = new CodeAssignStatement (); @@ -392,12 +421,15 @@ namespace System.Web.Compilation foreach (UnknownAttributeDescriptor uad in attrs) SetCustomAttribute (method, uad); } -#endif - protected virtual void AddStatementsToInitMethod (CodeMemberMethod method) + protected virtual void AddStatementsToInitMethodTop (ControlBuilder builder, CodeMemberMethod method) { } + protected virtual void AddStatementsToInitMethodBottom (ControlBuilder builder, CodeMemberMethod method) + { + } + void AddLiteralSubObject (ControlBuilder builder, string str) { if (!builder.HasAspCode) { @@ -411,35 +443,77 @@ namespace System.Web.Compilation CodeMethodInvokeExpression expr; expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str)); - builder.renderMethod.Statements.Add (expr); + builder.RenderMethod.Statements.Add (expr); } } - string TrimDB (string value) + string TrimDB (string value, bool trimTail) { string str = value.Trim (); - str = str.Substring (3); - return str.Substring (0, str.Length - 2); + int len = str.Length; + int idx = str.IndexOf ('#', 2) + 1; + if (idx >= len) + return String.Empty; + if (trimTail) + len -= 2; + + return str.Substring (idx, len - idx).Trim (); } + CodeExpression CreateEvalInvokeExpression (Regex regex, string value, bool isBind) + { + Match match = regex.Match (value); + if (!match.Success) { + if (isBind) + throw new HttpParseException ("Bind invocation wasn't formatted properly."); + return null; + } + + string sanitizedSnippet; + if (isBind) + sanitizedSnippet = SanitizeBindCall (match); + else + sanitizedSnippet = value; + + return new CodeSnippetExpression (sanitizedSnippet); + } + + string SanitizeBindCall (Match match) + { + GroupCollection groups = match.Groups; + StringBuilder sb = new StringBuilder ("Eval(\"" + groups [1] + "\""); + Group second = groups [4]; + if (second != null) { + string v = second.Value; + if (v != null && v.Length > 0) + sb.Append (",\"" + second + "\""); + } + + sb.Append (")"); + return sb.ToString (); + } + string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value) { - value = TrimDB (value); + value = TrimDB (value, true); CodeMemberMethod method; - string dbMethodName = builder.method.Name + "_DB_" + dataBoundAtts++; -#if NET_2_0 - bool need_if = false; + string dbMethodName = builder.Method.Name + "_DB_" + dataBoundAtts++; + CodeExpression valueExpression = null; value = value.Trim (); - if (StrUtils.StartsWith (value, "Bind", true)) { - Match match = bindRegexInValue.Match (value); - if (match.Success) { - value = "Eval" + value.Substring (4); + + bool need_if = false; + if (startsWithBindRegex.Match (value).Success) { + valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true); + if (valueExpression != null) need_if = true; - } - } -#endif - method = CreateDBMethod (dbMethodName, GetContainerType (builder), builder.ControlType); + } else + if (StrUtils.StartsWith (value, "Eval", true)) + valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false); + + if (valueExpression == null) + valueExpression = new CodeSnippetExpression (value); + method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType); CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target"); // This should be a CodePropertyReferenceExpression for properties... but it works anyway @@ -450,24 +524,19 @@ namespace System.Web.Compilation CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression (); CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert)); tostring.Method = new CodeMethodReferenceExpression (conv, "ToString"); - tostring.Parameters.Add (new CodeSnippetExpression (value)); + tostring.Parameters.Add (valueExpression); expr = tostring; - } else { - CodeSnippetExpression snippet = new CodeSnippetExpression (value); - expr = new CodeCastExpression (type, snippet); - } + } else + expr = new CodeCastExpression (type, valueExpression); CodeAssignStatement assign = new CodeAssignStatement (field, expr); -#if NET_2_0 if (need_if) { CodeExpression page = new CodePropertyReferenceExpression (thisRef, "Page"); CodeExpression left = new CodeMethodInvokeExpression (page, "GetDataItem"); CodeBinaryOperatorExpression ce = new CodeBinaryOperatorExpression (left, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null)); CodeConditionStatement ccs = new CodeConditionStatement (ce, assign); method.Statements.Add (ccs); - } - else -#endif + } else method.Statements.Add (assign); mainClass.Members.Add (method); @@ -476,82 +545,63 @@ namespace System.Web.Compilation void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound, bool isExpression) { - CodeMemberMethod method = builder.method; + CodeMemberMethod method = builder.Method; bool isWritable = IsWritablePropertyOrField (member); + if (isDataBound && isWritable) { string dbMethodName = DataBoundProperty (builder, type, var_name, att); - AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName); + AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName); return; - } -#if NET_2_0 - else if (isExpression && isWritable) { - AddExpressionAssign (method, member, type, var_name, att); + } else if (isExpression && isWritable) { + AddExpressionAssign (method, builder, member, type, var_name, att); return; } -#endif - + CodeAssignStatement assign = new CodeAssignStatement (); assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name); - currentLocation = builder.location; + currentLocation = builder.Location; assign.Right = GetExpressionFromString (type, att, member); - method.Statements.Add (assign); + method.Statements.Add (AddLinePragma (assign, builder)); } - bool IsDataBound (string value) - { - if (value == null || value == "") - return false; - - string str = value.Trim (); - return (StrUtils.StartsWith (str, "<%#") && StrUtils.EndsWith (str, "%>")); - } - -#if NET_2_0 - bool IsExpression (string value) - { - if (value == null || value == "") - return false; - string str = value.Trim (); - return (StrUtils.StartsWith (str, "<%$") && StrUtils.EndsWith (str, "%>")); - } - void RegisterBindingInfo (ControlBuilder builder, string propName, ref string value) { - string str = value.Trim (); - str = str.Substring (3).Trim (); // eats "<%#" + string str = TrimDB (value, false); if (StrUtils.StartsWith (str, "Bind", true)) { Match match = bindRegex.Match (str); if (match.Success) { string bindingName = match.Groups [1].Value; - TemplateBuilder templateBuilder = builder.ParentTemplateBuilder; - if (templateBuilder == null || templateBuilder.BindingDirection == BindingDirection.OneWay) + + if (templateBuilder == null) throw new HttpException ("Bind expression not allowed in this context."); - - string id = builder.attribs ["ID"] as string; - if (id == null) + + if (templateBuilder.BindingDirection == BindingDirection.OneWay) + return; + + string id = builder.GetAttribute ("ID"); + if (String.IsNullOrEmpty (id)) throw new HttpException ("Control of type '" + builder.ControlType + "' using two-way binding on property '" + propName + "' must have an ID."); templateBuilder.RegisterBoundProperty (builder.ControlType, propName, id, bindingName); } } } -#endif /* static bool InvariantCompare (string a, string b) { - return (0 == String.Compare (a, b, false, CultureInfo.InvariantCulture)); + return (0 == String.Compare (a, b, false, Helpers.InvariantCulture)); } */ static bool InvariantCompareNoCase (string a, string b) { - return (0 == String.Compare (a, b, true, CultureInfo.InvariantCulture)); + return (0 == String.Compare (a, b, true, Helpers.InvariantCulture)); } - static MemberInfo GetFieldOrProperty (Type type, string name) + internal static MemberInfo GetFieldOrProperty (Type type, string name) { MemberInfo member = null; try { @@ -572,7 +622,7 @@ namespace System.Web.Compilation { PropertyInfo pi = member as PropertyInfo; if (pi != null) - return pi.CanWrite; + return pi.GetSetMethod (false) != null; FieldInfo fi = member as FieldInfo; if (fi != null) return !fi.IsInitOnly; @@ -584,12 +634,8 @@ namespace System.Web.Compilation { int hyphen = id.IndexOf ('-'); bool isPropertyInfo = (member is PropertyInfo); - bool isDataBound = IsDataBound (attValue); -#if NET_2_0 - bool isExpression = !isDataBound && IsExpression (attValue); -#else - bool isExpression = false; -#endif + bool isDataBound = BaseParser.IsDataBound (attValue); + bool isExpression = !isDataBound && BaseParser.IsExpression (attValue); Type type; if (isPropertyInfo) { type = ((PropertyInfo) member).PropertyType; @@ -598,14 +644,12 @@ namespace System.Web.Compilation } if (InvariantCompareNoCase (member.Name, id)) { -#if NET_2_0 if (isDataBound) - RegisterBindingInfo (builder, member.Name, ref attValue); - -#endif + RegisterBindingInfo (builder, member.Name, ref attValue); + if (!IsWritablePropertyOrField (member)) return false; - + AddCodeForPropertyOrField (builder, type, member.Name, attValue, member, isDataBound, isExpression); return true; } @@ -613,9 +657,10 @@ namespace System.Web.Compilation if (hyphen == -1) return false; - string prop_field = id.Replace ("-", "."); + string prop_field = id.Replace ('-', '.'); string [] parts = prop_field.Split (new char [] {'.'}); int length = parts.Length; + if (length < 2 || !InvariantCompareNoCase (member.Name, parts [0])) return false; @@ -624,7 +669,7 @@ namespace System.Web.Compilation if (sub_member == null) return false; - string new_prefix = prefix + parts [0] + "."; + string new_prefix = prefix + member.Name + "."; string new_id = id.Substring (hyphen + 1); return ProcessPropertiesAndFields (builder, sub_member, new_id, attValue, new_prefix); } @@ -644,9 +689,10 @@ namespace System.Web.Compilation string val = attValue; if (attValue == null && is_bool) val = "true"; // Font-Bold <=> Font-Bold="true" -#if NET_2_0 - if (isDataBound) RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue); -#endif + + if (isDataBound) + RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue); + AddCodeForPropertyOrField (builder, subprop.PropertyType, prefix + member.Name + "." + subprop.Name, val, subprop, isDataBound, isExpression); @@ -654,46 +700,38 @@ namespace System.Web.Compilation return true; } -#if NET_2_0 - void AddExpressionAssign (CodeMemberMethod method, MemberInfo member, Type type, string name, string value) + internal CodeExpression CompileExpression (MemberInfo member, Type type, string value, bool useSetAttribute) { - CodeAssignStatement assign = new CodeAssignStatement (); - assign.Left = new CodePropertyReferenceExpression (ctrlVar, name); - // First let's find the correct expression builder - string expr = value.Substring (3, value.Length - 5).Trim (); - int colon = expr.IndexOf (':'); + value = value.Substring (3, value.Length - 5).Trim (); + int colon = value.IndexOf (':'); if (colon == -1) - return; - string prefix = expr.Substring (0, colon).Trim (); + return null; + string prefix = value.Substring (0, colon).Trim (); + string expr = value.Substring (colon + 1).Trim (); - System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration (""); - if (config == null) - return; - CompilationSection cs = (CompilationSection)config.GetSection ("system.web/compilation"); + CompilationSection cs = (CompilationSection)WebConfigurationManager.GetWebApplicationSection ("system.web/compilation"); if (cs == null) - return; + return null; + if (cs.ExpressionBuilders == null || cs.ExpressionBuilders.Count == 0) - return; + return null; System.Web.Configuration.ExpressionBuilder ceb = cs.ExpressionBuilders[prefix]; if (ceb == null) - return; + return null; + string builderType = ceb.Type; - Type t; + try { - t = System.Type.GetType (builderType, true); + t = HttpApplication.LoadType (builderType, true); } catch (Exception e) { - throw new HttpException ( - String.Format ("Failed to load expression builder type `{0}'", builderType), e); + throw new HttpException (String.Format ("Failed to load expression builder type `{0}'", builderType), e); } if (!typeof (System.Web.Compilation.ExpressionBuilder).IsAssignableFrom (t)) - throw new HttpException ( - String.Format ( - "Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder", - builderType)); + throw new HttpException (String.Format ("Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder", builderType)); System.Web.Compilation.ExpressionBuilder eb = null; object parsedData; @@ -702,35 +740,160 @@ namespace System.Web.Compilation try { eb = Activator.CreateInstance (t) as System.Web.Compilation.ExpressionBuilder; ctx = new ExpressionBuilderContext (HttpContext.Current.Request.FilePath); - parsedData = eb.ParseExpression (expr.Substring (colon + 1).Trim (), type, ctx); + parsedData = eb.ParseExpression (expr, type, ctx); } catch (Exception e) { - throw new HttpException ( - String.Format ("Failed to create an instance of type `{0}'", builderType), e); + throw new HttpException (String.Format ("Failed to create an instance of type `{0}'", builderType), e); } - BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr); - assign.Right = eb.GetCodeExpression (bpe, parsedData, ctx); + BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr, useSetAttribute); + return eb.GetCodeExpression (bpe, parsedData, ctx); + } + + void AddExpressionAssign (CodeMemberMethod method, ControlBuilder builder, MemberInfo member, Type type, string name, string value) + { + CodeExpression expr = CompileExpression (member, type, value, false); + + if (expr == null) + return; + + CodeAssignStatement assign = new CodeAssignStatement (); + assign.Left = new CodePropertyReferenceExpression (ctrlVar, name); + + TypeCode typeCode = Type.GetTypeCode (type); + if (typeCode != TypeCode.Empty && typeCode != TypeCode.Object && typeCode != TypeCode.DBNull) + assign.Right = CreateConvertToCall (typeCode, expr); + else + assign.Right = new CodeCastExpression (type, expr); - method.Statements.Add (assign); + builder.Method.Statements.Add (AddLinePragma (assign, builder)); } - BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr) + CodeMethodInvokeExpression CreateConvertToCall (TypeCode typeCode, CodeExpression expr) { - if (pi == null) - return null; + var ret = new CodeMethodInvokeExpression (); + string methodName; + + switch (typeCode) { + case TypeCode.Boolean: + methodName = "ToBoolean"; + break; + + case TypeCode.Char: + methodName = "ToChar"; + break; + + case TypeCode.SByte: + methodName = "ToSByte"; + break; + + case TypeCode.Byte: + methodName = "ToByte"; + break; + + case TypeCode.Int16: + methodName = "ToInt16"; + break; + + case TypeCode.UInt16: + methodName = "ToUInt16"; + break; + + case TypeCode.Int32: + methodName = "ToInt32"; + break; + + case TypeCode.UInt32: + methodName = "ToUInt32"; + break; + + case TypeCode.Int64: + methodName = "ToInt64"; + break; + + case TypeCode.UInt64: + methodName = "ToUInt64"; + break; + + case TypeCode.Single: + methodName = "ToSingle"; + break; + + case TypeCode.Double: + methodName = "ToDouble"; + break; + + case TypeCode.Decimal: + methodName = "ToDecimal"; + break; + + case TypeCode.DateTime: + methodName = "ToDateTime"; + break; + + case TypeCode.String: + methodName = "ToString"; + break; + default: + throw new InvalidOperationException (String.Format ("Unsupported TypeCode '{0}'", typeCode)); + } + + var typeRef = new CodeTypeReferenceExpression (typeof (Convert)); + typeRef.Type.Options = CodeTypeReferenceOptions.GlobalReference; + + ret.Method = new CodeMethodReferenceExpression (typeRef, methodName); + ret.Parameters.Add (expr); + + return ret; + } + + BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr, bool useSetAttribute) + { BoundPropertyEntry ret = new BoundPropertyEntry (); ret.Expression = expr; ret.ExpressionPrefix = prefix; ret.Generated = false; - ret.Name = pi.Name; - ret.PropertyInfo = pi; - ret.Type = pi.PropertyType; - + if (pi != null) { + ret.Name = pi.Name; + ret.PropertyInfo = pi; + ret.Type = pi.PropertyType; + } + ret.UseSetAttribute = useSetAttribute; + return ret; } + + bool ResourceProviderHasObject (string key) + { + IResourceProvider rp = HttpContext.GetResourceProvider (InputVirtualPath.Absolute, true); + if (rp == null) + return false; + + IResourceReader rr = rp.ResourceReader; + if (rr == null) + return false; + + try { + IDictionaryEnumerator ide = rr.GetEnumerator (); + if (ide == null) + return false; + + string dictKey; + while (ide.MoveNext ()) { + dictKey = ide.Key as string; + if (String.IsNullOrEmpty (dictKey)) + continue; + if (String.Compare (key, dictKey, StringComparison.Ordinal) == 0) + return true; + } + } finally { + rr.Close (); + } + + return false; + } - void AssignPropertyFromResources (CodeMemberMethod method, MemberInfo mi, string attvalue) + void AssignPropertyFromResources (ControlBuilder builder, MemberInfo mi, string attvalue) { bool isProperty = mi.MemberType == MemberTypes.Property; bool isField = !isProperty && (mi.MemberType == MemberTypes.Field); @@ -738,18 +901,29 @@ namespace System.Web.Compilation if (!isProperty && !isField || !IsWritablePropertyOrField (mi)) return; + object[] attrs = mi.GetCustomAttributes (typeof (LocalizableAttribute), true); + if (attrs != null && attrs.Length > 0 && !((LocalizableAttribute)attrs [0]).IsLocalizable) + return; + string memberName = mi.Name; - string resname = String.Format ("{0}.{1}", attvalue, memberName); + string resname = String.Concat (attvalue, ".", memberName); + if (!ResourceProviderHasObject (resname)) + return; + // __ctrl.Text = System.Convert.ToString(HttpContext.GetLocalResourceObject("ButtonResource1.Text")); string inputFile = parser.InputFile; string physPath = HttpContext.Current.Request.PhysicalApplicationPath; if (StrUtils.StartsWith (inputFile, physPath)) inputFile = parser.InputFile.Substring (physPath.Length - 1); - else + else return; - + + char dsc = System.IO.Path.DirectorySeparatorChar; + if (dsc != '/') + inputFile = inputFile.Replace (dsc, '/'); + object obj = HttpContext.GetLocalResourceObject (inputFile, resname); if (obj == null) return; @@ -757,16 +931,15 @@ namespace System.Web.Compilation if (!isProperty && !isField) return; // an "impossible" case - Type declaringType = mi.DeclaringType; CodeAssignStatement assign = new CodeAssignStatement (); assign.Left = new CodePropertyReferenceExpression (ctrlVar, memberName); assign.Right = ResourceExpressionBuilder.CreateGetLocalResourceObject (mi, resname); - method.Statements.Add (assign); + builder.Method.Statements.Add (AddLinePragma (assign, builder)); } - void AssignPropertiesFromResources (CodeMemberMethod method, Type controlType, string attvalue) + void AssignPropertiesFromResources (ControlBuilder builder, Type controlType, string attvalue) { // Process all public fields and properties of the control. We don't use GetMembers to make the code // faster @@ -778,9 +951,9 @@ namespace System.Web.Compilation BindingFlags.Public | BindingFlags.FlattenHierarchy); foreach (FieldInfo fi in fields) - AssignPropertyFromResources (method, fi, attvalue); + AssignPropertyFromResources (builder, fi, attvalue); foreach (PropertyInfo pi in properties) - AssignPropertyFromResources (method, pi, attvalue); + AssignPropertyFromResources (builder, pi, attvalue); } void AssignPropertiesFromResources (ControlBuilder builder, string attvalue) @@ -792,11 +965,10 @@ namespace System.Web.Compilation if (controlType == null) return; - AssignPropertiesFromResources (builder.method, controlType, attvalue); + AssignPropertiesFromResources (builder, controlType, attvalue); } -#endif - void AddEventAssign (CodeMemberMethod method, string name, Type type, string value) + void AddEventAssign (CodeMemberMethod method, ControlBuilder builder, string name, Type type, string value) { //"__ctrl.{0} += new {1} (this.{2});" CodeEventReferenceExpression evtID = new CodeEventReferenceExpression (ctrlVar, name); @@ -813,15 +985,16 @@ namespace System.Web.Compilation EventInfo [] ev_info = null; Type type = builder.ControlType; - string attvalue = builder.attribs [id] as string; - if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){ + string attvalue = builder.GetAttribute (id); + if (id.Length > 2 && String.Compare (id.Substring (0, 2), "ON", true, Helpers.InvariantCulture) == 0){ if (ev_info == null) ev_info = type.GetEvents (); string id_as_event = id.Substring (2); foreach (EventInfo ev in ev_info){ if (InvariantCompareNoCase (ev.Name, id_as_event)){ - AddEventAssign (builder.method, + AddEventAssign (builder.Method, + builder, ev.Name, ev.EventHandlerType, attvalue); @@ -832,12 +1005,10 @@ namespace System.Web.Compilation } -#if NET_2_0 - if (id.ToLower () == "meta:resourcekey") { + if (String.Compare (id, "meta:resourcekey", StringComparison.OrdinalIgnoreCase) == 0) { AssignPropertiesFromResources (builder, attvalue); return; } -#endif int hyphen = id.IndexOf ('-'); string alt_id = id; @@ -851,16 +1022,25 @@ namespace System.Web.Compilation } if (!typeof (IAttributeAccessor).IsAssignableFrom (type)) - throw new ParseException (builder.location, "Unrecognized attribute: " + id); + throw new ParseException (builder.Location, "Unrecognized attribute: " + id); - string val; - CodeMemberMethod method = builder.method; - bool databound = IsDataBound (attvalue); + CodeMemberMethod method = builder.Method; + bool isDatabound = BaseParser.IsDataBound (attvalue); + bool isExpression = !isDatabound && BaseParser.IsExpression (attvalue); - if (databound) { - val = attvalue.Substring (3); - val = val.Substring (0, val.Length - 2); - CreateDBAttributeMethod (builder, id, val); + if (isDatabound) { + string value = attvalue.Substring (3, attvalue.Length - 5).Trim (); + CodeExpression valueExpression = null; + if (startsWithBindRegex.Match (value).Success) + valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true); + else + if (StrUtils.StartsWith (value, "Eval", true)) + valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false); + + if (valueExpression == null && value != null && value.Trim () != String.Empty) + valueExpression = new CodeSnippetExpression (value); + + CreateDBAttributeMethod (builder, id, valueExpression); } else { CodeCastExpression cast; CodeMethodReferenceExpression methodExpr; @@ -870,55 +1050,56 @@ namespace System.Web.Compilation methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute"); expr = new CodeMethodInvokeExpression (methodExpr); expr.Parameters.Add (new CodePrimitiveExpression (id)); - expr.Parameters.Add (new CodePrimitiveExpression (attvalue)); - method.Statements.Add (expr); - } + CodeExpression valueExpr = null; + if (isExpression) + valueExpr = CompileExpression (null, typeof (string), attvalue, true); + + if (valueExpr == null) + valueExpr = new CodePrimitiveExpression (attvalue); + + expr.Parameters.Add (valueExpr); + method.Statements.Add (AddLinePragma (expr, builder)); + } } protected void CreateAssignStatementsFromAttributes (ControlBuilder builder) { -#if NET_2_0 - bool haveMetaKey = false; -#endif this.dataBoundAtts = 0; - IDictionary atts = builder.attribs; + IDictionary atts = builder.Attributes; if (atts == null || atts.Count == 0) return; foreach (string id in atts.Keys) { if (InvariantCompareNoCase (id, "runat")) continue; + // ID is assigned in BuildControltree + if (InvariantCompareNoCase (id, "id")) + continue; -#if NET_2_0 /* we skip SkinID here as it's assigned in BuildControlTree */ if (InvariantCompareNoCase (id, "skinid")) continue; - if (InvariantCompareNoCase (id, "meta:resourcekey")) { - haveMetaKey = true; - continue; - } -#endif + if (InvariantCompareNoCase (id, "meta:resourcekey")) + continue; // ignore, this one's processed at the very end of + // the method CreateAssignStatementFromAttribute (builder, id); } - -#if NET_2_0 - if (haveMetaKey) - CreateAssignStatementFromAttribute (builder, "meta:resourcekey"); -#endif } - void CreateDBAttributeMethod (ControlBuilder builder, string attr, string code) + void CreateDBAttributeMethod (ControlBuilder builder, string attr, CodeExpression code) { - if (code == null || code.Trim () == "") + if (code == null) return; string id = builder.GetNextID (null); string dbMethodName = "__DataBind_" + id; - CodeMemberMethod method = builder.method; - AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName); + CodeMemberMethod method = builder.Method; + AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName); + + method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType); + builder.DataBindingMethod = method; - method = CreateDBMethod (dbMethodName, GetContainerType (builder), builder.ControlType); CodeCastExpression cast; CodeMethodReferenceExpression methodExpr; CodeMethodInvokeExpression expr; @@ -932,7 +1113,7 @@ namespace System.Web.Compilation tostring.Method = new CodeMethodReferenceExpression ( new CodeTypeReferenceExpression (typeof (Convert)), "ToString"); - tostring.Parameters.Add (new CodeSnippetExpression (code)); + tostring.Parameters.Add (code); expr.Parameters.Add (tostring); method.Statements.Add (expr); mainClass.Members.Add (method); @@ -945,25 +1126,27 @@ namespace System.Web.Compilation new CodeArgumentReferenceExpression ("parameterContainer"), "Controls"); - indexer.Indices.Add (new CodePrimitiveExpression (builder.renderIndex)); + indexer.Indices.Add (new CodePrimitiveExpression (builder.RenderIndex)); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl"); invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output")); - builder.renderMethod.Statements.Add (invoke); - builder.renderIndex++; + builder.RenderMethod.Statements.Add (invoke); + builder.IncreaseRenderIndex (); } protected void AddChildCall (ControlBuilder parent, ControlBuilder child) { if (parent == null || child == null) return; - CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.method.Name); + + CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.Method.Name); CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m); object [] atts = null; - + if (child.ControlType != null) - child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true); + atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true); + if (atts != null && atts.Length > 0) { PartialCachingAttribute pca = (PartialCachingAttribute) atts [0]; CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl"); @@ -983,35 +1166,35 @@ namespace System.Web.Compilation build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByCustom)); build.Parameters.Add (new CodeDelegateCreateExpression ( new CodeTypeReference (typeof (System.Web.UI.BuildMethod)), - thisRef, child.method.Name)); - - parent.methodStatements.Add (build); + thisRef, child.Method.Name)); + + parent.MethodStatements.Add (AddLinePragma (build, parent)); if (parent.HasAspCode) AddRenderControl (parent); return; } - if (child.isProperty || parent.ChildrenAsProperties) { + if (child.IsProperty || parent.ChildrenAsProperties) { expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName)); - parent.methodStatements.Add (expr); + parent.MethodStatements.Add (AddLinePragma (expr, parent)); return; } - parent.methodStatements.Add (expr); + parent.MethodStatements.Add (AddLinePragma (expr, parent)); CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID); - if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType)) { + if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType)) AddParsedSubObjectStmt (parent, field); - } else { + else { CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add"); invoke.Parameters.Add (field); - parent.methodStatements.Add (invoke); + parent.MethodStatements.Add (AddLinePragma (invoke, parent)); } if (parent.HasAspCode) AddRenderControl (parent); } - void AddTemplateInvocation (CodeMemberMethod method, string name, string methodName) + void AddTemplateInvocation (ControlBuilder builder, string name, string methodName) { CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name); @@ -1022,11 +1205,10 @@ namespace System.Web.Compilation newCompiled.Parameters.Add (newBuild); CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled); - method.Statements.Add (assign); + builder.Method.Statements.Add (AddLinePragma (assign, builder)); } -#if NET_2_0 - void AddBindableTemplateInvocation (CodeMemberMethod method, string name, string methodName, string extractMethodName) + void AddBindableTemplateInvocation (ControlBuilder builder, string name, string methodName, string extractMethodName) { CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name); @@ -1041,7 +1223,7 @@ namespace System.Web.Compilation newCompiled.Parameters.Add (newExtract); CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled); - method.Statements.Add (assign); + builder.Method.Statements.Add (AddLinePragma (assign, builder)); } string CreateExtractValuesMethod (TemplateBuilder builder) @@ -1116,9 +1298,8 @@ namespace System.Web.Compilation invoke.Parameters.Add (new CodePrimitiveExpression (cbuilder.ContentPlaceHolderID)); invoke.Parameters.Add (newCompiled); - method.Statements.Add (invoke); + method.Statements.Add (AddLinePragma (invoke, cbuilder)); } -#endif void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr) { @@ -1127,7 +1308,7 @@ namespace System.Web.Compilation if (!cr.IsAssign) { CodeSnippetStatement code = new CodeSnippetStatement (cr.Code); - parent.renderMethod.Statements.Add (code); + parent.RenderMethod.Statements.Add (AddLinePragma (code, cr)); return; } @@ -1136,37 +1317,28 @@ namespace System.Web.Compilation new CodeArgumentReferenceExpression ("__output"), "Write"); - expr.Parameters.Add (new CodeSnippetExpression (cr.Code)); - parent.renderMethod.Statements.Add (expr); + expr.Parameters.Add (GetWrappedCodeExpression (cr)); + parent.RenderMethod.Statements.Add (AddLinePragma (expr, cr)); } + CodeExpression GetWrappedCodeExpression (CodeRenderBuilder cr) + { + var ret = new CodeSnippetExpression (cr.Code); +#if NET_4_0 + if (cr.HtmlEncode) { + var encodeRef = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (HttpUtility)), "HtmlEncode"); + return new CodeMethodInvokeExpression (encodeRef, new CodeExpression[] { ret }); + } else +#endif + return ret; + } + static Type GetContainerType (ControlBuilder builder) { - TemplateBuilder tb = builder as TemplateBuilder; - if (tb != null && tb.ContainerType != null) - return tb.ContainerType; -#if NET_2_0 return builder.BindingContainerType; -#else - Type type = builder.BindingContainerType; - - PropertyInfo prop = type.GetProperty ("Items", noCaseFlags & ~BindingFlags.NonPublic); - if (prop == null) - return type; - - Type ptype = prop.PropertyType; - if (!typeof (ICollection).IsAssignableFrom (ptype)) - return type; - - prop = ptype.GetProperty ("Item", noCaseFlags & ~BindingFlags.NonPublic); - if (prop == null) - return type; - - return prop.PropertyType; -#endif } - CodeMemberMethod CreateDBMethod (string name, Type container, Type target) + CodeMemberMethod CreateDBMethod (ControlBuilder builder, string name, Type container, Type target) { CodeMemberMethod method = new CodeMemberMethod (); method.Attributes = MemberAttributes.Public | MemberAttributes.Final; @@ -1181,7 +1353,7 @@ namespace System.Web.Compilation decl.Name = "Container"; decl.Type = containerRef; method.Statements.Add (decl); - + decl = new CodeVariableDeclarationStatement(); decl.Name = "target"; decl.Type = targetRef; @@ -1191,13 +1363,13 @@ namespace System.Web.Compilation CodeAssignStatement assign = new CodeAssignStatement (); assign.Left = targetExpr; assign.Right = new CodeCastExpression (targetRef, new CodeArgumentReferenceExpression ("sender")); - method.Statements.Add (assign); + method.Statements.Add (AddLinePragma (assign, builder)); assign = new CodeAssignStatement (); assign.Left = new CodeVariableReferenceExpression ("Container"); assign.Right = new CodeCastExpression (containerRef, new CodePropertyReferenceExpression (targetExpr, "BindingContainer")); - method.Statements.Add (assign); + method.Statements.Add (AddLinePragma (assign, builder)); return method; } @@ -1213,12 +1385,13 @@ namespace System.Web.Compilation string dbMethodName = "__DataBind_" + db.ID; // Add the method that builds the DataBoundLiteralControl InitMethod (db, false, false); - CodeMemberMethod method = db.method; - AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName); + CodeMemberMethod method = db.Method; + AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName); method.Statements.Add (new CodeMethodReturnStatement (ctrlVar)); // Add the DataBind handler - method = CreateDBMethod (dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl)); + method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl)); + builder.DataBindingMethod = method; CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target"); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (); @@ -1231,10 +1404,10 @@ namespace System.Web.Compilation "ToString"); tostring.Parameters.Add (new CodeSnippetExpression (db.Code)); invoke.Parameters.Add (tostring); - method.Statements.Add (invoke); + method.Statements.Add (AddLinePragma (invoke, builder)); mainClass.Members.Add (method); - + AddChildCall (builder, db); } @@ -1254,13 +1427,28 @@ namespace System.Web.Compilation if (!isTemplate && !inTemplate) { CreateField (builder, true); } else if (!isTemplate) { -#if NET_2_0 - TemplateBuilder pb = builder.parentBuilder as TemplateBuilder; - if (pb == null || pb.TemplateInstance != TemplateInstance.Single) -#endif - builder.ID = builder.GetNextID (null); + bool doCheck = false; + bool singleInstance = false; + ControlBuilder pb = builder.ParentBuilder; + TemplateBuilder tpb; + while (pb != null) { + tpb = pb as TemplateBuilder; + if (tpb == null) { + pb = pb.ParentBuilder; + continue; + } + + if (tpb.TemplateInstance == TemplateInstance.Single) + singleInstance = true; + break; + } - CreateField (builder, false); + if (!singleInstance) + builder.ID = builder.GetNextID (null); + else + doCheck = true; + + CreateField (builder, doCheck); } InitMethod (builder, isTemplate, childrenAsProperties); @@ -1268,8 +1456,6 @@ namespace System.Web.Compilation CreateAssignStatementsFromAttributes (builder); if (builder.Children != null && builder.Children.Count > 0) { - ArrayList templates = null; - StringBuilder sb = new StringBuilder (); foreach (object b in builder.Children) { if (b is string) { @@ -1280,110 +1466,103 @@ namespace System.Web.Compilation FlushText (builder, sb); if (b is ObjectTagBuilder) { ProcessObjectTag ((ObjectTagBuilder) b); - continue; - } - - StringPropertyBuilder pb = b as StringPropertyBuilder; - if (pb != null){ + } else if (b is StringPropertyBuilder) { + StringPropertyBuilder pb = b as StringPropertyBuilder; if (pb.Children != null && pb.Children.Count > 0) { StringBuilder asb = new StringBuilder (); foreach (string s in pb.Children) asb.Append (s); - CodeMemberMethod method = builder.method; + CodeMemberMethod method = builder.Method; CodeAssignStatement assign = new CodeAssignStatement (); assign.Left = new CodePropertyReferenceExpression (ctrlVar, pb.PropertyName); assign.Right = new CodePrimitiveExpression (asb.ToString ()); - method.Statements.Add (assign); + method.Statements.Add (AddLinePragma (assign, builder)); } - continue; - } - -#if NET_2_0 - if (b is ContentBuilderInternal) { + } else if (b is ContentBuilderInternal) { ContentBuilderInternal cb = (ContentBuilderInternal) b; CreateControlTree (cb, false, true); - AddContentTemplateInvocation (cb, builder.method, cb.method.Name); - continue; - } -#endif - - if (b is TemplateBuilder) { - if (templates == null) - templates = new ArrayList (); - - templates.Add (b); + AddContentTemplateInvocation (cb, builder.Method, cb.Method.Name); continue; } - if (b is CodeRenderBuilder) { + // Ignore TemplateBuilders - they are processed in InitMethod + else if (b is TemplateBuilder) { + } else if (b is CodeRenderBuilder) { AddCodeRender (builder, (CodeRenderBuilder) b); - continue; - } - - if (b is DataBindingBuilder) { + } else if (b is DataBindingBuilder) { AddDataBindingLiteral (builder, (DataBindingBuilder) b); - continue; - } - - if (b is ControlBuilder) { + } else if (b is ControlBuilder) { ControlBuilder child = (ControlBuilder) b; CreateControlTree (child, inTemplate, builder.ChildrenAsProperties); AddChildCall (builder, child); continue; - } + } else + throw new Exception ("???"); - throw new Exception ("???"); + ControlBuilder bldr = b as ControlBuilder; + bldr.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, bldr.Method, bldr.DataBindingMethod); } FlushText (builder, sb); - - if (templates != null) { - foreach (TemplateBuilder b in templates) { - CreateControlTree (b, true, false); -#if NET_2_0 - if (b.BindingDirection == BindingDirection.TwoWay) { - string extractMethod = CreateExtractValuesMethod (b); - AddBindableTemplateInvocation (builder.method, b.TagName, b.method.Name, extractMethod); - } - else -#endif - AddTemplateInvocation (builder.method, b.TagName, b.method.Name); - } - } - } - if (builder.defaultPropertyBuilder != null) { - ControlBuilder b = builder.defaultPropertyBuilder; - CreateControlTree (b, false, true); - AddChildCall (builder, b); + ControlBuilder defaultPropertyBuilder = builder.DefaultPropertyBuilder; + if (defaultPropertyBuilder != null) { + CreateControlTree (defaultPropertyBuilder, false, true); + AddChildCall (builder, defaultPropertyBuilder); } - + if (builder.HasAspCode) { + CodeMemberMethod renderMethod = builder.RenderMethod; CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (); m.TargetObject = thisRef; - m.MethodName = builder.renderMethod.Name; + m.MethodName = renderMethod.Name; CodeDelegateCreateExpression create = new CodeDelegateCreateExpression (); create.DelegateType = new CodeTypeReference (typeof (RenderMethod)); create.TargetObject = thisRef; - create.MethodName = builder.renderMethod.Name; + create.MethodName = renderMethod.Name; CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (); invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate"); invoke.Parameters.Add (create); - builder.methodStatements.Add (invoke); + builder.MethodStatements.Add (invoke); } - - if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType)) - builder.method.Statements.Add (new CodeMethodReturnStatement (ctrlVar)); -#if NET_2_0 if (builder is RootBuilder) if (!String.IsNullOrEmpty (parser.MetaResourceKey)) - AssignPropertiesFromResources (builder.method, parser.BaseType, parser.MetaResourceKey); -#endif + AssignPropertiesFromResources (builder, parser.BaseType, parser.MetaResourceKey); + + if ((!isTemplate || builder is RootBuilder) && !String.IsNullOrEmpty (builder.GetAttribute ("meta:resourcekey"))) + CreateAssignStatementFromAttribute (builder, "meta:resourcekey"); + + if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType)) + builder.Method.Statements.Add (new CodeMethodReturnStatement (ctrlVar)); + + builder.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, builder.Method, builder.DataBindingMethod); + } + + protected override void AddStatementsToConstructor (CodeConstructor ctor) + { + if (masterPageContentPlaceHolders == null || masterPageContentPlaceHolders.Count == 0) + return; + + var ilist = new CodeVariableDeclarationStatement (); + ilist.Name = "__contentPlaceHolders"; + ilist.Type = new CodeTypeReference (typeof (IList)); + ilist.InitExpression = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders"); + + var ilistRef = new CodeVariableReferenceExpression ("__contentPlaceHolders"); + CodeStatementCollection statements = ctor.Statements; + statements.Add (ilist); + + CodeMethodInvokeExpression mcall; + foreach (string id in masterPageContentPlaceHolders) { + mcall = new CodeMethodInvokeExpression (ilistRef, "Add"); + mcall.Parameters.Add (new CodePrimitiveExpression (id.ToLowerInvariant ())); + statements.Add (mcall); + } } protected internal override void CreateMethods () @@ -1395,13 +1574,42 @@ namespace System.Web.Compilation CreateFrameworkInitializeMethod (); } + protected override void InitializeType () + { + List registeredTagNames = parser.RegisteredTagNames; + RootBuilder rb = parser.RootBuilder; + if (rb == null || registeredTagNames == null || registeredTagNames.Count == 0) + return; + + AspComponent component; + foreach (string tagName in registeredTagNames) { + component = rb.Foundry.GetComponent (tagName); + if (component == null || component.Type == null) // unlikely + throw new HttpException ("Custom control '" + tagName + "' cannot be found."); + if (!(typeof (UserControl).IsAssignableFrom (component.Type))) + throw new ParseException (parser.Location, "Type '" + component.Type.ToString () + "' does not derive from 'System.Web.UI.UserControl'."); + AddReferencedAssembly (component.Type.Assembly); + } + } + void CallBaseFrameworkInitialize (CodeMemberMethod method) { CodeBaseReferenceExpression baseRef = new CodeBaseReferenceExpression (); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (baseRef, "FrameworkInitialize"); method.Statements.Add (invoke); } - + + void CallSetStringResourcePointer (CodeMemberMethod method) + { + CodeFieldReferenceExpression stringResource = GetMainClassFieldReferenceExpression ("__stringResource"); + method.Statements.Add ( + new CodeMethodInvokeExpression ( + thisRef, + "SetStringResourcePointer", + new CodeExpression[] {stringResource, new CodePrimitiveExpression (0)}) + ); + } + void CreateFrameworkInitializeMethod () { CodeMemberMethod method = new CodeMemberMethod (); @@ -1409,6 +1617,7 @@ namespace System.Web.Compilation method.Attributes = MemberAttributes.Family | MemberAttributes.Override; PrependStatementsToFrameworkInitialize (method); CallBaseFrameworkInitialize (method); + CallSetStringResourcePointer (method); AppendStatementsToFrameworkInitialize (method); mainClass.Members.Add (method); } @@ -1444,6 +1653,16 @@ namespace System.Web.Compilation } } + protected override void CreateStaticFields () + { + base.CreateStaticFields (); + + CodeMemberField fld = new CodeMemberField (typeof (object), "__stringResource"); + fld.Attributes = MemberAttributes.Private | MemberAttributes.Static; + fld.InitExpression = new CodePrimitiveExpression (null); + mainClass.Members.Add (fld); + } + protected void ProcessObjectTag (ObjectTagBuilder tag) { string fieldName = CreateFieldForObject (tag.Type, tag.ObjectID); @@ -1475,14 +1694,41 @@ namespace System.Web.Compilation CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef); prop.GetStatements.Add (new CodeMethodReturnStatement (cast)); -#if NET_2_0 if (partialClass != null) partialClass.Members.Add (prop); else -#endif - mainClass.Members.Add (prop); + mainClass.Members.Add (prop); } + void CreateContentPlaceHolderTemplateProperty (string backingField, string name) + { + CodeMemberProperty prop = new CodeMemberProperty (); + prop.Type = new CodeTypeReference (typeof (ITemplate)); + prop.Name = name; + prop.Attributes = MemberAttributes.Public; + + var ret = new CodeMethodReturnStatement (); + var fldRef = new CodeFieldReferenceExpression (thisRef, backingField); + ret.Expression = fldRef; + prop.GetStatements.Add (ret); + prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ())); + + prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateContainer", new CodeAttributeArgument [] { + new CodeAttributeArgument (new CodeTypeOfExpression (new CodeTypeReference (typeof (MasterPage)))) + } + ) + ); + + var enumValueRef = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof (TemplateInstance)), "Single"); + prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateInstanceAttribute", new CodeAttributeArgument [] { + new CodeAttributeArgument (enumValueRef) + } + ) + ); + + mainClass.Members.Add (prop); + } + void CreateAutoHandlers () { // Create AutoHandlers property @@ -1498,11 +1744,8 @@ namespace System.Web.Compilation prop.GetStatements.Add (ret); prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ())); -#if NET_2_0 CodeAttributeDeclaration attr = new CodeAttributeDeclaration ("System.Obsolete"); - prop.CustomAttributes.Add (attr); -#endif - + prop.CustomAttributes.Add (attr); mainClass.Members.Add (prop); // Add the __autoHandlers field @@ -1522,113 +1765,189 @@ namespace System.Web.Compilation mainClass.Members.Add (prop); } -#if NET_2_0 protected virtual string HandleUrlProperty (string str, MemberInfo member) { return str; } -#endif - + + TypeConverter GetConverterForMember (MemberInfo member) + { + TypeDescriptionProvider prov = TypeDescriptor.GetProvider (member.ReflectedType); + if (prov == null) + return null; + + ICustomTypeDescriptor desc = prov.GetTypeDescriptor (member.ReflectedType); + PropertyDescriptorCollection coll = desc != null ? desc.GetProperties () : null; + + if (coll == null || coll.Count == 0) + return null; + + PropertyDescriptor pd = coll.Find (member.Name, false); + if (pd == null) + return null; + + return pd.Converter; + } + + CodeExpression CreateNullableExpression (Type type, CodeExpression inst, bool nullable) + { + if (!nullable) + return inst; + + return new CodeObjectCreateExpression (type, new CodeExpression[] {inst}); + } + + bool SafeCanConvertFrom (Type type, TypeConverter cvt) + { + try { + return cvt.CanConvertFrom (type); + } catch (NotImplementedException) { + return false; + } + } + + bool SafeCanConvertTo (Type type, TypeConverter cvt) + { + try { + return cvt.CanConvertTo (type); + } catch (NotImplementedException) { + return false; + } + } + CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member) - { -#if NET_2_0 - bool wasNullable = false; + { + TypeConverter cvt = GetConverterForMember (member); + if (cvt != null && !SafeCanConvertFrom (typeof (string), cvt)) + cvt = null; + object convertedFromAttr = null; + bool preConverted = false; + if (cvt != null && str != null) { + convertedFromAttr = cvt.ConvertFromInvariantString (str); + if (convertedFromAttr != null) { + type = convertedFromAttr.GetType (); + preConverted = true; + } + } + + bool wasNullable = false; + Type originalType = type; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { Type[] types = type.GetGenericArguments(); + originalType = type; type = types[0]; // we're interested only in the first type here wasNullable = true; } -#endif if (type == typeof (string)) { -#if NET_2_0 object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true); if (urlAttr.Length != 0) - str = HandleUrlProperty (str, member); -#endif - return new CodePrimitiveExpression (str); - } - - if (type == typeof (bool)) { + str = HandleUrlProperty ((preConverted && convertedFromAttr is string) ? (string)convertedFromAttr : str, member); + else if (preConverted) + return CreateNullableExpression (originalType, + new CodePrimitiveExpression ((string) convertedFromAttr), + wasNullable); + + return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable); + } else if (type == typeof (bool)) { + if (preConverted) + return CreateNullableExpression (originalType, + new CodePrimitiveExpression ((bool) convertedFromAttr), + wasNullable); + if (str == null || str == "" || InvariantCompareNoCase (str, "true")) - return new CodePrimitiveExpression (true); + return CreateNullableExpression (originalType, new CodePrimitiveExpression (true), wasNullable); else if (InvariantCompareNoCase (str, "false")) - return new CodePrimitiveExpression (false); -#if NET_2_0 + return CreateNullableExpression (originalType, new CodePrimitiveExpression (false), wasNullable); else if (wasNullable && InvariantCompareNoCase(str, "null")) return new CodePrimitiveExpression (null); -#endif else throw new ParseException (currentLocation, "Value '" + str + "' is not a valid boolean."); - } - + } else if (type == monoTypeType) + type = typeof (System.Type); + if (str == null) return new CodePrimitiveExpression (null); if (type.IsPrimitive) - return new CodePrimitiveExpression (Convert.ChangeType (str, type, CultureInfo.InvariantCulture)); + return CreateNullableExpression (originalType, + new CodePrimitiveExpression ( + Convert.ChangeType (preConverted ? convertedFromAttr : str, + type, Helpers.InvariantCulture)), + wasNullable); if (type == typeof (string [])) { - string [] subs = str.Split (','); + string [] subs; + + if (preConverted) + subs = (string[])convertedFromAttr; + else + subs = str.Split (','); CodeArrayCreateExpression expr = new CodeArrayCreateExpression (); expr.CreateType = new CodeTypeReference (typeof (string)); - foreach (string v in subs) { + foreach (string v in subs) expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ())); - } - return expr; + return CreateNullableExpression (originalType, expr, wasNullable); } - - if (type == typeof (Color)){ - if (colorConverter == null) - colorConverter = TypeDescriptor.GetConverter (typeof (Color)); - - if (str.Trim().Length == 0) { - CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color)); - return new CodeFieldReferenceExpression (ft, "Empty"); - } - + + if (type == typeof (Color)) { Color c; - try { - if (str.IndexOf (',') == -1) { - c = (Color) colorConverter.ConvertFromString (str); - } else { - int [] argb = new int [4]; - argb [0] = 255; - - string [] parts = str.Split (','); - int length = parts.Length; - if (length < 3) - throw new Exception (); - - int basei = (length == 4) ? 0 : 1; - for (int i = length - 1; i >= 0; i--) { - argb [basei + i] = (int) Byte.Parse (parts [i]); - } - c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]); - } - } catch (Exception e){ - // Hack: "LightGrey" is accepted, but only for ASP.NET, as the - // TypeConverter for Color fails to ConvertFromString. - // Hence this hack... - if (InvariantCompareNoCase ("LightGrey", str)) { - c = Color.LightGray; - } else { - throw new ParseException (currentLocation, - "Color " + str + " is not a valid color.", e); + + if (!preConverted) { + if (colorConverter == null) + colorConverter = TypeDescriptor.GetConverter (typeof (Color)); + + if (str.Trim().Length == 0) { + CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color)); + return CreateNullableExpression (originalType, + new CodeFieldReferenceExpression (ft, "Empty"), + wasNullable); } - } - if (c.IsKnownColor){ + try { + if (str.IndexOf (',') == -1) { + c = (Color) colorConverter.ConvertFromString (str); + } else { + int [] argb = new int [4]; + argb [0] = 255; + + string [] parts = str.Split (','); + int length = parts.Length; + if (length < 3) + throw new Exception (); + + int basei = (length == 4) ? 0 : 1; + for (int i = length - 1; i >= 0; i--) { + argb [basei + i] = (int) Byte.Parse (parts [i]); + } + c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]); + } + } catch (Exception e) { + // Hack: "LightGrey" is accepted, but only for ASP.NET, as the + // TypeConverter for Color fails to ConvertFromString. + // Hence this hack... + if (InvariantCompareNoCase ("LightGrey", str)) { + c = Color.LightGray; + } else { + throw new ParseException (currentLocation, + "Color " + str + " is not a valid color.", e); + } + } + } else + c = (Color)convertedFromAttr; + + if (c.IsKnownColor) { CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression (); if (c.IsSystemColor) type = typeof (SystemColors); expr.TargetObject = new CodeTypeReferenceExpression (type); expr.FieldName = c.Name; - return expr; + return CreateNullableExpression (originalType, expr, wasNullable); } else { CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (); m.TargetObject = new CodeTypeReferenceExpression (type); @@ -1638,22 +1957,57 @@ namespace System.Web.Compilation invoke.Parameters.Add (new CodePrimitiveExpression (c.R)); invoke.Parameters.Add (new CodePrimitiveExpression (c.G)); invoke.Parameters.Add (new CodePrimitiveExpression (c.B)); - return invoke; + return CreateNullableExpression (originalType, invoke, wasNullable); } } - TypeConverter converter = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter; + TypeConverter converter = preConverted ? cvt : wasNullable ? TypeDescriptor.GetConverter (type) : null; + if (converter == null) { + PropertyDescriptor pdesc = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name]; + if (pdesc != null) + converter = pdesc.Converter; + else { + Type memberType; + switch (member.MemberType) { + case MemberTypes.Field: + memberType = ((FieldInfo)member).FieldType; + break; + + case MemberTypes.Property: + memberType = ((PropertyInfo)member).PropertyType; + break; + + default: + memberType = null; + break; + } + + if (memberType == null) + return null; + + converter = TypeDescriptor.GetConverter (memberType); + } + } - if (converter != null && converter.CanConvertFrom (typeof (string))) { - object value = converter.ConvertFromInvariantString (str); + if (preConverted || (converter != null && SafeCanConvertFrom (typeof (string), converter))) { + object value = preConverted ? convertedFromAttr : converter.ConvertFromInvariantString (str); - if (converter.CanConvertTo (typeof (InstanceDescriptor))) { + if (SafeCanConvertTo (typeof (InstanceDescriptor), converter)) { InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor)); - return GenerateInstance (idesc, true); + if (wasNullable) + return CreateNullableExpression (originalType, GenerateInstance (idesc, true), + wasNullable); + + CodeExpression instance = GenerateInstance (idesc, true); + if (type.IsPublic) + return new CodeCastExpression (type, instance); + else + return instance; } CodeExpression exp = GenerateObjectInstance (value, false); - if (exp != null) return exp; + if (exp != null) + return CreateNullableExpression (originalType, exp, wasNullable); CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (); m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor)); @@ -1665,12 +2019,15 @@ namespace System.Web.Compilation invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom"); invoke.Parameters.Add (new CodePrimitiveExpression (str)); - return new CodeCastExpression (tref, invoke); + if (wasNullable) + return CreateNullableExpression (originalType, invoke, wasNullable); + + return new CodeCastExpression (type, invoke); } Console.WriteLine ("Unknown type: " + type + " value: " + str); - - return new CodePrimitiveExpression (str); + + return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable); } CodeExpression GenerateInstance (InstanceDescriptor idesc, bool throwOnError) @@ -1707,11 +2064,17 @@ namespace System.Web.Compilation { if (value == null) return new CodePrimitiveExpression (null); + + if (value is System.Type) { + CodeTypeReference tref = new CodeTypeReference (value.ToString ()); + return new CodeTypeOfExpression (tref); + } - Type t = value.GetType(); + Type t = value.GetType (); + if (t.IsPrimitive || value is string) return new CodePrimitiveExpression (value); - + if (t.IsArray) { Array ar = (Array) value; CodeExpression[] items = new CodeExpression [ar.Length]; @@ -1742,6 +2105,10 @@ namespace System.Web.Compilation { if (value is System.Web.UI.WebControls.Unit) { System.Web.UI.WebControls.Unit s = (System.Web.UI.WebControls.Unit) value; + if (s.IsEmpty) { + FieldInfo f = typeof (Unit).GetField ("Empty"); + return new InstanceDescriptor (f, null); + } ConstructorInfo c = typeof(System.Web.UI.WebControls.Unit).GetConstructor ( BindingFlags.Instance | BindingFlags.Public, null, @@ -1753,6 +2120,10 @@ namespace System.Web.Compilation if (value is System.Web.UI.WebControls.FontUnit) { System.Web.UI.WebControls.FontUnit s = (System.Web.UI.WebControls.FontUnit) value; + if (s.IsEmpty) { + FieldInfo f = typeof (FontUnit).GetField ("Empty"); + return new InstanceDescriptor (f, null); + } Type cParamType = null; object cParam = null; @@ -1780,6 +2151,21 @@ namespace System.Web.Compilation } return null; } + +#if DEBUG + CodeMethodInvokeExpression CreateConsoleWriteLineCall (string format, params CodeExpression[] parms) + { + CodeMethodReferenceExpression cwl = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (System.Console)), "WriteLine"); + CodeMethodInvokeExpression cwlCall = new CodeMethodInvokeExpression (cwl); + + cwlCall.Parameters.Add (new CodePrimitiveExpression (format)); + if (parms != null && parms.Length > 0) + foreach (CodeExpression expr in parms) + cwlCall.Parameters.Add (expr); + + return cwlCall; + } +#endif } }