using System;
using System.Reflection;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using AnticipatingMinds.Genesis.AspNetDom;
namespace AnticipatingMinds.Genesis.CodeDOM{
/// <exclude/>
public class CodeAssemblyTypeManager
{
internal CodeAssemblyTypeManager(CodeAssembly codeAssembly)
{
this.codeAssembly = codeAssembly;
}
/// <summary>
/// Loads all the types imported or exported by assembly.
/// </summary>
private void BuildAssemblyTypeSystem(ManualResetEvent cancelEvent)
{
caseSensitiveAssebmlyTypes.Clear();
foreach(CodeTypeInfo typeInfo in CodeTypeManagerHelper.GetAssemblyDeclaredTypes(codeAssembly,true))
{
if(!caseSensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseSensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
if(!caseInsensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseInsensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
}
foreach(string assemblyReference in codeAssembly.ReferencedAssemblies)
{
if(cancelEvent!= null && cancelEvent.WaitOne(0,false))
return;
if(assemblyReference == null || assemblyReference.Length == 0)
continue;
string assemblyFileName = Path.GetFileName(assemblyReference);
if(codeAssembly.Solution != null && codeAssembly.Solution.GetCodeAssembly(assemblyFileName) != null)
{
foreach(CodeTypeInfo typeInfo in CodeTypeManagerHelper.GetAssemblyDeclaredTypes(codeAssembly.Solution.GetCodeAssembly(assemblyFileName),false))
{
if(!caseSensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseSensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
if(!caseInsensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseInsensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
}
}
else
{
foreach(CodeTypeInfo typeInfo in CodeTypeManagerHelper.GetAssemblyDeclaredTypes(assemblyReference))
{
if(!caseSensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseSensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
if(!caseInsensitiveAssebmlyTypes.Contains(typeInfo.FullName))
caseInsensitiveAssebmlyTypes[typeInfo.FullName] = typeInfo;
}
}
}
}
/// <summary>
/// Finds type defined by its full name passed in <paramref name="typeName"/>.
/// </summary>
/// <param name="typeName">Full name of the type to look for.</param>
/// <returns>Returns type information if found - null otherwise.</returns>
public CodeTypeInfo FindType(string typeName)
{
return FindType(typeName,true);
}
public CodeTypeInfo FindType(string typeName,bool isCaseSensitive)
{
IDictionary typesDictionary = null;
if(isCaseSensitive)
{
typesDictionary = caseSensitiveAssebmlyTypes;
}
else
{
typesDictionary = caseInsensitiveAssebmlyTypes;
}
CodeTypeInfo typeInfo = typesDictionary[typeName] as CodeTypeInfo;
if(typeInfo != null)
return typeInfo;
if(typeName.LastIndexOf('[') == -1)
return null;
string baseTypeName = typeName.Substring(0,typeName.LastIndexOf('['));
typeInfo = FindType(baseTypeName);
if(typeInfo != null)
{
CodeTypeInfo arrayTypeInfo = typeInfo.GetArrayType();
typesDictionary[typeName] = arrayTypeInfo;
return arrayTypeInfo;
}
return null;
}
/// <summary>
/// Finds a field specified by <paramref name="typeName"/> type.
/// </summary>
/// <param name="typeName">Full name of type that contains an field.</param>
/// <param name="fieldName">Name of the field.</param>
/// <param name="isStatic">True if filed shall be defined as static.</param>
/// <returns>Returns pointer to the field information if found - null otherwise.</returns>
public CodeFieldInfo FindField(string typeName, string fieldName,bool isStatic)
{
return FindField(typeName, fieldName,isStatic,true);
}
public CodeFieldInfo FindField(string typeName, string fieldName,bool isStatic,bool isCaseSensitive)
{
CodeTypeInfo typeInfo = FindType(typeName,isCaseSensitive);
while(typeInfo != null)
{
CodeFieldInfo fieldInfo = typeInfo.FindField(fieldName,isCaseSensitive);
if(fieldInfo != null)
{
if((fieldInfo.IsStatic || fieldInfo.IsConst)&& isStatic)
return fieldInfo;
if((fieldInfo.IsStatic || fieldInfo.IsConst) && !isStatic)
return fieldInfo;
if(!(fieldInfo.IsStatic || fieldInfo.IsConst) && !isStatic)
return fieldInfo;
}
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType());
}
return null;
}
/// <summary>
/// Finds an event specified by <paramref name="typeName"/> type.
/// </summary>
/// <param name="typeName">Full name of type that contains an event.</param>
/// <param name="eventName">Name of the event.</param>
/// <param name="isStatic">True if event shall be defined as static.</param>
/// <returns>Returns pointer to the event information if found - null otherwise.</returns>
public CodeEventInfo FindEvent(string typeName, string eventName,bool isStatic)
{
return FindEvent(typeName, eventName,isStatic,true);
}
public CodeEventInfo FindEvent(string typeName, string eventName,bool isStatic,bool isCaseSensitive)
{
CodeTypeInfo typeInfo = FindType(typeName,isCaseSensitive);
while(typeInfo != null)
{
CodeEventInfo eventInfo = typeInfo.FindEvent(eventName,isCaseSensitive);
if(eventInfo != null)
{
//Event static information is missing from metadata
//cannot check static requirement!
// if(eventInfo.IsStatic && isStatic)
// return eventInfo;
//
// if(eventInfo.IsStatic && !isStatic)
// return eventInfo;
//
// if(!eventInfo.IsStatic && !isStatic)
// return eventInfo;
return eventInfo;
}
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType());
}
return null;
}
/// <summary>
/// Finds a property specified by <paramref name="typeName"/> type.
/// </summary>
/// <param name="typeName">Full name of type that contains the property.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="isStatic">True if property shall be defined as static.</param>
/// <returns>Returns pointer to the property information if found - null otherwise.</returns>
public CodePropertyInfo FindProperty(string typeName, string propertyName,bool isStatic)
{
return FindProperty(typeName, propertyName,isStatic,true);
}
public CodePropertyInfo FindProperty(string typeName, string propertyName,bool isStatic,bool isCaseSensitive)
{
CodeTypeInfo typeInfo = FindType(typeName,isCaseSensitive);
while(typeInfo != null)
{
CodePropertyInfo propertyInfo = typeInfo.FindProperty(propertyName,isCaseSensitive);
if(propertyInfo != null)
{
if(propertyInfo.IsStatic && isStatic)
return propertyInfo;
if(propertyInfo.IsStatic && !isStatic)
return propertyInfo;
if(!propertyInfo.IsStatic && !isStatic)
return propertyInfo;
}
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType());
}
return null;
}
/// <summary>
/// Determines whether the type <paramref name="typeName"/> derives from the type specified by <paramref name="derivedTypeName"/>.
/// </summary>
/// <param name="typeName">Tested type name.</param>
/// <param name="baseTypeName">Targeted base class typename.</param>
/// <returns>True is typeName derives from derivedTypeName.</returns>
public bool IsTypeSubclassOf(string typeName, string baseTypeName)
{
CodeTypeInfo currentTypeInfo = FindType(typeName);
while(currentTypeInfo != null)
{
if(string.Compare(currentTypeInfo.FullName,baseTypeName) == 0)
return true;
if(currentTypeInfo.GetBaseType() == null)
return false;
currentTypeInfo = FindType(currentTypeInfo.GetBaseType());
}
return false;
}
/// <summary>
/// Determines if type <paramref name="typeName"/> implements interface <paramref name="interfaceName"/>.
/// </summary>
/// <param name="typeName">Name of the type to test.</param>
/// <param name="interfaceName">Name of the interface to test for.</param>
/// <returns>True if type implements specified interface.</returns>
public bool DoesTypeImplementInterface(string typeName, string interfaceName)
{
CodeTypeInfo currentTypeInfo = FindType(typeName);
while(currentTypeInfo != null)
{
foreach(string implementedInterfaceName in currentTypeInfo.GetInterfaces())
{
if(string.Compare(implementedInterfaceName,interfaceName) == 0)
return true;
}
if(currentTypeInfo.GetBaseType() == null)
return false;
currentTypeInfo = FindType(currentTypeInfo.GetBaseType());
}
return false;
}
/// <summary>
/// Checks if the implicit conversion exists between two types.
/// </summary>
/// <param name="typeNameFrom">Full name of the source type.</param>
/// <param name="typeNameTo">Full name of the target type.</param>
/// <returns>True if there is an explicit conversion defined by compiler or type specified by <paramref name="typeNameFrom"/>
/// to the type specified by <paramref name="typeNameTo"/>
/// </returns>
public bool DoesImplicitConversionExist(string typeNameFrom,string typeNameTo)
{
if(typeNameFrom == null || typeNameFrom.Length == 0 || typeNameTo == null || typeNameTo.Length == 0)
return false;
// if(typeNameFrom == "System.Decimal" && typeNameTo == "System.String")
// Debugger.Break();
if(string.Compare(typeNameFrom,typeNameTo) == 0)
return true;
//Allow conversion from null value to any reference type
if(CodeTypeInfo.GetNullTypeInfo().FullName == typeNameFrom && !IsTypeSubclassOf(typeNameTo,"System.ValueType"))
return true;
//6.1.4 Implicit reference conversions
#region 6.1.4 Implicit reference conversions
if(IsTypeSubclassOf(typeNameFrom,typeNameTo))
return true;
CodeTypeInfo typeTo = FindType(typeNameTo);
if(typeTo == null)
return false;
if(typeTo.IsInterface)
{
if(DoesTypeImplementInterface(typeNameFrom,typeNameTo))
return true;
}
#endregion
//6.1.2 Implicit numeric conversions
#region 6.1.2 Implicit numeric conversions
switch(typeNameFrom)
{
case "System.SByte": //sbyte
switch(typeNameTo)
{
case "System.Int16": //sbyte->short
return true;
case "System.Int32": //sbyte->int
return true;
case "System.Int64": //sbyte->long
return true;
case "System.Single": //sbyte->float
return true;
case "System.Double": //sbyte->double
return true;
case "System.Decimal": //sbyte->decimal
return true;
}
break;
case "System.Byte": //byte
switch(typeNameTo)
{
case "System.Int16": //byte->short
return true;
case "System.UInt16": //byte->ushort
return true;
case "System.Int32": //byte->int
return true;
case "System.UInt32": //byte->uint
return true;
case "System.Int64": //byte->long
return true;
case "System.UInt64": //byte->ulong
return true;
case "System.Single": //byte->float
return true;
case "System.Double": //byte->double
return true;
case "System.Decimal": //byte->decimal
return true;
}
break;
case "System.Int16": //short
switch(typeNameTo)
{
case "System.Int32": //short->int
return true;
case "System.Int64": //short->long
return true;
case "System.Single": //short->float
return true;
case "System.Double": //short->double
return true;
case "System.Decimal": //short->decimal
return true;
}
break;
case "System.UInt16": //ushort
switch(typeNameTo)
{
case "System.Int32": //ushort->int
return true;
case "System.UInt32": //ushort->uint
return true;
case "System.Int64": //ushort->long
return true;
case "System.UInt64": //ushort->ulong
return true;
case "System.Single": //ushort->float
return true;
case "System.Double": //ushort->double
return true;
case "System.Decimal": //ushort->decimal
return true;
}
break;
case "System.Int32": //int
switch(typeNameTo)
{
case "System.Int64": //int->long
return true;
case "System.Single": //int->float
return true;
case "System.Double": //int->double
return true;
case "System.Decimal": //int->decimal
return true;
}
break;
case "System.UInt32": //uint
switch(typeNameTo)
{
case "System.Int64": //uint->long
return true;
case "System.UInt64": //uint->ulong
return true;
case "System.Single": //uint->float
return true;
case "System.Double": //uint->double
return true;
case "System.Decimal": //uint->decimal
return true;
}
break;
case "System.Int64": //long
case "System.UInt64": //ulong
switch(typeNameTo)
{
case "System.Single": //(u)long->float
return true;
case "System.Double": //(u)long->double
return true;
case "System.Decimal": //(u)long->decimal
return true;
}
break;
case "System.Char": //char
switch(typeNameTo)
{
case "System.UInt16": //char->ushort
return true;
case "System.Int32": //char->int
return true;
case "System.UInt32": //char->uint
return true;
case "System.Int64": //char->long
return true;
case "System.UInt64": //char->ulong
return true;
case "System.Single": //char->float
return true;
case "System.Double": //char->double
return true;
case "System.Decimal": //char->decimal
return true;
}
break;
case "System.Single": //char
switch(typeNameTo)
{
case "System.Double": //float->double
return true;
}
break;
}
#endregion
//6.1.7 User-defined implicit conversions
#region 6.1.7 User-defined implicit conversions
CodeTypeInfo typeFrom = FindType(typeNameFrom);
if(typeFrom == null)
return false;
foreach(CodeMethodInfo methodInfo in typeFrom.GetMethods())
{
if(methodInfo.Name != "op_Implicit")
continue;
//If the implicit conversion type is not equal our from type
//check if there is a possible conversion from it to the
//targeted type.
if(methodInfo.ReturnType != typeNameFrom)
if(DoesImplicitConversionExist(methodInfo.ReturnType,typeNameTo))
return true;
}
#endregion
return false;
}
/// <summary>
/// Finds an indexer defined on <paramref name="typeName"/> type.
/// </summary>
/// <param name="typeName">Full name of indexer container type.</param>
/// <param name="argumentTypes">A colection full names of types passed as an indexer arguments.</param>
/// <param name="isStatic">True if indexer shall be defined as static.</param>
/// <returns>Returns indexer information if indexer is found - null otherwise.</returns>
public CodePropertyInfo FindIndexer(string typeName,StringCollection argumentTypes,bool isStatic)
{
CodeTypeInfo typeInfo = FindType(typeName);
while(typeInfo != null)
{
foreach(CodePropertyInfo property in typeInfo.GetProperties())
{
if(isStatic && !property.IsStatic)
continue;
if(!isStatic && property.IsStatic)
continue;
if(string.Compare(property.Name,"Item",false) != 0)
continue;
if(DoParametersMatchArguments(property.GetIndexParameters(),argumentTypes))
return property;
}
//Lookup base type members if base type exists.
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType());
}
return null;
}
/// <summary>
/// Finds a method defined on <paramref name="typeName"/> or one of its base types.
/// </summary>
/// <param name="typeName">Full name of the type to search.</param>
/// <param name="methodName">Name of the method to search for.</param>
/// <param name="argumentTypes">A collection of argument types passesd on method invokation.</param>
/// <param name="isStatic">True if method shall be defined as static method.</param>
/// <returns>Returns method information if found - null otherwise.</returns>
public CodeMethodInfo FindMethod(string typeName,string methodName,StringCollection argumentTypes,bool isStatic)
{
return FindMethod(typeName,methodName,argumentTypes,isStatic,true);
}
public CodeMethodInfo FindMethod(string typeName,string methodName,StringCollection argumentTypes,bool isStatic,bool isCaseSensitive)
{
CodeTypeInfo typeInfo = FindType(typeName,isCaseSensitive);
while(typeInfo != null)
{
foreach(CodeMethodInfo methodInfo in typeInfo.GetMethods())
{
if(isStatic && !methodInfo.IsStatic)
continue;
if(!isStatic && methodInfo.IsStatic)
continue;
if(string.Compare(methodInfo.Name,methodName,!isCaseSensitive) != 0)
continue;
if(DoParametersMatchArguments(methodInfo.GetParameters(),argumentTypes))
return methodInfo;
}
//Lookup base type members if base type exists.
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType(),isCaseSensitive);
}
return null;
}
/// <summary>
/// Finds a method defined on <paramref name="typeName"/> or one of its base types.
/// </summary>
/// <param name="typeName">Full name of the type to search.</param>
/// <param name="methodName">Name of the method to search for.</param>
/// <param name="isStatic">True if method shall be defined as static method.</param>
/// <returns>Returns method information if found - null otherwise.</returns>
/// <remarks>Method binding is weak as method signature comparison is not performed in this case</remarks>
public CodeMethodInfo FindMethod(string typeName,string methodName,bool isStatic)
{
return FindMethod(typeName,methodName,isStatic,true);
}
public CodeMethodInfo FindMethod(string typeName,string methodName,bool isStatic,bool isCaseSensitive)
{
CodeTypeInfo typeInfo = FindType(typeName);
while(typeInfo != null)
{
foreach(CodeMethodInfo methodInfo in typeInfo.GetMethods())
{
if(isStatic && !methodInfo.IsStatic)
continue;
if(!isStatic && methodInfo.IsStatic)
continue;
if(string.Compare(methodInfo.Name,methodName,!isCaseSensitive) != 0)
continue;
return methodInfo;
}
//Lookup base type members if base type exists.
if(typeInfo.GetBaseType() == null)
return null;
typeInfo = FindType(typeInfo.GetBaseType(),isCaseSensitive);
}
return null;
}
public void BuildAssemblyMetadata()
{
BuildAssemblyMetadata(null);
}
public void BuildAssemblyMetadata(ManualResetEvent cancelEvent)
{
if(cancelEvent!= null && cancelEvent.WaitOne(0,false))
return;
//Collect all unused memory as building Metadata is memory hungry process
HelperData data = new HelperData();
data.cancelEvent = cancelEvent;
BuildAssemblyTypeSystem(cancelEvent);
if(cancelEvent!= null && cancelEvent.WaitOne(0,false))
return;
//Pass one. Resolve all type references.
ResolveTypeReferences(data);
if(data.cancelEvent!= null && data.cancelEvent.WaitOne(0,false))
return;
//Pass two. Resolve expressions
//It is important that we do it AFTER we resolved type references.
ResolveExpressionTypes(data);
ResolveAspNetTagProperties(data);
}
public void RegisterTypeName(string fulltypeName,CodeTypeInfo typeInfo)
{
if(!caseSensitiveAssebmlyTypes.Contains(fulltypeName))
caseSensitiveAssebmlyTypes[fulltypeName] = typeInfo;
if(!caseInsensitiveAssebmlyTypes.Contains(fulltypeName))
caseInsensitiveAssebmlyTypes[fulltypeName] = typeInfo;
}
public CodeTypeInfo ResolveTypeReference(CodeTypeReference typeReference,CodeTypeDeclaration typeReferenceContainer)
{
HelperData data = new HelperData();
data.cancelEvent = new ManualResetEvent(false);
CodeElement originalReferenceParent = typeReference.Parent;
typeReference.Parent = typeReferenceContainer;
data.activeNamspace = typeReferenceContainer.DeclaringNamespace;
data.activeTypeDeclaration = typeReferenceContainer;
typeReference.TypeInfo = ResolveTypeReference(typeReference,data);
typeReference.Parent = originalReferenceParent;
return typeReference.TypeInfo;
}
private class HelperData
{
public CodeNamespace activeNamspace = null;
public CodeTypeDeclaration activeTypeDeclaration = null;
public ManualResetEvent cancelEvent;
}
private void SynchronizedReplacedElement(CodeElement newElement,CodeElement oldElement)
{
newElement.Parent = oldElement.Parent;
newElement.SourcePosition = oldElement.SourcePosition;
newElement.CompileUnit = oldElement.CompileUnit;
newElement.Comment = oldElement.Comment;
newElement.DocumentationComment = oldElement.DocumentationComment;
}
private CodeTypeInfo ResolveTypeReference(CodeTypeReference typeReference, HelperData data)
{
bool isTypeCaseSensitive = true;
//Type reference specified in aspx control is case insensitive!
if(typeReference.Parent is AspNetServerControl)
isTypeCaseSensitive = false;
//1. Resolve arrays and pointers
if(typeReference.IsPointer)
{
CodeTypeInfo pointerType = ResolveTypeReference(typeReference.PointerType,data);
if(pointerType != null)
return pointerType.GetPointerType();
else
return null;
}
if(typeReference.IsArray)
{
CodeTypeInfo elementType = ResolveTypeReference(typeReference.ArrayElementType,data);
if(elementType != null)
return elementType.GetArrayType();
else
return null;
}
string typeName = typeReference.TypeName;
//1. Check parent/container types
//If type reference type is equal to the name of any of those types - we found our type reference
CodeElement parentType = typeReference;
while(parentType != null)
{
if(parentType is CodeTypeDeclaration)
if(String.Compare(typeName,(parentType as CodeTypeDeclaration).Name,!parentType.CompileUnit.IsCaseSensitive) == 0)
return FindType((parentType as CodeTypeDeclaration).FullName,parentType.CompileUnit.IsCaseSensitive);
parentType = parentType.Parent;
}
//2. Build table of aliases and imported namespaces
StringCollection importedNamespaces = new StringCollection();
NameValueCollection importedAliases = new NameValueCollection();
CodeNamespace activeNamespace = data.activeNamspace;
while(activeNamespace != null)
{
foreach(CodeNamespaceImport namespaceImport in activeNamespace.Imports)
{
if(namespaceImport.Alias != null && namespaceImport.Alias.Length != 0)
importedAliases.Add(namespaceImport.Alias,namespaceImport.ImportedName);
else
importedNamespaces.Add(namespaceImport.ImportedName);
}
importedNamespaces.Add(activeNamespace.FullName);
activeNamespace = activeNamespace.Parent as CodeNamespace;
}
CodeTypeInfo typeInfo = null;
//3. Check aliases first
foreach(string alias in importedAliases.AllKeys)
if(typeName.StartsWith(alias + "."))
{
//It is aliased name - look it up
string modifiedTypeName = typeName.Replace(alias+".",importedAliases[alias] + ".");
typeInfo = FindType(modifiedTypeName,isTypeCaseSensitive);
if(typeInfo != null)
return typeInfo;
}
//4. Check all imported namespaces
foreach(string namespaceName in importedNamespaces)
{
StringBuilder currentNamespacePrefix = new StringBuilder();
foreach(string subNamespacename in namespaceName.Split('.'))
{
currentNamespacePrefix.Append(subNamespacename);
string modifiedTypeName = currentNamespacePrefix.ToString()+"." + typeName;
typeInfo = FindType(modifiedTypeName,isTypeCaseSensitive);
if(typeInfo != null)
return typeInfo;
currentNamespacePrefix.Append('.');
}
}
//5. Lookup the full type name. Who knows - maybe it is fully qulified type name
typeInfo = FindType(typeName,isTypeCaseSensitive);
if(typeInfo != null)
return typeInfo;
return null;
}
private void ResolveExpressionTypes(HelperData data)
{
foreach(CodeAssemblyFile assemblyFile in codeAssembly.AssemblyFiles)
{
if(data.cancelEvent!= null && data.cancelEvent.WaitOne(0,false))
return;
CodeDomWalker.WalkCompileUnit(assemblyFile,new CodeDomWalker.WalkerCallback(ResolveExpressionTypesCallBack),data);
}
}
private CodeExpression ResolveExpressionType(CodeArrayInitializerExpression expression,HelperData data)
{
expression.ExpressionType = FindType(typeof(System.Array).FullName);
return expression;
}
private CodeExpression ResolveExpressionType(CodeArrayCreateExpression expression,HelperData data)
{
expression.ExpressionType = expression.CreateType.TypeInfo;
return expression;
}
private CodeExpression ResolveExpressionType(CodePrimitiveExpression expression,HelperData data)
{
expression.ExpressionType = FindType(expression.Value.GetType().FullName);
if(expression.ExpressionType == null)
expression.ExpressionType = FindType(typeof(Object).FullName);
return expression;
}
private CodeExpression ResolveExpressionType(CodeVariableReferenceExpression expression,HelperData data)
{
expression.ExpressionType = expression.VariableDeclaration.DeclarationType.TypeInfo;;
return expression;
}
private CodeExpression ResolveExpressionType(CodeObjectCreateExpression expression,HelperData data)
{
expression.ExpressionType = expression.CreateType.TypeInfo;
return expression;
}
private CodeExpression ResolveExpressionType(CodeIndexerInvokeExpression expression,HelperData data)
{
StringCollection argumentTypes = new StringCollection();
for(int argumentIndex = 0; argumentIndex < expression.Arguments.Count; argumentIndex++)
{
expression.Arguments[argumentIndex].Value = ResolveExpressionType(expression.Arguments[argumentIndex].Value,data);
//If I cannot resolve argument type - I will not be able to resolve method call -
//hence we can return now.
if(expression.Arguments[argumentIndex].Value.ExpressionType == null)
return expression;
else
argumentTypes.Add(expression.Arguments[argumentIndex].Value.ExpressionType.FullName);
}
/// This is generaly either a reference expression (this[], base[])
/// or a named referenece Expression (item[])
/// or a parenthesized expression (((MyType)myProperty)[MyTypeIndexerArgument]; )
if(expression.IndexerReferenceExpression is CodeNamedReferenceExpression)
{
CodeNamedReferenceExpression indexerNamedReference = (expression.IndexerReferenceExpression as CodeNamedReferenceExpression);
//If named and target equals null = than just Item on itself (probably VB syntaxis)
if(indexerNamedReference.TargetObject == null)
{
indexerNamedReference.ExpressionType = FindType(data.activeTypeDeclaration.FullName);
}
else
{
if(indexerNamedReference.TargetObject.ExpressionType == null)
indexerNamedReference.TargetObject = ResolveExpressionType(indexerNamedReference.TargetObject,data);
indexerNamedReference.ExpressionType = indexerNamedReference.TargetObject.ExpressionType;
}
}
else
{
if(expression.IndexerReferenceExpression.ExpressionType == null)
expression.IndexerReferenceExpression = ResolveExpressionType(expression.IndexerReferenceExpression,data);
}
//If we cannot find indexer type - exit. There is nothing we can do
if(expression.IndexerReferenceExpression.ExpressionType == null)
return expression;
CodePropertyInfo indexerInfo = FindIndexer(expression.IndexerReferenceExpression.ExpressionType.FullName,argumentTypes,false);
if(indexerInfo != null)
{
expression.ExpressionType = FindType(indexerInfo.PropertyType);
return expression;
}
//But wait we cannot find indexer! Maybe it is not an indexer? Maybe it is just a field or a property
//reference expression? It is just an array property or field!
if(expression.IndexerReferenceExpression is CodeNamedReferenceExpression)
{
CodeExpression memberReferenceExpression = ResolveExpressionType(expression.IndexerReferenceExpression as CodeNamedReferenceExpression,data);
if(memberReferenceExpression != null)
{
expression.IndexerReferenceExpression = memberReferenceExpression;
expression.ExpressionType = memberReferenceExpression.ExpressionType;
expression.IndexerReferenceExpression.ExpressionType = memberReferenceExpression.ExpressionType;
SynchronizedReplacedElement(memberReferenceExpression,expression.IndexerReferenceExpression);
}
}
return expression;
}
private CodeExpression ResolveExpressionType(CodeMethodInvokeExpression expression,HelperData data)
{
//1. Resolve invocation argument types
StringCollection parameterTypes = new StringCollection();
for(int argumentIndex = 0; argumentIndex < expression.Arguments.Count; argumentIndex++)
{
expression.Arguments[argumentIndex].Value = ResolveExpressionType(expression.Arguments[argumentIndex].Value,data);
//If I cannot resolve argument type - I will not be able to resolve method call -
//hence we can return now.
if(expression.Arguments[argumentIndex].Value.ExpressionType == null)
return expression;
else
{
string parameterTypeName = expression.Arguments[argumentIndex].Value.ExpressionType.FullName;
if(expression.Arguments[argumentIndex].Modifier != CodeArgument.CodeArgumentModifier.None)
parameterTypeName = parameterTypeName + "&";
parameterTypes.Add(parameterTypeName);
}
}
//Can be
//Method call foo();
//Event call myEvent();
//Delegate reference myDelegates[0]();
//delegate reference in array myDelegates[0]
if(!(expression.MethodReferenceExpression is CodeNamedReferenceExpression))
{
//For now resolve the method reference and see if delegate information is available to determine
//return type
expression.MethodReferenceExpression = ResolveExpressionType(expression.MethodReferenceExpression,data);
if(expression.MethodReferenceExpression.ExpressionType != null)
if(expression.MethodReferenceExpression.ExpressionType.IsDelegate)
expression.ExpressionType = FindType(expression.MethodReferenceExpression.ExpressionType.GetDelegateReturnType());
return expression;
}
CodeNamedReferenceExpression methodNamedReferenceExpression = expression.MethodReferenceExpression as CodeNamedReferenceExpression;
CodeTypeInfo methodTypeInfo = null;
bool isStatic = false;
string methodReferenceName = (expression.MethodReferenceExpression as CodeNamedReferenceExpression).Name;
if(methodNamedReferenceExpression.TargetObject == null)
{
methodTypeInfo = FindType(data.activeTypeDeclaration.FullName);
//If there is no target object - than static if static container - otherwise instance
//find out if static binding
CodeElement parentElement = expression;
while(parentElement != null && !(parentElement is CodeTypeMemberDeclaration))
parentElement = parentElement.Parent;
if(parentElement is CodeTypeMemberDeclaration)
isStatic = ((parentElement as CodeTypeMemberDeclaration).Modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Static) != 0;
}
else
{
//Try to resolve target type
if(methodNamedReferenceExpression.TargetObject.ExpressionType == null)
methodNamedReferenceExpression.TargetObject = ResolveExpressionType(methodNamedReferenceExpression.TargetObject,data);
//If cannot resolve target type check if it is a reference to the type and we are calling static method
if(methodNamedReferenceExpression.TargetObject != null && methodNamedReferenceExpression.TargetObject.ExpressionType == null)
{
//For static type reference target expression must be named reference.
//otherwise - we do not know what it is and should return.
if(!(methodNamedReferenceExpression.TargetObject is CodeNamedReferenceExpression))
return expression;
string typeName = GetTargetReferencesAsString(methodNamedReferenceExpression.TargetObject as CodeNamedReferenceExpression);
CodeTypeReference typeReference = new CodeTypeReference(typeName);
typeReference.TypeInfo = ResolveTypeReference(typeReference,data);
if(typeReference.TypeInfo != null)
{
//Determine source position of new type reference.
CodeNamedReferenceExpression firstReference = methodNamedReferenceExpression.TargetObject as CodeNamedReferenceExpression;
while(firstReference.TargetObject != null && firstReference.TargetObject is CodeNamedReferenceExpression)
firstReference = firstReference.TargetObject as CodeNamedReferenceExpression;
methodNamedReferenceExpression.TargetObject = new CodeTypeReferenceExpression(typeReference);
SynchronizedReplacedElement(methodNamedReferenceExpression.TargetObject,firstReference);
SynchronizedReplacedElement(typeReference,firstReference);
methodNamedReferenceExpression.TargetObject.ExpressionType = typeReference.TypeInfo;
isStatic = true;
}
}
if(methodNamedReferenceExpression.TargetObject.ExpressionType == null) //Cannot resolve target type
return expression;
methodTypeInfo = methodNamedReferenceExpression.TargetObject.ExpressionType;
}
//Is it a method call?
CodeMethodInfo methodInfo = null;
methodInfo = FindMethod(methodTypeInfo.FullName,methodReferenceName,parameterTypes,isStatic);
if(methodInfo != null)
{
expression.MethodReferenceExpression = new CodeMethodReferenceExpression(methodNamedReferenceExpression.TargetObject,methodNamedReferenceExpression.Name);
SynchronizedReplacedElement(expression.MethodReferenceExpression,methodNamedReferenceExpression);
(expression.MethodReferenceExpression as CodeMethodReferenceExpression).MethodInfo = methodInfo;
expression.MethodReferenceExpression.ExpressionType = null;
expression.ExpressionType = FindType(methodInfo.ReturnType);
return expression;
}
//Is it an event activation?
CodeEventInfo eventInfo = null;
//The nice thing about events is that I do not need to verify argumets list.
//compiler wount allow duplicate event names with different invokation signatures.
eventInfo = FindEvent(methodTypeInfo.FullName,methodReferenceName,isStatic);
if(eventInfo != null)
{
CodeEventReferenceExpression eventReferenceExpression = new CodeEventReferenceExpression(methodNamedReferenceExpression.TargetObject,methodNamedReferenceExpression.Name);
SynchronizedReplacedElement(eventReferenceExpression,methodNamedReferenceExpression);
eventReferenceExpression.ExpressionType = FindType(eventInfo.EventHandlerType);
eventReferenceExpression.EventInfo = eventInfo;
expression.MethodReferenceExpression = eventReferenceExpression;
CodeTypeInfo eventHandlerType = FindType(eventInfo.EventHandlerType);
if(eventHandlerType != null && eventHandlerType.IsDelegate)
expression.ExpressionType = FindType(eventHandlerType.GetDelegateReturnType());
return expression;
}
//Debug.Assert(expression.ExpressionType != null,"Debug purposes. If it is null it is bad but is not fatal. Just stop processing these expressions.");
expression.ExpressionType = null;
return expression;
}
private CodeExpression ResolveExpressionType(CodeNamedReferenceExpression expression,HelperData data)
{
return ResolveExpressionType(expression,data,true);
}
private CodeExpression ResolveExpressionType(CodeNamedReferenceExpression expression,HelperData data,bool isCaseSensitive)
{
//If target is not null and have not been resolved yet - resolve it.
if(expression.TargetObject != null && expression.TargetObject.ExpressionType == null)
expression.TargetObject = ResolveExpressionType(expression.TargetObject,data);
//find out if static binding
bool isStatic = false;
//if target is not null and cannot be resolved - check if target is reference to a type
//and we are reference to the statc property of the type.
if(expression.TargetObject != null && expression.TargetObject.ExpressionType == null)
{
//For static type reference target expression must be named reference.
//otherwise - we do not know what it is and should return.
if(!(expression.TargetObject is CodeNamedReferenceExpression))
return expression;
string typeName = GetTargetReferencesAsString(expression.TargetObject as CodeNamedReferenceExpression);
CodeTypeReference typeReference = new CodeTypeReference(typeName);
typeReference.TypeInfo = ResolveTypeReference(typeReference,data);
if(typeReference.TypeInfo != null)
{
CodeNamedReferenceExpression firstReference = expression.TargetObject as CodeNamedReferenceExpression;
while(firstReference.TargetObject != null && firstReference.TargetObject is CodeNamedReferenceExpression)
firstReference = firstReference.TargetObject as CodeNamedReferenceExpression;
expression.TargetObject = new CodeTypeReferenceExpression(typeReference);
expression.TargetObject.ExpressionType = typeReference.TypeInfo;
SynchronizedReplacedElement(expression.TargetObject,firstReference);
SynchronizedReplacedElement(typeReference,firstReference);
isStatic = true;
}
}
//If I cannot determine resulting type - just exit.
if(expression.TargetObject != null && expression.TargetObject.ExpressionType == null)
return expression;
if(data.activeTypeDeclaration == null)
return expression;
//Check if it is reference to the type/target member
//field
//property
//event
CodeTypeInfo expressionType = null;
if(expression.TargetObject == null)
{
expressionType = FindType(data.activeTypeDeclaration.FullName,isCaseSensitive);
//If target object is null check container type. If static than call is also static
CodeElement parentElement = expression;
while(parentElement != null && !(parentElement is CodeTypeMemberDeclaration))
parentElement = parentElement.Parent;
if(parentElement is CodeTypeMemberDeclaration)
isStatic = ((parentElement as CodeTypeMemberDeclaration).Modifiers & CodeTypeMemberDeclaration.MemberDeclarationModifiers.Static) != 0;
}
else
expressionType = expression.TargetObject.ExpressionType;
Debug.Assert(expressionType != null,"Expression type must be defined here.");
CodeFieldInfo filedInfo = FindField(expressionType.FullName,expression.Name,isStatic,isCaseSensitive);
if(filedInfo != null)
{
CodeFieldReferenceExpression fieldReferenceExpression = null;
if(expression is CodeFieldReferenceExpression)
fieldReferenceExpression = (expression as CodeFieldReferenceExpression);
else
{
fieldReferenceExpression = new CodeFieldReferenceExpression(expression.TargetObject,expression.Name);
SynchronizedReplacedElement(fieldReferenceExpression,expression);
}
fieldReferenceExpression.ExpressionType = FindType(filedInfo.FieldType,isCaseSensitive);
fieldReferenceExpression.FieldInfo = filedInfo;
return fieldReferenceExpression;
}
CodePropertyInfo propertyInfo = FindProperty(expressionType.FullName,expression.Name,isStatic,isCaseSensitive);
if(propertyInfo != null)
{
CodePropertyReferenceExpression propertyReferenceExpression;
if(expression is CodePropertyReferenceExpression)
propertyReferenceExpression = expression as CodePropertyReferenceExpression;
else
{
propertyReferenceExpression = new CodePropertyReferenceExpression(expression.TargetObject,expression.Name);
SynchronizedReplacedElement(propertyReferenceExpression,expression);
}
propertyReferenceExpression.ExpressionType = FindType(propertyInfo.PropertyType,isCaseSensitive);
propertyReferenceExpression.PropertyInfo = propertyInfo;
return propertyReferenceExpression;
}
CodeEventInfo eventInfo = FindEvent(expressionType.FullName,expression.Name,isStatic,isCaseSensitive);
if(eventInfo != null)
{
CodeEventReferenceExpression eventReferenceExpression;
if(expression is CodeEventReferenceExpression)
eventReferenceExpression = expression as CodeEventReferenceExpression;
else
{
eventReferenceExpression = new CodeEventReferenceExpression(expression.TargetObject,expression.Name);
SynchronizedReplacedElement(eventReferenceExpression,expression);
}
eventReferenceExpression.ExpressionType = FindType(eventInfo.EventHandlerType,isCaseSensitive);
eventReferenceExpression.EventInfo = eventInfo;
return eventReferenceExpression;
}
//If we still could not resolve an expression
//it looks like we have a reference to a delegate in the method
//Try to find method and disregard any parametrs -
//it is for delegates only.
//Think if we can redo it to extend metadata and make method reference more accurate by
//analyzing delegate signature and requiring the same signature for our method.
if(expression is CodeUnresolvedReferenceExpression)
{
CodeMethodInfo methodInfo = FindMethod(expressionType.FullName,expression.Name,isStatic,isCaseSensitive);
//If we checked instance method and could not find it - check if we can find static method
//it will be as delegate reference
if(isStatic == false && methodInfo == null)
methodInfo = FindMethod(expressionType.FullName,expression.Name,true,isCaseSensitive);
if(methodInfo != null)
{
CodeMethodReferenceExpression methodReferenceExpression = new CodeMethodReferenceExpression(expression.TargetObject,expression.Name);
SynchronizedReplacedElement(methodReferenceExpression,expression);
methodReferenceExpression.MethodInfo = methodInfo;
methodReferenceExpression.ExpressionType = null;
return methodReferenceExpression;
}
}
return expression;
}
private string GetOperatorMethodName(CodeBinaryOperatorType binaryOperator)
{
switch(binaryOperator)
{
case CodeBinaryOperatorType.Addition:
return "op_Addition";
case CodeBinaryOperatorType.BitwiseAnd:
return "op_BitwiseAnd";
case CodeBinaryOperatorType.BitwiseOr:
return "op_BitwiseOr";
case CodeBinaryOperatorType.Division:
return "op_Division";
case CodeBinaryOperatorType.Equality:
return "op_Equality";
case CodeBinaryOperatorType.ExclusiveOr:
return "op_ExclusiveOr";
case CodeBinaryOperatorType.GreaterThan:
return "op_GreaterThan";
case CodeBinaryOperatorType.GreaterThanOrEqual:
return "op_GreaterThanOrEqual";
case CodeBinaryOperatorType.Inequality:
return "op_Inequality";
case CodeBinaryOperatorType.LeftShift:
return "op_LeftShift";
case CodeBinaryOperatorType.LessThan:
return "op_LessThan";
case CodeBinaryOperatorType.LessThanOrEqual:
return "op_LessThanOrEqual";
case CodeBinaryOperatorType.Modulus:
return "op_Modulus";
case CodeBinaryOperatorType.Multiply:
return "op_Multiply";
case CodeBinaryOperatorType.RightShift:
return "op_RightShift";
case CodeBinaryOperatorType.Subtraction:
return "op_Subtraction";
}
return null;
}
private CodeExpression ResolveExpressionType(CodeTypeReferenceExpression expression,HelperData data)
{
expression.ExpressionType = expression.ReferencedType.TypeInfo;
return expression;
}
private CodeExpression ResolveExpressionType(CodeBinaryExpression expression,HelperData data)
{
if(expression.RightOperand != null && expression.RightOperand.ExpressionType == null)
expression.RightOperand = ResolveExpressionType(expression.RightOperand,data);
if(expression.LeftOperand != null && expression.LeftOperand.ExpressionType == null)
expression.LeftOperand = ResolveExpressionType(expression.LeftOperand,data);
if(expression.RightOperand.ExpressionType == null || expression.LeftOperand.ExpressionType == null)
return expression;
switch(expression.Operator)
{
case CodeBinaryOperatorType.Equality:
case CodeBinaryOperatorType.GreaterThan:
case CodeBinaryOperatorType.GreaterThanOrEqual:
case CodeBinaryOperatorType.Inequality:
case CodeBinaryOperatorType.Is:
case CodeBinaryOperatorType.LessThan:
case CodeBinaryOperatorType.LessThanOrEqual:
case CodeBinaryOperatorType.LogicalAnd:
case CodeBinaryOperatorType.LogicalOr:
{
expression.ExpressionType = FindType("System.Boolean");
break;
}
case CodeBinaryOperatorType.Assign:
{
expression.ExpressionType = expression.LeftOperand.ExpressionType;
break;
}
case CodeBinaryOperatorType.As:
{
expression.ExpressionType = expression.RightOperand.ExpressionType;
break;
}
case CodeBinaryOperatorType.MemberSelection:
{
expression.ExpressionType = null;
break;
}
case CodeBinaryOperatorType.PointerToMemberSelection:
{
expression.ExpressionType = null;
break;
}
case CodeBinaryOperatorType.Question:
{
expression.LeftOperand = ResolveExpressionType(expression.LeftOperand as CodeConditionalExpression,data);
break;
}
default:
{
StringCollection arguments = new StringCollection();
string leftArgumentTypeName = expression.LeftOperand.ExpressionType.FullName;
string rightArgumentTypeName = expression.RightOperand.ExpressionType.FullName;
arguments.Add(leftArgumentTypeName);
arguments.Add(rightArgumentTypeName);
//Check user overloadable operators first
CodeMethodInfo opMethod = FindMethod(expression.LeftOperand.ExpressionType.FullName,GetOperatorMethodName(expression.Operator),arguments,true);
if(opMethod != null)
expression.ExpressionType = FindType(opMethod.ReturnType);
//Check predefined operators
//it is a little cheat but it will work as almost all operators define itself
//in a simetrical form T (T op T)
//except for couple of cases:
//Two special cases for string:
//string operator +(string x, object y);
//string operator +(object x, string y);
if(expression.Operator == CodeBinaryOperatorType.Addition || expression.Operator == CodeBinaryOperatorType.AdditionAssignment)
{
if( (DoesImplicitConversionExist(leftArgumentTypeName,"System.String") && DoesImplicitConversionExist(rightArgumentTypeName,"System.Object")) ||
(DoesImplicitConversionExist(leftArgumentTypeName,"System.Object") && DoesImplicitConversionExist(rightArgumentTypeName,"System.String")))
{
expression.ExpressionType = FindType("System.String");
break;
}
}
//Another special case are delegates - they define + and - operators
if(expression.Operator == CodeBinaryOperatorType.Addition ||
expression.Operator == CodeBinaryOperatorType.AdditionAssignment ||
expression.Operator == CodeBinaryOperatorType.SubtractionAssignment ||
expression.Operator == CodeBinaryOperatorType.Subtraction)
{
if((DoesImplicitConversionExist(leftArgumentTypeName,"System.Delegate") && DoesImplicitConversionExist(rightArgumentTypeName,"System.Delegate")))
{
expression.ExpressionType = FindType(leftArgumentTypeName);
break;
}
}
string[] predefinedTypeNames = new string[]
{
"System.Int32",
"System.UInt32",
"System.Int64",
"System.UInt64",
"System.Single",
"System.Double",
"System.Decimal",
};
foreach(string predefinedTypeName in predefinedTypeNames)
{
if(DoesImplicitConversionExist(leftArgumentTypeName,predefinedTypeName) && DoesImplicitConversionExist(rightArgumentTypeName,predefinedTypeName))
{
expression.ExpressionType = FindType(predefinedTypeName);
break;
}
}
break;
}
}
return expression;
}
private CodeExpression ResolveExpressionType(CodeConditionalExpression expression,HelperData data)
{
expression.TrueExpression = ResolveExpressionType(expression.TrueExpression,data);
expression.FalseExpression = ResolveExpressionType(expression.FalseExpression,data);
if(expression.TrueExpression.ExpressionType != null && !(expression.TrueExpression is CodeNullReferenceExpression))
{
expression.ExpressionType = expression.TrueExpression.ExpressionType;
}
else
{
expression.ExpressionType = expression.FalseExpression.ExpressionType;
}
return expression;
}
private CodeExpression ResolveExpressionType(CodeUnaryExpression expression,HelperData data)
{
if(expression.Operand != null && expression.Operand.ExpressionType == null)
ResolveExpressionType(expression.Operand,data);
if(expression.Operand != null)
expression.ExpressionType = expression.Operand.ExpressionType;
return expression;
}
private CodeExpression ResolveExpressionType(CodeMethodParameterReferenceExpression expression,HelperData data)
{
if(expression.MethodParameter != null && expression.MethodParameter.Type != null && expression.MethodParameter.Type.TypeInfo != null)
expression.ExpressionType = expression.MethodParameter.Type.TypeInfo;
else
expression.ExpressionType = null;
return expression;
}
private CodeExpression ResolveExpressionType(CodeParenthesizedExpression expression,HelperData data)
{
if(expression.Expression.ExpressionType == null)
expression.Expression = ResolveExpressionType(expression.Expression,data);
expression.ExpressionType = expression.Expression.ExpressionType;
return expression;
}
private CodeExpression ResolveExpressionType(CodeTypeCastExpression expression,HelperData data)
{
//If expression to be cast has not been resolved yet - do so!
if(expression.Expression != null && expression.Expression.ExpressionType == null)
expression.Expression = ResolveExpressionType(expression.Expression,data);
if(expression.TargetType.TypeInfo != null)
expression.ExpressionType = FindType(expression.TargetType.TypeInfo.FullName);
else
expression.ExpressionType = null;
return expression;
}
private CodeExpression ResolveExpressionType(CodeExpression expression,HelperData data)
{
if(expression.ExpressionType != null)
return expression;
if(expression is CodeMethodParameterReferenceExpression)
return ResolveExpressionType(expression as CodeMethodParameterReferenceExpression,data);
if(expression is CodeParenthesizedExpression)
return ResolveExpressionType(expression as CodeParenthesizedExpression,data);
if(expression is CodeArrayInitializerExpression)
return ResolveExpressionType(expression as CodeArrayInitializerExpression,data);
if(expression is CodeArrayCreateExpression)
return ResolveExpressionType(expression as CodeArrayCreateExpression,data);
if(expression is CodeObjectCreateExpression)
return ResolveExpressionType(expression as CodeObjectCreateExpression,data);
if(expression is CodePrimitiveExpression)
return ResolveExpressionType(expression as CodePrimitiveExpression,data);
if(expression is CodeVariableReferenceExpression)
return ResolveExpressionType(expression as CodeVariableReferenceExpression,data);
if(expression is CodeObjectCreateExpression)
return ResolveExpressionType(expression as CodeObjectCreateExpression,data);
if(expression is CodeMethodInvokeExpression)
return ResolveExpressionType(expression as CodeMethodInvokeExpression,data);
if(expression is CodeNamedReferenceExpression)
return ResolveExpressionType(expression as CodeNamedReferenceExpression,data);
if(expression is CodeBinaryExpression)
return ResolveExpressionType(expression as CodeBinaryExpression,data);
if(expression is CodeThisReferenceExpression)
return ResolveExpressionType(expression as CodeThisReferenceExpression,data);
if(expression is CodeBaseReferenceExpression)
return ResolveExpressionType(expression as CodeBaseReferenceExpression,data);
if(expression is CodeTypeReferenceExpression)
return ResolveExpressionType(expression as CodeTypeReferenceExpression,data);
if(expression is CodeUnaryExpression)
return ResolveExpressionType(expression as CodeUnaryExpression,data);
if(expression is CodeIndexerInvokeExpression)
return ResolveExpressionType(expression as CodeIndexerInvokeExpression,data);
if(expression is CodeTypeCastExpression)
return ResolveExpressionType(expression as CodeTypeCastExpression,data);
if(expression is CodeConditionalExpression)
return ResolveExpressionType(expression as CodeConditionalExpression,data);
//This expression does not have a type associated with it. It can only be used with
//method invoke expression.
if(expression is CodeMethodReferenceExpression)
{
expression.ExpressionType = null;
return expression;
}
///Null does not have a type null is null.
if(expression is CodeNullReferenceExpression)
{
expression.ExpressionType = CodeTypeInfo.GetNullTypeInfo();
return expression;
}
Debug.WriteLine(expression.GetType().FullName);
return expression;
}
private CodeExpression ResolveExpressionType(CodeThisReferenceExpression expression,HelperData data)
{
expression.ExpressionType = FindType(data.activeTypeDeclaration.FullName);
return expression;
}
private CodeExpression ResolveExpressionType(CodeBaseReferenceExpression expression,HelperData data)
{
string baseTypeName = string.Empty;
CodeTypeInfo activeTypeInfo = FindType(data.activeTypeDeclaration.FullName);
if(activeTypeInfo != null && activeTypeInfo.GetBaseType() != null)
{
CodeTypeInfo baseTypeInfo = FindType(activeTypeInfo.GetBaseType());
expression.ExpressionType = baseTypeInfo;
}
return expression;
}
private void ReplaceExpression(CodeExpression oldExpression,CodeExpression newExpression)
{
if(oldExpression.Parent == null)
return;
Type elementType = oldExpression.Parent.GetType();
foreach(PropertyInfo propertyInfo in elementType.GetProperties())
{
//If property does not have public getter - we are not intrested.
if(!propertyInfo.CanRead)
continue;
object properyValue = propertyInfo.GetValue(oldExpression.Parent,null);
if(properyValue == oldExpression)
{
propertyInfo.SetValue(oldExpression.Parent,newExpression,null);
return;
}
if(properyValue is CodeElementCollectionBase)
{
MethodInfo indexOfMethod = propertyInfo.PropertyType.GetMethod("IndexOf");
if(indexOfMethod.GetParameters().Length != 1)
continue;
int index = -1;
Type parametherType = indexOfMethod.GetParameters()[0].ParameterType;
if(parametherType == oldExpression.GetType() ||
oldExpression.GetType().IsSubclassOf(parametherType))
{
index = (int) indexOfMethod.Invoke(properyValue,new object[1] {oldExpression});
}
if(index != -1)
{
MethodInfo set_ItemMethod = propertyInfo.PropertyType.GetMethod("set_Item");
set_ItemMethod.Invoke(properyValue,new object[2] {index,newExpression});
return;
}
}
}
}
private CodeDomWalker.WalkerCallbackReturn ResolveExpressionTypesCallBack(CodeElement codeElement,CodeDomWalker.CallBackNotificationType notification,CodeDomWalkerContext walkerContext,object applicationData)
{
switch(notification)
{
case CodeDomWalker.CallBackNotificationType.OnElement:
{
if(codeElement is CodeExpression)
{
//Let's get it easier on CPU
System.Threading.Thread.Sleep(0);
CodeExpression newExpression = ResolveExpressionType(codeElement as CodeExpression,applicationData as HelperData);
if(newExpression != codeElement)
{
ReplaceExpression(codeElement as CodeExpression,newExpression);
return CodeDomWalker.WalkerCallbackReturn.NextSibling;
}
}
break;
}
case CodeDomWalker.CallBackNotificationType.OnElementChildrenStarted:
{
if(codeElement is CodeTypeDeclaration)
{
HelperData data = applicationData as HelperData;
data.activeTypeDeclaration = codeElement as CodeTypeDeclaration;
}
if(codeElement is CodeNamespace)
{
HelperData data = applicationData as HelperData;
data.activeNamspace = codeElement as CodeNamespace;
}
break;
}
case CodeDomWalker.CallBackNotificationType.OnElementChildrenFinished:
{
if(codeElement is CodeTypeDeclaration)
{
HelperData data = applicationData as HelperData;
data.activeTypeDeclaration = data.activeTypeDeclaration.DeclaringType;
}
if(codeElement is CodeNamespace)
{
HelperData data = applicationData as HelperData;
data.activeNamspace = data.activeNamspace.DeclaringNamespace;
}
break;
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
private void ResolveTypeReferences(HelperData data)
{
foreach(CodeAssemblyFile assemblyFile in codeAssembly.AssemblyFiles)
{
if(data.cancelEvent!= null && data.cancelEvent.WaitOne(0,false))
return;
CodeDomWalker.WalkCompileUnit(assemblyFile,new CodeDomWalker.WalkerCallback(UpdateCodeDomTypeReferencesCallBack),data);
}
}
private CodeDomWalker.WalkerCallbackReturn UpdateCodeDomTypeReferencesCallBack(CodeElement codeElement,CodeDomWalker.CallBackNotificationType notification,CodeDomWalkerContext walkerContext,object applicationData)
{
switch(notification)
{
case CodeDomWalker.CallBackNotificationType.OnElement:
{
if(codeElement is CodeTypeReference)
{
CodeTypeReference typeReference = codeElement as CodeTypeReference;
if(typeReference.TypeInfo != null)
break;
typeReference.TypeInfo = ResolveTypeReference(typeReference,applicationData as HelperData);
if(typeReference.TypeInfo == null && codeElement.Parent is CodeAttribute)
{
//If this is type reference for an attribute - lets see if we forgot an atribute suffix
if(!typeReference.TypeName.EndsWith("Attribute"))
{
string originalAttributeTypeName = typeReference.TypeName;
typeReference.TypeName = originalAttributeTypeName + "Attribute";
typeReference.TypeInfo = ResolveTypeReference(typeReference,applicationData as HelperData);
//Restore original type name
typeReference.TypeName = originalAttributeTypeName;
}
}
//Let's get it easier on CPU
System.Threading.Thread.Sleep(0);
}
break;
}
case CodeDomWalker.CallBackNotificationType.OnElementChildrenStarted:
{
if(codeElement is CodeNamespace)
{
HelperData data = applicationData as HelperData;
data.activeNamspace = codeElement as CodeNamespace;
}
if(codeElement is CodeTypeDeclaration)
{
HelperData data = applicationData as HelperData;
data.activeTypeDeclaration = codeElement as CodeTypeDeclaration;
}
break;
}
case CodeDomWalker.CallBackNotificationType.OnElementChildrenFinished:
{
if(codeElement is CodeNamespace)
{
HelperData data = applicationData as HelperData;
data.activeNamspace = data.activeNamspace.DeclaringNamespace;
}
if(codeElement is CodeTypeDeclaration)
{
HelperData data = applicationData as HelperData;
data.activeTypeDeclaration = data.activeTypeDeclaration.DeclaringType;
}
break;
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
private string GetTargetReferencesAsString(CodeNamedReferenceExpression namedReferenceExpression)
{
if(namedReferenceExpression == null)
return string.Empty;
if(namedReferenceExpression.TargetObject == null)
return namedReferenceExpression.Name;
if(!(namedReferenceExpression.TargetObject is CodeNamedReferenceExpression))
return namedReferenceExpression.Name;
return GetTargetReferencesAsString(namedReferenceExpression.TargetObject as CodeNamedReferenceExpression) + "." + namedReferenceExpression.Name;
}
private void ResolveAspNetTagProperties(HelperData data)
{
foreach(CodeAssemblyFile assemblyFile in codeAssembly.AssemblyFiles)
{
if(data.cancelEvent!= null && data.cancelEvent.WaitOne(0,false))
return;
CodeDomWalker.WalkCompileUnit(assemblyFile,new CodeDomWalker.WalkerCallback(ResolveAspNetControlPropertiesCallBack),data);
}
}
private void ResolveAspNetTagProperties(AspNetTag aspNetTag)
{
if(aspNetTag is AspNetServerControl)
{
ResolveAspNetServerControlProperties(aspNetTag as AspNetServerControl);
return;
}
Debug.Assert(false,"If you have added a new type of AspNet tag add property resolution handler here");
}
private void ResolveAspNetServerControlProperties(AspNetServerControl control)
{
//Step 1: Find out who is our parent:
CodeElement parent = control.Parent;
while(parent != null && !(parent is AspNetClassDeclaration))
parent = parent.Parent;
//If we could not resolve type - exit
if(parent == null || (parent as AspNetClassDeclaration).TypeInfo == null)
return;
AspNetClassDeclaration aspNetClass = parent as AspNetClassDeclaration;
//Helper struct
HelperData data = new HelperData();
data.activeNamspace = new CodeNamespace();
data.activeTypeDeclaration = aspNetClass;
data.cancelEvent = new ManualResetEvent(false);
foreach(AspNetTagAttribute attribute in control.Attributes)
{
string attributeName = null;
string attributeValue = null;
attributeName = attribute.Name.Name;
attributeValue = attribute.Value.Name;
if(attributeName == null || attributeValue == null)
return;
//Generaly speaking attributes are potential properties of control type where values are
//potential properties of defining (page) class.
//But in all known to mankind cases there is only one case when value of the attribute is
//a member of defining class - it is case when the attribute itself defines an event and
//than attribute value defines a method reference.
//I want to use already written routin to resolve attribute types. There is only one problem
//by default it uses type past in the declaration to resolve members - to avoid it
//I'll create a fake target and will set it to control's type - effectevly enabling
//resolution from control type's type.
//And oh, eah one more thing: Some .... decided to name event attributes on aspx tags
//with prefix 'On'
CodeUnresolvedReferenceExpression fakeTarget = new CodeUnresolvedReferenceExpression();
fakeTarget.ExpressionType = control.ControlType.TypeInfo;
attribute.Name.TargetObject = fakeTarget;
if(attribute.Name.Name.ToLower().StartsWith("on"))
{
string originalName = attribute.Name.Name;
attribute.Name.Name = originalName.Substring(2);
attribute.Name = ResolveExpressionType(attribute.Name,data,false) as CodeNamedReferenceExpression;
attribute.Name.Name = originalName;
}
if(attribute.Name is CodeUnresolvedReferenceExpression)
attribute.Name = ResolveExpressionType(attribute.Name,data,false) as CodeNamedReferenceExpression;
attribute.Name.TargetObject = null;
//Now lets resolve attribute value. We only care if attribute name is an event
if(attribute.Name is CodeEventReferenceExpression)
{
CodeMethodInfo methodInfo = FindMethod(aspNetClass.TypeInfo.FullName,attribute.Value.Name,false,false);
if(methodInfo != null)
{
CodeMethodReferenceExpression methodReference = new CodeMethodReferenceExpression();
SynchronizedReplacedElement(methodReference,attribute.Value);
methodReference.Name = attribute.Value.Name;
methodReference.MethodInfo = methodInfo;
methodReference.ExpressionType = null;
attribute.Value = methodReference;
}
}
else
{
attribute.Value = ResolveExpressionType(attribute.Value,data,false) as CodeNamedReferenceExpression;
}
}
}
private CodeDomWalker.WalkerCallbackReturn ResolveAspNetControlPropertiesCallBack(CodeElement codeElement,CodeDomWalker.CallBackNotificationType notification,CodeDomWalkerContext walkerContext,object applicationData)
{
switch(notification)
{
case CodeDomWalker.CallBackNotificationType.OnElement:
{
if(codeElement is AspNetTag)
{
ResolveAspNetTagProperties(codeElement as AspNetTag);
}
break;
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
private bool DoParametersMatchArguments(CodeParameterInfo[] methodParameters,StringCollection argumentTypes)
{
//No one has paramethers?
if(methodParameters.Length == 0 && argumentTypes.Count == 0)
return true;
//Method parameters are optional
if(methodParameters.Length == 1 && methodParameters[0].IsParameterArray && argumentTypes.Count == 0)
return true;
for(int methodParameterIndex = 0,argumentIndex = 0; methodParameterIndex < methodParameters.Length; methodParameterIndex++,argumentIndex++)
{
if(argumentIndex >= argumentTypes.Count)
return false;
//If current methodArgument is optional array of parameters
//then process the rest of the arguments and see if they match
if(methodParameters[methodParameterIndex].IsParameterArray && methodParameters[methodParameterIndex].TypeName.LastIndexOf('[') != -1)
{
string optionalParameterType = methodParameters[methodParameterIndex].TypeName;
optionalParameterType = optionalParameterType.Substring(0,optionalParameterType.LastIndexOf('['));
bool signatureMistmatch = false;
for(int optionalArgumentIndex = argumentIndex; optionalArgumentIndex < argumentTypes.Count; optionalArgumentIndex++)
if(!DoesImplicitConversionExist(argumentTypes[optionalArgumentIndex],optionalParameterType))
{
signatureMistmatch = true;
break;
}
if(signatureMistmatch)
break;
else
return true;
}
if(!DoesImplicitConversionExist(argumentTypes[argumentIndex],methodParameters[methodParameterIndex].TypeName))
break;
//If we have processed all arguments - we possibly found our match
if(argumentIndex == argumentTypes.Count-1)
{
//Is it the last method parameter as well?
if(methodParameterIndex == methodParameters.Length-1)
return true;
//No there are more method parameters.
//Are they optional?
if(methodParameters[methodParameterIndex+1].IsParameterArray)
return true; //Yes, rest of paramethers are optional - we found out match
else
break; //Otherwise - signatures do not match
}
}
return false;
}
private CodeAssembly codeAssembly;
private Hashtable caseSensitiveAssebmlyTypes = new Hashtable();
private Hashtable caseInsensitiveAssebmlyTypes = new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
}
}
|