]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
vala: Add foreach statement support for GLib.Array
authorPrinceton Ferro <princetonferro@gmail.com>
Mon, 24 Jan 2022 16:50:25 +0000 (11:50 -0500)
committerRico Tzschichholz <ricotz@ubuntu.com>
Mon, 24 Jan 2022 19:24:06 +0000 (20:24 +0100)
It is now possible to use foreach with a GLib.Array

codegen/valaccodebasemodule.vala
codegen/valaccodecontrolflowmodule.vala
tests/Makefile.am
tests/basic-types/garray.c-expected
tests/basic-types/garray.vala
tests/control-flow/foreach.c-expected
tests/control-flow/foreach.vala
tests/control-flow/garray-foreach-variable.test [new file with mode: 0644]
vala/valaforeachstatement.vala

index 2e34003f1674b1edf986381b55dccb4b45a78c07..ccb2fe988d60294b56eedb0fe6b4b1c09d8f447e 100644 (file)
@@ -335,7 +335,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
        public Class gqueue_type;
        public Class gvaluearray_type;
        public TypeSymbol gstringbuilder_type;
-       public TypeSymbol garray_type;
+       public Class garray_type;
        public TypeSymbol gbytearray_type;
        public TypeSymbol genericarray_type;
        public TypeSymbol gthreadpool_type;
@@ -509,7 +509,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                        gqueue_type = (Class) glib_ns.scope.lookup ("Queue");
                        gvaluearray_type = (Class) glib_ns.scope.lookup ("ValueArray");
                        gstringbuilder_type = (TypeSymbol) glib_ns.scope.lookup ("StringBuilder");
-                       garray_type = (TypeSymbol) glib_ns.scope.lookup ("Array");
+                       garray_type = (Class) glib_ns.scope.lookup ("Array");
                        gbytearray_type = (TypeSymbol) glib_ns.scope.lookup ("ByteArray");
                        genericarray_type = (TypeSymbol) glib_ns.scope.lookup ("GenericArray");
                        gthreadpool_type = (TypeSymbol) glib_ns.scope.lookup ("ThreadPool");
index bbc53024713fc1d57ba699c55dc165fdc836e3fe..dc278e4755328c07943154bdf1c2b5a67b330353 100644 (file)
@@ -276,6 +276,40 @@ public abstract class Vala.CCodeControlFlowModule : CCodeMethodModule {
 
                        stmt.body.emit (this);
 
+                       ccode.close ();
+               } else if (stmt.collection.value_type.compatible (new ObjectType (garray_type))) {
+                       // iterating over a GArray
+
+                       var iterator_variable = new LocalVariable (uint_type.copy (), "%s_index".printf (stmt.variable_name));
+                       visit_local_variable (iterator_variable);
+                       var arr_index = get_variable_cname (get_local_cname (iterator_variable));
+
+                       var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (arr_index), new CCodeMemberAccess.pointer (get_variable_cexpression (get_local_cname (collection_backup)), "len"));
+
+                       ccode.open_for (new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeConstant ("0")),
+                                          ccond,
+                                          new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (arr_index), new CCodeConstant ("1"))));
+
+                       var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_array_index"));
+                       get_item.add_argument (get_variable_cexpression (get_local_cname (collection_backup)));
+                       get_item.add_argument (new CCodeIdentifier (get_ccode_name (stmt.type_reference)));
+                       get_item.add_argument (get_variable_cexpression (arr_index));
+
+                       if (collection_type.get_type_arguments ().size != 1) {
+                               Report.error (stmt.source_reference, "internal error: missing generic type argument");
+                               stmt.error = true;
+                               return;
+                       }
+
+                       var element_type = collection_type.get_type_arguments ().get (0).copy ();
+                       element_type.value_owned = false;
+                       var element_expr = get_cvalue_ (transform_value (new GLibValue (element_type, get_item, true), stmt.type_reference, stmt));
+
+                       visit_local_variable (stmt.element_variable);
+                       ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
+
+                       stmt.body.emit (this);
+
                        ccode.close ();
                } else if (stmt.collection.value_type.compatible (new ObjectType (glist_type)) || stmt.collection.value_type.compatible (new ObjectType (gslist_type))) {
                        // iterating over a GList or GSList
index 77eb144a15a3ed0f0be420a4c9dd2649ce52bd70..b56263e8fbb3ab1115926e175356586f6713ebd9 100644 (file)
@@ -277,6 +277,7 @@ TESTS = \
        control-flow/for.vala \
        control-flow/for-switch-continue.vala \
        control-flow/foreach.vala \
+       control-flow/garray-foreach-variable.test \
        control-flow/gptrarray-foreach-variable.test \
        control-flow/local-clash-with-implicit-this.vala \
        control-flow/missing-break.test \
index e81314d5773cfd59a7c71d92f8ddb0d22d2c2833..07713ca1a05af594e06d6a7853c6a1b52d0ff980 100644 (file)
@@ -74,6 +74,7 @@ VALA_EXTERN void foo_struct_destroy (FooStruct* self);
 VALA_EXTERN void test_garray (void);
 static void _g_object_unref0_ (gpointer var);
 static void _vala_Foo_free_function_content_of (gpointer data);
+VALA_EXTERN void test_garray_foreach (void);
 VALA_EXTERN void test_int_garray (void);
 VALA_EXTERN GArray* create_struct_garray (void);
 static void _foo_struct_free0_ (gpointer var);
@@ -386,6 +387,182 @@ test_garray (void)
        _g_array_unref0 (array);
 }
 
