}
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.
*/
int first_non_interface_slot;
#endif
GSList *virt_methods = NULL, *l;
+ int stelemref_slot = 0;
if (class->vtable)
return;
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);
}
}
+ /*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++) {
class->ghcimpl = cached_info.ghcimpl;
class->has_cctor = cached_info.has_cctor;
} else if (class->rank == 1 && class->byval_arg.type == MONO_TYPE_SZARRAY) {
- static int szarray_vtable_size = 0;
+ /* 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) {
+ if (!szarray_vtable_size [slot]) {
mono_class_setup_vtable (class);
- szarray_vtable_size = class->vtable_size;
+ szarray_vtable_size [slot] = class->vtable_size;
} else {
- class->vtable_size = szarray_vtable_size;
+ class->vtable_size = szarray_vtable_size[slot];
}
} else if (class->generic_class && !MONO_CLASS_IS_INTERFACE (class)) {
MonoClass *gklass = class->generic_class->container_class;
}
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) {
if (mono_loader_get_last_error ())
goto leave;
}
- setup_interface_offsets (class, class->parent->vtable_size);
+ 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);
}
return res;
}
+enum {
+ STELEMREF_OBJECT, /*no check at all*/
+ STELEMREF_SEALED_CLASS, /*check vtable->klass->element_type */
+ STELEMREF_CLASS, /*only the klass->parents check*/
+ STELEMREF_COMPLEX, /*arrays, interfaces, MBR or classes with variant generic args - go straight to icalls*/
+ STELEMREF_KIND_COUNT
+};
+
+static const char *strelemref_wrapper_name[] = {
+ "object", "sealed_class", "class", "complex"
+};
+
+static gboolean
+is_monomorphic_array (MonoClass *klass)
+{
+ MonoClass *element_class;
+ if (klass->rank != 1)
+ return FALSE;
+
+ element_class = klass->element_class;
+ return (element_class->flags & TYPE_ATTRIBUTE_SEALED) || element_class->valuetype;
+}
+
+static int
+get_virtual_stelemref_kind (MonoClass *element_class)
+{
+ if (element_class == mono_defaults.object_class)
+ return STELEMREF_OBJECT;
+ if (is_monomorphic_array (element_class))
+ return STELEMREF_SEALED_CLASS;
+ /*Arrays are sealed but are covariant on their element type, We can't use any of the fast paths.*/
+ if (MONO_CLASS_IS_INTERFACE (element_class) || element_class->marshalbyref || element_class->rank || mono_class_has_variant_generic_params (element_class))
+ return STELEMREF_COMPLEX;
+ if (element_class->flags & TYPE_ATTRIBUTE_SEALED)
+ return STELEMREF_SEALED_CLASS;
+ return STELEMREF_CLASS;
+}
+
+static void
+load_array_element_address (MonoMethodBuilder *mb)
+{
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
+}
+
+static void
+load_array_class (MonoMethodBuilder *mb, int aklass)
+{
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, element_class));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, aklass);
+}
+
+static void
+load_value_class (MonoMethodBuilder *mb, int vklass)
+{
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, vklass);
+}
+
+#if 0
+static void
+record_vstore_2 (MonoObject *array, size_t index, MonoObject *value)
+{
+ char *name = mono_type_get_full_name (array->vtable->klass->element_class);
+ printf ("slow vstore of %s\n", name);
+ g_free (name);
+}
+#endif
+
+/*
+ * TODO:
+ * - Separate simple interfaces from variant interfaces or mbr types. This way we can avoid the icall for them.
+ * - Emit a (new) mono bytecode that produces OP_COND_EXC_NE_UN to raise ArrayTypeMismatch
+ * - Maybe mve some MonoClass field into the vtable to reduce the number of loads
+ * - Add a case for arrays of arrays.
+ */
+MonoMethod*
+mono_marshal_get_virtual_stelemref (MonoClass *array_class)
+{
+ static MonoMethod *cached_methods [STELEMREF_KIND_COUNT] = { NULL }; /*object iface sealed regular*/
+ static MonoMethodSignature *signature;
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ int kind;
+
+ guint32 b1, b2, b3;
+ int aklass, vklass;
+ int array_slot_addr;
+
+ g_assert (array_class->rank == 1);
+ kind = get_virtual_stelemref_kind (array_class->element_class);
+
+ if (cached_methods [kind])
+ return cached_methods [kind];
+
+ mb = mono_mb_new_no_dup_name (mono_defaults.object_class, g_strdup_printf ("virt_stelemref_%s", strelemref_wrapper_name [kind]), MONO_WRAPPER_STELEMREF);
+
+ if (!signature) {
+ MonoMethodSignature *sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
+
+ /* void this::stelemref (size_t idx, void* value) */
+ sig->ret = &mono_defaults.void_class->byval_arg;
+ sig->hasthis = TRUE;
+ sig->params [0] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */
+ sig->params [1] = &mono_defaults.object_class->byval_arg;
+ signature = sig;
+ }
+
+ /*For now simply call plain old stelemref*/
+ switch (kind) {
+ case STELEMREF_OBJECT:
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ /* do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+ break;
+
+ case STELEMREF_COMPLEX:
+ /*
+ <ldelema (bound check)>
+ if (!value)
+ goto store;
+ if (!mono_object_isinst (value, aklass))
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+
+ do_exception:
+ throw new ArrayTypeMismatchException ();
+ */
+
+ aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
+
+#if 0
+ {
+ /*Use this to debug stores that are going thru the slow path*/
+ MonoMethodSignature *csig;
+ csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+ csig->ret = &mono_defaults.void_class->byval_arg;
+ csig->params [0] = &mono_defaults.object_class->byval_arg;
+ csig->params [1] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */
+ csig->params [2] = &mono_defaults.object_class->byval_arg;
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_native_call (mb, csig, record_vstore_2);
+ }
+#endif
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /*if (mono_object_isinst (value, aklass)) */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_icall (mb, mono_object_isinst);
+ b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ case STELEMREF_SEALED_CLASS:
+ /*
+ <ldelema (bound check)>
+ if (!value)
+ goto store;
+
+ aklass = array->vtable->klass->element_class;
+ vklass = value->vtable->klass;
+
+ if (vklass != aklass)
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+
+ do_exception:
+ throw new ArrayTypeMismatchException ();
+ */
+ aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
+
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /*if (vklass != aklass) goto do_exception; */
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldloc (mb, vklass);
+ b2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ case STELEMREF_CLASS:
+ /*
+ the method:
+ <ldelema (bound check)>
+ if (!value)
+ goto do_store;
+
+ aklass = array->vtable->klass->element_class;
+ vklass = value->vtable->klass;
+
+ if (vklass->idepth < aklass->idepth)
+ goto do_exception;
+
+ if (vklass->supertypes [aklass->idepth - 1] != aklass)
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+ return;
+
+ long:
+ throw new ArrayTypeMismatchException ();
+ */
+ aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /*if (mono_object_isinst (value, aklass)) */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_icall (mb, mono_object_isinst);
+ b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* if (vklass->idepth < aklass->idepth) goto failue */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ b2 = mono_mb_emit_branch (mb, CEE_BLT_UN);
+
+ /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, supertypes));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+ mono_mb_emit_icon (mb, 1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_icon (mb, sizeof (void*));
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ b3 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+ mono_mb_patch_branch (mb, b3);
+
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ default:
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_managed_call (mb, mono_marshal_get_stelemref (), NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+ g_assert (0);
+ }
+
+ res = mono_mb_create_method (mb, signature, 4);
+ res->flags |= METHOD_ATTRIBUTE_VIRTUAL;
+ cached_methods [kind] = res;
+
+ mono_mb_free (mb);
+ return res;
+}
+
MonoMethod*
mono_marshal_get_stelemref ()
{