// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.Diagnostics; using dnlib.Threading; using dnlib.Utils; namespace dnlib.DotNet { /// /// A list of all method parameters /// [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(ParameterList_CollectionDebugView))] public sealed class ParameterList : IList { readonly MethodDef method; readonly List parameters; readonly Parameter hiddenThisParameter; ParamDef hiddenThisParamDef; readonly Parameter returnParameter; int methodSigIndexBase; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif /// /// Gets the owner method /// public MethodDef Method => method; /// /// Gets the number of parameters, including a possible hidden 'this' parameter /// public int Count { get { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return parameters.Count; #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } } /// /// Gets the index of the first parameter that is present in the method signature. /// If this is a static method, the value is 0, else it's an instance method so the /// index is 1 since the first parameter is the hidden 'this' parameter. /// public int MethodSigIndexBase { get { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return methodSigIndexBase == 1 ? 1 : 0; #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } } /// /// Gets the N'th parameter /// /// The parameter index public Parameter this[int index] { get { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return parameters[index]; #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } set => throw new NotSupportedException(); } /// /// Gets the method return parameter /// public Parameter ReturnParameter { get { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return returnParameter; #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } } /// /// Constructor /// /// The method with all parameters /// 's declaring type public ParameterList(MethodDef method, TypeDef declaringType) { this.method = method; parameters = new List(); methodSigIndexBase = -1; hiddenThisParameter = new Parameter(this, 0, Parameter.HIDDEN_THIS_METHOD_SIG_INDEX); returnParameter = new Parameter(this, -1, Parameter.RETURN_TYPE_METHOD_SIG_INDEX); UpdateThisParameterType(declaringType); UpdateParameterTypes(); } /// /// Should be called when the method's declaring type has changed /// /// Method declaring type internal void UpdateThisParameterType(TypeDef methodDeclaringType) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (methodDeclaringType is null) hiddenThisParameter.Type = null; else { bool isValueType = methodDeclaringType.IsValueType; ClassOrValueTypeSig instSig; if (isValueType) instSig = new ValueTypeSig(methodDeclaringType); else instSig = new ClassSig(methodDeclaringType); TypeSig thisTypeSig; if (methodDeclaringType.HasGenericParameters) { int gpCount = methodDeclaringType.GenericParameters.Count; var genArgs = new List(gpCount); for (int i = 0; i < gpCount; i++) genArgs.Add(new GenericVar(i, methodDeclaringType)); thisTypeSig = new GenericInstSig(instSig, genArgs); } else thisTypeSig = instSig; hiddenThisParameter.Type = isValueType ? new ByRefSig(thisTypeSig) : thisTypeSig; } #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// /// Should be called when the method sig has changed /// public void UpdateParameterTypes() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif var sig = method.MethodSig; if (sig is null) { methodSigIndexBase = -1; parameters.Clear(); return; } if (UpdateThisParameter_NoLock(sig)) parameters.Clear(); returnParameter.Type = sig.RetType; ResizeParameters_NoLock(sig.Params.Count + methodSigIndexBase); if (methodSigIndexBase > 0) parameters[0] = hiddenThisParameter; for (int i = 0; i < sig.Params.Count; i++) parameters[i + methodSigIndexBase].Type = sig.Params[i]; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } bool UpdateThisParameter_NoLock(MethodSig methodSig) { int newIndex; if (methodSig is null) newIndex = -1; else newIndex = methodSig.ImplicitThis ? 1 : 0; if (methodSigIndexBase == newIndex) return false; methodSigIndexBase = newIndex; return true; } void ResizeParameters_NoLock(int length) { if (parameters.Count == length) return; if (parameters.Count < length) { for (int i = parameters.Count; i < length; i++) parameters.Add(new Parameter(this, i, i - methodSigIndexBase)); } else { while (parameters.Count > length) parameters.RemoveAt(parameters.Count - 1); } } internal ParamDef FindParamDef(Parameter param) { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return FindParamDef_NoLock(param); #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } ParamDef FindParamDef_NoLock(Parameter param) { int seq; if (param.IsReturnTypeParameter) seq = 0; else if (param.IsNormalMethodParameter) seq = param.MethodSigIndex + 1; else return hiddenThisParamDef; var paramDefs = method.ParamDefs; int count = paramDefs.Count; for (int i = 0; i < count; i++) { var paramDef = paramDefs[i]; if (paramDef is not null && paramDef.Sequence == seq) return paramDef; } return null; } internal void TypeUpdated(Parameter param) { var sig = method.MethodSig; if (sig is null) return; int index = param.MethodSigIndex; if (index == Parameter.RETURN_TYPE_METHOD_SIG_INDEX) sig.RetType = param.Type; else if (index >= 0) sig.Params[index] = param.Type; } internal void CreateParamDef(Parameter param) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif var paramDef = FindParamDef_NoLock(param); if (paramDef is not null) return; if (param.IsHiddenThisParameter) { hiddenThisParamDef = UpdateRowId_NoLock(new ParamDefUser(UTF8String.Empty, ushort.MaxValue, 0)); return; } int seq = param.IsReturnTypeParameter ? 0 : param.MethodSigIndex + 1; paramDef = UpdateRowId_NoLock(new ParamDefUser(UTF8String.Empty, (ushort)seq, 0)); method.ParamDefs.Add(paramDef); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } ParamDef UpdateRowId_NoLock(ParamDef pd) { var dt = method.DeclaringType; if (dt is null) return pd; var module = dt.Module; if (module is null) return pd; return module.UpdateRowId(pd); } /// public int IndexOf(Parameter item) { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return parameters.IndexOf(item); #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } void IList.Insert(int index, Parameter item) => throw new NotSupportedException(); void IList.RemoveAt(int index) => throw new NotSupportedException(); void ICollection.Add(Parameter item) => throw new NotSupportedException(); void ICollection.Clear() => throw new NotSupportedException(); bool ICollection.Contains(Parameter item) { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif return parameters.Contains(item); #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } void ICollection.CopyTo(Parameter[] array, int arrayIndex) { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif parameters.CopyTo(array, arrayIndex); #if THREAD_SAFE } finally { theLock.ExitReadLock(); } #endif } bool ICollection.IsReadOnly => true; bool ICollection.Remove(Parameter item) => throw new NotSupportedException(); /// /// Enumerator /// public struct Enumerator : IEnumerator { readonly ParameterList list; List.Enumerator listEnumerator; Parameter current; internal Enumerator(ParameterList list) { this.list = list; current = default; #if THREAD_SAFE list.theLock.EnterReadLock(); try { #endif listEnumerator = list.parameters.GetEnumerator(); #if THREAD_SAFE } finally { list.theLock.ExitReadLock(); } #endif } /// /// Gets the current value /// public Parameter Current => current; Parameter IEnumerator.Current => current; object System.Collections.IEnumerator.Current => current; /// /// Moves to the next element in the collection /// /// public bool MoveNext() { #if THREAD_SAFE list.theLock.EnterWriteLock(); try { #endif var res = listEnumerator.MoveNext(); current = listEnumerator.Current; return res; #if THREAD_SAFE } finally { list.theLock.ExitWriteLock(); } #endif } /// /// Disposes the enumerator /// public void Dispose() => listEnumerator.Dispose(); void System.Collections.IEnumerator.Reset() => throw new NotSupportedException(); } /// /// Gets the list enumerator /// /// public Enumerator GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } /// /// A method parameter /// public sealed class Parameter : IVariable { readonly ParameterList parameterList; TypeSig typeSig; readonly int paramIndex; readonly int methodSigIndex; /// /// The hidden 'this' parameter's /// public const int HIDDEN_THIS_METHOD_SIG_INDEX = -2; /// /// The return type parameter's /// public const int RETURN_TYPE_METHOD_SIG_INDEX = -1; /// /// Gets the parameter index. If the method has a hidden 'this' parameter, that parameter /// has index 0 and the remaining parameters in the method signature start from index 1. /// The method return parameter has index -1. /// public int Index => paramIndex; /// /// Gets the index of the parameter in the method signature. See also /// and /// public int MethodSigIndex => methodSigIndex; /// /// true if it's a normal visible method parameter, i.e., it's not the hidden /// 'this' parameter and it's not the method return type parameter. /// public bool IsNormalMethodParameter => methodSigIndex >= 0; /// /// true if it's the hidden 'this' parameter /// public bool IsHiddenThisParameter => methodSigIndex == HIDDEN_THIS_METHOD_SIG_INDEX; /// /// true if it's the method return type parameter /// public bool IsReturnTypeParameter => methodSigIndex == RETURN_TYPE_METHOD_SIG_INDEX; /// /// Gets the parameter type /// public TypeSig Type { get => typeSig; set { typeSig = value; if (parameterList is not null) parameterList.TypeUpdated(this); } } /// /// Gets the owner method /// public MethodDef Method => parameterList?.Method; /// /// Gets the or null if not present /// public ParamDef ParamDef => parameterList?.FindParamDef(this); /// /// true if it has a /// public bool HasParamDef => ParamDef is not null; /// /// Gets the name from . If is null, /// an empty string is returned. /// public string Name { get { var paramDef = ParamDef; return paramDef is null ? string.Empty : UTF8String.ToSystemStringOrEmpty(paramDef.Name); } set { var paramDef = ParamDef; if (paramDef is not null) paramDef.Name = value; } } /// /// Constructor /// /// Parameter index public Parameter(int paramIndex) { this.paramIndex = paramIndex; methodSigIndex = paramIndex; } /// /// Constructor /// /// Parameter index /// Parameter type public Parameter(int paramIndex, TypeSig type) { this.paramIndex = paramIndex; methodSigIndex = paramIndex; typeSig = type; } /// /// Constructor /// /// Parameter index (0 is hidden this param if it exists) /// Index in method signature public Parameter(int paramIndex, int methodSigIndex) { this.paramIndex = paramIndex; this.methodSigIndex = methodSigIndex; } /// /// Constructor /// /// Parameter index (0 is hidden this param if it exists) /// Index in method signature /// Parameter type public Parameter(int paramIndex, int methodSigIndex, TypeSig type) { this.paramIndex = paramIndex; this.methodSigIndex = methodSigIndex; typeSig = type; } internal Parameter(ParameterList parameterList, int paramIndex, int methodSigIndex) { this.parameterList = parameterList; this.paramIndex = paramIndex; this.methodSigIndex = methodSigIndex; } /// /// Creates a if it doesn't already exist /// public void CreateParamDef() { if (parameterList is not null) parameterList.CreateParamDef(this); } /// public override string ToString() { var name = Name; if (string.IsNullOrEmpty(name)) { if (IsReturnTypeParameter) return "RET_PARAM"; return $"A_{paramIndex}"; } return name; } } }