[runtime] Implement support for fixed char arrays. Fixes #48429. (#4072)
authorZoltan Varga <vargaz@gmail.com>
Fri, 2 Dec 2016 00:02:33 +0000 (19:02 -0500)
committerGitHub <noreply@github.com>
Fri, 2 Dec 2016 00:02:33 +0000 (19:02 -0500)
mono/metadata/marshal.c
mono/metadata/metadata.h
mono/tests/libtest.c
mono/tests/pinvoke2.cs

index a9ebe94ea7070604ec8bf18cad03a88f06e7e0b2..87c55ba890ab4df66e7c419b5fa42b9b9bb5b57a 100644 (file)
@@ -216,10 +216,11 @@ static void
 mono_icall_end (MonoThreadInfo *info, HandleStackMark *stackmark, MonoError *error);
 
 /* Lazy class loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (string_builder, System.Text, StringBuilder)
-static GENERATE_GET_CLASS_WITH_CACHE (date_time, System, DateTime)
-static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, System.Runtime.InteropServices, UnmanagedFunctionPointerAttribute)
-static GENERATE_TRY_GET_CLASS_WITH_CACHE (icustom_marshaler, System.Runtime.InteropServices, ICustomMarshaler)
+static GENERATE_GET_CLASS_WITH_CACHE (string_builder, System.Text, StringBuilder);
+static GENERATE_GET_CLASS_WITH_CACHE (date_time, System, DateTime);
+static GENERATE_GET_CLASS_WITH_CACHE (fixed_buffer_attribute, System.Runtime.CompilerServices, FixedBufferAttribute);
+static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, System.Runtime.InteropServices, UnmanagedFunctionPointerAttribute);
+static GENERATE_TRY_GET_CLASS_WITH_CACHE (icustom_marshaler, System.Runtime.InteropServices, ICustomMarshaler);
 
 /* MonoMethod pointers to SafeHandle::DangerousAddRef and ::DangerousRelease */
 static MonoMethod *sh_dangerous_add_ref;
