2 * Browser D-Bus Bridge, JavaScriptCore version
4 * Copyright © 2008 Movial Creative Technologies Inc
5 * Contact: Movial Creative Technologies Inc, <info@movial.com>
6 * Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 #include <dbus/dbus.h>
27 #include <JavaScriptCore/JavaScript.h>
29 #include "jscorebus-classfactory.h"
30 #include "jscorebus-marshal.h"
33 jsvalue_append_basic(JSContextRef context,
36 DBusMessageIter *iter);
39 gboolean jsvalue_array_append_to_message_iter (JSContextRef context,
40 const JSValueRef jsvalues[],
42 DBusMessageIter *iter,
43 const char *signature)
45 DBusSignatureIter siter;
47 char *sig = (char*) signature; /* This is a bit silly, I know */
50 /* If there is no signature, we need to do some autodetection */
51 if (signature == NULL)
54 parts = g_new0(char*, n_values + 1);
55 for (i = 0; i < n_values; i++)
57 parts[i] = jsvalue_to_signature(context, jsvalues[i]);
59 sig = g_strjoinv(NULL, parts);
63 /* If there *still* is no signature, or it is empty, we bork.
64 * Empty messages have no business going through this code path.
66 if (sig == NULL || strlen(sig) == 0)
68 g_warning("Could not autodetect signature for message arguments!");
73 /* First of all, we need to validate the signature */
74 dbus_error_init(&error);
75 if (!dbus_signature_validate(sig, &error))
77 g_warning(error.message);
83 dbus_signature_iter_init(&siter, sig);
86 char *arg_sig = dbus_signature_iter_get_signature(&siter);
87 if (!jsvalue_append_to_message_iter(context, jsvalues[i++], iter, arg_sig))
89 g_warning("Appending '%s' to message failed!", arg_sig);
96 } while (dbus_signature_iter_next(&siter));
104 gboolean jsvalue_append_to_message_iter(JSContextRef context,
106 DBusMessageIter *iter,
107 const char *signature)
109 DBusSignatureIter siter;
111 dbus_signature_iter_init(&siter, signature);
113 switch (dbus_signature_iter_get_current_type(&siter))
115 /* JSValueToBoolean follows the JS rules of what's true and false so we can
116 * simply take the value without checking the type of it
118 case DBUS_TYPE_BOOLEAN:
120 dbus_bool_t value = JSValueToBoolean(context, jsvalue);
121 if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value))
123 g_warning("Could not append a boolean to message iter");
129 case DBUS_TYPE_INT16:
130 case DBUS_TYPE_INT32:
131 case DBUS_TYPE_INT64:
132 case DBUS_TYPE_UINT16:
133 case DBUS_TYPE_UINT32:
134 case DBUS_TYPE_UINT64:
136 case DBUS_TYPE_STRING:
137 case DBUS_TYPE_OBJECT_PATH:
138 case DBUS_TYPE_SIGNATURE:
140 int type = dbus_signature_iter_get_current_type(&siter);
141 if (!jsvalue_append_basic(context, jsvalue, type, iter))
143 g_warning("Could not append a '%c' to message iter", type);
148 case DBUS_TYPE_DOUBLE:
150 /* Conversions between dbus_uint64_t and double seem to loose precision,
151 * that's why doubles are special-cased
153 double value = JSValueToNumber(context, jsvalue, NULL);
154 if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value))
156 g_warning("Could not append a double to message iter");
161 case DBUS_TYPE_ARRAY:
163 /* Dicts are implemented as arrays of entries in D-Bus */
164 if (dbus_signature_iter_get_element_type(&siter) == DBUS_TYPE_DICT_ENTRY)
167 JSPropertyNameArrayRef propnames;
168 char *dict_signature;
169 DBusMessageIter subiter;
170 DBusSignatureIter dictsiter;
171 DBusSignatureIter dictsubsiter;
173 dbus_signature_iter_recurse(&siter, &dictsiter);
174 dict_signature = dbus_signature_iter_get_signature(&dictsiter);
176 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
177 dict_signature, &subiter))
179 dbus_free(dict_signature);
180 g_warning("Memory exhausted!");
183 dbus_free(dict_signature);
184 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
185 props = JSPropertyNameArrayGetCount(propnames);
187 /* Move the signature iter to the value part of the signature
188 * We only support string->value dicts currently, though we coud do
191 dbus_signature_iter_recurse(&dictsiter, &dictsubsiter); /* Now at key */
192 dbus_signature_iter_next(&dictsubsiter); /* Now at value */
194 for (i = 0; i < props; i++)
197 DBusMessageIter dictiter;
199 if (!dbus_message_iter_open_container(&subiter, DBUS_TYPE_DICT_ENTRY,
202 g_warning("Memory exhausted!");
205 jsstr = JSPropertyNameArrayGetNameAtIndex(propnames, i);
206 cstr = string_from_jsstring(context, jsstr);
207 dbus_message_iter_append_basic(&dictiter, DBUS_TYPE_STRING, &cstr);
209 cstr = dbus_signature_iter_get_signature(&dictsubsiter);
210 jsvalue_append_to_message_iter(context,
211 JSObjectGetProperty(context, (JSObjectRef)jsvalue, jsstr, NULL),
214 dbus_message_iter_close_container(&subiter, &dictiter);
216 dbus_message_iter_close_container(iter, &subiter);
220 JSPropertyNameArrayRef propnames;
221 JSValueRef *jsvalues;
222 DBusMessageIter subiter;
223 DBusSignatureIter arraysiter;
224 char *array_signature = NULL;
225 if (!jsvalue_instanceof(context, jsvalue, "Array"))
227 g_warning("Expected JavaScript Array type, got %i",
228 JSValueGetType(context, jsvalue));
232 dbus_signature_iter_recurse(&siter, &arraysiter);
233 array_signature = dbus_signature_iter_get_signature(&arraysiter);
235 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
236 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
237 array_signature, &subiter))
239 g_warning("Memory exhausted!");
240 JSPropertyNameArrayRelease(propnames);
241 g_free(array_signature);
245 props = JSPropertyNameArrayGetCount(propnames);
247 for (i = 0; i < props; i++)
249 JSValueRef value = JSObjectGetPropertyAtIndex(context,
250 (JSObjectRef)jsvalue,
253 jsvalue_append_to_message_iter(context, value,
254 &subiter, array_signature);
256 dbus_message_iter_close_container(iter, &subiter);
257 g_free(array_signature);
258 JSPropertyNameArrayRelease(propnames);
262 case DBUS_TYPE_VARIANT:
264 DBusMessageIter subiter;
265 DBusSignatureIter vsiter;
267 JSValueRef value = NULL;
269 if (jsvalue_typeof(context, jsvalue, "DBusVariant"))
271 value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
276 dbus_signature_iter_recurse(&siter, &vsiter);
277 vsignature = jsvalue_to_signature(context, value);
279 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
280 vsignature, &subiter))
282 g_warning("Memory exhausted!");
287 if (!jsvalue_append_to_message_iter(context, value,
288 &subiter, vsignature))
290 g_warning("Failed to append variant contents with signature %s",
297 dbus_message_iter_close_container(iter, &subiter);
300 case DBUS_TYPE_STRUCT:
303 JSPropertyNameArrayRef propnames;
304 DBusMessageIter subiter;
305 DBusSignatureIter stsiter;
307 JSValueRef value = NULL;
309 if (jsvalue_typeof(context, jsvalue, "DBusStruct"))
311 value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
316 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)value);
317 props = JSPropertyNameArrayGetCount(propnames);
321 g_warning("Empty struct not allowed");
325 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
328 g_warning("Memory exhausted!");
332 dbus_signature_iter_recurse(&siter, &stsiter);
334 for (i = 0; i < props; i++)
336 const char *sig = dbus_signature_iter_get_signature(&stsiter);
337 JSValueRef child_value = JSObjectGetProperty(context,
339 JSPropertyNameArrayGetNameAtIndex(propnames, i),
342 if (!jsvalue_append_to_message_iter(context, child_value,
345 g_warning("Failed to append struct contents with signature %s",
350 if (!dbus_signature_iter_next(&stsiter))
355 JSPropertyNameArrayRelease(propnames);
356 dbus_message_iter_close_container(iter, &subiter);
360 g_warning("Tried to append invalid or unsupported argument '%s' "
361 "(base type '%c') to a message", signature,
362 dbus_signature_iter_get_current_type(&siter));
370 jsvalue_append_basic(JSContextRef context,
373 DBusMessageIter *iter)
376 dbus_uint64_t *value = NULL;
377 char *strvalue = NULL;
378 switch (JSValueGetType(context, jsvalue))
382 v = (dbus_uint64_t)JSValueToNumber(context, jsvalue, NULL);
388 strvalue = string_from_jsvalue(context, jsvalue);
391 case kJSTypeUndefined:
394 g_warning("Tried to pass undefined or null as basic type");
400 for (i = 0; i < JSCOREBUS_N_NUMBER_CLASSES; i++)
402 if (jscorebus_number_class_types[i] == type
403 && jsvalue_typeof(context, jsvalue, jscorebus_number_class_names[i]))
405 value = (dbus_uint64_t *)JSObjectGetPrivate((JSObjectRef)jsvalue);
410 if (jsvalue_typeof(context, jsvalue, "DBusObjectPath"))
412 strvalue = string_from_jsvalue(context,
413 JSObjectGetPrivate((JSObjectRef)jsvalue));
417 if (jsvalue_typeof(context, jsvalue, "DBusSignature"))
419 strvalue = string_from_jsvalue(context,
420 JSObjectGetPrivate((JSObjectRef)jsvalue));
424 /* Intentionally falls through to default */
427 g_warning("JSValue wasn't a '%c' (it was %i), or it is not supported",
428 type, JSValueGetType(context, jsvalue));
432 if (value != NULL && dbus_message_iter_append_basic(iter, type, value))
435 } else if (strvalue != NULL
436 && dbus_message_iter_append_basic(iter, type, &strvalue)) {
445 #define NUMERIC_VALUE(NAME) \
448 dbus_uint64_t value = 0; \
449 dbus_message_iter_get_basic(iter, &value); \
450 jsvalue = JSValueMakeNumber(context, (double)value); \
454 JSValueRef jsvalue_from_message_iter(JSContextRef context,
455 DBusMessageIter *iter)
459 switch (dbus_message_iter_get_arg_type (iter))
461 case DBUS_TYPE_BOOLEAN:
464 dbus_message_iter_get_basic(iter, &value);
465 jsvalue = JSValueMakeBoolean(context, value);
468 case NUMERIC_VALUE(BYTE)
469 case NUMERIC_VALUE(INT16)
470 case NUMERIC_VALUE(UINT16)
471 case NUMERIC_VALUE(INT32)
472 case NUMERIC_VALUE(UINT32)
473 case NUMERIC_VALUE(INT64)
474 case NUMERIC_VALUE(UINT64)
475 case DBUS_TYPE_DOUBLE:
478 dbus_message_iter_get_basic(iter, &value);
479 jsvalue = JSValueMakeNumber(context, value);
482 case DBUS_TYPE_OBJECT_PATH:
483 case DBUS_TYPE_SIGNATURE:
484 case DBUS_TYPE_STRING:
488 dbus_message_iter_get_basic(iter, &value);
489 jsstr = JSStringCreateWithUTF8CString(value);
490 jsvalue = JSValueMakeString(context, jsstr);
491 JSStringRelease(jsstr);
494 case DBUS_TYPE_ARRAY:
496 JSStringRef arrayProperty;
497 JSObjectRef arrayConstructor;
498 DBusMessageIter child_iter;
501 arrayProperty = JSStringCreateWithUTF8CString("Array");
502 arrayConstructor = JSValueToObject(context,
503 JSObjectGetProperty(context,
504 JSContextGetGlobalObject(context),
505 arrayProperty, NULL),
507 JSStringRelease(arrayProperty);
509 jsvalue = JSObjectCallAsConstructor(context, arrayConstructor,
512 dbus_message_iter_recurse(iter, &child_iter);
517 if (dbus_message_iter_get_arg_type(&child_iter) == DBUS_TYPE_DICT_ENTRY)
522 DBusMessageIter dictiter;
524 dbus_message_iter_recurse(&child_iter, &dictiter);
525 key = jsvalue_from_message_iter(context, &dictiter);
526 key_str = JSValueToStringCopy(context, key, NULL);
527 dbus_message_iter_next(&dictiter);
528 value = jsvalue_from_message_iter(context, &dictiter);
530 JSObjectSetProperty(context, (JSObjectRef)jsvalue,
531 key_str, value, 0, NULL);
532 JSStringRelease(key_str);
534 JSObjectSetPropertyAtIndex(context, (JSObjectRef)jsvalue, i++,
535 jsvalue_from_message_iter(context, &child_iter), NULL);
537 } while (dbus_message_iter_next(&child_iter));
541 case DBUS_TYPE_VARIANT:
543 DBusMessageIter child_iter;
544 dbus_message_iter_recurse(iter, &child_iter);
545 jsvalue = jsvalue_from_message_iter(context, &child_iter);
548 case DBUS_TYPE_INVALID:
549 /* Convert invalid to undefined */
550 jsvalue = JSValueMakeUndefined(context);
552 case DBUS_TYPE_DICT_ENTRY:
553 /* Dict entries should always be handled in the array branch */
554 g_assert_not_reached();
556 g_warning("Could not convert value from type %c (%i)",
557 dbus_message_iter_get_arg_type (iter),
558 dbus_message_iter_get_arg_type (iter));
559 jsvalue = JSValueMakeUndefined(context);
567 char *string_from_jsstring(JSContextRef context, JSStringRef jsstr)
572 len = JSStringGetMaximumUTF8CStringSize(jsstr);
573 cstr = g_new(char, len);
574 JSStringGetUTF8CString(jsstr, cstr, len);
579 char *string_from_jsvalue(JSContextRef context, JSValueRef jsvalue)
584 if (!JSValueIsString(context, jsvalue))
589 jsstr = JSValueToStringCopy(context, jsvalue, NULL);
590 cstr = string_from_jsstring(context, jsstr);
591 JSStringRelease(jsstr);
596 JSObjectRef function_from_jsvalue(JSContextRef context,
598 JSValueRef* exception)
600 JSObjectRef function;
602 if (JSValueIsNull(context, value))
607 if (!JSValueIsObject(context, value) )
609 /* TODO: set exception */
610 g_warning("%s: Value wasn't an object", G_STRFUNC);
614 function = JSValueToObject(context, value, exception);
615 if (!JSObjectIsFunction(context, function))
617 /* TODO: set exception */
618 g_warning("%s: Value wasn't a function", G_STRFUNC);
625 void call_function_with_message_args(JSContextRef context,
626 JSObjectRef thisObject,
627 JSObjectRef function,
628 DBusMessage *message)
630 size_t argumentCount;
633 DBusMessageIter iter;
636 * Iterate over the message arguments and append them to args
638 dbus_message_iter_init (message, &iter);
642 /* Error messages should have the error name as the first param */
643 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR)
645 const char *error_name = dbus_message_get_error_name(message);
647 if (error_name != NULL)
652 jsstr = JSStringCreateWithUTF8CString(error_name);
654 tmp = g_renew(JSValueRef, args, argumentCount);
656 args[argumentCount-1] = JSValueMakeString(context, jsstr);;
657 JSStringRelease(jsstr);
661 while ((arg_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
666 tmp = g_renew(JSValueRef, args, argumentCount);
668 args[argumentCount-1] = (JSValueRef)jsvalue_from_message_iter(context, &iter);
669 if (args[argumentCount-1] == NULL) {
670 g_warning("Couldn't get argument from argument type %c", arg_type);
671 args[argumentCount-1] = JSValueMakeUndefined(context);
673 dbus_message_iter_next (&iter);
676 JSObjectCallAsFunction(context, function, thisObject,
677 argumentCount, args, NULL);