+void
+test_garray_foreach (void)
+{
+       GArray* array = NULL;
+       GArray* _tmp0_;
+       Foo* foo1 = NULL;
+       Foo* _tmp1_;
+       Foo* foo2 = NULL;
+       Foo* _tmp2_;
+       Foo* foo3 = NULL;
+       Foo* _tmp3_;
+       GArray* _tmp4_;
+       Foo* _tmp5_;
+       Foo* _tmp6_;
+       Foo* _tmp7_;
+       GArray* _tmp8_;
+       Foo* _tmp9_;
+       Foo* _tmp10_;
+       Foo* _tmp11_;
+       GArray* _tmp12_;
+       Foo* _tmp13_;
+       Foo* _tmp14_;
+       Foo* _tmp15_;
+       GArray* _tmp16_;
+       gint loop_size = 0;
+       GArray* _tmp17_;
+       GArray* _tmp26_;
+       Foo* _tmp36_;
+       Foo* _tmp37_;
+       Foo* _tmp38_;
+       _tmp0_ = g_array_new (TRUE, TRUE, sizeof (Foo*));
+       g_array_set_clear_func (_tmp0_, (GDestroyNotify) _vala_Foo_free_function_content_of);
+       array = _tmp0_;
+       _tmp1_ = foo_new ();
+       foo1 = _tmp1_;
+       _tmp2_ = foo_new ();
+       foo2 = _tmp2_;
+       _tmp3_ = foo_new ();
+       foo3 = _tmp3_;
+       _tmp4_ = array;
+       _tmp5_ = foo1;
+       _tmp6_ = _g_object_ref0 (_tmp5_);
+       g_array_append_val (_tmp4_, _tmp6_);
+       _tmp7_ = foo1;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp7_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo1.ref_count == 2");
+       _tmp8_ = array;
+       _tmp9_ = foo2;
+       _tmp10_ = _g_object_ref0 (_tmp9_);
+       g_array_append_val (_tmp8_, _tmp10_);
+       _tmp11_ = foo2;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp11_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo2.ref_count == 2");
+       _tmp12_ = array;
+       _tmp13_ = foo3;
+       _tmp14_ = _g_object_ref0 (_tmp13_);
+       g_array_append_val (_tmp12_, _tmp14_);
+       _tmp15_ = foo3;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp15_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo3.ref_count == 2");
+       _tmp16_ = array;
+       _vala_assert (_tmp16_->len == ((guint) 3), "array.length == 3");
+       loop_size = 0;
+       _tmp17_ = array;
+       {
+               GArray* element_collection = NULL;
+               guint element_index = 0U;
+               element_collection = _tmp17_;
+               for (element_index = 0; element_index < element_collection->len; element_index = element_index + 1) {
+                       Foo* element = NULL;
+                       element = g_array_index (element_collection, Foo*, element_index);
+                       {
+                               gint _tmp18_;
+                               Foo* _tmp19_;
+                               _tmp18_ = loop_size;
+                               loop_size = _tmp18_ + 1;
+                               _tmp19_ = element;
+                               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp19_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "element.ref_count == 2");
+                               switch (loop_size) {
+                                       case 1:
+                                       {
+                                               Foo* _tmp20_;
+                                               Foo* _tmp21_;
+                                               _tmp20_ = element;
+                                               _tmp21_ = foo1;
+                                               _vala_assert (_tmp20_ == _tmp21_, "element == foo1");
+                                               break;
+                                       }
+                                       case 2:
+                                       {
+                                               Foo* _tmp22_;
+                                               Foo* _tmp23_;
+                                               _tmp22_ = element;
+                                               _tmp23_ = foo2;
+                                               _vala_assert (_tmp22_ == _tmp23_, "element == foo2");
+                                               break;
+                                       }
+                                       case 3:
+                                       {
+                                               Foo* _tmp24_;
+                                               Foo* _tmp25_;
+                                               _tmp24_ = element;
+                                               _tmp25_ = foo3;
+                                               _vala_assert (_tmp24_ == _tmp25_, "element == foo3");
+                                               break;
+                                       }
+                                       default:
+                                       break;
+                               }
+                       }
+               }
+       }
+       _vala_assert (loop_size == 3, "loop_size == 3");
+       loop_size = 0;
+       _tmp26_ = array;
+       {
+               GArray* element_collection = NULL;
+               guint element_index = 0U;
+               element_collection = _tmp26_;
+               for (element_index = 0; element_index < element_collection->len; element_index = element_index + 1) {
+                       Foo* _tmp27_;
+                       Foo* element = NULL;
+                       _tmp27_ = _g_object_ref0 (g_array_index (element_collection, Foo*, element_index));
+                       element = _tmp27_;
+                       {
+                               gint _tmp28_;
+                               Foo* _tmp29_;
+                               _tmp28_ = loop_size;
+                               loop_size = _tmp28_ + 1;
+                               _tmp29_ = element;
+                               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp29_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 3), "element.ref_count == 3");
+                               switch (loop_size) {
+                                       case 1:
+                                       {
+                                               Foo* _tmp30_;
+                                               Foo* _tmp31_;
+                                               _tmp30_ = element;
+                                               _tmp31_ = foo1;
+                                               _vala_assert (_tmp30_ == _tmp31_, "element == foo1");
+                                               break;
+                                       }
+                                       case 2:
+                                       {
+                                               Foo* _tmp32_;
+                                               Foo* _tmp33_;
+                                               _tmp32_ = element;
+                                               _tmp33_ = foo2;
+                                               _vala_assert (_tmp32_ == _tmp33_, "element == foo2");
+                                               break;
+                                       }
+                                       case 3:
+                                       {
+                                               Foo* _tmp34_;
+                                               Foo* _tmp35_;
+                                               _tmp34_ = element;
+                                               _tmp35_ = foo3;
+                                               _vala_assert (_tmp34_ == _tmp35_, "element == foo3");
+                                               break;
+                                       }
+                                       default:
+                                       break;
+                               }
+                               _g_object_unref0 (element);
+                       }
+               }
+       }
+       _vala_assert (loop_size == 3, "loop_size == 3");
+       _tmp36_ = foo1;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp36_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo1.ref_count == 2");
+       _tmp37_ = foo2;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp37_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo2.ref_count == 2");
+       _tmp38_ = foo3;
+       _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (_tmp38_, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 2), "foo3.ref_count == 2");
+       _g_object_unref0 (foo3);
+       _g_object_unref0 (foo2);
+       _g_object_unref0 (foo1);
+       _g_array_unref0 (array);
+}
+
 void
 test_int_garray (void)
 {
@@ -632,6 +809,7 @@ static void
 _vala_main (void)
 {
        test_garray ();
+       test_garray_foreach ();
        test_int_garray ();
        test_struct_garray ();
        test_object_garray ();
index 8732fa42fc73ce49075be69105ad6fa2ad4ca130..4f66c56af1acc9120be022c4916bdedc5b0e4900 100644 (file)
@@ -33,6 +33,49 @@ void test_garray () {
        assert (foo.ref_count == 1);
 }
 
+void test_garray_foreach () {
+       var array = new GLib.Array<Foo> ();
+
+       var foo1 = new Foo ();
+       var foo2 = new Foo ();
+       var foo3 = new Foo ();
+
+       array.append_val (foo1);
+       assert (foo1.ref_count == 2);
+       array.append_val (foo2);
+       assert (foo2.ref_count == 2);
+       array.append_val (foo3);
+       assert (foo3.ref_count == 2);
+       assert (array.length == 3);
+
+       int loop_size = 0;
+       foreach (weak Foo element in array) {
+               loop_size++;
+               assert (element.ref_count == 2);
+               switch (loop_size) {
+                       case 1: assert (element == foo1); break;
+                       case 2: assert (element == foo2); break;
+                       case 3: assert (element == foo3); break;
+               }
+       }
+       assert (loop_size == 3);
+
+       loop_size = 0;
+       foreach (Foo element in array) {
+               loop_size++;
+               assert (element.ref_count == 3);
+               switch (loop_size) {
+                       case 1: assert (element == foo1); break;
+                       case 2: assert (element == foo2); break;
+                       case 3: assert (element == foo3); break;
+               }
+       }
+       assert (loop_size == 3);
+       assert (foo1.ref_count == 2);
+       assert (foo2.ref_count == 2);
+       assert (foo3.ref_count == 2);
+}
+
 void test_int_garray () {
        var array = new GLib.Array<int> ();
        // g_array_append_val() is a macro which uses a reference to the value parameter and thus can't use constants.
@@ -115,6 +158,7 @@ void test_gvalue_garray () {
 
 void main () {
        test_garray ();
+       test_garray_foreach ();
        test_int_garray ();
        test_struct_garray ();
        test_object_garray ();
index 3a704b938f1e0eb61ee244d333df92af71a5cc3c..c7d2e6da111395389bf34843ea1491773281ed0a 100644 (file)
@@ -19,6 +19,7 @@
 #define _g_value_array_free0(var) ((var == NULL) ? NULL : (var = (g_value_array_free (var), NULL)))
 #define __vala_GValue_free0(var) ((var == NULL) ? NULL : (var = (_vala_GValue_free (var), NULL)))
 #define _g_ptr_array_unref0(var) ((var == NULL) ? NULL : (var = (g_ptr_array_unref (var), NULL)))
+#define _g_array_unref0(var) ((var == NULL) ? NULL : (var = (g_array_unref (var), NULL)))
 #define _g_free0(var) (var = (g_free (var), NULL))
 #define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
 #define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; }
@@ -34,6 +35,10 @@ static void _vala_GValue_free (GValue* self);
 VALA_EXTERN void test_generic_array_unowned (GPtrArray* array);
 VALA_EXTERN void test_foreach_genericarray (void);
 static void __vala_GValue_free0_ (gpointer var);
+VALA_EXTERN void test_garray_owned (GArray* array);
+VALA_EXTERN void test_garray_unowned (GArray* array);
+VALA_EXTERN void test_foreach_garray (void);
+static void _vala_GValue_free_function_content_of (gpointer data);
 VALA_EXTERN void test_foreach_multidim_array (void);
 VALA_EXTERN void test_foreach_const_array (void);
 VALA_EXTERN void test_foreach_slice_array (void);
@@ -259,6 +264,114 @@ test_foreach_genericarray (void)
        G_IS_VALUE (&value) ? (g_value_unset (&value), NULL) : NULL;
 }
 
+void
+test_garray_owned (GArray* array)
+{
+       guint i = 0U;
+       g_return_if_fail (array != NULL);
+       i = (guint) 0;
+       {
+               GArray* item_collection = NULL;
+               guint item_index = 0U;
+               item_collection = array;
+               for (item_index = 0; item_index < item_collection->len; item_index = item_index + 1) {
+                       GValue* _tmp0_;
+                       GValue* item = NULL;
+                       _tmp0_ = __g_value_dup0 (g_array_index (item_collection, GValue*, item_index));
+                       item = _tmp0_;
+                       {
+                               guint _tmp1_;
+                               _tmp1_ = i;
+                               i = _tmp1_ + 1;
+                               __vala_GValue_free0 (item);
+                       }
+               }
+       }
+       _vala_assert (i == ((guint) 3), "i == 3");
+}
+
+void
+test_garray_unowned (GArray* array)
+{
+       guint i = 0U;
+       g_return_if_fail (array != NULL);
+       i = (guint) 0;
+       {
+               GArray* item_collection = NULL;
+               guint item_index = 0U;
+               item_collection = array;
+               for (item_index = 0; item_index < item_collection->len; item_index = item_index + 1) {
+                       GValue* item = NULL;
+                       item = g_array_index (item_collection, GValue*, item_index);
+                       {
+                               guint _tmp0_;
+                               _tmp0_ = i;
+                               i = _tmp0_ + 1;
+                       }
+               }
+       }
+       _vala_assert (i == ((guint) 3), "i == 3");
+}
+
+static void
+_vala_GValue_free_function_content_of (gpointer data)
+{
+       GValue* self;
+       self = *((GValue**) data);
+       __vala_GValue_free0_ (self);
+}
+
+void
+test_foreach_garray (void)
+{
+       GValue value = {0};
+       GArray* array = NULL;
+       GArray* _tmp0_;
+       GValue _tmp1_ = {0};
+       GValue _tmp2_;
+       GValue _tmp3_;
+       GValue* _tmp4_;
+       GValue _tmp5_ = {0};
+       GValue _tmp6_;
+       GValue _tmp7_;
+       GValue* _tmp8_;
+       GValue _tmp9_ = {0};
+       GValue _tmp10_;
+       GValue _tmp11_;
+       GValue* _tmp12_;
+       _tmp0_ = g_array_new (TRUE, TRUE, sizeof (GValue*));
+       g_array_set_clear_func (_tmp0_, (GDestroyNotify) _vala_GValue_free_function_content_of);
+       array = _tmp0_;
+       g_value_init (&_tmp1_, G_TYPE_INT);
+       g_value_set_int (&_tmp1_, 1);
+       G_IS_VALUE (&value) ? (g_value_unset (&value), NULL) : NULL;
+       value = _tmp1_;
+       _tmp2_ = value;
+       _tmp3_ = _tmp2_;
+       _tmp4_ = __g_value_dup0 (&_tmp3_);
+       g_array_append_val (array, _tmp4_);
+       g_value_init (&_tmp5_, G_TYPE_DOUBLE);
+       g_value_set_double (&_tmp5_, 2.0);
+       G_IS_VALUE (&value) ? (g_value_unset (&value), NULL) : NULL;
+       value = _tmp5_;
+       _tmp6_ = value;
+       _tmp7_ = _tmp6_;
+       _tmp8_ = __g_value_dup0 (&_tmp7_);
+       g_array_append_val (array, _tmp8_);
+       g_value_init (&_tmp9_, G_TYPE_STRING);
+       g_value_set_string (&_tmp9_, "three");
+       G_IS_VALUE (&value) ? (g_value_unset (&value), NULL) : NULL;
+       value = _tmp9_;
+       _tmp10_ = value;
+       _tmp11_ = _tmp10_;
+       _tmp12_ = __g_value_dup0 (&_tmp11_);
+       g_array_append_val (array, _tmp12_);
+       test_garray_owned (array);
+       test_garray_unowned (array);
+       _g_array_unref0 (array);
+       G_IS_VALUE (&value) ? (g_value_unset (&value), NULL) : NULL;
+}
+
 void
 test_foreach_multidim_array (void)
 {
@@ -454,6 +567,7 @@ static void
 _vala_main (void)
 {
        test_foreach_gvaluearray ();
+       test_foreach_garray ();
        test_foreach_genericarray ();
        test_foreach_const_array ();
        test_foreach_multidim_array ();
index 052e03d3c9aa6734689b9b0535089b0785787ef8..a0a01a865a5de4966014fbf8294d3ff4e120ff39 100644 (file)
@@ -68,6 +68,41 @@ void test_foreach_genericarray () {
        test_generic_array_unowned (array);
 }
 
+void test_garray_owned (Array<Value?> array) {
+       uint i = 0;
+
+       foreach (Value? item in array) {
+               i++;
+       }
+
+       assert (i == 3);
+}
+
+void test_garray_unowned (Array<Value?> array) {
+       uint i = 0;
+
+       foreach (unowned Value? item in array) {
+               i++;
+       }
+
+       assert (i == 3);
+}
+
+void test_foreach_garray () {
+       Value value;
+       var array = new Array<Value?> ();
+
+       value = 1;
+       array.append_val (value);
+       value = 2.0;
+       array.append_val (value);
+       value = "three";
+       array.append_val (value);
+
+       test_garray_owned (array);
+       test_garray_unowned (array);
+}
+
 void test_foreach_multidim_array () {
        int[,] foo = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
        string result = "";
@@ -105,6 +140,7 @@ void test_foreach_slice_array () {
 
 void main () {
        test_foreach_gvaluearray ();
+       test_foreach_garray ();
        test_foreach_genericarray ();
        test_foreach_const_array ();
        test_foreach_multidim_array ();
diff --git a/tests/control-flow/garray-foreach-variable.test b/tests/control-flow/garray-foreach-variable.test
new file mode 100644 (file)
index 0000000..b79f343
--- /dev/null
@@ -0,0 +1,7 @@
+Invalid Code
+
+void main () {
+       var array = new Array<string> ();
+       foreach (int element in array) {
+       }
+}
index 3d8ce2ba975f43e840a62ba0b6d2746ad2c8d9ea..4daddcb845bd006c37b89269913a79416e81208f 100644 (file)
@@ -181,7 +181,9 @@ public class Vala.ForeachStatement : Block {
 
                        return check_without_iterator (context, collection_type, array_type.element_type);
                } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type)
-                   || collection_type.compatible (context.analyzer.gslist_type) || collection_type.compatible (context.analyzer.genericarray_type))) {
+                   || collection_type.compatible (context.analyzer.gslist_type)
+                   || collection_type.compatible (context.analyzer.genericarray_type)
+                   || collection_type.compatible (context.analyzer.garray_type))) {
                        if (collection_type.get_type_arguments ().size != 1) {
                                error = true;
                                Report.error (collection.source_reference, "missing type argument for collection");