dfsm: Add all the missing API reference comments
[bendy-bus:bendy-bus.git] / dfsm / dfsm-ast-statement-emit.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * D-Bus Simulator
4  * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
5  * 
6  * D-Bus Simulator is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * D-Bus Simulator is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with D-Bus Simulator.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * SECTION:dfsm-ast-statement-emit
22  * @short_description: AST emit statement node
23  * @stability: Unstable
24  * @include: dfsm/dfsm-ast-statement-emit.h
25  *
26  * AST emit statement implementing support for D-Bus signal emission.
27  */
28
29 #include "config.h"
30
31 #include <glib.h>
32 #include <glib/gi18n-lib.h>
33
34 #include "dfsm-ast-statement-emit.h"
35 #include "dfsm-internal.h"
36 #include "dfsm-parser.h"
37 #include "dfsm-parser-internal.h"
38
39 static void dfsm_ast_statement_emit_dispose (GObject *object);
40 static void dfsm_ast_statement_emit_finalize (GObject *object);
41 static void dfsm_ast_statement_emit_sanity_check (DfsmAstNode *node);
42 static void dfsm_ast_statement_emit_pre_check_and_register (DfsmAstNode *node, DfsmEnvironment *environment, GError **error);
43 static void dfsm_ast_statement_emit_check (DfsmAstNode *node, DfsmEnvironment *environment, GError **error);
44 static void dfsm_ast_statement_emit_execute (DfsmAstStatement *statement, DfsmEnvironment *environment, DfsmOutputSequence *output_sequence);
45
46 struct _DfsmAstStatementEmitPrivate {
47         gchar *signal_name;
48         gchar *interface_name; /* initially NULL; set in check() */
49         DfsmAstExpression *expression;
50 };
51
52 G_DEFINE_TYPE (DfsmAstStatementEmit, dfsm_ast_statement_emit, DFSM_TYPE_AST_STATEMENT)
53
54 static void
55 dfsm_ast_statement_emit_class_init (DfsmAstStatementEmitClass *klass)
56 {
57         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
58         DfsmAstNodeClass *node_class = DFSM_AST_NODE_CLASS (klass);
59         DfsmAstStatementClass *statement_class = DFSM_AST_STATEMENT_CLASS (klass);
60
61         g_type_class_add_private (klass, sizeof (DfsmAstStatementEmitPrivate));
62
63         gobject_class->dispose = dfsm_ast_statement_emit_dispose;
64         gobject_class->finalize = dfsm_ast_statement_emit_finalize;
65
66         node_class->sanity_check = dfsm_ast_statement_emit_sanity_check;
67         node_class->pre_check_and_register = dfsm_ast_statement_emit_pre_check_and_register;
68         node_class->check = dfsm_ast_statement_emit_check;
69
70         statement_class->execute = dfsm_ast_statement_emit_execute;
71 }
72
73 static void
74 dfsm_ast_statement_emit_init (DfsmAstStatementEmit *self)
75 {
76         self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, DFSM_TYPE_AST_STATEMENT_EMIT, DfsmAstStatementEmitPrivate);
77 }
78
79 static void
80 dfsm_ast_statement_emit_dispose (GObject *object)
81 {
82         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (object)->priv;
83
84         g_clear_object (&priv->expression);
85
86         /* Chain up to the parent class */
87         G_OBJECT_CLASS (dfsm_ast_statement_emit_parent_class)->dispose (object);
88 }
89
90 static void
91 dfsm_ast_statement_emit_finalize (GObject *object)
92 {
93         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (object)->priv;
94
95         g_free (priv->signal_name);
96         g_free (priv->interface_name);
97
98         /* Chain up to the parent class */
99         G_OBJECT_CLASS (dfsm_ast_statement_emit_parent_class)->finalize (object);
100 }
101
102 static void
103 dfsm_ast_statement_emit_sanity_check (DfsmAstNode *node)
104 {
105         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (node)->priv;
106
107         g_assert (priv->signal_name != NULL);
108
109         g_assert (priv->expression != NULL);
110         dfsm_ast_node_sanity_check (DFSM_AST_NODE (priv->expression));
111 }
112
113 static void
114 dfsm_ast_statement_emit_pre_check_and_register (DfsmAstNode *node, DfsmEnvironment *environment, GError **error)
115 {
116         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (node)->priv;
117
118         if (g_dbus_is_member_name (priv->signal_name) == FALSE) {
119                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID, _("Invalid D-Bus signal name: %s"), priv->signal_name);
120                 return;
121         }
122
123         dfsm_ast_node_pre_check_and_register (DFSM_AST_NODE (priv->expression), environment, error);
124
125         if (*error != NULL) {
126                 return;
127         }
128 }
129
130 static void
131 dfsm_ast_statement_emit_check (DfsmAstNode *node, DfsmEnvironment *environment, GError **error)
132 {
133         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (node)->priv;
134         GDBusSignalInfo *signal_info = NULL;
135         GVariantType *expr_parameters_type, *signal_parameters_type;
136         GPtrArray/*<GDBusInterfaceInfo>*/ *interfaces;
137         guint i;
138
139         dfsm_ast_node_check (DFSM_AST_NODE (priv->expression), environment, error);
140
141         if (*error != NULL) {
142                 return;
143         }
144
145         /* Find the interface declaring the signal out of the interfaces declared as implemented by the object */
146         interfaces = dfsm_environment_get_interfaces (environment);
147
148         for (i = 0; i < interfaces->len; i++) {
149                 GDBusInterfaceInfo *interface_info = (GDBusInterfaceInfo*) g_ptr_array_index (interfaces, i);
150
151                 signal_info = g_dbus_interface_info_lookup_signal (interface_info, priv->signal_name);
152
153                 if (signal_info != NULL) {
154                         /* Found the interface defining signal_name. */
155                         priv->interface_name = g_strdup (interface_info->name);
156                         break;
157                 }
158         }
159
160         /* Failed to find a suitable interface? */
161         if (signal_info == NULL) {
162                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
163                              _("Undeclared D-Bus signal referenced by an ‘emit’ statement: %s"), priv->signal_name);
164                 return;
165         }
166
167         g_assert (priv->interface_name != NULL);
168
169         /* Check the expression's type is a tuple. */
170         expr_parameters_type = dfsm_ast_expression_calculate_type (priv->expression, environment);
171         signal_parameters_type = dfsm_internal_dbus_arg_info_array_to_variant_type ((const GDBusArgInfo**) signal_info->args);
172
173         if (g_variant_type_is_subtype_of (expr_parameters_type, signal_parameters_type) == FALSE) {
174                 gchar *expr_parameters_type_string, *signal_parameters_type_string;
175
176                 expr_parameters_type_string = g_variant_type_dup_string (expr_parameters_type);
177                 signal_parameters_type_string = g_variant_type_dup_string (signal_parameters_type);
178
179                 g_variant_type_free (signal_parameters_type);
180                 g_variant_type_free (expr_parameters_type);
181
182                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
183                              _("Type mismatch between formal and actual parameters to D-Bus signal ‘%s’: expects type ‘%s’ but received type ‘%s’."),
184                              priv->signal_name, signal_parameters_type_string, expr_parameters_type_string);
185
186                 g_free (signal_parameters_type_string);
187                 g_free (expr_parameters_type_string);
188
189                 return;
190         }
191
192         g_variant_type_free (signal_parameters_type);
193         g_variant_type_free (expr_parameters_type);
194 }
195
196 static void
197 dfsm_ast_statement_emit_execute (DfsmAstStatement *statement, DfsmEnvironment *environment, DfsmOutputSequence *output_sequence)
198 {
199         DfsmAstStatementEmitPrivate *priv = DFSM_AST_STATEMENT_EMIT (statement)->priv;
200         GVariant *expression_value;
201
202         /* Evaluate the child expression to get the signal parameters, then emit the signal. */
203         expression_value = dfsm_ast_expression_evaluate (priv->expression, environment);
204         dfsm_output_sequence_add_emit (output_sequence, priv->interface_name, priv->signal_name, expression_value);
205         g_variant_unref (expression_value);
206 }
207
208 /**
209  * dfsm_ast_statement_emit_new:
210  * @signal_name: name of the D-Bus signal to emit
211  * @expression: expression to evaluate as the signal parameters
212  *
213  * Create a new #DfsmAstStatement for emitting a D-Bus signal of name @signal_name with value given by @expression.
214  *
215  * Return value: (transfer full): a new AST node
216  */
217 DfsmAstStatement *
218 dfsm_ast_statement_emit_new (const gchar *signal_name, DfsmAstExpression *expression)
219 {
220         DfsmAstStatementEmit *statement;
221         DfsmAstStatementEmitPrivate *priv;
222
223         g_return_val_if_fail (signal_name != NULL && *signal_name != '\0', NULL);
224         g_return_val_if_fail (DFSM_IS_AST_EXPRESSION (expression), NULL);
225
226         statement = g_object_new (DFSM_TYPE_AST_STATEMENT_EMIT, NULL);
227         priv = statement->priv;
228
229         priv->signal_name = g_strdup (signal_name);
230         priv->expression = g_object_ref (expression);
231
232         return DFSM_AST_STATEMENT (statement);
233 }