1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
|
//------------------------------------------------------------------------------
// <copyright file="CodeDomUtility.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Compilation {
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System;
using System.Collections;
using System.Reflection;
using System.IO;
using System.Globalization;
using System.Web.Util;
using System.Web.UI;
using System.Web.Configuration;
using System.Diagnostics;
using Debug = System.Diagnostics.Debug;
using System.CodeDom;
using System.CodeDom.Compiler;
using Util = System.Web.UI.Util;
internal static class CodeDomUtility {
internal static BooleanSwitch WebFormsCompilation = new BooleanSwitch("WebFormsCompilation", "Outputs information about the WebForms compilation of ASPX templates");
internal /*public*/ static CodeExpression GenerateExpressionForValue(PropertyInfo propertyInfo, object value, Type valueType) {
#if DEBUG
if (WebFormsCompilation.Enabled) {
Debug.WriteLine("GenerateExpressionForValue() {");
Debug.Indent();
}
#endif // DEBUG
CodeExpression rightExpr = null;
if (valueType == null) {
throw new ArgumentNullException("valueType");
}
PropertyDescriptor pd = null;
if (propertyInfo != null) {
pd = TypeDescriptor.GetProperties(propertyInfo.ReflectedType)[propertyInfo.Name];
}
if (valueType == typeof(string) && value is string) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("simple string");
rightExpr = new CodePrimitiveExpression((string)value);
}
else if (valueType.IsPrimitive) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("primitive");
rightExpr = new CodePrimitiveExpression(value);
}
else if (propertyInfo == null && valueType == typeof(object) &&
(value == null || value.GetType().IsPrimitive)) {
// If the type is object, and the value is a primitive, simply use a
// CodePrimitiveExpression instead of trying to use a TypeConverter (VSWhidbey 518773)
if (WebFormsCompilation.Enabled) Debug.WriteLine("primitive to object");
rightExpr = new CodePrimitiveExpression(value);
}
else if (valueType.IsArray) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("array");
Array array = (Array)value;
CodeArrayCreateExpression exp = new CodeArrayCreateExpression();
exp.CreateType = new CodeTypeReference(valueType.GetElementType());
if (array != null) {
foreach (object o in array) {
exp.Initializers.Add(GenerateExpressionForValue(null, o, valueType.GetElementType()));
}
}
rightExpr = exp;
}
else if (valueType == typeof(Type)) {
rightExpr = new CodeTypeOfExpression((Type) value);
}
else {
if (WebFormsCompilation.Enabled) Debug.WriteLine("other");
TypeConverter converter = null;
if (pd != null) {
converter = pd.Converter;
}
else {
converter = TypeDescriptor.GetConverter(valueType);
}
bool added = false;
if (converter != null) {
InstanceDescriptor desc = null;
if (converter.CanConvertTo(typeof(InstanceDescriptor))) {
desc = (InstanceDescriptor)converter.ConvertTo(value, typeof(InstanceDescriptor));
}
if (desc != null) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("has converter with instance descriptor");
// static field ref...
//
if (desc.MemberInfo is FieldInfo) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("persistinfo is a field ref");
CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(BuildGlobalCodeTypeReferenceExpression(desc.MemberInfo.DeclaringType.FullName), desc.MemberInfo.Name);
rightExpr = fieldRef;
added = true;
}
// static property ref
else if (desc.MemberInfo is PropertyInfo) {
if (WebFormsCompilation.Enabled) Debug.WriteLine("persistinfo is a property ref");
CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression(BuildGlobalCodeTypeReferenceExpression(desc.MemberInfo.DeclaringType.FullName), desc.MemberInfo.Name);
rightExpr = propRef;
added = true;
}
// static method invoke
//
else {
object[] args = new object[desc.Arguments.Count];
desc.Arguments.CopyTo(args, 0);
CodeExpression[] expressions = new CodeExpression[args.Length];
if (desc.MemberInfo is MethodInfo) {
MethodInfo mi = (MethodInfo)desc.MemberInfo;
ParameterInfo[] parameters = mi.GetParameters();
for(int i = 0; i < args.Length; i++) {
expressions[i] = GenerateExpressionForValue(null, args[i], parameters[i].ParameterType);
}
if (WebFormsCompilation.Enabled) Debug.WriteLine("persistinfo is a method invoke");
CodeMethodInvokeExpression methCall = new CodeMethodInvokeExpression(BuildGlobalCodeTypeReferenceExpression(desc.MemberInfo.DeclaringType.FullName), desc.MemberInfo.Name);
foreach (CodeExpression e in expressions) {
methCall.Parameters.Add(e);
}
rightExpr = new CodeCastExpression(valueType, methCall);
added = true;
}
else if (desc.MemberInfo is ConstructorInfo) {
ConstructorInfo ci = (ConstructorInfo)desc.MemberInfo;
ParameterInfo[] parameters = ci.GetParameters();
for(int i = 0; i < args.Length; i++) {
expressions[i] = GenerateExpressionForValue(null, args[i], parameters[i].ParameterType);
}
if (WebFormsCompilation.Enabled) Debug.WriteLine("persistinfo is a constructor call");
CodeObjectCreateExpression objectCreate = new CodeObjectCreateExpression(desc.MemberInfo.DeclaringType.FullName);
foreach (CodeExpression e in expressions) {
objectCreate.Parameters.Add(e);
}
rightExpr = objectCreate;
added = true;
}
}
}
}
if (!added) {
#if DEBUG
if (WebFormsCompilation.Enabled) {
Debug.WriteLine("unabled to determine type, attempting Parse");
Debug.Indent();
Debug.WriteLine("value.GetType == " + value.GetType().FullName);
Debug.WriteLine("value.ToString == " + value.ToString());
Debug.WriteLine("valueType == " + valueType.FullName);
if (propertyInfo != null) {
Debug.WriteLine("propertyInfo == " + propertyInfo.ReflectedType.FullName + "." + propertyInfo.Name + " : " + propertyInfo.PropertyType.FullName);
}
else {
Debug.WriteLine("propertyInfo == (null)");
}
Debug.Unindent();
}
#endif // DEBUG
// Not a known type: try calling Parse
// If possible, pass it an InvariantCulture (ASURT 79412)
if (valueType.GetMethod("Parse", new Type[] {typeof(string), typeof(CultureInfo)}) != null) {
CodeMethodInvokeExpression methCall = new CodeMethodInvokeExpression(BuildGlobalCodeTypeReferenceExpression(valueType.FullName), "Parse");
// Convert the object to a string.
// If we have a type converter, use it to convert to a string in a culture
// invariant way (ASURT 87094)
string s;
if (converter != null) {
s = converter.ConvertToInvariantString(value);
}
else {
s = value.ToString();
}
methCall.Parameters.Add(new CodePrimitiveExpression(s));
methCall.Parameters.Add(new CodePropertyReferenceExpression(BuildGlobalCodeTypeReferenceExpression(typeof(CultureInfo)), "InvariantCulture"));
rightExpr = methCall;
}
else if (valueType.GetMethod("Parse", new Type[] {typeof(string)}) != null) {
// Otherwise, settle for passing just the string
CodeMethodInvokeExpression methCall = new CodeMethodInvokeExpression(BuildGlobalCodeTypeReferenceExpression(valueType.FullName), "Parse");
methCall.Parameters.Add(new CodePrimitiveExpression(value.ToString()));
rightExpr = methCall;
}
else {
throw new HttpException(SR.GetString(SR.CantGenPropertySet, propertyInfo.Name, valueType.FullName));
}
}
}
#if DEBUG
if (WebFormsCompilation.Enabled) {
Debug.Unindent();
Debug.WriteLine("}");
}
#endif // DEBUG
return rightExpr;
}
// Adds a property assignment statement to "statements". This takes into account
// checking for nulls when the destination property type is a value type. This can
// also generate expressions that use IAttributeAccessor when desinationType is null.
internal static void CreatePropertySetStatements(CodeStatementCollection methodStatements, CodeStatementCollection statements,
CodeExpression target, string targetPropertyName, Type destinationType,
CodeExpression value,
CodeLinePragma linePragma) {
// Generates:
// If destination is property:
// If destination type is string:
// {{target}}.{{targetPropertyName}} = System.Convert.ToString( {{value}} );
// Else If destination type is reference type:
// {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) {{value}};
// Else destination type is value type:
// {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) ({value});
// Else use SetAttribute (expandos):
// ((IAttributeAccessor) {{target}} ).SetAttribute( {{targetPropertyName}} , System.Convert.ToString( {{value}} ));
bool useSetAttribute = false;
// This signifies it's using SetAttribute
if (destinationType == null) {
useSetAttribute = true;
}
if (useSetAttribute) {
// ((IAttributeAccessor) {{target}} ).SetAttribute( {{targetPropertyName}} , System.Convert.ToString( {{value}} ));
CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression();
CodeExpressionStatement setAttributeCall = new CodeExpressionStatement(methodInvoke);
setAttributeCall.LinePragma = linePragma;
// Dev11 128332: Ensure style attribute on html control is lowercase as required for xhtml validation
if (targetPropertyName.Equals("Style", StringComparison.Ordinal)) {
targetPropertyName = "style";
}
methodInvoke.Method.TargetObject = new CodeCastExpression(typeof(IAttributeAccessor), target);
methodInvoke.Method.MethodName = "SetAttribute";
methodInvoke.Parameters.Add(new CodePrimitiveExpression(targetPropertyName));
methodInvoke.Parameters.Add(GenerateConvertToString(value));
statements.Add(setAttributeCall);
}
else {
// Use the property setter. Must take into account that null cannot be
// cast to a value type, so we have to explicitly check for that in
// the code we generate.
if (destinationType.IsValueType) {
// {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) ({value});
CodeAssignStatement assignStmt = new CodeAssignStatement(
BuildPropertyReferenceExpression(target, targetPropertyName),
new CodeCastExpression(destinationType, value));
assignStmt.LinePragma = linePragma;
statements.Add(assignStmt);
}
else {
CodeExpression rightSide;
if (destinationType == typeof(string)) {
// {{target}}.{{targetPropertyName}} = System.Convert.ToString( {{value}} );
rightSide = GenerateConvertToString(value);
}
else {
// {{target}}.{{targetPropertyName}} = ( {{destinationType}} ) {{value}};
rightSide = new CodeCastExpression(destinationType, value);
}
CodeAssignStatement assignStmt = new CodeAssignStatement(
BuildPropertyReferenceExpression(target, targetPropertyName),
rightSide);
assignStmt.LinePragma = linePragma;
statements.Add(assignStmt);
}
}
}
// Generate a call to System.Convert.ToString(value, CultureInfo.CurrentCulture)
internal static CodeExpression GenerateConvertToString(CodeExpression value) {
CodeMethodInvokeExpression invokeExpr = new CodeMethodInvokeExpression();
invokeExpr.Method.TargetObject = BuildGlobalCodeTypeReferenceExpression(typeof(System.Convert));
invokeExpr.Method.MethodName = "ToString";
invokeExpr.Parameters.Add(value);
invokeExpr.Parameters.Add(new CodePropertyReferenceExpression(
BuildGlobalCodeTypeReferenceExpression(typeof(CultureInfo)), "CurrentCulture"));
return invokeExpr;
}
// Prepend a string TO the CompilerOptions string
internal static void PrependCompilerOption(
CompilerParameters compilParams, string compilerOptions) {
if (compilParams.CompilerOptions == null)
compilParams.CompilerOptions = compilerOptions;
else
compilParams.CompilerOptions = compilerOptions + " " + compilParams.CompilerOptions;
}
// Append a string to the CompilerOptions string
internal static void AppendCompilerOption(
CompilerParameters compilParams, string compilerOptions) {
if (compilParams.CompilerOptions == null)
compilParams.CompilerOptions = compilerOptions;
else
compilParams.CompilerOptions = compilParams.CompilerOptions + " " + compilerOptions;
}
internal static CodeExpression BuildPropertyReferenceExpression(
CodeExpression objRefExpr, string propName) {
// The name may contain several '.' separated properties, so we
// need to make sure we build the CodeDom accordingly (ASURT 91875, VSWhidbey 313018)
string[] parts = propName.Split('.');
CodeExpression ret = objRefExpr;
foreach (string part in parts)
ret = new CodePropertyReferenceExpression(ret, part);
return ret;
}
internal static CodeCastExpression BuildJSharpCastExpression(Type castType, CodeExpression expression) {
// VJ# does not support automatic boxing of value types, so passing a simple type, say a bool, into a method
// expecting an object will give a compiler error. We are working around this issue by adding special code for
// VJ# that will cast the expression for boxing. When the VJ# team adds implicit boxing of value types, we
// should remove this code. VSWhidbey 269028
CodeCastExpression castedExpression = new CodeCastExpression(castType, expression);
castedExpression.UserData.Add("CastIsBoxing", true);
return castedExpression;
}
internal static CodeTypeReference BuildGlobalCodeTypeReference(string typeName) {
return new CodeTypeReference(typeName, CodeTypeReferenceOptions.GlobalReference);
}
internal static CodeTypeReference BuildGlobalCodeTypeReference(Type type) {
return new CodeTypeReference(type, CodeTypeReferenceOptions.GlobalReference);
}
private static CodeTypeReferenceExpression BuildGlobalCodeTypeReferenceExpression(string typeName) {
// Returns an expression that resolves the type name from the root namespace,
// eg global::Namespace.TypeName
CodeTypeReference codeTypeReference = BuildGlobalCodeTypeReference(typeName);
CodeTypeReferenceExpression codeTypeReferenceExpression = new CodeTypeReferenceExpression(codeTypeReference);
return codeTypeReferenceExpression;
}
private static CodeTypeReferenceExpression BuildGlobalCodeTypeReferenceExpression(Type type) {
// Returns an expression that resolves the type name from the root namespace,
// eg global::Namespace.TypeName
CodeTypeReference codeTypeReference = BuildGlobalCodeTypeReference(type);
CodeTypeReferenceExpression codeTypeReferenceExpression = new CodeTypeReferenceExpression(codeTypeReference);
return codeTypeReferenceExpression;
}
}
}
|