internal unsafe static object[] GetCustomAttributes(
Module decoratedModule, int decoratedMetadataToken, int pcaCount,
RuntimeType attributeFilterType, bool mustBeInheritable, IList derivedAttributes)
{
if (decoratedModule.Assembly.ReflectionOnly)
throw new InvalidOperationException(Environment.GetResourceString("Arg_ReflectionOnlyCA"));
MetadataImport scope = decoratedModule.MetadataImport;
CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken);
bool useObjectArray = (attributeFilterType == null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters);
Type arrayType = useObjectArray ? typeof(object) : attributeFilterType;
if (attributeFilterType == null && car.Length == 0)
return Array.CreateInstance(arrayType, 0) as object[];
object[] attributes = Array.CreateInstance(arrayType, car.Length) as object[];
int cAttributes = 0;
// Custom attribute security checks are done with respect to the assembly *decorated* with the
// custom attribute as opposed to the *caller of GetCustomAttributes*.
// Since this assembly might not be on the stack and the attribute ctor or property setters we're about to invoke may
// make security demands, we push a frame on the stack as a proxy for the decorated assembly (this frame will be picked
// up an interpreted by the security stackwalker).
// Once we push the frame it will be automatically popped in the event of an exception, so no need to use CERs or the
// like.
SecurityContextFrame frame = new SecurityContextFrame();
frame.Push(decoratedModule.Assembly);
// Optimization for the case where attributes decorate entities in the same assembly in which case
// we can cache the successful APTCA check between the decorated and the declared assembly.
Assembly lastAptcaOkAssembly = null;
for (int i = 0; i < car.Length; i++)
{
object attribute = null;
CustomAttributeRecord caRecord = car[i];
RuntimeMethodHandle ctor = new RuntimeMethodHandle();
RuntimeType attributeType = null;
bool ctorHasParameters, isVarArg;
int cNamedArgs = 0;
IntPtr blobStart = caRecord.blob.Signature;
IntPtr blobEnd = (IntPtr)((byte*)blobStart + caRecord.blob.Length);
if (!FilterCustomAttributeRecord(caRecord, scope, ref lastAptcaOkAssembly,
decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable,
attributes, derivedAttributes,
out attributeType, out ctor, out ctorHasParameters, out isVarArg))
continue;
if (!ctor.IsNullHandle())
{
// Linktime demand checks
// decoratedMetadataToken needed as it may be "transparent" in which case we do a full stack walk
ctor.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
}
else
{
}
// Leverage RuntimeConstructorInfo standard .ctor verfication
RuntimeConstructorInfo.CheckCanCreateInstance(attributeType, isVarArg);
// Create custom attribute object
if (ctorHasParameters)
{
attribute = CreateCaObject(decoratedModule, ctor, ref blobStart, blobEnd, out cNamedArgs);
}
else
{
attribute = attributeType.TypeHandle.CreateCaInstance(ctor);
if (Marshal.ReadInt16(blobStart) != 1)
throw new CustomAttributeFormatException();
blobStart = (IntPtr)((byte*)blobStart + 2); // skip 0x0001 prefix
cNamedArgs = Marshal.ReadInt16(blobStart);
blobStart = (IntPtr)((byte*)blobStart + 2); // skip namedArgs count
}
for (int j = 0; j < cNamedArgs; j++)
{
#region // Initialize named properties and fields
string name;
bool isProperty;
Type type;
object value;
IntPtr blobItr = caRecord.blob.Signature;
GetPropertyOrFieldData(decoratedModule, ref blobStart, blobEnd, out name, out isProperty, out type, out value);
try
{
if (isProperty)
{
#region // Initialize property
if (type == null && value != null)
type = (value.GetType() == typeof(RuntimeType)) ? typeof(Type) : value.GetType();
RuntimePropertyInfo property = null;
if (type == null)
property = attributeType.GetProperty(name) as RuntimePropertyInfo;
else
property = attributeType.GetProperty(name, type, Type.EmptyTypes) as RuntimePropertyInfo;
RuntimeMethodInfo setMethod = property.GetSetMethod(true) as RuntimeMethodInfo;
// Public properties may have non-public setter methods
if (!setMethod.IsPublic)
continue;
setMethod.MethodHandle.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
setMethod.Invoke(attribute, BindingFlags.Default, null, new object[] { value }, null, true);
#endregion
}
else
{
RtFieldInfo field = attributeType.GetField(name) as RtFieldInfo;
field.InternalSetValue(attribute, value, BindingFlags.Default, Type.DefaultBinder, null, false);
}
}
catch (Exception e)
{
throw new CustomAttributeFormatException(
String.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(
isProperty ? "RFLCT.InvalidPropFail" : "RFLCT.InvalidFieldFail"), name), e);
}
#endregion
}
if (!blobStart.Equals(blobEnd))
throw new CustomAttributeFormatException();
attributes[cAttributes++] = attribute;
}
// The frame will be popped automatically if we take an exception any time after we pushed it. So no need of a catch or
// finally or CERs here.
frame.Pop();
if (cAttributes == car.Length && pcaCount == 0)
return attributes;
if (cAttributes == 0)
Array.CreateInstance(arrayType, 0);
object[] result = Array.CreateInstance(arrayType, cAttributes + pcaCount) as object[];
Array.Copy(attributes, 0, result, 0, cAttributes);
return result;
}