|
// ==++==
//
// Copyright(c) Microsoft Corporation. All rights reserved.
//
// ==--==
// <OWNER>Microsoft</OWNER>
//
namespace System.Reflection
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
#if FEATURE_REMOTING
using System.Runtime.Remoting.Metadata;
#endif //FEATURE_REMOTING
using System.Security.Permissions;
using System.Threading;
using MdToken = System.Reflection.MetadataToken;
[Serializable]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(_ParameterInfo))]
[System.Runtime.InteropServices.ComVisible(true)]
public class ParameterInfo : _ParameterInfo, ICustomAttributeProvider, IObjectReference
{
#region Legacy Protected Members
protected String NameImpl;
protected Type ClassImpl;
protected int PositionImpl;
protected ParameterAttributes AttrsImpl;
protected Object DefaultValueImpl; // cannot cache this as it may be non agile user defined enum
protected MemberInfo MemberImpl;
#endregion
#region Legacy Private Members
// These are here only for backwards compatibility -- they are not set
// until this instance is serialized, so don't rely on their values from
// arbitrary code.
#pragma warning disable 169
[OptionalField]
private IntPtr _importer;
[OptionalField]
private int _token;
[OptionalField]
private bool bExtraConstChecked;
#pragma warning restore 169
#endregion
#region Constructor
protected ParameterInfo()
{
}
#endregion
#region Internal Members
// this is an internal api for DynamicMethod. A better solution is to change the relationship
// between ParameterInfo and ParameterBuilder so that a ParameterBuilder can be seen as a writer
// api over a ParameterInfo. However that is a possible breaking change so it needs to go through some process first
internal void SetName(String name)
{
NameImpl = name;
}
internal void SetAttributes(ParameterAttributes attributes)
{
AttrsImpl = attributes;
}
#endregion
#region Public Methods
public virtual Type ParameterType
{
get
{
return ClassImpl;
}
}
public virtual String Name
{
get
{
return NameImpl;
}
}
public virtual bool HasDefaultValue { get { throw new NotImplementedException(); } }
public virtual Object DefaultValue { get { throw new NotImplementedException(); } }
public virtual Object RawDefaultValue { get { throw new NotImplementedException(); } }
public virtual int Position { get { return PositionImpl; } }
public virtual ParameterAttributes Attributes { get { return AttrsImpl; } }
public virtual MemberInfo Member {
get {
Contract.Ensures(Contract.Result<MemberInfo>() != null);
return MemberImpl;
}
}
public bool IsIn { get { return((Attributes & ParameterAttributes.In) != 0); } }
public bool IsOut { get { return((Attributes & ParameterAttributes.Out) != 0); } }
#if FEATURE_USE_LCID
public bool IsLcid { get { return((Attributes & ParameterAttributes.Lcid) != 0); } }
#endif
public bool IsRetval { get { return((Attributes & ParameterAttributes.Retval) != 0); } }
public bool IsOptional { get { return((Attributes & ParameterAttributes.Optional) != 0); } }
public virtual int MetadataToken
{
get
{
// This API was made virtual in V4. Code compiled against V2 might use
// "call" rather than "callvirt" to call it.
// This makes sure those code still works.
RuntimeParameterInfo rtParam = this as RuntimeParameterInfo;
if (rtParam != null)
return rtParam.MetadataToken;
// return a null token
return (int)MetadataTokenType.ParamDef;
}
}
public virtual Type[] GetRequiredCustomModifiers()
{
return EmptyArray<Type>.Value;
}
public virtual Type[] GetOptionalCustomModifiers()
{
return EmptyArray<Type>.Value;
}
#endregion
#region Object Overrides
public override String ToString()
{
return ParameterType.FormatTypeName() + " " + Name;
}
#endregion
public virtual IEnumerable<CustomAttributeData> CustomAttributes
{
get
{
return GetCustomAttributesData();
}
}
#region ICustomAttributeProvider
public virtual Object[] GetCustomAttributes(bool inherit)
{
return EmptyArray<Object>.Value;
}
public virtual Object[] GetCustomAttributes(Type attributeType, bool inherit)
{
if (attributeType == null)
throw new ArgumentNullException("attributeType");
Contract.EndContractBlock();
return EmptyArray<Object>.Value;
}
public virtual bool IsDefined(Type attributeType, bool inherit)
{
if (attributeType == null)
throw new ArgumentNullException("attributeType");
Contract.EndContractBlock();
return false;
}
public virtual IList<CustomAttributeData> GetCustomAttributesData()
{
throw new NotImplementedException();
}
#endregion
#region _ParameterInfo implementation
#if !FEATURE_CORECLR
void _ParameterInfo.GetTypeInfoCount(out uint pcTInfo)
{
throw new NotImplementedException();
}
void _ParameterInfo.GetTypeInfo(uint iTInfo, uint lcid, IntPtr ppTInfo)
{
throw new NotImplementedException();
}
void _ParameterInfo.GetIDsOfNames([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
{
throw new NotImplementedException();
}
void _ParameterInfo.Invoke(uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
{
throw new NotImplementedException();
}
#endif
#endregion
#region IObjectReference
// In V4 RuntimeParameterInfo is introduced.
// To support deserializing ParameterInfo instances serialized in earlier versions
// we need to implement IObjectReference.
[System.Security.SecurityCritical]
public object GetRealObject(StreamingContext context)
{
Contract.Ensures(Contract.Result<Object>() != null);
// Once all the serializable fields have come in we can set up the real
// instance based on just two of them (MemberImpl and PositionImpl).
if (MemberImpl == null)
throw new SerializationException(Environment.GetResourceString(ResId.Serialization_InsufficientState));
ParameterInfo[] args = null;
switch (MemberImpl.MemberType)
{
case MemberTypes.Constructor:
case MemberTypes.Method:
if (PositionImpl == -1)
{
if (MemberImpl.MemberType == MemberTypes.Method)
return ((MethodInfo)MemberImpl).ReturnParameter;
else
throw new SerializationException(Environment.GetResourceString(ResId.Serialization_BadParameterInfo));
}
else
{
args = ((MethodBase)MemberImpl).GetParametersNoCopy();
if (args != null && PositionImpl < args.Length)
return args[PositionImpl];
else
throw new SerializationException(Environment.GetResourceString(ResId.Serialization_BadParameterInfo));
}
case MemberTypes.Property:
args = ((RuntimePropertyInfo)MemberImpl).GetIndexParametersNoCopy();
if (args != null && PositionImpl > -1 && PositionImpl < args.Length)
return args[PositionImpl];
else
throw new SerializationException(Environment.GetResourceString(ResId.Serialization_BadParameterInfo));
default:
throw new SerializationException(Environment.GetResourceString(ResId.Serialization_NoParameterInfo));
}
}
#endregion
}
[Serializable]
internal unsafe sealed class RuntimeParameterInfo : ParameterInfo, ISerializable
{
#region Static Members
[System.Security.SecurityCritical] // auto-generated
internal unsafe static ParameterInfo[] GetParameters(IRuntimeMethodInfo method, MemberInfo member, Signature sig)
{
Contract.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
ParameterInfo dummy;
return GetParameters(method, member, sig, out dummy, false);
}
[System.Security.SecurityCritical] // auto-generated
internal unsafe static ParameterInfo GetReturnParameter(IRuntimeMethodInfo method, MemberInfo member, Signature sig)
{
Contract.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
ParameterInfo returnParameter;
GetParameters(method, member, sig, out returnParameter, true);
return returnParameter;
}
[System.Security.SecurityCritical] // auto-generated
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(Environment.GetResourceString("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(Environment.GetResourceString("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(Environment.GetResourceString("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;
Contract.Assert(result != null);
return result;
}
}
#endregion
#region VTS magic to serialize/deserialized to/from pre-Whidbey endpoints.
[System.Security.SecurityCritical]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new ArgumentNullException("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;
Contract.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);
Contract.Assert(MdToken.IsNullToken(tkParamDef) == scope.Equals(MetadataImport.EmptyImport));
Contract.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];
Contract.Assert(parameterType != null);
// different thread could only write ClassImpl to the same value, so ---- is not a problem here
ClassImpl = parameterType;
}
return ClassImpl;
}
}
public override String Name
{
[System.Security.SecuritySafeCritical] // auto-generated
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 ---- 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 FEATURE_LEGACYNETCF
if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
defaultValue = null;
else
#endif
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
[System.Security.SecuritySafeCritical]
private Object GetDefaultValueInternal(bool raw)
{
Contract.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 EmptyArray<Object>.Value;
return CustomAttribute.GetCustomAttributes(this, typeof(object) as RuntimeType);
}
public override Object[] GetCustomAttributes(Type attributeType, bool inherit)
{
if (attributeType == null)
throw new ArgumentNullException("attributeType");
Contract.EndContractBlock();
if (MdToken.IsNullToken(m_tkParamDef))
return EmptyArray<Object>.Value;
RuntimeType attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType;
if (attributeRuntimeType == null)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "attributeType");
return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType);
}
[System.Security.SecuritySafeCritical] // auto-generated
public override bool IsDefined(Type attributeType, bool inherit)
{
if (attributeType == null)
throw new ArgumentNullException("attributeType");
Contract.EndContractBlock();
if (MdToken.IsNullToken(m_tkParamDef))
return false;
RuntimeType attributeRuntimeType = attributeType.UnderlyingSystemType as RuntimeType;
if (attributeRuntimeType == null)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "attributeType");
return CustomAttribute.IsDefined(this, attributeRuntimeType);
}
public override IList<CustomAttributeData> GetCustomAttributesData()
{
return CustomAttributeData.GetCustomAttributesInternal(this);
}
#endregion
#if FEATURE_REMOTING
#region Remoting Cache
private RemotingParameterCachedData m_cachedData;
internal RemotingParameterCachedData RemotingCache
{
get
{
// This grabs an internal copy of m_cachedData and uses
// that instead of looking at m_cachedData directly because
// the cache may get cleared asynchronously. This prevents
// us from having to take a lock.
RemotingParameterCachedData cache = m_cachedData;
if (cache == null)
{
cache = new RemotingParameterCachedData(this);
RemotingParameterCachedData ret = Interlocked.CompareExchange(ref m_cachedData, cache, null);
if (ret != null)
cache = ret;
}
return cache;
}
}
#endregion
#endif //FEATURE_REMOTING
}
}
|