Click here to Skip to main content
15,886,689 members
Articles / Programming Languages / MSIL

Assembly Manipulation and C# / VB.NET Code Injection

Rate me:
Please Sign up or sign in to vote.
4.95/5 (200 votes)
26 Apr 2013MIT5 min read 922K   25K   486  
Reflexil is an assembly editor and runs as a plug-in for Reflector or JustDecompile. Reflexil is able to manipulate IL code and save the modified assemblies to disk. Reflexil also supports "on-the-fly" C#/VB.NET code injection.
// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision$</version>
// </file>

using System;
using System.Linq;
using System.Collections.Generic;

namespace ICSharpCode.SharpDevelop.Dom.CSharp
{
	/// <summary>
	/// Implements C# 3.0 overload resolution.
	/// </summary>
	public sealed class OverloadResolution
	{
		private OverloadResolution() {}
		
		public static IMethodOrProperty FindOverload(IList<IMethodOrProperty> list,
		                                             IReturnType[] arguments,
		                                             bool allowAdditionalArguments,
		                                             bool substituteInferredTypes,
		                                             out bool acceptableMatch)
		{
			OverloadResolution or = new OverloadResolution();
			or.candidates = list.Select(m => new Candidate(m)).ToList();
			or.arguments = arguments;
			or.allowAdditionalArguments = allowAdditionalArguments;
			
			if (or.candidates.Count == 0)
				throw new ArgumentException("at least one candidate is required");
			
			MemberLookupHelper.Log("OverloadResolution");
			MemberLookupHelper.Log("  arguments = ", arguments);
			
			or.ConstructExpandedForms();
			or.InferTypeArguments();
			or.CheckApplicability();
			
			Candidate result = or.FindBestCandidate();
			MemberLookupHelper.Log("Overload resolution finished. Winning candidate = " + result);
			acceptableMatch = result.Status == CandidateStatus.Success;
			if (substituteInferredTypes)
				return result.Method;
			else
				return result.OriginalMethod;
		}
		
		enum CandidateStatus
		{
			Success,
			WrongParameterCount,
			TypeInferenceFailed,
			NotApplicable
		}
		
		sealed class Candidate
		{
			public bool IsExpanded;
			public IMethodOrProperty Method;
			public IMethodOrProperty OriginalMethod;
			public CandidateStatus Status = CandidateStatus.Success;
			public int ApplicableArgumentCount;
			
			public IList<IParameter> Parameters {
				get { return Method.Parameters; }
			}
			
			public int TypeParameterCount {
				get {
					IMethod m = Method as IMethod;
					if (m != null)
						return m.TypeParameters.Count;
					else
						return 0;
				}
			}
			
			public Candidate(IMethodOrProperty method)
			{
				this.Method = method;
				this.OriginalMethod = method;
			}
			
			public override string ToString()
			{
				return "[Candidate: " + Method + ", Status=" + Status + "]";
			}
		}
		
		List<Candidate> candidates;
		IList<IReturnType> arguments;
		bool allowAdditionalArguments;
		
		/// <summary>
		/// For methods having a params-array as last parameter, expand the params array to
		/// n parameters of the element type and add those as new candidates.
		/// Mark candidates with the wrong parameter count as Status.WrongParameterCount.
		/// </summary>
		void ConstructExpandedForms()
		{
			LogStep("Step 1 (Construct expanded forms)");
			foreach (Candidate candidate in candidates.ToArray()) {
				if (candidate.Status == CandidateStatus.Success) {
					if (candidate.Parameters.Count > 0 && arguments.Count >= candidate.Parameters.Count - 1) {
						IParameter lastParameter = candidate.Parameters[candidate.Parameters.Count - 1];
						if (lastParameter.IsParams && lastParameter.ReturnType.IsArrayReturnType) {
							// try to construct an expanded form with the correct parameter count
							IReturnType elementType = lastParameter.ReturnType.CastToArrayReturnType().ArrayElementType;
							IMethodOrProperty expanded = (IMethodOrProperty)candidate.Method.CreateSpecializedMember();
							expanded.Parameters.RemoveAt(candidate.Parameters.Count - 1);
							int index = 0;
							while (expanded.Parameters.Count < arguments.Count) {
								expanded.Parameters.Add(new DefaultParameter(lastParameter.Name + (index++), elementType, lastParameter.Region));
							}
							candidates.Add(new Candidate(expanded) {
							               	IsExpanded = true,
							               	OriginalMethod = candidate.Method
							               });
						}
					}
					
					if (allowAdditionalArguments) {
						if (candidate.Parameters.Count < arguments.Count) {
							candidate.Status = CandidateStatus.WrongParameterCount;
						}
					} else {
						if (candidate.Parameters.Count != arguments.Count) {
							candidate.Status = CandidateStatus.WrongParameterCount;
						}
					}
				}
			}
		}
		
