From: Rico Tzschichholz Date: Wed, 15 Jan 2020 14:52:49 +0000 (+0100) Subject: Add further support for params arrays X-Git-Tag: 0.47.3~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fmerge-requests%2F84%2Fhead;p=thirdparty%2Fvala.git Add further support for params arrays This brings support for params-arrays in normal vala source code. The parameter is a null-terminated array which is represented as variadic parameter in generated C. This feature is considered experimental for now. Fixes https://gitlab.gnome.org/GNOME/vala/issues/128 --- diff --git a/codegen/valaccodearraymodule.vala b/codegen/valaccodearraymodule.vala index 607deda3c..0f2e60147 100644 --- a/codegen/valaccodearraymodule.vala +++ b/codegen/valaccodearraymodule.vala @@ -780,7 +780,7 @@ public class Vala.CCodeArrayModule : CCodeMethodCallModule { } public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map cparam_map, Map? carg_map) { - if (!(param.variable_type is ArrayType)) { + if (param.params_array || !(param.variable_type is ArrayType)) { return base.generate_parameter (param, decl_space, cparam_map, carg_map); } @@ -822,4 +822,67 @@ public class Vala.CCodeArrayModule : CCodeMethodCallModule { return main_cparam; } + + public override void append_params_array (LocalVariable local) { + var array_type = (ArrayType) local.variable_type; + + var local_length = new LocalVariable (array_type.length_type.copy (), get_array_length_cname (local.name, 1), null, local.source_reference); + var local_size = new LocalVariable (array_type.length_type.copy (), get_array_size_cname (get_local_cname (local))); + + CCodeFunctionCall gnew; + if (context.profile == Profile.POSIX) { + cfile.add_include ("stdlib.h"); + gnew = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + } else { + gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0")); + gnew.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type))); + } + + CCodeExpression length_expr = get_local_cexpression (local_length); + // add extra item to have array NULL-terminated for all reference types + if (array_type.element_type.type_symbol != null && array_type.element_type.type_symbol.is_reference_type ()) { + length_expr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, length_expr, new CCodeConstant ("1")); + } + gnew.add_argument (length_expr); + if (context.profile == Profile.POSIX) { + var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type))); + gnew.add_argument (csizeof); + } + ccode.add_assignment (get_local_cexpression (local), gnew); + + var element = new LocalVariable (array_type.element_type.copy (), "_%s_element".printf (get_ccode_name (local)), null, local.source_reference); + emit_temp_var (element); + + if (context.profile == Profile.POSIX) { + cfile.add_include ("stdarg.h"); + } + ccode.add_declaration ("va_list", new CCodeVariableDeclarator ("_va_list_%s".printf (get_ccode_name (local)))); + var va_start = new CCodeFunctionCall (new CCodeIdentifier ("va_start")); + va_start.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local)))); + va_start.add_argument (new CCodeIdentifier ("_first_%s".printf (get_ccode_name (local)))); + ccode.add_expression (va_start); + + ccode.add_assignment (get_local_cexpression (element), new CCodeIdentifier ("_first_%s".printf (get_ccode_name (local)))); + ccode.open_while (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_local_cexpression (element), new CCodeConstant ("NULL"))); + + var va_arg = new CCodeFunctionCall (new CCodeIdentifier ("va_arg")); + va_arg.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local)))); + va_arg.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type))); + + var ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_array_add_wrapper (array_type))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local_length))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local_size))); + ccall.add_argument (get_local_cexpression (element)); + + ccode.add_expression (ccall); + ccode.add_assignment (get_local_cexpression (element), va_arg); + + ccode.close (); + + var va_end = new CCodeFunctionCall (new CCodeIdentifier ("va_end")); + va_end.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local)))); + ccode.add_expression (va_end); + } } diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index 9e836b1d7..668d3cee2 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -727,6 +727,9 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { public virtual void append_vala_array_length () { } + public virtual void append_params_array (LocalVariable local) { + } + public void append_vala_clear_mutex (string typename, string funcprefix) { // memset cfile.add_include ("string.h"); @@ -2342,7 +2345,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { if (!unreachable_exit_block && b.parent_symbol is Method) { var m = (Method) b.parent_symbol; foreach (Parameter param in m.get_parameters ()) { - if (!param.captured && !param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) { + if (!param.captured && !param.ellipsis && !param.params_array && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) { ccode.add_expression (destroy_parameter (param)); } else if (param.direction == ParameterDirection.OUT && !m.coroutine) { return_out_parameter (param); @@ -3839,7 +3842,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { private void append_param_free (Method m) { foreach (Parameter param in m.get_parameters ()) { - if (!param.captured && !param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) { + if (!param.captured && !param.ellipsis && !param.params_array && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) { ccode.add_expression (destroy_parameter (param)); } } @@ -4728,7 +4731,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { Parameter last_param = null; // FIXME: this doesn't take into account exception handling parameters foreach (var param in current_method.get_parameters ()) { - if (param.ellipsis) { + if (param.ellipsis || param.params_array) { break; } last_param = param; @@ -4791,7 +4794,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { Parameter param = null; if (params_it.next ()) { param = params_it.get (); - ellipsis = param.ellipsis; + ellipsis = param.ellipsis || param.params_array; if (!ellipsis) { if (param.direction == ParameterDirection.OUT) { carg_map = out_arg_map; diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index fe73655c2..191a4fc7f 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -541,7 +541,10 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { } foreach (Parameter param in m.get_parameters ()) { - if (param.ellipsis) { + if (param.ellipsis || param.params_array) { + if (param.params_array) { + append_params_array (m.params_array_var); + } break; } @@ -847,7 +850,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { public virtual CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map cparam_map, Map? carg_map) { CCodeParameter cparam; - if (!param.ellipsis) { + if (!param.ellipsis && !param.params_array) { string ctypename = get_ccode_name (param.variable_type); generate_type_declaration (param.variable_type, decl_space); @@ -874,15 +877,42 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { if (param.format_arg) { cparam.modifiers = CCodeModifiers.FORMAT_ARG; } - } else if (ellipses_to_valist) { - cparam = new CCodeParameter ("_vala_va_list", "va_list"); } else { - cparam = new CCodeParameter.with_ellipsis (); + // Add _first_* parameter for the params array parameter + if (param.params_array) { + var param_type = ((ArrayType) param.variable_type).element_type; + string ctypename = get_ccode_name (param_type); + + generate_type_declaration (param_type, decl_space); + + // pass non-simple structs always by reference + if (param_type.type_symbol is Struct) { + var st = (Struct) param_type.type_symbol; + if (!st.is_simple_type () && param.direction == ParameterDirection.IN) { + if (st.is_immutable && !param.variable_type.value_owned) { + ctypename = "const " + ctypename; + } + + if (!param_type.nullable) { + ctypename += "*"; + } + } + } + + cparam = new CCodeParameter ("_first_%s".printf (get_ccode_name (param)), ctypename); + cparam_map.set (get_param_pos (get_ccode_pos (param), false), cparam); + } + + if (ellipses_to_valist) { + cparam = new CCodeParameter ("_vala_va_list", "va_list"); + } else { + cparam = new CCodeParameter.with_ellipsis (); + } } - cparam_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis), cparam); - if (carg_map != null && !param.ellipsis) { - carg_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis), get_parameter_cexpression (param)); + cparam_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis || param.params_array), cparam); + if (carg_map != null && !param.ellipsis && !param.params_array) { + carg_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis || param.params_array), get_parameter_cexpression (param)); } return cparam; diff --git a/codegen/valagirwriter.vala b/codegen/valagirwriter.vala index 65fafeada..0198cc908 100644 --- a/codegen/valagirwriter.vala +++ b/codegen/valagirwriter.vala @@ -1127,7 +1127,7 @@ public class Vala.GIRWriter : CodeVisitor { } foreach (Parameter param in params) { - write_param_or_return (param.variable_type, true, ref index, get_ccode_array_length (param), param.name, get_parameter_comment (param), param.direction, false, false, param.ellipsis); + write_param_or_return (param.variable_type, true, ref index, get_ccode_array_length (param), param.name, get_parameter_comment (param), param.direction, false, false, param.ellipsis || param.params_array); write_implicit_params (param.variable_type, ref index, get_ccode_array_length (param), param.name, param.direction); } @@ -1226,7 +1226,7 @@ public class Vala.GIRWriter : CodeVisitor { return false; } foreach (var param in m.get_parameters ()) { - if (param.ellipsis || !is_type_introspectable (param.variable_type)) { + if (param.ellipsis || param.params_array || !is_type_introspectable (param.variable_type)) { return false; } } diff --git a/tests/Makefile.am b/tests/Makefile.am index f7c520de8..c3b44aa7f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -153,6 +153,7 @@ TESTS = \ methods/bug791283.vala \ methods/argument-array-initilizer.vala \ methods/generics.vala \ + methods/params-array.vala \ methods/print-attribute.vala \ methods/print-attribute-invalid.test \ methods/printf-invalid.test \ @@ -512,6 +513,7 @@ TESTS = \ asynchronous/finish-name.vala \ asynchronous/generator.vala \ asynchronous/out-parameter-invalid.test \ + asynchronous/params-array-invalid.test \ asynchronous/result-pos.vala \ asynchronous/variadic-invalid.test \ asynchronous/variadic-invalid-2.test \ @@ -809,6 +811,8 @@ TESTS = \ semantic/parameter-params.test \ semantic/parameter-ref-default.test \ semantic/parameter-void.test \ + semantic/params-array-multiple.test \ + semantic/params-array-struct-length.test \ semantic/pointer-indirection-type-not-supported.test \ semantic/pointer-indirection-void-not-supported.test \ semantic/property-abstract.test \ diff --git a/tests/asynchronous/params-array-invalid.test b/tests/asynchronous/params-array-invalid.test new file mode 100644 index 000000000..17426647c --- /dev/null +++ b/tests/asynchronous/params-array-invalid.test @@ -0,0 +1,7 @@ +Invalid Code + +async void foo (params string[] args) { +} + +void main () { +} diff --git a/tests/methods/params-array.vala b/tests/methods/params-array.vala new file mode 100644 index 000000000..3c76d7048 --- /dev/null +++ b/tests/methods/params-array.vala @@ -0,0 +1,42 @@ +void foo (params string[] strv) { + assert (strv.length == 3); + assert (strv[0] == "foo"); + assert (strv[1] == "bar"); + assert (strv[2] == "manam"); +} + +void bar (params int[] intv) { + assert (intv.length == 3); + assert (intv[0] == 23); + assert (intv[1] == 42); + assert (intv[2] == 4711); +} + +void manam (params Value?[] valuev) { + assert (valuev.length == 3); + assert (valuev[0] == "foo"); + assert (valuev[1] == 4711); + assert (valuev[2] == 3.1415); +} + +void manam_owned (params owned Value?[] valuev) { + assert (valuev.length == 3); + assert (valuev[0] == "foo"); + assert (valuev[1] == 4711); + assert (valuev[2] == 3.1415); +} + +void minim (params Variant[] variantv) { + assert (variantv.length == 3); + assert ((string) variantv[0] == "foo"); + assert ((int) variantv[1] == 4711); + assert ((double) variantv[2] == 3.1415); +} + +void main () { + foo ("foo", "bar", "manam"); + bar (23, 42, 4711); + manam ("foo", 4711, 3.1415); + manam_owned ("foo", 4711, 3.1415); + minim ("foo", 4711, 3.1415); +} diff --git a/tests/semantic/params-array-multiple.test b/tests/semantic/params-array-multiple.test new file mode 100644 index 000000000..355515e6e --- /dev/null +++ b/tests/semantic/params-array-multiple.test @@ -0,0 +1,7 @@ +Invalid Code + +void foo (params string[] strv, params int[] intv) { +} + +void main () { +} diff --git a/tests/semantic/params-array-struct-length.test b/tests/semantic/params-array-struct-length.test new file mode 100644 index 000000000..e4f91381c --- /dev/null +++ b/tests/semantic/params-array-struct-length.test @@ -0,0 +1,12 @@ +Invalid Code + +struct Foo { + public int i; + public string s; +} + +void foo (params Foo foov[3]) { +} + +void main () { +} diff --git a/vala/valamethod.vala b/vala/valamethod.vala index b1b4cc77c..f6ce67d31 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -175,6 +175,8 @@ public class Vala.Method : Subroutine, Callable { } } + public LocalVariable? params_array_var { get; private set; } + public weak Signal signal_reference { get; set; } public bool closure { get; set; } @@ -826,7 +828,7 @@ public class Vala.Method : Subroutine, Callable { error = true; Report.error (param.source_reference, "Reference parameters are not supported for async methods"); } - if (!external_package && coroutine && (param.ellipsis || param.variable_type.type_symbol == context.analyzer.va_list_type.type_symbol)) { + if (!external_package && coroutine && (param.ellipsis || param.params_array || param.variable_type.type_symbol == context.analyzer.va_list_type.type_symbol)) { error = true; Report.error (param.source_reference, "Variadic parameters are not supported for async methods"); return false; @@ -840,6 +842,28 @@ public class Vala.Method : Subroutine, Callable { } else if (param.initializer != null) { optional_param = true; } + + // Add local variable to provide access to params arrays which will be constructed out of the given va-args + if (param.params_array && body != null) { + if (params_array_var != null) { + Report.error (param.source_reference, "Only one params-array parameter is allowed"); + continue; + } + if (!context.experimental) { + Report.warning (param.source_reference, "Support of params-arrays is experimental"); + } + var type = (ArrayType) param.variable_type.copy (); + type.element_type.value_owned = type.value_owned; + type.value_owned = true; + if (type.element_type.is_real_struct_type () && !type.element_type.nullable) { + Report.error (param.source_reference, "Only nullable struct elements are supported in params-array"); + } + if (type.length != null) { + Report.error (param.source_reference, "Passing length to params-array is not supported yet"); + } + params_array_var = new LocalVariable (type, param.name, null, param.source_reference); + body.insert_statement (0, new DeclarationStatement (params_array_var, param.source_reference)); + } } if (coroutine) { @@ -1228,6 +1252,9 @@ public class Vala.Method : Subroutine, Callable { if (result_var != null) { collection.add (result_var); } + if (params_array_var != null) { + collection.add (params_array_var); + } // capturing variables is only supported if they are initialized // therefore assume that captured variables are initialized diff --git a/vala/valamethodcall.vala b/vala/valamethodcall.vala index 1c6e2b404..b9772fed8 100644 --- a/vala/valamethodcall.vala +++ b/vala/valamethodcall.vala @@ -450,7 +450,7 @@ public class Vala.MethodCall : Expression { // recreate iterator and skip to right position arg_it = argument_list.iterator (); foreach (Parameter param in params) { - if (param.ellipsis) { + if (param.ellipsis || param.params_array) { break; } arg_it.next (); diff --git a/vala/valascope.vala b/vala/valascope.vala index 1a59d24ea..f0f02561b 100644 --- a/vala/valascope.vala +++ b/vala/valascope.vala @@ -56,6 +56,10 @@ public class Vala.Scope { * @param sym a symbol */ public void add (string? name, Symbol sym) { + // Ignore params-array parameters which can not be conflicted with + if (sym is Parameter && ((Parameter) sym).params_array) { + name = null; + } if (name != null) { if (symbol_table == null) { symbol_table = new HashMap (str_hash, str_equal);