summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs')
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs528
1 files changed, 528 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs b/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
new file mode 100644
index 0000000000..addf68e75d
--- /dev/null
+++ b/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
@@ -0,0 +1,528 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+using System.Runtime.CompilerServices;
+using MdToken = System.Reflection.MetadataToken;
+
+namespace System.Reflection
+{
+ [Serializable]
+ internal unsafe sealed class RuntimeParameterInfo : ParameterInfo, ISerializable
+ {
+ #region Static Members
+ internal unsafe static ParameterInfo[] GetParameters(IRuntimeMethodInfo method, MemberInfo member, Signature sig)
+ {
+ Debug.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
+
+ ParameterInfo dummy;
+ return GetParameters(method, member, sig, out dummy, false);
+ }
+
+ internal unsafe static ParameterInfo GetReturnParameter(IRuntimeMethodInfo method, MemberInfo member, Signature sig)
+ {
+ Debug.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
+
+ ParameterInfo returnParameter;
+ GetParameters(method, member, sig, out returnParameter, true);
+ return returnParameter;
+ }
+
+ internal unsafe static ParameterInfo[] GetParameters(
+ IRuntimeMethodInfo methodHandle, MemberInfo member, Signature sig, out ParameterInfo returnParameter, bool fetchReturnParameter)
+ {
+ returnParameter = null;
+ int sigArgCount = sig.Arguments.Length;
+ ParameterInfo[] args = fetchReturnParameter ? null : new ParameterInfo[sigArgCount];
+
+ int tkMethodDef = RuntimeMethodHandle.GetMethodDef(methodHandle);
+ int cParamDefs = 0;
+
+ // Not all methods have tokens. Arrays, pointers and byRef types do not have tokens as they
+ // are generated on the fly by the runtime.
+ if (!MdToken.IsNullToken(tkMethodDef))
+ {
+ MetadataImport scope = RuntimeTypeHandle.GetMetadataImport(RuntimeMethodHandle.GetDeclaringType(methodHandle));
+
+ MetadataEnumResult tkParamDefs;
+ scope.EnumParams(tkMethodDef, out tkParamDefs);
+
+ cParamDefs = tkParamDefs.Length;
+
+ // Not all parameters have tokens. Parameters may have no token
+ // if they have no name and no attributes.
+ if (cParamDefs > sigArgCount + 1 /* return type */)
+ throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);
+
+ for (int i = 0; i < cParamDefs; i++)
+ {
+ #region Populate ParameterInfos
+ ParameterAttributes attr;
+ int position, tkParamDef = tkParamDefs[i];
+
+ scope.GetParamDefProps(tkParamDef, out position, out attr);
+
+ position--;
+
+ if (fetchReturnParameter == true && position == -1)
+ {
+ // more than one return parameter?
+ if (returnParameter != null)
+ throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);
+
+ returnParameter = new RuntimeParameterInfo(sig, scope, tkParamDef, position, attr, member);
+ }
+ else if (fetchReturnParameter == false && position >= 0)
+ {
+ // position beyong sigArgCount?
+ if (position >= sigArgCount)
+ throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);
+
+ args[position] = new RuntimeParameterInfo(sig, scope, tkParamDef, position, attr, member);
+ }
+ #endregion
+ }
+ }
+
+ // Fill in empty ParameterInfos for those without tokens
+ if (fetchReturnParameter)
+ {
+ if (returnParameter == null)
+ {
+ returnParameter = new RuntimeParameterInfo(sig, MetadataImport.EmptyImport, 0, -1, (ParameterAttributes)0, member);
+ }
+ }
+ else
+ {
+ if (cParamDefs < args.Length + 1)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ if (args[i] != null)
+ continue;
+
+ args[i] = new RuntimeParameterInfo(sig, MetadataImport.EmptyImport, 0, i, (ParameterAttributes)0, member);
+ }
+ }
+ }
+
+ return args;
+ }
+ #endregion
+
+ #region Private Statics
+ private static readonly Type s_DecimalConstantAttributeType = typeof(DecimalConstantAttribute);
+ private static readonly Type s_CustomConstantAttributeType = typeof(CustomConstantAttribute);
+ #endregion
+
+ #region Private Data Members
+ // These are new in Whidbey, so we cannot serialize them directly or we break backwards compatibility.
+ [NonSerialized]
+ private int m_tkParamDef;
+ [NonSerialized]
+ private MetadataImport m_scope;
+ [NonSerialized]
+ private Signature m_signature;
+ [NonSerialized]
+ private volatile bool m_nameIsCached = false;
+ [NonSerialized]
+ private readonly bool m_noMetadata = false;
+ [NonSerialized]
+ private bool m_noDefaultValue = false;
+ [NonSerialized]
+ private MethodBase m_originalMember = null;
+ #endregion
+
+ #region Internal Properties
+ internal MethodBase DefiningMethod
+ {
+ get
+ {
+ MethodBase result = m_originalMember != null ? m_originalMember : MemberImpl as MethodBase;
+ Debug.Assert(result != null);
+ return result;
+ }
+ }
+ #endregion
+
+ #region Internal Methods
+ internal void SetName(string name)
+ {
+ NameImpl = name;
+ }
+
+ internal void SetAttributes(ParameterAttributes attributes)
+ {
+ AttrsImpl = attributes;
+ }
+ #endregion
+
+ #region VTS magic to serialize/deserialized to/from pre-Whidbey endpoints.
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // We could be serializing for consumption by a pre-Whidbey
+ // endpoint. Therefore we set up all the serialized fields to look
+ // just like a v1.0/v1.1 instance.
+
+ // Need to set the type to ParameterInfo so that pre-Whidbey and Whidbey code
+ // can deserialize this. This is also why we cannot simply use [OnSerializing].
+ info.SetType(typeof(ParameterInfo));
+
+ // Use the properties intead of the fields in case the fields haven't been et
+ // _importer, bExtraConstChecked, and m_cachedData don't need to be set
+
+ // Now set the legacy fields that the current implementation doesn't
+ // use any more. Note that _importer is a raw pointer that should
+ // never have been serialized in V1. We set it to zero here; if the
+ // deserializer uses it (by calling GetCustomAttributes() on this
+ // instance) they'll AV, but at least it will be a well defined
+ // exception and not a random AV.
+
+ info.AddValue("AttrsImpl", Attributes);
+ info.AddValue("ClassImpl", ParameterType);
+ info.AddValue("DefaultValueImpl", DefaultValue);
+ info.AddValue("MemberImpl", Member);
+ info.AddValue("NameImpl", Name);
+ info.AddValue("PositionImpl", Position);
+ info.AddValue("_token", m_tkParamDef);
+ }
+ #endregion
+
+ #region Constructor
+ // used by RuntimePropertyInfo
+ internal RuntimeParameterInfo(RuntimeParameterInfo accessor, RuntimePropertyInfo property)
+ : this(accessor, (MemberInfo)property)
+ {
+ m_signature = property.Signature;
+ }
+
+ private RuntimeParameterInfo(RuntimeParameterInfo accessor, MemberInfo member)
+ {
+ // Change ownership
+ MemberImpl = member;
+
+ // The original owner should always be a method, because this method is only used to
+ // change the owner from a method to a property.
+ m_originalMember = accessor.MemberImpl as MethodBase;
+ Debug.Assert(m_originalMember != null);
+
+ // Populate all the caches -- we inherit this behavior from RTM
+ NameImpl = accessor.Name;
+ m_nameIsCached = true;
+ ClassImpl = accessor.ParameterType;
+ PositionImpl = accessor.Position;
+ AttrsImpl = accessor.Attributes;
+
+ // Strictly speeking, property's don't contain paramter tokens
+ // However we need this to make ca's work... oh well...
+ m_tkParamDef = MdToken.IsNullToken(accessor.MetadataToken) ? (int)MetadataTokenType.ParamDef : accessor.MetadataToken;
+ m_scope = accessor.m_scope;
+ }
+
+ private RuntimeParameterInfo(
+ Signature signature, MetadataImport scope, int tkParamDef,
+ int position, ParameterAttributes attributes, MemberInfo member)
+ {
+ Contract.Requires(member != null);
+ Debug.Assert(MdToken.IsNullToken(tkParamDef) == scope.Equals(MetadataImport.EmptyImport));
+ Debug.Assert(MdToken.IsNullToken(tkParamDef) || MdToken.IsTokenOfType(tkParamDef, MetadataTokenType.ParamDef));
+
+ PositionImpl = position;
+ MemberImpl = member;
+ m_signature = signature;
+ m_tkParamDef = MdToken.IsNullToken(tkParamDef) ? (int)MetadataTokenType.ParamDef : tkParamDef;
+ m_scope = scope;
+ AttrsImpl = attributes;
+
+ ClassImpl = null;
+ NameImpl = null;
+ }
+
+ // ctor for no metadata MethodInfo in the DynamicMethod and RuntimeMethodInfo cases
+ internal RuntimeParameterInfo(MethodInfo owner, String name, Type parameterType, int position)
+ {
+ MemberImpl = owner;
+ NameImpl = name;
+ m_nameIsCached = true;
+ m_noMetadata = true;
+ ClassImpl = parameterType;
+ PositionImpl = position;
+ AttrsImpl = ParameterAttributes.None;
+ m_tkParamDef = (int)MetadataTokenType.ParamDef;
+ m_scope = MetadataImport.EmptyImport;
+ }
+ #endregion
+
+ #region Public Methods
+ public override Type ParameterType
+ {
+ get
+ {
+ // only instance of ParameterInfo has ClassImpl, all its subclasses don't
+ if (ClassImpl == null)
+ {
+ RuntimeType parameterType;
+ if (PositionImpl == -1)
+ parameterType = m_signature.ReturnType;
+ else
+ parameterType = m_signature.Arguments[PositionImpl];
+
+ Debug.Assert(parameterType != null);
+ // different thread could only write ClassImpl to the same value, so a race condition is not a problem here
+ ClassImpl = parameterType;
+ }
+
+ return ClassImpl;
+ }
+ }
+
+ public override String Name
+ {
+ get
+ {
+ if (!m_nameIsCached)
+ {
+ if (!MdToken.IsNullToken(m_tkParamDef))
+ {
+ string name;
+ name = m_scope.GetName(m_tkParamDef).ToString();
+ NameImpl = name;
+ }
+
+ // other threads could only write it to true, so a race condition is OK
+ // this field is volatile, so the write ordering is guaranteed
+ m_nameIsCached = true;
+ }
+
+ // name may be null
+ return NameImpl;
+ }
+ }
+
+ public override bool HasDefaultValue
+ {
+ get
+ {
+ if (m_noMetadata || m_noDefaultValue)
+ return false;
+
+ object defaultValue = GetDefaultValueInternal(false);
+
+ return (defaultValue != DBNull.Value);
+ }
+ }
+
+ public override Object DefaultValue { get { return GetDefaultValue(false); } }
+ public override Object RawDefaultValue { get { return GetDefaultValue(true); } }
+
+ private Object GetDefaultValue(bool raw)
+ {
+ // OLD COMMENT (Is this even true?)
+ // Cannot cache because default value could be non-agile user defined enumeration.
+ // OLD COMMENT ends
+ if (m_noMetadata)
+ return null;
+
+ // for dynamic method we pretend to have cached the value so we do not go to metadata
+ object defaultValue = GetDefaultValueInternal(raw);
+
+ if (defaultValue == DBNull.Value)
+ {
+ #region Handle case if no default value was found
+ if (IsOptional)
+ {
+ // If the argument is marked as optional then the default value is Missing.Value.
+ defaultValue = Type.Missing;
+ }
+ #endregion
+ }
+
+ return defaultValue;
+ }
+
+ // returns DBNull.Value if the parameter doesn't have a default value
+ private Object GetDefaultValueInternal(bool raw)
+ {
+ Debug.Assert(!m_noMetadata);
+
+ if (m_noDefaultValue)
+ return DBNull.Value;
+
+ object defaultValue = null;
+
+ // Why check the parameter type only for DateTime and only for the ctor arguments?
+ // No check on the parameter type is done for named args and for Decimal.
+
+ // We should move this after MdToken.IsNullToken(m_tkParamDef) and combine it
+ // with the other custom attribute logic. But will that be a breaking change?
+ // For a DateTime parameter on which both an md constant and a ca constant are set,
+ // which one should win?
+ if (ParameterType == typeof(DateTime))
+ {
+ if (raw)
+ {
+ CustomAttributeTypedArgument value =
+ CustomAttributeData.Filter(
+ CustomAttributeData.GetCustomAttributes(this), typeof(DateTimeConstantAttribute), 0);
+
+ if (value.ArgumentType != null)
+ return new DateTime((long)value.Value);
+ }
+ else
+ {
+ object[] dt = GetCustomAttributes(typeof(DateTimeConstantAttribute), false);
+ if (dt != null && dt.Length != 0)
+ return ((DateTimeConstantAttribute)dt[0]).Value;
+ }
+ }
+
+ #region Look for a default value in metadata
+ if (!MdToken.IsNullToken(m_tkParamDef))
+ {
+ // This will return DBNull.Value if no constant value is defined on m_tkParamDef in the metadata.
+ defaultValue = MdConstant.GetValue(m_scope, m_tkParamDef, ParameterType.GetTypeHandleInternal(), raw);
+ }
+ #endregion
+
+ if (defaultValue == DBNull.Value)
+ {
+ #region Look for a default value in the custom attributes
+ if (raw)
+ {
+ foreach (CustomAttributeData attr in CustomAttributeData.GetCustomAttributes(this))
+ {
+ Type attrType = attr.Constructor.DeclaringType;
+
+ if (attrType == typeof(DateTimeConstantAttribute))
+ {
+ defaultValue = DateTimeConstantAttribute.GetRawDateTimeConstant(attr);
+ }
+ else if (attrType == typeof(DecimalConstantAttribute))
+ {
+ defaultValue = DecimalConstantAttribute.GetRawDecimalConstant(attr);
+ }
+ else if (attrType.IsSubclassOf(s_CustomConstantAttributeType))
+ {
+ defaultValue = CustomConstantAttribute.GetRawConstant(attr);
+ }
+ }
+ }
+ else
+ {
+ Object[] CustomAttrs = GetCustomAttributes(s_CustomConstantAttributeType, false);
+ if (CustomAttrs.Length != 0)
+ {
+ defaultValue = ((CustomConstantAttribute)CustomAttrs[0]).Value;
+ }
+ else
+ {
+ CustomAttrs = GetCustomAttributes(s_DecimalConstantAttributeType, false);
+ if (CustomAttrs.Length != 0)
+ {
+ defaultValue = ((DecimalConstantAttribute)CustomAttrs[0]).Value;
+ }
+ }
+ }
+ #endregion
+ }
+
+ if (defaultValue == DBNull.Value)
+ m_noDefaultValue = true;
+
+ return defaultValue;
+ }
+
+ internal RuntimeModule GetRuntimeModule()
+ {
+ RuntimeMethodInfo method = Member as RuntimeMethodInfo;
+ RuntimeConstructorInfo constructor = Member as RuntimeConstructorInfo;
+ RuntimePropertyInfo property = Member as RuntimePropertyInfo;
+
+ if (method != null)
+ return method.GetRuntimeModule();
+ else if (constructor != null)
+ return constructor.GetRuntimeModule();
+ else if (property != null)
+ return property.GetRuntimeModule();
+ else
+ return null;
+ }
+
+ public override int MetadataToken
+ {
+ get
+ {
+ return m_tkParamDef;
+ }
+ }
+
+ public override Type[] GetRequiredCustomModifiers()
+ {
+ return m_signature.GetCustomModifiers(PositionImpl + 1, true);
+ }
+
+ public override Type[] GetOptionalCustomModifiers()
+ {
+ return m_signature.GetCustomModifiers(PositionImpl + 1, false);
+ }
+
+ #endregion
+
+ #region ICustomAttributeProvider
+ public override Object[] GetCustomAttributes(bool inherit)
+ {
+ if (MdToken.IsNullToken(m_tkParamDef))
+ return Array.Empty<Object>();
+
+ return CustomAttribute.GetCustomAttributes(this, typeof(object) as RuntimeType);
+ }
+
+ public override Object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ if (attributeType == null)
+ throw new ArgumentNullException(nameof(attributeType));
+ Contract.EndContractBlock();
+
+ if (MdToken.IsNullToken(m_tkParamDef))
+ return Array.Empty<Object>();
+
+ RuntimeType attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType;
+
+ if (attributeRuntimeType == null)
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
+
+ return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType);
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ if (attributeType == null)
+ throw new ArgumentNullException(nameof(attributeType));
+ Contract.EndContractBlock();
+
+ if (MdToken.IsNullToken(m_tkParamDef))
+ return false;
+
+ RuntimeType attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType;
+
+ if (attributeRuntimeType == null)
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
+
+ return CustomAttribute.IsDefined(this, attributeRuntimeType);
+ }
+
+ public override IList<CustomAttributeData> GetCustomAttributesData()
+ {
+ return CustomAttributeData.GetCustomAttributesInternal(this);
+ }
+ #endregion
+ }
+}