@@ -1933,6 +1934,7 @@ offset_of_first_nonstatic_field (MonoClass *klass)
 {
        int i;
        int fcount = mono_class_get_field_count (klass);
+       mono_class_setup_fields (klass);
        for (i = 0; i < fcount; i++) {
                if (!(klass->fields[i].type->attrs & FIELD_ATTRIBUTE_STATIC) && !mono_field_is_deleted (&klass->fields[i]))
                        return klass->fields[i].offset - sizeof (MonoObject);
@@ -1941,6 +1943,145 @@ offset_of_first_nonstatic_field (MonoClass *klass)
        return 0;
 }
 
+static gboolean
+get_fixed_buffer_attr (MonoClassField *field, MonoType **out_etype, int *out_len)
+{
+       MonoError error;
+       MonoCustomAttrInfo *cinfo;
+       MonoCustomAttrEntry *attr;
+       int aindex;
+
+       cinfo = mono_custom_attrs_from_field_checked (field->parent, field, &error);
+       if (!is_ok (&error))
+               return FALSE;
+       attr = NULL;
+       if (cinfo) {
+               for (aindex = 0; aindex < cinfo->num_attrs; ++aindex) {
+                       MonoClass *ctor_class = cinfo->attrs [aindex].ctor->klass;
+                       if (mono_class_has_parent (ctor_class, mono_class_get_fixed_buffer_attribute_class ())) {
+                               attr = &cinfo->attrs [aindex];
+                               break;
+                       }
+               }
+       }
+       if (attr) {
+               MonoArray *typed_args, *named_args;
+               CattrNamedArg *arginfo;
+               MonoObject *o;
+
+               mono_reflection_create_custom_attr_data_args (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error);
+               if (!is_ok (&error))
+                       return FALSE;
+               g_assert (mono_array_length (typed_args) == 2);
+
+               /* typed args */
+               o = mono_array_get (typed_args, MonoObject*, 0);
+               *out_etype = monotype_cast (o)->type;
+               o = mono_array_get (typed_args, MonoObject*, 1);
+               g_assert (o->vtable->klass == mono_defaults.int32_class);
+               *out_len = *(gint32*)mono_object_unbox (o);
+               g_free (arginfo);
+       }
+       if (cinfo && !cinfo->cached)
+               mono_custom_attrs_free (cinfo);
+       return attr != NULL;
+}
+
+static void
+emit_fixed_buf_conv (MonoMethodBuilder *mb, MonoType *type, MonoType *etype, int len, gboolean to_object, int *out_usize)
+{
+       MonoClass *klass = mono_class_from_mono_type (type);
+       MonoClass *eklass = mono_class_from_mono_type (etype);
+       int esize;
+
+       esize = mono_class_native_size (eklass, NULL);
+
+       MonoMarshalNative string_encoding = klass->unicode ? MONO_NATIVE_LPWSTR : MONO_NATIVE_LPSTR;
+       int usize = mono_class_value_size (eklass, NULL);
+       int msize = mono_class_value_size (eklass, NULL);
+
+       //printf ("FIXED: %s %d %d\n", mono_type_full_name (type), eklass->blittable, string_encoding);
+
+       if (eklass->blittable) {
+               /* copy the elements */
+               mono_mb_emit_ldloc (mb, 1);
+               mono_mb_emit_ldloc (mb, 0);
+               mono_mb_emit_icon (mb, len * esize);
+               mono_mb_emit_byte (mb, CEE_PREFIX1);
+               mono_mb_emit_byte (mb, CEE_CPBLK);
+       } else {
+               int index_var;
+               guint32 label2, label3;
+
+               /* Emit marshalling loop */
+               index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+               mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+               mono_mb_emit_stloc (mb, index_var);
+
+               /* Loop header */
+               label2 = mono_mb_get_label (mb);
+               mono_mb_emit_ldloc (mb, index_var);
+               mono_mb_emit_icon (mb, len);
+               label3 = mono_mb_emit_branch (mb, CEE_BGE);
+
+               /* src/dst is already set */
+
+               /* Do the conversion */
+               MonoTypeEnum t = etype->type;
+               switch (t) {
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               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_I8:
+               case MONO_TYPE_U8:
+               case MONO_TYPE_PTR:
+               case MONO_TYPE_R4:
+               case MONO_TYPE_R8:
+                       mono_mb_emit_ldloc (mb, 1);
+                       mono_mb_emit_ldloc (mb, 0);
+                       if (t == MONO_TYPE_CHAR && string_encoding != MONO_NATIVE_LPWSTR) {
+                               if (to_object) {
+                                       mono_mb_emit_byte (mb, CEE_LDIND_U1);
+                                       mono_mb_emit_byte (mb, CEE_STIND_I2);
+                               } else {
+                                       mono_mb_emit_byte (mb, CEE_LDIND_U2);
+                                       mono_mb_emit_byte (mb, CEE_STIND_I1);
+                               }
+                               usize = 1;
+                       } else {
+                               mono_mb_emit_byte (mb, mono_type_to_ldind (etype));
+                               mono_mb_emit_byte (mb, mono_type_to_stind (etype));
+                       }
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
+               if (to_object) {
+                       mono_mb_emit_add_to_local (mb, 0, usize);
+                       mono_mb_emit_add_to_local (mb, 1, msize);
+               } else {
+                       mono_mb_emit_add_to_local (mb, 0, msize);
+                       mono_mb_emit_add_to_local (mb, 1, usize);
+               }
+
+               /* Loop footer */
+               mono_mb_emit_add_to_local (mb, index_var, 1);
+
+               mono_mb_emit_branch_label (mb, CEE_BR, label2);
+
+               mono_mb_patch_branch (mb, label3);
+       }
+
+       *out_usize = usize * len;
+}
+
 static void
 emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object,
                                                int offset_of_first_child_field, MonoMarshalNative string_encoding)
@@ -2067,6 +2208,8 @@ emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_obje
                                break;
                        case MONO_TYPE_VALUETYPE: {
                                int src_var, dst_var;
+                               MonoType *etype;
+                               int len;
 
                                if (ftype->data.klass->enumtype) {
                                        ftype = mono_class_enum_basetype (ftype->data.klass);
@@ -2083,7 +2226,11 @@ emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_obje
                                mono_mb_emit_ldloc (mb, 1);
                                mono_mb_emit_stloc (mb, dst_var);
 
-                               emit_struct_conv (mb, ftype->data.klass, to_object);
+                               if (get_fixed_buffer_attr (info->fields [i].field, &etype, &len)) {
+                                       emit_fixed_buf_conv (mb, ftype, etype, len, to_object, &usize);
+                               } else {
+                                       emit_struct_conv (mb, ftype->data.klass, to_object);
+                               }
 
                                /* restore the old src pointer */
                                mono_mb_emit_ldloc (mb, src_var);
index d8e48318b4da77868fe8e06b40810e31849b6402..1c611f430070d99e83fc233d765162e3dcbffa6f 100644 (file)
@@ -170,6 +170,7 @@ typedef enum {
        MONO_MARSHAL_CONV_SB_UTF8STR,
        MONO_MARSHAL_CONV_UTF8STR_STR,
        MONO_MARSHAL_CONV_UTF8STR_SB,
+       MONO_MARSHAL_CONV_FIXED_BUFFER
 } MonoMarshalConv;
 
 #define MONO_MARSHAL_CONV_INVALID ((MonoMarshalConv)-1)
index 3b0d98c37c81207db3d43c1683278182a8a31611..774a0ce1022bd4ee6e4463e2376c1ae5d384fcea 100644 (file)
@@ -7230,6 +7230,38 @@ mono_test_marshal_fixed_array (FixedArrayStruct s)
        return s.array [0] + s.array [1] + s.array [2];
 }
 
+typedef struct {
+       char array [16];
+       char c;
+} FixedBufferChar;
+
+LIBTEST_API int STDCALL
+mono_test_marshal_fixed_buffer_char (FixedBufferChar *s)
+{
+       if (!(s->array [0] == 'A' && s->array [1] == 'B' && s->array [2] == 'C' && s->c == 'D'))
+               return 1;
+       s->array [0] = 'E';
+       s->array [1] = 'F';
+       s->c = 'G';
+       return 0;
+}
+
+typedef struct {
+       short array [16];
+       short c;
+} FixedBufferUnicode;
+
+LIBTEST_API int STDCALL
+mono_test_marshal_fixed_buffer_unicode (FixedBufferUnicode *s)
+{
+       if (!(s->array [0] == 'A' && s->array [1] == 'B' && s->array [2] == 'C' && s->c == 'D'))
+               return 1;
+       s->array [0] = 'E';
+       s->array [1] = 'F';
+       s->c = 'G';
+       return 0;
+}
+
 const int NSTRINGS = 6;
 //test strings
 const char  *utf8Strings[] = {  
index f9ecf68a4027a95b9da5b0681024afd47b435f97..807bc0046df051ead22af0bd730e00e3b38ce217 100644 (file)
@@ -1939,5 +1939,53 @@ public unsafe class Tests {
                }
                return mono_test_marshal_pointer_array (arr2);
        }
+
+    [StructLayout(LayoutKind.Sequential)]
+       public struct FixedBufferChar {
+        public fixed char array[16];
+               public char c;
+       }
+
+       [DllImport ("libtest", EntryPoint="mono_test_marshal_fixed_buffer_char")]
+       public static extern int mono_test_marshal_fixed_buffer_char (ref FixedBufferChar s);
+
+       public static unsafe int test_0_fixed_buffer_char () {
+               var s = new FixedBufferChar ();
+               s.array [0] = 'A';
+               s.array [1] = 'B';
+               s.array [2] = 'C';
+               s.c = 'D';
+
+               int res = mono_test_marshal_fixed_buffer_char (ref s);
+               if (res != 0)
+                       return 1;
+               if (s.array [0] != 'E' || s.array [1] != 'F' || s.c != 'G')
+                       return 2;
+               return 0;
+       }
+
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+       public struct FixedBufferUnicode {
+        public fixed char array[16];
+               public char c;
+       }
+
+       [DllImport ("libtest", EntryPoint="mono_test_marshal_fixed_buffer_unicode")]
+       public static extern int mono_test_marshal_fixed_buffer_unicode (ref FixedBufferUnicode s);
+
+       public static unsafe int test_0_fixed_buffer_unicode () {
+               var s = new FixedBufferUnicode ();
+               s.array [0] = 'A';
+               s.array [1] = 'B';
+               s.array [2] = 'C';
+               s.c = 'D';
+
+               int res = mono_test_marshal_fixed_buffer_unicode (ref s);
+               if (res != 0)
+                       return 1;
+               if (s.array [0] != 'E' || s.array [1] != 'F' || s.c != 'G')
+                       return 2;
+               return 0;
+       }
 }