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 const 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);
94 } while (dbus_signature_iter_next(&siter));
102 gboolean jsvalue_append_to_message_iter(JSContextRef context,
104 DBusMessageIter *iter,
105 const char *signature)
107 DBusSignatureIter siter;
109 dbus_signature_iter_init(&siter, signature);
111 switch (dbus_signature_iter_get_current_type(&siter))
113 /* JSValueToBoolean follows the JS rules of what's true and false so we can
114 * simply take the value without checking the type of it
116 case DBUS_TYPE_BOOLEAN:
118 dbus_bool_t value = JSValueToBoolean(context, jsvalue);
119 if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value))
121 g_warning("Could not append a boolean to message iter");
127 case DBUS_TYPE_INT16:
128 case DBUS_TYPE_INT32:
129 case DBUS_TYPE_INT64:
130 case DBUS_TYPE_UINT16:
131 case DBUS_TYPE_UINT32:
132 case DBUS_TYPE_UINT64:
134 case DBUS_TYPE_STRING:
135 case DBUS_TYPE_OBJECT_PATH:
136 case DBUS_TYPE_SIGNATURE:
138 int type = dbus_signature_iter_get_current_type(&siter);
139 if (!jsvalue_append_basic(context, jsvalue, type, iter))
141 g_warning("Could not append a '%c' to message iter", type);
146 case DBUS_TYPE_DOUBLE:
148 /* Conversions between dbus_uint64_t and double seem to loose precision,
149 * that's why doubles are special-cased
151 double value = JSValueToNumber(context, jsvalue, NULL);
152 if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value))
154 g_warning("Could not append a double to message iter");
159 case DBUS_TYPE_ARRAY:
161 /* Dicts are implemented as arrays of entries in D-Bus */
162 if (dbus_signature_iter_get_element_type(&siter) == DBUS_TYPE_DICT_ENTRY)
165 JSPropertyNameArrayRef propnames;
166 char *dict_signature;
167 DBusMessageIter subiter;
168 DBusSignatureIter dictsiter;
169 DBusSignatureIter dictsubsiter;
171 dbus_signature_iter_recurse(&siter, &dictsiter);
172 dict_signature = dbus_signature_iter_get_signature(&dictsiter);
174 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
175 dict_signature, &subiter))
177 g_warning("Memory exhausted!");
180 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
181 props = JSPropertyNameArrayGetCount(propnames);
183 /* Move the signature iter to the value part of the signature
184 * We only support string->value dicts currently, though we coud do
187 dbus_signature_iter_recurse(&dictsiter, &dictsubsiter); /* Now at key */
188 dbus_signature_iter_next(&dictsubsiter); /* Now at value */
190 for (i = 0; i < props; i++)
193 DBusMessageIter dictiter;
195 if (!dbus_message_iter_open_container(&subiter, DBUS_TYPE_DICT_ENTRY,
198 g_warning("Memory exhausted!");
201 jsstr = JSPropertyNameArrayGetNameAtIndex(propnames, i);
202 cstr = string_from_jsstring(context, jsstr);
203 dbus_message_iter_append_basic(&dictiter, DBUS_TYPE_STRING, &cstr);
205 jsvalue_append_to_message_iter(context,
206 JSObjectGetProperty(context, (JSObjectRef)jsvalue, jsstr, NULL),
207 &dictiter, dbus_signature_iter_get_signature(&dictsubsiter));
208 dbus_message_iter_close_container(&subiter, &dictiter);
210 dbus_message_iter_close_container(iter, &subiter);
214 JSPropertyNameArrayRef propnames;
215 JSValueRef *jsvalues;
216 DBusMessageIter subiter;
217 DBusSignatureIter arraysiter;
218 char *array_signature = NULL;
219 if (!jsvalue_instanceof(context, jsvalue, "Array"))
221 g_warning("Expected JavaScript Array type, got %i",
222 JSValueGetType(context, jsvalue));
226 dbus_signature_iter_recurse(&siter, &arraysiter);
227 array_signature = dbus_signature_iter_get_signature(&arraysiter);
229 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)jsvalue);
230 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
231 array_signature, &subiter))
233 g_warning("Memory exhausted!");
234 JSPropertyNameArrayRelease(propnames);
235 g_free(array_signature);
239 props = JSPropertyNameArrayGetCount(propnames);
241 for (i = 0; i < props; i++)
243 JSValueRef value = JSObjectGetPropertyAtIndex(context,
244 (JSObjectRef)jsvalue,
247 jsvalue_append_to_message_iter(context, value,
248 &subiter, array_signature);
250 dbus_message_iter_close_container(iter, &subiter);
251 g_free(array_signature);
252 JSPropertyNameArrayRelease(propnames);
256 case DBUS_TYPE_VARIANT:
258 DBusMessageIter subiter;
259 DBusSignatureIter vsiter;
261 JSValueRef value = NULL;
263 if (jsvalue_typeof(context, jsvalue, "DBusVariant"))
265 value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
270 dbus_signature_iter_recurse(&siter, &vsiter);
271 vsignature = jsvalue_to_signature(context, value);
273 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
274 vsignature, &subiter))
276 g_warning("Memory exhausted!");
281 if (!jsvalue_append_to_message_iter(context, value,
282 &subiter, vsignature))
284 g_warning("Failed to append variant contents with signature %s",
291 dbus_message_iter_close_container(iter, &subiter);
294 case DBUS_TYPE_STRUCT:
297 JSPropertyNameArrayRef propnames;
298 DBusMessageIter subiter;
299 DBusSignatureIter stsiter;
301 JSValueRef value = NULL;
303 if (jsvalue_typeof(context, jsvalue, "DBusStruct"))
305 value = (JSValueRef)JSObjectGetPrivate((JSObjectRef)jsvalue);
310 propnames = JSObjectCopyPropertyNames(context, (JSObjectRef)value);
311 props = JSPropertyNameArrayGetCount(propnames);
315 g_warning("Empty struct not allowed");
319 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
322 g_warning("Memory exhausted!");
326 dbus_signature_iter_recurse(&siter, &stsiter);
328 for (i = 0; i < props; i++)
330 const char *sig = dbus_signature_iter_get_signature(&stsiter);
331 JSValueRef child_value = JSObjectGetProperty(context,
333 JSPropertyNameArrayGetNameAtIndex(propnames, i),
336 if (!jsvalue_append_to_message_iter(context, child_value,
339 g_warning("Failed to append struct contents with signature %s",
344 if (!dbus_signature_iter_next(&stsiter))
349 JSPropertyNameArrayRelease(propnames);
350 dbus_message_iter_close_container(iter, &subiter);
354 g_warning("Tried to append invalid or unsupported argument '%s' "
355 "(base type '%c') to a message", signature,
356 dbus_signature_iter_get_current_type(&siter));
364 jsvalue_append_basic(JSContextRef context,
367 DBusMessageIter *iter)
370 dbus_uint64_t *value = NULL;
371 char *strvalue = NULL;
372 switch (JSValueGetType(context, jsvalue))
376 v = (dbus_uint64_t)JSValueToNumber(context, jsvalue, NULL);
382 strvalue = string_from_jsvalue(context, jsvalue);
388 for (i = 0; i < JSCOREBUS_N_NUMBER_CLASSES; i++)
390 if (jscorebus_number_class_types[i] == type
391 && jsvalue_typeof(context, jsvalue, jscorebus_number_class_names[i]))
393 value = (dbus_uint64_t *)JSObjectGetPrivate((JSObjectRef)jsvalue);
398 if (jsvalue_typeof(context, jsvalue, "DBusObjectPath"))
400 strvalue = string_from_jsvalue(context,
401 JSObjectGetPrivate((JSObjectRef)jsvalue));
405 if (jsvalue_typeof(context, jsvalue, "DBusSignature"))
407 strvalue = string_from_jsvalue(context,
408 JSObjectGetPrivate((JSObjectRef)jsvalue));
412 /* Intentionally falls through to default */
414 case kJSTypeUndefined:
417 g_warning("Tried to pass undefined or null as basic type");
421 g_warning("JSValue wasn't a '%c' (it was %i), or it is not supported",
422 type, JSValueGetType(context, jsvalue));
426 if (value != NULL && dbus_message_iter_append_basic(iter, type, value))
429 } else if (strvalue != NULL
430 && dbus_message_iter_append_basic(iter, type, &strvalue)) {
439 #define NUMERIC_VALUE(NAME) \
442 dbus_uint64_t value = 0; \
443 dbus_message_iter_get_basic(iter, &value); \
444 jsvalue = JSValueMakeNumber(context, (double)value); \
448 JSValueRef jsvalue_from_message_iter(JSContextRef context,
449 DBusMessageIter *iter)
453 switch (dbus_message_iter_get_arg_type (iter))
455 case DBUS_TYPE_BOOLEAN:
458 dbus_message_iter_get_basic(iter, &value);
459 jsvalue = JSValueMakeBoolean(context, value);
462 case NUMERIC_VALUE(BYTE)
463 case NUMERIC_VALUE(INT16)
464 case NUMERIC_VALUE(UINT16)
465 case NUMERIC_VALUE(INT32)
466 case NUMERIC_VALUE(UINT32)
467 case NUMERIC_VALUE(INT64)
468 case NUMERIC_VALUE(UINT64)
469 case DBUS_TYPE_DOUBLE:
472 dbus_message_iter_get_basic(iter, &value);
473 jsvalue = JSValueMakeNumber(context, value);
476 case DBUS_TYPE_OBJECT_PATH:
477 case DBUS_TYPE_SIGNATURE:
478 case DBUS_TYPE_STRING:
482 dbus_message_iter_get_basic(iter, &value);
483 jsstr = JSStringCreateWithUTF8CString(value);
484 jsvalue = JSValueMakeString(context, jsstr);
485 JSStringRelease(jsstr);
488 case DBUS_TYPE_ARRAY:
490 JSStringRef arrayProperty;
491 JSObjectRef arrayConstructor;
492 DBusMessageIter child_iter;
495 arrayProperty = JSStringCreateWithUTF8CString("Array");
496 arrayConstructor = JSValueToObject(context,
497 JSObjectGetProperty(context,
498 JSContextGetGlobalObject(context),
499 arrayProperty, NULL),
501 JSStringRelease(arrayProperty);
503 jsvalue = JSObjectCallAsConstructor(context, arrayConstructor,
506 dbus_message_iter_recurse(iter, &child_iter);
511 if (dbus_message_iter_get_arg_type(&child_iter) == DBUS_TYPE_DICT_ENTRY)
516 DBusMessageIter dictiter;
518 dbus_message_iter_recurse(&child_iter, &dictiter);
519 key = jsvalue_from_message_iter(context, &dictiter);
520 key_str = JSValueToStringCopy(context, key, NULL);
521 dbus_message_iter_next(&dictiter);
522 value = jsvalue_from_message_iter(context, &dictiter);
524 JSObjectSetProperty(context, (JSObjectRef)jsvalue,
525 key_str, value, 0, NULL);
526 JSStringRelease(key_str);
528 JSObjectSetPropertyAtIndex(context, (JSObjectRef)jsvalue, i++,
529 jsvalue_from_message_iter(context, &child_iter), NULL);
531 } while (dbus_message_iter_next(&child_iter));
535 case DBUS_TYPE_VARIANT:
537 DBusMessageIter child_iter;
538 dbus_message_iter_recurse(iter, &child_iter);
539 jsvalue = jsvalue_from_message_iter(context, &child_iter);
542 case DBUS_TYPE_INVALID:
543 /* Convert invalid to undefined */
544 jsvalue = JSValueMakeUndefined(context);
546 case DBUS_TYPE_DICT_ENTRY:
547 /* Dict entries should always be handled in the array branch */
548 g_assert_not_reached();
550 g_warning("Could not convert value from type %c (%i)",
551 dbus_message_iter_get_arg_type (iter),
552 dbus_message_iter_get_arg_type (iter));
553 jsvalue = JSValueMakeUndefined(context);
561 char *string_from_jsstring(JSContextRef context, JSStringRef jsstr)
566 len = JSStringGetMaximumUTF8CStringSize(jsstr);
567 cstr = g_new(char, len);
568 JSStringGetUTF8CString(jsstr, cstr, len);
573 char *string_from_jsvalue(JSContextRef context, JSValueRef jsvalue)
578 if (!JSValueIsString(context, jsvalue))
583 jsstr = JSValueToStringCopy(context, jsvalue, NULL);
584 cstr = string_from_jsstring(context, jsstr);
585 JSStringRelease(jsstr);
590 JSObjectRef function_from_jsvalue(JSContextRef context,
592 JSValueRef* exception)
594 JSObjectRef function;
596 if (JSValueIsNull(context, value))
601 if (!JSValueIsObject(context, value) )
603 /* TODO: set exception */
604 g_warning("%s: Value wasn't an object", G_STRFUNC);
608 function = JSValueToObject(context, value, exception);
609 if (!JSObjectIsFunction(context, function))
611 /* TODO: set exception */
612 g_warning("%s: Value wasn't a function", G_STRFUNC);
619 void call_function_with_message_args(JSContextRef context,
620 JSObjectRef thisObject,
621 JSObjectRef function,
622 DBusMessage *message)
624 size_t argumentCount;
627 DBusMessageIter iter;
630 * Iterate over the message arguments and append them to args
632 dbus_message_iter_init (message, &iter);
636 /* Error messages should have the error name as the first param */
637 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR)
639 const char *error_name = dbus_message_get_error_name(message);
641 if (error_name != NULL)
646 jsstr = JSStringCreateWithUTF8CString(error_name);
648 tmp = g_renew(JSValueRef, args, argumentCount);
650 args[argumentCount-1] = JSValueMakeString(context, jsstr);;
651 JSStringRelease(jsstr);
655 while ((arg_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
660 tmp = g_renew(JSValueRef, args, argumentCount);
662 args[argumentCount-1] = (JSValueRef)jsvalue_from_message_iter(context, &iter);
663 if (args[argumentCount-1] == NULL) {
664 g_warning("Couldn't get argument from argument type %c", arg_type);
665 args[argumentCount-1] = JSValueMakeUndefined(context);
667 dbus_message_iter_next (&iter);
670 JSObjectCallAsFunction(context, function, thisObject,
671 argumentCount, args, NULL);