summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrinceton Ferro <princetonferro@gmail.com>2022-01-13 02:40:18 -0500
committerRico Tzschichholz <ricotz@ubuntu.com>2022-01-17 17:33:22 +0100
commit45fe8523ad392f1aaa0f24d83b63e91774938775 (patch)
tree08b17992102f62a18712d1d563461ee481568732
parentcd25750b52d03962220663e5c29d2a336a5fceaa (diff)
downloadvala-45fe8523ad392f1aaa0f24d83b63e91774938775.tar.gz
codegen: Add support for async main
If main() is async then setup a new GMainLoop and call _vala_main() asynchronously. Fixes https://gitlab.gnome.org/GNOME/vala/issues/1275
-rw-r--r--codegen/valaccodeattribute.vala6
-rw-r--r--codegen/valaccodemethodmodule.vala76
-rw-r--r--codegen/valagasyncmodule.vala4
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/asynchronous/method-main-async-void.c-expected114
-rw-r--r--tests/asynchronous/method-main-async-void.vala4
-rw-r--r--tests/asynchronous/method-main-async.c-expected138
-rw-r--r--tests/asynchronous/method-main-async.vala11
-rw-r--r--tests/semantic/method-main-async.test5
-rw-r--r--vala/valamethod.vala5
10 files changed, 347 insertions, 19 deletions
diff --git a/codegen/valaccodeattribute.vala b/codegen/valaccodeattribute.vala
index 8cf459107..bd4c6b29d 100644
--- a/codegen/valaccodeattribute.vala
+++ b/codegen/valaccodeattribute.vala
@@ -762,7 +762,11 @@ public class Vala.CCodeAttribute : AttributeCache {
}
if (sym.name == "main" && sym.parent_symbol.name == null) {
// avoid conflict with generated main function
- return "_vala_main";
+ if (m.coroutine) {
+ return "_vala_main_async";
+ } else {
+ return "_vala_main";
+ }
} else if (sym.name.has_prefix ("_")) {
return "_%s%s".printf (get_ccode_lower_case_prefix (sym.parent_symbol), sym.name.substring (1));
} else {
diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala
index ff6d679ac..9b9fd3690 100644
--- a/codegen/valaccodemethodmodule.vala
+++ b/codegen/valaccodemethodmodule.vala
@@ -840,6 +840,42 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
if (m.entry_point) {
// m is possible entry point, add appropriate startup code
+
+ if (m.coroutine) {
+ // add callback for async main
+ var asyncmain_callback = new CCodeFunction (real_name + "_callback");
+ asyncmain_callback.add_parameter (new CCodeParameter ("source_object", "GObject*"));
+ asyncmain_callback.add_parameter (new CCodeParameter ("res", "GAsyncResult*"));
+ asyncmain_callback.add_parameter (new CCodeParameter ("user_data", "gpointer"));
+ asyncmain_callback.modifiers |= CCodeModifiers.STATIC;
+
+ push_function (asyncmain_callback);
+ ccode.add_declaration ("GMainLoop*", new CCodeVariableDeclarator.zero ("loop", new CCodeIdentifier ("user_data")));
+
+ // get the return value
+ var finish_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m)));
+ finish_call.add_argument (new CCodeIdentifier ("res"));
+ if (m.return_type is VoidType) {
+ ccode.add_expression (finish_call);
+ } else {
+ // save the return value
+ var asyncmain_result = new CCodeIdentifier (real_name + "_result");
+ var asyncmain_result_decl = new CCodeDeclaration (get_ccode_name (int_type));
+ asyncmain_result_decl.add_declarator (new CCodeVariableDeclarator (asyncmain_result.name));
+ asyncmain_result_decl.modifiers = CCodeModifiers.STATIC;
+ cfile.add_type_member_declaration (asyncmain_result_decl);
+ ccode.add_assignment (asyncmain_result, finish_call);
+ }
+
+ // quit the main loop
+ var loop_quit_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_quit"));
+ loop_quit_call.add_argument (new CCodeIdentifier ("loop"));
+ ccode.add_expression (loop_quit_call);
+
+ pop_function ();
+ cfile.add_function (asyncmain_callback);
+ }
+
var cmain = new CCodeFunction ("main", "int");
cmain.line = function.line;
cmain.add_parameter (new CCodeParameter ("argc", "int"));
@@ -855,17 +891,47 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
}
}
- var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name));
+ var main_call = new CCodeFunctionCall (new CCodeIdentifier (m.coroutine ? real_name : function.name));
if (m.get_parameters ().size == 1) {
main_call.add_argument (new CCodeIdentifier ("argv"));
main_call.add_argument (new CCodeIdentifier ("argc"));
}
- if (m.return_type is VoidType) {
- // method returns void, always use 0 as exit code
+
+ if (m.coroutine) {
+ // main method is asynchronous, so we have to setup GMainLoop and run it
+ var main_loop_new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_new"));
+ main_loop_new_call.add_argument (new CCodeConstantIdentifier ("NULL"));
+ main_loop_new_call.add_argument (new CCodeConstantIdentifier ("FALSE"));
+ ccode.add_declaration ("GMainLoop*", new CCodeVariableDeclarator.zero ("loop", main_loop_new_call));
+
+ // add some more arguments to main_call
+ main_call.add_argument (new CCodeIdentifier (real_name + "_callback"));
+ main_call.add_argument (new CCodeIdentifier ("loop"));
ccode.add_expression (main_call);
- ccode.add_return (new CCodeConstant ("0"));
+
+ var main_loop_run_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_run"));
+ main_loop_run_call.add_argument (new CCodeIdentifier ("loop"));
+ ccode.add_expression (main_loop_run_call);
+
+ var main_loop_unref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_unref"));
+ main_loop_unref_call.add_argument (new CCodeIdentifier ("loop"));
+ ccode.add_expression (main_loop_unref_call);
+
+ if (m.return_type is VoidType) {
+ // method returns void, always use 0 as exit code
+ ccode.add_return (new CCodeConstant ("0"));
+ } else {
+ // get the saved return value
+ ccode.add_return (new CCodeIdentifier (real_name + "_result"));
+ }
} else {
- ccode.add_return (main_call);
+ if (m.return_type is VoidType) {
+ // method returns void, always use 0 as exit code
+ ccode.add_expression (main_call);
+ ccode.add_return (new CCodeConstant ("0"));
+ } else {
+ ccode.add_return (main_call);
+ }
}
pop_function ();
cfile.add_function (cmain);
diff --git a/codegen/valagasyncmodule.vala b/codegen/valagasyncmodule.vala
index 4f130946e..bbe810496 100644
--- a/codegen/valagasyncmodule.vala
+++ b/codegen/valagasyncmodule.vala
@@ -354,7 +354,7 @@ public class Vala.GAsyncModule : GtkModule {
var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
- if (m.is_private_symbol ()) {
+ if (m.is_private_symbol () || m.entry_point) {
asyncfunc.modifiers |= CCodeModifiers.STATIC;
} else if (context.hide_internal && m.is_internal_symbol ()) {
asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
@@ -374,7 +374,7 @@ public class Vala.GAsyncModule : GtkModule {
cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
- if (m.is_private_symbol ()) {
+ if (m.is_private_symbol () || m.entry_point) {
finishfunc.modifiers |= CCodeModifiers.STATIC;
} else if (context.hide_internal && m.is_internal_symbol ()) {
finishfunc.modifiers |= CCodeModifiers.INTERNAL;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5156172d2..f95b6bfaa 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -731,6 +731,8 @@ TESTS = \
asynchronous/constructor-argument-check.vala \
asynchronous/finish-name.vala \
asynchronous/generator.vala \
+ asynchronous/method-main-async.vala \
+ asynchronous/method-main-async-void.vala \
asynchronous/nowrapper.vala \
asynchronous/out-parameter-free-on-error.vala \
asynchronous/out-parameter-invalid.test \
@@ -1139,7 +1141,6 @@ TESTS = \
semantic/method-extern-virtual.test \
semantic/method-interface-already-found.test \
semantic/method-interface-not-found.test \
- semantic/method-main-async.test \
semantic/method-main-inline.test \
semantic/method-main-throws.test \
semantic/method-override.test \
diff --git a/tests/asynchronous/method-main-async-void.c-expected b/tests/asynchronous/method-main-async-void.c-expected
new file mode 100644
index 000000000..c0733f84e
--- /dev/null
+++ b/tests/asynchronous/method-main-async-void.c-expected
@@ -0,0 +1,114 @@
+/* asynchronous_method_main_async_void.c generated by valac, the Vala compiler
+ * generated from asynchronous_method_main_async_void.vala, do not modify */
+
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+typedef struct _ValaMainAsyncData ValaMainAsyncData;
+
+struct _ValaMainAsyncData {
+ int _state_;
+ GObject* _source_object_;
+ GAsyncResult* _res_;
+ GTask* _async_result;
+ gchar** args;
+ gint args_length1;
+};
+
+static void _vala_main_async_data_free (gpointer _data);
+static void _vala_main_async (gchar** args,
+ gint args_length1,
+ GAsyncReadyCallback _callback_,
+ gpointer _user_data_);
+static void _vala_main_finish (GAsyncResult* _res_);
+static gboolean _vala_main_async_co (ValaMainAsyncData* _data_);
+static gboolean __vala_main_async_co_gsource_func (gpointer self);
+
+static void
+_vala_main_async_data_free (gpointer _data)
+{
+ ValaMainAsyncData* _data_;
+ _data_ = _data;
+ g_slice_free (ValaMainAsyncData, _data_);
+}
+
+void
+_vala_main_async (gchar** args,
+ gint args_length1,
+ GAsyncReadyCallback _callback_,
+ gpointer _user_data_)
+{
+ ValaMainAsyncData* _data_;
+ _data_ = g_slice_new0 (ValaMainAsyncData);
+ _data_->_async_result = g_task_new (NULL, NULL, _callback_, _user_data_);
+ g_task_set_task_data (_data_->_async_result, _data_, _vala_main_async_data_free);
+ _data_->args = args;
+ _data_->args_length1 = args_length1;
+ _vala_main_async_co (_data_);
+}
+
+void
+_vala_main_finish (GAsyncResult* _res_)
+{
+ ValaMainAsyncData* _data_;
+ _data_ = g_task_propagate_pointer (G_TASK (_res_), NULL);
+}
+
+static gboolean
+__vala_main_async_co_gsource_func (gpointer self)
+{
+ gboolean result;
+ result = _vala_main_async_co (self);
+ return result;
+}
+
+static gboolean
+_vala_main_async_co (ValaMainAsyncData* _data_)
+{
+ switch (_data_->_state_) {
+ case 0:
+ goto _state_0;
+ case 1:
+ goto _state_1;
+ default:
+ g_assert_not_reached ();
+ }
+ _state_0:
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, __vala_main_async_co_gsource_func, _data_, NULL);
+ _data_->_state_ = 1;
+ return FALSE;
+ _state_1:
+ ;
+ g_task_return_pointer (_data_->_async_result, _data_, NULL);
+ if (_data_->_state_ != 0) {
+ while (!g_task_get_completed (_data_->_async_result)) {
+ g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE);
+ }
+ }
+ g_object_unref (_data_->_async_result);
+ return FALSE;
+}
+
+static void
+_vala_main_async_callback (GObject* source_object,
+ GAsyncResult* res,
+ gpointer user_data)
+{
+ GMainLoop* loop = user_data;
+ _vala_main_finish (res);
+ g_main_loop_quit (loop);
+}
+
+int
+main (int argc,
+ char ** argv)
+{
+ GMainLoop* loop = g_main_loop_new (NULL, FALSE);
+ _vala_main_async (argv, argc, _vala_main_async_callback, loop);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ return 0;
+}
+
diff --git a/tests/asynchronous/method-main-async-void.vala b/tests/asynchronous/method-main-async-void.vala
new file mode 100644
index 000000000..0ce4e822d
--- /dev/null
+++ b/tests/asynchronous/method-main-async-void.vala
@@ -0,0 +1,4 @@
+async void main (string[] args) {
+ Idle.add (main.callback);
+ yield;
+}
diff --git a/tests/asynchronous/method-main-async.c-expected b/tests/asynchronous/method-main-async.c-expected
new file mode 100644
index 000000000..968f8eef4
--- /dev/null
+++ b/tests/asynchronous/method-main-async.c-expected
@@ -0,0 +1,138 @@
+/* asynchronous_method_main_async.c generated by valac, the Vala compiler
+ * generated from asynchronous_method_main_async.vala, do not modify */
+
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+typedef struct _ValaMainAsyncData ValaMainAsyncData;
+#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; }
+#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
+#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
+
+struct _ValaMainAsyncData {
+ int _state_;
+ GObject* _source_object_;
+ GAsyncResult* _res_;
+ GTask* _async_result;
+ gchar** args;
+ gint args_length1;
+ gint result;
+ const gchar* _tmp0_;
+ gchar* foo;
+ const gchar* _tmp1_;
+ gchar* _tmp2_;
+};
+
+static gint _vala_main_async_result;
+
+static void _vala_main_async_data_free (gpointer _data);
+static void _vala_main_async (gchar** args,
+ gint args_length1,
+ GAsyncReadyCallback _callback_,
+ gpointer _user_data_);
+static gint _vala_main_finish (GAsyncResult* _res_);
+static gboolean _vala_main_async_co (ValaMainAsyncData* _data_);
+static gboolean __vala_main_async_co_gsource_func (gpointer self);
+
+static void
+_vala_main_async_data_free (gpointer _data)
+{
+ ValaMainAsyncData* _data_;
+ _data_ = _data;
+ g_slice_free (ValaMainAsyncData, _data_);
+}
+
+void
+_vala_main_async (gchar** args,
+ gint args_length1,
+ GAsyncReadyCallback _callback_,
+ gpointer _user_data_)
+{
+ ValaMainAsyncData* _data_;
+ _data_ = g_slice_new0 (ValaMainAsyncData);
+ _data_->_async_result = g_task_new (NULL, NULL, _callback_, _user_data_);
+ g_task_set_task_data (_data_->_async_result, _data_, _vala_main_async_data_free);
+ _data_->args = args;
+ _data_->args_length1 = args_length1;
+ _vala_main_async_co (_data_);
+}
+
+gint
+_vala_main_finish (GAsyncResult* _res_)
+{
+ gint result;
+ ValaMainAsyncData* _data_;
+ _data_ = g_task_propagate_pointer (G_TASK (_res_), NULL);
+ result = _data_->result;
+ return result;
+}
+
+static gboolean
+__vala_main_async_co_gsource_func (gpointer self)
+{
+ gboolean result;
+ result = _vala_main_async_co (self);
+ return result;
+}
+
+static gboolean
+_vala_main_async_co (ValaMainAsyncData* _data_)
+{
+ switch (_data_->_state_) {
+ case 0:
+ goto _state_0;
+ case 1:
+ goto _state_1;
+ default:
+ g_assert_not_reached ();
+ }
+ _state_0:
+ _vala_assert (_data_->args_length1 >= 1, "args.length >= 1");
+ _data_->_tmp0_ = _data_->args[0];
+ _vala_assert (_data_->_tmp0_ != NULL, "args[0] != null");
+ _data_->_tmp1_ = _data_->args[0];
+ _data_->_tmp2_ = g_strdup (_data_->_tmp1_);
+ _data_->foo = _data_->_tmp2_;
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, __vala_main_async_co_gsource_func, _data_, NULL);
+ _data_->_state_ = 1;
+ return FALSE;
+ _state_1:
+ ;
+ _vala_assert (g_strcmp0 (_data_->foo, "./asynchronous_method_main_async") == 0, "foo == \"./asynchronous_method_main_async\"");
+ _data_->result = 0;
+ _g_free0 (_data_->foo);
+ g_task_return_pointer (_data_->_async_result, _data_, NULL);
+ if (_data_->_state_ != 0) {
+ while (!g_task_get_completed (_data_->_async_result)) {
+ g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE);
+ }
+ }
+ g_object_unref (_data_->_async_result);
+ return FALSE;
+}
+
+static void
+_vala_main_async_callback (GObject* source_object,
+ GAsyncResult* res,
+ gpointer user_data)
+{
+ GMainLoop* loop = user_data;
+ _vala_main_async_result = _vala_main_finish (res);
+ g_main_loop_quit (loop);
+}
+
+int
+main (int argc,
+ char ** argv)
+{
+ GMainLoop* loop = g_main_loop_new (NULL, FALSE);
+ _vala_main_async (argv, argc, _vala_main_async_callback, loop);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ return _vala_main_async_result;
+}
+
diff --git a/tests/asynchronous/method-main-async.vala b/tests/asynchronous/method-main-async.vala
new file mode 100644
index 000000000..c28aa3506
--- /dev/null
+++ b/tests/asynchronous/method-main-async.vala
@@ -0,0 +1,11 @@
+async int main (string[] args) {
+ assert (args.length >= 1);
+ assert (args[0] != null);
+ var foo = args[0];
+
+ Idle.add (main.callback);
+ yield;
+
+ assert (foo == "./asynchronous_method_main_async");
+ return 0;
+}
diff --git a/tests/semantic/method-main-async.test b/tests/semantic/method-main-async.test
deleted file mode 100644
index df468fa9f..000000000
--- a/tests/semantic/method-main-async.test
+++ /dev/null
@@ -1,5 +0,0 @@
-Packages: gio-2.0
-Invalid Code
-
-async void main () {
-}
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 27e417291..53d5ce72d 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -1114,11 +1114,6 @@ public class Vala.Method : Subroutine, Callable {
error = true;
Report.error (source_reference, "\"main\" method cannot be inline");
}
-
- if (coroutine) {
- error = true;
- Report.error (source_reference, "\"main\" method cannot be async");
- }
}
if (get_attribute ("GtkCallback") != null) {