/* * class.c: Class management for the Mono runtime * * Author: * Miguel de Icaza (miguel@ximian.com) * * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Copyright 2012 Xamarin Inc (http://www.xamarin.com) */ #include #ifdef HAVE_ALLOCA_H #include #endif #include #include #include #include #if !HOST_WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MonoStats mono_stats; gboolean mono_print_vtable = FALSE; /* Statistics */ guint32 inflated_classes, inflated_classes_size, inflated_methods_size; guint32 classes_size, class_ext_size; /* Function supplied by the runtime to find classes by name using information from the AOT file */ static MonoGetClassFromName get_class_from_name = NULL; static MonoClass * mono_class_create_from_typedef (MonoImage *image, guint32 type_token); static gboolean mono_class_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res); static gboolean can_access_type (MonoClass *access_klass, MonoClass *member_klass); static MonoMethod* find_method_in_metadata (MonoClass *klass, const char *name, int param_count, int flags); static int generic_array_methods (MonoClass *class); static void setup_generic_array_ifaces (MonoClass *class, MonoClass *iface, MonoMethod **methods, int pos); static MonoMethod* mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter); static char* mono_assembly_name_from_token (MonoImage *image, guint32 type_token); static void mono_field_resolve_type (MonoClassField *field, MonoError *error); static guint32 mono_field_resolve_flags (MonoClassField *field); static void mono_class_setup_vtable_full (MonoClass *class, GList *in_setup); void (*mono_debugger_class_init_func) (MonoClass *klass) = NULL; void (*mono_debugger_class_loaded_methods_func) (MonoClass *klass) = NULL; /* * mono_class_from_typeref: * @image: a MonoImage * @type_token: a TypeRef token * * Creates the MonoClass* structure representing the type defined by * the typeref token valid inside @image. * Returns: the MonoClass* representing the typeref token, NULL ifcould * not be loaded. */ MonoClass * mono_class_from_typeref (MonoImage *image, guint32 type_token) { MonoError error; guint32 cols [MONO_TYPEREF_SIZE]; MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; guint32 idx; const char *name, *nspace; MonoClass *res; MonoImage *module; if (!mono_verifier_verify_typeref_row (image, (type_token & 0xffffff) - 1, &error)) { mono_trace_warning (MONO_TRACE_TYPE, "Failed to resolve typeref from %s due to '%s'", image->name, mono_error_get_message (&error)); return NULL; } mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE); name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLTION_SCOPE_BITS; switch (cols [MONO_TYPEREF_SCOPE] & MONO_RESOLTION_SCOPE_MASK) { case MONO_RESOLTION_SCOPE_MODULE: if (!idx) g_error ("null ResolutionScope not yet handled"); /* a typedef in disguise */ return mono_class_from_name (image, nspace, name); case MONO_RESOLTION_SCOPE_MODULEREF: module = mono_image_load_module (image, idx); if (module) return mono_class_from_name (module, nspace, name); else { char *msg = g_strdup_printf ("%s%s%s", nspace, nspace [0] ? "." : "", name); char *human_name; human_name = mono_stringify_assembly_name (&image->assembly->aname); mono_loader_set_error_type_load (msg, human_name); g_free (msg); g_free (human_name); return NULL; } case MONO_RESOLTION_SCOPE_TYPEREF: { MonoClass *enclosing; GList *tmp; if (idx == mono_metadata_token_index (type_token)) { mono_loader_set_error_bad_image (g_strdup_printf ("Image %s with self-referencing typeref token %08x.", image->name, type_token)); return NULL; } enclosing = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | idx); if (!enclosing) return NULL; if (enclosing->nested_classes_inited && enclosing->ext) { /* Micro-optimization: don't scan the metadata tables if enclosing is already inited */ for (tmp = enclosing->ext->nested_classes; tmp; tmp = tmp->next) { res = tmp->data; if (strcmp (res->name, name) == 0) return res; } } else { /* Don't call mono_class_init as we might've been called by it recursively */ int i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, 1); while (i) { guint32 class_nested = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, MONO_NESTED_CLASS_NESTED); guint32 string_offset = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_TYPEDEF], class_nested - 1, MONO_TYPEDEF_NAME); const char *nname = mono_metadata_string_heap (enclosing->image, string_offset); if (strcmp (nname, name) == 0) return mono_class_create_from_typedef (enclosing->image, MONO_TOKEN_TYPE_DEF | class_nested); i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, i + 1); } } g_warning ("TypeRef ResolutionScope not yet handled (%d) for %s.%s in image %s", idx, nspace, name, image->name); return NULL; } case MONO_RESOLTION_SCOPE_ASSEMBLYREF: break; } if (idx > image->tables [MONO_TABLE_ASSEMBLYREF].rows) { mono_loader_set_error_bad_image (g_strdup_printf ("Image %s with invalid assemblyref token %08x.", image->name, idx)); return NULL; } if (!image->references || !image->references [idx - 1]) mono_assembly_load_reference (image, idx - 1); g_assert (image->references [idx - 1]); /* If the assembly did not load, register this as a type load exception */ if (image->references [idx - 1] == REFERENCE_MISSING){ MonoAssemblyName aname; char *human_name; mono_assembly_get_assemblyref (image, idx - 1, &aname); human_name = mono_stringify_assembly_name (&aname); mono_loader_set_error_assembly_load (human_name, image->assembly ? image->assembly->ref_only : FALSE); g_free (human_name); return NULL; } return mono_class_from_name (image->references [idx - 1]->image, nspace, name); } static void * mono_image_memdup (MonoImage *image, void *data, guint size) { void *res = mono_image_alloc (image, size); memcpy (res, data, size); return res; } /* Copy everything mono_metadata_free_array free. */ MonoArrayType * mono_dup_array_type (MonoImage *image, MonoArrayType *a) { if (image) { a = mono_image_memdup (image, a, sizeof (MonoArrayType)); if (a->sizes) a->sizes = mono_image_memdup (image, a->sizes, a->numsizes * sizeof (int)); if (a->lobounds) a->lobounds = mono_image_memdup (image, a->lobounds, a->numlobounds * sizeof (int)); } else { a = g_memdup (a, sizeof (MonoArrayType)); if (a->sizes) a->sizes = g_memdup (a->sizes, a->numsizes * sizeof (int)); if (a->lobounds) a->lobounds = g_memdup (a->lobounds, a->numlobounds * sizeof (int)); } return a; } /* Copy everything mono_metadata_free_method_signature free. */ MonoMethodSignature* mono_metadata_signature_deep_dup (MonoImage *image, MonoMethodSignature *sig) { int i; sig = mono_metadata_signature_dup_full (image, sig); sig->ret = mono_metadata_type_dup (image, sig->ret); for (i = 0; i < sig->param_count; ++i) sig->params [i] = mono_metadata_type_dup (image, sig->params [i]); return sig; } static void _mono_type_get_assembly_name (MonoClass *klass, GString *str) { MonoAssembly *ta = klass->image->assembly; char *name; name = mono_stringify_assembly_name (&ta->aname); g_string_append_printf (str, ", %s", name); g_free (name); } static inline void mono_type_name_check_byref (MonoType *type, GString *str) { if (type->byref) g_string_append_c (str, '&'); } static void mono_type_get_name_recurse (MonoType *type, GString *str, gboolean is_recursed, MonoTypeNameFormat format) { MonoClass *klass; switch (type->type) { case MONO_TYPE_ARRAY: { int i, rank = type->data.array->rank; MonoTypeNameFormat nested_format; nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; mono_type_get_name_recurse ( &type->data.array->eklass->byval_arg, str, FALSE, nested_format); g_string_append_c (str, '['); if (rank == 1) g_string_append_c (str, '*'); for (i = 1; i < rank; i++) g_string_append_c (str, ','); g_string_append_c (str, ']'); mono_type_name_check_byref (type, str); if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) _mono_type_get_assembly_name (type->data.array->eklass, str); break; } case MONO_TYPE_SZARRAY: { MonoTypeNameFormat nested_format; nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; mono_type_get_name_recurse ( &type->data.klass->byval_arg, str, FALSE, nested_format); g_string_append (str, "[]"); mono_type_name_check_byref (type, str); if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) _mono_type_get_assembly_name (type->data.klass, str); break; } case MONO_TYPE_PTR: { MonoTypeNameFormat nested_format; nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; mono_type_get_name_recurse ( type->data.type, str, FALSE, nested_format); g_string_append_c (str, '*'); mono_type_name_check_byref (type, str); if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) _mono_type_get_assembly_name (mono_class_from_mono_type (type->data.type), str); break; } case MONO_TYPE_VAR: case MONO_TYPE_MVAR: if (!mono_generic_param_info (type->data.generic_param)) g_string_append_printf (str, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", type->data.generic_param->num); else g_string_append (str, mono_generic_param_info (type->data.generic_param)->name); mono_type_name_check_byref (type, str); break; default: klass = mono_class_from_mono_type (type); if (klass->nested_in) { mono_type_get_name_recurse ( &klass->nested_in->byval_arg, str, TRUE, format); if (format == MONO_TYPE_NAME_FORMAT_IL) g_string_append_c (str, '.'); else g_string_append_c (str, '+'); } else if (*klass->name_space) { g_string_append (str, klass->name_space); g_string_append_c (str, '.'); } if (format == MONO_TYPE_NAME_FORMAT_IL) { char *s = strchr (klass->name, '`'); int len = s ? s - klass->name : strlen (klass->name); g_string_append_len (str, klass->name, len); } else g_string_append (str, klass->name); if (is_recursed) break; if (klass->generic_class) { MonoGenericClass *gclass = klass->generic_class; MonoGenericInst *inst = gclass->context.class_inst; MonoTypeNameFormat nested_format; int i; nested_format = format == MONO_TYPE_NAME_FORMAT_FULL_NAME ? MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : format; if (format == MONO_TYPE_NAME_FORMAT_IL) g_string_append_c (str, '<'); else g_string_append_c (str, '['); for (i = 0; i < inst->type_argc; i++) { MonoType *t = inst->type_argv [i]; if (i) g_string_append_c (str, ','); if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) g_string_append_c (str, '['); mono_type_get_name_recurse (inst->type_argv [i], str, FALSE, nested_format); if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) g_string_append_c (str, ']'); } if (format == MONO_TYPE_NAME_FORMAT_IL) g_string_append_c (str, '>'); else g_string_append_c (str, ']'); } else if (klass->generic_container && (format != MONO_TYPE_NAME_FORMAT_FULL_NAME) && (format != MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)) { int i; if (format == MONO_TYPE_NAME_FORMAT_IL) g_string_append_c (str, '<'); else g_string_append_c (str, '['); for (i = 0; i < klass->generic_container->type_argc; i++) { if (i) g_string_append_c (str, ','); g_string_append (str, mono_generic_container_get_param_info (klass->generic_container, i)->name); } if (format == MONO_TYPE_NAME_FORMAT_IL) g_string_append_c (str, '>'); else g_string_append_c (str, ']'); } mono_type_name_check_byref (type, str); if ((format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && (type->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) _mono_type_get_assembly_name (klass, str); break; } } /** * mono_type_get_name_full: * @type: a type * @format: the format for the return string. * * * Returns: the string representation in a number of formats: * * if format is MONO_TYPE_NAME_FORMAT_REFLECTION, the return string is * returned in the formatrequired by System.Reflection, this is the * inverse of mono_reflection_parse_type (). * * if format is MONO_TYPE_NAME_FORMAT_IL, it returns a syntax that can * be used by the IL assembler. * * if format is MONO_TYPE_NAME_FORMAT_FULL_NAME * * if format is MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED */ char* mono_type_get_name_full (MonoType *type, MonoTypeNameFormat format) { GString* result; result = g_string_new (""); mono_type_get_name_recurse (type, result, FALSE, format); return g_string_free (result, FALSE); } /** * mono_type_get_full_name: * @class: a class * * Returns: the string representation for type as required by System.Reflection. * The inverse of mono_reflection_parse_type (). */ char * mono_type_get_full_name (MonoClass *class) { return mono_type_get_name_full (mono_class_get_type (class), MONO_TYPE_NAME_FORMAT_REFLECTION); } /** * mono_type_get_name: * @type: a type * * Returns: the string representation for type as it would be represented in IL code. */ char* mono_type_get_name (MonoType *type) { return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); } /* * mono_type_get_underlying_type: * @type: a type * * Returns: the MonoType for the underlying integer type if @type * is an enum and byref is false, otherwise the type itself. */ MonoType* mono_type_get_underlying_type (MonoType *type) { if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype && !type->byref) return mono_class_enum_basetype (type->data.klass); if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype && !type->byref) return mono_class_enum_basetype (type->data.generic_class->container_class); return type; } /* * mono_class_is_open_constructed_type: * @type: a type * * Returns TRUE if type represents a generics open constructed type. * IOW, not all type parameters required for the instantiation have * been provided or it's a generic type definition. * * An open constructed type means it's a non realizable type. Not to * be mixed up with an abstract type - we can't cast or dispatch to * an open type, for example. */ gboolean mono_class_is_open_constructed_type (MonoType *t) { switch (t->type) { case MONO_TYPE_VAR: case MONO_TYPE_MVAR: return TRUE; case MONO_TYPE_SZARRAY: return mono_class_is_open_constructed_type (&t->data.klass->byval_arg); case MONO_TYPE_ARRAY: return mono_class_is_open_constructed_type (&t->data.array->eklass->byval_arg); case MONO_TYPE_PTR: return mono_class_is_open_constructed_type (t->data.type); case MONO_TYPE_GENERICINST: return t->data.generic_class->context.class_inst->is_open; case MONO_TYPE_CLASS: case MONO_TYPE_VALUETYPE: return t->data.klass->generic_container != NULL; default: return FALSE; } } static MonoType* inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) { mono_error_init (error); switch (type->type) { case MONO_TYPE_MVAR: { MonoType *nt; int num = mono_type_get_generic_param_num (type); MonoGenericInst *inst = context->method_inst; if (!inst || !inst->type_argv) return NULL; if (num >= inst->type_argc) { MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded in this context with %d instantiations", num, info ? info->name : "", inst->type_argc); return NULL; } /* * Note that the VAR/MVAR cases are different from the rest. The other cases duplicate @type, * while the VAR/MVAR duplicates a type from the context. So, we need to ensure that the * ->byref and ->attrs from @type are propagated to the returned type. */ nt = mono_metadata_type_dup (image, inst->type_argv [num]); nt->byref = type->byref; nt->attrs = type->attrs; return nt; } case MONO_TYPE_VAR: { MonoType *nt; int num = mono_type_get_generic_param_num (type); MonoGenericInst *inst = context->class_inst; if (!inst) return NULL; if (num >= inst->type_argc) { MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param); mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded in this context with %d instantiations", num, info ? info->name : "", inst->type_argc); return NULL; } nt = mono_metadata_type_dup (image, inst->type_argv [num]); nt->byref = type->byref; nt->attrs = type->attrs; return nt; } case MONO_TYPE_SZARRAY: { MonoClass *eclass = type->data.klass; MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error); if (!inflated || !mono_error_ok (error)) return NULL; nt = mono_metadata_type_dup (image, type); nt->data.klass = mono_class_from_mono_type (inflated); mono_metadata_free_type (inflated); return nt; } case MONO_TYPE_ARRAY: { MonoClass *eclass = type->data.array->eklass; MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error); if (!inflated || !mono_error_ok (error)) return NULL; nt = mono_metadata_type_dup (image, type); nt->data.array->eklass = mono_class_from_mono_type (inflated); mono_metadata_free_type (inflated); return nt; } case MONO_TYPE_GENERICINST: { MonoGenericClass *gclass = type->data.generic_class; MonoGenericInst *inst; MonoType *nt; if (!gclass->context.class_inst->is_open) return NULL; inst = mono_metadata_inflate_generic_inst (gclass->context.class_inst, context, error); if (!mono_error_ok (error)) return NULL; if (inst != gclass->context.class_inst) gclass = mono_metadata_lookup_generic_class (gclass->container_class, inst, gclass->is_dynamic); if (gclass == type->data.generic_class) return NULL; nt = mono_metadata_type_dup (image, type); nt->data.generic_class = gclass; return nt; } case MONO_TYPE_CLASS: case MONO_TYPE_VALUETYPE: { MonoClass *klass = type->data.klass; MonoGenericContainer *container = klass->generic_container; MonoGenericInst *inst; MonoGenericClass *gclass = NULL; MonoType *nt; if (!container) return NULL; /* We can't use context->class_inst directly, since it can have more elements */ inst = mono_metadata_inflate_generic_inst (container->context.class_inst, context, error); if (!mono_error_ok (error)) return NULL; if (inst == container->context.class_inst) return NULL; gclass = mono_metadata_lookup_generic_class (klass, inst, klass->image->dynamic); nt = mono_metadata_type_dup (image, type); nt->type = MONO_TYPE_GENERICINST; nt->data.generic_class = gclass; return nt; } default: return NULL; } return NULL; } MonoGenericContext * mono_generic_class_get_context (MonoGenericClass *gclass) { return &gclass->context; } MonoGenericContext * mono_class_get_context (MonoClass *class) { return class->generic_class ? mono_generic_class_get_context (class->generic_class) : NULL; } /* * mono_class_get_generic_container: * * Return the generic container of KLASS which should be a generic type definition. */ MonoGenericContainer* mono_class_get_generic_container (MonoClass *klass) { g_assert (klass->is_generic); return klass->generic_container; } /* * mono_class_get_generic_class: * * Return the MonoGenericClass of KLASS, which should be a generic instance. */ MonoGenericClass* mono_class_get_generic_class (MonoClass *klass) { g_assert (klass->is_inflated); return klass->generic_class; } /* * mono_class_inflate_generic_type_with_mempool: * @mempool: a mempool * @type: a type * @context: a generics context * @error: error context * * The same as mono_class_inflate_generic_type, but allocates the MonoType * from mempool if it is non-NULL. If it is NULL, the MonoType is * allocated on the heap and is owned by the caller. * The returned type can potentially be the same as TYPE, so it should not be * modified by the caller, and it should be freed using mono_metadata_free_type (). */ MonoType* mono_class_inflate_generic_type_with_mempool (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) { MonoType *inflated = NULL; mono_error_init (error); if (context) inflated = inflate_generic_type (image, type, context, error); if (!mono_error_ok (error)) return NULL; if (!inflated) { MonoType *shared = mono_metadata_get_shared_type (type); if (shared) { return shared; } else { return mono_metadata_type_dup (image, type); } } mono_stats.inflated_type_count++; return inflated; } /* * mono_class_inflate_generic_type: * @type: a type * @context: a generics context * * If @type is a generic type and @context is not NULL, instantiate it using the * generics context @context. * * Returns: the instantiated type or a copy of @type. The returned MonoType is allocated * on the heap and is owned by the caller. Returns NULL on error. * * @deprecated Please use mono_class_inflate_generic_type_checked instead */ MonoType* mono_class_inflate_generic_type (MonoType *type, MonoGenericContext *context) { MonoError error; MonoType *result; result = mono_class_inflate_generic_type_checked (type, context, &error); if (!mono_error_ok (&error)) { mono_error_cleanup (&error); return NULL; } return result; } /* * mono_class_inflate_generic_type: * @type: a type * @context: a generics context * @error: error context to use * * If @type is a generic type and @context is not NULL, instantiate it using the * generics context @context. * * Returns: the instantiated type or a copy of @type. The returned MonoType is allocated * on the heap and is owned by the caller. */ MonoType* mono_class_inflate_generic_type_checked (MonoType *type, MonoGenericContext *context, MonoError *error) { return mono_class_inflate_generic_type_with_mempool (NULL, type, context, error); } /* * mono_class_inflate_generic_type_no_copy: * * Same as inflate_generic_type_with_mempool, but return TYPE if no inflation * was done. */ static MonoType* mono_class_inflate_generic_type_no_copy (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error) { MonoType *inflated = NULL; mono_error_init (error); if (context) { inflated = inflate_generic_type (image, type, context, error); if (!mono_error_ok (error)) return NULL; } if (!inflated) return type; mono_stats.inflated_type_count++; return inflated; } static MonoClass* mono_class_inflate_generic_class_checked (MonoClass *gklass, MonoGenericContext *context, MonoError *error) { MonoClass *res; MonoType *inflated; inflated = mono_class_inflate_generic_type_checked (&gklass->byval_arg, context, error); if (!mono_error_ok (error)) return NULL; res = mono_class_from_mono_type (inflated); mono_metadata_free_type (inflated); return res; } /* * mono_class_inflate_generic_class: * * Inflate the class GKLASS with CONTEXT. */ MonoClass* mono_class_inflate_generic_class (MonoClass *gklass, MonoGenericContext *context) { MonoError error; MonoClass *res; res = mono_class_inflate_generic_class_checked (gklass, context, &error); g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/ return res; } static MonoGenericContext inflate_generic_context (MonoGenericContext *context, MonoGenericContext *inflate_with, MonoError *error) { MonoGenericInst *class_inst = NULL; MonoGenericInst *method_inst = NULL; MonoGenericContext res = { NULL, NULL }; mono_error_init (error); if (context->class_inst) { class_inst = mono_metadata_inflate_generic_inst (context->class_inst, inflate_with, error); if (!mono_error_ok (error)) goto fail; } if (context->method_inst) { method_inst = mono_metadata_inflate_generic_inst (context->method_inst, inflate_with, error); if (!mono_error_ok (error)) goto fail; } res.class_inst = class_inst; res.method_inst = method_inst; fail: return res; } /* * mono_class_inflate_generic_method: * @method: a generic method * @context: a generics context * * Instantiate the generic method @method using the generics context @context. * * Returns: the new instantiated method */ MonoMethod * mono_class_inflate_generic_method (MonoMethod *method, MonoGenericContext *context) { return mono_class_inflate_generic_method_full (method, NULL, context); } /** * mono_class_inflate_generic_method_full: * * Instantiate method @method with the generic context @context. * BEWARE: All non-trivial fields are invalid, including klass, signature, and header. * Use mono_method_signature () and mono_method_get_header () to get the correct values. */ MonoMethod* mono_class_inflate_generic_method_full (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context) { MonoError error; MonoMethod *res = mono_class_inflate_generic_method_full_checked (method, klass_hint, context, &error); if (!mono_error_ok (&error)) /*FIXME do proper error handling - on this case, kill this function. */ g_error ("Could not inflate generic method due to %s", mono_error_get_message (&error)); return res; } /** * mono_class_inflate_generic_method_full_checked: * Same as mono_class_inflate_generic_method_full but return failure using @error. */ MonoMethod* mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context, MonoError *error) { MonoMethod *result; MonoMethodInflated *iresult, *cached; MonoMethodSignature *sig; MonoGenericContext tmp_context; gboolean is_mb_open = FALSE; mono_error_init (error); /* The `method' has already been instantiated before => we need to peel out the instantiation and create a new context */ while (method->is_inflated) { MonoGenericContext *method_context = mono_method_get_context (method); MonoMethodInflated *imethod = (MonoMethodInflated *) method; tmp_context = inflate_generic_context (method_context, context, error); if (!mono_error_ok (error)) return NULL; context = &tmp_context; if (mono_metadata_generic_context_equal (method_context, context)) return method; method = imethod->declaring; } /* * A method only needs to be inflated if the context has argument for which it is * parametric. Eg: * * class Foo { void Bar(); } - doesn't need to be inflated if only mvars' are supplied * class Foo { void Bar (); } - doesn't need to be if only vars' are supplied * */ if (!((method->is_generic && context->method_inst) || (method->klass->generic_container && context->class_inst))) return method; /* * The reason for this hack is to fix the behavior of inflating generic methods that come from a MethodBuilder. * What happens is that instantiating a generic MethodBuilder with its own arguments should create a diferent object. * This is opposite to the way non-SRE MethodInfos behave. * * This happens, for example, when we want to emit a recursive generic method. Given the following C# code: * * void Example () { * Example (); * } * * In Example, the method token must be encoded as: "void Example()" * * The reference to the first generic argument, "!!0", must be explicit otherwise it won't be inflated * properly. To get that we need to inflate the MethodBuilder with its own arguments. * * On the other hand, inflating a non-SRE generic method with its own arguments should * return itself. For example: * * MethodInfo m = ... //m is a generic method definition * MethodInfo res = m.MakeGenericMethod (m.GetGenericArguments ()); * res == m * * To allow such scenarios we must allow inflation of MethodBuilder to happen in a diferent way than * what happens with regular methods. * * There is one last touch to this madness, once a TypeBuilder is finished, IOW CreateType() is called, * everything should behave like a regular type or method. * */ is_mb_open = method->is_generic && method->klass->image->dynamic && !method->klass->wastypebuilder && /* that is a MethodBuilder from an unfinished TypeBuilder */ context->method_inst == mono_method_get_generic_container (method)->context.method_inst; /* and it's been instantiated with its own arguments. */ iresult = g_new0 (MonoMethodInflated, 1); iresult->context = *context; iresult->declaring = method; iresult->method.method.is_mb_open = is_mb_open; if (!context->method_inst && method->is_generic) iresult->context.method_inst = mono_method_get_generic_container (method)->context.method_inst; if (!context->class_inst) { g_assert (!iresult->declaring->klass->generic_class); if (iresult->declaring->klass->generic_container) iresult->context.class_inst = iresult->declaring->klass->generic_container->context.class_inst; else if (iresult->declaring->klass->generic_class) iresult->context.class_inst = iresult->declaring->klass->generic_class->context.class_inst; } mono_loader_lock (); cached = mono_method_inflated_lookup (iresult, FALSE); if (cached) { mono_loader_unlock (); g_free (iresult); return (MonoMethod*)cached; } mono_stats.inflated_method_count++; inflated_methods_size += sizeof (MonoMethodInflated); sig = mono_method_signature (method); if (!sig) { char *name = mono_type_get_full_name (method->klass); mono_error_set_bad_image (error, method->klass->image, "Could not resolve signature of method %s:%s", name, method->name); g_free (name); goto fail; } if (sig->pinvoke) { memcpy (&iresult->method.pinvoke, method, sizeof (MonoMethodPInvoke)); } else { memcpy (&iresult->method.method, method, sizeof (MonoMethod)); } result = (MonoMethod *) iresult; result->is_inflated = TRUE; result->is_generic = FALSE; result->sre_method = FALSE; result->signature = NULL; result->is_mb_open = is_mb_open; if (!context->method_inst) { /* Set the generic_container of the result to the generic_container of method */ MonoGenericContainer *generic_container = mono_method_get_generic_container (method); if (generic_container) { result->is_generic = 1; mono_method_set_generic_container (result, generic_container); } } if (!klass_hint || !klass_hint->generic_class || klass_hint->generic_class->container_class != method->klass || klass_hint->generic_class->context.class_inst != context->class_inst) klass_hint = NULL; if (method->klass->generic_container) result->klass = klass_hint; if (!result->klass) { MonoType *inflated = inflate_generic_type (NULL, &method->klass->byval_arg, context, error); if (!mono_error_ok (error)) goto fail; result->klass = inflated ? mono_class_from_mono_type (inflated) : method->klass; if (inflated) mono_metadata_free_type (inflated); } /* * FIXME: This should hold, but it doesn't: * * if (result->is_inflated && mono_method_get_context (result)->method_inst && * mono_method_get_context (result)->method_inst == mono_method_get_generic_container (((MonoMethodInflated*)result)->declaring)->context.method_inst) { * g_assert (result->is_generic); * } * * Fixing this here causes other things to break, hence a very * ugly hack in mini-trampolines.c - see * is_generic_method_definition(). */ mono_method_inflated_lookup (iresult, TRUE); mono_loader_unlock (); return result; fail: mono_loader_unlock (); g_free (iresult); return NULL; } /** * mono_get_inflated_method: * * Obsolete. We keep it around since it's mentioned in the public API. */ MonoMethod* mono_get_inflated_method (MonoMethod *method) { return method; } /* * mono_method_get_context_general: * @method: a method * @uninflated: handle uninflated methods? * * Returns the generic context of a method or NULL if it doesn't have * one. For an inflated method that's the context stored in the * method. Otherwise it's in the method's generic container or in the * generic container of the method's class. */ MonoGenericContext* mono_method_get_context_general (MonoMethod *method, gboolean uninflated) { if (method->is_inflated) { MonoMethodInflated *imethod = (MonoMethodInflated *) method; return &imethod->context; } if (!uninflated) return NULL; if (method->is_generic) return &(mono_method_get_generic_container (method)->context); if (method->klass->generic_container) return &method->klass->generic_container->context; return NULL; } /* * mono_method_get_context: * @method: a method * * Returns the generic context for method if it's inflated, otherwise * NULL. */ MonoGenericContext* mono_method_get_context (MonoMethod *method) { return mono_method_get_context_general (method, FALSE); } /* * mono_method_get_generic_container: * * Returns the generic container of METHOD, which should be a generic method definition. * Returns NULL if METHOD is not a generic method definition. * LOCKING: Acquires the loader lock. */ MonoGenericContainer* mono_method_get_generic_container (MonoMethod *method) { MonoGenericContainer *container; if (!method->is_generic) return NULL; container = mono_image_property_lookup (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER); g_assert (container); return container; } /* * mono_method_set_generic_container: * * Sets the generic container of METHOD to CONTAINER. * LOCKING: Acquires the loader lock. */ void mono_method_set_generic_container (MonoMethod *method, MonoGenericContainer* container) { g_assert (method->is_generic); mono_image_property_insert (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER, container); } /** * mono_class_find_enum_basetype: * @class: The enum class * * Determine the basetype of an enum by iterating through its fields. We do this * in a separate function since it is cheaper than calling mono_class_setup_fields. */ static MonoType* mono_class_find_enum_basetype (MonoClass *class) { MonoGenericContainer *container = NULL; MonoImage *m = class->image; const int top = class->field.count; int i; g_assert (class->enumtype); if (class->generic_container) container = class->generic_container; else if (class->generic_class) { MonoClass *gklass = class->generic_class->container_class; container = gklass->generic_container; g_assert (container); } /* * Fetch all the field information. */ for (i = 0; i < top; i++){ const char *sig; guint32 cols [MONO_FIELD_SIZE]; int idx = class->field.first + i; MonoType *ftype; /* class->field.first and idx points into the fieldptr table */ mono_metadata_decode_table_row (m, MONO_TABLE_FIELD, idx, cols, MONO_FIELD_SIZE); if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_STATIC) //no need to decode static fields continue; if (!mono_verifier_verify_field_signature (class->image, cols [MONO_FIELD_SIGNATURE], NULL)) return NULL; sig = mono_metadata_blob_heap (m, cols [MONO_FIELD_SIGNATURE]); mono_metadata_decode_value (sig, &sig); /* FIELD signature == 0x06 */ if (*sig != 0x06) return NULL; ftype = mono_metadata_parse_type_full (m, container, MONO_PARSE_FIELD, cols [MONO_FIELD_FLAGS], sig + 1, &sig); if (!ftype) return NULL; if (class->generic_class) { //FIXME do we leak here? ftype = mono_class_inflate_generic_type (ftype, mono_class_get_context (class)); ftype->attrs = cols [MONO_FIELD_FLAGS]; } return ftype; } return NULL; } /* * Checks for MonoClass::exception_type without resolving all MonoType's into MonoClass'es */ static gboolean mono_type_has_exceptions (MonoType *type) { switch (type->type) { case MONO_TYPE_CLASS: case MONO_TYPE_VALUETYPE: case MONO_TYPE_SZARRAY: return type->data.klass->exception_type; case MONO_TYPE_ARRAY: return type->data.array->eklass->exception_type; case MONO_TYPE_GENERICINST: return mono_generic_class_get_class (type->data.generic_class)->exception_type; } return FALSE; } /* * mono_class_alloc: * * Allocate memory for some data belonging to CLASS, either from its image's mempool, * or from the heap. */ static gpointer mono_class_alloc (MonoClass *class, int size) { if (class->generic_class) return mono_image_set_alloc (class->generic_class->owner, size); else return mono_image_alloc (class->image, size); } static gpointer mono_class_alloc0 (MonoClass *class, int size) { gpointer res; res = mono_class_alloc (class, size); memset (res, 0, size); return res; } #define mono_class_new0(class,struct_type, n_structs) \ ((struct_type *) mono_class_alloc0 ((class), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) /** * mono_class_setup_basic_field_info: * @class: The class to initialize * * Initializes the class->fields. * LOCKING: Assumes the loader lock is held. */ static void mono_class_setup_basic_field_info (MonoClass *class) { MonoClassField *field; MonoClass *gtd; MonoImage *image; int i, top; if (class->fields) return; gtd = class->generic_class ? mono_class_get_generic_type_definition (class) : NULL; image = class->image; top = class->field.count; if (class->generic_class && class->generic_class->container_class->image->dynamic && !class->generic_class->container_class->wastypebuilder) { /* * This happens when a generic instance of an unfinished generic typebuilder * is used as an element type for creating an array type. We can't initialize * the fields of this class using the fields of gklass, since gklass is not * finished yet, fields could be added to it later. */ return; } if (gtd) { mono_class_setup_basic_field_info (gtd); top = gtd->field.count; class->field.first = gtd->field.first; class->field.count = gtd->field.count; } class->fields = mono_class_alloc0 (class, sizeof (MonoClassField) * top); /* * Fetch all the field information. */ for (i = 0; i < top; i++){ field = &class->fields [i]; field->parent = class; if (gtd) { field->name = mono_field_get_name (>d->fields [i]); } else { int idx = class->field.first + i; /* class->field.first and idx points into the fieldptr table */ guint32 name_idx = mono_metadata_decode_table_row_col (image, MONO_TABLE_FIELD, idx, MONO_FIELD_NAME); /* The name is needed for fieldrefs */ field->name = mono_metadata_string_heap (image, name_idx); } } } /** * mono_class_setup_fields: * @class: The class to initialize * * Initializes the class->fields. * LOCKING: Assumes the loader lock is held. */ static void mono_class_setup_fields (MonoClass *class) { MonoError error; MonoImage *m = class->image; int top; guint32 layout = class->flags & TYPE_ATTRIBUTE_LAYOUT_MASK; int i, blittable = TRUE; guint32 real_size = 0; guint32 packing_size = 0; gboolean explicit_size; MonoClassField *field; MonoGenericContainer *container = NULL; MonoClass *gtd = class->generic_class ? mono_class_get_generic_type_definition (class) : NULL; if (class->size_inited) return; if (class->generic_class && class->generic_class->container_class->image->dynamic && !class->generic_class->container_class->wastypebuilder) { /* * This happens when a generic instance of an unfinished generic typebuilder * is used as an element type for creating an array type. We can't initialize * the fields of this class using the fields of gklass, since gklass is not * finished yet, fields could be added to it later. */ return; } mono_class_setup_basic_field_info (class); top = class->field.count; if (gtd) { mono_class_setup_fields (gtd); if (gtd->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } } class->instance_size = 0; if (!class->rank) class->sizes.class_size = 0; if (class->parent) { /* For generic instances, class->parent might not have been initialized */ mono_class_init (class->parent); if (!class->parent->size_inited) { mono_class_setup_fields (class->parent); if (class->parent->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } } class->instance_size += class->parent->instance_size; class->min_align = class->parent->min_align; /* we use |= since it may have been set already */ class->has_references |= class->parent->has_references; blittable = class->parent->blittable; } else { class->instance_size = sizeof (MonoObject); class->min_align = 1; } /* We can't really enable 16 bytes alignment until the GC supports it. The whole layout/instance size code must be reviewed because we do alignment calculation in terms of the boxed instance, which leads to unexplainable holes at the beginning of an object embedding a simd type. Bug #506144 is an example of this issue. if (class->simd_type) class->min_align = 16; */ /* Get the real size */ explicit_size = mono_metadata_packing_from_typedef (class->image, class->type_token, &packing_size, &real_size); if (explicit_size) { if ((packing_size & 0xfffffff0) != 0) { char *err_msg = g_strdup_printf ("Could not load struct '%s' with packing size %d >= 16", class->name, packing_size); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, err_msg); return; } class->packing_size = packing_size; real_size += class->instance_size; } if (!top) { if (explicit_size && real_size) { class->instance_size = MAX (real_size, class->instance_size); } class->size_inited = 1; class->blittable = blittable; return; } if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) blittable = FALSE; /* Prevent infinite loops if the class references itself */ class->size_inited = 1; if (class->generic_container) { container = class->generic_container; } else if (gtd) { container = gtd->generic_container; g_assert (container); } /* * Fetch all the field information. */ for (i = 0; i < top; i++){ int idx = class->field.first + i; field = &class->fields [i]; field->parent = class; if (!field->type) { mono_field_resolve_type (field, &error); if (!mono_error_ok (&error)) { /*mono_field_resolve_type already failed class*/ mono_error_cleanup (&error); return; } if (!field->type) g_error ("could not resolve %s:%s\n", mono_type_get_full_name(class), field->name); g_assert (field->type); } if (mono_field_is_deleted (field)) continue; if (gtd) { MonoClassField *gfield = >d->fields [i]; field->offset = gfield->offset; } else { if (layout == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { guint32 offset; mono_metadata_field_info (m, idx, &offset, NULL, NULL); field->offset = offset; if (field->offset == (guint32)-1 && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Missing field layout info for %s", field->name)); break; } if (field->offset < -1) { /*-1 is used to encode special static fields */ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Invalid negative field offset %d for %s", field->offset, field->name)); break; } } } /* Only do these checks if we still think this type is blittable */ if (blittable && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { if (field->type->byref || MONO_TYPE_IS_REFERENCE (field->type)) { blittable = FALSE; } else { MonoClass *field_class = mono_class_from_mono_type (field->type); if (field_class) { mono_class_setup_fields (field_class); if (field_class->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); break; } } if (!field_class || !field_class->blittable) blittable = FALSE; } } if (class->enumtype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { class->cast_class = class->element_class = mono_class_from_mono_type (field->type); blittable = class->element_class->blittable; } if (mono_type_has_exceptions (field->type)) { char *class_name = mono_type_get_full_name (class); char *type_name = mono_type_full_name (field->type); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); g_warning ("Invalid type %s for instance field %s:%s", type_name, class_name, field->name); g_free (class_name); g_free (type_name); break; } /* The def_value of fields is compute lazily during vtable creation */ } if (class == mono_defaults.string_class) blittable = FALSE; class->blittable = blittable; if (class->enumtype && !mono_class_enum_basetype (class)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } if (explicit_size && real_size) { class->instance_size = MAX (real_size, class->instance_size); } if (class->exception_type) return; mono_class_layout_fields (class); /*valuetypes can't be neither bigger than 1Mb or empty. */ if (class->valuetype && (class->instance_size <= 0 || class->instance_size > (0x100000 + sizeof (MonoObject)))) mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); } /** * mono_class_setup_fields_locking: * @class: The class to initialize * * Initializes the class->fields array of fields. * Aquires the loader lock. */ static void mono_class_setup_fields_locking (MonoClass *class) { mono_loader_lock (); mono_class_setup_fields (class); mono_loader_unlock (); } /* * mono_class_has_references: * * Returns whenever @klass->has_references is set, initializing it if needed. * Aquires the loader lock. */ static gboolean mono_class_has_references (MonoClass *klass) { if (klass->init_pending) { /* Be conservative */ return TRUE; } else { mono_class_init (klass); return klass->has_references; } } /* * mono_type_get_basic_type_from_generic: * @type: a type * * Returns a closed type corresponding to the possibly open type * passed to it. */ MonoType* mono_type_get_basic_type_from_generic (MonoType *type) { /* When we do generic sharing we let type variables stand for reference types. */ if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) return &mono_defaults.object_class->byval_arg; return type; } /* * mono_class_layout_fields: * @class: a class * * Compute the placement of fields inside an object or struct, according to * the layout rules and set the following fields in @class: * - has_references (if the class contains instance references firled or structs that contain references) * - has_static_refs (same, but for static fields) * - instance_size (size of the object in memory) * - class_size (size needed for the static fields) * - size_inited (flag set when the instance_size is set) * * LOCKING: this is supposed to be called with the loader lock held. */ void mono_class_layout_fields (MonoClass *class) { int i; const int top = class->field.count; guint32 layout = class->flags & TYPE_ATTRIBUTE_LAYOUT_MASK; guint32 pass, passes, real_size; gboolean gc_aware_layout = FALSE; MonoClassField *field; /* * When we do generic sharing we need to have layout * information for open generic classes (either with a generic * context containing type variables or with a generic * container), so we don't return in that case anymore. */ /* * Enable GC aware auto layout: in this mode, reference * fields are grouped together inside objects, increasing collector * performance. * Requires that all classes whose layout is known to native code be annotated * with [StructLayout (LayoutKind.Sequential)] * Value types have gc_aware_layout disabled by default, as per * what the default is for other runtimes. */ /* corlib is missing [StructLayout] directives in many places */ if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) { if (class->byval_arg.type != MONO_TYPE_VALUETYPE) gc_aware_layout = TRUE; } /* Compute klass->has_references */ /* * Process non-static fields first, since static fields might recursively * refer to the class itself. */ for (i = 0; i < top; i++) { MonoType *ftype; field = &class->fields [i]; if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) class->has_references = TRUE; } } for (i = 0; i < top; i++) { MonoType *ftype; field = &class->fields [i]; if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) { ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) class->has_static_refs = TRUE; } } for (i = 0; i < top; i++) { MonoType *ftype; field = &class->fields [i]; ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) { if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) class->has_static_refs = TRUE; else class->has_references = TRUE; } } /* * Compute field layout and total size (not considering static fields) */ switch (layout) { case TYPE_ATTRIBUTE_AUTO_LAYOUT: case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT: if (gc_aware_layout) passes = 2; else passes = 1; if (layout != TYPE_ATTRIBUTE_AUTO_LAYOUT) passes = 1; if (class->parent) { mono_class_setup_fields (class->parent); if (class->parent->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } real_size = class->parent->instance_size; } else { real_size = sizeof (MonoObject); } for (pass = 0; pass < passes; ++pass) { for (i = 0; i < top; i++){ gint32 align; guint32 size; MonoType *ftype; field = &class->fields [i]; if (mono_field_is_deleted (field)) continue; if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) continue; ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); if (gc_aware_layout) { if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) { if (pass == 1) continue; } else { if (pass == 0) continue; } } if ((top == 1) && (class->instance_size == sizeof (MonoObject)) && (strcmp (mono_field_get_name (field), "$PRIVATE$") == 0)) { /* This field is a hack inserted by MCS to empty structures */ continue; } size = mono_type_size (field->type, &align); /* FIXME (LAMESPEC): should we also change the min alignment according to pack? */ align = class->packing_size ? MIN (class->packing_size, align): align; /* if the field has managed references, we need to force-align it * see bug #77788 */ if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) align = MAX (align, sizeof (gpointer)); class->min_align = MAX (align, class->min_align); field->offset = real_size; if (align) { field->offset += align - 1; field->offset &= ~(align - 1); } /*TypeBuilders produce all sort of weird things*/ g_assert (class->image->dynamic || field->offset > 0); real_size = field->offset + size; } class->instance_size = MAX (real_size, class->instance_size); if (class->instance_size & (class->min_align - 1)) { class->instance_size += class->min_align - 1; class->instance_size &= ~(class->min_align - 1); } } break; case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: real_size = 0; for (i = 0; i < top; i++) { gint32 align; guint32 size; MonoType *ftype; field = &class->fields [i]; /* * There must be info about all the fields in a type if it * uses explicit layout. */ if (mono_field_is_deleted (field)) continue; if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) continue; size = mono_type_size (field->type, &align); class->min_align = MAX (align, class->min_align); /* * When we get here, field->offset is already set by the * loader (for either runtime fields or fields loaded from metadata). * The offset is from the start of the object: this works for both * classes and valuetypes. */ field->offset += sizeof (MonoObject); ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); if (MONO_TYPE_IS_REFERENCE (ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && mono_class_has_references (mono_class_from_mono_type (ftype))))) { if (field->offset % sizeof (gpointer)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); } } /* * Calc max size. */ real_size = MAX (real_size, size + field->offset); } class->instance_size = MAX (real_size, class->instance_size); break; } if (layout != TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { /* * For small structs, set min_align to at least the struct size to improve * performance, and since the JIT memset/memcpy code assumes this and generates * unaligned accesses otherwise. See #78990 for a testcase. */ if (class->instance_size <= sizeof (MonoObject) + sizeof (gpointer)) class->min_align = MAX (class->min_align, class->instance_size - sizeof (MonoObject)); } class->size_inited = 1; /* * Compute static field layout and size */ for (i = 0; i < top; i++){ gint32 align; guint32 size; field = &class->fields [i]; if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC) || field->type->attrs & FIELD_ATTRIBUTE_LITERAL) continue; if (mono_field_is_deleted (field)) continue; if (mono_type_has_exceptions (field->type)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); break; } size = mono_type_size (field->type, &align); field->offset = class->sizes.class_size; /*align is always non-zero here*/ field->offset += align - 1; field->offset &= ~(align - 1); class->sizes.class_size = field->offset + size; } } static MonoMethod* create_array_method (MonoClass *class, const char *name, MonoMethodSignature *sig) { MonoMethod *method; method = (MonoMethod *) mono_image_alloc0 (class->image, sizeof (MonoMethodPInvoke)); method->klass = class; method->flags = METHOD_ATTRIBUTE_PUBLIC; method->iflags = METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL; method->signature = sig; method->name = name; method->slot = -1; /* .ctor */ if (name [0] == '.') { method->flags |= METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME; } else { method->iflags |= METHOD_IMPL_ATTRIBUTE_RUNTIME; } return method; } /* * mono_class_setup_methods: * @class: a class * * Initializes the 'methods' array in the klass. * Calling this method should be avoided if possible since it allocates a lot * of long-living MonoMethod structures. * Methods belonging to an interface are assigned a sequential slot starting * from 0. * * On failure this function sets class->exception_type */ void mono_class_setup_methods (MonoClass *class) { int i; MonoMethod **methods; if (class->methods) return; mono_loader_lock (); if (class->methods) { mono_loader_unlock (); return; } if (class->generic_class) { MonoError error; MonoClass *gklass = class->generic_class->container_class; mono_class_init (gklass); if (!gklass->exception_type) mono_class_setup_methods (gklass); if (gklass->exception_type) { /*FIXME make exception_data less opaque so it's possible to dup it here*/ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Generic type definition failed to load")); mono_loader_unlock (); return; } /* The + 1 makes this always non-NULL to pass the check in mono_class_setup_methods () */ class->method.count = gklass->method.count; methods = mono_class_alloc0 (class, sizeof (MonoMethod*) * (class->method.count + 1)); for (i = 0; i < class->method.count; i++) { methods [i] = mono_class_inflate_generic_method_full_checked ( gklass->methods [i], class, mono_class_get_context (class), &error); if (!mono_error_ok (&error)) { char *method = mono_method_full_name (gklass->methods [i], TRUE); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Could not inflate method %s due to %s", method, mono_error_get_message (&error))); g_free (method); mono_error_cleanup (&error); mono_loader_unlock (); return; } } } else if (class->rank) { MonoError error; MonoMethod *amethod; MonoMethodSignature *sig; int count_generic = 0, first_generic = 0; int method_num = 0; class->method.count = 3 + (class->rank > 1? 2: 1); mono_class_setup_interfaces (class, &error); g_assert (mono_error_ok (&error)); /*FIXME can this fail for array types?*/ if (class->interface_count) { count_generic = generic_array_methods (class); first_generic = class->method.count; class->method.count += class->interface_count * count_generic; } methods = mono_class_alloc0 (class, sizeof (MonoMethod*) * class->method.count); sig = mono_metadata_signature_alloc (class->image, class->rank); sig->ret = &mono_defaults.void_class->byval_arg; sig->pinvoke = TRUE; sig->hasthis = TRUE; for (i = 0; i < class->rank; ++i) sig->params [i] = &mono_defaults.int32_class->byval_arg; amethod = create_array_method (class, ".ctor", sig); methods [method_num++] = amethod; if (class->rank > 1) { sig = mono_metadata_signature_alloc (class->image, class->rank * 2); sig->ret = &mono_defaults.void_class->byval_arg; sig->pinvoke = TRUE; sig->hasthis = TRUE; for (i = 0; i < class->rank * 2; ++i) sig->params [i] = &mono_defaults.int32_class->byval_arg; amethod = create_array_method (class, ".ctor", sig); methods [method_num++] = amethod; } /* element Get (idx11, [idx2, ...]) */ sig = mono_metadata_signature_alloc (class->image, class->rank); sig->ret = &class->element_class->byval_arg; sig->pinvoke = TRUE; sig->hasthis = TRUE; for (i = 0; i < class->rank; ++i) sig->params [i] = &mono_defaults.int32_class->byval_arg; amethod = create_array_method (class, "Get", sig); methods [method_num++] = amethod; /* element& Address (idx11, [idx2, ...]) */ sig = mono_metadata_signature_alloc (class->image, class->rank); sig->ret = &class->element_class->this_arg; sig->pinvoke = TRUE; sig->hasthis = TRUE; for (i = 0; i < class->rank; ++i) sig->params [i] = &mono_defaults.int32_class->byval_arg; amethod = create_array_method (class, "Address", sig); methods [method_num++] = amethod; /* void Set (idx11, [idx2, ...], element) */ sig = mono_metadata_signature_alloc (class->image, class->rank + 1); sig->ret = &mono_defaults.void_class->byval_arg; sig->pinvoke = TRUE; sig->hasthis = TRUE; for (i = 0; i < class->rank; ++i) sig->params [i] = &mono_defaults.int32_class->byval_arg; sig->params [i] = &class->element_class->byval_arg; amethod = create_array_method (class, "Set", sig); methods [method_num++] = amethod; for (i = 0; i < class->interface_count; i++) setup_generic_array_ifaces (class, class->interfaces [i], methods, first_generic + i * count_generic); } else { methods = mono_class_alloc (class, sizeof (MonoMethod*) * class->method.count); for (i = 0; i < class->method.count; ++i) { int idx = mono_metadata_translate_token_index (class->image, MONO_TABLE_METHOD, class->method.first + i + 1); methods [i] = mono_get_method (class->image, MONO_TOKEN_METHOD_DEF | idx, class); } } if (MONO_CLASS_IS_INTERFACE (class)) { int slot = 0; /*Only assign slots to virtual methods as interfaces are allowed to have static methods.*/ for (i = 0; i < class->method.count; ++i) { if (methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL) methods [i]->slot = slot++; } } /* Needed because of the double-checking locking pattern */ mono_memory_barrier (); class->methods = methods; if (mono_debugger_class_loaded_methods_func) mono_debugger_class_loaded_methods_func (class); mono_loader_unlock (); } /* * mono_class_get_method_by_index: * * Returns class->methods [index], initializing class->methods if neccesary. * * LOCKING: Acquires the loader lock. */ MonoMethod* mono_class_get_method_by_index (MonoClass *class, int index) { /* Avoid calling setup_methods () if possible */ if (class->generic_class && !class->methods) { MonoClass *gklass = class->generic_class->container_class; MonoMethod *m; m = mono_class_inflate_generic_method_full ( gklass->methods [index], class, mono_class_get_context (class)); /* * If setup_methods () is called later for this class, no duplicates are created, * since inflate_generic_method guarantees that only one instance of a method * is created for each context. */ /* mono_class_setup_methods (class); g_assert (m == class->methods [index]); */ return m; } else { mono_class_setup_methods (class); if (class->exception_type) /*FIXME do proper error handling*/ return NULL; g_assert (index >= 0 && index < class->method.count); return class->methods [index]; } } /* * mono_class_get_inflated_method: * * Given an inflated class CLASS and a method METHOD which should be a method of * CLASS's generic definition, return the inflated method corresponding to METHOD. */ MonoMethod* mono_class_get_inflated_method (MonoClass *class, MonoMethod *method) { MonoClass *gklass = class->generic_class->container_class; int i; g_assert (method->klass == gklass); mono_class_setup_methods (gklass); g_assert (!gklass->exception_type); /*FIXME do proper error handling*/ for (i = 0; i < gklass->method.count; ++i) { if (gklass->methods [i] == method) { if (class->methods) return class->methods [i]; else return mono_class_inflate_generic_method_full (gklass->methods [i], class, mono_class_get_context (class)); } } return NULL; } /* * mono_class_get_vtable_entry: * * Returns class->vtable [offset], computing it if neccesary. Returns NULL on failure. * LOCKING: Acquires the loader lock. */ MonoMethod* mono_class_get_vtable_entry (MonoClass *class, int offset) { MonoMethod *m; if (class->rank == 1) { /* * szarrays do not overwrite any methods of Array, so we can avoid * initializing their vtables in some cases. */ mono_class_setup_vtable (class->parent); if (offset < class->parent->vtable_size) return class->parent->vtable [offset]; } if (class->generic_class) { MonoClass *gklass = class->generic_class->container_class; mono_class_setup_vtable (gklass); m = gklass->vtable [offset]; m = mono_class_inflate_generic_method_full (m, class, mono_class_get_context (class)); } else { mono_class_setup_vtable (class); if (class->exception_type) return NULL; m = class->vtable [offset]; } return m; } /* * mono_class_get_vtable_size: * * Return the vtable size for KLASS. */ int mono_class_get_vtable_size (MonoClass *klass) { mono_class_setup_vtable (klass); return klass->vtable_size; } /*This method can fail the class.*/ static void mono_class_setup_properties (MonoClass *class) { guint startm, endm, i, j; guint32 cols [MONO_PROPERTY_SIZE]; MonoTableInfo *msemt = &class->image->tables [MONO_TABLE_METHODSEMANTICS]; MonoProperty *properties; guint32 last; if (class->ext && class->ext->properties) return; mono_loader_lock (); if (class->ext && class->ext->properties) { mono_loader_unlock (); return; } mono_class_alloc_ext (class); if (class->generic_class) { MonoClass *gklass = class->generic_class->container_class; mono_class_init (gklass); mono_class_setup_properties (gklass); if (gklass->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Generic type definition failed to load")); mono_loader_unlock (); return; } class->ext->property = gklass->ext->property; properties = mono_class_new0 (class, MonoProperty, class->ext->property.count + 1); for (i = 0; i < class->ext->property.count; i++) { MonoProperty *prop = &properties [i]; *prop = gklass->ext->properties [i]; if (prop->get) prop->get = mono_class_inflate_generic_method_full ( prop->get, class, mono_class_get_context (class)); if (prop->set) prop->set = mono_class_inflate_generic_method_full ( prop->set, class, mono_class_get_context (class)); prop->parent = class; } } else { int first = mono_metadata_properties_from_typedef (class->image, mono_metadata_token_index (class->type_token) - 1, &last); int count = last - first; if (count) { mono_class_setup_methods (class); if (class->exception_type) { mono_loader_unlock (); return; } } class->ext->property.first = first; class->ext->property.count = count; properties = mono_class_alloc0 (class, sizeof (MonoProperty) * count); for (i = first; i < last; ++i) { mono_metadata_decode_table_row (class->image, MONO_TABLE_PROPERTY, i, cols, MONO_PROPERTY_SIZE); properties [i - first].parent = class; properties [i - first].attrs = cols [MONO_PROPERTY_FLAGS]; properties [i - first].name = mono_metadata_string_heap (class->image, cols [MONO_PROPERTY_NAME]); startm = mono_metadata_methods_from_property (class->image, i, &endm); for (j = startm; j < endm; ++j) { MonoMethod *method; mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE); if (class->image->uncompressed_metadata) /* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */ method = mono_get_method (class->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], class); else method = class->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - class->method.first]; switch (cols [MONO_METHOD_SEMA_SEMANTICS]) { case METHOD_SEMANTIC_SETTER: properties [i - first].set = method; break; case METHOD_SEMANTIC_GETTER: properties [i - first].get = method; break; default: break; } } } } /*Flush any pending writes as we do double checked locking on class->properties */ mono_memory_barrier (); /* Leave this assignment as the last op in the function */ class->ext->properties = properties; mono_loader_unlock (); } static MonoMethod** inflate_method_listz (MonoMethod **methods, MonoClass *class, MonoGenericContext *context) { MonoMethod **om, **retval; int count; for (om = methods, count = 0; *om; ++om, ++count) ; retval = g_new0 (MonoMethod*, count + 1); count = 0; for (om = methods, count = 0; *om; ++om, ++count) retval [count] = mono_class_inflate_generic_method_full (*om, class, context); return retval; } /*This method can fail the class.*/ static void mono_class_setup_events (MonoClass *class) { int first, count; guint startm, endm, i, j; guint32 cols [MONO_EVENT_SIZE]; MonoTableInfo *msemt = &class->image->tables [MONO_TABLE_METHODSEMANTICS]; guint32 last; MonoEvent *events; if (class->ext && class->ext->events) return; mono_loader_lock (); if (class->ext && class->ext->events) { mono_loader_unlock (); return; } mono_class_alloc_ext (class); if (class->generic_class) { MonoClass *gklass = class->generic_class->container_class; MonoGenericContext *context; mono_class_setup_events (gklass); if (gklass->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Generic type definition failed to load")); mono_loader_unlock (); return; } class->ext->event = gklass->ext->event; class->ext->events = mono_class_new0 (class, MonoEvent, class->ext->event.count); if (class->ext->event.count) context = mono_class_get_context (class); for (i = 0; i < class->ext->event.count; i++) { MonoEvent *event = &class->ext->events [i]; MonoEvent *gevent = &gklass->ext->events [i]; event->parent = class; event->name = gevent->name; event->add = gevent->add ? mono_class_inflate_generic_method_full (gevent->add, class, context) : NULL; event->remove = gevent->remove ? mono_class_inflate_generic_method_full (gevent->remove, class, context) : NULL; event->raise = gevent->raise ? mono_class_inflate_generic_method_full (gevent->raise, class, context) : NULL; #ifndef MONO_SMALL_CONFIG event->other = gevent->other ? inflate_method_listz (gevent->other, class, context) : NULL; #endif event->attrs = gevent->attrs; } mono_loader_unlock (); return; } first = mono_metadata_events_from_typedef (class->image, mono_metadata_token_index (class->type_token) - 1, &last); count = last - first; if (count) { mono_class_setup_methods (class); if (class->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Generic type definition failed to load")); mono_loader_unlock (); return; } } class->ext->event.first = first; class->ext->event.count = count; events = mono_class_alloc0 (class, sizeof (MonoEvent) * class->ext->event.count); for (i = first; i < last; ++i) { MonoEvent *event = &events [i - first]; mono_metadata_decode_table_row (class->image, MONO_TABLE_EVENT, i, cols, MONO_EVENT_SIZE); event->parent = class; event->attrs = cols [MONO_EVENT_FLAGS]; event->name = mono_metadata_string_heap (class->image, cols [MONO_EVENT_NAME]); startm = mono_metadata_methods_from_event (class->image, i, &endm); for (j = startm; j < endm; ++j) { MonoMethod *method; mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE); if (class->image->uncompressed_metadata) /* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */ method = mono_get_method (class->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], class); else method = class->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - class->method.first]; switch (cols [MONO_METHOD_SEMA_SEMANTICS]) { case METHOD_SEMANTIC_ADD_ON: event->add = method; break; case METHOD_SEMANTIC_REMOVE_ON: event->remove = method; break; case METHOD_SEMANTIC_FIRE: event->raise = method; break; case METHOD_SEMANTIC_OTHER: { #ifndef MONO_SMALL_CONFIG int n = 0; if (event->other == NULL) { event->other = g_new0 (MonoMethod*, 2); } else { while (event->other [n]) n++; event->other = g_realloc (event->other, (n + 2) * sizeof (MonoMethod*)); } event->other [n] = method; /* NULL terminated */ event->other [n + 1] = NULL; #endif break; } default: break; } } } /*Flush any pending writes as we do double checked locking on class->properties */ mono_memory_barrier (); /* Leave this assignment as the last op in the function */ class->ext->events = events; mono_loader_unlock (); } /* * Global pool of interface IDs, represented as a bitset. * LOCKING: this is supposed to be accessed with the loader lock held. */ static MonoBitSet *global_interface_bitset = NULL; /* * mono_unload_interface_ids: * @bitset: bit set of interface IDs * * When an image is unloaded, the interface IDs associated with * the image are put back in the global pool of IDs so the numbers * can be reused. */ void mono_unload_interface_ids (MonoBitSet *bitset) { mono_loader_lock (); mono_bitset_sub (global_interface_bitset, bitset); mono_loader_unlock (); } void mono_unload_interface_id (MonoClass *class) { if (global_interface_bitset && class->interface_id) { mono_loader_lock (); mono_bitset_clear (global_interface_bitset, class->interface_id); mono_loader_unlock (); } } /* * mono_get_unique_iid: * @class: interface * * Assign a unique integer ID to the interface represented by @class. * The ID will positive and as small as possible. * LOCKING: this is supposed to be called with the loader lock held. * Returns: the new ID. */ static guint mono_get_unique_iid (MonoClass *class) { int iid; g_assert (MONO_CLASS_IS_INTERFACE (class)); if (!global_interface_bitset) { global_interface_bitset = mono_bitset_new (128, 0); } iid = mono_bitset_find_first_unset (global_interface_bitset, -1); if (iid < 0) { int old_size = mono_bitset_size (global_interface_bitset); MonoBitSet *new_set = mono_bitset_clone (global_interface_bitset, old_size * 2); mono_bitset_free (global_interface_bitset); global_interface_bitset = new_set; iid = old_size; } mono_bitset_set (global_interface_bitset, iid); /* set the bit also in the per-image set */ if (!class->generic_class) { if (class->image->interface_bitset) { if (iid >= mono_bitset_size (class->image->interface_bitset)) { MonoBitSet *new_set = mono_bitset_clone (class->image->interface_bitset, iid + 1); mono_bitset_free (class->image->interface_bitset); class->image->interface_bitset = new_set; } } else { class->image->interface_bitset = mono_bitset_new (iid + 1, 0); } mono_bitset_set (class->image->interface_bitset, iid); } #ifndef MONO_SMALL_CONFIG if (mono_print_vtable) { int generic_id; char *type_name = mono_type_full_name (&class->byval_arg); if (class->generic_class && !class->generic_class->context.class_inst->is_open) { generic_id = class->generic_class->context.class_inst->id; g_assert (generic_id != 0); } else { generic_id = 0; } printf ("Interface: assigned id %d to %s|%s|%d\n", iid, class->image->name, type_name, generic_id); g_free (type_name); } #endif g_assert (iid <= 65535); return iid; } static void collect_implemented_interfaces_aux (MonoClass *klass, GPtrArray **res, MonoError *error) { int i; MonoClass *ic; mono_class_setup_interfaces (klass, error); if (!mono_error_ok (error)) return; for (i = 0; i < klass->interface_count; i++) { ic = klass->interfaces [i]; if (*res == NULL) *res = g_ptr_array_new (); g_ptr_array_add (*res, ic); mono_class_init (ic); if (ic->exception_type) { mono_error_set_type_load_class (error, ic, "Error Loading class"); return; } collect_implemented_interfaces_aux (ic, res, error); if (!mono_error_ok (error)) return; } } GPtrArray* mono_class_get_implemented_interfaces (MonoClass *klass, MonoError *error) { GPtrArray *res = NULL; collect_implemented_interfaces_aux (klass, &res, error); if (!mono_error_ok (error)) { if (res) g_ptr_array_free (res, TRUE); return NULL; } return res; } static int compare_interface_ids (const void *p_key, const void *p_element) { const MonoClass *key = p_key; const MonoClass *element = *(MonoClass**) p_element; return (key->interface_id - element->interface_id); } /*FIXME verify all callers if they should switch to mono_class_interface_offset_with_variance*/ int mono_class_interface_offset (MonoClass *klass, MonoClass *itf) { MonoClass **result = bsearch ( itf, klass->interfaces_packed, klass->interface_offsets_count, sizeof (MonoClass *), compare_interface_ids); if (result) { return klass->interface_offsets_packed [result - (klass->interfaces_packed)]; } else { return -1; } } /* * mono_class_interface_offset_with_variance: * * Return the interface offset of @itf in @klass. Sets @non_exact_match to TRUE if the match required variance check * If @itf is an interface with generic variant arguments, try to find the compatible one. * * Note that this function is responsible for resolving ambiguities. Right now we use whatever ordering interfaces_packed gives us. * * FIXME figure out MS disambiguation rules and fix this function. */ int mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match) { int i = mono_class_interface_offset (klass, itf); *non_exact_match = FALSE; if (i >= 0) return i; if (!mono_class_has_variant_generic_params (itf)) return -1; for (i = 0; i < klass->interface_offsets_count; i++) { if (mono_class_is_variant_compatible (itf, klass->interfaces_packed [i], FALSE)) { *non_exact_match = TRUE; return klass->interface_offsets_packed [i]; } } return -1; } static void print_implemented_interfaces (MonoClass *klass) { char *name; MonoError error; GPtrArray *ifaces = NULL; int i; int ancestor_level = 0; name = mono_type_get_full_name (klass); printf ("Packed interface table for class %s has size %d\n", name, klass->interface_offsets_count); g_free (name); for (i = 0; i < klass->interface_offsets_count; i++) printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i, klass->interfaces_packed [i]->interface_id, klass->interface_offsets_packed [i], klass->interfaces_packed [i]->method.count, klass->interfaces_packed [i]->name_space, klass->interfaces_packed [i]->name ); printf ("Interface flags: "); for (i = 0; i <= klass->max_interface_id; i++) if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i)) printf ("(%d,T)", i); else printf ("(%d,F)", i); printf ("\n"); printf ("Dump interface flags:"); #ifdef COMPRESSED_INTERFACE_BITMAP { const uint8_t* p = klass->interface_bitmap; i = klass->max_interface_id; while (i > 0) { printf (" %d x 00 %02X", p [0], p [1]); i -= p [0] * 8; i -= 8; } } #else for (i = 0; i < ((((klass->max_interface_id + 1) >> 3)) + (((klass->max_interface_id + 1) & 7)? 1 :0)); i++) printf (" %02X", klass->interface_bitmap [i]); #endif printf ("\n"); while (klass != NULL) { printf ("[LEVEL %d] Implemented interfaces by class %s:\n", ancestor_level, klass->name); ifaces = mono_class_get_implemented_interfaces (klass, &error); if (!mono_error_ok (&error)) { printf (" Type failed due to %s\n", mono_error_get_message (&error)); mono_error_cleanup (&error); } else if (ifaces) { for (i = 0; i < ifaces->len; i++) { MonoClass *ic = g_ptr_array_index (ifaces, i); printf (" [UIID %d] interface %s\n", ic->interface_id, ic->name); printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i, ic->interface_id, mono_class_interface_offset (klass, ic), ic->method.count, ic->name_space, ic->name ); } g_ptr_array_free (ifaces, TRUE); } ancestor_level ++; klass = klass->parent; } } static MonoClass* inflate_class_one_arg (MonoClass *gtype, MonoClass *arg0) { MonoType *args [1]; args [0] = &arg0->byval_arg; return mono_class_bind_generic_parameters (gtype, 1, args, FALSE); } static MonoClass* array_class_get_if_rank (MonoClass *class, guint rank) { return rank ? mono_array_class_get (class, rank) : class; } static void fill_valuetype_array_derived_types (MonoClass **valuetype_types, MonoClass *eclass, int rank) { valuetype_types [0] = eclass; if (eclass == mono_defaults.int16_class) valuetype_types [1] = mono_defaults.uint16_class; else if (eclass == mono_defaults.uint16_class) valuetype_types [1] = mono_defaults.int16_class; else if (eclass == mono_defaults.int32_class) valuetype_types [1] = mono_defaults.uint32_class; else if (eclass == mono_defaults.uint32_class) valuetype_types [1] = mono_defaults.int32_class; else if (eclass == mono_defaults.int64_class) valuetype_types [1] = mono_defaults.uint64_class; else if (eclass == mono_defaults.uint64_class) valuetype_types [1] = mono_defaults.int64_class; else if (eclass == mono_defaults.byte_class) valuetype_types [1] = mono_defaults.sbyte_class; else if (eclass == mono_defaults.sbyte_class) valuetype_types [1] = mono_defaults.byte_class; else if (eclass->enumtype && mono_class_enum_basetype (eclass)) valuetype_types [1] = mono_class_from_mono_type (mono_class_enum_basetype (eclass)); } /* this won't be needed once bug #325495 is completely fixed * though we'll need something similar to know which interfaces to allow * in arrays when they'll be lazyly created * * FIXME: System.Array/InternalEnumerator don't need all this interface fabrication machinery. * MS returns diferrent types based on which instance is called. For example: * object obj = new byte[10][]; * Type a = ((IEnumerable)obj).GetEnumerator ().GetType (); * Type b = ((IEnumerable>)obj).GetEnumerator ().GetType (); * a != b ==> true * * Fixing this should kill quite some code, save some bits and improve compatibility. */ static MonoClass** get_implicit_generic_array_interfaces (MonoClass *class, int *num, int *is_enumerator) { MonoClass *eclass = class->element_class; static MonoClass* generic_icollection_class = NULL; static MonoClass* generic_ienumerable_class = NULL; static MonoClass* generic_ienumerator_class = NULL; MonoClass *valuetype_types[2] = { NULL, NULL }; MonoClass **interfaces = NULL; int i, interface_count, real_count, original_rank; int all_interfaces; gboolean internal_enumerator; gboolean eclass_is_valuetype; if (!mono_defaults.generic_ilist_class) { *num = 0; return NULL; } internal_enumerator = FALSE; eclass_is_valuetype = FALSE; original_rank = eclass->rank; if (class->byval_arg.type != MONO_TYPE_SZARRAY) { if (class->generic_class && class->nested_in == mono_defaults.array_class && strcmp (class->name, "InternalEnumerator`1") == 0) { /* * For a Enumerator we need to get the list of interfaces for T. */ eclass = mono_class_from_mono_type (class->generic_class->context.class_inst->type_argv [0]); original_rank = eclass->rank; eclass = eclass->element_class; internal_enumerator = TRUE; *is_enumerator = TRUE; } else { *num = 0; return NULL; } } /* * with this non-lazy impl we can't implement all the interfaces so we do just the minimal stuff * for deep levels of arrays of arrays (string[][] has all the interfaces, string[][][] doesn't) */ all_interfaces = eclass->rank && eclass->element_class->rank? FALSE: TRUE; if (!generic_icollection_class) { generic_icollection_class = mono_class_from_name (mono_defaults.corlib, "System.Collections.Generic", "ICollection`1"); generic_ienumerable_class = mono_class_from_name (mono_defaults.corlib, "System.Collections.Generic", "IEnumerable`1"); generic_ienumerator_class = mono_class_from_name (mono_defaults.corlib, "System.Collections.Generic", "IEnumerator`1"); } mono_class_init (eclass); /* * Arrays in 2.0 need to implement a number of generic interfaces * (IList`1, ICollection`1, IEnumerable`1 for a number of types depending * on the element class). We collect the types needed to build the * instantiations in interfaces at intervals of 3, because 3 are * the generic interfaces needed to implement. */ if (eclass->valuetype) { fill_valuetype_array_derived_types (valuetype_types, eclass, original_rank); /* IList, ICollection, IEnumerable */ real_count = interface_count = valuetype_types [1] ? 6 : 3; if (internal_enumerator) { ++real_count; if (valuetype_types [1]) ++real_count; } interfaces = g_malloc0 (sizeof (MonoClass*) * real_count); interfaces [0] = valuetype_types [0]; if (valuetype_types [1]) interfaces [3] = valuetype_types [1]; eclass_is_valuetype = TRUE; } else { int j; int idepth = eclass->idepth; if (!internal_enumerator) idepth--; // FIXME: This doesn't seem to work/required for generic params if (!(eclass->this_arg.type == MONO_TYPE_VAR || eclass->this_arg.type == MONO_TYPE_MVAR || (eclass->image->dynamic && !eclass->wastypebuilder))) mono_class_setup_interface_offsets (eclass); interface_count = all_interfaces? eclass->interface_offsets_count: eclass->interface_count; /* we add object for interfaces and the supertypes for the other * types. The last of the supertypes is the element class itself which we * already created the explicit interfaces for (so we include it for IEnumerator * and exclude it for arrays). */ if (MONO_CLASS_IS_INTERFACE (eclass)) interface_count++; else interface_count += idepth; if (eclass->rank && eclass->element_class->valuetype) { fill_valuetype_array_derived_types (valuetype_types, eclass->element_class, original_rank); if (valuetype_types [1]) ++interface_count; } /* IList, ICollection, IEnumerable */ interface_count *= 3; real_count = interface_count; if (internal_enumerator) { real_count += (MONO_CLASS_IS_INTERFACE (eclass) ? 1 : idepth) + eclass->interface_offsets_count; if (valuetype_types [1]) ++real_count; } interfaces = g_malloc0 (sizeof (MonoClass*) * real_count); if (MONO_CLASS_IS_INTERFACE (eclass)) { interfaces [0] = mono_defaults.object_class; j = 3; } else { j = 0; for (i = 0; i < idepth; i++) { mono_class_init (eclass->supertypes [i]); interfaces [j] = eclass->supertypes [i]; j += 3; } } if (all_interfaces) { for (i = 0; i < eclass->interface_offsets_count; i++) { interfaces [j] = eclass->interfaces_packed [i]; j += 3; } } else { for (i = 0; i < eclass->interface_count; i++) { interfaces [j] = eclass->interfaces [i]; j += 3; } } if (valuetype_types [1]) { interfaces [j] = array_class_get_if_rank (valuetype_types [1], original_rank); j += 3; } } /* instantiate the generic interfaces */ for (i = 0; i < interface_count; i += 3) { MonoClass *iface = interfaces [i]; interfaces [i + 0] = inflate_class_one_arg (mono_defaults.generic_ilist_class, iface); interfaces [i + 1] = inflate_class_one_arg (generic_icollection_class, iface); interfaces [i + 2] = inflate_class_one_arg (generic_ienumerable_class, iface); } if (internal_enumerator) { int j; /* instantiate IEnumerator */ for (i = 0; i < interface_count; i++) { interfaces [i] = inflate_class_one_arg (generic_ienumerator_class, interfaces [i]); } j = interface_count; if (!eclass_is_valuetype) { if (MONO_CLASS_IS_INTERFACE (eclass)) { interfaces [j] = inflate_class_one_arg (generic_ienumerator_class, mono_defaults.object_class); j ++; } else { for (i = 0; i < eclass->idepth; i++) { interfaces [j] = inflate_class_one_arg (generic_ienumerator_class, eclass->supertypes [i]); j ++; } } for (i = 0; i < eclass->interface_offsets_count; i++) { interfaces [j] = inflate_class_one_arg (generic_ienumerator_class, eclass->interfaces_packed [i]); j ++; } } else { interfaces [j++] = inflate_class_one_arg (generic_ienumerator_class, array_class_get_if_rank (valuetype_types [0], original_rank)); } if (valuetype_types [1]) interfaces [j] = inflate_class_one_arg (generic_ienumerator_class, array_class_get_if_rank (valuetype_types [1], original_rank)); } #if 0 { char *type_name = mono_type_get_name_full (&class->byval_arg, 0); for (i = 0; i < real_count; ++i) { char *name = mono_type_get_name_full (&interfaces [i]->byval_arg, 0); g_print ("%s implements %s\n", type_name, name); g_free (name); } g_free (type_name); } #endif *num = real_count; return interfaces; } static int find_array_interface (MonoClass *klass, const char *name) { int i; for (i = 0; i < klass->interface_count; ++i) { if (strcmp (klass->interfaces [i]->name, name) == 0) return i; } return -1; } /* * Return the number of virtual methods. * Even for interfaces we can't simply return the number of methods as all CLR types are allowed to have static methods. * Return -1 on failure. * FIXME It would be nice if this information could be cached somewhere. */ static int count_virtual_methods (MonoClass *class) { int i, count = 0; guint32 flags; class = mono_class_get_generic_type_definition (class); /*We can find this information by looking at the GTD*/ if (class->methods || !MONO_CLASS_HAS_STATIC_METADATA (class)) { mono_class_setup_methods (class); if (class->exception_type) return -1; for (i = 0; i < class->method.count; ++i) { flags = class->methods [i]->flags; if (flags & METHOD_ATTRIBUTE_VIRTUAL) ++count; } } else { for (i = 0; i < class->method.count; ++i) { flags = mono_metadata_decode_table_row_col (class->image, MONO_TABLE_METHOD, class->method.first + i, MONO_METHOD_FLAGS); if (flags & METHOD_ATTRIBUTE_VIRTUAL) ++count; } } return count; } static int find_interface (int num_ifaces, MonoClass **interfaces_full, MonoClass *ic) { int m, l = 0; if (!num_ifaces) return -1; while (1) { if (l > num_ifaces) return -1; m = (l + num_ifaces) / 2; if (interfaces_full [m] == ic) return m; if (l == num_ifaces) return -1; if (!interfaces_full [m] || interfaces_full [m]->interface_id > ic->interface_id) { num_ifaces = m - 1; } else { l = m + 1; } } } static int find_interface_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic) { int i = find_interface (num_ifaces, interfaces_full, ic); if (ic >= 0) return interface_offsets_full [i]; return -1; } static mono_bool set_interface_and_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic, int offset, mono_bool force_set) { int i = find_interface (num_ifaces, interfaces_full, ic); if (i >= 0) { if (!force_set) return TRUE; interface_offsets_full [i] = offset; return FALSE; } for (i = 0; i < num_ifaces; ++i) { if (interfaces_full [i]) { int end; if (interfaces_full [i]->interface_id < ic->interface_id) continue; end = i + 1; while (end < num_ifaces && interfaces_full [end]) end++; memmove (interfaces_full + i + 1, interfaces_full + i, sizeof (MonoClass*) * (end - i)); memmove (interface_offsets_full + i + 1, interface_offsets_full + i, sizeof (int) * (end - i)); } interfaces_full [i] = ic; interface_offsets_full [i] = offset; break; } return FALSE; } #ifdef COMPRESSED_INTERFACE_BITMAP /* * Compressed interface bitmap design. * * Interface bitmaps take a large amount of memory, because their size is * linear with the maximum interface id assigned in the process (each interface * is assigned a unique id as it is loaded). The number of interface classes * is high because of the many implicit interfaces implemented by arrays (we'll * need to lazy-load them in the future). * Most classes implement a very small number of interfaces, so the bitmap is * sparse. This bitmap needs to be checked by interface casts, so access to the * needed bit must be fast and doable with few jit instructions. * * The current compression format is as follows: * *) it is a sequence of one or more two-byte elements * *) the first byte in the element is the count of empty bitmap bytes * at the current bitmap position * *) the second byte in the element is an actual bitmap byte at the current * bitmap position * * As an example, the following compressed bitmap bytes: * 0x07 0x01 0x00 0x7 * correspond to the following bitmap: * 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x07 * * Each two-byte element can represent up to 2048 bitmap bits, but as few as a single * bitmap byte for non-sparse sequences. In practice the interface bitmaps created * during a gmcs bootstrap are reduced to less tha 5% of the original size. */ /** * mono_compress_bitmap: * @dest: destination buffer * @bitmap: bitmap buffer * @size: size of @bitmap in bytes * * This is a mono internal function. * The @bitmap data is compressed into a format that is small but * still searchable in few instructions by the JIT and runtime. * The compressed data is stored in the buffer pointed to by the * @dest array. Passing a #NULL value for @dest allows to just compute * the size of the buffer. * This compression algorithm assumes the bits set in the bitmap are * few and far between, like in interface bitmaps. * Returns: the size of the compressed bitmap in bytes. */ int mono_compress_bitmap (uint8_t *dest, const uint8_t *bitmap, int size) { int numz = 0; int res = 0; const uint8_t *end = bitmap + size; while (bitmap < end) { if (*bitmap || numz == 255) { if (dest) { *dest++ = numz; *dest++ = *bitmap; } res += 2; numz = 0; bitmap++; continue; } bitmap++; numz++; } if (numz) { res += 2; if (dest) { *dest++ = numz; *dest++ = 0; } } return res; } /** * mono_class_interface_match: * @bitmap: a compressed bitmap buffer * @id: the index to check in the bitmap * * This is a mono internal function. * Checks if a bit is set in a compressed interface bitmap. @id must * be already checked for being smaller than the maximum id encoded in the * bitmap. * * Returns: a non-zero value if bit @id is set in the bitmap @bitmap, * #FALSE otherwise. */ int mono_class_interface_match (const uint8_t *bitmap, int id) { while (TRUE) { id -= bitmap [0] * 8; if (id < 8) { if (id < 0) return 0; return bitmap [1] & (1 << id); } bitmap += 2; id -= 8; } } #endif /* * LOCKING: this is supposed to be called with the loader lock held. * Return -1 on failure and set exception_type */ static int setup_interface_offsets (MonoClass *class, int cur_slot) { MonoError error; MonoClass *k, *ic; int i, j, max_iid, num_ifaces; MonoClass **interfaces_full = NULL; int *interface_offsets_full = NULL; GPtrArray *ifaces; GPtrArray **ifaces_array = NULL; int interface_offsets_count; MonoClass **array_interfaces = NULL; int num_array_interfaces; int is_enumerator = FALSE; mono_class_setup_supertypes (class); /* * get the implicit generic interfaces for either the arrays or for System.Array/InternalEnumerator * implicit interfaces have the property that they are assigned the same slot in the * vtables for compatible interfaces */ array_interfaces = get_implicit_generic_array_interfaces (class, &num_array_interfaces, &is_enumerator); /* compute maximum number of slots and maximum interface id */ max_iid = 0; num_ifaces = num_array_interfaces; /* this can include duplicated ones */ ifaces_array = g_new0 (GPtrArray *, class->idepth); for (j = 0; j < class->idepth; j++) { k = class->supertypes [j]; num_ifaces += k->interface_count; for (i = 0; i < k->interface_count; i++) { ic = k->interfaces [i]; if (!ic->inited) mono_class_init (ic); if (max_iid < ic->interface_id) max_iid = ic->interface_id; } ifaces = mono_class_get_implemented_interfaces (k, &error); if (!mono_error_ok (&error)) { char *name = mono_type_get_full_name (k); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Error getting the interfaces of %s due to %s", name, mono_error_get_message (&error))); g_free (name); mono_error_cleanup (&error); cur_slot = -1; goto end; } if (ifaces) { num_ifaces += ifaces->len; for (i = 0; i < ifaces->len; ++i) { ic = g_ptr_array_index (ifaces, i); if (max_iid < ic->interface_id) max_iid = ic->interface_id; } ifaces_array [j] = ifaces; } } for (i = 0; i < num_array_interfaces; ++i) { ic = array_interfaces [i]; mono_class_init (ic); if (max_iid < ic->interface_id) max_iid = ic->interface_id; } if (MONO_CLASS_IS_INTERFACE (class)) { num_ifaces++; if (max_iid < class->interface_id) max_iid = class->interface_id; } class->max_interface_id = max_iid; /* compute vtable offset for interfaces */ interfaces_full = g_malloc0 (sizeof (MonoClass*) * num_ifaces); interface_offsets_full = g_malloc (sizeof (int) * num_ifaces); for (i = 0; i < num_ifaces; i++) { interface_offsets_full [i] = -1; } /* skip the current class */ for (j = 0; j < class->idepth - 1; j++) { k = class->supertypes [j]; ifaces = ifaces_array [j]; if (ifaces) { for (i = 0; i < ifaces->len; ++i) { int io; ic = g_ptr_array_index (ifaces, i); /*Force the sharing of interface offsets between parent and subtypes.*/ io = mono_class_interface_offset (k, ic); g_assert (io >= 0); set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, io, TRUE); } } } g_assert (class == class->supertypes [class->idepth - 1]); ifaces = ifaces_array [class->idepth - 1]; if (ifaces) { for (i = 0; i < ifaces->len; ++i) { int count; ic = g_ptr_array_index (ifaces, i); if (set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, cur_slot, FALSE)) continue; count = count_virtual_methods (ic); if (count == -1) { char *name = mono_type_get_full_name (ic); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Error calculating interface offset of %s", name)); g_free (name); cur_slot = -1; goto end; } cur_slot += count; } } if (MONO_CLASS_IS_INTERFACE (class)) set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, class, cur_slot, TRUE); if (num_array_interfaces) { if (is_enumerator) { int ienumerator_idx = find_array_interface (class, "IEnumerator`1"); int ienumerator_offset = find_interface_offset (num_ifaces, interfaces_full, interface_offsets_full, class->interfaces [ienumerator_idx]); g_assert (ienumerator_offset >= 0); for (i = 0; i < num_array_interfaces; ++i) { ic = array_interfaces [i]; if (strcmp (ic->name, "IEnumerator`1") == 0) set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, ienumerator_offset, TRUE); else g_assert_not_reached (); /*g_print ("type %s has %s offset at %d (%s)\n", class->name, ic->name, interface_offsets_full [ic->interface_id], class->interfaces [0]->name);*/ } } else { int ilist_offset, icollection_offset, ienumerable_offset; int ilist_iface_idx = find_array_interface (class, "IList`1"); MonoClass* ilist_class = class->interfaces [ilist_iface_idx]; int icollection_iface_idx = find_array_interface (ilist_class, "ICollection`1"); int ienumerable_iface_idx = find_array_interface (ilist_class, "IEnumerable`1"); ilist_offset = find_interface_offset (num_ifaces, interfaces_full, interface_offsets_full, class->interfaces [ilist_iface_idx]); icollection_offset = find_interface_offset (num_ifaces, interfaces_full, interface_offsets_full, ilist_class->interfaces [icollection_iface_idx]); ienumerable_offset = find_interface_offset (num_ifaces, interfaces_full, interface_offsets_full, ilist_class->interfaces [ienumerable_iface_idx]); g_assert (ilist_offset >= 0 && icollection_offset >= 0 && ienumerable_offset >= 0); for (i = 0; i < num_array_interfaces; ++i) { int offset; ic = array_interfaces [i]; if (ic->generic_class->container_class == mono_defaults.generic_ilist_class) offset = ilist_offset; else if (strcmp (ic->name, "ICollection`1") == 0) offset = icollection_offset; else if (strcmp (ic->name, "IEnumerable`1") == 0) offset = ienumerable_offset; else g_assert_not_reached (); set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, offset, TRUE); /*g_print ("type %s has %s offset at %d (%s)\n", class->name, ic->name, offset, class->interfaces [0]->name);*/ } } } for (interface_offsets_count = 0, i = 0; i < num_ifaces; i++) { if (interface_offsets_full [i] != -1) { interface_offsets_count ++; } } /* * We might get called twice: once from mono_class_init () then once from * mono_class_setup_vtable (). */ if (class->interfaces_packed) { g_assert (class->interface_offsets_count == interface_offsets_count); } else { uint8_t *bitmap; int bsize; class->interface_offsets_count = interface_offsets_count; class->interfaces_packed = mono_class_alloc (class, sizeof (MonoClass*) * interface_offsets_count); class->interface_offsets_packed = mono_class_alloc (class, sizeof (guint16) * interface_offsets_count); bsize = (sizeof (guint8) * ((max_iid + 1) >> 3)) + (((max_iid + 1) & 7)? 1 :0); #ifdef COMPRESSED_INTERFACE_BITMAP bitmap = g_malloc0 (bsize); #else bitmap = mono_class_alloc0 (class, bsize); #endif for (i = 0; i < interface_offsets_count; i++) { int id = interfaces_full [i]->interface_id; bitmap [id >> 3] |= (1 << (id & 7)); class->interfaces_packed [i] = interfaces_full [i]; class->interface_offsets_packed [i] = interface_offsets_full [i]; /*if (num_array_interfaces) g_print ("type %s has %s offset at %d\n", mono_type_get_name_full (&class->byval_arg, 0), mono_type_get_name_full (&interfaces_full [i]->byval_arg, 0), interface_offsets_full [i]);*/ } #ifdef COMPRESSED_INTERFACE_BITMAP i = mono_compress_bitmap (NULL, bitmap, bsize); class->interface_bitmap = mono_class_alloc0 (class, i); mono_compress_bitmap (class->interface_bitmap, bitmap, bsize); g_free (bitmap); #else class->interface_bitmap = bitmap; #endif } end: g_free (interfaces_full); g_free (interface_offsets_full); g_free (array_interfaces); for (i = 0; i < class->idepth; i++) { ifaces = ifaces_array [i]; if (ifaces) g_ptr_array_free (ifaces, TRUE); } g_free (ifaces_array); //printf ("JUST DONE: "); //print_implemented_interfaces (class); return cur_slot; } /* * Setup interface offsets for interfaces. * Initializes: * - class->max_interface_id * - class->interface_offsets_count * - class->interfaces_packed * - class->interface_offsets_packed * - class->interface_bitmap * * This function can fail @class. */ void mono_class_setup_interface_offsets (MonoClass *class) { mono_loader_lock (); setup_interface_offsets (class, 0); mono_loader_unlock (); } /*Checks if @klass has @parent as one of it's parents type gtd * * For example: * Foo * Bar : Foo>> * */ static gboolean mono_class_has_gtd_parent (MonoClass *klass, MonoClass *parent) { klass = mono_class_get_generic_type_definition (klass); parent = mono_class_get_generic_type_definition (parent); mono_class_setup_supertypes (klass); mono_class_setup_supertypes (parent); return klass->idepth >= parent->idepth && mono_class_get_generic_type_definition (klass->supertypes [parent->idepth - 1]) == parent; } gboolean mono_class_check_vtable_constraints (MonoClass *class, GList *in_setup) { MonoGenericInst *ginst; int i; if (!class->generic_class) { mono_class_setup_vtable_full (class, in_setup); return class->exception_type == 0; } mono_class_setup_vtable_full (mono_class_get_generic_type_definition (class), in_setup); if (class->generic_class->container_class->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Failed to load generic definition vtable")); return FALSE; } ginst = class->generic_class->context.class_inst; for (i = 0; i < ginst->type_argc; ++i) { MonoClass *arg; if (ginst->type_argv [i]->type != MONO_TYPE_GENERICINST) continue; arg = mono_class_from_mono_type (ginst->type_argv [i]); /*Those 2 will be checked by mono_class_setup_vtable itself*/ if (mono_class_has_gtd_parent (class, arg) || mono_class_has_gtd_parent (arg, class)) continue; if (!mono_class_check_vtable_constraints (arg, in_setup)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Failed to load generic parameter %d", i)); return FALSE; } } return TRUE; } /* * mono_class_setup_vtable: * * Creates the generic vtable of CLASS. * Initializes the following fields in MonoClass: * - vtable * - vtable_size * Plus all the fields initialized by setup_interface_offsets (). * If there is an error during vtable construction, class->exception_type is set. * * LOCKING: Acquires the loader lock. */ void mono_class_setup_vtable (MonoClass *class) { mono_class_setup_vtable_full (class, NULL); } static void mono_class_setup_vtable_full (MonoClass *class, GList *in_setup) { MonoMethod **overrides; MonoGenericContext *context; guint32 type_token; int onum = 0; gboolean ok = TRUE; if (class->vtable) return; if (mono_debug_using_mono_debugger ()) /* The debugger currently depends on this */ mono_class_setup_methods (class); if (MONO_CLASS_IS_INTERFACE (class)) { /* This sets method->slot for all methods if this is an interface */ mono_class_setup_methods (class); return; } if (class->exception_type) return; if (g_list_find (in_setup, class)) return; mono_loader_lock (); if (class->vtable) { mono_loader_unlock (); return; } mono_stats.generic_vtable_count ++; in_setup = g_list_prepend (in_setup, class); if (class->generic_class) { if (!mono_class_check_vtable_constraints (class, in_setup)) { mono_loader_unlock (); g_list_remove (in_setup, class); return; } context = mono_class_get_context (class); type_token = class->generic_class->container_class->type_token; } else { context = (MonoGenericContext *) class->generic_container; type_token = class->type_token; } if (class->image->dynamic) { /* Generic instances can have zero method overrides without causing any harm. * This is true since we don't do layout all over again for them, we simply inflate * the layout of the parent. */ mono_reflection_get_dynamic_overrides (class, &overrides, &onum); } else { /* The following call fails if there are missing methods in the type */ /* FIXME it's probably a good idea to avoid this for generic instances. */ ok = mono_class_get_overrides_full (class->image, type_token, &overrides, &onum, context); } if (ok) mono_class_setup_vtable_general (class, overrides, onum, in_setup); else mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load list of method overrides")); g_free (overrides); mono_loader_unlock (); g_list_remove (in_setup, class); return; } #define DEBUG_INTERFACE_VTABLE_CODE 0 #define TRACE_INTERFACE_VTABLE_CODE 0 #define VERIFY_INTERFACE_VTABLE_CODE 0 #define VTABLE_SELECTOR (1) #if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) #define DEBUG_INTERFACE_VTABLE(stmt) do {\ if (!(VTABLE_SELECTOR)) break; \ stmt;\ } while (0) #else #define DEBUG_INTERFACE_VTABLE(stmt) #endif #if TRACE_INTERFACE_VTABLE_CODE #define TRACE_INTERFACE_VTABLE(stmt) do {\ if (!(VTABLE_SELECTOR)) break; \ stmt;\ } while (0) #else #define TRACE_INTERFACE_VTABLE(stmt) #endif #if VERIFY_INTERFACE_VTABLE_CODE #define VERIFY_INTERFACE_VTABLE(stmt) do {\ if (!(VTABLE_SELECTOR)) break; \ stmt;\ } while (0) #else #define VERIFY_INTERFACE_VTABLE(stmt) #endif #if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) static char* mono_signature_get_full_desc (MonoMethodSignature *sig, gboolean include_namespace) { int i; char *result; GString *res = g_string_new (""); g_string_append_c (res, '('); for (i = 0; i < sig->param_count; ++i) { if (i > 0) g_string_append_c (res, ','); mono_type_get_desc (res, sig->params [i], include_namespace); } g_string_append (res, ")=>"); if (sig->ret != NULL) { mono_type_get_desc (res, sig->ret, include_namespace); } else { g_string_append (res, "NULL"); } result = res->str; g_string_free (res, FALSE); return result; } static void print_method_signatures (MonoMethod *im, MonoMethod *cm) { char *im_sig = mono_signature_get_full_desc (mono_method_signature (im), TRUE); char *cm_sig = mono_signature_get_full_desc (mono_method_signature (cm), TRUE); printf ("(IM \"%s\", CM \"%s\")", im_sig, cm_sig); g_free (im_sig); g_free (cm_sig); } #endif static gboolean is_wcf_hack_disabled (void) { static gboolean disabled; static gboolean inited = FALSE; if (!inited) { disabled = g_getenv ("MONO_DISABLE_WCF_HACK") != NULL; inited = TRUE; } return disabled; } static gboolean check_interface_method_override (MonoClass *class, MonoMethod *im, MonoMethod *cm, gboolean require_newslot, gboolean interface_is_explicitly_implemented_by_class, gboolean slot_is_empty, gboolean security_enabled) { MonoMethodSignature *cmsig, *imsig; if (strcmp (im->name, cm->name) == 0) { if (! (cm->flags & METHOD_ATTRIBUTE_PUBLIC)) { TRACE_INTERFACE_VTABLE (printf ("[PUBLIC CHECK FAILED]")); return FALSE; } if (! slot_is_empty) { if (require_newslot) { if (! interface_is_explicitly_implemented_by_class) { TRACE_INTERFACE_VTABLE (printf ("[NOT EXPLICIT IMPLEMENTATION IN FULL SLOT REFUSED]")); return FALSE; } if (! (cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) { TRACE_INTERFACE_VTABLE (printf ("[NEWSLOT CHECK FAILED]")); return FALSE; } } else { TRACE_INTERFACE_VTABLE (printf ("[FULL SLOT REFUSED]")); } } cmsig = mono_method_signature (cm); imsig = mono_method_signature (im); if (!cmsig || !imsig) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not resolve the signature of a virtual method")); return FALSE; } if (! mono_metadata_signature_equal (cmsig, imsig)) { TRACE_INTERFACE_VTABLE (printf ("[SIGNATURE CHECK FAILED ")); TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm)); TRACE_INTERFACE_VTABLE (printf ("]")); return FALSE; } TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS]")); /* CAS - SecurityAction.InheritanceDemand on interface */ if (security_enabled && (im->flags & METHOD_ATTRIBUTE_HAS_SECURITY)) { mono_secman_inheritancedemand_method (cm, im); } if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_override (class, cm, im); TRACE_INTERFACE_VTABLE (printf ("[NAME CHECK OK]")); if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) { char *body_name = mono_method_full_name (cm, TRUE); char *decl_name = mono_method_full_name (im, TRUE); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Method %s overrides method '%s' which is not accessible", body_name, decl_name)); g_free (body_name); g_free (decl_name); return FALSE; } return TRUE; } else { MonoClass *ic = im->klass; const char *ic_name_space = ic->name_space; const char *ic_name = ic->name; char *subname; if (! require_newslot) { TRACE_INTERFACE_VTABLE (printf ("[INJECTED METHOD REFUSED]")); return FALSE; } if (cm->klass->rank == 0) { TRACE_INTERFACE_VTABLE (printf ("[RANK CHECK FAILED]")); return FALSE; } cmsig = mono_method_signature (cm); imsig = mono_method_signature (im); if (!cmsig || !imsig) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not resolve the signature of a virtual method")); return FALSE; } if (! mono_metadata_signature_equal (cmsig, imsig)) { TRACE_INTERFACE_VTABLE (printf ("[(INJECTED) SIGNATURE CHECK FAILED ")); TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm)); TRACE_INTERFACE_VTABLE (printf ("]")); return FALSE; } if (mono_class_get_image (ic) != mono_defaults.corlib) { TRACE_INTERFACE_VTABLE (printf ("[INTERFACE CORLIB CHECK FAILED]")); return FALSE; } if ((ic_name_space == NULL) || (strcmp (ic_name_space, "System.Collections.Generic") != 0)) { TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAMESPACE CHECK FAILED]")); return FALSE; } if ((ic_name == NULL) || ((strcmp (ic_name, "IEnumerable`1") != 0) && (strcmp (ic_name, "ICollection`1") != 0) && (strcmp (ic_name, "IList`1") != 0))) { TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAME CHECK FAILED]")); return FALSE; } subname = strstr (cm->name, ic_name_space); if (subname != cm->name) { TRACE_INTERFACE_VTABLE (printf ("[ACTUAL NAMESPACE CHECK FAILED]")); return FALSE; } subname += strlen (ic_name_space); if (subname [0] != '.') { TRACE_INTERFACE_VTABLE (printf ("[FIRST DOT CHECK FAILED]")); return FALSE; } subname ++; if (strstr (subname, ic_name) != subname) { TRACE_INTERFACE_VTABLE (printf ("[ACTUAL CLASS NAME CHECK FAILED]")); return FALSE; } subname += strlen (ic_name); if (subname [0] != '.') { TRACE_INTERFACE_VTABLE (printf ("[SECOND DOT CHECK FAILED]")); return FALSE; } subname ++; if (strcmp (subname, im->name) != 0) { TRACE_INTERFACE_VTABLE (printf ("[METHOD NAME CHECK FAILED]")); return FALSE; } TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS (INJECTED CASE)]")); /* CAS - SecurityAction.InheritanceDemand on interface */ if (security_enabled && (im->flags & METHOD_ATTRIBUTE_HAS_SECURITY)) { mono_secman_inheritancedemand_method (cm, im); } if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_override (class, cm, im); TRACE_INTERFACE_VTABLE (printf ("[INJECTED INTERFACE CHECK OK]")); if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) { char *body_name = mono_method_full_name (cm, TRUE); char *decl_name = mono_method_full_name (im, TRUE); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Method %s overrides method '%s' which is not accessible", body_name, decl_name)); g_free (body_name); g_free (decl_name); return FALSE; } return TRUE; } } #if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE) static void foreach_override (gpointer key, gpointer value, gpointer user_data) { MonoMethod *method = key; MonoMethod *override = value; MonoClass *method_class = mono_method_get_class (method); MonoClass *override_class = mono_method_get_class (override); printf (" Method '%s.%s:%s' has override '%s.%s:%s'\n", mono_class_get_namespace (method_class), mono_class_get_name (method_class), mono_method_get_name (method), mono_class_get_namespace (override_class), mono_class_get_name (override_class), mono_method_get_name (override)); } static void print_overrides (GHashTable *override_map, const char *message) { if (override_map) { printf ("Override map \"%s\" START:\n", message); g_hash_table_foreach (override_map, foreach_override, NULL); printf ("Override map \"%s\" END.\n", message); } else { printf ("Override map \"%s\" EMPTY.\n", message); } } static void print_vtable_full (MonoClass *class, MonoMethod** vtable, int size, int first_non_interface_slot, const char *message, gboolean print_interfaces) { char *full_name = mono_type_full_name (&class->byval_arg); int i; int parent_size; printf ("*** Vtable for class '%s' at \"%s\" (size %d)\n", full_name, message, size); if (print_interfaces) { print_implemented_interfaces (class); printf ("* Interfaces for class '%s' done.\nStarting vtable (size %d):\n", full_name, size); } if (class->parent) { parent_size = class->parent->vtable_size; } else { parent_size = 0; } for (i = 0; i < size; ++i) { MonoMethod *cm = vtable [i]; char *cm_name = cm ? mono_method_full_name (cm, TRUE) : g_strdup ("nil"); char newness = (i < parent_size) ? 'O' : ((i < first_non_interface_slot) ? 'I' : 'N'); printf (" [%c][%03d][INDEX %03d] %s [%p]\n", newness, i, cm ? cm->slot : - 1, cm_name, cm); g_free (cm_name); } g_free (full_name); } #endif #if VERIFY_INTERFACE_VTABLE_CODE static int mono_method_try_get_vtable_index (MonoMethod *method) { if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { MonoMethodInflated *imethod = (MonoMethodInflated*)method; if (imethod->declaring->is_generic) return imethod->declaring->slot; } return method->slot; } static void mono_class_verify_vtable (MonoClass *class) { int i; char *full_name = mono_type_full_name (&class->byval_arg); printf ("*** Verifying VTable of class '%s' \n", full_name); g_free (full_name); full_name = NULL; if (!class->methods) return; for (i = 0; i < class->method.count; ++i) { MonoMethod *cm = class->methods [i]; int slot; if (!(cm->flags & METHOD_ATTRIBUTE_VIRTUAL)) continue; g_free (full_name); full_name = mono_method_full_name (cm, TRUE); slot = mono_method_try_get_vtable_index (cm); if (slot >= 0) { if (slot >= class->vtable_size) { printf ("\tInvalid method %s at index %d with vtable of length %d\n", full_name, slot, class->vtable_size); continue; } if (slot >= 0 && class->vtable [slot] != cm && (class->vtable [slot])) { char *other_name = class->vtable [slot] ? mono_method_full_name (class->vtable [slot], TRUE) : g_strdup ("[null value]"); printf ("\tMethod %s has slot %d but vtable has %s on it\n", full_name, slot, other_name); g_free (other_name); } } else printf ("\tVirtual method %s does n't have an assigned slot\n", full_name); } g_free (full_name); } #endif static void print_unimplemented_interface_method_info (MonoClass *class, MonoClass *ic, MonoMethod *im, int im_slot, MonoMethod **overrides, int onum) { int index; char *method_signature; char *type_name; for (index = 0; index < onum; ++index) { mono_trace_warning (MONO_TRACE_TYPE, " at slot %d: %s (%d) overrides %s (%d)\n", im_slot, overrides [index*2+1]->name, overrides [index*2+1]->slot, overrides [index*2]->name, overrides [index*2]->slot); } method_signature = mono_signature_get_desc (mono_method_signature (im), FALSE); type_name = mono_type_full_name (&class->byval_arg); mono_trace_warning (MONO_TRACE_TYPE, "no implementation for interface method %s::%s(%s) in class %s\n", mono_type_get_name (&ic->byval_arg), im->name, method_signature, type_name); g_free (method_signature); g_free (type_name); mono_class_setup_methods (class); if (class->exception_type) { char *name = mono_type_get_full_name (class); mono_trace_warning (MONO_TRACE_TYPE, "CLASS %s failed to resolve methods\n", name); g_free (name); return; } for (index = 0; index < class->method.count; ++index) { MonoMethod *cm = class->methods [index]; method_signature = mono_signature_get_desc (mono_method_signature (cm), TRUE); mono_trace_warning (MONO_TRACE_TYPE, "METHOD %s(%s)\n", cm->name, method_signature); g_free (method_signature); } } static MonoMethod* mono_method_get_method_definition (MonoMethod *method) { while (method->is_inflated) method = ((MonoMethodInflated*)method)->declaring; return method; } static gboolean verify_class_overrides (MonoClass *class, MonoMethod **overrides, int onum) { int i; for (i = 0; i < onum; ++i) { MonoMethod *decl = overrides [i * 2]; MonoMethod *body = overrides [i * 2 + 1]; if (mono_class_get_generic_type_definition (body->klass) != mono_class_get_generic_type_definition (class)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Method belongs to a different class than the declared one")); return FALSE; } if (!(body->flags & METHOD_ATTRIBUTE_VIRTUAL) || (body->flags & METHOD_ATTRIBUTE_STATIC)) { if (body->flags & METHOD_ATTRIBUTE_STATIC) mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Method must not be static to override a base type")); else mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Method must be virtual to override a base type")); return FALSE; } if (!(decl->flags & METHOD_ATTRIBUTE_VIRTUAL) || (decl->flags & METHOD_ATTRIBUTE_STATIC)) { if (body->flags & METHOD_ATTRIBUTE_STATIC) mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Cannot override a static method in a base type")); else mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Cannot override a non virtual method in a base type")); return FALSE; } if (!mono_class_is_assignable_from_slow (decl->klass, class)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Method overrides a class or interface that extended or implemented by this type")); return FALSE; } body = mono_method_get_method_definition (body); decl = mono_method_get_method_definition (decl); if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (body, decl, NULL)) { char *body_name = mono_method_full_name (body, TRUE); char *decl_name = mono_method_full_name (decl, TRUE); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Method %s overrides method '%s' which is not accessible", body_name, decl_name)); g_free (body_name); g_free (decl_name); return FALSE; } } return TRUE; } static gboolean mono_class_need_stelemref_method (MonoClass *class) { return class->rank == 1 && MONO_TYPE_IS_REFERENCE (&class->element_class->byval_arg); } /* * LOCKING: this is supposed to be called with the loader lock held. */ void mono_class_setup_vtable_general (MonoClass *class, MonoMethod **overrides, int onum, GList *in_setup) { MonoError error; MonoClass *k, *ic; MonoMethod **vtable; int i, max_vtsize = 0, max_iid, cur_slot = 0; GPtrArray *ifaces = NULL; GHashTable *override_map = NULL; gboolean security_enabled = mono_is_security_manager_active (); MonoMethod *cm; gpointer class_iter; #if (DEBUG_INTERFACE_VTABLE_CODE|TRACE_INTERFACE_VTABLE_CODE) int first_non_interface_slot; #endif GSList *virt_methods = NULL, *l; int stelemref_slot = 0; if (class->vtable) return; if (overrides && !verify_class_overrides (class, overrides, onum)) return; ifaces = mono_class_get_implemented_interfaces (class, &error); if (!mono_error_ok (&error)) { char *name = mono_type_get_full_name (class); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Could not resolve %s interfaces due to %s", name, mono_error_get_message (&error))); g_free (name); mono_error_cleanup (&error); return; } else if (ifaces) { for (i = 0; i < ifaces->len; i++) { MonoClass *ic = g_ptr_array_index (ifaces, i); max_vtsize += ic->method.count; } g_ptr_array_free (ifaces, TRUE); ifaces = NULL; } if (class->parent) { mono_class_init (class->parent); mono_class_setup_vtable_full (class->parent, in_setup); if (class->parent->exception_type) { char *name = mono_type_get_full_name (class->parent); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Parent %s failed to load", name)); g_free (name); return; } max_vtsize += class->parent->vtable_size; cur_slot = class->parent->vtable_size; } max_vtsize += class->method.count; /*Array have a slot for stelemref*/ if (mono_class_need_stelemref_method (class)) { stelemref_slot = cur_slot; ++max_vtsize; ++cur_slot; } vtable = alloca (sizeof (gpointer) * max_vtsize); memset (vtable, 0, sizeof (gpointer) * max_vtsize); /* printf ("METAINIT %s.%s\n", class->name_space, class->name); */ cur_slot = setup_interface_offsets (class, cur_slot); if (cur_slot == -1) /*setup_interface_offsets fails the type.*/ return; max_iid = class->max_interface_id; DEBUG_INTERFACE_VTABLE (first_non_interface_slot = cur_slot); /* Optimized version for generic instances */ if (class->generic_class) { MonoError error; MonoClass *gklass = class->generic_class->container_class; MonoMethod **tmp; mono_class_setup_vtable_full (gklass, in_setup); if (gklass->exception_type != MONO_EXCEPTION_NONE) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } tmp = mono_class_alloc0 (class, sizeof (gpointer) * gklass->vtable_size); class->vtable_size = gklass->vtable_size; for (i = 0; i < gklass->vtable_size; ++i) if (gklass->vtable [i]) { MonoMethod *inflated = mono_class_inflate_generic_method_full_checked (gklass->vtable [i], class, mono_class_get_context (class), &error); if (!mono_error_ok (&error)) { char *err_msg = g_strdup_printf ("Could not inflate method due to %s", mono_error_get_message (&error)); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, err_msg); g_free (err_msg); mono_error_cleanup (&error); return; } tmp [i] = inflated; tmp [i]->slot = gklass->vtable [i]->slot; } mono_memory_barrier (); class->vtable = tmp; /* Have to set method->slot for abstract virtual methods */ if (class->methods && gklass->methods) { for (i = 0; i < class->method.count; ++i) if (class->methods [i]->slot == -1) class->methods [i]->slot = gklass->methods [i]->slot; } return; } if (class->parent && class->parent->vtable_size) { MonoClass *parent = class->parent; int i; memcpy (vtable, parent->vtable, sizeof (gpointer) * parent->vtable_size); // Also inherit parent interface vtables, just as a starting point. // This is needed otherwise bug-77127.exe fails when the property methods // have different names in the iterface and the class, because for child // classes the ".override" information is not used anymore. for (i = 0; i < parent->interface_offsets_count; i++) { MonoClass *parent_interface = parent->interfaces_packed [i]; int interface_offset = mono_class_interface_offset (class, parent_interface); /*FIXME this is now dead code as this condition will never hold true. Since interface offsets are inherited then the offset of an interface implemented by a parent will never be the out of it's vtable boundary. */ if (interface_offset >= parent->vtable_size) { int parent_interface_offset = mono_class_interface_offset (parent, parent_interface); int j; mono_class_setup_methods (parent_interface); /*FIXME Just kill this whole chunk of dead code*/ TRACE_INTERFACE_VTABLE (printf (" +++ Inheriting interface %s.%s\n", parent_interface->name_space, parent_interface->name)); for (j = 0; j < parent_interface->method.count && !class->exception_type; j++) { vtable [interface_offset + j] = parent->vtable [parent_interface_offset + j]; TRACE_INTERFACE_VTABLE (printf (" --- Inheriting: [%03d][(%03d)+(%03d)] => [%03d][(%03d)+(%03d)]\n", parent_interface_offset + j, parent_interface_offset, j, interface_offset + j, interface_offset, j)); } } } } /*Array have a slot for stelemref*/ if (mono_class_need_stelemref_method (class)) { MonoMethod *method = mono_marshal_get_virtual_stelemref (class); if (!method->slot) method->slot = stelemref_slot; else g_assert (method->slot == stelemref_slot); vtable [stelemref_slot] = method; } TRACE_INTERFACE_VTABLE (print_vtable_full (class, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE)); /* override interface methods */ for (i = 0; i < onum; i++) { MonoMethod *decl = overrides [i*2]; if (MONO_CLASS_IS_INTERFACE (decl->klass)) { int dslot; dslot = mono_method_get_vtable_slot (decl); if (dslot == -1) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } dslot += mono_class_interface_offset (class, decl->klass); vtable [dslot] = overrides [i*2 + 1]; vtable [dslot]->slot = dslot; if (!override_map) override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); g_hash_table_insert (override_map, overrides [i * 2], overrides [i * 2 + 1]); if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_override (class, vtable [dslot], decl); } } TRACE_INTERFACE_VTABLE (print_overrides (override_map, "AFTER OVERRIDING INTERFACE METHODS")); TRACE_INTERFACE_VTABLE (print_vtable_full (class, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING INTERFACE METHODS", FALSE)); /* * Create a list of virtual methods to avoid calling * mono_class_get_virtual_methods () which is slow because of the metadata * optimization. */ { gpointer iter = NULL; MonoMethod *cm; virt_methods = NULL; while ((cm = mono_class_get_virtual_methods (class, &iter))) { virt_methods = g_slist_prepend (virt_methods, cm); } if (class->exception_type) goto fail; } // Loop on all implemented interfaces... for (i = 0; i < class->interface_offsets_count; i++) { MonoClass *parent = class->parent; int ic_offset; gboolean interface_is_explicitly_implemented_by_class; int im_index; ic = class->interfaces_packed [i]; ic_offset = mono_class_interface_offset (class, ic); mono_class_setup_methods (ic); if (ic->exception_type) goto fail; // Check if this interface is explicitly implemented (instead of just inherited) if (parent != NULL) { int implemented_interfaces_index; interface_is_explicitly_implemented_by_class = FALSE; for (implemented_interfaces_index = 0; implemented_interfaces_index < class->interface_count; implemented_interfaces_index++) { if (ic == class->interfaces [implemented_interfaces_index]) { interface_is_explicitly_implemented_by_class = TRUE; break; } } } else { interface_is_explicitly_implemented_by_class = TRUE; } // Loop on all interface methods... for (im_index = 0; im_index < ic->method.count; im_index++) { MonoMethod *im = ic->methods [im_index]; int im_slot = ic_offset + im->slot; MonoMethod *override_im = (override_map != NULL) ? g_hash_table_lookup (override_map, im) : NULL; if (im->flags & METHOD_ATTRIBUTE_STATIC) continue; TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1))); // If there is an explicit implementation, just use it right away, // otherwise look for a matching method if (override_im == NULL) { int cm_index; gpointer iter; MonoMethod *cm; // First look for a suitable method among the class methods iter = NULL; for (l = virt_methods; l; l = l->next) { cm = l->data; TRACE_INTERFACE_VTABLE (printf (" For slot %d ('%s'.'%s':'%s'), trying method '%s'.'%s':'%s'... [EXPLICIT IMPLEMENTATION = %d][SLOT IS NULL = %d]", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL))); if (check_interface_method_override (class, im, cm, TRUE, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL), security_enabled)) { TRACE_INTERFACE_VTABLE (printf ("[check ok]: ASSIGNING")); vtable [im_slot] = cm; /* Why do we need this? */ if (cm->slot < 0) { cm->slot = im_slot; } } TRACE_INTERFACE_VTABLE (printf ("\n")); if (class->exception_type) /*Might be set by check_interface_method_override*/ goto fail; } // If the slot is still empty, look in all the inherited virtual methods... if ((vtable [im_slot] == NULL) && class->parent != NULL) { MonoClass *parent = class->parent; // Reverse order, so that last added methods are preferred for (cm_index = parent->vtable_size - 1; cm_index >= 0; cm_index--) { MonoMethod *cm = parent->vtable [cm_index]; TRACE_INTERFACE_VTABLE ((cm != NULL) && printf (" For slot %d ('%s'.'%s':'%s'), trying (ancestor) method '%s'.'%s':'%s'... ", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name)); if ((cm != NULL) && check_interface_method_override (class, im, cm, FALSE, FALSE, TRUE, security_enabled)) { TRACE_INTERFACE_VTABLE (printf ("[everything ok]: ASSIGNING")); vtable [im_slot] = cm; /* Why do we need this? */ if (cm->slot < 0) { cm->slot = im_slot; } break; } if (class->exception_type) /*Might be set by check_interface_method_override*/ goto fail; TRACE_INTERFACE_VTABLE ((cm != NULL) && printf ("\n")); } } } else { g_assert (vtable [im_slot] == override_im); } } } // If the class is not abstract, check that all its interface slots are full. // The check is done here and not directly at the end of the loop above because // it can happen (for injected generic array interfaces) that the same slot is // processed multiple times (those interfaces have overlapping slots), and it // will not always be the first pass the one that fills the slot. if (! (class->flags & TYPE_ATTRIBUTE_ABSTRACT)) { for (i = 0; i < class->interface_offsets_count; i++) { int ic_offset; int im_index; ic = class->interfaces_packed [i]; ic_offset = mono_class_interface_offset (class, ic); for (im_index = 0; im_index < ic->method.count; im_index++) { MonoMethod *im = ic->methods [im_index]; int im_slot = ic_offset + im->slot; if (im->flags & METHOD_ATTRIBUTE_STATIC) continue; TRACE_INTERFACE_VTABLE (printf (" [class is not abstract, checking slot %d for interface '%s'.'%s', method %s, slot check is %d]\n", im_slot, ic->name_space, ic->name, im->name, (vtable [im_slot] == NULL))); if (vtable [im_slot] == NULL) { print_unimplemented_interface_method_info (class, ic, im, im_slot, overrides, onum); goto fail; } } } } TRACE_INTERFACE_VTABLE (print_vtable_full (class, vtable, cur_slot, first_non_interface_slot, "AFTER SETTING UP INTERFACE METHODS", FALSE)); class_iter = NULL; for (l = virt_methods; l; l = l->next) { cm = l->data; /* * If the method is REUSE_SLOT, we must check in the * base class for a method to override. */ if (!(cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) { int slot = -1; for (k = class->parent; k ; k = k->parent) { gpointer k_iter; MonoMethod *m1; k_iter = NULL; while ((m1 = mono_class_get_virtual_methods (k, &k_iter))) { MonoMethodSignature *cmsig, *m1sig; cmsig = mono_method_signature (cm); m1sig = mono_method_signature (m1); if (!cmsig || !m1sig) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } if (!strcmp(cm->name, m1->name) && mono_metadata_signature_equal (cmsig, m1sig)) { /* CAS - SecurityAction.InheritanceDemand */ if (security_enabled && (m1->flags & METHOD_ATTRIBUTE_HAS_SECURITY)) { mono_secman_inheritancedemand_method (cm, m1); } if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_override (class, cm, m1); slot = mono_method_get_vtable_slot (m1); if (slot == -1) goto fail; if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, m1, NULL)) { char *body_name = mono_method_full_name (cm, TRUE); char *decl_name = mono_method_full_name (m1, TRUE); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Method %s overrides method '%s' which is not accessible", body_name, decl_name)); g_free (body_name); g_free (decl_name); goto fail; } g_assert (cm->slot < max_vtsize); if (!override_map) override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); TRACE_INTERFACE_VTABLE (printf ("adding iface override from %s [%p] to %s [%p]\n", mono_method_full_name (m1, 1), m1, mono_method_full_name (cm, 1), cm)); g_hash_table_insert (override_map, m1, cm); break; } } if (k->exception_type) goto fail; if (slot >= 0) break; } if (slot >= 0) cm->slot = slot; } /*Non final newslot methods must be given a non-interface vtable slot*/ if ((cm->flags & METHOD_ATTRIBUTE_NEW_SLOT) && !(cm->flags & METHOD_ATTRIBUTE_FINAL) && cm->slot >= 0) cm->slot = -1; if (cm->slot < 0) cm->slot = cur_slot++; if (!(cm->flags & METHOD_ATTRIBUTE_ABSTRACT)) vtable [cm->slot] = cm; } /* override non interface methods */ for (i = 0; i < onum; i++) { MonoMethod *decl = overrides [i*2]; if (!MONO_CLASS_IS_INTERFACE (decl->klass)) { g_assert (decl->slot != -1); vtable [decl->slot] = overrides [i*2 + 1]; overrides [i * 2 + 1]->slot = decl->slot; if (!override_map) override_map = g_hash_table_new (mono_aligned_addr_hash, NULL); TRACE_INTERFACE_VTABLE (printf ("adding explicit override from %s [%p] to %s [%p]\n", mono_method_full_name (decl, 1), decl, mono_method_full_name (overrides [i * 2 + 1], 1), overrides [i * 2 + 1])); g_hash_table_insert (override_map, decl, overrides [i * 2 + 1]); if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_override (class, vtable [decl->slot], decl); } } /* * If a method occupies more than one place in the vtable, and it is * overriden, then change the other occurances too. */ if (override_map) { MonoMethod *cm; for (i = 0; i < max_vtsize; ++i) if (vtable [i]) { TRACE_INTERFACE_VTABLE (printf ("checking slot %d method %s[%p] for overrides\n", i, mono_method_full_name (vtable [i], 1), vtable [i])); cm = g_hash_table_lookup (override_map, vtable [i]); if (cm) vtable [i] = cm; } g_hash_table_destroy (override_map); override_map = NULL; } g_slist_free (virt_methods); virt_methods = NULL; /* Ensure that all vtable slots are filled with concrete instance methods */ if (!(class->flags & TYPE_ATTRIBUTE_ABSTRACT)) { for (i = 0; i < cur_slot; ++i) { if (vtable [i] == NULL || (vtable [i]->flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_STATIC))) { char *type_name = mono_type_get_full_name (class); char *method_name = vtable [i] ? mono_method_full_name (vtable [i], TRUE) : g_strdup ("none"); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Type %s has invalid vtable method slot %d with method %s", type_name, i, method_name)); g_free (type_name); g_free (method_name); return; } } } if (class->generic_class) { MonoClass *gklass = class->generic_class->container_class; mono_class_init (gklass); class->vtable_size = MAX (gklass->vtable_size, cur_slot); } else { /* Check that the vtable_size value computed in mono_class_init () is correct */ if (class->vtable_size) g_assert (cur_slot == class->vtable_size); class->vtable_size = cur_slot; } /* Try to share the vtable with our parent. */ if (class->parent && (class->parent->vtable_size == class->vtable_size) && (memcmp (class->parent->vtable, vtable, sizeof (gpointer) * class->vtable_size) == 0)) { mono_memory_barrier (); class->vtable = class->parent->vtable; } else { MonoMethod **tmp = mono_class_alloc0 (class, sizeof (gpointer) * class->vtable_size); memcpy (tmp, vtable, sizeof (gpointer) * class->vtable_size); mono_memory_barrier (); class->vtable = tmp; } DEBUG_INTERFACE_VTABLE (print_vtable_full (class, class->vtable, class->vtable_size, first_non_interface_slot, "FINALLY", FALSE)); if (mono_print_vtable) { int icount = 0; print_implemented_interfaces (class); for (i = 0; i <= max_iid; i++) if (MONO_CLASS_IMPLEMENTS_INTERFACE (class, i)) icount++; printf ("VTable %s (vtable entries = %d, interfaces = %d)\n", mono_type_full_name (&class->byval_arg), class->vtable_size, icount); for (i = 0; i < cur_slot; ++i) { MonoMethod *cm; cm = vtable [i]; if (cm) { printf (" slot assigned: %03d, slot index: %03d %s\n", i, cm->slot, mono_method_full_name (cm, TRUE)); } } if (icount) { printf ("Interfaces %s.%s (max_iid = %d)\n", class->name_space, class->name, max_iid); for (i = 0; i < class->interface_count; i++) { ic = class->interfaces [i]; printf (" slot offset: %03d, method count: %03d, iid: %03d %s\n", mono_class_interface_offset (class, ic), count_virtual_methods (ic), ic->interface_id, mono_type_full_name (&ic->byval_arg)); } for (k = class->parent; k ; k = k->parent) { for (i = 0; i < k->interface_count; i++) { ic = k->interfaces [i]; printf (" parent slot offset: %03d, method count: %03d, iid: %03d %s\n", mono_class_interface_offset (class, ic), count_virtual_methods (ic), ic->interface_id, mono_type_full_name (&ic->byval_arg)); } } } } VERIFY_INTERFACE_VTABLE (mono_class_verify_vtable (class)); return; fail: { char *name = mono_type_get_full_name (class); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("VTable setup of type %s failed", name)); g_free (name); if (override_map) g_hash_table_destroy (override_map); if (virt_methods) g_slist_free (virt_methods); } } /* * mono_method_get_vtable_slot: * * Returns method->slot, computing it if neccesary. Return -1 on failure. * LOCKING: Acquires the loader lock. * * FIXME Use proper MonoError machinery here. */ int mono_method_get_vtable_slot (MonoMethod *method) { if (method->slot == -1) { mono_class_setup_vtable (method->klass); if (method->klass->exception_type) return -1; g_assert (method->slot != -1); } return method->slot; } /** * mono_method_get_vtable_index: * @method: a method * * Returns the index into the runtime vtable to access the method or, * in the case of a virtual generic method, the virtual generic method * thunk. Returns -1 on failure. * * FIXME Use proper MonoError machinery here. */ int mono_method_get_vtable_index (MonoMethod *method) { if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { MonoMethodInflated *imethod = (MonoMethodInflated*)method; if (imethod->declaring->is_generic) return mono_method_get_vtable_slot (imethod->declaring); } return mono_method_get_vtable_slot (method); } static MonoMethod *default_ghc = NULL; static MonoMethod *default_finalize = NULL; static int finalize_slot = -1; static int ghc_slot = -1; static void initialize_object_slots (MonoClass *class) { int i; if (default_ghc) return; if (class == mono_defaults.object_class) { mono_class_setup_vtable (class); for (i = 0; i < class->vtable_size; ++i) { MonoMethod *cm = class->vtable [i]; if (!strcmp (cm->name, "GetHashCode")) ghc_slot = i; else if (!strcmp (cm->name, "Finalize")) finalize_slot = i; } g_assert (ghc_slot > 0); default_ghc = class->vtable [ghc_slot]; g_assert (finalize_slot > 0); default_finalize = class->vtable [finalize_slot]; } } typedef struct { MonoMethod *array_method; char *name; } GenericArrayMethodInfo; static int generic_array_method_num = 0; static GenericArrayMethodInfo *generic_array_method_info = NULL; static int generic_array_methods (MonoClass *class) { int i, count_generic = 0; GList *list = NULL, *tmp; if (generic_array_method_num) return generic_array_method_num; mono_class_setup_methods (class->parent); /*This is setting up System.Array*/ g_assert (!class->parent->exception_type); /*So hitting this assert is a huge problem*/ for (i = 0; i < class->parent->method.count; i++) { MonoMethod *m = class->parent->methods [i]; if (!strncmp (m->name, "InternalArray__", 15)) { count_generic++; list = g_list_prepend (list, m); } } list = g_list_reverse (list); generic_array_method_info = mono_image_alloc (mono_defaults.corlib, sizeof (GenericArrayMethodInfo) * count_generic); i = 0; for (tmp = list; tmp; tmp = tmp->next) { const char *mname, *iname; gchar *name; MonoMethod *m = tmp->data; generic_array_method_info [i].array_method = m; if (!strncmp (m->name, "InternalArray__ICollection_", 27)) { iname = "System.Collections.Generic.ICollection`1."; mname = m->name + 27; } else if (!strncmp (m->name, "InternalArray__IEnumerable_", 27)) { iname = "System.Collections.Generic.IEnumerable`1."; mname = m->name + 27; } else if (!strncmp (m->name, "InternalArray__", 15)) { iname = "System.Collections.Generic.IList`1."; mname = m->name + 15; } else { g_assert_not_reached (); } name = mono_image_alloc (mono_defaults.corlib, strlen (iname) + strlen (mname) + 1); strcpy (name, iname); strcpy (name + strlen (iname), mname); generic_array_method_info [i].name = name; i++; } /*g_print ("array generic methods: %d\n", count_generic);*/ generic_array_method_num = count_generic; g_list_free (list); return generic_array_method_num; } static void setup_generic_array_ifaces (MonoClass *class, MonoClass *iface, MonoMethod **methods, int pos) { MonoGenericContext tmp_context; int i; tmp_context.class_inst = NULL; tmp_context.method_inst = iface->generic_class->context.class_inst; //g_print ("setting up array interface: %s\n", mono_type_get_name_full (&iface->byval_arg, 0)); for (i = 0; i < generic_array_method_num; i++) { MonoMethod *m = generic_array_method_info [i].array_method; MonoMethod *inflated; inflated = mono_class_inflate_generic_method (m, &tmp_context); methods [pos++] = mono_marshal_get_generic_array_helper (class, iface, generic_array_method_info [i].name, inflated); } } static char* concat_two_strings_with_zero (MonoImage *image, const char *s1, const char *s2) { int null_length = strlen ("(null)"); int len = (s1 ? strlen (s1) : null_length) + (s2 ? strlen (s2) : null_length) + 2; char *s = mono_image_alloc (image, len); int result; result = g_snprintf (s, len, "%s%c%s", s1 ? s1 : "(null)", '\0', s2 ? s2 : "(null)"); g_assert (result == len - 1); return s; } static void set_failure_from_loader_error (MonoClass *class, MonoLoaderError *error) { gpointer exception_data = NULL; switch (error->exception_type) { case MONO_EXCEPTION_TYPE_LOAD: exception_data = concat_two_strings_with_zero (class->image, error->class_name, error->assembly_name); break; case MONO_EXCEPTION_MISSING_METHOD: exception_data = concat_two_strings_with_zero (class->image, error->class_name, error->member_name); break; case MONO_EXCEPTION_MISSING_FIELD: { const char *name_space = error->klass->name_space ? error->klass->name_space : NULL; const char *class_name; if (name_space) class_name = g_strdup_printf ("%s.%s", name_space, error->klass->name); else class_name = error->klass->name; exception_data = concat_two_strings_with_zero (class->image, class_name, error->member_name); if (name_space) g_free ((void*)class_name); break; } case MONO_EXCEPTION_FILE_NOT_FOUND: { const char *msg; if (error->ref_only) msg = "Cannot resolve dependency to assembly '%s' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event."; else msg = "Could not load file or assembly '%s' or one of its dependencies."; exception_data = concat_two_strings_with_zero (class->image, msg, error->assembly_name); break; } case MONO_EXCEPTION_BAD_IMAGE: exception_data = error->msg; break; default : g_assert_not_reached (); } mono_class_set_failure (class, error->exception_type, exception_data); } /** * mono_class_init: * @class: the class to initialize * * Compute the instance_size, class_size and other infos that cannot be * computed at mono_class_get() time. Also compute vtable_size if possible. * Returns TRUE on success or FALSE if there was a problem in loading * the type (incorrect assemblies, missing assemblies, methods, etc). * * LOCKING: Acquires the loader lock. */ gboolean mono_class_init (MonoClass *class) { int i; MonoCachedClassInfo cached_info; gboolean has_cached_info; g_assert (class); /* Double-checking locking pattern */ if (class->inited || class->exception_type) return class->exception_type == MONO_EXCEPTION_NONE; /*g_print ("Init class %s\n", mono_type_get_full_name (class));*/ /* We do everything inside the lock to prevent races */ mono_loader_lock (); if (class->inited || class->exception_type) { mono_loader_unlock (); /* Somebody might have gotten in before us */ return class->exception_type == MONO_EXCEPTION_NONE; } if (class->init_pending) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Recursive type definition detected")); goto leave; } class->init_pending = 1; if (mono_verifier_is_enabled_for_class (class) && !mono_verifier_verify_class (class)) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, concat_two_strings_with_zero (class->image, class->name, class->image->assembly_name)); goto leave; } if (class->byval_arg.type == MONO_TYPE_ARRAY || class->byval_arg.type == MONO_TYPE_SZARRAY) { MonoClass *element_class = class->element_class; if (!element_class->inited) mono_class_init (element_class); if (element_class->exception_type != MONO_EXCEPTION_NONE) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); goto leave; } } /* CAS - SecurityAction.InheritanceDemand */ if (mono_is_security_manager_active () && class->parent && (class->parent->flags & TYPE_ATTRIBUTE_HAS_SECURITY)) { mono_secman_inheritancedemand_class (class, class->parent); } mono_stats.initialized_class_count++; if (class->generic_class && !class->generic_class->is_dynamic) { MonoClass *gklass = class->generic_class->container_class; mono_stats.generic_class_count++; class->method = gklass->method; class->field = gklass->field; mono_class_init (gklass); // FIXME: Why is this needed ? if (!gklass->exception_type) mono_class_setup_methods (gklass); if (gklass->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Generic Type Defintion failed to init")); goto leave; } if (MONO_CLASS_IS_INTERFACE (class)) class->interface_id = mono_get_unique_iid (class); } if (class->parent && !class->parent->inited) mono_class_init (class->parent); has_cached_info = mono_class_get_cached_class_info (class, &cached_info); if (class->generic_class || class->image->dynamic || !class->type_token || (has_cached_info && !cached_info.has_nested_classes)) class->nested_classes_inited = TRUE; /* * Computes the size used by the fields, and their locations */ if (has_cached_info) { class->instance_size = cached_info.instance_size; class->sizes.class_size = cached_info.class_size; class->packing_size = cached_info.packing_size; class->min_align = cached_info.min_align; class->blittable = cached_info.blittable; class->has_references = cached_info.has_references; class->has_static_refs = cached_info.has_static_refs; class->no_special_static_fields = cached_info.no_special_static_fields; } else if (!class->size_inited){ mono_class_setup_fields (class); if (class->exception_type || mono_loader_get_last_error ()) goto leave; } /* Initialize arrays */ if (class->rank) { class->method.count = 3 + (class->rank > 1? 2: 1); if (class->interface_count) { int count_generic = generic_array_methods (class); class->method.count += class->interface_count * count_generic; } } mono_class_setup_supertypes (class); if (!default_ghc) initialize_object_slots (class); /* * Initialize the rest of the data without creating a generic vtable if possible. * If possible, also compute vtable_size, so mono_class_create_runtime_vtable () can * also avoid computing a generic vtable. */ if (has_cached_info) { /* AOT case */ class->vtable_size = cached_info.vtable_size; class->has_finalize = cached_info.has_finalize; class->has_finalize_inited = TRUE; class->ghcimpl = cached_info.ghcimpl; class->has_cctor = cached_info.has_cctor; } else if (class->rank == 1 && class->byval_arg.type == MONO_TYPE_SZARRAY) { /* SZARRAY can have 2 vtable layouts, with and without the stelemref method. * The first slot if for array with. */ static int szarray_vtable_size[2] = { 0 }; int slot = MONO_TYPE_IS_REFERENCE (&class->element_class->byval_arg) ? 0 : 1; /* SZARRAY case */ if (!szarray_vtable_size [slot]) { mono_class_setup_vtable (class); szarray_vtable_size [slot] = class->vtable_size; } else { class->vtable_size = szarray_vtable_size[slot]; } class->has_finalize_inited = TRUE; } else if (class->generic_class && !MONO_CLASS_IS_INTERFACE (class)) { MonoClass *gklass = class->generic_class->container_class; /* Generic instance case */ class->ghcimpl = gklass->ghcimpl; class->has_finalize = mono_class_has_finalizer (gklass); class->has_finalize_inited = TRUE; class->has_cctor = gklass->has_cctor; mono_class_setup_vtable (gklass); if (gklass->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); goto leave; } class->vtable_size = gklass->vtable_size; } else { /* General case */ /* ghcimpl is not currently used class->ghcimpl = 1; if (class->parent) { MonoMethod *cmethod = class->vtable [ghc_slot]; if (cmethod->is_inflated) cmethod = ((MonoMethodInflated*)cmethod)->declaring; if (cmethod == default_ghc) { class->ghcimpl = 0; } } */ /* C# doesn't allow interfaces to have cctors */ if (!MONO_CLASS_IS_INTERFACE (class) || class->image != mono_defaults.corlib) { MonoMethod *cmethod = NULL; if (class->type_token) { cmethod = find_method_in_metadata (class, ".cctor", 0, METHOD_ATTRIBUTE_SPECIAL_NAME); /* The find_method function ignores the 'flags' argument */ if (cmethod && (cmethod->flags & METHOD_ATTRIBUTE_SPECIAL_NAME)) class->has_cctor = 1; } else { mono_class_setup_methods (class); if (class->exception_type) goto leave; for (i = 0; i < class->method.count; ++i) { MonoMethod *method = class->methods [i]; if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && (strcmp (".cctor", method->name) == 0)) { class->has_cctor = 1; break; } } } } } if (class->parent) { int first_iface_slot; /* This will compute class->parent->vtable_size for some classes */ mono_class_init (class->parent); if (class->parent->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); goto leave; } if (mono_loader_get_last_error ()) goto leave; if (!class->parent->vtable_size) { /* FIXME: Get rid of this somehow */ mono_class_setup_vtable (class->parent); if (class->parent->exception_type) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); goto leave; } if (mono_loader_get_last_error ()) goto leave; } first_iface_slot = class->parent->vtable_size; if (mono_class_need_stelemref_method (class)) ++first_iface_slot; setup_interface_offsets (class, first_iface_slot); } else { setup_interface_offsets (class, 0); } if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) mono_security_core_clr_check_inheritance (class); if (mono_loader_get_last_error ()) { if (class->exception_type == MONO_EXCEPTION_NONE) { set_failure_from_loader_error (class, mono_loader_get_last_error ()); } mono_loader_clear_error (); } if (class->generic_class && !mono_verifier_class_is_valid_generic_instantiation (class)) mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Invalid generic instantiation")); goto leave; leave: /* Because of the double-checking locking pattern */ mono_memory_barrier (); class->inited = 1; class->init_pending = 0; mono_loader_unlock (); if (mono_debugger_class_init_func) mono_debugger_class_init_func (class); return class->exception_type == MONO_EXCEPTION_NONE; } /* * mono_class_has_finalizer: * * Return whenever KLASS has a finalizer, initializing klass->has_finalizer in the * process. */ gboolean mono_class_has_finalizer (MonoClass *klass) { if (!klass->has_finalize_inited) { MonoClass *class = klass; mono_loader_lock (); /* Interfaces and valuetypes are not supposed to have finalizers */ if (!(MONO_CLASS_IS_INTERFACE (class) || class->valuetype)) { MonoMethod *cmethod = NULL; if (class->parent && class->parent->has_finalize) { class->has_finalize = 1; } else { if (class->parent) { /* * Can't search in metadata for a method named Finalize, because that * ignores overrides. */ mono_class_setup_vtable (class); if (class->exception_type || mono_loader_get_last_error ()) goto leave; cmethod = class->vtable [finalize_slot]; } if (cmethod) { g_assert (class->vtable_size > finalize_slot); class->has_finalize = 0; if (class->parent) { if (cmethod->is_inflated) cmethod = ((MonoMethodInflated*)cmethod)->declaring; if (cmethod != default_finalize) { class->has_finalize = 1; } } } } } mono_memory_barrier (); klass->has_finalize_inited = TRUE; mono_loader_unlock (); } return klass->has_finalize; leave: mono_loader_unlock (); return FALSE; } gboolean mono_is_corlib_image (MonoImage *image) { /* FIXME: allow the dynamic case for our compilers and with full trust */ if (image->dynamic) return image->assembly && !strcmp (image->assembly->aname.name, "mscorlib"); else return image == mono_defaults.corlib; } /* * LOCKING: this assumes the loader lock is held */ void mono_class_setup_mono_type (MonoClass *class) { const char *name = class->name; const char *nspace = class->name_space; gboolean is_corlib = mono_is_corlib_image (class->image); class->this_arg.byref = 1; class->this_arg.data.klass = class; class->this_arg.type = MONO_TYPE_CLASS; class->byval_arg.data.klass = class; class->byval_arg.type = MONO_TYPE_CLASS; if (is_corlib && !strcmp (nspace, "System")) { if (!strcmp (name, "ValueType")) { /* * do not set the valuetype bit for System.ValueType. * class->valuetype = 1; */ class->blittable = TRUE; } else if (!strcmp (name, "Enum")) { /* * do not set the valuetype bit for System.Enum. * class->valuetype = 1; */ class->valuetype = 0; class->enumtype = 0; } else if (!strcmp (name, "Object")) { class->this_arg.type = class->byval_arg.type = MONO_TYPE_OBJECT; } else if (!strcmp (name, "String")) { class->this_arg.type = class->byval_arg.type = MONO_TYPE_STRING; } else if (!strcmp (name, "TypedReference")) { class->this_arg.type = class->byval_arg.type = MONO_TYPE_TYPEDBYREF; } } if (class->valuetype) { int t = MONO_TYPE_VALUETYPE; if (is_corlib && !strcmp (nspace, "System")) { switch (*name) { case 'B': if (!strcmp (name, "Boolean")) { t = MONO_TYPE_BOOLEAN; } else if (!strcmp(name, "Byte")) { t = MONO_TYPE_U1; class->blittable = TRUE; } break; case 'C': if (!strcmp (name, "Char")) { t = MONO_TYPE_CHAR; } break; case 'D': if (!strcmp (name, "Double")) { t = MONO_TYPE_R8; class->blittable = TRUE; } break; case 'I': if (!strcmp (name, "Int32")) { t = MONO_TYPE_I4; class->blittable = TRUE; } else if (!strcmp(name, "Int16")) { t = MONO_TYPE_I2; class->blittable = TRUE; } else if (!strcmp(name, "Int64")) { t = MONO_TYPE_I8; class->blittable = TRUE; } else if (!strcmp(name, "IntPtr")) { t = MONO_TYPE_I; class->blittable = TRUE; } break; case 'S': if (!strcmp (name, "Single")) { t = MONO_TYPE_R4; class->blittable = TRUE; } else if (!strcmp(name, "SByte")) { t = MONO_TYPE_I1; class->blittable = TRUE; } break; case 'U': if (!strcmp (name, "UInt32")) { t = MONO_TYPE_U4; class->blittable = TRUE; } else if (!strcmp(name, "UInt16")) { t = MONO_TYPE_U2; class->blittable = TRUE; } else if (!strcmp(name, "UInt64")) { t = MONO_TYPE_U8; class->blittable = TRUE; } else if (!strcmp(name, "UIntPtr")) { t = MONO_TYPE_U; class->blittable = TRUE; } break; case 'T': if (!strcmp (name, "TypedReference")) { t = MONO_TYPE_TYPEDBYREF; class->blittable = TRUE; } break; case 'V': if (!strcmp (name, "Void")) { t = MONO_TYPE_VOID; } break; default: break; } } class->this_arg.type = class->byval_arg.type = t; } if (MONO_CLASS_IS_INTERFACE (class)) class->interface_id = mono_get_unique_iid (class); } /* * COM initialization (using mono_init_com_types) is delayed until needed. * However when a [ComImport] attribute is present on a type it will trigger * the initialization. This is not a problem unless the BCL being executed * lacks the types that COM depends on (e.g. Variant on Silverlight). */ static void init_com_from_comimport (MonoClass *class) { /* we don't always allow COM initialization under the CoreCLR (e.g. Moonlight does not require it) */ if ((mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR)) { /* but some other CoreCLR user could requires it for their platform (i.e. trusted) code */ if (!mono_security_core_clr_determine_platform_image (class->image)) { /* but it can not be made available for application (i.e. user code) since all COM calls * are considered native calls. In this case we fail with a TypeLoadException (just like * Silverlight 2 does */ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } } /* FIXME : we should add an extra checks to ensure COM can be initialized properly before continuing */ mono_init_com_types (); } /* * LOCKING: this assumes the loader lock is held */ void mono_class_setup_parent (MonoClass *class, MonoClass *parent) { gboolean system_namespace; gboolean is_corlib = mono_is_corlib_image (class->image); system_namespace = !strcmp (class->name_space, "System") && is_corlib; /* if root of the hierarchy */ if (system_namespace && !strcmp (class->name, "Object")) { class->parent = NULL; class->instance_size = sizeof (MonoObject); return; } if (!strcmp (class->name, "")) { class->parent = NULL; class->instance_size = 0; return; } if (!MONO_CLASS_IS_INTERFACE (class)) { /* Imported COM Objects always derive from __ComObject. */ if (MONO_CLASS_IS_IMPORT (class)) { init_com_from_comimport (class); if (parent == mono_defaults.object_class) parent = mono_defaults.com_object_class; } if (!parent) { /* set the parent to something useful and safe, but mark the type as broken */ parent = mono_defaults.object_class; mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); } class->parent = parent; if (parent->generic_class && !parent->name) { /* * If the parent is a generic instance, we may get * called before it is fully initialized, especially * before it has its name. */ return; } class->marshalbyref = parent->marshalbyref; class->contextbound = parent->contextbound; class->delegate = parent->delegate; if (MONO_CLASS_IS_IMPORT (class)) class->is_com_object = 1; else class->is_com_object = parent->is_com_object; if (system_namespace) { if (*class->name == 'M' && !strcmp (class->name, "MarshalByRefObject")) class->marshalbyref = 1; if (*class->name == 'C' && !strcmp (class->name, "ContextBoundObject")) class->contextbound = 1; if (*class->name == 'D' && !strcmp (class->name, "Delegate")) class->delegate = 1; } if (class->parent->enumtype || (mono_is_corlib_image (class->parent->image) && (strcmp (class->parent->name, "ValueType") == 0) && (strcmp (class->parent->name_space, "System") == 0))) class->valuetype = 1; if (mono_is_corlib_image (class->parent->image) && ((strcmp (class->parent->name, "Enum") == 0) && (strcmp (class->parent->name_space, "System") == 0))) { class->valuetype = class->enumtype = 1; } /*class->enumtype = class->parent->enumtype; */ } else { /* initialize com types if COM interfaces are present */ if (MONO_CLASS_IS_IMPORT (class)) init_com_from_comimport (class); class->parent = NULL; } } /* * mono_class_setup_supertypes: * @class: a class * * Build the data structure needed to make fast type checks work. * This currently sets two fields in @class: * - idepth: distance between @class and System.Object in the type * hierarchy + 1 * - supertypes: array of classes: each element has a class in the hierarchy * starting from @class up to System.Object * * LOCKING: this assumes the loader lock is held */ void mono_class_setup_supertypes (MonoClass *class) { int ms; MonoClass **supertypes; if (class->supertypes) return; if (class->parent && !class->parent->supertypes) mono_class_setup_supertypes (class->parent); if (class->parent) class->idepth = class->parent->idepth + 1; else class->idepth = 1; ms = MAX (MONO_DEFAULT_SUPERTABLE_SIZE, class->idepth); supertypes = mono_class_alloc0 (class, sizeof (MonoClass *) * ms); if (class->parent) { supertypes [class->idepth - 1] = class; memcpy (supertypes, class->parent->supertypes, class->parent->idepth * sizeof (gpointer)); } else { supertypes [0] = class; } mono_atomic_store_release (&class->supertypes, supertypes); } /** * mono_class_create_from_typedef: * @image: image where the token is valid * @type_token: typedef token * * Create the MonoClass* representing the specified type token. * @type_token must be a TypeDef token. * * FIXME: don't return NULL on failure, just the the caller figure it out. */ static MonoClass * mono_class_create_from_typedef (MonoImage *image, guint32 type_token) { MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; MonoClass *class, *parent = NULL; guint32 cols [MONO_TYPEDEF_SIZE]; guint32 cols_next [MONO_TYPEDEF_SIZE]; guint tidx = mono_metadata_token_index (type_token); MonoGenericContext *context = NULL; const char *name, *nspace; guint icount = 0; MonoClass **interfaces; guint32 field_last, method_last; guint32 nesting_tokeen; if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows) return NULL; mono_loader_lock (); if ((class = mono_internal_hash_table_lookup (&image->class_cache, GUINT_TO_POINTER (type_token)))) { mono_loader_unlock (); return class; } mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); class = mono_image_alloc0 (image, sizeof (MonoClass)); class->name = name; class->name_space = nspace; mono_profiler_class_event (class, MONO_PROFILE_START_LOAD); class->image = image; class->type_token = type_token; class->flags = cols [MONO_TYPEDEF_FLAGS]; mono_internal_hash_table_insert (&image->class_cache, GUINT_TO_POINTER (type_token), class); classes_size += sizeof (MonoClass); /* * Check whether we're a generic type definition. */ class->generic_container = mono_metadata_load_generic_params (image, class->type_token, NULL); if (class->generic_container) { class->is_generic = 1; class->generic_container->owner.klass = class; context = &class->generic_container->context; } if (cols [MONO_TYPEDEF_EXTENDS]) { MonoClass *tmp; guint32 parent_token = mono_metadata_token_from_dor (cols [MONO_TYPEDEF_EXTENDS]); if (mono_metadata_token_table (parent_token) == MONO_TABLE_TYPESPEC) { /*WARNING: this must satisfy mono_metadata_type_hash*/ class->this_arg.byref = 1; class->this_arg.data.klass = class; class->this_arg.type = MONO_TYPE_CLASS; class->byval_arg.data.klass = class; class->byval_arg.type = MONO_TYPE_CLASS; } parent = mono_class_get_full (image, parent_token, context); if (parent == NULL){ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load parent type")); mono_loader_clear_error (); goto parent_failure; } for (tmp = parent; tmp; tmp = tmp->parent) { if (tmp == class) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Cycle found while resolving parent")); goto parent_failure; } if (class->generic_container && tmp->generic_class && tmp->generic_class->container_class == class) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Parent extends generic instance of this type")); goto parent_failure; } } } mono_class_setup_parent (class, parent); /* uses ->valuetype, which is initialized by mono_class_setup_parent above */ mono_class_setup_mono_type (class); /* * This might access class->byval_arg for recursion generated by generic constraints, * so it has to come after setup_mono_type (). */ if ((nesting_tokeen = mono_metadata_nested_in_typedef (image, type_token))) { class->nested_in = mono_class_create_from_typedef (image, nesting_tokeen); if (!class->nested_in) { mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load nestedin type")); mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_FAILED); return NULL; } } if ((class->flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == TYPE_ATTRIBUTE_UNICODE_CLASS) class->unicode = 1; #ifdef HOST_WIN32 if ((class->flags & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == TYPE_ATTRIBUTE_AUTO_CLASS) class->unicode = 1; #endif class->cast_class = class->element_class = class; if (!class->enumtype) { if (!mono_metadata_interfaces_from_typedef_full ( image, type_token, &interfaces, &icount, FALSE, context)){ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load interfaces")); mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_FAILED); return NULL; } class->interfaces = interfaces; class->interface_count = icount; class->interfaces_inited = 1; } /*g_print ("Load class %s\n", name);*/ /* * Compute the field and method lists */ class->field.first = cols [MONO_TYPEDEF_FIELD_LIST] - 1; class->method.first = cols [MONO_TYPEDEF_METHOD_LIST] - 1; if (tt->rows > tidx){ mono_metadata_decode_row (tt, tidx, cols_next, MONO_TYPEDEF_SIZE); field_last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; method_last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; } else { field_last = image->tables [MONO_TABLE_FIELD].rows; method_last = image->tables [MONO_TABLE_METHOD].rows; } if (cols [MONO_TYPEDEF_FIELD_LIST] && cols [MONO_TYPEDEF_FIELD_LIST] <= image->tables [MONO_TABLE_FIELD].rows) class->field.count = field_last - class->field.first; else class->field.count = 0; if (cols [MONO_TYPEDEF_METHOD_LIST] <= image->tables [MONO_TABLE_METHOD].rows) class->method.count = method_last - class->method.first; else class->method.count = 0; /* reserve space to store vector pointer in arrays */ if (mono_is_corlib_image (image) && !strcmp (nspace, "System") && !strcmp (name, "Array")) { class->instance_size += 2 * sizeof (gpointer); g_assert (class->field.count == 0); } if (class->enumtype) { MonoType *enum_basetype = mono_class_find_enum_basetype (class); if (!enum_basetype) { /*set it to a default value as the whole runtime can't handle this to be null*/ class->cast_class = class->element_class = mono_defaults.int32_class; mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_FAILED); return NULL; } class->cast_class = class->element_class = mono_class_from_mono_type (enum_basetype); } /* * If we're a generic type definition, load the constraints. * We must do this after the class has been constructed to make certain recursive scenarios * work. */ if (class->generic_container && !mono_metadata_load_generic_param_constraints_full (image, type_token, class->generic_container)){ char *class_name = g_strdup_printf("%s.%s", class->name_space, class->name); char *error = concat_two_strings_with_zero (class->image, class_name, class->image->assembly_name); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, error); g_free (class_name); mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_FAILED); return NULL; } if (class->image->assembly_name && !strcmp (class->image->assembly_name, "Mono.Simd") && !strcmp (nspace, "Mono.Simd")) { if (!strncmp (name, "Vector", 6)) class->simd_type = !strcmp (name + 6, "2d") || !strcmp (name + 6, "2ul") || !strcmp (name + 6, "2l") || !strcmp (name + 6, "4f") || !strcmp (name + 6, "4ui") || !strcmp (name + 6, "4i") || !strcmp (name + 6, "8s") || !strcmp (name + 6, "8us") || !strcmp (name + 6, "16b") || !strcmp (name + 6, "16sb"); } mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_OK); return class; parent_failure: mono_class_setup_mono_type (class); mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_FAILED); return NULL; } /** is klass Nullable? */ gboolean mono_class_is_nullable (MonoClass *klass) { return klass->generic_class != NULL && klass->generic_class->container_class == mono_defaults.generic_nullable_class; } /** if klass is T? return T */ MonoClass* mono_class_get_nullable_param (MonoClass *klass) { g_assert (mono_class_is_nullable (klass)); return mono_class_from_mono_type (klass->generic_class->context.class_inst->type_argv [0]); } /* * Create the `MonoClass' for an instantiation of a generic type. * We only do this if we actually need it. */ MonoClass* mono_generic_class_get_class (MonoGenericClass *gclass) { MonoClass *klass, *gklass; if (gclass->cached_class) return gclass->cached_class; mono_loader_lock (); if (gclass->cached_class) { mono_loader_unlock (); return gclass->cached_class; } klass = mono_image_set_alloc0 (gclass->owner, sizeof (MonoClass)); gklass = gclass->container_class; if (gklass->nested_in) { /* The nested_in type should not be inflated since it's possible to produce a nested type with less generic arguments*/ klass->nested_in = gklass->nested_in; } klass->name = gklass->name; klass->name_space = gklass->name_space; mono_profiler_class_event (klass, MONO_PROFILE_START_LOAD); klass->image = gklass->image; klass->flags = gklass->flags; klass->type_token = gklass->type_token; klass->field.count = gklass->field.count; klass->is_inflated = 1; klass->generic_class = gclass; klass->this_arg.type = klass->byval_arg.type = MONO_TYPE_GENERICINST; klass->this_arg.data.generic_class = klass->byval_arg.data.generic_class = gclass; klass->this_arg.byref = TRUE; klass->enumtype = gklass->enumtype; klass->valuetype = gklass->valuetype; klass->cast_class = klass->element_class = klass; if (mono_class_is_nullable (klass)) klass->cast_class = klass->element_class = mono_class_get_nullable_param (klass); /* * We're not interested in the nested classes of a generic instance. * We use the generic type definition to look for nested classes. */ if (gklass->parent) { MonoError error; klass->parent = mono_class_inflate_generic_class_checked (gklass->parent, mono_generic_class_get_context (gclass), &error); if (!mono_error_ok (&error)) { /*Set parent to something safe as the runtime doesn't handle well this kind of failure.*/ klass->parent = mono_defaults.object_class; mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL); mono_error_cleanup (&error); } } if (klass->parent) mono_class_setup_parent (klass, klass->parent); if (klass->enumtype) { klass->cast_class = gklass->cast_class; klass->element_class = gklass->element_class; } if (gclass->is_dynamic) { klass->inited = 1; mono_class_setup_supertypes (klass); if (klass->enumtype) { /* * For enums, gklass->fields might not been set, but instance_size etc. is * already set in mono_reflection_create_internal_class (). For non-enums, * these will be computed normally in mono_class_layout_fields (). */ klass->instance_size = gklass->instance_size; klass->sizes.class_size = gklass->sizes.class_size; klass->size_inited = 1; } } mono_memory_barrier (); gclass->cached_class = klass; mono_profiler_class_loaded (klass, MONO_PROFILE_OK); inflated_classes ++; inflated_classes_size += sizeof (MonoClass); mono_loader_unlock (); return klass; } static MonoClass* make_generic_param_class (MonoGenericParam *param, MonoImage *image, gboolean is_mvar, MonoGenericParamInfo *pinfo) { MonoClass *klass, **ptr; int count, pos, i; MonoGenericContainer *container = mono_generic_param_owner (param); if (!image) /* FIXME: */ image = mono_defaults.corlib; klass = mono_image_alloc0 (image, sizeof (MonoClass)); classes_size += sizeof (MonoClass); if (pinfo) { klass->name = pinfo->name; } else { int n = mono_generic_param_num (param); klass->name = mono_image_alloc0 (image, 16); sprintf ((char*)klass->name, "%d", n); } if (container) { if (is_mvar) { MonoMethod *omethod = container->owner.method; klass->name_space = (omethod && omethod->klass) ? omethod->klass->name_space : ""; } else { MonoClass *oklass = container->owner.klass; klass->name_space = oklass ? oklass->name_space : ""; } } else { klass->name_space = ""; } mono_profiler_class_event (klass, MONO_PROFILE_START_LOAD); count = 0; if (pinfo) for (ptr = pinfo->constraints; ptr && *ptr; ptr++, count++) ; pos = 0; if ((count > 0) && !MONO_CLASS_IS_INTERFACE (pinfo->constraints [0])) { klass->parent = pinfo->constraints [0]; pos++; } else if (pinfo && pinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) klass->parent = mono_class_from_name (mono_defaults.corlib, "System", "ValueType"); else klass->parent = mono_defaults.object_class; if (count - pos > 0) { klass->interface_count = count - pos; klass->interfaces = mono_image_alloc0 (image, sizeof (MonoClass *) * (count - pos)); klass->interfaces_inited = TRUE; for (i = pos; i < count; i++) klass->interfaces [i - pos] = pinfo->constraints [i]; } klass->image = image; klass->inited = TRUE; klass->cast_class = klass->element_class = klass; klass->flags = TYPE_ATTRIBUTE_PUBLIC; klass->this_arg.type = klass->byval_arg.type = is_mvar ? MONO_TYPE_MVAR : MONO_TYPE_VAR; klass->this_arg.data.generic_param = klass->byval_arg.data.generic_param = param; klass->this_arg.byref = TRUE; /* We don't use type_token for VAR since only classes can use it (not arrays, pointer, VARs, etc) */ klass->sizes.generic_param_token = pinfo ? pinfo->token : 0; /*Init these fields to sane values*/ klass->min_align = 1; klass->instance_size = sizeof (gpointer); klass->size_inited = 1; mono_class_setup_supertypes (klass); if (count - pos > 0) { mono_class_setup_vtable (klass->parent); if (klass->parent->exception_type) mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Failed to setup parent interfaces")); else setup_interface_offsets (klass, klass->parent->vtable_size); } return klass; } #define FAST_CACHE_SIZE 16 static MonoClass * get_anon_gparam_class (MonoGenericParam *param, gboolean is_mvar) { int n = mono_generic_param_num (param); MonoImage *image = param->image; GHashTable *ht; g_assert (image); if (n < FAST_CACHE_SIZE) { if (is_mvar) return image->mvar_cache_fast ? image->mvar_cache_fast [n] : NULL; else return image->var_cache_fast ? image->var_cache_fast [n] : NULL; } else { ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; return ht ? g_hash_table_lookup (ht, GINT_TO_POINTER (n)) : NULL; } } /* * LOCKING: Acquires the loader lock. */ static void set_anon_gparam_class (MonoGenericParam *param, gboolean is_mvar, MonoClass *klass) { int n = mono_generic_param_num (param); MonoImage *image = param->image; GHashTable *ht; g_assert (image); if (n < FAST_CACHE_SIZE) { if (is_mvar) { /* No locking needed */ if (!image->mvar_cache_fast) image->mvar_cache_fast = mono_image_alloc0 (image, sizeof (MonoClass*) * FAST_CACHE_SIZE); image->mvar_cache_fast [n] = klass; } else { if (!image->var_cache_fast) image->var_cache_fast = mono_image_alloc0 (image, sizeof (MonoClass*) * FAST_CACHE_SIZE); image->var_cache_fast [n] = klass; } return; } ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; if (!ht) { mono_loader_lock (); ht = is_mvar ? image->mvar_cache_slow : image->var_cache_slow; if (!ht) { ht = g_hash_table_new (NULL, NULL); mono_memory_barrier (); if (is_mvar) image->mvar_cache_slow = ht; else image->var_cache_slow = ht; } mono_loader_unlock (); } g_hash_table_insert (ht, GINT_TO_POINTER (n), klass); } /* * LOCKING: Acquires the loader lock. */ MonoClass * mono_class_from_generic_parameter (MonoGenericParam *param, MonoImage *image, gboolean is_mvar) { MonoGenericContainer *container = mono_generic_param_owner (param); MonoGenericParamInfo *pinfo; MonoClass *klass; mono_loader_lock (); if (container) { pinfo = mono_generic_param_info (param); if (pinfo->pklass) { mono_loader_unlock (); return pinfo->pklass; } } else { pinfo = NULL; image = NULL; klass = get_anon_gparam_class (param, is_mvar); if (klass) { mono_loader_unlock (); return klass; } } if (!image && container) { if (is_mvar) { MonoMethod *method = container->owner.method; image = (method && method->klass) ? method->klass->image : NULL; } else { MonoClass *klass = container->owner.klass; // FIXME: 'klass' should not be null // But, monodis creates GenericContainers without associating a owner to it image = klass ? klass->image : NULL; } } klass = make_generic_param_class (param, image, is_mvar, pinfo); mono_memory_barrier (); if (container) pinfo->pklass = klass; else set_anon_gparam_class (param, is_mvar, klass); mono_loader_unlock (); /* FIXME: Should this go inside 'make_generic_param_klass'? */ mono_profiler_class_loaded (klass, MONO_PROFILE_OK); return klass; } MonoClass * mono_ptr_class_get (MonoType *type) { MonoClass *result; MonoClass *el_class; MonoImage *image; char *name; el_class = mono_class_from_mono_type (type); image = el_class->image; mono_loader_lock (); if (!image->ptr_cache) image->ptr_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); if ((result = g_hash_table_lookup (image->ptr_cache, el_class))) { mono_loader_unlock (); return result; } result = mono_image_alloc0 (image, sizeof (MonoClass)); classes_size += sizeof (MonoClass); result->parent = NULL; /* no parent for PTR types */ result->name_space = el_class->name_space; name = g_strdup_printf ("%s*", el_class->name); result->name = mono_image_strdup (image, name); g_free (name); mono_profiler_class_event (result, MONO_PROFILE_START_LOAD); result->image = el_class->image; result->inited = TRUE; result->flags = TYPE_ATTRIBUTE_CLASS | (el_class->flags & TYPE_ATTRIBUTE_VISIBILITY_MASK); /* Can pointers get boxed? */ result->instance_size = sizeof (gpointer); result->cast_class = result->element_class = el_class; result->blittable = TRUE; result->this_arg.type = result->byval_arg.type = MONO_TYPE_PTR; result->this_arg.data.type = result->byval_arg.data.type = &result->element_class->byval_arg; result->this_arg.byref = TRUE; mono_class_setup_supertypes (result); g_hash_table_insert (image->ptr_cache, el_class, result); mono_loader_unlock (); mono_profiler_class_loaded (result, MONO_PROFILE_OK); return result; } static MonoClass * mono_fnptr_class_get (MonoMethodSignature *sig) { MonoClass *result; static GHashTable *ptr_hash = NULL; /* FIXME: These should be allocate from a mempool as well, but which one ? */ mono_loader_lock (); if (!ptr_hash) ptr_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); if ((result = g_hash_table_lookup (ptr_hash, sig))) { mono_loader_unlock (); return result; } result = g_new0 (MonoClass, 1); result->parent = NULL; /* no parent for PTR types */ result->name_space = "System"; result->name = "MonoFNPtrFakeClass"; mono_profiler_class_event (result, MONO_PROFILE_START_LOAD); result->image = mono_defaults.corlib; /* need to fix... */ result->inited = TRUE; result->flags = TYPE_ATTRIBUTE_CLASS; /* | (el_class->flags & TYPE_ATTRIBUTE_VISIBILITY_MASK); */ /* Can pointers get boxed? */ result->instance_size = sizeof (gpointer); result->cast_class = result->element_class = result; result->blittable = TRUE; result->this_arg.type = result->byval_arg.type = MONO_TYPE_FNPTR; result->this_arg.data.method = result->byval_arg.data.method = sig; result->this_arg.byref = TRUE; result->blittable = TRUE; mono_class_setup_supertypes (result); g_hash_table_insert (ptr_hash, sig, result); mono_loader_unlock (); mono_profiler_class_loaded (result, MONO_PROFILE_OK); return result; } MonoClass * mono_class_from_mono_type (MonoType *type) { switch (type->type) { case MONO_TYPE_OBJECT: return type->data.klass? type->data.klass: mono_defaults.object_class; case MONO_TYPE_VOID: return type->data.klass? type->data.klass: mono_defaults.void_class; case MONO_TYPE_BOOLEAN: return type->data.klass? type->data.klass: mono_defaults.boolean_class; case MONO_TYPE_CHAR: return type->data.klass? type->data.klass: mono_defaults.char_class; case MONO_TYPE_I1: return type->data.klass? type->data.klass: mono_defaults.sbyte_class; case MONO_TYPE_U1: return type->data.klass? type->data.klass: mono_defaults.byte_class; case MONO_TYPE_I2: return type->data.klass? type->data.klass: mono_defaults.int16_class; case MONO_TYPE_U2: return type->data.klass? type->data.klass: mono_defaults.uint16_class; case MONO_TYPE_I4: return type->data.klass? type->data.klass: mono_defaults.int32_class; case MONO_TYPE_U4: return type->data.klass? type->data.klass: mono_defaults.uint32_class; case MONO_TYPE_I: return type->data.klass? type->data.klass: mono_defaults.int_class; case MONO_TYPE_U: return type->data.klass? type->data.klass: mono_defaults.uint_class; case MONO_TYPE_I8: return type->data.klass? type->data.klass: mono_defaults.int64_class; case MONO_TYPE_U8: return type->data.klass? type->data.klass: mono_defaults.uint64_class; case MONO_TYPE_R4: return type->data.klass? type->data.klass: mono_defaults.single_class; case MONO_TYPE_R8: return type->data.klass? type->data.klass: mono_defaults.double_class; case MONO_TYPE_STRING: return type->data.klass? type->data.klass: mono_defaults.string_class; case MONO_TYPE_TYPEDBYREF: return type->data.klass? type->data.klass: mono_defaults.typed_reference_class; case MONO_TYPE_ARRAY: return mono_bounded_array_class_get (type->data.array->eklass, type->data.array->rank, TRUE); case MONO_TYPE_PTR: return mono_ptr_class_get (type->data.type); case MONO_TYPE_FNPTR: return mono_fnptr_class_get (type->data.method); case MONO_TYPE_SZARRAY: return mono_array_class_get (type->data.klass, 1); case MONO_TYPE_CLASS: case MONO_TYPE_VALUETYPE: return type->data.klass; case MONO_TYPE_GENERICINST: return mono_generic_class_get_class (type->data.generic_class); case MONO_TYPE_VAR: return mono_class_from_generic_parameter (type->data.generic_param, NULL, FALSE); case MONO_TYPE_MVAR: return mono_class_from_generic_parameter (type->data.generic_param, NULL, TRUE); default: g_warning ("mono_class_from_mono_type: implement me 0x%02x\n", type->type); g_assert_not_reached (); } return NULL; } /** * mono_type_retrieve_from_typespec * @image: context where the image is created * @type_spec: typespec token * @context: the generic context used to evaluate generic instantiations in */ static MonoType * mono_type_retrieve_from_typespec (MonoImage *image, guint32 type_spec, MonoGenericContext *context, gboolean *did_inflate, MonoError *error) { MonoType *t = mono_type_create_from_typespec (image, type_spec); mono_error_init (error); *did_inflate = FALSE; if (!t) { char *name = mono_class_name_from_token (image, type_spec); char *assembly = mono_assembly_name_from_token (image, type_spec); mono_error_set_type_load_name (error, name, assembly, "Could not resolve typespec token %08x", type_spec); return NULL; } if (context && (context->class_inst || context->method_inst)) { MonoType *inflated = inflate_generic_type (NULL, t, context, error); if (!mono_error_ok (error)) return NULL; if (inflated) { t = inflated; *did_inflate = TRUE; } } return t; } /** * mono_class_create_from_typespec * @image: context where the image is created * @type_spec: typespec token * @context: the generic context used to evaluate generic instantiations in */ static MonoClass * mono_class_create_from_typespec (MonoImage *image, guint32 type_spec, MonoGenericContext *context, MonoError *error) { MonoClass *ret; gboolean inflated = FALSE; MonoType *t = mono_type_retrieve_from_typespec (image, type_spec, context, &inflated, error); if (!mono_error_ok (error)) return NULL; ret = mono_class_from_mono_type (t); if (inflated) mono_metadata_free_type (t); return ret; } /** * mono_bounded_array_class_get: * @element_class: element class * @rank: the dimension of the array class * @bounded: whenever the array has non-zero bounds * * Returns: a class object describing the array with element type @element_type and * dimension @rank. */ MonoClass * mono_bounded_array_class_get (MonoClass *eclass, guint32 rank, gboolean bounded) { MonoImage *image; MonoClass *class; MonoClass *parent = NULL; GSList *list, *rootlist = NULL; int nsize; char *name; gboolean corlib_type = FALSE; g_assert (rank <= 255); if (rank > 1) /* bounded only matters for one-dimensional arrays */ bounded = FALSE; image = eclass->image; if (rank == 1 && !bounded) { /* * This case is very frequent not just during compilation because of calls * from mono_class_from_mono_type (), mono_array_new (), * Array:CreateInstance (), etc, so use a separate cache + a separate lock. */ EnterCriticalSection (&image->szarray_cache_lock); if (!image->szarray_cache) image->szarray_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); class = g_hash_table_lookup (image->szarray_cache, eclass); LeaveCriticalSection (&image->szarray_cache_lock); if (class) return class; mono_loader_lock (); } else { mono_loader_lock (); if (!image->array_cache) image->array_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); if ((rootlist = list = g_hash_table_lookup (image->array_cache, eclass))) { for (; list; list = list->next) { class = list->data; if ((class->rank == rank) && (class->byval_arg.type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { mono_loader_unlock (); return class; } } } } /* for the building corlib use System.Array from it */ if (image->assembly && image->assembly->dynamic && image->assembly_name && strcmp (image->assembly_name, "mscorlib") == 0) { parent = mono_class_from_name (image, "System", "Array"); corlib_type = TRUE; } else { parent = mono_defaults.array_class; if (!parent->inited) mono_class_init (parent); } class = mono_image_alloc0 (image, sizeof (MonoClass)); class->image = image; class->name_space = eclass->name_space; nsize = strlen (eclass->name); name = g_malloc (nsize + 2 + rank + 1); memcpy (name, eclass->name, nsize); name [nsize] = '['; if (rank > 1) memset (name + nsize + 1, ',', rank - 1); if (bounded) name [nsize + rank] = '*'; name [nsize + rank + bounded] = ']'; name [nsize + rank + bounded + 1] = 0; class->name = mono_image_strdup (image, name); g_free (name); mono_profiler_class_event (class, MONO_PROFILE_START_LOAD); classes_size += sizeof (MonoClass); class->type_token = 0; /* all arrays are marked serializable and sealed, bug #42779 */ class->flags = TYPE_ATTRIBUTE_CLASS | TYPE_ATTRIBUTE_SERIALIZABLE | TYPE_ATTRIBUTE_SEALED | TYPE_ATTRIBUTE_PUBLIC; class->parent = parent; class->instance_size = mono_class_instance_size (class->parent); if (eclass->byval_arg.type == MONO_TYPE_TYPEDBYREF || eclass->byval_arg.type == MONO_TYPE_VOID) { /*Arrays of those two types are invalid.*/ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); } else if (eclass->enumtype && !mono_class_enum_basetype (eclass)) { if (!eclass->ref_info_handle || eclass->wastypebuilder) { g_warning ("Only incomplete TypeBuilder objects are allowed to be an enum without base_type"); g_assert (eclass->ref_info_handle && !eclass->wastypebuilder); } /* element_size -1 is ok as this is not an instantitable type*/ class->sizes.element_size = -1; } else class->sizes.element_size = mono_class_array_element_size (eclass); mono_class_setup_supertypes (class); if (eclass->generic_class) mono_class_init (eclass); if (!eclass->size_inited) mono_class_setup_fields (eclass); if (eclass->exception_type) /*FIXME we fail the array type, but we have to let other fields be set.*/ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); class->has_references = MONO_TYPE_IS_REFERENCE (&eclass->byval_arg) || eclass->has_references? TRUE: FALSE; class->rank = rank; if (eclass->enumtype) class->cast_class = eclass->element_class; else class->cast_class = eclass; switch (class->cast_class->byval_arg.type) { case MONO_TYPE_I1: class->cast_class = mono_defaults.byte_class; break; case MONO_TYPE_U2: class->cast_class = mono_defaults.int16_class; break; case MONO_TYPE_U4: #if SIZEOF_VOID_P == 4 case MONO_TYPE_I: case MONO_TYPE_U: #endif class->cast_class = mono_defaults.int32_class; break; case MONO_TYPE_U8: #if SIZEOF_VOID_P == 8 case MONO_TYPE_I: case MONO_TYPE_U: #endif class->cast_class = mono_defaults.int64_class; break; } class->element_class = eclass; if ((rank > 1) || bounded) { MonoArrayType *at = mono_image_alloc0 (image, sizeof (MonoArrayType)); class->byval_arg.type = MONO_TYPE_ARRAY; class->byval_arg.data.array = at; at->eklass = eclass; at->rank = rank; /* FIXME: complete.... */ } else { class->byval_arg.type = MONO_TYPE_SZARRAY; class->byval_arg.data.klass = eclass; } class->this_arg = class->byval_arg; class->this_arg.byref = 1; if (corlib_type) { class->inited = 1; } class->generic_container = eclass->generic_container; if (rank == 1 && !bounded) { MonoClass *prev_class; EnterCriticalSection (&image->szarray_cache_lock); prev_class = g_hash_table_lookup (image->szarray_cache, eclass); if (prev_class) /* Someone got in before us */ class = prev_class; else g_hash_table_insert (image->szarray_cache, eclass, class); LeaveCriticalSection (&image->szarray_cache_lock); } else { list = g_slist_append (rootlist, class); g_hash_table_insert (image->array_cache, eclass, list); } mono_loader_unlock (); mono_profiler_class_loaded (class, MONO_PROFILE_OK); return class; } /** * mono_array_class_get: * @element_class: element class * @rank: the dimension of the array class * * Returns: a class object describing the array with element type @element_type and * dimension @rank. */ MonoClass * mono_array_class_get (MonoClass *eclass, guint32 rank) { return mono_bounded_array_class_get (eclass, rank, FALSE); } /** * mono_class_instance_size: * @klass: a class * * Returns: the size of an object instance */ gint32 mono_class_instance_size (MonoClass *klass) { if (!klass->size_inited) mono_class_init (klass); return klass->instance_size; } /** * mono_class_min_align: * @klass: a class * * Returns: minimm alignment requirements */ gint32 mono_class_min_align (MonoClass *klass) { if (!klass->size_inited) mono_class_init (klass); return klass->min_align; } /** * mono_class_value_size: * @klass: a class * * This function is used for value types, and return the * space and the alignment to store that kind of value object. * * Returns: the size of a value of kind @klass */ gint32 mono_class_value_size (MonoClass *klass, guint32 *align) { gint32 size; /* fixme: check disable, because we still have external revereces to * mscorlib and Dummy Objects */ /*g_assert (klass->valuetype);*/ size = mono_class_instance_size (klass) - sizeof (MonoObject); if (align) *align = klass->min_align; return size; } /** * mono_class_data_size: * @klass: a class * * Returns: the size of the static class data */ gint32 mono_class_data_size (MonoClass *klass) { if (!klass->inited) mono_class_init (klass); /* in arrays, sizes.class_size is unioned with element_size * and arrays have no static fields */ if (klass->rank) return 0; return klass->sizes.class_size; } /* * Auxiliary routine to mono_class_get_field * * Takes a field index instead of a field token. */ static MonoClassField * mono_class_get_field_idx (MonoClass *class, int idx) { mono_class_setup_fields_locking (class); if (class->exception_type) return NULL; while (class) { if (class->image->uncompressed_metadata) { /* * class->field.first points to the FieldPtr table, while idx points into the * Field table, so we have to do a search. */ /*FIXME this is broken for types with multiple fields with the same name.*/ const char *name = mono_metadata_string_heap (class->image, mono_metadata_decode_row_col (&class->image->tables [MONO_TABLE_FIELD], idx, MONO_FIELD_NAME)); int i; for (i = 0; i < class->field.count; ++i) if (mono_field_get_name (&class->fields [i]) == name) return &class->fields [i]; g_assert_not_reached (); } else { if (class->field.count) { if ((idx >= class->field.first) && (idx < class->field.first + class->field.count)){ return &class->fields [idx - class->field.first]; } } } class = class->parent; } return NULL; } /** * mono_class_get_field: * @class: the class to lookup the field. * @field_token: the field token * * Returns: A MonoClassField representing the type and offset of * the field, or a NULL value if the field does not belong to this * class. */ MonoClassField * mono_class_get_field (MonoClass *class, guint32 field_token) { int idx = mono_metadata_token_index (field_token); g_assert (mono_metadata_token_code (field_token) == MONO_TOKEN_FIELD_DEF); return mono_class_get_field_idx (class, idx - 1); } /** * mono_class_get_field_from_name: * @klass: the class to lookup the field. * @name: the field name * * Search the class @klass and it's parents for a field with the name @name. * * Returns: the MonoClassField pointer of the named field or NULL */ MonoClassField * mono_class_get_field_from_name (MonoClass *klass, const char *name) { return mono_class_get_field_from_name_full (klass, name, NULL); } /** * mono_class_get_field_from_name_full: * @klass: the class to lookup the field. * @name: the field name * @type: the type of the fields. This optional. * * Search the class @klass and it's parents for a field with the name @name and type @type. * * If @klass is an inflated generic type, the type comparison is done with the equivalent field * of its generic type definition. * * Returns: the MonoClassField pointer of the named field or NULL */ MonoClassField * mono_class_get_field_from_name_full (MonoClass *klass, const char *name, MonoType *type) { int i; mono_class_setup_fields_locking (klass); if (klass->exception_type) return NULL; while (klass) { for (i = 0; i < klass->field.count; ++i) { MonoClassField *field = &klass->fields [i]; if (strcmp (name, mono_field_get_name (field)) != 0) continue; if (type) { MonoType *field_type = mono_metadata_get_corresponding_field_from_generic_type_definition (field)->type; if (!mono_metadata_type_equal_full (type, field_type, TRUE)) continue; } return field; } klass = klass->parent; } return NULL; } /** * mono_class_get_field_token: * @field: the field we need the token of * * Get the token of a field. Note that the tokesn is only valid for the image * the field was loaded from. Don't use this function for fields in dynamic types. * * Returns: the token representing the field in the image it was loaded from. */ guint32 mono_class_get_field_token (MonoClassField *field) { MonoClass *klass = field->parent; int i; mono_class_setup_fields_locking (klass); while (klass) { if (!klass->fields) return 0; for (i = 0; i < klass->field.count; ++i) { if (&klass->fields [i] == field) { int idx = klass->field.first + i + 1; if (klass->image->uncompressed_metadata) idx = mono_metadata_translate_token_index (klass->image, MONO_TABLE_FIELD, idx); return mono_metadata_make_token (MONO_TABLE_FIELD, idx); } } klass = klass->parent; } g_assert_not_reached (); return 0; } static int mono_field_get_index (MonoClassField *field) { int index = field - field->parent->fields; g_assert (index >= 0 && index < field->parent->field.count); return index; } /* * mono_class_get_field_default_value: * * Return the default value of the field as a pointer into the metadata blob. */ const char* mono_class_get_field_default_value (MonoClassField *field, MonoTypeEnum *def_type) { guint32 cindex; guint32 constant_cols [MONO_CONSTANT_SIZE]; int field_index; MonoClass *klass = field->parent; g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT); if (!klass->ext || !klass->ext->field_def_values) { mono_loader_lock (); mono_class_alloc_ext (klass); if (!klass->ext->field_def_values) klass->ext->field_def_values = mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * klass->field.count); mono_loader_unlock (); } field_index = mono_field_get_index (field); if (!klass->ext->field_def_values [field_index].data) { cindex = mono_metadata_get_constant_index (field->parent->image, mono_class_get_field_token (field), 0); if (!cindex) return NULL; g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)); mono_metadata_decode_row (&field->parent->image->tables [MONO_TABLE_CONSTANT], cindex - 1, constant_cols, MONO_CONSTANT_SIZE); klass->ext->field_def_values [field_index].def_type = constant_cols [MONO_CONSTANT_TYPE]; klass->ext->field_def_values [field_index].data = (gpointer)mono_metadata_blob_heap (field->parent->image, constant_cols [MONO_CONSTANT_VALUE]); } *def_type = klass->ext->field_def_values [field_index].def_type; return klass->ext->field_def_values [field_index].data; } static int mono_property_get_index (MonoProperty *prop) { int index = prop - prop->parent->ext->properties; g_assert (index >= 0 && index < prop->parent->ext->property.count); return index; } /* * mono_class_get_property_default_value: * * Return the default value of the field as a pointer into the metadata blob. */ const char* mono_class_get_property_default_value (MonoProperty *property, MonoTypeEnum *def_type) { guint32 cindex; guint32 constant_cols [MONO_CONSTANT_SIZE]; MonoClass *klass = property->parent; g_assert (property->attrs & PROPERTY_ATTRIBUTE_HAS_DEFAULT); /* * We don't cache here because it is not used by C# so it's quite rare, but * we still do the lookup in klass->ext because that is where the data * is stored for dynamic assemblies. */ if (klass->image->dynamic) { int prop_index = mono_property_get_index (property); if (klass->ext->prop_def_values && klass->ext->prop_def_values [prop_index].data) { *def_type = klass->ext->prop_def_values [prop_index].def_type; return klass->ext->prop_def_values [prop_index].data; } return NULL; } cindex = mono_metadata_get_constant_index (klass->image, mono_class_get_property_token (property), 0); if (!cindex) return NULL; mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_CONSTANT], cindex - 1, constant_cols, MONO_CONSTANT_SIZE); *def_type = constant_cols [MONO_CONSTANT_TYPE]; return (gpointer)mono_metadata_blob_heap (klass->image, constant_cols [MONO_CONSTANT_VALUE]); } guint32 mono_class_get_event_token (MonoEvent *event) { MonoClass *klass = event->parent; int i; while (klass) { if (klass->ext) { for (i = 0; i < klass->ext->event.count; ++i) { if (&klass->ext->events [i] == event) return mono_metadata_make_token (MONO_TABLE_EVENT, klass->ext->event.first + i + 1); } } klass = klass->parent; } g_assert_not_reached (); return 0; } MonoProperty* mono_class_get_property_from_name (MonoClass *klass, const char *name) { while (klass) { MonoProperty* p; gpointer iter = NULL; while ((p = mono_class_get_properties (klass, &iter))) { if (! strcmp (name, p->name)) return p; } klass = klass->parent; } return NULL; } guint32 mono_class_get_property_token (MonoProperty *prop) { MonoClass *klass = prop->parent; while (klass) { MonoProperty* p; int i = 0; gpointer iter = NULL; while ((p = mono_class_get_properties (klass, &iter))) { if (&klass->ext->properties [i] == prop) return mono_metadata_make_token (MONO_TABLE_PROPERTY, klass->ext->property.first + i + 1); i ++; } klass = klass->parent; } g_assert_not_reached (); return 0; } char * mono_class_name_from_token (MonoImage *image, guint32 type_token) { const char *name, *nspace; if (image->dynamic) return g_strdup_printf ("DynamicType 0x%08x", type_token); switch (type_token & 0xff000000){ case MONO_TOKEN_TYPE_DEF: { guint32 cols [MONO_TYPEDEF_SIZE]; MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF]; guint tidx = mono_metadata_token_index (type_token); if (tidx > tt->rows) return g_strdup_printf ("Invalid type token 0x%08x", type_token); mono_metadata_decode_row (tt, tidx - 1, cols, MONO_TYPEDEF_SIZE); name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); if (strlen (nspace) == 0) return g_strdup_printf ("%s", name); else return g_strdup_printf ("%s.%s", nspace, name); } case MONO_TOKEN_TYPE_REF: { MonoError error; guint32 cols [MONO_TYPEREF_SIZE]; MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; guint tidx = mono_metadata_token_index (type_token); if (tidx > t->rows) return g_strdup_printf ("Invalid type token 0x%08x", type_token); if (!mono_verifier_verify_typeref_row (image, tidx - 1, &error)) { char *msg = g_strdup_printf ("Invalid type token 0x%08x due to '%s'", type_token, mono_error_get_message (&error)); mono_error_cleanup (&error); return msg; } mono_metadata_decode_row (t, tidx-1, cols, MONO_TYPEREF_SIZE); name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); if (strlen (nspace) == 0) return g_strdup_printf ("%s", name); else return g_strdup_printf ("%s.%s", nspace, name); } case MONO_TOKEN_TYPE_SPEC: return g_strdup_printf ("Typespec 0x%08x", type_token); default: return g_strdup_printf ("Invalid type token 0x%08x", type_token); } } static char * mono_assembly_name_from_token (MonoImage *image, guint32 type_token) { if (image->dynamic) return g_strdup_printf ("DynamicAssembly %s", image->name); switch (type_token & 0xff000000){ case MONO_TOKEN_TYPE_DEF: if (image->assembly) return mono_stringify_assembly_name (&image->assembly->aname); else if (image->assembly_name) return g_strdup (image->assembly_name); return g_strdup_printf ("%s", image->name ? image->name : "[Could not resolve assembly name"); case MONO_TOKEN_TYPE_REF: { MonoError error; MonoAssemblyName aname; guint32 cols [MONO_TYPEREF_SIZE]; MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF]; guint32 idx = mono_metadata_token_index (type_token); if (idx > t->rows) return g_strdup_printf ("Invalid type token 0x%08x", type_token); if (!mono_verifier_verify_typeref_row (image, idx - 1, &error)) { char *msg = g_strdup_printf ("Invalid type token 0x%08x due to '%s'", type_token, mono_error_get_message (&error)); mono_error_cleanup (&error); return msg; } mono_metadata_decode_row (t, idx-1, cols, MONO_TYPEREF_SIZE); idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLTION_SCOPE_BITS; switch (cols [MONO_TYPEREF_SCOPE] & MONO_RESOLTION_SCOPE_MASK) { case MONO_RESOLTION_SCOPE_MODULE: /* FIXME: */ return g_strdup (""); case MONO_RESOLTION_SCOPE_MODULEREF: /* FIXME: */ return g_strdup (""); case MONO_RESOLTION_SCOPE_TYPEREF: /* FIXME: */ return g_strdup (""); case MONO_RESOLTION_SCOPE_ASSEMBLYREF: mono_assembly_get_assemblyref (image, idx - 1, &aname); return mono_stringify_assembly_name (&aname); default: g_assert_not_reached (); } break; } case MONO_TOKEN_TYPE_SPEC: /* FIXME: */ return g_strdup (""); default: g_assert_not_reached (); } return NULL; } /** * mono_class_get_full: * @image: the image where the class resides * @type_token: the token for the class * @context: the generic context used to evaluate generic instantiations in * * Returns: the MonoClass that represents @type_token in @image */ MonoClass * mono_class_get_full (MonoImage *image, guint32 type_token, MonoGenericContext *context) { MonoError error; MonoClass *class = NULL; if (image->dynamic) { int table = mono_metadata_token_table (type_token); if (table != MONO_TABLE_TYPEDEF && table != MONO_TABLE_TYPEREF && table != MONO_TABLE_TYPESPEC) { mono_loader_set_error_bad_image (g_strdup ("Bad type token.")); return NULL; } return mono_lookup_dynamic_token (image, type_token, context); } switch (type_token & 0xff000000){ case MONO_TOKEN_TYPE_DEF: class = mono_class_create_from_typedef (image, type_token); break; case MONO_TOKEN_TYPE_REF: class = mono_class_from_typeref (image, type_token); break; case MONO_TOKEN_TYPE_SPEC: class = mono_class_create_from_typespec (image, type_token, context, &error); if (!mono_error_ok (&error)) { /*FIXME don't swallow the error message*/ mono_error_cleanup (&error); } break; default: g_warning ("unknown token type %x", type_token & 0xff000000); g_assert_not_reached (); } if (!class){ char *name = mono_class_name_from_token (image, type_token); char *assembly = mono_assembly_name_from_token (image, type_token); mono_loader_set_error_type_load (name, assembly); g_free (name); g_free (assembly); } return class; } /** * mono_type_get_full: * @image: the image where the type resides * @type_token: the token for the type * @context: the generic context used to evaluate generic instantiations in * * This functions exists to fullfill the fact that sometimes it's desirable to have access to the * * Returns: the MonoType that represents @type_token in @image */ MonoType * mono_type_get_full (MonoImage *image, guint32 type_token, MonoGenericContext *context) { MonoError error; MonoType *type = NULL; gboolean inflated = FALSE; //FIXME: this will not fix the very issue for which mono_type_get_full exists -but how to do it then? if (image->dynamic) return mono_class_get_type (mono_lookup_dynamic_token (image, type_token, context)); if ((type_token & 0xff000000) != MONO_TOKEN_TYPE_SPEC) { MonoClass *class = mono_class_get_full (image, type_token, context); return class ? mono_class_get_type (class) : NULL; } type = mono_type_retrieve_from_typespec (image, type_token, context, &inflated, &error); if (!mono_error_ok (&error)) { /*FIXME don't swalloc the error message.*/ char *name = mono_class_name_from_token (image, type_token); char *assembly = mono_assembly_name_from_token (image, type_token); g_warning ("Error loading type %s from %s due to %s", name, assembly, mono_error_get_message (&error)); mono_error_cleanup (&error); mono_loader_set_error_type_load (name, assembly); return NULL; } if (inflated) { MonoType *tmp = type; type = mono_class_get_type (mono_class_from_mono_type (type)); /* FIXME: This is a workaround fo the fact that a typespec token sometimes reference to the generic type definition. * A MonoClass::byval_arg of a generic type definion has type CLASS. * Some parts of mono create a GENERICINST to reference a generic type definition and this generates confict with byval_arg. * * The long term solution is to chaise this places and make then set MonoType::type correctly. * */ if (type->type != tmp->type) type = tmp; else mono_metadata_free_type (tmp); } return type; } MonoClass * mono_class_get (MonoImage *image, guint32 type_token) { return mono_class_get_full (image, type_token, NULL); } /** * mono_image_init_name_cache: * * Initializes the class name cache stored in image->name_cache. * * LOCKING: Acquires the corresponding image lock. */ void mono_image_init_name_cache (MonoImage *image) { MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEDEF]; guint32 cols [MONO_TYPEDEF_SIZE]; const char *name; const char *nspace; guint32 i, visib, nspace_index; GHashTable *name_cache2, *nspace_table; mono_image_lock (image); if (image->name_cache) { mono_image_unlock (image); return; } image->name_cache = g_hash_table_new (g_str_hash, g_str_equal); if (image->dynamic) { mono_image_unlock (image); return; } /* Temporary hash table to avoid lookups in the nspace_table */ name_cache2 = g_hash_table_new (NULL, NULL); for (i = 1; i <= t->rows; ++i) { mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE); visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; /* * Nested types are accessed from the nesting name. We use the fact that nested types use different visibility flags * than toplevel types, thus avoiding the need to grovel through the NESTED_TYPE table */ if (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) continue; name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); nspace_index = cols [MONO_TYPEDEF_NAMESPACE]; nspace_table = g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index)); if (!nspace_table) { nspace_table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (image->name_cache, (char*)nspace, nspace_table); g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index), nspace_table); } g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (i)); } /* Load type names from EXPORTEDTYPES table */ { MonoTableInfo *t = &image->tables [MONO_TABLE_EXPORTEDTYPE]; guint32 cols [MONO_EXP_TYPE_SIZE]; int i; for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_EXP_TYPE_SIZE); name = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAMESPACE]); nspace_index = cols [MONO_EXP_TYPE_NAMESPACE]; nspace_table = g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index)); if (!nspace_table) { nspace_table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (image->name_cache, (char*)nspace, nspace_table); g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index), nspace_table); } g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (mono_metadata_make_token (MONO_TABLE_EXPORTEDTYPE, i + 1))); } } g_hash_table_destroy (name_cache2); mono_image_unlock (image); } /*FIXME Only dynamic assemblies should allow this operation.*/ void mono_image_add_to_name_cache (MonoImage *image, const char *nspace, const char *name, guint32 index) { GHashTable *nspace_table; GHashTable *name_cache; guint32 old_index; mono_image_lock (image); if (!image->name_cache) mono_image_init_name_cache (image); name_cache = image->name_cache; if (!(nspace_table = g_hash_table_lookup (name_cache, nspace))) { nspace_table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (name_cache, (char *)nspace, (char *)nspace_table); } if ((old_index = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, (char*) name)))) g_error ("overrwritting old token %x on image %s for type %s::%s", old_index, image->name, nspace, name); g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (index)); mono_image_unlock (image); } typedef struct { gconstpointer key; gpointer value; } FindUserData; static void find_nocase (gpointer key, gpointer value, gpointer user_data) { char *name = (char*)key; FindUserData *data = (FindUserData*)user_data; if (!data->value && (mono_utf8_strcasecmp (name, (char*)data->key) == 0)) data->value = value; } /** * mono_class_from_name_case: * @image: The MonoImage where the type is looked up in * @name_space: the type namespace * @name: the type short name. * * Obtains a MonoClass with a given namespace and a given name which * is located in the given MonoImage. The namespace and name * lookups are case insensitive. */ MonoClass * mono_class_from_name_case (MonoImage *image, const char* name_space, const char *name) { MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEDEF]; guint32 cols [MONO_TYPEDEF_SIZE]; const char *n; const char *nspace; guint32 i, visib; if (image->dynamic) { guint32 token = 0; FindUserData user_data; mono_image_lock (image); if (!image->name_cache) mono_image_init_name_cache (image); user_data.key = name_space; user_data.value = NULL; g_hash_table_foreach (image->name_cache, find_nocase, &user_data); if (user_data.value) { GHashTable *nspace_table = (GHashTable*)user_data.value; user_data.key = name; user_data.value = NULL; g_hash_table_foreach (nspace_table, find_nocase, &user_data); if (user_data.value) token = GPOINTER_TO_UINT (user_data.value); } mono_image_unlock (image); if (token) return mono_class_get (image, MONO_TOKEN_TYPE_DEF | token); else return NULL; } /* add a cache if needed */ for (i = 1; i <= t->rows; ++i) { mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE); visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; /* * Nested types are accessed from the nesting name. We use the fact that nested types use different visibility flags * than toplevel types, thus avoiding the need to grovel through the NESTED_TYPE table */ if (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) continue; n = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); if (mono_utf8_strcasecmp (n, name) == 0 && mono_utf8_strcasecmp (nspace, name_space) == 0) return mono_class_get (image, MONO_TOKEN_TYPE_DEF | i); } return NULL; } static MonoClass* return_nested_in (MonoClass *class, char *nested) { MonoClass *found; char *s = strchr (nested, '/'); gpointer iter = NULL; if (s) { *s = 0; s++; } while ((found = mono_class_get_nested_types (class, &iter))) { if (strcmp (found->name, nested) == 0) { if (s) return return_nested_in (found, s); return found; } } return NULL; } static MonoClass* search_modules (MonoImage *image, const char *name_space, const char *name) { MonoTableInfo *file_table = &image->tables [MONO_TABLE_FILE]; MonoImage *file_image; MonoClass *class; int i; /* * The EXPORTEDTYPES table only contains public types, so have to search the * modules as well. * Note: image->modules contains the contents of the MODULEREF table, while * the real module list is in the FILE table. */ for (i = 0; i < file_table->rows; i++) { guint32 cols [MONO_FILE_SIZE]; mono_metadata_decode_row (file_table, i, cols, MONO_FILE_SIZE); if (cols [MONO_FILE_FLAGS] == FILE_CONTAINS_NO_METADATA) continue; file_image = mono_image_load_file_for_image (image, i + 1); if (file_image) { class = mono_class_from_name (file_image, name_space, name); if (class) return class; } } return NULL; } /** * mono_class_from_name: * @image: The MonoImage where the type is looked up in * @name_space: the type namespace * @name: the type short name. * * Obtains a MonoClass with a given namespace and a given name which * is located in the given MonoImage. */ MonoClass * mono_class_from_name (MonoImage *image, const char* name_space, const char *name) { GHashTable *nspace_table; MonoImage *loaded_image; guint32 token = 0; int i; MonoClass *class; char *nested; char buf [1024]; if ((nested = strchr (name, '/'))) { int pos = nested - name; int len = strlen (name); if (len > 1023) return NULL; memcpy (buf, name, len + 1); buf [pos] = 0; nested = buf + pos + 1; name = buf; } /* FIXME: get_class_from_name () can't handle types in the EXPORTEDTYPE table */ if (get_class_from_name && image->tables [MONO_TABLE_EXPORTEDTYPE].rows == 0) { gboolean res = get_class_from_name (image, name_space, name, &class); if (res) { if (!class) class = search_modules (image, name_space, name); if (nested) return class ? return_nested_in (class, nested) : NULL; else return class; } } mono_image_lock (image); if (!image->name_cache) mono_image_init_name_cache (image); nspace_table = g_hash_table_lookup (image->name_cache, name_space); if (nspace_table) token = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, name)); mono_image_unlock (image); if (!token && image->dynamic && image->modules) { /* Search modules as well */ for (i = 0; i < image->module_count; ++i) { MonoImage *module = image->modules [i]; class = mono_class_from_name (module, name_space, name); if (class) return class; } } if (!token) { class = search_modules (image, name_space, name); if (class) return class; } if (!token) return NULL; if (mono_metadata_token_table (token) == MONO_TABLE_EXPORTEDTYPE) { MonoTableInfo *t = &image->tables [MONO_TABLE_EXPORTEDTYPE]; guint32 cols [MONO_EXP_TYPE_SIZE]; guint32 idx, impl; idx = mono_metadata_token_index (token); mono_metadata_decode_row (t, idx - 1, cols, MONO_EXP_TYPE_SIZE); impl = cols [MONO_EXP_TYPE_IMPLEMENTATION]; if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_FILE) { loaded_image = mono_assembly_load_module (image->assembly, impl >> MONO_IMPLEMENTATION_BITS); if (!loaded_image) return NULL; class = mono_class_from_name (loaded_image, name_space, name); if (nested) return return_nested_in (class, nested); return class; } else if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_ASSEMBLYREF) { guint32 assembly_idx; assembly_idx = impl >> MONO_IMPLEMENTATION_BITS; mono_assembly_load_reference (image, assembly_idx - 1); g_assert (image->references [assembly_idx - 1]); if (image->references [assembly_idx - 1] == (gpointer)-1) return NULL; else /* FIXME: Cycle detection */ return mono_class_from_name (image->references [assembly_idx - 1]->image, name_space, name); } else { g_error ("not yet implemented"); } } token = MONO_TOKEN_TYPE_DEF | token; class = mono_class_get (image, token); if (nested) return return_nested_in (class, nested); return class; } /*FIXME test for interfaces with variant generic arguments*/ gboolean mono_class_is_subclass_of (MonoClass *klass, MonoClass *klassc, gboolean check_interfaces) { if (check_interfaces && MONO_CLASS_IS_INTERFACE (klassc) && !MONO_CLASS_IS_INTERFACE (klass)) { if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, klassc->interface_id)) return TRUE; } else if (check_interfaces && MONO_CLASS_IS_INTERFACE (klassc) && MONO_CLASS_IS_INTERFACE (klass)) { int i; for (i = 0; i < klass->interface_count; i ++) { MonoClass *ic = klass->interfaces [i]; if (ic == klassc) return TRUE; } } else { if (!MONO_CLASS_IS_INTERFACE (klass) && mono_class_has_parent (klass, klassc)) return TRUE; } /* * MS.NET thinks interfaces are a subclass of Object, so we think it as * well. */ if (klassc == mono_defaults.object_class) return TRUE; return FALSE; } static gboolean mono_type_is_generic_argument (MonoType *type) { return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR; } gboolean mono_class_has_variant_generic_params (MonoClass *klass) { int i; MonoGenericContainer *container; if (!klass->generic_class) return FALSE; container = klass->generic_class->container_class->generic_container; for (i = 0; i < container->type_argc; ++i) if (mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)) return TRUE; return FALSE; } static gboolean mono_gparam_is_reference_conversible (MonoClass *target, MonoClass *candidate, gboolean check_for_reference_conv) { if (target == candidate) return TRUE; if (check_for_reference_conv && mono_type_is_generic_argument (&target->byval_arg) && mono_type_is_generic_argument (&candidate->byval_arg)) { MonoGenericParam *gparam = candidate->byval_arg.data.generic_param; MonoGenericParamInfo *pinfo = mono_generic_param_info (gparam); if (!pinfo || (pinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) == 0) return FALSE; } if (!mono_class_is_assignable_from (target, candidate)) return FALSE; return TRUE; } /** * @container the generic container from the GTD * @klass: the class to be assigned to * @oklass: the source class * * Both klass and oklass must be instances of the same generic interface. * Return true if @klass can be assigned to a @klass variable */ gboolean mono_class_is_variant_compatible (MonoClass *klass, MonoClass *oklass, gboolean check_for_reference_conv) { int j; MonoType **klass_argv, **oklass_argv; MonoClass *klass_gtd = mono_class_get_generic_type_definition (klass); MonoGenericContainer *container = klass_gtd->generic_container; if (klass == oklass) return TRUE; /*Viable candidates are instances of the same generic interface*/ if (mono_class_get_generic_type_definition (oklass) != klass_gtd || oklass == klass_gtd) return FALSE; klass_argv = &klass->generic_class->context.class_inst->type_argv [0]; oklass_argv = &oklass->generic_class->context.class_inst->type_argv [0]; for (j = 0; j < container->type_argc; ++j) { MonoClass *param1_class = mono_class_from_mono_type (klass_argv [j]); MonoClass *param2_class = mono_class_from_mono_type (oklass_argv [j]); if (param1_class->valuetype != param2_class->valuetype || (param1_class->valuetype && param1_class != param2_class)) return FALSE; /* * The _VARIANT and _COVARIANT constants should read _COVARIANT and * _CONTRAVARIANT, but they are in a public header so we can't fix it. */ if (param1_class != param2_class) { if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_VARIANT) { if (!mono_gparam_is_reference_conversible (param1_class, param2_class, check_for_reference_conv)) return FALSE; } else if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_COVARIANT) { if (!mono_gparam_is_reference_conversible (param2_class, param1_class, check_for_reference_conv)) return FALSE; } else return FALSE; } } return TRUE; } static gboolean mono_gparam_is_assignable_from (MonoClass *target, MonoClass *candidate) { MonoGenericParam *gparam, *ogparam; MonoGenericParamInfo *tinfo, *cinfo; MonoClass **candidate_class; gboolean class_constraint_satisfied, valuetype_constraint_satisfied; int tmask, cmask; if (target == candidate) return TRUE; if (target->byval_arg.type != candidate->byval_arg.type) return FALSE; gparam = target->byval_arg.data.generic_param; ogparam = candidate->byval_arg.data.generic_param; tinfo = mono_generic_param_info (gparam); cinfo = mono_generic_param_info (ogparam); class_constraint_satisfied = FALSE; valuetype_constraint_satisfied = FALSE; /*candidate must have a super set of target's special constraints*/ tmask = tinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; cmask = cinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK; if (cinfo->constraints) { for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { MonoClass *cc = *candidate_class; if (mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) class_constraint_satisfied = TRUE; else if (!mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc)) valuetype_constraint_satisfied = TRUE; } } class_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0; valuetype_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0; if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && !class_constraint_satisfied) return FALSE; if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && !valuetype_constraint_satisfied) return FALSE; if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !((cmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) || valuetype_constraint_satisfied)) { return FALSE; } /*candidate type constraints must be a superset of target's*/ if (tinfo->constraints) { MonoClass **target_class; for (target_class = tinfo->constraints; *target_class; ++target_class) { MonoClass *tc = *target_class; /* * A constraint from @target might inflate into @candidate itself and in that case we don't need * check it's constraints since it satisfy the constraint by itself. */ if (mono_metadata_type_equal (&tc->byval_arg, &candidate->byval_arg)) continue; if (!cinfo->constraints) return FALSE; for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { MonoClass *cc = *candidate_class; if (mono_class_is_assignable_from (tc, cc)) break; /* * This happens when we have the following: * * Bar where K : IFace * Foo where T : U where U : IFace * ... * Bar <- T here satisfy K constraint transitively through to U's constraint * */ if (mono_type_is_generic_argument (&cc->byval_arg)) { if (mono_gparam_is_assignable_from (target, cc)) break; } } if (!*candidate_class) return FALSE; } } /*candidate itself must have a constraint that satisfy target*/ if (cinfo->constraints) { for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) { MonoClass *cc = *candidate_class; if (mono_class_is_assignable_from (target, cc)) return TRUE; } } return FALSE; } /** * mono_class_is_assignable_from: * @klass: the class to be assigned to * @oklass: the source class * * Return: true if an instance of object oklass can be assigned to an * instance of object @klass */ gboolean mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass) { /*FIXME this will cause a lot of irrelevant stuff to be loaded.*/ if (!klass->inited) mono_class_init (klass); if (!oklass->inited) mono_class_init (oklass); if (klass->exception_type || oklass->exception_type) return FALSE; if (mono_type_is_generic_argument (&klass->byval_arg)) { if (!mono_type_is_generic_argument (&oklass->byval_arg)) return FALSE; return mono_gparam_is_assignable_from (klass, oklass); } if (MONO_CLASS_IS_INTERFACE (klass)) { if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) { MonoGenericParam *gparam = oklass->byval_arg.data.generic_param; MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints; int i; if (constraints) { for (i = 0; constraints [i]; ++i) { if (mono_class_is_assignable_from (klass, constraints [i])) return TRUE; } } return FALSE; } /* interface_offsets might not be set for dynamic classes */ if (oklass->ref_info_handle && !oklass->interface_bitmap) /* * oklass might be a generic type parameter but they have * interface_offsets set. */ return mono_reflection_call_is_assignable_to (oklass, klass); if (!oklass->interface_bitmap) /* Happens with generic instances of not-yet created dynamic types */ return FALSE; if (MONO_CLASS_IMPLEMENTS_INTERFACE (oklass, klass->interface_id)) return TRUE; if (mono_class_has_variant_generic_params (klass)) { MonoError error; int i; mono_class_setup_interfaces (oklass, &error); if (!mono_error_ok (&error)) { mono_error_cleanup (&error); return FALSE; } /*klass is a generic variant interface, We need to extract from oklass a list of ifaces which are viable candidates.*/ for (i = 0; i < oklass->interface_offsets_count; ++i) { MonoClass *iface = oklass->interfaces_packed [i]; if (mono_class_is_variant_compatible (klass, iface, FALSE)) return TRUE; } } return FALSE; } else if (klass->delegate) { if (mono_class_has_variant_generic_params (klass) && mono_class_is_variant_compatible (klass, oklass, FALSE)) return TRUE; }else if (klass->rank) { MonoClass *eclass, *eoclass; if (oklass->rank != klass->rank) return FALSE; /* vectors vs. one dimensional arrays */ if (oklass->byval_arg.type != klass->byval_arg.type) return FALSE; eclass = klass->cast_class; eoclass = oklass->cast_class; /* * a is b does not imply a[] is b[] when a is a valuetype, and * b is a reference type. */ if (eoclass->valuetype) { if ((eclass == mono_defaults.enum_class) || (eclass == mono_defaults.enum_class->parent) || (eclass == mono_defaults.object_class)) return FALSE; } return mono_class_is_assignable_from (klass->cast_class, oklass->cast_class); } else if (mono_class_is_nullable (klass)) { if (mono_class_is_nullable (oklass)) return mono_class_is_assignable_from (klass->cast_class, oklass->cast_class); else return mono_class_is_assignable_from (klass->cast_class, oklass); } else if (klass == mono_defaults.object_class) return TRUE; return mono_class_has_parent (oklass, klass); } /*Check if @oklass is variant compatible with @klass.*/ static gboolean mono_class_is_variant_compatible_slow (MonoClass *klass, MonoClass *oklass) { int j; MonoType **klass_argv, **oklass_argv; MonoClass *klass_gtd = mono_class_get_generic_type_definition (klass); MonoGenericContainer *container = klass_gtd->generic_container; /*Viable candidates are instances of the same generic interface*/ if (mono_class_get_generic_type_definition (oklass) != klass_gtd || oklass == klass_gtd) return FALSE; klass_argv = &klass->generic_class->context.class_inst->type_argv [0]; oklass_argv = &oklass->generic_class->context.class_inst->type_argv [0]; for (j = 0; j < container->type_argc; ++j) { MonoClass *param1_class = mono_class_from_mono_type (klass_argv [j]); MonoClass *param2_class = mono_class_from_mono_type (oklass_argv [j]); if (param1_class->valuetype != param2_class->valuetype) return FALSE; /* * The _VARIANT and _COVARIANT constants should read _COVARIANT and * _CONTRAVARIANT, but they are in a public header so we can't fix it. */ if (param1_class != param2_class) { if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_VARIANT) { if (!mono_class_is_assignable_from_slow (param1_class, param2_class)) return FALSE; } else if (mono_generic_container_get_param_info (container, j)->flags & MONO_GEN_PARAM_COVARIANT) { if (!mono_class_is_assignable_from_slow (param2_class, param1_class)) return FALSE; } else return FALSE; } } return TRUE; } /*Check if @candidate implements the interface @target*/ static gboolean mono_class_implement_interface_slow (MonoClass *target, MonoClass *candidate) { MonoError error; int i; gboolean is_variant = mono_class_has_variant_generic_params (target); if (is_variant && MONO_CLASS_IS_INTERFACE (candidate)) { if (mono_class_is_variant_compatible_slow (target, candidate)) return TRUE; } do { if (candidate == target) return TRUE; /*A TypeBuilder can have more interfaces on tb->interfaces than on candidate->interfaces*/ if (candidate->image->dynamic && !candidate->wastypebuilder) { MonoReflectionTypeBuilder *tb = mono_class_get_ref_info (candidate); int j; if (tb && tb->interfaces) { for (j = mono_array_length (tb->interfaces) - 1; j >= 0; --j) { MonoReflectionType *iface = mono_array_get (tb->interfaces, MonoReflectionType*, j); MonoClass *iface_class; /* we can't realize the type here since it can do pretty much anything. */ if (!iface->type) continue; iface_class = mono_class_from_mono_type (iface->type); if (iface_class == target) return TRUE; if (is_variant && mono_class_is_variant_compatible_slow (target, iface_class)) return TRUE; if (mono_class_implement_interface_slow (target, iface_class)) return TRUE; } } } else { /*setup_interfaces don't mono_class_init anything*/ mono_class_setup_interfaces (candidate, &error); if (!mono_error_ok (&error)) { mono_error_cleanup (&error); return FALSE; } for (i = 0; i < candidate->interface_count; ++i) { if (candidate->interfaces [i] == target) return TRUE; if (is_variant && mono_class_is_variant_compatible_slow (target, candidate->interfaces [i])) return TRUE; if (mono_class_implement_interface_slow (target, candidate->interfaces [i])) return TRUE; } } candidate = candidate->parent; } while (candidate); return FALSE; } /* * Check if @oklass can be assigned to @klass. * This function does the same as mono_class_is_assignable_from but is safe to be used from mono_class_init context. */ gboolean mono_class_is_assignable_from_slow (MonoClass *target, MonoClass *candidate) { if (candidate == target) return TRUE; if (target == mono_defaults.object_class) return TRUE; if (mono_class_has_parent (candidate, target)) return TRUE; /*If target is not an interface there is no need to check them.*/ if (MONO_CLASS_IS_INTERFACE (target)) return mono_class_implement_interface_slow (target, candidate); if (target->delegate && mono_class_has_variant_generic_params (target)) return mono_class_is_variant_compatible (target, candidate, FALSE); /*FIXME properly handle nullables and arrays */ /*FIXME properly handle (M)VAR */ return FALSE; } /** * mono_class_get_cctor: * @klass: A MonoClass pointer * * Returns: the static constructor of @klass if it exists, NULL otherwise. */ MonoMethod* mono_class_get_cctor (MonoClass *klass) { MonoCachedClassInfo cached_info; if (klass->image->dynamic) { /* * has_cctor is not set for these classes because mono_class_init () is * not run for them. */ return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME); } if (!klass->has_cctor) return NULL; if (mono_class_get_cached_class_info (klass, &cached_info)) return mono_get_method (klass->image, cached_info.cctor_token, klass); if (klass->generic_class && !klass->methods) return mono_class_get_inflated_method (klass, mono_class_get_cctor (klass->generic_class->container_class)); return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME); } /** * mono_class_get_finalizer: * @klass: The MonoClass pointer * * Returns: the finalizer method of @klass if it exists, NULL otherwise. */ MonoMethod* mono_class_get_finalizer (MonoClass *klass) { MonoCachedClassInfo cached_info; if (!klass->inited) mono_class_init (klass); if (!mono_class_has_finalizer (klass)) return NULL; if (mono_class_get_cached_class_info (klass, &cached_info)) return mono_get_method (cached_info.finalize_image, cached_info.finalize_token, NULL); else { mono_class_setup_vtable (klass); return klass->vtable [finalize_slot]; } } /** * mono_class_needs_cctor_run: * @klass: the MonoClass pointer * @caller: a MonoMethod describing the caller * * Determines whenever the class has a static constructor and whenever it * needs to be called when executing CALLER. */ gboolean mono_class_needs_cctor_run (MonoClass *klass, MonoMethod *caller) { MonoMethod *method; method = mono_class_get_cctor (klass); if (method) return (method == caller) ? FALSE : TRUE; else return FALSE; } /** * mono_class_array_element_size: * @klass: * * Returns: the number of bytes an element of type @klass * uses when stored into an array. */ gint32 mono_class_array_element_size (MonoClass *klass) { MonoType *type = &klass->byval_arg; handle_enum: switch (type->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: return 1; case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: return 2; case MONO_TYPE_I4: case MONO_TYPE_U4: case MONO_TYPE_R4: return 4; case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_PTR: case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_VAR: case MONO_TYPE_MVAR: return sizeof (gpointer); case MONO_TYPE_I8: case MONO_TYPE_U8: case MONO_TYPE_R8: return 8; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { type = mono_class_enum_basetype (type->data.klass); klass = klass->element_class; goto handle_enum; } return mono_class_instance_size (klass) - sizeof (MonoObject); case MONO_TYPE_GENERICINST: type = &type->data.generic_class->container_class->byval_arg; goto handle_enum; case MONO_TYPE_VOID: return 0; default: g_error ("unknown type 0x%02x in mono_class_array_element_size", type->type); } return -1; } /** * mono_array_element_size: * @ac: pointer to a #MonoArrayClass * * Returns: the size of single array element. */ gint32 mono_array_element_size (MonoClass *ac) { g_assert (ac->rank); return ac->sizes.element_size; } gpointer mono_ldtoken (MonoImage *image, guint32 token, MonoClass **handle_class, MonoGenericContext *context) { if (image->dynamic) { MonoClass *tmp_handle_class; gpointer obj = mono_lookup_dynamic_token_class (image, token, TRUE, &tmp_handle_class, context); g_assert (tmp_handle_class); if (handle_class) *handle_class = tmp_handle_class; if (tmp_handle_class == mono_defaults.typehandle_class) return &((MonoClass*)obj)->byval_arg; else return obj; } switch (token & 0xff000000) { case MONO_TOKEN_TYPE_DEF: case MONO_TOKEN_TYPE_REF: case MONO_TOKEN_TYPE_SPEC: { MonoType *type; if (handle_class) *handle_class = mono_defaults.typehandle_class; type = mono_type_get_full (image, token, context); if (!type) return NULL; mono_class_init (mono_class_from_mono_type (type)); /* We return a MonoType* as handle */ return type; } case MONO_TOKEN_FIELD_DEF: { MonoClass *class; guint32 type = mono_metadata_typedef_from_field (image, mono_metadata_token_index (token)); if (!type) return NULL; if (handle_class) *handle_class = mono_defaults.fieldhandle_class; class = mono_class_get_full (image, MONO_TOKEN_TYPE_DEF | type, context); if (!class) return NULL; mono_class_init (class); return mono_class_get_field (class, token); } case MONO_TOKEN_METHOD_DEF: case MONO_TOKEN_METHOD_SPEC: { MonoMethod *meth; meth = mono_get_method_full (image, token, NULL, context); if (handle_class) *handle_class = mono_defaults.methodhandle_class; return meth; } case MONO_TOKEN_MEMBER_REF: { guint32 cols [MONO_MEMBERREF_SIZE]; const char *sig; mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, cols, MONO_MEMBERREF_SIZE); sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); mono_metadata_decode_blob_size (sig, &sig); if (*sig == 0x6) { /* it's a field */ MonoClass *klass; MonoClassField *field; field = mono_field_from_token (image, token, &klass, context); if (handle_class) *handle_class = mono_defaults.fieldhandle_class; return field; } else { MonoMethod *meth; meth = mono_get_method_full (image, token, NULL, context); if (handle_class) *handle_class = mono_defaults.methodhandle_class; return meth; } } default: g_warning ("Unknown token 0x%08x in ldtoken", token); break; } return NULL; } /** * This function might need to call runtime functions so it can't be part * of the metadata library. */ static MonoLookupDynamicToken lookup_dynamic = NULL; void mono_install_lookup_dynamic_token (MonoLookupDynamicToken func) { lookup_dynamic = func; } gpointer mono_lookup_dynamic_token (MonoImage *image, guint32 token, MonoGenericContext *context) { MonoClass *handle_class; return lookup_dynamic (image, token, TRUE, &handle_class, context); } gpointer mono_lookup_dynamic_token_class (MonoImage *image, guint32 token, gboolean valid_token, MonoClass **handle_class, MonoGenericContext *context) { return lookup_dynamic (image, token, valid_token, handle_class, context); } static MonoGetCachedClassInfo get_cached_class_info = NULL; void mono_install_get_cached_class_info (MonoGetCachedClassInfo func) { get_cached_class_info = func; } static gboolean mono_class_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res) { if (!get_cached_class_info) return FALSE; else return get_cached_class_info (klass, res); } void mono_install_get_class_from_name (MonoGetClassFromName func) { get_class_from_name = func; } MonoImage* mono_class_get_image (MonoClass *klass) { return klass->image; } /** * mono_class_get_element_class: * @klass: the MonoClass to act on * * Returns: the element class of an array or an enumeration. */ MonoClass* mono_class_get_element_class (MonoClass *klass) { return klass->element_class; } /** * mono_class_is_valuetype: * @klass: the MonoClass to act on * * Returns: true if the MonoClass represents a ValueType. */ gboolean mono_class_is_valuetype (MonoClass *klass) { return klass->valuetype; } /** * mono_class_is_enum: * @klass: the MonoClass to act on * * Returns: true if the MonoClass represents an enumeration. */ gboolean mono_class_is_enum (MonoClass *klass) { return klass->enumtype; } /** * mono_class_enum_basetype: * @klass: the MonoClass to act on * * Returns: the underlying type representation for an enumeration. */ MonoType* mono_class_enum_basetype (MonoClass *klass) { if (klass->element_class == klass) /* SRE or broken types */ return NULL; else return &klass->element_class->byval_arg; } /** * mono_class_get_parent * @klass: the MonoClass to act on * * Returns: the parent class for this class. */ MonoClass* mono_class_get_parent (MonoClass *klass) { return klass->parent; } /** * mono_class_get_nesting_type; * @klass: the MonoClass to act on * * Returns: the container type where this type is nested or NULL if this type is not a nested type. */ MonoClass* mono_class_get_nesting_type (MonoClass *klass) { return klass->nested_in; } /** * mono_class_get_rank: * @klass: the MonoClass to act on * * Returns: the rank for the array (the number of dimensions). */ int mono_class_get_rank (MonoClass *klass) { return klass->rank; } /** * mono_class_get_flags: * @klass: the MonoClass to act on * * The type flags from the TypeDef table from the metadata. * see the TYPE_ATTRIBUTE_* definitions on tabledefs.h for the * different values. * * Returns: the flags from the TypeDef table. */ guint32 mono_class_get_flags (MonoClass *klass) { return klass->flags; } /** * mono_class_get_name * @klass: the MonoClass to act on * * Returns: the name of the class. */ const char* mono_class_get_name (MonoClass *klass) { return klass->name; } /** * mono_class_get_namespace: * @klass: the MonoClass to act on * * Returns: the namespace of the class. */ const char* mono_class_get_namespace (MonoClass *klass) { return klass->name_space; } /** * mono_class_get_type: * @klass: the MonoClass to act on * * This method returns the internal Type representation for the class. * * Returns: the MonoType from the class. */ MonoType* mono_class_get_type (MonoClass *klass) { return &klass->byval_arg; } /** * mono_class_get_type_token * @klass: the MonoClass to act on * * This method returns type token for the class. * * Returns: the type token for the class. */ guint32 mono_class_get_type_token (MonoClass *klass) { return klass->type_token; } /** * mono_class_get_byref_type: * @klass: the MonoClass to act on * * */ MonoType* mono_class_get_byref_type (MonoClass *klass) { return &klass->this_arg; } /** * mono_class_num_fields: * @klass: the MonoClass to act on * * Returns: the number of static and instance fields in the class. */ int mono_class_num_fields (MonoClass *klass) { return klass->field.count; } /** * mono_class_num_methods: * @klass: the MonoClass to act on * * Returns: the number of methods in the class. */ int mono_class_num_methods (MonoClass *klass) { return klass->method.count; } /** * mono_class_num_properties * @klass: the MonoClass to act on * * Returns: the number of properties in the class. */ int mono_class_num_properties (MonoClass *klass) { mono_class_setup_properties (klass); return klass->ext->property.count; } /** * mono_class_num_events: * @klass: the MonoClass to act on * * Returns: the number of events in the class. */ int mono_class_num_events (MonoClass *klass) { mono_class_setup_events (klass); return klass->ext->event.count; } /** * mono_class_get_fields: * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the fields in a class. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @MonoClassField* on each iteration, or NULL when no more fields are available. */ MonoClassField* mono_class_get_fields (MonoClass* klass, gpointer *iter) { MonoClassField* field; if (!iter) return NULL; if (!*iter) { mono_class_setup_fields_locking (klass); if (klass->exception_type) return NULL; /* start from the first */ if (klass->field.count) { return *iter = &klass->fields [0]; } else { /* no fields */ return NULL; } } field = *iter; field++; if (field < &klass->fields [klass->field.count]) { return *iter = field; } return NULL; } /** * mono_class_get_methods * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the fields in a class. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a MonoMethod on each iteration or NULL when no more methods are available. */ MonoMethod* mono_class_get_methods (MonoClass* klass, gpointer *iter) { MonoMethod** method; if (!iter) return NULL; if (!*iter) { mono_class_setup_methods (klass); /* * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. * FIXME we should better report this error to the caller */ if (!klass->methods) return NULL; /* start from the first */ if (klass->method.count) { *iter = &klass->methods [0]; return klass->methods [0]; } else { /* no method */ return NULL; } } method = *iter; method++; if (method < &klass->methods [klass->method.count]) { *iter = method; return *method; } return NULL; } /* * mono_class_get_virtual_methods: * * Iterate over the virtual methods of KLASS. * * LOCKING: Assumes the loader lock is held (because of the klass->methods check). */ static MonoMethod* mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter) { MonoMethod** method; if (!iter) return NULL; if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass) || mono_debug_using_mono_debugger ()) { if (!*iter) { mono_class_setup_methods (klass); /* * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. * FIXME we should better report this error to the caller */ if (!klass->methods) return NULL; /* start from the first */ method = &klass->methods [0]; } else { method = *iter; method++; } while (method < &klass->methods [klass->method.count]) { if (*method && ((*method)->flags & METHOD_ATTRIBUTE_VIRTUAL)) break; method ++; } if (method < &klass->methods [klass->method.count]) { *iter = method; return *method; } else { return NULL; } } else { /* Search directly in metadata to avoid calling setup_methods () */ MonoMethod *res = NULL; int i, start_index; if (!*iter) { start_index = 0; } else { start_index = GPOINTER_TO_UINT (*iter); } for (i = start_index; i < klass->method.count; ++i) { guint32 flags; /* class->method.first points into the methodptr table */ flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, klass->method.first + i, MONO_METHOD_FLAGS); if (flags & METHOD_ATTRIBUTE_VIRTUAL) break; } if (i < klass->method.count) { res = mono_get_method (klass->image, MONO_TOKEN_METHOD_DEF | (klass->method.first + i + 1), klass); /* Add 1 here so the if (*iter) check fails */ *iter = GUINT_TO_POINTER (i + 1); return res; } else { return NULL; } } } /** * mono_class_get_properties: * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the properties in a class. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @MonoProperty* on each invocation, or NULL when no more are available. */ MonoProperty* mono_class_get_properties (MonoClass* klass, gpointer *iter) { MonoProperty* property; if (!iter) return NULL; if (!*iter) { mono_class_setup_properties (klass); /* start from the first */ if (klass->ext->property.count) { return *iter = &klass->ext->properties [0]; } else { /* no fields */ return NULL; } } property = *iter; property++; if (property < &klass->ext->properties [klass->ext->property.count]) { return *iter = property; } return NULL; } /** * mono_class_get_events: * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the properties in a class. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @MonoEvent* on each invocation, or NULL when no more are available. */ MonoEvent* mono_class_get_events (MonoClass* klass, gpointer *iter) { MonoEvent* event; if (!iter) return NULL; if (!*iter) { mono_class_setup_events (klass); /* start from the first */ if (klass->ext->event.count) { return *iter = &klass->ext->events [0]; } else { /* no fields */ return NULL; } } event = *iter; event++; if (event < &klass->ext->events [klass->ext->event.count]) { return *iter = event; } return NULL; } /** * mono_class_get_interfaces * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the interfaces implemented by this class. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @Monoclass* on each invocation, or NULL when no more are available. */ MonoClass* mono_class_get_interfaces (MonoClass* klass, gpointer *iter) { MonoError error; MonoClass** iface; if (!iter) return NULL; if (!*iter) { if (!klass->inited) mono_class_init (klass); if (!klass->interfaces_inited) { mono_class_setup_interfaces (klass, &error); if (!mono_error_ok (&error)) { mono_error_cleanup (&error); return NULL; } } /* start from the first */ if (klass->interface_count) { *iter = &klass->interfaces [0]; return klass->interfaces [0]; } else { /* no interface */ return NULL; } } iface = *iter; iface++; if (iface < &klass->interfaces [klass->interface_count]) { *iter = iface; return *iface; } return NULL; } /** * mono_class_get_nested_types * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the nested types of a class. * This works only if @klass is non-generic, or a generic type definition. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @Monoclass* on each invocation, or NULL when no more are available. */ MonoClass* mono_class_get_nested_types (MonoClass* klass, gpointer *iter) { GList *item; int i; if (!iter) return NULL; if (!klass->nested_classes_inited) { if (!klass->type_token) klass->nested_classes_inited = TRUE; mono_loader_lock (); if (!klass->nested_classes_inited) { i = mono_metadata_nesting_typedef (klass->image, klass->type_token, 1); while (i) { MonoClass* nclass; guint32 cols [MONO_NESTED_CLASS_SIZE]; mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, cols, MONO_NESTED_CLASS_SIZE); nclass = mono_class_create_from_typedef (klass->image, MONO_TOKEN_TYPE_DEF | cols [MONO_NESTED_CLASS_NESTED]); if (!nclass) { mono_loader_clear_error (); i = mono_metadata_nesting_typedef (klass->image, klass->type_token, i + 1); continue; } mono_class_alloc_ext (klass); klass->ext->nested_classes = g_list_prepend_image (klass->image, klass->ext->nested_classes, nclass); i = mono_metadata_nesting_typedef (klass->image, klass->type_token, i + 1); } } mono_memory_barrier (); klass->nested_classes_inited = TRUE; mono_loader_unlock (); } if (!*iter) { /* start from the first */ if (klass->ext && klass->ext->nested_classes) { *iter = klass->ext->nested_classes; return klass->ext->nested_classes->data; } else { /* no nested types */ return NULL; } } item = *iter; item = item->next; if (item) { *iter = item; return item->data; } return NULL; } /** * mono_field_get_name: * @field: the MonoClassField to act on * * Returns: the name of the field. */ const char* mono_field_get_name (MonoClassField *field) { return field->name; } /** * mono_field_get_type: * @field: the MonoClassField to act on * * Returns: MonoType of the field. */ MonoType* mono_field_get_type (MonoClassField *field) { MonoError error; MonoType *type = mono_field_get_type_checked (field, &error); if (!mono_error_ok (&error)) { mono_trace_warning (MONO_TRACE_TYPE, "Could not load field's type due to %s", mono_error_get_message (&error)); mono_error_cleanup (&error); } return type; } /** * mono_field_get_type_checked: * @field: the MonoClassField to act on * @error: used to return any erro found while retrieving @field type * * Returns: MonoType of the field. */ MonoType* mono_field_get_type_checked (MonoClassField *field, MonoError *error) { mono_error_init (error); if (!field->type) mono_field_resolve_type (field, error); return field->type; } /** * mono_field_get_parent: * @field: the MonoClassField to act on * * Returns: MonoClass where the field was defined. */ MonoClass* mono_field_get_parent (MonoClassField *field) { return field->parent; } /** * mono_field_get_flags; * @field: the MonoClassField to act on * * The metadata flags for a field are encoded using the * FIELD_ATTRIBUTE_* constants. See the tabledefs.h file for details. * * Returns: the flags for the field. */ guint32 mono_field_get_flags (MonoClassField *field) { if (!field->type) return mono_field_resolve_flags (field); return field->type->attrs; } /** * mono_field_get_offset; * @field: the MonoClassField to act on * * Returns: the field offset. */ guint32 mono_field_get_offset (MonoClassField *field) { return field->offset; } static const char * mono_field_get_rva (MonoClassField *field) { guint32 rva; int field_index; MonoClass *klass = field->parent; g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA); if (!klass->ext || !klass->ext->field_def_values) { mono_loader_lock (); mono_class_alloc_ext (klass); if (!klass->ext->field_def_values) klass->ext->field_def_values = mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * klass->field.count); mono_loader_unlock (); } field_index = mono_field_get_index (field); if (!klass->ext->field_def_values [field_index].data && !klass->image->dynamic) { mono_metadata_field_info (field->parent->image, klass->field.first + field_index, NULL, &rva, NULL); if (!rva) g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), field->parent->name); klass->ext->field_def_values [field_index].data = mono_image_rva_map (field->parent->image, rva); } return klass->ext->field_def_values [field_index].data; } /** * mono_field_get_data; * @field: the MonoClassField to act on * * Returns: pointer to the metadata constant value or to the field * data if it has an RVA flag. */ const char * mono_field_get_data (MonoClassField *field) { if (field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT) { MonoTypeEnum def_type; return mono_class_get_field_default_value (field, &def_type); } else if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { return mono_field_get_rva (field); } else { return NULL; } } /** * mono_property_get_name: * @prop: the MonoProperty to act on * * Returns: the name of the property */ const char* mono_property_get_name (MonoProperty *prop) { return prop->name; } /** * mono_property_get_set_method * @prop: the MonoProperty to act on. * * Returns: the setter method of the property (A MonoMethod) */ MonoMethod* mono_property_get_set_method (MonoProperty *prop) { return prop->set; } /** * mono_property_get_get_method * @prop: the MonoProperty to act on. * * Returns: the setter method of the property (A MonoMethod) */ MonoMethod* mono_property_get_get_method (MonoProperty *prop) { return prop->get; } /** * mono_property_get_parent: * @prop: the MonoProperty to act on. * * Returns: the MonoClass where the property was defined. */ MonoClass* mono_property_get_parent (MonoProperty *prop) { return prop->parent; } /** * mono_property_get_flags: * @prop: the MonoProperty to act on. * * The metadata flags for a property are encoded using the * PROPERTY_ATTRIBUTE_* constants. See the tabledefs.h file for details. * * Returns: the flags for the property. */ guint32 mono_property_get_flags (MonoProperty *prop) { return prop->attrs; } /** * mono_event_get_name: * @event: the MonoEvent to act on * * Returns: the name of the event. */ const char* mono_event_get_name (MonoEvent *event) { return event->name; } /** * mono_event_get_add_method: * @event: The MonoEvent to act on. * * Returns: the @add' method for the event (a MonoMethod). */ MonoMethod* mono_event_get_add_method (MonoEvent *event) { return event->add; } /** * mono_event_get_remove_method: * @event: The MonoEvent to act on. * * Returns: the @remove method for the event (a MonoMethod). */ MonoMethod* mono_event_get_remove_method (MonoEvent *event) { return event->remove; } /** * mono_event_get_raise_method: * @event: The MonoEvent to act on. * * Returns: the @raise method for the event (a MonoMethod). */ MonoMethod* mono_event_get_raise_method (MonoEvent *event) { return event->raise; } /** * mono_event_get_parent: * @event: the MonoEvent to act on. * * Returns: the MonoClass where the event is defined. */ MonoClass* mono_event_get_parent (MonoEvent *event) { return event->parent; } /** * mono_event_get_flags * @event: the MonoEvent to act on. * * The metadata flags for an event are encoded using the * EVENT_* constants. See the tabledefs.h file for details. * * Returns: the flags for the event. */ guint32 mono_event_get_flags (MonoEvent *event) { return event->attrs; } /** * mono_class_get_method_from_name: * @klass: where to look for the method * @name_space: name of the method * @param_count: number of parameters. -1 for any number. * * Obtains a MonoMethod with a given name and number of parameters. * It only works if there are no multiple signatures for any given method name. */ MonoMethod * mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count) { return mono_class_get_method_from_name_flags (klass, name, param_count, 0); } static MonoMethod* find_method_in_metadata (MonoClass *klass, const char *name, int param_count, int flags) { MonoMethod *res = NULL; int i; /* Search directly in the metadata to avoid calling setup_methods () */ for (i = 0; i < klass->method.count; ++i) { guint32 cols [MONO_METHOD_SIZE]; MonoMethod *method; MonoMethodSignature *sig; /* class->method.first points into the methodptr table */ mono_metadata_decode_table_row (klass->image, MONO_TABLE_METHOD, klass->method.first + i, cols, MONO_METHOD_SIZE); if (!strcmp (mono_metadata_string_heap (klass->image, cols [MONO_METHOD_NAME]), name)) { method = mono_get_method (klass->image, MONO_TOKEN_METHOD_DEF | (klass->method.first + i + 1), klass); if (param_count == -1) { res = method; break; } sig = mono_method_signature (method); if (sig && sig->param_count == param_count) { res = method; break; } } } return res; } /** * mono_class_get_method_from_name_flags: * @klass: where to look for the method * @name_space: name of the method * @param_count: number of parameters. -1 for any number. * @flags: flags which must be set in the method * * Obtains a MonoMethod with a given name and number of parameters. * It only works if there are no multiple signatures for any given method name. */ MonoMethod * mono_class_get_method_from_name_flags (MonoClass *klass, const char *name, int param_count, int flags) { MonoMethod *res = NULL; int i; mono_class_init (klass); if (klass->generic_class && !klass->methods) { res = mono_class_get_method_from_name_flags (klass->generic_class->container_class, name, param_count, flags); if (res) res = mono_class_inflate_generic_method_full (res, klass, mono_class_get_context (klass)); return res; } if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) { mono_class_setup_methods (klass); /* We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. See mono/tests/array_load_exception.il FIXME we should better report this error to the caller */ if (!klass->methods) return NULL; for (i = 0; i < klass->method.count; ++i) { MonoMethod *method = klass->methods [i]; if (method->name[0] == name [0] && !strcmp (name, method->name) && (param_count == -1 || mono_method_signature (method)->param_count == param_count) && ((method->flags & flags) == flags)) { res = method; break; } } } else { res = find_method_in_metadata (klass, name, param_count, flags); } return res; } /** * mono_class_set_failure: * @klass: class in which the failure was detected * @ex_type: the kind of exception/error to be thrown (later) * @ex_data: exception data (specific to each type of exception/error) * * Keep a detected failure informations in the class for later processing. * Note that only the first failure is kept. * * LOCKING: Acquires the loader lock. */ gboolean mono_class_set_failure (MonoClass *klass, guint32 ex_type, void *ex_data) { if (klass->exception_type) return FALSE; mono_loader_lock (); klass->exception_type = ex_type; if (ex_data) mono_image_property_insert (klass->image, klass, MONO_CLASS_PROP_EXCEPTION_DATA, ex_data); mono_loader_unlock (); return TRUE; } /* * mono_class_get_exception_data: * * Return the exception_data property of KLASS. * * LOCKING: Acquires the loader lock. */ gpointer mono_class_get_exception_data (MonoClass *klass) { return mono_image_property_lookup (klass->image, klass, MONO_CLASS_PROP_EXCEPTION_DATA); } /** * mono_classes_init: * * Initialize the resources used by this module. */ void mono_classes_init (void) { mono_counters_register ("Inflated methods size", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_methods_size); mono_counters_register ("Inflated classes", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_classes); mono_counters_register ("Inflated classes size", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_classes_size); mono_counters_register ("MonoClass size", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &classes_size); mono_counters_register ("MonoClassExt size", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_ext_size); } /** * mono_classes_cleanup: * * Free the resources used by this module. */ void mono_classes_cleanup (void) { if (global_interface_bitset) mono_bitset_free (global_interface_bitset); global_interface_bitset = NULL; } /** * mono_class_get_exception_for_failure: * @klass: class in which the failure was detected * * Return a constructed MonoException than the caller can then throw * using mono_raise_exception - or NULL if no failure is present (or * doesn't result in an exception). */ MonoException* mono_class_get_exception_for_failure (MonoClass *klass) { gpointer exception_data = mono_class_get_exception_data (klass); switch (klass->exception_type) { case MONO_EXCEPTION_SECURITY_INHERITANCEDEMAND: { MonoDomain *domain = mono_domain_get (); MonoSecurityManager* secman = mono_security_manager_get_methods (); MonoMethod *method = exception_data; guint32 error = (method) ? MONO_METADATA_INHERITANCEDEMAND_METHOD : MONO_METADATA_INHERITANCEDEMAND_CLASS; MonoObject *exc = NULL; gpointer args [4]; args [0] = &error; args [1] = mono_assembly_get_object (domain, mono_image_get_assembly (klass->image)); args [2] = mono_type_get_object (domain, &klass->byval_arg); args [3] = (method) ? mono_method_get_object (domain, method, NULL) : NULL; mono_runtime_invoke (secman->inheritsecurityexception, NULL, args, &exc); return (MonoException*) exc; } case MONO_EXCEPTION_TYPE_LOAD: { MonoString *name; MonoException *ex; char *str = mono_type_get_full_name (klass); char *astr = klass->image->assembly? mono_stringify_assembly_name (&klass->image->assembly->aname): NULL; name = mono_string_new (mono_domain_get (), str); g_free (str); ex = mono_get_exception_type_load (name, astr); g_free (astr); return ex; } case MONO_EXCEPTION_MISSING_METHOD: { char *class_name = exception_data; char *assembly_name = class_name + strlen (class_name) + 1; return mono_get_exception_missing_method (class_name, assembly_name); } case MONO_EXCEPTION_MISSING_FIELD: { char *class_name = exception_data; char *member_name = class_name + strlen (class_name) + 1; return mono_get_exception_missing_field (class_name, member_name); } case MONO_EXCEPTION_FILE_NOT_FOUND: { char *msg_format = exception_data; char *assembly_name = msg_format + strlen (msg_format) + 1; char *msg = g_strdup_printf (msg_format, assembly_name); MonoException *ex; ex = mono_get_exception_file_not_found2 (msg, mono_string_new (mono_domain_get (), assembly_name)); g_free (msg); return ex; } case MONO_EXCEPTION_BAD_IMAGE: { return mono_get_exception_bad_image_format (exception_data); } default: { MonoLoaderError *error; MonoException *ex; error = mono_loader_get_last_error (); if (error != NULL){ ex = mono_loader_error_prepare_exception (error); return ex; } /* TODO - handle other class related failures */ return NULL; } } } static gboolean is_nesting_type (MonoClass *outer_klass, MonoClass *inner_klass) { outer_klass = mono_class_get_generic_type_definition (outer_klass); inner_klass = mono_class_get_generic_type_definition (inner_klass); do { if (outer_klass == inner_klass) return TRUE; inner_klass = inner_klass->nested_in; } while (inner_klass); return FALSE; } MonoClass * mono_class_get_generic_type_definition (MonoClass *klass) { return klass->generic_class ? klass->generic_class->container_class : klass; } /* * Check if @klass is a subtype of @parent ignoring generic instantiations. * * Generic instantiations are ignored for all super types of @klass. * * Visibility checks ignoring generic instantiations. */ gboolean mono_class_has_parent_and_ignore_generics (MonoClass *klass, MonoClass *parent) { int i; klass = mono_class_get_generic_type_definition (klass); parent = mono_class_get_generic_type_definition (parent); mono_class_setup_supertypes (klass); for (i = 0; i < klass->idepth; ++i) { if (parent == mono_class_get_generic_type_definition (klass->supertypes [i])) return TRUE; } return FALSE; } /* * Subtype can only access parent members with family protection if the site object * is subclass of Subtype. For example: * class A { protected int x; } * class B : A { * void valid_access () { * B b; * b.x = 0; * } * void invalid_access () { * A a; * a.x = 0; * } * } * */ static gboolean is_valid_family_access (MonoClass *access_klass, MonoClass *member_klass, MonoClass *context_klass) { if (!mono_class_has_parent_and_ignore_generics (access_klass, member_klass)) return FALSE; if (context_klass == NULL) return TRUE; /*if access_klass is not member_klass context_klass must be type compat*/ if (access_klass != member_klass && !mono_class_has_parent_and_ignore_generics (context_klass, access_klass)) return FALSE; return TRUE; } static gboolean can_access_internals (MonoAssembly *accessing, MonoAssembly* accessed) { GSList *tmp; if (accessing == accessed) return TRUE; if (!accessed || !accessing) return FALSE; /* extra safety under CoreCLR - the runtime does not verify the strongname signatures * anywhere so untrusted friends are not safe to access platform's code internals */ if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) { if (!mono_security_core_clr_can_access_internals (accessing->image, accessed->image)) return FALSE; } mono_assembly_load_friends (accessed); for (tmp = accessed->friend_assembly_names; tmp; tmp = tmp->next) { MonoAssemblyName *friend = tmp->data; /* Be conservative with checks */ if (!friend->name) continue; if (strcmp (accessing->aname.name, friend->name)) continue; if (friend->public_key_token [0]) { if (!accessing->aname.public_key_token [0]) continue; if (!mono_public_tokens_are_equal (friend->public_key_token, accessing->aname.public_key_token)) continue; } return TRUE; } return FALSE; } /* * If klass is a generic type or if it is derived from a generic type, return the * MonoClass of the generic definition * Returns NULL if not found */ static MonoClass* get_generic_definition_class (MonoClass *klass) { while (klass) { if (klass->generic_class && klass->generic_class->container_class) return klass->generic_class->container_class; klass = klass->parent; } return NULL; } static gboolean can_access_instantiation (MonoClass *access_klass, MonoGenericInst *ginst) { int i; for (i = 0; i < ginst->type_argc; ++i) { MonoType *type = ginst->type_argv[i]; switch (type->type) { case MONO_TYPE_SZARRAY: if (!can_access_type (access_klass, type->data.klass)) return FALSE; break; case MONO_TYPE_ARRAY: if (!can_access_type (access_klass, type->data.array->eklass)) return FALSE; break; case MONO_TYPE_PTR: if (!can_access_type (access_klass, mono_class_from_mono_type (type->data.type))) return FALSE; break; case MONO_TYPE_CLASS: case MONO_TYPE_VALUETYPE: case MONO_TYPE_GENERICINST: if (!can_access_type (access_klass, mono_class_from_mono_type (type))) return FALSE; } } return TRUE; } static gboolean can_access_type (MonoClass *access_klass, MonoClass *member_klass) { int access_level; if (access_klass->image->assembly && access_klass->image->assembly->corlib_internal) return TRUE; if (access_klass->element_class && !access_klass->enumtype) access_klass = access_klass->element_class; if (member_klass->element_class && !member_klass->enumtype) member_klass = member_klass->element_class; access_level = member_klass->flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; if (member_klass->byval_arg.type == MONO_TYPE_VAR || member_klass->byval_arg.type == MONO_TYPE_MVAR) return TRUE; if (member_klass->generic_class && !can_access_instantiation (access_klass, member_klass->generic_class->context.class_inst)) return FALSE; if (is_nesting_type (access_klass, member_klass) || (access_klass->nested_in && is_nesting_type (access_klass->nested_in, member_klass))) return TRUE; if (member_klass->nested_in && !can_access_type (access_klass, member_klass->nested_in)) return FALSE; /*Non nested type with nested visibility. We just fail it.*/ if (access_level >= TYPE_ATTRIBUTE_NESTED_PRIVATE && access_level <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM && member_klass->nested_in == NULL) return FALSE; switch (access_level) { case TYPE_ATTRIBUTE_NOT_PUBLIC: return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); case TYPE_ATTRIBUTE_PUBLIC: return TRUE; case TYPE_ATTRIBUTE_NESTED_PUBLIC: return TRUE; case TYPE_ATTRIBUTE_NESTED_PRIVATE: return is_nesting_type (member_klass, access_klass); case TYPE_ATTRIBUTE_NESTED_FAMILY: return mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); case TYPE_ATTRIBUTE_NESTED_ASSEMBLY: return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM: return can_access_internals (access_klass->image->assembly, member_klass->nested_in->image->assembly) && mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM: return can_access_internals (access_klass->image->assembly, member_klass->nested_in->image->assembly) || mono_class_has_parent_and_ignore_generics (access_klass, member_klass->nested_in); } return FALSE; } /* FIXME: check visibility of type, too */ static gboolean can_access_member (MonoClass *access_klass, MonoClass *member_klass, MonoClass* context_klass, int access_level) { MonoClass *member_generic_def; if (access_klass->image->assembly && access_klass->image->assembly->corlib_internal) return TRUE; if (((access_klass->generic_class && access_klass->generic_class->container_class) || access_klass->generic_container) && (member_generic_def = get_generic_definition_class (member_klass))) { MonoClass *access_container; if (access_klass->generic_container) access_container = access_klass; else access_container = access_klass->generic_class->container_class; if (can_access_member (access_container, member_generic_def, context_klass, access_level)) return TRUE; } /* Partition I 8.5.3.2 */ /* the access level values are the same for fields and methods */ switch (access_level) { case FIELD_ATTRIBUTE_COMPILER_CONTROLLED: /* same compilation unit */ return access_klass->image == member_klass->image; case FIELD_ATTRIBUTE_PRIVATE: return access_klass == member_klass; case FIELD_ATTRIBUTE_FAM_AND_ASSEM: if (is_valid_family_access (access_klass, member_klass, context_klass) && can_access_internals (access_klass->image->assembly, member_klass->image->assembly)) return TRUE; return FALSE; case FIELD_ATTRIBUTE_ASSEMBLY: return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); case FIELD_ATTRIBUTE_FAMILY: if (is_valid_family_access (access_klass, member_klass, context_klass)) return TRUE; return FALSE; case FIELD_ATTRIBUTE_FAM_OR_ASSEM: if (is_valid_family_access (access_klass, member_klass, context_klass)) return TRUE; return can_access_internals (access_klass->image->assembly, member_klass->image->assembly); case FIELD_ATTRIBUTE_PUBLIC: return TRUE; } return FALSE; } gboolean mono_method_can_access_field (MonoMethod *method, MonoClassField *field) { /* FIXME: check all overlapping fields */ int can = can_access_member (method->klass, field->parent, NULL, mono_field_get_type (field)->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); if (!can) { MonoClass *nested = method->klass->nested_in; while (nested) { can = can_access_member (nested, field->parent, NULL, mono_field_get_type (field)->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); if (can) return TRUE; nested = nested->nested_in; } } return can; } gboolean mono_method_can_access_method (MonoMethod *method, MonoMethod *called) { int can = can_access_member (method->klass, called->klass, NULL, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); if (!can) { MonoClass *nested = method->klass->nested_in; while (nested) { can = can_access_member (nested, called->klass, NULL, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); if (can) return TRUE; nested = nested->nested_in; } } /* * FIXME: * with generics calls to explicit interface implementations can be expressed * directly: the method is private, but we must allow it. This may be opening * a hole or the generics code should handle this differently. * Maybe just ensure the interface type is public. */ if ((called->flags & METHOD_ATTRIBUTE_VIRTUAL) && (called->flags & METHOD_ATTRIBUTE_FINAL)) return TRUE; return can; } /* * mono_method_can_access_method_full: * @method: The caller method * @called: The called method * @context_klass: The static type on stack of the owner @called object used * * This function must be used with instance calls, as they have more strict family accessibility. * It can be used with static methods, but context_klass should be NULL. * * Returns: TRUE if caller have proper visibility and acessibility to @called */ gboolean mono_method_can_access_method_full (MonoMethod *method, MonoMethod *called, MonoClass *context_klass) { MonoClass *access_class = method->klass; MonoClass *member_class = called->klass; int can = can_access_member (access_class, member_class, context_klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); if (!can) { MonoClass *nested = access_class->nested_in; while (nested) { can = can_access_member (nested, member_class, context_klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK); if (can) break; nested = nested->nested_in; } } if (!can) return FALSE; can = can_access_type (access_class, member_class); if (!can) { MonoClass *nested = access_class->nested_in; while (nested) { can = can_access_type (nested, member_class); if (can) break; nested = nested->nested_in; } } if (!can) return FALSE; if (called->is_inflated) { MonoMethodInflated * infl = (MonoMethodInflated*)called; if (infl->context.method_inst && !can_access_instantiation (access_class, infl->context.method_inst)) return FALSE; } return TRUE; } /* * mono_method_can_access_field_full: * @method: The caller method * @field: The accessed field * @context_klass: The static type on stack of the owner @field object used * * This function must be used with instance fields, as they have more strict family accessibility. * It can be used with static fields, but context_klass should be NULL. * * Returns: TRUE if caller have proper visibility and acessibility to @field */ gboolean mono_method_can_access_field_full (MonoMethod *method, MonoClassField *field, MonoClass *context_klass) { MonoClass *access_class = method->klass; MonoClass *member_class = field->parent; /* FIXME: check all overlapping fields */ int can = can_access_member (access_class, member_class, context_klass, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); if (!can) { MonoClass *nested = access_class->nested_in; while (nested) { can = can_access_member (nested, member_class, context_klass, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK); if (can) break; nested = nested->nested_in; } } if (!can) return FALSE; can = can_access_type (access_class, member_class); if (!can) { MonoClass *nested = access_class->nested_in; while (nested) { can = can_access_type (nested, member_class); if (can) break; nested = nested->nested_in; } } if (!can) return FALSE; return TRUE; } /** * mono_type_is_valid_enum_basetype: * @type: The MonoType to check * * Returns: TRUE if the type can be used as the basetype of an enum */ gboolean mono_type_is_valid_enum_basetype (MonoType * type) { switch (type->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: case MONO_TYPE_I8: case MONO_TYPE_U8: case MONO_TYPE_I: case MONO_TYPE_U: return TRUE; } return FALSE; } /** * mono_class_is_valid_enum: * @klass: An enum class to be validated * * This method verify the required properties an enum should have. * * Returns: TRUE if the informed enum class is valid * * FIXME: TypeBuilder enums are allowed to implement interfaces, but since they cannot have methods, only empty interfaces are possible * FIXME: enum types are not allowed to have a cctor, but mono_reflection_create_runtime_class sets has_cctor to 1 for all types * FIXME: TypeBuilder enums can have any kind of static fields, but the spec is very explicit about that (P II 14.3) */ gboolean mono_class_is_valid_enum (MonoClass *klass) { MonoClassField * field; gpointer iter = NULL; gboolean found_base_field = FALSE; g_assert (klass->enumtype); /* we cannot test against mono_defaults.enum_class, or mcs won't be able to compile the System namespace*/ if (!klass->parent || strcmp (klass->parent->name, "Enum") || strcmp (klass->parent->name_space, "System") ) { return FALSE; } if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_AUTO_LAYOUT) return FALSE; while ((field = mono_class_get_fields (klass, &iter))) { if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) { if (found_base_field) return FALSE; found_base_field = TRUE; if (!mono_type_is_valid_enum_basetype (field->type)) return FALSE; } } if (!found_base_field) return FALSE; if (klass->method.count > 0) return FALSE; return TRUE; } gboolean mono_generic_class_is_generic_type_definition (MonoGenericClass *gklass) { return gklass->context.class_inst == gklass->container_class->generic_container->context.class_inst; } /* * mono_class_setup_interface_id: * * Initializes MonoClass::interface_id if required. * * LOCKING: Acquires the loader lock. */ void mono_class_setup_interface_id (MonoClass *class) { mono_loader_lock (); if (MONO_CLASS_IS_INTERFACE (class) && !class->interface_id) class->interface_id = mono_get_unique_iid (class); mono_loader_unlock (); } /* * mono_class_alloc_ext: * * Allocate klass->ext if not already done. * LOCKING: Assumes the loader lock is held. */ void mono_class_alloc_ext (MonoClass *klass) { if (!klass->ext) { klass->ext = mono_class_alloc0 (klass, sizeof (MonoClassExt)); class_ext_size += sizeof (MonoClassExt); } } /* * mono_class_setup_interfaces: * * Initialize class->interfaces/interfaces_count. * LOCKING: Acquires the loader lock. * This function can fail the type. */ void mono_class_setup_interfaces (MonoClass *klass, MonoError *error) { int i; mono_error_init (error); if (klass->interfaces_inited) return; mono_loader_lock (); if (klass->interfaces_inited) { mono_loader_unlock (); return; } if (klass->rank == 1 && klass->byval_arg.type != MONO_TYPE_ARRAY && mono_defaults.generic_ilist_class) { MonoType *args [1]; /* generic IList, ICollection, IEnumerable */ klass->interface_count = 1; klass->interfaces = mono_image_alloc0 (klass->image, sizeof (MonoClass*) * klass->interface_count); args [0] = &klass->element_class->byval_arg; klass->interfaces [0] = mono_class_bind_generic_parameters ( mono_defaults.generic_ilist_class, 1, args, FALSE); } else if (klass->generic_class) { MonoClass *gklass = klass->generic_class->container_class; klass->interface_count = gklass->interface_count; klass->interfaces = mono_class_new0 (klass, MonoClass *, klass->interface_count); for (i = 0; i < klass->interface_count; i++) { klass->interfaces [i] = mono_class_inflate_generic_class_checked (gklass->interfaces [i], mono_generic_class_get_context (klass->generic_class), error); if (!mono_error_ok (error)) { mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not setup the interfaces")); klass->interfaces = NULL; return; } } } mono_memory_barrier (); klass->interfaces_inited = TRUE; mono_loader_unlock (); } static void mono_field_resolve_type (MonoClassField *field, MonoError *error) { MonoClass *class = field->parent; MonoImage *image = class->image; MonoClass *gtd = class->generic_class ? mono_class_get_generic_type_definition (class) : NULL; int field_idx = field - class->fields; mono_error_init (error); if (gtd) { MonoClassField *gfield = >d->fields [field_idx]; MonoType *gtype = mono_field_get_type_checked (gfield, error); if (!mono_error_ok (error)) { char *err_msg = g_strdup_printf ("Could not load field %d type due to: %s", field_idx, mono_error_get_message (error)); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, err_msg); g_free (err_msg); } field->type = mono_class_inflate_generic_type_no_copy (image, gtype, mono_class_get_context (class), error); if (!mono_error_ok (error)) { char *err_msg = g_strdup_printf ("Could not load field %d type due to: %s", field_idx, mono_error_get_message (error)); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, err_msg); g_free (err_msg); } } else { const char *sig; guint32 cols [MONO_FIELD_SIZE]; MonoGenericContainer *container = NULL; int idx = class->field.first + field_idx; /*FIXME, in theory we do not lazy load SRE fields*/ g_assert (!image->dynamic); if (class->generic_container) { container = class->generic_container; } else if (gtd) { container = gtd->generic_container; g_assert (container); } /* class->field.first and idx points into the fieldptr table */ mono_metadata_decode_table_row (image, MONO_TABLE_FIELD, idx, cols, MONO_FIELD_SIZE); if (!mono_verifier_verify_field_signature (image, cols [MONO_FIELD_SIGNATURE], NULL)) { mono_error_set_type_load_class (error, class, "Could not verify field %s signature", field->name); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); return; } sig = mono_metadata_blob_heap (image, cols [MONO_FIELD_SIGNATURE]); mono_metadata_decode_value (sig, &sig); /* FIELD signature == 0x06 */ g_assert (*sig == 0x06); field->type = mono_metadata_parse_type_full (image, container, MONO_PARSE_FIELD, cols [MONO_FIELD_FLAGS], sig + 1, &sig); if (!field->type) { mono_error_set_type_load_class (error, class, "Could not load field %s type", field->name); mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL); mono_loader_clear_error (); } } } static guint32 mono_field_resolve_flags (MonoClassField *field) { MonoClass *class = field->parent; MonoImage *image = class->image; MonoClass *gtd = class->generic_class ? mono_class_get_generic_type_definition (class) : NULL; int field_idx = field - class->fields; if (gtd) { MonoClassField *gfield = >d->fields [field_idx]; return mono_field_get_flags (gfield); } else { int idx = class->field.first + field_idx; /*FIXME, in theory we do not lazy load SRE fields*/ g_assert (!image->dynamic); return mono_metadata_decode_table_row_col (image, MONO_TABLE_FIELD, idx, MONO_FIELD_FLAGS); } } /** * mono_class_setup_basic_field_info: * @class: The class to initialize * * Initializes the class->fields array of fields. * Aquires the loader lock. */ static void mono_class_setup_basic_field_info_locking (MonoClass *class) { mono_loader_lock (); mono_class_setup_basic_field_info (class); mono_loader_unlock (); } /** * mono_class_get_fields_lazy: * @klass: the MonoClass to act on * * This routine is an iterator routine for retrieving the fields in a class. * Only minimal information about fields are loaded. Accessors must be used * for all MonoClassField returned. * * You must pass a gpointer that points to zero and is treated as an opaque handle to * iterate over all of the elements. When no more values are * available, the return value is NULL. * * Returns: a @MonoClassField* on each iteration, or NULL when no more fields are available. */ MonoClassField* mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter) { MonoClassField* field; if (!iter) return NULL; if (!*iter) { mono_class_setup_basic_field_info_locking (klass); if (!klass->fields) return NULL; /* start from the first */ if (klass->field.count) { return *iter = &klass->fields [0]; } else { /* no fields */ return NULL; } } field = *iter; field++; if (field < &klass->fields [klass->field.count]) { return *iter = field; } return NULL; }