dfsm: Add all the missing API reference comments
[bendy-bus:bendy-bus.git] / dfsm / dfsm-environment.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-environment
22  * @short_description: simulation environment
23  * @stability: Unstable
24  * @include: dfsm/dfsm-environment.h
25  *
26  * Simulation environment for a single simulated D-Bus object, storing its local- and object-scoped variables, and allowing for built-in functions
27  * to be executed using that variable store.
28  */
29
30 #include "config.h"
31
32 #include <string.h>
33 #include <glib.h>
34 #include <glib/gi18n-lib.h>
35
36 #include "dfsm-environment.h"
37 #include "dfsm-environment-functions.h"
38 #include "dfsm-marshal.h"
39 #include "dfsm-parser.h"
40 #include "dfsm-parser-internal.h"
41
42 typedef struct {
43         GVariantType *type;
44         GVariant *value;
45 } VariableInfo;
46
47 static VariableInfo *
48 variable_info_copy (VariableInfo *data)
49 {
50         VariableInfo *new_data;
51
52         new_data = g_slice_new (VariableInfo);
53
54         new_data->type = g_variant_type_copy (data->type);
55         new_data->value = g_variant_ref (data->value);
56
57         return new_data;
58 }
59
60 static void
61 variable_info_free (VariableInfo *data)
62 {
63         if (data->value != NULL) {
64                 g_variant_unref (data->value);
65         }
66
67         g_variant_type_free (data->type);
68         g_slice_free (VariableInfo, data);
69 }
70
71 static void dfsm_environment_dispose (GObject *object);
72 static void dfsm_environment_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
73 static void dfsm_environment_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
74
75 struct _DfsmEnvironmentPrivate {
76         GHashTable/*<string, VariableInfo>*/ *local_variables, *local_variables_original; /* string for variable name → variable */
77         GHashTable/*<string, VariableInfo>*/ *object_variables, *object_variables_original; /* string for variable name → variable */
78         GPtrArray/*<GDBusInterfaceInfo>*/ *interfaces;
79 };
80
81 enum {
82         PROP_INTERFACES = 1,
83 };
84
85 G_DEFINE_TYPE (DfsmEnvironment, dfsm_environment, G_TYPE_OBJECT)
86
87 static void
88 dfsm_environment_class_init (DfsmEnvironmentClass *klass)
89 {
90         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
91
92         g_type_class_add_private (klass, sizeof (DfsmEnvironmentPrivate));
93
94         gobject_class->get_property = dfsm_environment_get_property;
95         gobject_class->set_property = dfsm_environment_set_property;
96         gobject_class->dispose = dfsm_environment_dispose;
97
98         /**
99          * DfsmEnvironment:interfaces:
100          *
101          * Information about the D-Bus interfaces in use by objects using this environment. An array of #GDBusInterfaceInfo structures.
102          */
103         g_object_class_install_property (gobject_class, PROP_INTERFACES,
104                                          g_param_spec_boxed ("interfaces",
105                                                              "D-Bus Interface Information",
106                                                              "Information about the D-Bus interfaces in use by objects using this environment.",
107                                                              G_TYPE_PTR_ARRAY,
108                                                              G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
109 }
110
111 static void
112 dfsm_environment_init (DfsmEnvironment *self)
113 {
114         self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, DFSM_TYPE_ENVIRONMENT, DfsmEnvironmentPrivate);
115
116         self->priv->local_variables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) variable_info_free);
117         self->priv->local_variables_original = NULL;
118         self->priv->object_variables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) variable_info_free);
119         self->priv->object_variables_original = NULL;
120 }
121
122 static void
123 dfsm_environment_dispose (GObject *object)
124 {
125         DfsmEnvironmentPrivate *priv = DFSM_ENVIRONMENT (object)->priv;
126
127         if (priv->interfaces != NULL) {
128                 g_ptr_array_unref (priv->interfaces);
129                 priv->interfaces = NULL;
130         }
131
132         if (priv->local_variables != NULL) {
133                 g_hash_table_unref (priv->local_variables);
134                 priv->local_variables = NULL;
135         }
136
137         if (priv->local_variables_original != NULL) {
138                 g_hash_table_unref (priv->local_variables_original);
139                 priv->local_variables_original = NULL;
140         }
141
142         if (priv->object_variables != NULL) {
143                 g_hash_table_unref (priv->object_variables);
144                 priv->object_variables = NULL;
145         }
146
147         if (priv->object_variables_original != NULL) {
148                 g_hash_table_unref (priv->object_variables_original);
149                 priv->object_variables_original = NULL;
150         }
151
152         /* Chain up to the parent class */
153         G_OBJECT_CLASS (dfsm_environment_parent_class)->dispose (object);
154 }
155
156 static void
157 dfsm_environment_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
158 {
159         DfsmEnvironmentPrivate *priv = DFSM_ENVIRONMENT (object)->priv;
160
161         switch (property_id) {
162                 case PROP_INTERFACES:
163                         g_value_set_boxed (value, priv->interfaces);
164                         break;
165                 default:
166                         /* We don't have any other property... */
167                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
168                         break;
169         }
170 }
171
172 static void
173 dfsm_environment_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
174 {
175         DfsmEnvironmentPrivate *priv = DFSM_ENVIRONMENT (object)->priv;
176
177         switch (property_id) {
178                 case PROP_INTERFACES:
179                         /* Construct-only */
180                         priv->interfaces = g_ptr_array_ref (g_value_get_boxed (value));
181                         break;
182                 default:
183                         /* We don't have any other property... */
184                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
185                         break;
186         }
187 }
188
189 /*
190  * dfsm_environment_new:
191  * @interfaces: an array of #GDBusInterfaceInfo structures describing the interfaces used by objects using this environment
192  *
193  * Creates a new #DfsmEnvironment initialised with data from the given @interfaces, the default function table, and no local or object variables.
194  *
195  * Return value: (transfer full): a new #DfsmEnvironment
196  */
197 DfsmEnvironment *
198 _dfsm_environment_new (GPtrArray/*<GDBusInterfaceInfo>*/ *interfaces)
199 {
200         g_return_val_if_fail (interfaces != NULL, NULL);
201         g_return_val_if_fail (interfaces->len > 0, NULL);
202
203         return g_object_new (DFSM_TYPE_ENVIRONMENT,
204                              "interfaces", interfaces,
205                              NULL);
206 }
207
208 static GHashTable *
209 get_map_for_scope (DfsmEnvironment *self, DfsmVariableScope scope)
210 {
211         /* Get the right map to extract the variable from. */
212         switch (scope) {
213                 case DFSM_VARIABLE_SCOPE_LOCAL:
214                         return self->priv->local_variables;
215                 case DFSM_VARIABLE_SCOPE_OBJECT:
216                         return self->priv->object_variables;
217                 default:
218                         g_assert_not_reached ();
219         }
220 }
221
222 static VariableInfo *
223 look_up_variable_info (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name, gboolean create_if_nonexistent)
224 {
225         GHashTable *variable_map;
226         VariableInfo *variable_info;
227
228         /* Grab the variable. */
229         variable_map = get_map_for_scope (self, scope);
230         variable_info = g_hash_table_lookup (variable_map, variable_name);
231
232         /* Create the data if it doesn't exist. The members of variable_info will be filled in later by the caller. */
233         if (create_if_nonexistent == TRUE && variable_info == NULL) {
234                 variable_info = g_slice_new0 (VariableInfo);
235                 g_hash_table_insert (variable_map, g_strdup (variable_name), variable_info);
236         }
237
238         return variable_info;
239 }
240
241 /**
242  * dfsm_environment_has_variable:
243  * @self: a #DfsmEnvironment
244  * @scope: the scope of the variable
245  * @variable_name: the name of the variable in the given @scope
246  *
247  * Look up the value of the variable with the given @variable_name in @scope and see if it exists. This should only be used during the construction
248  * of abstract syntax trees, since all variables should be guaranteed to exist afterwards.
249  *
250  * Return value: %TRUE if the variable exists in the environment, %FALSE otherwise
251  */
252 gboolean
253 dfsm_environment_has_variable (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name)
254 {
255         VariableInfo *variable_info;
256
257         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (self), FALSE);
258         g_return_val_if_fail (variable_name != NULL, FALSE);
259
260         variable_info = look_up_variable_info (self, scope, variable_name, FALSE);
261
262         return (variable_info != NULL) ? TRUE : FALSE;
263 }
264
265 /**
266  * dfsm_environment_dup_variable_type:
267  * @self: a #DfsmEnvironment
268  * @scope: the scope of the variable
269  * @variable_name: the name of the variable in the given @scope
270  *
271  * Look up the type of the variable with the given @variable_name in @scope.
272  *
273  * Return value: (transfer full): type of the variable
274  */
275 GVariantType *
276 dfsm_environment_dup_variable_type (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name)
277 {
278         VariableInfo *variable_info;
279
280         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (self), NULL);
281         g_return_val_if_fail (variable_name != NULL, NULL);
282
283         variable_info = look_up_variable_info (self, scope, variable_name, FALSE);
284         g_assert (variable_info != NULL);
285         g_assert (variable_info->type != NULL);
286
287         return g_variant_type_copy (variable_info->type);
288 }
289
290 /**
291  * dfsm_environment_set_variable_type:
292  * @self: a #DfsmEnvironment
293  * @scope: the scope of the variable
294  * @variable_name: the name of the variable in the given @scope
295  * @new_type: the new type for the variable
296  *
297  * Set the type of the variable named @variable_name in @scope to @new_value. The variable must not exist already, and must not have its value queried
298  * by dfsm_environment_dup_variable_value() until it's set using dfsm_environment_set_variable_value().
299  */
300 void
301 dfsm_environment_set_variable_type (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name, const GVariantType *new_type)
302 {
303         VariableInfo *variable_info;
304         gchar *new_type_string;
305
306         g_return_if_fail (DFSM_IS_ENVIRONMENT (self));
307         g_return_if_fail (variable_name != NULL);
308         g_return_if_fail (new_type != NULL);
309         g_return_if_fail (g_variant_type_is_definite (new_type) == TRUE);
310
311         new_type_string = g_variant_type_dup_string (new_type);
312         g_debug ("Setting type of variable ‘%s’ (scope: %u) in environment %p to type: %s", variable_name, scope, self, new_type_string);
313         g_free (new_type_string);
314
315         variable_info = look_up_variable_info (self, scope, variable_name, TRUE);
316         g_assert (variable_info != NULL);
317         g_assert (variable_info->type == NULL);
318         g_assert (variable_info->value == NULL);
319
320         /* Set the new variable's type. */
321         variable_info->type = g_variant_type_copy (new_type);
322 }
323
324 /**
325  * dfsm_environment_dup_variable_value:
326  * @self: a #DfsmEnvironment
327  * @scope: the scope of the variable
328  * @variable_name: the name of the variable in the given @scope
329  *
330  * Look up the value of the variable with the given @variable_name in @scope.
331  *
332  * Return value: (transfer full): value of the variable
333  */
334 GVariant *
335 dfsm_environment_dup_variable_value (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name)
336 {
337         VariableInfo *variable_info;
338
339         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (self), NULL);
340         g_return_val_if_fail (variable_name != NULL, NULL);
341
342         variable_info = look_up_variable_info (self, scope, variable_name, FALSE);
343         g_assert (variable_info != NULL);
344         g_assert (variable_info->value != NULL);
345
346         return g_variant_ref (variable_info->value);
347 }
348
349 /**
350  * dfsm_environment_set_variable_value:
351  * @self: a #DfsmEnvironment
352  * @scope: the scope of the variable
353  * @variable_name: the name of the variable in the given @scope
354  * @new_value: the new value for the variable
355  *
356  * Set the value of the variable named @variable_name in @scope to @new_value. The variable must have already been created using
357  * dfsm_environment_set_variable_type() and the type of @new_value must match the type set then.
358  */
359 void
360 dfsm_environment_set_variable_value (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name, GVariant *new_value)
361 {
362         VariableInfo *variable_info;
363         gchar *new_value_string;
364
365         g_return_if_fail (DFSM_IS_ENVIRONMENT (self));
366         g_return_if_fail (variable_name != NULL);
367         g_return_if_fail (new_value != NULL);
368
369         new_value_string = g_variant_print (new_value, FALSE);
370         g_debug ("Setting variable ‘%s’ (scope: %u) in environment %p to value: %s", variable_name, scope, self, new_value_string);
371         g_free (new_value_string);
372
373         variable_info = look_up_variable_info (self, scope, variable_name, FALSE);
374         g_assert (variable_info != NULL);
375         g_assert (variable_info->type != NULL);
376         g_assert (g_variant_type_is_subtype_of (g_variant_get_type (new_value), variable_info->type) == TRUE);
377
378         /* Set the variable's value. Don't update its type. */
379         g_variant_ref_sink (new_value);
380
381         if (variable_info->value != NULL) {
382                 g_variant_unref (variable_info->value);
383         }
384
385         variable_info->value = new_value;
386 }
387
388 /**
389  * dfsm_environment_unset_variable_value:
390  * @self: a #DfsmEnvironment
391  * @scope: the scope of the variable
392  * @variable_name: the name of the variable in the given @scope
393  *
394  * Unset the value of the variable named @variable_name in @scope.
395  */
396 void
397 dfsm_environment_unset_variable_value (DfsmEnvironment *self, DfsmVariableScope scope, const gchar *variable_name)
398 {
399         GHashTable *variable_map;
400
401         g_return_if_fail (DFSM_IS_ENVIRONMENT (self));
402         g_return_if_fail (variable_name != NULL);
403
404         g_debug ("Unsetting variable ‘%s’ (scope: %u) in environment %p.", variable_name, scope, self);
405
406         /* Remove the variable. */
407         variable_map = get_map_for_scope (self, scope);
408         g_hash_table_remove (variable_map, variable_name);
409 }
410
411 static GHashTable *
412 copy_environment_hash_table (GHashTable *table)
413 {
414         GHashTable *new_table;
415         GHashTableIter iter;
416         gchar *key;
417         VariableInfo *value;
418
419         new_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) variable_info_free);
420
421         g_hash_table_iter_init (&iter, table);
422
423         while (g_hash_table_iter_next (&iter, (gpointer*) &key, (gpointer*) &value) == TRUE) {
424                 g_hash_table_insert (new_table, g_strdup (key), variable_info_copy (value));
425         }
426
427         return new_table;
428 }
429
430 /**
431  * dfsm_environment_save_reset_point:
432  * @self: a #DfsmEnvironment
433  *
434  * Save the current values of all the variables in the environment into a special area of memory, where they'll stay untouched. If
435  * dfsm_environment_reset() is called later, these original values will then replace the current values of variables in the environment. This is
436  * a useful but hacky way of allowing the simulation to be reset.
437  *
438  * This must only be called once in the lifetime of a given #DfsmEnvironment.
439  */
440 void
441 dfsm_environment_save_reset_point (DfsmEnvironment *self)
442 {
443         DfsmEnvironmentPrivate *priv;
444
445         g_return_if_fail (DFSM_IS_ENVIRONMENT (self));
446
447         priv = self->priv;
448
449         g_assert (priv->local_variables_original == NULL && priv->object_variables_original == NULL);
450
451         /* Copy local_variables into local_variables_original and the same for object_variables. */
452         priv->local_variables_original = copy_environment_hash_table (priv->local_variables);
453         priv->object_variables_original = copy_environment_hash_table (priv->object_variables);
454 }
455
456 /**
457  * dfsm_environment_reset:
458  * @self: a #DfsmEnvironment
459  *
460  * Reset the values of the variables in the environment to those saved previously using dfsm_environment_save_reset_point(). This function may only be
461  * called after dfsm_environment_save_reset_point() has been called, but can then be called as many times as necessary.
462  */
463 void
464 dfsm_environment_reset (DfsmEnvironment *self)
465 {
466         DfsmEnvironmentPrivate *priv;
467
468         g_return_if_fail (DFSM_IS_ENVIRONMENT (self));
469
470         priv = self->priv;
471
472         g_assert (priv->local_variables_original != NULL && priv->object_variables_original != NULL);
473
474         /* Copy local_variables_original over local_variables and the same for object_variables. */
475         g_hash_table_unref (priv->local_variables);
476         priv->local_variables = copy_environment_hash_table (priv->local_variables_original);
477
478         g_hash_table_unref (priv->object_variables);
479         priv->object_variables = copy_environment_hash_table (priv->object_variables_original);
480 }
481
482 static void
483 func_set_calculate_type_error (GError **error, const gchar *function_name, const GVariantType *parameters_supertype,
484                                const GVariantType *parameters_type)
485 {
486         gchar *parameters_supertype_string, *parameters_type_string;
487
488         /* Error */
489         parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
490         parameters_type_string = g_variant_type_dup_string (parameters_type);
491
492         g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
493                      _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ but received type ‘%s’."),
494                      function_name, parameters_supertype_string, parameters_type_string);
495
496         g_free (parameters_type_string);
497         g_free (parameters_supertype_string);
498 }
499
500 static GVariantType *
501 _keys_calculate_type (const GVariantType *parameters_type, GError **error)
502 {
503         const GVariantType *parameters_supertype = (const GVariantType*) "a{?*}";
504
505         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
506                 /* Error */
507                 func_set_calculate_type_error (error, "keys", parameters_supertype, parameters_type);
508                 return NULL;
509         }
510
511         /* For a{?*}, return a?. i.e. Return an array of the first element of the dictionary. */
512         return g_variant_type_new_array (g_variant_type_first (g_variant_type_element (parameters_type)));
513 }
514
515 static GVariant *
516 _keys_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
517 {
518         GVariantIter iter;
519         GVariantBuilder builder;
520         GVariant *parameters, *child_variant;
521
522         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
523         g_variant_builder_init (&builder, return_type);
524         g_variant_iter_init (&iter, parameters);
525         g_variant_unref (parameters);
526
527         while ((child_variant = g_variant_iter_next_value (&iter)) != NULL) {
528                 /* child_variant is a dictionary entry of any type; we want its key. */
529                 GVariant *key;
530
531                 key = g_variant_get_child_value (child_variant, 0);
532                 g_variant_builder_add_value (&builder, key);
533                 g_variant_unref (key);
534
535                 g_variant_unref (child_variant);
536         }
537
538         return g_variant_ref_sink (g_variant_builder_end (&builder));
539 }
540
541 static GVariantType *
542 _pair_keys_calculate_type (const GVariantType *parameters_type, GError **error)
543 {
544         const GVariantType *parameters_supertype = (const GVariantType*) "(a?*)";
545         const GVariantType *first_type;
546         GVariantType *pair_type, *entry_type;
547
548         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
549                 /* Error */
550                 func_set_calculate_type_error (error, "pairKeys", parameters_supertype, parameters_type);
551                 return NULL;
552         }
553
554         /* For (a?*), return a{?*}. i.e. Return a dictionary mapping the elements of the first input array to the second element (which will
555          * typically be fuzzy and evaluated once per key). */
556         first_type = g_variant_type_first (parameters_type);
557         entry_type = g_variant_type_new_dict_entry (g_variant_type_element (first_type), g_variant_type_next (first_type));
558         pair_type = g_variant_type_new_array (entry_type);
559         g_variant_type_free (entry_type);
560
561         return pair_type;
562 }
563
564 static GVariant *
565 _pair_keys_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
566 {
567         GVariantBuilder builder;
568         GVariantIter iter;
569         GVariant *parameters, *keys, *child_variant;
570
571         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
572         keys = g_variant_get_child_value (parameters, 0);
573         g_variant_unref (parameters);
574
575         g_variant_builder_init (&builder, return_type);
576         g_variant_iter_init (&iter, keys);
577
578         while ((child_variant = g_variant_iter_next_value (&iter)) != NULL) {
579                 GVariant *value;
580
581                 /* Re-evaluate the parameters so that the value is re-evaluated. This call-by-reference behaviour is useful because pairKeys is
582                  * typically called using a fuzzy value:
583                  *  pairKeys (["key1", "key2", "key3"], "some-value"?)
584                  * If we used call-by-value, the same fuzzy value would be assigned to all of the keys, which is a bit rubbish.
585                  * Unfortunately, this does mean we have to re-evaluate all of the input parameters (including the array of keys), since the
586                  * AST expression could be another function call, or something else other than a data structure expression. Oh well. */
587                 parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
588                 value = g_variant_get_child_value (parameters, 1);
589                 g_variant_unref (parameters);
590
591                 g_variant_builder_open (&builder, g_variant_type_element (return_type));
592                 g_variant_builder_add_value (&builder, child_variant);
593                 g_variant_builder_add_value (&builder, value);
594                 g_variant_builder_close (&builder);
595
596                 g_variant_unref (value);
597                 g_variant_unref (child_variant);
598         }
599
600         g_variant_unref (keys);
601
602         return g_variant_ref_sink (g_variant_builder_end (&builder));
603 }
604
605 static GVariantType *
606 _in_array_calculate_type (const GVariantType *parameters_type, GError **error)
607 {
608         const GVariantType *parameters_supertype = (const GVariantType*) "(*a*)";
609         const GVariantType *first_type;
610
611         /* As well as conforming to the supertype, the two *s have to be in a subtype relationship. */
612         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
613                 /* Error */
614                 func_set_calculate_type_error (error, "inArray", parameters_supertype, parameters_type);
615                 return NULL;
616         }
617
618         first_type = g_variant_type_first (parameters_type);
619
620         if (g_variant_type_is_subtype_of (first_type, g_variant_type_element (g_variant_type_next (first_type))) == FALSE) {
621                 gchar *parameters_supertype_string, *parameters_type_string;
622
623                 /* Error */
624                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
625                 parameters_type_string = g_variant_type_dup_string (parameters_type);
626
627                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
628                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the first item a subtype "
629                                "of the element type of the second item, but received type ‘%s’."),
630                              "inArray", parameters_supertype_string, parameters_type_string);
631
632                 g_free (parameters_type_string);
633                 g_free (parameters_supertype_string);
634
635                 return NULL;
636         }
637
638         /* Always return a boolean. */
639         return g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
640 }
641
642 static GVariant *
643 _in_array_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
644 {
645         GVariantIter iter;
646         GVariant *parameters, *needle, *haystack, *child_variant;
647         gboolean found = FALSE;
648
649         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
650         needle = g_variant_get_child_value (parameters, 0);
651         haystack = g_variant_get_child_value (parameters, 1);
652         g_variant_unref (parameters);
653
654         g_variant_iter_init (&iter, haystack);
655
656         while (found == FALSE && (child_variant = g_variant_iter_next_value (&iter)) != NULL) {
657                 found = g_variant_equal (needle, child_variant);
658
659                 g_variant_unref (child_variant);
660         }
661
662         g_variant_unref (haystack);
663         g_variant_unref (needle);
664
665         /* Return found. */
666         return g_variant_ref_sink (g_variant_new_boolean (found));
667 }
668
669 static GVariantType *
670 _array_get_calculate_type (const GVariantType *parameters_type, GError **error)
671 {
672         const GVariantType *parameters_supertype = (const GVariantType*) "(a*u*)";
673         const GVariantType *array_type, *element_type;
674
675         /* As well as conforming to the supertype, the two *s have to be in a subtype relationship. */
676         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
677                 /* Error */
678                 func_set_calculate_type_error (error, "arrayGet", parameters_supertype, parameters_type);
679                 return NULL;
680         }
681
682         array_type = g_variant_type_first (parameters_type);
683         element_type = g_variant_type_next (g_variant_type_next (array_type));
684
685         if (g_variant_type_is_subtype_of (element_type, g_variant_type_element (array_type)) == FALSE) {
686                 gchar *parameters_supertype_string, *parameters_type_string;
687
688                 /* Error */
689                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
690                 parameters_type_string = g_variant_type_dup_string (parameters_type);
691
692                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
693                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the third item a subtype "
694                                "of the element type of the first item, but received type ‘%s’."),
695                              "arrayGet", parameters_supertype_string, parameters_type_string);
696
697                 g_free (parameters_type_string);
698                 g_free (parameters_supertype_string);
699
700                 return NULL;
701         }
702
703         /* Always return the element of the array. */
704         return g_variant_type_copy (element_type);
705 }
706
707 static GVariant *
708 _array_get_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
709 {
710         GVariant *parameters, *haystack, *default_value, *child_variant;
711         guint array_index;
712
713         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
714         haystack = g_variant_get_child_value (parameters, 0);
715         g_variant_get_child (parameters, 1, "u", &array_index);
716         default_value = g_variant_get_child_value (parameters, 2);
717         g_variant_unref (parameters);
718
719         if (array_index >= g_variant_n_children (haystack)) {
720                 /* If the index is out of bounds, return the default value. */
721                 child_variant = g_variant_ref (default_value);
722         } else {
723                 /* Get the child value. */
724                 child_variant = g_variant_get_child_value (haystack, array_index);
725         }
726
727         g_variant_unref (default_value);
728         g_variant_unref (haystack);
729
730         /* Return the child. */
731         return child_variant;
732 }
733
734 static GVariantType *
735 _array_insert_calculate_type (const GVariantType *parameters_type, GError **error)
736 {
737         const GVariantType *parameters_supertype = (const GVariantType*) "(a*u*)";
738         const GVariantType *array_type, *third_type;
739
740         /* As well as conforming to the supertype, the two *s have to be in a subtype relationship. */
741         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
742                 /* Error */
743                 func_set_calculate_type_error (error, "arrayInsert", parameters_supertype, parameters_type);
744                 return NULL;
745         }
746
747         array_type = g_variant_type_first (parameters_type);
748         third_type = g_variant_type_next (g_variant_type_next (array_type));
749
750         if (g_variant_type_is_subtype_of (third_type, g_variant_type_element (array_type)) == FALSE) {
751                 gchar *parameters_supertype_string, *parameters_type_string;
752
753                 /* Error */
754                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
755                 parameters_type_string = g_variant_type_dup_string (parameters_type);
756
757                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
758                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the third item a subtype "
759                                "of the element type of the first item, but received type ‘%s’."),
760                              "arrayInsert", parameters_supertype_string, parameters_type_string);
761
762                 g_free (parameters_type_string);
763                 g_free (parameters_supertype_string);
764
765                 return NULL;
766         }
767
768         /* Always return an array of the same type. */
769         return g_variant_type_copy (array_type);
770 }
771
772 static GVariant *
773 _array_insert_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
774 {
775         GVariantIter iter;
776         GVariantBuilder builder;
777         GVariant *parameters, *new_value, *old_array, *child_variant;
778         guint array_index;
779
780         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
781         old_array = g_variant_get_child_value (parameters, 0);
782         g_variant_get_child (parameters, 1, "u", &array_index);
783         new_value = g_variant_get_child_value (parameters, 2);
784         g_variant_unref (parameters);
785
786         /* Silently clamp the insertion index to the size of the input array. */
787         array_index = MIN (array_index, g_variant_n_children (old_array));
788
789         /* Copy the old array, inserting the new value. */
790         g_variant_builder_init (&builder, return_type);
791         g_variant_iter_init (&iter, old_array);
792
793         while ((child_variant = g_variant_iter_next_value (&iter)) != NULL) {
794                 /* Insert the new value if we're at the right place.
795                  * The array_index will then wrap around to G_MAXUINT so we won't consider it again. */
796                 if (array_index-- == 0) {
797                         g_variant_builder_add_value (&builder, new_value);
798                 }
799
800                 /* Insert an existing value. */
801                 g_variant_builder_add_value (&builder, child_variant);
802
803                 g_variant_unref (child_variant);
804         }
805
806         /* If the new value is being appended, append it. */
807         if (array_index == 0) {
808                 g_variant_builder_add_value (&builder, new_value);
809         }
810
811         g_variant_unref (old_array);
812         g_variant_unref (new_value);
813
814         /* Return the new array. */
815         return g_variant_ref_sink (g_variant_builder_end (&builder));
816 }
817
818 static GVariantType *
819 _array_remove_calculate_type (const GVariantType *parameters_type, GError **error)
820 {
821         const GVariantType *parameters_supertype = (const GVariantType*) "(a*u)";
822
823         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
824                 /* Error */
825                 func_set_calculate_type_error (error, "arrayRemove", parameters_supertype, parameters_type);
826                 return NULL;
827         }
828
829         /* Always return an array of the same type */
830         return g_variant_type_copy (g_variant_type_first (parameters_type));
831 }
832
833 static GVariant *
834 _array_remove_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
835 {
836         GVariantIter iter;
837         GVariantBuilder builder;
838         GVariant *parameters, *old_array, *child_variant;
839         guint array_index;
840
841         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
842         old_array = g_variant_get_child_value (parameters, 0);
843         g_variant_get_child (parameters, 1, "u", &array_index);
844         g_variant_unref (parameters);
845
846         /* Silently clamp the removal index to the size of the input array. */
847         array_index = MIN (array_index, g_variant_n_children (old_array) - 1);
848
849         /* Copy the old array, removing the old value. */
850         g_variant_builder_init (&builder, return_type);
851         g_variant_iter_init (&iter, old_array);
852
853         while ((child_variant = g_variant_iter_next_value (&iter)) != NULL) {
854                 /* Insert an existing value, unless it's the one to remove. */
855                 if (array_index-- != 0) {
856                         g_variant_builder_add_value (&builder, child_variant);
857                 }
858
859                 g_variant_unref (child_variant);
860         }
861
862         g_variant_unref (old_array);
863
864         /* Return the new array. */
865         return g_variant_ref_sink (g_variant_builder_end (&builder));
866 }
867
868 static GVariantType *
869 _dict_set_calculate_type (const GVariantType *parameters_type, GError **error)
870 {
871         const GVariantType *parameters_supertype = (const GVariantType*) "(a{?*}?*)";
872         const GVariantType *dict_type, *key_type, *value_type;
873
874         /* As well as conforming to the supertype, the two *s have to be in a subtype relationship, as do the two ?s. */
875         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
876                 /* Error */
877                 func_set_calculate_type_error (error, "dictSet", parameters_supertype, parameters_type);
878                 return NULL;
879         }
880
881         dict_type = g_variant_type_first (parameters_type);
882         key_type = g_variant_type_next (dict_type);
883         value_type = g_variant_type_next (key_type);
884
885         if (g_variant_type_is_subtype_of (key_type, g_variant_type_key (g_variant_type_element (dict_type))) == FALSE) {
886                 gchar *parameters_supertype_string, *parameters_type_string;
887
888                 /* Error */
889                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
890                 parameters_type_string = g_variant_type_dup_string (parameters_type);
891
892                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
893                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the second item a subtype "
894                                "of the key type of the first item, but received type ‘%s’."),
895                              "dictSet", parameters_supertype_string, parameters_type_string);
896
897                 g_free (parameters_type_string);
898                 g_free (parameters_supertype_string);
899
900                 return NULL;
901         } else if (g_variant_type_is_subtype_of (value_type, g_variant_type_value (g_variant_type_element (dict_type))) == FALSE) {
902                 gchar *parameters_supertype_string, *parameters_type_string;
903
904                 /* Error */
905                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
906                 parameters_type_string = g_variant_type_dup_string (parameters_type);
907
908                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
909                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the third item a subtype "
910                                "of the value type of the first item, but received type ‘%s’."),
911                              "dictSet", parameters_supertype_string, parameters_type_string);
912
913                 g_free (parameters_type_string);
914                 g_free (parameters_supertype_string);
915
916                 return NULL;
917         }
918
919         /* Always return a dict of the same type. */
920         return g_variant_type_copy (dict_type);
921 }
922
923 static GVariant *
924 _dict_set_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
925 {
926         GVariantIter iter;
927         GVariantBuilder builder;
928         GVariant *parameters, *new_key, *new_value, *old_dict, *child_entry;
929         gboolean found = FALSE;
930
931         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
932         old_dict = g_variant_get_child_value (parameters, 0);
933         new_key = g_variant_get_child_value (parameters, 1);
934         new_value = g_variant_get_child_value (parameters, 2);
935         g_variant_unref (parameters);
936
937         /* Copy the old dict, inserting the new key-value pair. */
938         g_variant_builder_init (&builder, return_type);
939         g_variant_iter_init (&iter, old_dict);
940
941         while ((child_entry = g_variant_iter_next_value (&iter)) != NULL) {
942                 GVariant *current_key;
943
944                 /* If the current key matches the new key, replace the current value with the new value.
945                  * Otherwise, just insert the current key-value pair. */
946                 current_key = g_variant_get_child_value (child_entry, 0);
947
948                 if (g_variant_compare (current_key, new_key) == 0) {
949                         g_assert (found == FALSE);
950                         found = TRUE;
951
952                         /* Replace existing pair. */
953                         g_variant_builder_open (&builder, g_variant_type_element (return_type));
954
955                         g_variant_builder_add_value (&builder, new_key);
956                         g_variant_builder_add_value (&builder, new_value);
957
958                         g_variant_builder_close (&builder);
959                 } else {
960                         /* Insert existing pair. */
961                         g_variant_builder_add_value (&builder, child_entry);
962                 }
963
964                 g_variant_unref (current_key);
965                 g_variant_unref (child_entry);
966         }
967
968         /* If the new pair wasn't already in the dict, add it to the end */
969         if (found == FALSE) {
970                 g_variant_builder_open (&builder, g_variant_type_element (return_type));
971
972                 g_variant_builder_add_value (&builder, new_key);
973                 g_variant_builder_add_value (&builder, new_value);
974
975                 g_variant_builder_close (&builder);
976         }
977
978         g_variant_unref (new_value);
979         g_variant_unref (new_key);
980         g_variant_unref (old_dict);
981
982         /* Return the new dict. */
983         return g_variant_ref_sink (g_variant_builder_end (&builder));
984 }
985
986 static GVariantType *
987 _dict_unset_calculate_type (const GVariantType *parameters_type, GError **error)
988 {
989         const GVariantType *parameters_supertype = (const GVariantType*) "(a{?*}?)";
990         const GVariantType *dict_type, *key_type;
991
992         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
993                 /* Error */
994                 func_set_calculate_type_error (error, "dictUnset", parameters_supertype, parameters_type);
995                 return NULL;
996         }
997
998         dict_type = g_variant_type_first (parameters_type);
999         key_type = g_variant_type_next (dict_type);
1000
1001         if (g_variant_type_is_subtype_of (key_type, g_variant_type_key (g_variant_type_element (dict_type))) == FALSE) {
1002                 gchar *parameters_supertype_string, *parameters_type_string;
1003
1004                 /* Error */
1005                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
1006                 parameters_type_string = g_variant_type_dup_string (parameters_type);
1007
1008                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
1009                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the second item a subtype "
1010                                "of the key type of the first item, but received type ‘%s’."),
1011                              "dictUnset", parameters_supertype_string, parameters_type_string);
1012
1013                 g_free (parameters_type_string);
1014                 g_free (parameters_supertype_string);
1015
1016                 return NULL;
1017         }
1018
1019         /* Always return a dict of the same type */
1020         return g_variant_type_copy (dict_type);
1021 }
1022
1023 static GVariant *
1024 _dict_unset_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
1025 {
1026         GVariantIter iter;
1027         GVariantBuilder builder;
1028         GVariant *parameters, *old_key, *old_dict, *child_entry;
1029
1030         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
1031         old_dict = g_variant_get_child_value (parameters, 0);
1032         old_key = g_variant_get_child_value (parameters, 1);
1033         g_variant_unref (parameters);
1034
1035         /* Copy the old dict, removing the old key if it exists. */
1036         g_variant_builder_init (&builder, return_type);
1037         g_variant_iter_init (&iter, old_dict);
1038
1039         while ((child_entry = g_variant_iter_next_value (&iter)) != NULL) {
1040                 GVariant *current_key;
1041
1042                 /* Insert an existing value, unless it's the one to remove. */
1043                 current_key = g_variant_get_child_value (child_entry, 0);
1044
1045                 if (g_variant_compare (current_key, old_key) != 0) {
1046                         g_variant_builder_add_value (&builder, child_entry);
1047                 }
1048
1049                 g_variant_unref (current_key);
1050                 g_variant_unref (child_entry);
1051         }
1052
1053         g_variant_unref (old_key);
1054         g_variant_unref (old_dict);
1055
1056         /* Return the new dict. */
1057         return g_variant_ref_sink (g_variant_builder_end (&builder));
1058 }
1059
1060 static GVariantType *
1061 _dict_get_calculate_type (const GVariantType *parameters_type, GError **error)
1062 {
1063         const GVariantType *parameters_supertype = (const GVariantType*) "(a{?*}?*)";
1064         const GVariantType *dict_type, *key_type, *value_type;
1065
1066         /* As well as conforming to the supertype, the two *s have to be in a subtype relationship, as do the two ?s. */
1067         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
1068                 /* Error */
1069                 func_set_calculate_type_error (error, "dictGet", parameters_supertype, parameters_type);
1070                 return NULL;
1071         }
1072
1073         dict_type = g_variant_type_first (parameters_type);
1074         key_type = g_variant_type_next (dict_type);
1075         value_type = g_variant_type_next (key_type);
1076
1077         if (g_variant_type_is_subtype_of (key_type, g_variant_type_key (g_variant_type_element (dict_type))) == FALSE) {
1078                 gchar *parameters_supertype_string, *parameters_type_string;
1079
1080                 /* Error */
1081                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
1082                 parameters_type_string = g_variant_type_dup_string (parameters_type);
1083
1084                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
1085                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the second item a subtype "
1086                                "of the key type of the first item, but received type ‘%s’."),
1087                              "dictGet", parameters_supertype_string, parameters_type_string);
1088
1089                 g_free (parameters_type_string);
1090                 g_free (parameters_supertype_string);
1091
1092                 return NULL;
1093         } else if (g_variant_type_is_subtype_of (value_type, g_variant_type_value (g_variant_type_element (dict_type))) == FALSE) {
1094                 gchar *parameters_supertype_string, *parameters_type_string;
1095
1096                 /* Error */
1097                 parameters_supertype_string = g_variant_type_dup_string (parameters_supertype);
1098                 parameters_type_string = g_variant_type_dup_string (parameters_type);
1099
1100                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
1101                              _("Type mismatch between formal and actual parameters to function ‘%s’: expects type ‘%s’ with the third item a subtype "
1102                                "of the value type of the first item, but received type ‘%s’."),
1103                              "dictGet", parameters_supertype_string, parameters_type_string);
1104
1105                 g_free (parameters_type_string);
1106                 g_free (parameters_supertype_string);
1107
1108                 return NULL;
1109         }
1110
1111         /* Always return the value of the dict. */
1112         return g_variant_type_copy (value_type);
1113 }
1114
1115 static GVariant *
1116 _dict_get_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
1117 {
1118         GVariantIter iter;
1119         GVariant *parameters, *key, *dict, *default_value, *output_value = NULL, *child_entry;
1120
1121         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
1122         dict = g_variant_get_child_value (parameters, 0);
1123         key = g_variant_get_child_value (parameters, 1);
1124         default_value = g_variant_get_child_value (parameters, 2);
1125         g_variant_unref (parameters);
1126
1127         /* Search through the dict for the given key. If it doesn't exist, return the default value (third parameter). */
1128         g_variant_iter_init (&iter, dict);
1129
1130         while (output_value == NULL && (child_entry = g_variant_iter_next_value (&iter)) != NULL) {
1131                 GVariant *current_key;
1132
1133                 current_key = g_variant_get_child_value (child_entry, 0);
1134
1135                 if (g_variant_compare (current_key, key) == 0) {
1136                         output_value = g_variant_get_child_value (child_entry, 1);
1137                 }
1138
1139                 g_variant_unref (current_key);
1140                 g_variant_unref (child_entry);
1141         }
1142
1143         /* Default value. */
1144         if (output_value == NULL) {
1145                 output_value = g_variant_ref (default_value);
1146         }
1147
1148         g_variant_unref (key);
1149         g_variant_unref (dict);
1150         g_variant_unref (default_value);
1151
1152         /* Return the found or default value. */
1153         return output_value;
1154 }
1155
1156 static GVariantType *
1157 _dict_to_tuple_array_calculate_type (const GVariantType *parameters_type, GError **error)
1158 {
1159         const GVariantType *parameters_supertype = (const GVariantType*) "(a{?*})";
1160         const GVariantType *dict_entry_type, *key_type, *value_type;
1161         const GVariantType *items[2];
1162         GVariantType *array_type, *tuple_type;
1163
1164         /* The parameters have to conform to the supertype. */
1165         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
1166                 /* Error */
1167                 func_set_calculate_type_error (error, "dictToTupleArray", parameters_supertype, parameters_type);
1168                 return NULL;
1169         }
1170
1171         dict_entry_type = g_variant_type_element (g_variant_type_first (parameters_type));
1172         key_type = g_variant_type_key (dict_entry_type);
1173         value_type = g_variant_type_value (dict_entry_type);
1174
1175         /* Return an array of tuples containing the keys and values. */
1176         items[0] = key_type;
1177         items[1] = value_type;
1178         tuple_type = g_variant_type_new_tuple (items, 2);
1179         array_type = g_variant_type_new_array (tuple_type);
1180         g_variant_type_free (tuple_type);
1181
1182         return array_type;
1183 }
1184
1185 static GVariant *
1186 _dict_to_tuple_array_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
1187 {
1188         GVariantIter iter;
1189         GVariant *parameters, *dict, *child_entry, *output_array = NULL;
1190         GVariantBuilder array_builder;
1191
1192         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
1193         dict = g_variant_get_child_value (parameters, 0);
1194         g_variant_unref (parameters);
1195
1196         /* Iterate through the dict and copy all its entries into a new array of tuples. This function is designed primarily to bridge the gap between
1197          * http://telepathy.freedesktop.org/spec/Connection_Interface_Aliasing.html#Method:SetAliases and
1198          * http://telepathy.freedesktop.org/spec/Connection_Interface_Aliasing.html#Signal:AliasesChanged, since one uses a dict while the other
1199          * (erroneously) uses an array of tuples. */
1200         g_variant_builder_init (&array_builder, return_type);
1201         g_variant_iter_init (&iter, dict);
1202
1203         while ((child_entry = g_variant_iter_next_value (&iter)) != NULL) {
1204                 GVariant *current_key, *current_value;
1205                 GVariant *children[2];
1206
1207                 current_key = g_variant_get_child_value (child_entry, 0);
1208                 current_value = g_variant_get_child_value (child_entry, 1);
1209
1210                 children[0] = current_key;
1211                 children[1] = current_value;
1212                 g_variant_builder_add_value (&array_builder, g_variant_new_tuple (children, 2));
1213
1214                 g_variant_unref (current_key);
1215                 g_variant_unref (child_entry);
1216         }
1217
1218         output_array = g_variant_ref_sink (g_variant_builder_end (&array_builder));
1219
1220         g_variant_unref (dict);
1221
1222         /* Return the new array. */
1223         return output_array;
1224 }
1225
1226 static GVariantType *
1227 _struct_head_calculate_type (const GVariantType *parameters_type, GError **error)
1228 {
1229         const GVariantType *parameters_supertype = (const GVariantType*) "(r)";
1230
1231         /* Ideally, we'd have a structGet(ru) method which took the struct and a statically-defined integer giving the index. We could then check
1232          * that the integer was a valid index into the struct, and calculate the type accordingly. However, we don't currently support statically
1233          * defined values in the right way, so this isn't possible without introducing a runtime check on the index value. That's unsafe, so
1234          * instead we use a head-and-tail approach, which can be safely typed without needing parametric typing. */
1235         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
1236                 /* Error */
1237                 func_set_calculate_type_error (error, "structHead", parameters_supertype, parameters_type);
1238                 return NULL;
1239         }
1240
1241         /* Always return the value of the first element in the struct. */
1242         return g_variant_type_copy (g_variant_type_first (g_variant_type_first (parameters_type)));
1243 }
1244
1245 static GVariant *
1246 _struct_head_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
1247 {
1248         GVariant *parameters, *_struct, *output_value;
1249
1250         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
1251         _struct = g_variant_get_child_value (parameters, 0);
1252         output_value = g_variant_get_child_value (_struct, 0);
1253         g_variant_unref (_struct);
1254         g_variant_unref (parameters);
1255
1256         /* Return the output value. */
1257         return output_value;
1258 }
1259
1260 static GVariantType *
1261 _string_join_calculate_type (const GVariantType *parameters_type, GError **error)
1262 {
1263         const GVariantType *parameters_supertype = (const GVariantType*) "(sas)";
1264
1265         if (g_variant_type_is_subtype_of (parameters_type, parameters_supertype) == FALSE) {
1266                 /* Error */
1267                 func_set_calculate_type_error (error, "stringJoin", parameters_supertype, parameters_type);
1268                 return NULL;
1269         }
1270
1271         /* Always return a string. */
1272         return g_variant_type_copy (G_VARIANT_TYPE_STRING);
1273 }
1274
1275 static GVariant *
1276 _string_join_evaluate (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment)
1277 {
1278         GVariant *parameters, *separator, *stringlets, *stringlet;
1279         GVariantIter iter;
1280         GString *output_string;
1281         const gchar *separator_string;
1282         gboolean first = TRUE;
1283
1284         /* Decode the parameters. */
1285         parameters = dfsm_ast_expression_evaluate (parameters_expression, environment);
1286         separator = g_variant_get_child_value (parameters, 0);
1287         stringlets = g_variant_get_child_value (parameters, 1);
1288         g_variant_unref (parameters);
1289
1290         separator_string = g_variant_get_string (separator, NULL);
1291
1292         /* Join the stringlets together. */
1293         output_string = g_string_new ("");
1294         g_variant_iter_init (&iter, stringlets);
1295
1296         while ((stringlet = g_variant_iter_next_value (&iter)) != NULL) {
1297                 /* Add the stringlet to the output string. */
1298                 if (first == FALSE) {
1299                         /* Separator. */
1300                         g_string_append (output_string, separator_string);
1301                 }
1302
1303                 g_string_append (output_string, g_variant_get_string (stringlet, NULL));
1304                 first = FALSE;
1305
1306                 g_variant_unref (stringlet);
1307         }
1308
1309         g_variant_unref (stringlets);
1310         g_variant_unref (separator);
1311
1312         /* Return the output string. */
1313         return g_variant_ref_sink (g_variant_new_string (g_string_free (output_string, FALSE)));
1314 }
1315
1316 typedef struct {
1317         const gchar *name;
1318         GVariantType *(*calculate_type_func) (const GVariantType *parameters_type, GError **error);
1319         /* Return value of evaluate_func must not be floating. */
1320         GVariant *(*evaluate_func) (DfsmAstExpression *parameters_expression, const GVariantType *return_type, DfsmEnvironment *environment);
1321 } DfsmFunctionInfo;
1322
1323 static const DfsmFunctionInfo _function_info[] = {
1324         /* Name,                Calculate type func,            Evaluate func. */
1325         { "keys",               _keys_calculate_type,           _keys_evaluate },
1326         { "pairKeys",           _pair_keys_calculate_type,      _pair_keys_evaluate },
1327         { "inArray",            _in_array_calculate_type,       _in_array_evaluate },
1328         { "arrayGet",           _array_get_calculate_type,      _array_get_evaluate },
1329         { "arrayInsert",        _array_insert_calculate_type,   _array_insert_evaluate },
1330         { "arrayRemove",        _array_remove_calculate_type,   _array_remove_evaluate },
1331         { "dictSet",            _dict_set_calculate_type,       _dict_set_evaluate },
1332         { "dictUnset",          _dict_unset_calculate_type,     _dict_unset_evaluate },
1333         { "dictGet",            _dict_get_calculate_type,       _dict_get_evaluate },
1334         { "dictToTupleArray",   _dict_to_tuple_array_calculate_type,    _dict_to_tuple_array_evaluate },
1335         { "structHead",         _struct_head_calculate_type,    _struct_head_evaluate },
1336         { "stringJoin",         _string_join_calculate_type,    _string_join_evaluate },
1337 };
1338
1339 /*
1340  * dfsm_environment_get_function_info:
1341  * @function_name: name of the function to look up
1342  *
1343  * Look up static information about the given @function_name, such as its parameter and return types. If the function isn't known, %NULL will be
1344  * returned.
1345  *
1346  * Return value: (transfer none): information about the function, or %NULL
1347  */
1348 static const DfsmFunctionInfo *
1349 _get_function_info (const gchar *function_name)
1350 {
1351         guint i;
1352
1353         g_return_val_if_fail (function_name != NULL && *function_name != '\0', NULL);
1354
1355         /* Do a linear search for now, since there aren't many functions at all. */
1356         for (i = 0; i < G_N_ELEMENTS (_function_info); i++) {
1357                 if (strcmp (function_name, _function_info[i].name) == 0) {
1358                         return &_function_info[i];
1359                 }
1360         }
1361
1362         return NULL;
1363 }
1364
1365 /**
1366  * dfsm_environment_function_exists:
1367  * @function_name: name of the function to check whether it exists
1368  *
1369  * Check whether the built-in function with name @function_name exists.
1370  *
1371  * Return value: %TRUE if the function exists, %FALSE otherwise
1372  */
1373 gboolean
1374 dfsm_environment_function_exists (const gchar *function_name)
1375 {
1376         g_return_val_if_fail (function_name != NULL && *function_name != '\0', FALSE);
1377
1378         return (_get_function_info (function_name) != NULL) ? TRUE : FALSE;
1379 }
1380
1381 /**
1382  * dfsm_environment_function_calculate_type:
1383  * @function_name: name of the function to calculate the return type for
1384  * @parameters_type: the type of the input parameter (or parameters, if it's a tuple)
1385  * @error: (allow-none): a #GError, or %NULL
1386  *
1387  * Calculate the return type of @function_name when passed an input parameter of type @parameters_type. If the input parameter type is incompatible
1388  * with the function, an error will be set and %NULL will be returned.
1389  *
1390  * It is an error to pass a non-definite type in @parameters_type; or to pass a non-existent @function_name.
1391  *
1392  * Return value: (transfer full): type of the return value of the function
1393  */
1394 GVariantType *
1395 dfsm_environment_function_calculate_type (const gchar *function_name, const GVariantType *parameters_type, GError **error)
1396 {
1397         const DfsmFunctionInfo *function_info;
1398         GVariantType *return_type;
1399         GError *child_error = NULL;
1400
1401         g_return_val_if_fail (function_name != NULL && *function_name != '\0', NULL);
1402         g_return_val_if_fail (parameters_type != NULL, NULL);
1403         g_return_val_if_fail (g_variant_type_is_definite (parameters_type) == TRUE, NULL);
1404         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1405
1406         function_info = _get_function_info (function_name);
1407         g_assert (function_info != NULL);
1408
1409         g_assert (function_info->calculate_type_func != NULL);
1410         return_type = function_info->calculate_type_func (parameters_type, &child_error);
1411         g_assert ((return_type == NULL) != (child_error == NULL));
1412
1413         if (child_error != NULL) {
1414                 g_propagate_error (error, child_error);
1415         }
1416
1417         return return_type;
1418 }
1419
1420 /**
1421  * dfsm_environment_function_evaluate:
1422  * @function_name: name of the function to evaluate
1423  * @parameters_expression: an AST expression giving the input parameters
1424  * @environment: an environment containing all defined variables
1425  *
1426  * Evaluate @function_name when passed an input parameter expression, @parameters_expression. The return value will have type as given by
1427  * dfsm_environment_function_calculate_type() for the type of @parameters_expression.
1428  *
1429  * It is an error to pass an incompatible type in @parameters_expression; or to pass a non-existent @function_name. The parameters must have
1430  * previously been type checked using dfsm_environment_function_calculate_type().
1431  *
1432  * Individual functions may behave as call-by-value or call-by-reference as necessary, evaluating @parameters_expression as they see fit.
1433  *
1434  * Return value: (transfer full): return value of the function
1435  */
1436 GVariant *
1437 dfsm_environment_function_evaluate (const gchar *function_name, DfsmAstExpression *parameters_expression, DfsmEnvironment *environment)
1438 {
1439         const DfsmFunctionInfo *function_info;
1440         GVariantType *parameters_type, *return_type;
1441         GVariant *return_value = NULL;
1442         GError *child_error = NULL;
1443
1444         g_return_val_if_fail (function_name != NULL && *function_name != '\0', NULL);
1445         g_return_val_if_fail (DFSM_IS_AST_EXPRESSION (parameters_expression), NULL);
1446         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (environment), NULL);
1447
1448         function_info = _get_function_info (function_name);
1449         g_assert (function_info != NULL);
1450         g_assert (function_info->evaluate_func != NULL);
1451
1452         /* Calculate the return type. This has the added side-effect of dynamically type checking the input parameters. */
1453         parameters_type = dfsm_ast_expression_calculate_type (parameters_expression, environment);
1454         return_type = dfsm_environment_function_calculate_type (function_name, parameters_type, &child_error);
1455         g_variant_type_free (parameters_type);
1456
1457         g_assert (child_error == NULL);
1458
1459         /* Evaluate the function. */
1460         return_value = function_info->evaluate_func (parameters_expression, return_type, environment);
1461         g_assert (return_value != NULL);
1462
1463         g_variant_type_free (return_type);
1464
1465         return return_value;
1466 }
1467
1468 /**
1469  * dfsm_environment_get_interfaces:
1470  * @self: a #DfsmEnvironment
1471  *
1472  * Gets the value of the #DfsmEnvironment:interfaces property.
1473  *
1474  * Return value: (transfer none): information about the D-Bus interfaces implemented by DFSMs which use this environment
1475  */
1476 GPtrArray/*<GDBusInterfaceInfo>*/ *
1477 dfsm_environment_get_interfaces (DfsmEnvironment *self)
1478 {
1479         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (self), NULL);
1480
1481         return self->priv->interfaces;
1482 }