From: Princeton Ferro Date: Mon, 24 Jan 2022 16:50:25 +0000 (-0500) Subject: vala: Add foreach statement support for GLib.Array X-Git-Tag: 0.55.2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0db2ebd9035c95efa062ecab310bca88a70d8faf;p=thirdparty%2Fvala.git vala: Add foreach statement support for GLib.Array It is now possible to use foreach with a GLib.Array --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index 2e34003f1..ccb2fe988 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -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"); diff --git a/codegen/valaccodecontrolflowmodule.vala b/codegen/valaccodecontrolflowmodule.vala index bbc530247..dc278e475 100644 --- a/codegen/valaccodecontrolflowmodule.vala +++ b/codegen/valaccodecontrolflowmodule.vala @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index 77eb144a1..b56263e8f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 \ diff --git a/tests/basic-types/garray.c-expected b/tests/basic-types/garray.c-expected index e81314d57..07713ca1a 100644 --- a/tests/basic-types/garray.c-expected +++ b/tests/basic-types/garray.c-expected @@ -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 (); diff --git a/tests/basic-types/garray.vala b/tests/basic-types/garray.vala index 8732fa42f..4f66c56af 100644 --- a/tests/basic-types/garray.vala +++ b/tests/basic-types/garray.vala @@ -33,6 +33,49 @@ void test_garray () { assert (foo.ref_count == 1); } +void test_garray_foreach () { + var array = new GLib.Array (); + + 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 (); // 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 (); diff --git a/tests/control-flow/foreach.c-expected b/tests/control-flow/foreach.c-expected index 3a704b938..c7d2e6da1 100644 --- a/tests/control-flow/foreach.c-expected +++ b/tests/control-flow/foreach.c-expected @@ -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 (); diff --git a/tests/control-flow/foreach.vala b/tests/control-flow/foreach.vala index 052e03d3c..a0a01a865 100644 --- a/tests/control-flow/foreach.vala +++ b/tests/control-flow/foreach.vala @@ -68,6 +68,41 @@ void test_foreach_genericarray () { test_generic_array_unowned (array); } +void test_garray_owned (Array array) { + uint i = 0; + + foreach (Value? item in array) { + i++; + } + + assert (i == 3); +} + +void test_garray_unowned (Array 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 = 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 index 000000000..b79f34397 --- /dev/null +++ b/tests/control-flow/garray-foreach-variable.test @@ -0,0 +1,7 @@ +Invalid Code + +void main () { + var array = new Array (); + foreach (int element in array) { + } +} diff --git a/vala/valaforeachstatement.vala b/vala/valaforeachstatement.vala index 3d8ce2ba9..4daddcb84 100644 --- a/vala/valaforeachstatement.vala +++ b/vala/valaforeachstatement.vala @@ -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");