// 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;
}
}
}