		/// <summary>
		/// Infer the type arguments for generic methods.
		/// </summary>
		void InferTypeArguments()
		{
			LogStep("Step 2 (Infer type arguments)");
			foreach (Candidate candidate in candidates) {
				IMethod method = candidate.Method as IMethod;
				if (method != null && method.TypeParameters.Count > 0
				    && candidate.Status == CandidateStatus.Success)
				{
					bool success;
					IReturnType[] typeArguments = TypeInference.InferTypeArguments(method, arguments, out success);
					if (!success) {
						candidate.Status = CandidateStatus.TypeInferenceFailed;
					}
					if (typeArguments != null) {
						// apply inferred type arguments
						method = (IMethod)method.CreateSpecializedMember();
						method.ReturnType = ConstructedReturnType.TranslateType(method.ReturnType, typeArguments, true);
						for (int i = 0; i < method.Parameters.Count; ++i) {
							method.Parameters[i].ReturnType = ConstructedReturnType.TranslateType(method.Parameters[i].ReturnType, typeArguments, true);
						}
						candidate.Method = method;
					}
				}
			}
		}
		
		void CheckApplicability()
		{
			LogStep("Step 3 (CheckApplicability)");
			foreach (Candidate candidate in candidates) {
				int c = Math.Min(arguments.Count, candidate.Parameters.Count);
				for (int i = 0; i < c; i++) {
					if (MemberLookupHelper.IsApplicable(arguments[i], candidate.Parameters[i], candidate.Method as IMethod))
						candidate.ApplicableArgumentCount++;
				}
				if (candidate.Status == CandidateStatus.Success && candidate.ApplicableArgumentCount < arguments.Count) {
					candidate.Status = CandidateStatus.NotApplicable;
				}
			}
		}
		
		Candidate FindBestCandidate()
		{
			LogStep("Step 4 (FindBestCandidate)");
			
			// Find a candidate that is better than all other candidates
			Candidate best = null;
			foreach (Candidate candidate in candidates) {
				if (candidate.Status == CandidateStatus.Success) {
					if (best == null || GetBetterFunctionMember(best, candidate) == 2) {
						best = candidate;
					}
				}
			}
			
			if (best != null)
				return best;
			
			// no successful candidate found:
			// find the candidate that is nearest to being applicable
			// first try only candidates with the correct parameter count
			foreach (Candidate candidate in candidates) {
				if (candidate.Status != CandidateStatus.WrongParameterCount) {
					if (best == null || candidate.ApplicableArgumentCount > best.ApplicableArgumentCount)
						best = candidate;
				}
			}
			if (best != null)
				return best;
			// if all candidates have the wrong parameter count, return the candidate
			// with the most applicable parameters.
			best = candidates[0];
			foreach (Candidate candidate in candidates) {
				if (candidate.ApplicableArgumentCount > best.ApplicableArgumentCount)
					best = candidate;
			}
			return best;
		}
		
