/** * \file * Custom attributes. * * Author: * Paolo Molaro (lupus@ximian.com) * * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Copyright 2011 Rodrigo Kumpera * Copyright 2016 Microsoft * * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include "mono/metadata/assembly.h" #include "mono/metadata/gc-internals.h" #include "mono/metadata/mono-endian.h" #include "mono/metadata/object-internals.h" #include "mono/metadata/custom-attrs-internals.h" #include "mono/metadata/sre-internals.h" #include "mono/metadata/reflection-internals.h" #include "mono/metadata/tabledefs.h" #include "mono/metadata/tokentype.h" #include "mono/metadata/verify-internals.h" #include "mono/utils/checked-build.h" #define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) #define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) #if SIZEOF_VOID_P == 4 #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) #else #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) #endif #define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) #define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) static gboolean type_is_reference (MonoType *type); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_typed_argument, "System.Reflection", "CustomAttributeTypedArgument"); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_named_argument, "System.Reflection", "CustomAttributeNamedArgument"); static MonoCustomAttrInfo* mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs); static gboolean bcheck_blob (const char *ptr, int bump, const char *endp, MonoError *error); static gboolean decode_blob_value_checked (const char *ptr, const char *endp, guint32 *size_out, const char **retp, MonoError *error); /* * LOCKING: Acquires the loader lock. */ static MonoCustomAttrInfo* lookup_custom_attr (MonoImage *image, gpointer member) { MONO_REQ_GC_NEUTRAL_MODE; MonoCustomAttrInfo* res; res = (MonoCustomAttrInfo *)mono_image_property_lookup (image, member, MONO_PROP_DYNAMIC_CATTR); if (!res) return NULL; res = (MonoCustomAttrInfo *)g_memdup (res, MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * res->num_attrs); res->cached = 0; return res; } static gboolean custom_attr_visible (MonoImage *image, MonoReflectionCustomAttr *cattr) { MONO_REQ_GC_UNSAFE_MODE; /* FIXME: Need to do more checks */ if (cattr->ctor->method && (cattr->ctor->method->klass->image != image)) { int visibility = mono_class_get_flags (cattr->ctor->method->klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK; if ((visibility != TYPE_ATTRIBUTE_PUBLIC) && (visibility != TYPE_ATTRIBUTE_NESTED_PUBLIC)) return FALSE; } return TRUE; } static gboolean type_is_reference (MonoType *type) { switch (type->type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: case MONO_TYPE_U: case MONO_TYPE_I: case MONO_TYPE_U1: case MONO_TYPE_I1: case MONO_TYPE_U2: case MONO_TYPE_I2: case MONO_TYPE_U4: case MONO_TYPE_I4: case MONO_TYPE_U8: case MONO_TYPE_I8: case MONO_TYPE_R8: case MONO_TYPE_R4: case MONO_TYPE_VALUETYPE: return FALSE; default: return TRUE; } } static void free_param_data (MonoMethodSignature *sig, void **params) { int i; for (i = 0; i < sig->param_count; ++i) { if (!type_is_reference (sig->params [i])) g_free (params [i]); } } /* * Find the field index in the metadata FieldDef table. */ static guint32 find_field_index (MonoClass *klass, MonoClassField *field) { int i; int fcount = mono_class_get_field_count (klass); for (i = 0; i < fcount; ++i) { if (field == &klass->fields [i]) return mono_class_get_first_field_idx (klass) + 1 + i; } return 0; } /* * Find the property index in the metadata Property table. */ static guint32 find_property_index (MonoClass *klass, MonoProperty *property) { int i; MonoClassPropertyInfo *info = mono_class_get_property_info (klass); for (i = 0; i < info->count; ++i) { if (property == &info->properties [i]) return info->first + 1 + i; } return 0; } /* * Find the event index in the metadata Event table. */ static guint32 find_event_index (MonoClass *klass, MonoEvent *event) { int i; MonoClassEventInfo *info = mono_class_get_event_info (klass); for (i = 0; i < info->count; ++i) { if (event == &info->events [i]) return info->first + 1 + i; } return 0; } /* * Load the type with name @n on behalf of image @image. On failure sets @error and returns NULL. * The @is_enum flag only affects the error message that's displayed on failure. */ static MonoType* cattr_type_from_name (char *n, MonoImage *image, gboolean is_enum, MonoError *error) { MonoError inner_error; MonoType *t = mono_reflection_type_from_name_checked (n, image, &inner_error); if (!t) { mono_error_set_type_load_name (error, g_strdup(n), NULL, "Could not load %s %s while decoding custom attribute: %s", is_enum ? "enum type": "type", n, mono_error_get_message (&inner_error)); mono_error_cleanup (&inner_error); return NULL; } return t; } static MonoClass* load_cattr_enum_type (MonoImage *image, const char *p, const char *boundp, const char **end, MonoError *error) { char *n; MonoType *t; guint32 slen; error_init (error); if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) return NULL; if (boundp && slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) return NULL; n = (char *)g_memdup (p, slen + 1); n [slen] = 0; t = cattr_type_from_name (n, image, TRUE, error); g_free (n); return_val_if_nok (error, NULL); p += slen; *end = p; return mono_class_from_mono_type (t); } static void* load_cattr_value (MonoImage *image, MonoType *t, const char *p, const char *boundp, const char **end, MonoError *error) { int type = t->type; guint32 slen; MonoClass *tklass = t->data.klass; g_assert (boundp); error_init (error); handle_enum: switch (type) { case MONO_TYPE_U1: case MONO_TYPE_I1: case MONO_TYPE_BOOLEAN: { MonoBoolean *bval = (MonoBoolean *)g_malloc (sizeof (MonoBoolean)); if (!bcheck_blob (p, 0, boundp, error)) return NULL; *bval = *p; *end = p + 1; return bval; } case MONO_TYPE_CHAR: case MONO_TYPE_U2: case MONO_TYPE_I2: { guint16 *val = (guint16 *)g_malloc (sizeof (guint16)); if (!bcheck_blob (p, 1, boundp, error)) return NULL; *val = read16 (p); *end = p + 2; return val; } #if SIZEOF_VOID_P == 4 case MONO_TYPE_U: case MONO_TYPE_I: #endif case MONO_TYPE_R4: case MONO_TYPE_U4: case MONO_TYPE_I4: { guint32 *val = (guint32 *)g_malloc (sizeof (guint32)); if (!bcheck_blob (p, 3, boundp, error)) return NULL; *val = read32 (p); *end = p + 4; return val; } #if SIZEOF_VOID_P == 8 case MONO_TYPE_U: /* error out instead? this should probably not happen */ case MONO_TYPE_I: #endif case MONO_TYPE_U8: case MONO_TYPE_I8: { guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); if (!bcheck_blob (p, 7, boundp, error)) return NULL; *val = read64 (p); *end = p + 8; return val; } case MONO_TYPE_R8: { double *val = (double *)g_malloc (sizeof (double)); if (!bcheck_blob (p, 7, boundp, error)) return NULL; readr8 (p, val); *end = p + 8; return val; } case MONO_TYPE_VALUETYPE: if (t->data.klass->enumtype) { type = mono_class_enum_basetype (t->data.klass)->type; goto handle_enum; } else { MonoClass *k = t->data.klass; if (mono_is_corlib_image (k->image) && strcmp (k->name_space, "System") == 0 && strcmp (k->name, "DateTime") == 0){ guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); if (!bcheck_blob (p, 7, boundp, error)) return NULL; *val = read64 (p); *end = p + 8; return val; } } g_error ("generic valutype %s not handled in custom attr value decoding", t->data.klass->name); break; case MONO_TYPE_STRING: if (!bcheck_blob (p, 0, boundp, error)) return NULL; if (*p == (char)0xFF) { *end = p + 1; return NULL; } if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) return NULL; if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) return NULL; *end = p + slen; return mono_string_new_len_checked (mono_domain_get (), p, slen, error); case MONO_TYPE_CLASS: { MonoReflectionType *rt; char *n; MonoType *t; if (!bcheck_blob (p, 0, boundp, error)) return NULL; if (*p == (char)0xFF) { *end = p + 1; return NULL; } handle_type: if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) return NULL; if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) return NULL; n = (char *)g_memdup (p, slen + 1); n [slen] = 0; t = cattr_type_from_name (n, image, FALSE, error); g_free (n); return_val_if_nok (error, NULL); *end = p + slen; rt = mono_type_get_object_checked (mono_domain_get (), t, error); if (!mono_error_ok (error)) return NULL; return rt; } case MONO_TYPE_OBJECT: { if (!bcheck_blob (p, 0, boundp, error)) return NULL; char subt = *p++; MonoObject *obj; MonoClass *subc = NULL; void *val; if (subt == 0x50) { goto handle_type; } else if (subt == 0x0E) { type = MONO_TYPE_STRING; goto handle_enum; } else if (subt == 0x1D) { MonoType simple_type = {{0}}; if (!bcheck_blob (p, 0, boundp, error)) return NULL; int etype = *p; p ++; type = MONO_TYPE_SZARRAY; if (etype == 0x50) { tklass = mono_defaults.systemtype_class; } else if (etype == 0x55) { tklass = load_cattr_enum_type (image, p, boundp, &p, error); if (!is_ok (error)) return NULL; } else { if (etype == 0x51) /* See Partition II, Appendix B3 */ etype = MONO_TYPE_OBJECT; simple_type.type = (MonoTypeEnum)etype; tklass = mono_class_from_mono_type (&simple_type); } goto handle_enum; } else if (subt == 0x55) { char *n; MonoType *t; if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) return NULL; if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) return NULL; n = (char *)g_memdup (p, slen + 1); n [slen] = 0; t = cattr_type_from_name (n, image, FALSE, error); g_free (n); return_val_if_nok (error, NULL); p += slen; subc = mono_class_from_mono_type (t); } else if (subt >= MONO_TYPE_BOOLEAN && subt <= MONO_TYPE_R8) { MonoType simple_type = {{0}}; simple_type.type = (MonoTypeEnum)subt; subc = mono_class_from_mono_type (&simple_type); } else { g_error ("Unknown type 0x%02x for object type encoding in custom attr", subt); } val = load_cattr_value (image, &subc->byval_arg, p, boundp, end, error); obj = NULL; if (is_ok (error)) { obj = mono_object_new_checked (mono_domain_get (), subc, error); g_assert (!subc->has_references); if (is_ok (error)) mono_gc_memmove_atomic ((char*)obj + sizeof (MonoObject), val, mono_class_value_size (subc, NULL)); } g_free (val); return obj; } case MONO_TYPE_SZARRAY: { MonoArray *arr; guint32 i, alen, basetype; if (!bcheck_blob (p, 3, boundp, error)) return NULL; alen = read32 (p); p += 4; if (alen == 0xffffffff) { *end = p; return NULL; } arr = mono_array_new_checked (mono_domain_get(), tklass, alen, error); return_val_if_nok (error, NULL); basetype = tklass->byval_arg.type; if (basetype == MONO_TYPE_VALUETYPE && tklass->enumtype) basetype = mono_class_enum_basetype (tklass)->type; switch (basetype) { case MONO_TYPE_U1: case MONO_TYPE_I1: case MONO_TYPE_BOOLEAN: for (i = 0; i < alen; i++) { if (!bcheck_blob (p, 0, boundp, error)) return NULL; MonoBoolean val = *p++; mono_array_set (arr, MonoBoolean, i, val); } break; case MONO_TYPE_CHAR: case MONO_TYPE_U2: case MONO_TYPE_I2: for (i = 0; i < alen; i++) { if (!bcheck_blob (p, 1, boundp, error)) return NULL; guint16 val = read16 (p); mono_array_set (arr, guint16, i, val); p += 2; } break; case MONO_TYPE_R4: case MONO_TYPE_U4: case MONO_TYPE_I4: for (i = 0; i < alen; i++) { if (!bcheck_blob (p, 3, boundp, error)) return NULL; guint32 val = read32 (p); mono_array_set (arr, guint32, i, val); p += 4; } break; case MONO_TYPE_R8: for (i = 0; i < alen; i++) { if (!bcheck_blob (p, 7, boundp, error)) return NULL; double val; readr8 (p, &val); mono_array_set (arr, double, i, val); p += 8; } break; case MONO_TYPE_U8: case MONO_TYPE_I8: for (i = 0; i < alen; i++) { if (!bcheck_blob (p, 7, boundp, error)) return NULL; guint64 val = read64 (p); mono_array_set (arr, guint64, i, val); p += 8; } break; case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: for (i = 0; i < alen; i++) { MonoObject *item = (MonoObject *)load_cattr_value (image, &tklass->byval_arg, p, boundp, &p, error); if (!is_ok (error)) return NULL; mono_array_setref (arr, i, item); } break; default: g_error ("Type 0x%02x not handled in custom attr array decoding", basetype); } *end=p; return arr; } default: g_error ("Type 0x%02x not handled in custom attr value decoding", type); } return NULL; } static MonoObject* load_cattr_value_boxed (MonoDomain *domain, MonoImage *image, MonoType *t, const char* p, const char *boundp, const char** end, MonoError *error) { error_init (error); gboolean is_ref = type_is_reference (t); void *val = load_cattr_value (image, t, p, boundp, end, error); if (!is_ok (error)) { if (is_ref) g_free (val); return NULL; } if (is_ref) return (MonoObject*)val; MonoObject *boxed = mono_value_box_checked (domain, mono_class_from_mono_type (t), val, error); g_free (val); return boxed; } static MonoObject* create_cattr_typed_arg (MonoType *t, MonoObject *val, MonoError *error) { static MonoMethod *ctor; MonoObject *retval; void *params [2], *unboxed; error_init (error); if (!ctor) ctor = mono_class_get_method_from_name (mono_class_get_custom_attribute_typed_argument_class (), ".ctor", 2); params [0] = mono_type_get_object_checked (mono_domain_get (), t, error); return_val_if_nok (error, NULL); params [1] = val; retval = mono_object_new_checked (mono_domain_get (), mono_class_get_custom_attribute_typed_argument_class (), error); return_val_if_nok (error, NULL); unboxed = mono_object_unbox (retval); mono_runtime_invoke_checked (ctor, unboxed, params, error); return_val_if_nok (error, NULL); return retval; } static MonoObject* create_cattr_named_arg (void *minfo, MonoObject *typedarg, MonoError *error) { static MonoMethod *ctor; MonoObject *retval; void *unboxed, *params [2]; error_init (error); if (!ctor) ctor = mono_class_get_method_from_name (mono_class_get_custom_attribute_named_argument_class (), ".ctor", 2); params [0] = minfo; params [1] = typedarg; retval = mono_object_new_checked (mono_domain_get (), mono_class_get_custom_attribute_named_argument_class (), error); return_val_if_nok (error, NULL); unboxed = mono_object_unbox (retval); mono_runtime_invoke_checked (ctor, unboxed, params, error); return_val_if_nok (error, NULL); return retval; } static MonoCustomAttrInfo* mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs) { return mono_custom_attrs_from_builders (alloc_img, image, MONO_HANDLE_RAW (cattrs)); /* FIXME use coop handles for mono_custom_attrs_from_builders */ } MonoCustomAttrInfo* mono_custom_attrs_from_builders (MonoImage *alloc_img, MonoImage *image, MonoArray *cattrs) { MONO_REQ_GC_UNSAFE_MODE; int i, index, count, not_visible; MonoCustomAttrInfo *ainfo; MonoReflectionCustomAttr *cattr; if (!cattrs) return NULL; /* FIXME: check in assembly the Run flag is set */ count = mono_array_length (cattrs); /* Skip nonpublic attributes since MS.NET seems to do the same */ /* FIXME: This needs to be done more globally */ not_visible = 0; for (i = 0; i < count; ++i) { cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i); if (!custom_attr_visible (image, cattr)) not_visible ++; } int num_attrs = count - not_visible; ainfo = (MonoCustomAttrInfo *)mono_image_g_malloc0 (alloc_img, MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * num_attrs); ainfo->image = image; ainfo->num_attrs = num_attrs; ainfo->cached = alloc_img != NULL; index = 0; for (i = 0; i < count; ++i) { cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i); if (custom_attr_visible (image, cattr)) { unsigned char *saved = (unsigned char *)mono_image_alloc (image, mono_array_length (cattr->data)); memcpy (saved, mono_array_addr (cattr->data, char, 0), mono_array_length (cattr->data)); ainfo->attrs [index].ctor = cattr->ctor->method; g_assert (cattr->ctor->method); ainfo->attrs [index].data = saved; ainfo->attrs [index].data_size = mono_array_length (cattr->data); index ++; } } g_assert (index == num_attrs && count == num_attrs + not_visible); return ainfo; } static void set_custom_attr_fmt_error (MonoError *error) { error_init (error); mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); } /** * bcheck_blob: * \param ptr a pointer into a blob * \param bump how far we plan on reading past \p ptr. * \param endp upper bound for \p ptr - one past the last valid value for \p ptr. * \param error set on error * * Check that ptr+bump is below endp. Returns TRUE on success, or FALSE on * failure and sets \p error. */ static gboolean bcheck_blob (const char *ptr, int bump, const char *endp, MonoError *error) { error_init (error); if (ADDP_IS_GREATER_OR_OVF (ptr, bump, endp - 1)) { set_custom_attr_fmt_error (error); return FALSE; } else return TRUE; } /** * decode_blob_size_checked: * \param ptr a pointer into a blob * \param endp upper bound for \p ptr - one pas the last valid value for \p ptr * \param size_out on success set to the decoded size * \param retp on success set to the next byte after the encoded size * \param error set on error * * Decode an encoded size value which takes 1, 2, or 4 bytes and set \p * size_out to the decoded size and \p retp to the next byte after the encoded * size. Returns TRUE on success, or FALASE on failure and sets \p error. */ static gboolean decode_blob_size_checked (const char *ptr, const char *endp, guint32 *size_out, const char **retp, MonoError *error) { error_init (error); if (endp && !bcheck_blob (ptr, 0, endp, error)) goto leave; if ((*ptr & 0x80) != 0) { if ((*ptr & 0x40) == 0 && !bcheck_blob (ptr, 1, endp, error)) goto leave; else if (!bcheck_blob (ptr, 3, endp, error)) goto leave; } *size_out = mono_metadata_decode_blob_size (ptr, retp); leave: return is_ok (error); } /** * decode_blob_value_checked: * \param ptr a pointer into a blob * \param endp upper bound for \p ptr - one pas the last valid value for \p ptr * \param value_out on success set to the decoded value * \param retp on success set to the next byte after the encoded size * \param error set on error * * Decode an encoded uint32 value which takes 1, 2, or 4 bytes and set \p * value_out to the decoded value and \p retp to the next byte after the * encoded value. Returns TRUE on success, or FALASE on failure and sets \p * error. */ static gboolean decode_blob_value_checked (const char *ptr, const char *endp, guint32 *value_out, const char **retp, MonoError *error) { /* This similar to decode_blob_size_checked, above but delegates to * mono_metadata_decode_value which is semantically different. */ error_init (error); if (!bcheck_blob (ptr, 0, endp, error)) goto leave; if ((*ptr & 0x80) != 0) { if ((*ptr & 0x40) == 0 && !bcheck_blob (ptr, 1, endp, error)) goto leave; else if (!bcheck_blob (ptr, 3, endp, error)) goto leave; } *value_out = mono_metadata_decode_value (ptr, retp); leave: return is_ok (error); } static MonoObject* create_custom_attr (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoError *error) { const char *p = (const char*)data; const char *data_end = (const char*)data + len; const char *named; guint32 i, j, num_named; MonoObject *attr; void *params_buf [32]; void **params = NULL; MonoMethodSignature *sig; error_init (error); mono_class_init (method->klass); if (!mono_verifier_verify_cattr_content (image, method, data, len, NULL)) { set_custom_attr_fmt_error (error); return NULL; } if (len == 0) { attr = mono_object_new_checked (mono_domain_get (), method->klass, error); if (!mono_error_ok (error)) return NULL; mono_runtime_invoke_checked (method, attr, NULL, error); if (!mono_error_ok (error)) return NULL; return attr; } if (len < 2 || read16 (p) != 0x0001) /* Prolog */ return NULL; /*g_print ("got attr %s\n", method->klass->name);*/ sig = mono_method_signature (method); if (sig->param_count < 32) { params = params_buf; memset (params, 0, sizeof (void*) * sig->param_count); } else { /* Allocate using GC so it gets GC tracking */ params = (void **)mono_gc_alloc_fixed (sig->param_count * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_REFLECTION, "custom attribute parameters"); } /* skip prolog */ p += 2; for (i = 0; i < mono_method_signature (method)->param_count; ++i) { params [i] = load_cattr_value (image, mono_method_signature (method)->params [i], p, data_end, &p, error); if (!is_ok (error)) goto fail; } named = p; attr = mono_object_new_checked (mono_domain_get (), method->klass, error); if (!mono_error_ok (error)) goto fail; MonoObject *exc = NULL; mono_runtime_try_invoke (method, attr, params, &exc, error); if (!mono_error_ok (error)) goto fail; if (exc) { mono_error_set_exception_instance (error, (MonoException*)exc); goto fail; } if (named + 1 < data_end) { num_named = read16 (named); named += 2; } else { /* CoreCLR allows p == data + len */ if (named == data_end) num_named = 0; else { set_custom_attr_fmt_error (error); goto fail; } } for (j = 0; j < num_named; j++) { guint32 name_len; char *name, named_type, data_type; if (!bcheck_blob (named, 1, data_end, error)) goto fail; named_type = *named++; data_type = *named++; /* type of data */ if (data_type == MONO_TYPE_SZARRAY) { if (!bcheck_blob (named, 0, data_end, error)) goto fail; data_type = *named++; } if (data_type == MONO_TYPE_ENUM) { guint32 type_len; char *type_name; if (!decode_blob_size_checked (named, data_end, &type_len, &named, error)) goto fail; if (type_len > 0 && !bcheck_blob (named, type_len - 1, data_end, error)) goto fail; type_name = (char *)g_malloc (type_len + 1); memcpy (type_name, named, type_len); type_name [type_len] = 0; named += type_len; /* FIXME: lookup the type and check type consistency */ g_free (type_name); } if (!decode_blob_size_checked (named, data_end, &name_len, &named, error)) goto fail; if (name_len > 0 && !bcheck_blob (named, name_len - 1, data_end, error)) goto fail; name = (char *)g_malloc (name_len + 1); memcpy (name, named, name_len); name [name_len] = 0; named += name_len; if (named_type == 0x53) { MonoClassField *field; void *val; /* how this fail is a blackbox */ field = mono_class_get_field_from_name (mono_object_class (attr), name); if (!field) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find a field with name %s", name); g_free (name); goto fail; } val = load_cattr_value (image, field->type, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); if (!type_is_reference (field->type)) g_free (val); goto fail; } mono_field_set_value (attr, field, val); if (!type_is_reference (field->type)) g_free (val); } else if (named_type == 0x54) { MonoProperty *prop; void *pparams [1]; MonoType *prop_type; prop = mono_class_get_property_from_name (mono_object_class (attr), name); if (!prop) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find a property with name %s", name); g_free (name); goto fail; } if (!prop->set) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find the setter for %s", name); g_free (name); goto fail; } /* can we have more that 1 arg in a custom attr named property? */ prop_type = prop->get? mono_method_signature (prop->get)->ret : mono_method_signature (prop->set)->params [mono_method_signature (prop->set)->param_count - 1]; pparams [0] = load_cattr_value (image, prop_type, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); if (!type_is_reference (prop_type)) g_free (pparams [0]); goto fail; } mono_property_set_value_checked (prop, attr, pparams, error); if (!type_is_reference (prop_type)) g_free (pparams [0]); if (!is_ok (error)) { g_free (name); goto fail; } } g_free (name); } free_param_data (method->signature, params); if (params != params_buf) mono_gc_free_fixed (params); return attr; fail: free_param_data (method->signature, params); if (params != params_buf) mono_gc_free_fixed (params); return NULL; } /* * mono_reflection_create_custom_attr_data_args: * * Create an array of typed and named arguments from the cattr blob given by DATA. * TYPED_ARGS and NAMED_ARGS will contain the objects representing the arguments, * NAMED_ARG_INFO will contain information about the named arguments. */ void mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoArray **typed_args, MonoArray **named_args, CattrNamedArg **named_arg_info, MonoError *error) { MonoArray *typedargs, *namedargs; MonoClass *attrklass; MonoDomain *domain; const char *p = (const char*)data; const char *data_end = p + len; const char *named; guint32 i, j, num_named; CattrNamedArg *arginfo = NULL; *typed_args = NULL; *named_args = NULL; *named_arg_info = NULL; error_init (error); if (!mono_verifier_verify_cattr_content (image, method, data, len, NULL)) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); return; } mono_class_init (method->klass); domain = mono_domain_get (); if (len < 2 || read16 (p) != 0x0001) /* Prolog */ return; /* skip prolog */ p += 2; /* Parse each argument corresponding to the signature's parameters from * the blob and store in typedargs. */ typedargs = mono_array_new_checked (domain, mono_get_object_class (), mono_method_signature (method)->param_count, error); return_if_nok (error); for (i = 0; i < mono_method_signature (method)->param_count; ++i) { MonoObject *obj; obj = load_cattr_value_boxed (domain, image, mono_method_signature (method)->params [i], p, data_end, &p, error); return_if_nok (error); mono_array_setref (typedargs, i, obj); } named = p; /* Parse mandatory count of named arguments (could be zero) */ if (!bcheck_blob (named, 1, data_end, error)) return; num_named = read16 (named); namedargs = mono_array_new_checked (domain, mono_get_object_class (), num_named, error); return_if_nok (error); named += 2; attrklass = method->klass; arginfo = g_new0 (CattrNamedArg, num_named); *named_arg_info = arginfo; /* Parse each named arg, and add to arginfo. Each named argument could * be a field name or a property name followed by a value. */ for (j = 0; j < num_named; j++) { guint32 name_len; char *name, named_type, data_type; if (!bcheck_blob (named, 1, data_end, error)) return; named_type = *named++; /* field or property? */ data_type = *named++; /* type of data */ if (data_type == MONO_TYPE_SZARRAY) { if (!bcheck_blob (named, 0, data_end, error)) return; data_type = *named++; } if (data_type == MONO_TYPE_ENUM) { guint32 type_len; char *type_name; if (!decode_blob_size_checked (named, data_end, &type_len, &named, error)) return; if (ADDP_IS_GREATER_OR_OVF ((const guchar*)named, type_len, data + len)) goto fail; type_name = (char *)g_malloc (type_len + 1); memcpy (type_name, named, type_len); type_name [type_len] = 0; named += type_len; /* FIXME: lookup the type and check type consistency */ g_free (type_name); } /* named argument name: length, then name */ if (!decode_blob_size_checked(named, data_end, &name_len, &named, error)) return; if (ADDP_IS_GREATER_OR_OVF ((const guchar*)named, name_len, data + len)) goto fail; name = (char *)g_malloc (name_len + 1); memcpy (name, named, name_len); name [name_len] = 0; named += name_len; if (named_type == 0x53) { /* Named arg is a field. */ MonoObject *obj; MonoClassField *field = mono_class_get_field_from_name (attrklass, name); if (!field) { g_free (name); goto fail; } arginfo [j].type = field->type; arginfo [j].field = field; obj = load_cattr_value_boxed (domain, image, field->type, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); return; } mono_array_setref (namedargs, j, obj); } else if (named_type == 0x54) { /* Named arg is a property */ MonoObject *obj; MonoType *prop_type; MonoProperty *prop = mono_class_get_property_from_name (attrklass, name); if (!prop || !prop->set) { g_free (name); goto fail; } prop_type = prop->get? mono_method_signature (prop->get)->ret : mono_method_signature (prop->set)->params [mono_method_signature (prop->set)->param_count - 1]; arginfo [j].type = prop_type; arginfo [j].prop = prop; obj = load_cattr_value_boxed (domain, image, prop_type, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); return; } mono_array_setref (namedargs, j, obj); } g_free (name); } *typed_args = typedargs; *named_args = namedargs; return; fail: mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); g_free (arginfo); *named_arg_info = NULL; } static gboolean reflection_resolve_custom_attribute_data (MonoReflectionMethod *ref_method, MonoReflectionAssembly *assembly, gpointer data, guint32 len, MonoArray **ctor_args, MonoArray **named_args, MonoError *error) { MonoDomain *domain; MonoArray *typedargs, *namedargs; MonoImage *image; MonoMethod *method; CattrNamedArg *arginfo = NULL; int i; error_init (error); *ctor_args = NULL; *named_args = NULL; if (len == 0) return TRUE; image = assembly->assembly->image; method = ref_method->method; domain = mono_object_domain (ref_method); if (!mono_class_init (method->klass)) { mono_error_set_for_class_failure (error, method->klass); goto leave; } mono_reflection_create_custom_attr_data_args (image, method, (const guchar *)data, len, &typedargs, &namedargs, &arginfo, error); if (!is_ok (error)) goto leave; if (!typedargs || !namedargs) goto leave; for (i = 0; i < mono_method_signature (method)->param_count; ++i) { MonoObject *obj = mono_array_get (typedargs, MonoObject*, i); MonoObject *typedarg; typedarg = create_cattr_typed_arg (mono_method_signature (method)->params [i], obj, error); if (!is_ok (error)) goto leave; mono_array_setref (typedargs, i, typedarg); } for (i = 0; i < mono_array_length (namedargs); ++i) { MonoObject *obj = mono_array_get (namedargs, MonoObject*, i); MonoObject *typedarg, *namedarg, *minfo; if (arginfo [i].prop) { minfo = (MonoObject*)mono_property_get_object_checked (domain, NULL, arginfo [i].prop, error); if (!minfo) goto leave; } else { minfo = (MonoObject*)mono_field_get_object_checked (domain, NULL, arginfo [i].field, error); if (!is_ok (error)) goto leave; } typedarg = create_cattr_typed_arg (arginfo [i].type, obj, error); if (!is_ok (error)) goto leave; namedarg = create_cattr_named_arg (minfo, typedarg, error); if (!is_ok (error)) goto leave; mono_array_setref (namedargs, i, namedarg); } *ctor_args = typedargs; *named_args = namedargs; leave: g_free (arginfo); return mono_error_ok (error); } void ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal (MonoReflectionMethod *ref_method, MonoReflectionAssembly *assembly, gpointer data, guint32 len, MonoArray **ctor_args, MonoArray **named_args) { MonoError error; (void) reflection_resolve_custom_attribute_data (ref_method, assembly, data, len, ctor_args, named_args, &error); mono_error_set_pending_exception (&error); } static MonoObjectHandle create_custom_attr_data_handle (MonoImage *image, MonoCustomAttrEntry *cattr, MonoError *error) { static MonoMethod *ctor; MonoDomain *domain; void *params [4]; error_init (error); g_assert (image->assembly); if (!ctor) ctor = mono_class_get_method_from_name (mono_defaults.customattribute_data_class, ".ctor", 4); domain = mono_domain_get (); MonoObjectHandle attr = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (domain, mono_defaults.customattribute_data_class, error)); if (!is_ok (error)) goto fail; MonoReflectionMethod *ctor_obj = mono_method_get_object_checked (domain, cattr->ctor, NULL, error); if (!is_ok (error)) goto fail; MonoReflectionAssemblyHandle assm = mono_assembly_get_object_handle (domain, image->assembly, error); if (!is_ok (error)) goto fail; params [0] = ctor_obj; params [1] = MONO_HANDLE_RAW (assm); params [2] = (gpointer)&cattr->data; params [3] = &cattr->data_size; mono_runtime_invoke_checked (ctor, MONO_HANDLE_RAW (attr), params, error); return attr; fail: return MONO_HANDLE_NEW (MonoObject, NULL); } static MonoObject * create_custom_attr_data (MonoImage *image, MonoCustomAttrEntry *cattr, MonoError *error) { HANDLE_FUNCTION_ENTER (); MonoObjectHandle obj = create_custom_attr_data_handle (image, cattr, error); HANDLE_FUNCTION_RETURN_OBJ (obj); } static MonoArray* mono_custom_attrs_construct_by_type (MonoCustomAttrInfo *cinfo, MonoClass *attr_klass, MonoError *error) { MonoArray *result; MonoObject *attr; int i, n; error_init (error); for (i = 0; i < cinfo->num_attrs; ++i) { MonoCustomAttrEntry *centry = &cinfo->attrs[i]; if (!centry->ctor) { /* The cattr type is not finished yet */ /* We should include the type name but cinfo doesn't contain it */ mono_error_set_type_load_name (error, NULL, NULL, "Custom attribute constructor is null because the custom attribute type is not finished yet."); return NULL; } } n = 0; if (attr_klass) { for (i = 0; i < cinfo->num_attrs; ++i) { MonoMethod *ctor = cinfo->attrs[i].ctor; g_assert (ctor); if (mono_class_is_assignable_from (attr_klass, ctor->klass)) n++; } } else { n = cinfo->num_attrs; } result = mono_array_new_cached (mono_domain_get (), mono_defaults.attribute_class, n, error); return_val_if_nok (error, NULL); n = 0; for (i = 0; i < cinfo->num_attrs; ++i) { MonoCustomAttrEntry *centry = &cinfo->attrs [i]; if (!attr_klass || mono_class_is_assignable_from (attr_klass, centry->ctor->klass)) { attr = create_custom_attr (cinfo->image, centry->ctor, centry->data, centry->data_size, error); if (!mono_error_ok (error)) return result; mono_array_setref (result, n, attr); n ++; } } return result; } /** * mono_custom_attrs_construct: */ MonoArray* mono_custom_attrs_construct (MonoCustomAttrInfo *cinfo) { MonoError error; MonoArray *result = mono_custom_attrs_construct_by_type (cinfo, NULL, &error); mono_error_assert_ok (&error); /*FIXME proper error handling*/ return result; } static MonoArray* mono_custom_attrs_data_construct (MonoCustomAttrInfo *cinfo, MonoError *error) { MonoArray *result; MonoObject *attr; int i; error_init (error); result = mono_array_new_checked (mono_domain_get (), mono_defaults.customattribute_data_class, cinfo->num_attrs, error); return_val_if_nok (error, NULL); for (i = 0; i < cinfo->num_attrs; ++i) { attr = create_custom_attr_data (cinfo->image, &cinfo->attrs [i], error); return_val_if_nok (error, NULL); mono_array_setref (result, i, attr); } return result; } /** * mono_custom_attrs_from_index: * * Returns: NULL if no attributes are found or if a loading error occurs. */ MonoCustomAttrInfo* mono_custom_attrs_from_index (MonoImage *image, guint32 idx) { MonoError error; MonoCustomAttrInfo *result = mono_custom_attrs_from_index_checked (image, idx, FALSE, &error); mono_error_cleanup (&error); return result; } /** * mono_custom_attrs_from_index_checked: * \returns NULL if no attributes are found. On error returns NULL and sets \p error. */ MonoCustomAttrInfo* mono_custom_attrs_from_index_checked (MonoImage *image, guint32 idx, gboolean ignore_missing, MonoError *error) { guint32 mtoken, i, len; guint32 cols [MONO_CUSTOM_ATTR_SIZE]; MonoTableInfo *ca; MonoCustomAttrInfo *ainfo; GList *tmp, *list = NULL; const char *data; MonoCustomAttrEntry* attr; error_init (error); ca = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; i = mono_metadata_custom_attrs_from_index (image, idx); if (!i) return NULL; i --; while (i < ca->rows) { if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) break; list = g_list_prepend (list, GUINT_TO_POINTER (i)); ++i; } len = g_list_length (list); if (!len) return NULL; ainfo = (MonoCustomAttrInfo *)g_malloc0 (MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * len); ainfo->num_attrs = len; ainfo->image = image; for (i = len, tmp = list; i != 0; --i, tmp = tmp->next) { mono_metadata_decode_row (ca, GPOINTER_TO_UINT (tmp->data), cols, MONO_CUSTOM_ATTR_SIZE); mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { case MONO_CUSTOM_ATTR_TYPE_METHODDEF: mtoken |= MONO_TOKEN_METHOD_DEF; break; case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: mtoken |= MONO_TOKEN_MEMBER_REF; break; default: g_error ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); break; } attr = &ainfo->attrs [i - 1]; attr->ctor = mono_get_method_checked (image, mtoken, NULL, NULL, error); if (!attr->ctor) { g_warning ("Can't find custom attr constructor image: %s mtoken: 0x%08x due to: %s", image->name, mtoken, mono_error_get_message (error)); if (ignore_missing) { mono_error_cleanup (error); error_init (error); } else { g_list_free (list); g_free (ainfo); return NULL; } } if (!mono_verifier_verify_cattr_blob (image, cols [MONO_CUSTOM_ATTR_VALUE], NULL)) { /*FIXME raising an exception here doesn't make any sense*/ g_warning ("Invalid custom attribute blob on image %s for index %x", image->name, idx); g_list_free (list); g_free (ainfo); return NULL; } data = mono_metadata_blob_heap (image, cols [MONO_CUSTOM_ATTR_VALUE]); attr->data_size = mono_metadata_decode_value (data, &data); attr->data = (guchar*)data; } g_list_free (list); return ainfo; } /** * mono_custom_attrs_from_method: */ MonoCustomAttrInfo* mono_custom_attrs_from_method (MonoMethod *method) { MonoError error; MonoCustomAttrInfo* result = mono_custom_attrs_from_method_checked (method, &error); mono_error_cleanup (&error); /* FIXME want a better API that doesn't swallow the error */ return result; } MonoCustomAttrInfo* mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error) { guint32 idx; error_init (error); /* * An instantiated method has the same cattrs as the generic method definition. * * LAMESPEC: The .NET SRE throws an exception for instantiations of generic method builders * Note that this stanza is not necessary for non-SRE types, but it's a micro-optimization */ if (method->is_inflated) method = ((MonoMethodInflated *) method)->declaring; if (method_is_dynamic (method) || image_is_dynamic (method->klass->image)) return lookup_custom_attr (method->klass->image, method); if (!method->token) /* Synthetic methods */ return NULL; idx = mono_method_get_index (method); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_METHODDEF; return mono_custom_attrs_from_index_checked (method->klass->image, idx, FALSE, error); } /** * mono_custom_attrs_from_class: */ MonoCustomAttrInfo* mono_custom_attrs_from_class (MonoClass *klass) { MonoError error; MonoCustomAttrInfo *result = mono_custom_attrs_from_class_checked (klass, &error); mono_error_cleanup (&error); return result; } MonoCustomAttrInfo* mono_custom_attrs_from_class_checked (MonoClass *klass, MonoError *error) { guint32 idx; error_init (error); if (mono_class_is_ginst (klass)) klass = mono_class_get_generic_class (klass)->container_class; if (image_is_dynamic (klass->image)) return lookup_custom_attr (klass->image, klass); if (klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR) { idx = mono_metadata_token_index (klass->sizes.generic_param_token); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_GENERICPAR; } else { idx = mono_metadata_token_index (klass->type_token); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_TYPEDEF; } return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); } /** * mono_custom_attrs_from_assembly: */ MonoCustomAttrInfo* mono_custom_attrs_from_assembly (MonoAssembly *assembly) { MonoError error; MonoCustomAttrInfo *result = mono_custom_attrs_from_assembly_checked (assembly, FALSE, &error); mono_error_cleanup (&error); return result; } MonoCustomAttrInfo* mono_custom_attrs_from_assembly_checked (MonoAssembly *assembly, gboolean ignore_missing, MonoError *error) { guint32 idx; error_init (error); if (image_is_dynamic (assembly->image)) return lookup_custom_attr (assembly->image, assembly); idx = 1; /* there is only one assembly */ idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_ASSEMBLY; return mono_custom_attrs_from_index_checked (assembly->image, idx, ignore_missing, error); } static MonoCustomAttrInfo* mono_custom_attrs_from_module (MonoImage *image, MonoError *error) { guint32 idx; error_init (error); if (image_is_dynamic (image)) return lookup_custom_attr (image, image); idx = 1; /* there is only one module */ idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_MODULE; return mono_custom_attrs_from_index_checked (image, idx, FALSE, error); } /** * mono_custom_attrs_from_property: */ MonoCustomAttrInfo* mono_custom_attrs_from_property (MonoClass *klass, MonoProperty *property) { MonoError error; MonoCustomAttrInfo * result = mono_custom_attrs_from_property_checked (klass, property, &error); mono_error_cleanup (&error); return result; } MonoCustomAttrInfo* mono_custom_attrs_from_property_checked (MonoClass *klass, MonoProperty *property, MonoError *error) { guint32 idx; error_init (error); if (image_is_dynamic (klass->image)) { property = mono_metadata_get_corresponding_property_from_generic_type_definition (property); return lookup_custom_attr (klass->image, property); } idx = find_property_index (klass, property); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_PROPERTY; return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); } /** * mono_custom_attrs_from_event: */ MonoCustomAttrInfo* mono_custom_attrs_from_event (MonoClass *klass, MonoEvent *event) { MonoError error; MonoCustomAttrInfo * result = mono_custom_attrs_from_event_checked (klass, event, &error); mono_error_cleanup (&error); return result; } MonoCustomAttrInfo* mono_custom_attrs_from_event_checked (MonoClass *klass, MonoEvent *event, MonoError *error) { guint32 idx; error_init (error); if (image_is_dynamic (klass->image)) { event = mono_metadata_get_corresponding_event_from_generic_type_definition (event); return lookup_custom_attr (klass->image, event); } idx = find_event_index (klass, event); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_EVENT; return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); } /** * mono_custom_attrs_from_field: */ MonoCustomAttrInfo* mono_custom_attrs_from_field (MonoClass *klass, MonoClassField *field) { MonoError error; MonoCustomAttrInfo * result = mono_custom_attrs_from_field_checked (klass, field, &error); mono_error_cleanup (&error); return result; } MonoCustomAttrInfo* mono_custom_attrs_from_field_checked (MonoClass *klass, MonoClassField *field, MonoError *error) { guint32 idx; error_init (error); if (image_is_dynamic (klass->image)) { field = mono_metadata_get_corresponding_field_from_generic_type_definition (field); return lookup_custom_attr (klass->image, field); } idx = find_field_index (klass, field); idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_FIELDDEF; return mono_custom_attrs_from_index_checked (klass->image, idx, FALSE, error); } /** * mono_custom_attrs_from_param: * \param method handle to the method that we want to retrieve custom parameter information from * \param param parameter number, where zero represent the return value, and one is the first parameter in the method * * The result must be released with mono_custom_attrs_free(). * * \returns the custom attribute object for the specified parameter, or NULL if there are none. */ MonoCustomAttrInfo* mono_custom_attrs_from_param (MonoMethod *method, guint32 param) { MonoError error; MonoCustomAttrInfo *result = mono_custom_attrs_from_param_checked (method, param, &error); mono_error_cleanup (&error); return result; } /** * mono_custom_attrs_from_param_checked: * \param method handle to the method that we want to retrieve custom parameter information from * \param param parameter number, where zero represent the return value, and one is the first parameter in the method * \param error set on error * * The result must be released with mono_custom_attrs_free(). * * \returns the custom attribute object for the specified parameter, or NULL if there are none. On failure returns NULL and sets \p error. */ MonoCustomAttrInfo* mono_custom_attrs_from_param_checked (MonoMethod *method, guint32 param, MonoError *error) { MonoTableInfo *ca; guint32 i, idx, method_index; guint32 param_list, param_last, param_pos, found; MonoImage *image; MonoReflectionMethodAux *aux; error_init (error); /* * An instantiated method has the same cattrs as the generic method definition. * * LAMESPEC: The .NET SRE throws an exception for instantiations of generic method builders * Note that this stanza is not necessary for non-SRE types, but it's a micro-optimization */ if (method->is_inflated) method = ((MonoMethodInflated *) method)->declaring; if (image_is_dynamic (method->klass->image)) { MonoCustomAttrInfo *res, *ainfo; int size; aux = (MonoReflectionMethodAux *)g_hash_table_lookup (((MonoDynamicImage*)method->klass->image)->method_aux_hash, method); if (!aux || !aux->param_cattr) return NULL; /* Need to copy since it will be freed later */ ainfo = aux->param_cattr [param]; if (!ainfo) return NULL; size = MONO_SIZEOF_CUSTOM_ATTR_INFO + sizeof (MonoCustomAttrEntry) * ainfo->num_attrs; res = (MonoCustomAttrInfo *)g_malloc0 (size); memcpy (res, ainfo, size); return res; } image = method->klass->image; method_index = mono_method_get_index (method); if (!method_index) return NULL; ca = &image->tables [MONO_TABLE_METHOD]; param_list = mono_metadata_decode_row_col (ca, method_index - 1, MONO_METHOD_PARAMLIST); if (method_index == ca->rows) { ca = &image->tables [MONO_TABLE_PARAM]; param_last = ca->rows + 1; } else { param_last = mono_metadata_decode_row_col (ca, method_index, MONO_METHOD_PARAMLIST); ca = &image->tables [MONO_TABLE_PARAM]; } found = FALSE; for (i = param_list; i < param_last; ++i) { param_pos = mono_metadata_decode_row_col (ca, i - 1, MONO_PARAM_SEQUENCE); if (param_pos == param) { found = TRUE; break; } } if (!found) return NULL; idx = i; idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_PARAMDEF; return mono_custom_attrs_from_index_checked (image, idx, FALSE, error); } /** * mono_custom_attrs_has_attr: */ gboolean mono_custom_attrs_has_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass) { int i; for (i = 0; i < ainfo->num_attrs; ++i) { MonoCustomAttrEntry *centry = &ainfo->attrs[i]; if (centry->ctor == NULL) continue; MonoClass *klass = centry->ctor->klass; if (klass == attr_klass || mono_class_has_parent (klass, attr_klass) || (MONO_CLASS_IS_INTERFACE (attr_klass) && mono_class_is_assignable_from (attr_klass, klass))) return TRUE; } return FALSE; } /** * mono_custom_attrs_get_attr: */ MonoObject* mono_custom_attrs_get_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass) { MonoError error; MonoObject *res = mono_custom_attrs_get_attr_checked (ainfo, attr_klass, &error); mono_error_assert_ok (&error); /*FIXME proper error handling*/ return res; } MonoObject* mono_custom_attrs_get_attr_checked (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass, MonoError *error) { int i; MonoCustomAttrEntry *centry = NULL; g_assert (attr_klass != NULL); error_init (error); for (i = 0; i < ainfo->num_attrs; ++i) { centry = &ainfo->attrs[i]; if (centry->ctor == NULL) continue; MonoClass *klass = centry->ctor->klass; if (attr_klass == klass || mono_class_is_assignable_from (attr_klass, klass)) break; } if (centry == NULL) return NULL; return create_custom_attr (ainfo->image, centry->ctor, centry->data, centry->data_size, error); } /** * mono_reflection_get_custom_attrs_info: * \param obj a reflection object handle * * \returns the custom attribute info for attributes defined for the * reflection handle \p obj. The objects. * * FIXME this function leaks like a sieve for SRE objects. */ MonoCustomAttrInfo* mono_reflection_get_custom_attrs_info (MonoObject *obj_raw) { HANDLE_FUNCTION_ENTER (); MonoError error; MONO_HANDLE_DCL (MonoObject, obj); MonoCustomAttrInfo *result = mono_reflection_get_custom_attrs_info_checked (obj, &error); mono_error_assert_ok (&error); HANDLE_FUNCTION_RETURN_VAL (result); } /** * mono_reflection_get_custom_attrs_info_checked: * \param obj a reflection object handle * \param error set on error * * \returns the custom attribute info for attributes defined for the * reflection handle \p obj. The objects. On failure returns NULL and sets \p error. * * FIXME this function leaks like a sieve for SRE objects. */ MonoCustomAttrInfo* mono_reflection_get_custom_attrs_info_checked (MonoObjectHandle obj, MonoError *error) { HANDLE_FUNCTION_ENTER (); MonoClass *klass; MonoCustomAttrInfo *cinfo = NULL; error_init (error); klass = mono_handle_class (obj); if (klass == mono_defaults.runtimetype_class) { MonoType *type = mono_reflection_type_handle_mono_type (MONO_HANDLE_CAST(MonoReflectionType, obj), error); if (!is_ok (error)) goto leave; klass = mono_class_from_mono_type (type); /*We cannot mono_class_init the class from which we'll load the custom attributes since this must work with broken types.*/ cinfo = mono_custom_attrs_from_class_checked (klass, error); if (!is_ok (error)) goto leave; } else if (strcmp ("Assembly", klass->name) == 0 || strcmp ("MonoAssembly", klass->name) == 0) { MonoReflectionAssemblyHandle rassembly = MONO_HANDLE_CAST (MonoReflectionAssembly, obj); cinfo = mono_custom_attrs_from_assembly_checked (MONO_HANDLE_GETVAL (rassembly, assembly), FALSE, error); if (!is_ok (error)) goto leave; } else if (strcmp ("Module", klass->name) == 0 || strcmp ("MonoModule", klass->name) == 0) { MonoReflectionModuleHandle module = MONO_HANDLE_CAST (MonoReflectionModule, obj); cinfo = mono_custom_attrs_from_module (MONO_HANDLE_GETVAL (module, image), error); if (!is_ok (error)) goto leave; } else if (strcmp ("MonoProperty", klass->name) == 0) { MonoReflectionPropertyHandle rprop = MONO_HANDLE_CAST (MonoReflectionProperty, obj); MonoProperty *property = MONO_HANDLE_GETVAL (rprop, property); cinfo = mono_custom_attrs_from_property_checked (property->parent, property, error); if (!is_ok (error)) goto leave; } else if (strcmp ("MonoEvent", klass->name) == 0) { MonoReflectionMonoEventHandle revent = MONO_HANDLE_CAST (MonoReflectionMonoEvent, obj); MonoEvent *event = MONO_HANDLE_GETVAL (revent, event); cinfo = mono_custom_attrs_from_event_checked (event->parent, event, error); if (!is_ok (error)) goto leave; } else if (strcmp ("MonoField", klass->name) == 0) { MonoReflectionFieldHandle rfield = MONO_HANDLE_CAST (MonoReflectionField, obj); MonoClassField *field = MONO_HANDLE_GETVAL (rfield, field); cinfo = mono_custom_attrs_from_field_checked (field->parent, field, error); if (!is_ok (error)) goto leave; } else if ((strcmp ("MonoMethod", klass->name) == 0) || (strcmp ("MonoCMethod", klass->name) == 0)) { MonoReflectionMethodHandle rmethod = MONO_HANDLE_CAST (MonoReflectionMethod, obj); cinfo = mono_custom_attrs_from_method_checked (MONO_HANDLE_GETVAL (rmethod, method), error); if (!is_ok (error)) goto leave; } else if (strcmp ("ParameterInfo", klass->name) == 0 || strcmp ("MonoParameterInfo", klass->name) == 0) { MonoReflectionParameterHandle param = MONO_HANDLE_CAST (MonoReflectionParameter, obj); MonoObjectHandle member_impl = MONO_HANDLE_NEW_GET (MonoObject, param, MemberImpl); MonoClass *member_class = mono_handle_class (member_impl); if (mono_class_is_reflection_method_or_constructor (member_class)) { MonoReflectionMethodHandle rmethod = MONO_HANDLE_CAST (MonoReflectionMethod, member_impl); cinfo = mono_custom_attrs_from_param_checked (MONO_HANDLE_GETVAL (rmethod, method), MONO_HANDLE_GETVAL (param, PositionImpl) + 1, error); if (!is_ok (error)) goto leave; } else if (mono_is_sr_mono_property (member_class)) { MonoReflectionPropertyHandle prop = MONO_HANDLE_CAST (MonoReflectionProperty, member_impl); MonoProperty *property = MONO_HANDLE_GETVAL (prop, property); MonoMethod *method; if (!(method = property->get)) method = property->set; g_assert (method); cinfo = mono_custom_attrs_from_param_checked (method, MONO_HANDLE_GETVAL (param, PositionImpl) + 1, error); if (!is_ok (error)) goto leave; } #ifndef DISABLE_REFLECTION_EMIT else if (mono_is_sre_method_on_tb_inst (member_class)) {/*XXX This is a workaround for Compiler Context*/ // FIXME: Is this still needed ? g_assert_not_reached (); } else if (mono_is_sre_ctor_on_tb_inst (member_class)) { /*XX This is a workaround for Compiler Context*/ // FIXME: Is this still needed ? g_assert_not_reached (); } #endif else { char *type_name = mono_type_get_full_name (member_class); mono_error_set_not_supported (error, "Custom attributes on a ParamInfo with member %s are not supported", type_name); g_free (type_name); goto leave; } } else if (strcmp ("AssemblyBuilder", klass->name) == 0) { MonoReflectionAssemblyBuilderHandle assemblyb = MONO_HANDLE_CAST (MonoReflectionAssemblyBuilder, obj); MonoReflectionAssemblyHandle assembly = MONO_HANDLE_CAST (MonoReflectionAssembly, assemblyb); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, assemblyb, cattrs); MonoImage * image = MONO_HANDLE_GETVAL (assembly, assembly)->image; g_assert (image); cinfo = mono_custom_attrs_from_builders_handle (NULL, image, cattrs); } else if (strcmp ("TypeBuilder", klass->name) == 0) { MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_CAST (MonoReflectionTypeBuilder, obj); MonoReflectionModuleBuilderHandle module = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, tb, module); MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (module, dynamic_image); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, tb, cattrs); cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); } else if (strcmp ("ModuleBuilder", klass->name) == 0) { MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionModuleBuilder, obj); MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, mb, cattrs); cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); } else if (strcmp ("ConstructorBuilder", klass->name) == 0) { MonoReflectionCtorBuilderHandle cb = MONO_HANDLE_CAST (MonoReflectionCtorBuilder, obj); MonoMethod *mhandle = MONO_HANDLE_GETVAL (cb, mhandle); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, cb, cattrs); cinfo = mono_custom_attrs_from_builders_handle (NULL, mhandle->klass->image, cattrs); } else if (strcmp ("MethodBuilder", klass->name) == 0) { MonoReflectionMethodBuilderHandle mb = MONO_HANDLE_CAST (MonoReflectionMethodBuilder, obj); MonoMethod *mhandle = MONO_HANDLE_GETVAL (mb, mhandle); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, mb, cattrs); cinfo = mono_custom_attrs_from_builders_handle (NULL, mhandle->klass->image, cattrs); } else if (strcmp ("FieldBuilder", klass->name) == 0) { MonoReflectionFieldBuilderHandle fb = MONO_HANDLE_CAST (MonoReflectionFieldBuilder, obj); MonoReflectionTypeBuilderHandle tb = MONO_HANDLE_NEW_GET (MonoReflectionTypeBuilder, fb, typeb); MonoReflectionModuleBuilderHandle mb = MONO_HANDLE_NEW_GET (MonoReflectionModuleBuilder, tb, module); MonoDynamicImage *dynamic_image = MONO_HANDLE_GETVAL (mb, dynamic_image); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, fb, cattrs); cinfo = mono_custom_attrs_from_builders_handle (NULL, &dynamic_image->image, cattrs); } else if (strcmp ("MonoGenericClass", klass->name) == 0) { MonoReflectionGenericClassHandle gclass = MONO_HANDLE_CAST (MonoReflectionGenericClass, obj); MonoReflectionTypeHandle generic_type = MONO_HANDLE_NEW_GET (MonoReflectionType, gclass, generic_type); cinfo = mono_reflection_get_custom_attrs_info_checked (MONO_HANDLE_CAST (MonoObject, generic_type), error); if (!is_ok (error)) goto leave; } else { /* handle other types here... */ g_error ("get custom attrs not yet supported for %s", klass->name); } leave: HANDLE_FUNCTION_RETURN_VAL (cinfo); } /** * mono_reflection_get_custom_attrs_by_type: * \param obj a reflection object handle * \returns an array with all the custom attributes defined of the * reflection handle \p obj. If \p attr_klass is non-NULL, only custom attributes * of that type are returned. The objects are fully build. Return NULL if a loading error * occurs. */ MonoArray* mono_reflection_get_custom_attrs_by_type (MonoObject *obj_raw, MonoClass *attr_klass, MonoError *error) { HANDLE_FUNCTION_ENTER (); MONO_HANDLE_DCL (MonoObject, obj); MonoArrayHandle result = mono_reflection_get_custom_attrs_by_type_handle (obj, attr_klass, error); HANDLE_FUNCTION_RETURN_OBJ (result); } MonoArrayHandle mono_reflection_get_custom_attrs_by_type_handle (MonoObjectHandle obj, MonoClass *attr_klass, MonoError *error) { MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); MonoCustomAttrInfo *cinfo; error_init (error); cinfo = mono_reflection_get_custom_attrs_info_checked (obj, error); if (!is_ok (error)) goto leave; if (cinfo) { MONO_HANDLE_ASSIGN (result, MONO_HANDLE_NEW (MonoArray, mono_custom_attrs_construct_by_type (cinfo, attr_klass, error))); /* FIXME use coop handles for mono_custom_attrs_construct_by_type */ if (!cinfo->cached) mono_custom_attrs_free (cinfo); if (!result) goto leave; } else { MONO_HANDLE_ASSIGN (result, mono_array_new_handle (mono_domain_get (), mono_defaults.attribute_class, 0, error)); } leave: return result; } /** * mono_reflection_get_custom_attrs: * \param obj a reflection object handle * \return an array with all the custom attributes defined of the * reflection handle \p obj. The objects are fully build. Return NULL if a loading error * occurs. */ MonoArray* mono_reflection_get_custom_attrs (MonoObject *obj_raw) { HANDLE_FUNCTION_ENTER (); MonoError error; MONO_HANDLE_DCL (MonoObject, obj); MonoArrayHandle result = mono_reflection_get_custom_attrs_by_type_handle (obj, NULL, &error); mono_error_cleanup (&error); HANDLE_FUNCTION_RETURN_OBJ (result); } /** * mono_reflection_get_custom_attrs_data: * \param obj a reflection obj handle * \returns an array of \c System.Reflection.CustomAttributeData, * which include information about attributes reflected on * types loaded using the Reflection Only methods */ MonoArray* mono_reflection_get_custom_attrs_data (MonoObject *obj_raw) { HANDLE_FUNCTION_ENTER (); MonoError error; MONO_HANDLE_DCL (MonoObject, obj); MonoArrayHandle result = mono_reflection_get_custom_attrs_data_checked (obj, &error); mono_error_cleanup (&error); HANDLE_FUNCTION_RETURN_OBJ (result); } /* * mono_reflection_get_custom_attrs_data_checked: * @obj: a reflection obj handle * @error: set on error * * Returns an array of System.Reflection.CustomAttributeData, * which include information about attributes reflected on * types loaded using the Reflection Only methods */ MonoArrayHandle mono_reflection_get_custom_attrs_data_checked (MonoObjectHandle obj, MonoError *error) { MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL); MonoCustomAttrInfo *cinfo; error_init (error); cinfo = mono_reflection_get_custom_attrs_info_checked (obj, error); if (!is_ok (error)) goto leave; if (cinfo) { MONO_HANDLE_ASSIGN (result, MONO_HANDLE_NEW (MonoArray, mono_custom_attrs_data_construct (cinfo, error))); /* FIXME use coop handles in mono_custom_attrs_data_construct */ if (!cinfo->cached) mono_custom_attrs_free (cinfo); if (!is_ok (error)) goto leave; } else MONO_HANDLE_ASSIGN (result, mono_array_new_handle (mono_domain_get (), mono_defaults.customattribute_data_class, 0, error)); leave: return result; } static gboolean custom_attr_class_name_from_methoddef (MonoImage *image, guint32 method_token, const gchar **nspace, const gchar **class_name) { /* mono_get_method_from_token () */ g_assert (mono_metadata_token_table (method_token) == MONO_TABLE_METHOD); guint32 type_token = mono_metadata_typedef_from_method (image, method_token); if (!type_token) { /* Bad method token (could not find corresponding typedef) */ return FALSE; } type_token |= MONO_TOKEN_TYPE_DEF; { /* mono_class_create_from_typedef () */ MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; guint32 cols [MONO_TYPEDEF_SIZE]; guint tidx = mono_metadata_token_index (type_token); if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows) { /* "Invalid typedef token %x", type_token */ return FALSE; } mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); if (class_name) *class_name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); if (nspace) *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); return TRUE; } } /** * custom_attr_class_name_from_method_token: * @image: The MonoImage * @method_token: a token for a custom attr constructor in @image * @assembly_token: out argment set to the assembly ref token of the custom attr * @nspace: out argument set to namespace (a string in the string heap of @image) of the custom attr * @class_name: out argument set to the class name of the custom attr. * * Given an @image and a @method_token (which is assumed to be a * constructor), fills in the out arguments with the assembly ref (if * a methodref) and the namespace and class name of the custom * attribute. * * Returns: TRUE on success, FALSE otherwise. * * LOCKING: does not take locks */ static gboolean custom_attr_class_name_from_method_token (MonoImage *image, guint32 method_token, guint32 *assembly_token, const gchar **nspace, const gchar **class_name) { /* This only works with method tokens constructed from a * custom attr token, which can only be methoddef or * memberref */ g_assert (mono_metadata_token_table (method_token) == MONO_TABLE_METHOD || mono_metadata_token_table (method_token) == MONO_TABLE_MEMBERREF); if (mono_metadata_token_table (method_token) == MONO_TABLE_MEMBERREF) { /* method_from_memberref () */ guint32 cols[6]; guint32 nindex, class_index; int idx = mono_metadata_token_index (method_token); mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], idx-1, cols, 3); nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; if (class_index == MONO_MEMBERREF_PARENT_TYPEREF) { guint32 type_token = MONO_TOKEN_TYPE_REF | nindex; /* mono_class_from_typeref_checked () */ { guint32 cols [MONO_TYPEREF_SIZE]; MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE); if (class_name) *class_name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); if (nspace) *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); if (assembly_token) *assembly_token = cols [MONO_TYPEREF_SCOPE]; return TRUE; } } else if (class_index == MONO_MEMBERREF_PARENT_METHODDEF) { guint32 methoddef_token = MONO_TOKEN_METHOD_DEF | nindex; if (assembly_token) *assembly_token = 0; return custom_attr_class_name_from_methoddef (image, methoddef_token, nspace, class_name); } else { /* Attributes can't be generic, so it won't be * a typespec, and they're always * constructors, so it won't be a moduleref */ g_assert_not_reached (); } } else { /* must be MONO_TABLE_METHOD */ if (assembly_token) *assembly_token = 0; return custom_attr_class_name_from_methoddef (image, method_token, nspace, class_name); } } /** * mono_assembly_metadata_foreach_custom_attr: * \param assembly the assembly to iterate over * \param func the function to call for each custom attribute * \param user_data passed to \p func * Calls \p func for each custom attribute type on the given assembly until \p func returns TRUE. * Everything is done using low-level metadata APIs, so it is safe to use during assembly loading. */ void mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssemblyMetadataCustomAttrIterFunc func, gpointer user_data) { MonoImage *image; guint32 mtoken, i; guint32 cols [MONO_CUSTOM_ATTR_SIZE]; MonoTableInfo *ca; guint32 idx; /* * This might be called during assembly loading, so do everything using the low-level * metadata APIs. */ image = assembly->image; /* Dynamic images would need to go through the AssemblyBuilder's * CustomAttributeBuilder array. Going through the tables below * definitely won't work. */ g_assert (!image_is_dynamic (image)); idx = 1; /* there is only one assembly */ idx <<= MONO_CUSTOM_ATTR_BITS; idx |= MONO_CUSTOM_ATTR_ASSEMBLY; /* Inlined from mono_custom_attrs_from_index_checked () */ ca = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; i = mono_metadata_custom_attrs_from_index (image, idx); if (!i) return; i --; gboolean stop_iterating = FALSE; while (!stop_iterating && i < ca->rows) { if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) break; mono_metadata_decode_row (ca, i, cols, MONO_CUSTOM_ATTR_SIZE); i ++; mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { case MONO_CUSTOM_ATTR_TYPE_METHODDEF: mtoken |= MONO_TOKEN_METHOD_DEF; break; case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: mtoken |= MONO_TOKEN_MEMBER_REF; break; default: g_warning ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]); continue; } const char *nspace = NULL; const char *name = NULL; guint32 assembly_token = 0; if (!custom_attr_class_name_from_method_token (image, mtoken, &assembly_token, &nspace, &name)) continue; stop_iterating = func (image, assembly_token, nspace, name, mtoken, user_data); } }