		/// <summary>
		/// Gets which function member is better. (� 14.4.2.2)
		/// </summary>
		/// <returns>0 if neither method is better. 1 if c1 is better. 2 if c2 is better.</returns>
		int GetBetterFunctionMember(Candidate c1, Candidate c2)
		{
			int length = Math.Min(Math.Min(c1.Parameters.Count, c2.Parameters.Count), arguments.Count);
			bool foundBetterParamIn1 = false;
			bool foundBetterParamIn2 = false;
			for (int i = 0; i < length; i++) {
				if (arguments[i] == null)
					continue;
				int res = MemberLookupHelper.GetBetterConversion(arguments[i], c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType);
				if (res == 1) foundBetterParamIn1 = true;
				if (res == 2) foundBetterParamIn2 = true;
			}
			if (foundBetterParamIn1 && !foundBetterParamIn2)
				return 1;
			if (foundBetterParamIn2 && !foundBetterParamIn1)
				return 2;
			if (foundBetterParamIn1 && foundBetterParamIn2)
				return 0; // ambigous
			// If none conversion is better than any other, it is possible that the
			// expanded parameter lists are the same:
			for (int i = 0; i < length; i++) {
				if (!object.Equals(c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType)) {
					// if expanded parameters are not the same, neither function member is better
					return 0;
				}
			}
			
			// the expanded parameters are the same, apply the tie-breaking rules from the spec:
			
			// if one method is generic and the other non-generic, the non-generic is better
			bool m1IsGeneric = c1.TypeParameterCount > 0;
			bool m2IsGeneric = c2.TypeParameterCount > 0;
			if (m1IsGeneric && !m2IsGeneric) return 2;
			if (m2IsGeneric && !m1IsGeneric) return 1;
			
			// for params parameters: non-expanded calls are better
			if (c1.IsExpanded && !c2.IsExpanded) return 2;
			if (c2.IsExpanded && !c1.IsExpanded) return 1;
			
			// if the number of parameters is different, the one with more parameters is better
			// this occurs when only when both methods are expanded
			if (c1.OriginalMethod.Parameters.Count > c2.OriginalMethod.Parameters.Count) return 1;
			if (c2.OriginalMethod.Parameters.Count > c1.OriginalMethod.Parameters.Count) return 2;
			
			IReturnType[] m1ParamTypes = new IReturnType[c1.Parameters.Count];
			IReturnType[] m2ParamTypes = new IReturnType[c2.Parameters.Count];
			for (int i = 0; i < m1ParamTypes.Length; i++) {
				m1ParamTypes[i] = c1.Parameters[i].ReturnType;
				m2ParamTypes[i] = c2.Parameters[i].ReturnType;
			}
			return GetMoreSpecific(m1ParamTypes, m2ParamTypes);
		}
		
		
		/// <summary>
		/// Gets which return type list is more specific.
		/// � 14.4.2.2: types with generic arguments are less specific than types with fixed arguments
		/// </summary>
		/// <returns>0 if both are equally specific, 1 if <paramref name="r"/> is more specific,
		/// 2 if <paramref name="s"/> is more specific.</returns>
		static int GetMoreSpecific(IList<IReturnType> r, IList<IReturnType> s)
		{
			bool foundMoreSpecificParamIn1 = false;
			bool foundMoreSpecificParamIn2 = false;
			int length = Math.Min(r.Count, s.Count);
			for (int i = 0; i < length; i++) {
				int res = GetMoreSpecific(r[i], s[i]);
				if (res == 1) foundMoreSpecificParamIn1 = true;
				if (res == 2) foundMoreSpecificParamIn2 = true;
			}
			if (foundMoreSpecificParamIn1 && !foundMoreSpecificParamIn2)
				return 1;
			if (foundMoreSpecificParamIn2 && !foundMoreSpecificParamIn1)
				return 2;
			return 0;
		}
		
		static int GetMoreSpecific(IReturnType r, IReturnType s)
		{
			if (r == null && s == null) return 0;
			if (r == null) return 2;
			if (s == null) return 1;
			if (r.IsGenericReturnType && !(s.IsGenericReturnType))
				return 2;
			if (s.IsGenericReturnType && !(r.IsGenericReturnType))
				return 1;
			if (r.IsArrayReturnType && s.IsArrayReturnType)
				return GetMoreSpecific(r.CastToArrayReturnType().ArrayElementType, s.CastToArrayReturnType().ArrayElementType);
			if (r.IsConstructedReturnType && s.IsConstructedReturnType)
				return GetMoreSpecific(r.CastToConstructedReturnType().TypeArguments, s.CastToConstructedReturnType().TypeArguments);
			return 0;
		}
		
		[System.Diagnostics.ConditionalAttribute("DEBUG")]
		void LogStep(string title)
		{
			MemberLookupHelper.Log("  candidates = ", candidates);
			MemberLookupHelper.Log("Overload resolution (" + title + ")");
		}
		
		[System.Diagnostics.ConditionalAttribute("DEBUG")]
		void Log(string text)
		{
			MemberLookupHelper.Log(text);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior) Microsoft
United States United States
Sebastien Lebreton is a Software Engineer at Microsoft.

He is particularly interested in optimization, reverse engineering and distributed objects technologies.

Comments and Discussions